1 /*
2 * texturebrush.c
3 *
4 * Copyright (C) 2003,2006-2007 Novell, Inc. http://www.novell.com
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
7 * and associated documentation files (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all copies or substantial
13 * portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
16 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
19 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors:
22 * Ravindra (rkumar@novell.com)
23 * Sebastien Pouliot <sebastien@ximian.com>
24 *
25 * TODO
26 * reduce duplication between draw_tile*_texture functions
27 */
28
29 #include "texturebrush-private.h"
30 #include "general-private.h"
31 #include "graphics-private.h"
32 #include "bitmap-private.h"
33 #include "matrix-private.h"
34 #include "metafile-private.h"
35
36 static GpStatus gdip_texture_setup (GpGraphics *graphics, GpBrush *brush);
37 static GpStatus gdip_texture_clone (GpBrush *brush, GpBrush **clonedBrush);
38 static GpStatus gdip_texture_destroy (GpBrush *brush);
39
40 /*
41 * we have a single copy of vtable for
42 * all instances of texturebrush.
43 */
44
45 static BrushClass vtable = { BrushTypeTextureFill,
46 gdip_texture_setup,
47 gdip_texture_clone,
48 gdip_texture_destroy };
49
50 static cairo_content_t
from_cairoformat_to_content(cairo_format_t format)51 from_cairoformat_to_content (cairo_format_t format)
52 {
53 switch (format) {
54 case CAIRO_FORMAT_RGB24:
55 return CAIRO_CONTENT_COLOR;
56 case CAIRO_FORMAT_A8:
57 return CAIRO_CONTENT_ALPHA;
58 case CAIRO_FORMAT_ARGB32:
59 default:
60 return CAIRO_CONTENT_COLOR_ALPHA;
61 }
62 }
63
64 static void
gdip_texture_init(GpTexture * texture)65 gdip_texture_init (GpTexture *texture)
66 {
67 gdip_brush_init (&texture->base, &vtable);
68 texture->wrapMode = WrapModeTile;
69 texture->rectangle.X = 0;
70 texture->rectangle.Y = 0;
71 texture->rectangle.Width = 0;
72 texture->rectangle.Height = 0;
73 texture->pattern = NULL;
74 cairo_matrix_init_identity (&texture->matrix);
75 }
76
77 static GpTexture*
gdip_texture_new(void)78 gdip_texture_new (void)
79 {
80 GpTexture *result = (GpTexture *) GdipAlloc (sizeof (GpTexture));
81 if (result)
82 gdip_texture_init (result);
83
84 return result;
85 }
86
87 /*
88 * functions to create different wrapmodes.
89 */
90 static GpStatus
draw_tile_texture(cairo_t * ct,GpBitmap * bitmap,GpTexture * brush)91 draw_tile_texture (cairo_t *ct, GpBitmap *bitmap, GpTexture *brush)
92 {
93 cairo_surface_t *texture;
94 cairo_pattern_t *pat;
95 GpStatus status;
96 GpRect *rect = &brush->rectangle;
97 cairo_t *ct2;
98
99 if (!rect)
100 return InvalidParameter;
101 if (gdip_bitmap_ensure_surface (bitmap) == NULL)
102 return OutOfMemory;
103
104 /* Use the bitmap->surface as a pattern */
105 pat = cairo_pattern_create_for_surface (bitmap->surface);
106 status = gdip_get_pattern_status (pat);
107 if (status != Ok) {
108 return status;
109 }
110
111 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
112
113 /* texture surface to be created */
114 texture = cairo_surface_create_similar (
115 bitmap->surface, from_cairoformat_to_content (bitmap->cairo_format),
116 rect->Width, rect->Height);
117 status = gdip_get_status (cairo_surface_status (texture));
118 if (status != Ok) {
119 cairo_pattern_destroy (pat);
120 return status;
121 }
122
123 /* Draw the texture */
124 ct2 = cairo_create (texture);
125 cairo_set_source (ct2, pat);
126 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
127 cairo_fill (ct2);
128 cairo_destroy (ct2);
129
130 brush->pattern = cairo_pattern_create_for_surface (texture);
131 status = gdip_get_pattern_status (brush->pattern);
132 if (status == Ok)
133 cairo_pattern_set_extend (brush->pattern, CAIRO_EXTEND_REPEAT);
134
135 cairo_pattern_destroy (pat);
136 cairo_surface_destroy (texture);
137
138 return gdip_get_status (cairo_status (ct));
139 }
140
141 static GpStatus
draw_tile_flipX_texture(cairo_t * ct,GpBitmap * bitmap,GpTexture * brush)142 draw_tile_flipX_texture (cairo_t *ct, GpBitmap *bitmap, GpTexture *brush)
143 {
144 cairo_surface_t *texture;
145 cairo_pattern_t *pat;
146 GpRect *rect = &brush->rectangle;
147 GpStatus status;
148 cairo_t *ct2;
149 cairo_matrix_t tempMatrix;
150
151 if (!rect)
152 return InvalidParameter;
153 if (gdip_bitmap_ensure_surface (bitmap) == NULL)
154 return OutOfMemory;
155
156 /* Use the bitmap->surface as a pattern */
157 pat = cairo_pattern_create_for_surface (bitmap->surface);
158 status = gdip_get_pattern_status (pat);
159 if (status != Ok)
160 return status;
161
162 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
163
164 /* texture surface to be created */
165 texture = cairo_surface_create_similar (
166 bitmap->surface, from_cairoformat_to_content (bitmap->cairo_format),
167 2 * rect->Width, rect->Height);
168 status = gdip_get_status (cairo_surface_status (texture));
169 if (status != Ok) {
170 cairo_pattern_destroy (pat);
171 return status;
172 }
173
174 /* Draw left part of the texture */
175 ct2 = cairo_create (texture);
176 cairo_set_source (ct2, pat);
177 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
178 cairo_fill (ct2);
179
180 /* Not sure if this is a bug, but using rect->Width - 1 avoids the seam. */
181 cairo_matrix_init_identity (&tempMatrix);
182 cairo_matrix_translate (&tempMatrix, rect->Width - 1, 0);
183
184 /* scale in -X direction to flip along X */
185 cairo_matrix_scale (&tempMatrix, -1.0, 1.0);
186 cairo_pattern_set_matrix (pat, &tempMatrix);
187
188 /* Draw right part of the texture */
189 cairo_set_source (ct2, pat);
190 cairo_rectangle (ct2, rect->Width, 0, rect->Width, rect->Height);
191
192 cairo_fill (ct2);
193 cairo_destroy (ct2);
194
195 brush->pattern = cairo_pattern_create_for_surface (texture);
196 status = gdip_get_pattern_status (brush->pattern);
197 if (status == Ok)
198 cairo_pattern_set_extend (brush->pattern, CAIRO_EXTEND_REPEAT);
199
200 cairo_pattern_destroy (pat);
201 cairo_surface_destroy (texture);
202
203 return gdip_get_status (cairo_status (ct));
204 }
205
206 static GpStatus
draw_tile_flipY_texture(cairo_t * ct,GpBitmap * bitmap,GpTexture * brush)207 draw_tile_flipY_texture (cairo_t *ct, GpBitmap *bitmap, GpTexture *brush)
208 {
209 cairo_surface_t *texture;
210 cairo_pattern_t *pat;
211 GpMatrix tempMatrix;
212 GpRect *rect = &brush->rectangle;
213 GpStatus status;
214 cairo_t *ct2;
215
216 if (!rect)
217 return InvalidParameter;
218 if (gdip_bitmap_ensure_surface (bitmap) == NULL)
219 return OutOfMemory;
220
221 /* Use the bitmap->surface as a pattern */
222 pat = cairo_pattern_create_for_surface (bitmap->surface);
223 status = gdip_get_pattern_status (pat);
224 if (status != Ok)
225 return status;
226
227 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
228
229 /* texture surface to be created */
230 texture = cairo_surface_create_similar (
231 bitmap->surface, from_cairoformat_to_content (bitmap->cairo_format),
232 rect->Width, 2 * rect->Height);
233 status = gdip_get_status (cairo_surface_status (texture));
234 if (status != Ok) {
235 cairo_pattern_destroy (pat);
236 return status;
237 }
238
239 /* Draw upper part of the texture */
240 ct2 = cairo_create (texture);
241 cairo_set_source (ct2, pat);
242 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
243 cairo_fill (ct2);
244
245 /* Not sure if this is a bug, but using rect->Height - 1 avoids the seam. */
246 cairo_matrix_init_identity (&tempMatrix);
247 cairo_matrix_translate (&tempMatrix, 0, rect->Height - 1);
248
249 /* scale in -Y direction to flip along Y */
250 cairo_matrix_scale (&tempMatrix, 1.0, -1.0);
251 cairo_pattern_set_matrix (pat, &tempMatrix);
252
253 /* Draw lower part of the texture */
254 cairo_translate (ct2, 0, rect->Height);
255 cairo_set_source (ct2, pat);
256 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
257 cairo_fill (ct2);
258 cairo_destroy(ct2);
259
260 brush->pattern = cairo_pattern_create_for_surface (texture);
261 status = gdip_get_pattern_status (brush->pattern);
262 if (status != Ok) {
263 cairo_pattern_destroy (pat);
264 cairo_surface_destroy (texture);
265 return status;
266 }
267 cairo_pattern_set_extend (brush->pattern, CAIRO_EXTEND_REPEAT);
268
269 cairo_pattern_destroy (pat);
270 cairo_surface_destroy (texture);
271
272 return gdip_get_status (cairo_status (ct));
273 }
274
275 static GpStatus
draw_tile_flipXY_texture(cairo_t * ct,GpBitmap * bitmap,GpTexture * brush)276 draw_tile_flipXY_texture (cairo_t *ct, GpBitmap *bitmap, GpTexture *brush)
277 {
278 cairo_surface_t *texture;
279 cairo_pattern_t *pat;
280 GpMatrix tempMatrix;
281 GpRect *rect = &brush->rectangle;
282 GpStatus status;
283 cairo_t *ct2;
284
285 if (!rect)
286 return InvalidParameter;
287 if (gdip_bitmap_ensure_surface (bitmap) == NULL)
288 return OutOfMemory;
289
290 /* Use the original as a pattern */
291 pat = cairo_pattern_create_for_surface (bitmap->surface);
292 status = gdip_get_pattern_status (pat);
293 if (status != Ok)
294 return status;
295
296 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
297
298 /* texture surface to be created */
299 texture = cairo_surface_create_similar (
300 bitmap->surface, from_cairoformat_to_content (bitmap->cairo_format),
301 2 * rect->Width, 2 * rect->Height);
302 status = gdip_get_status (cairo_surface_status (texture));
303 if (status != Ok) {
304 cairo_pattern_destroy (pat);
305 return status;
306 }
307
308 /* Draw upper left part of the texture */
309 ct2 = cairo_create (texture);
310 cairo_set_source (ct2, pat);
311 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
312 cairo_fill (ct2);
313
314 /* Not sure if this is a bug, but using rect->Height - 1 avoids the seam. */
315 cairo_matrix_init_identity (&tempMatrix);
316 cairo_matrix_translate (&tempMatrix, 0, rect->Height - 1);
317 /* scale in -Y direction to flip along Y */
318 cairo_matrix_scale (&tempMatrix, 1.0, -1.0);
319 cairo_pattern_set_matrix (pat, &tempMatrix);
320
321 /* Draw lower left part of the texture */
322 cairo_translate (ct2, 0, rect->Height);
323 cairo_set_source (ct2, pat);
324 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
325 cairo_fill (ct2);
326
327 /* Reset the pattern matrix and do fresh transformation */
328 cairo_matrix_init_identity (&tempMatrix);
329
330 /* Not sure if this is a bug, but using rect->Width - 1 avoids the seam. */
331 cairo_matrix_translate (&tempMatrix, rect->Width - 1, 0);
332
333 /* scale in -X direction to flip along X */
334 cairo_matrix_scale (&tempMatrix, -1.0, 1.0);
335 cairo_pattern_set_matrix (pat, &tempMatrix);
336
337 /* Draw upper right part of the texture */
338 cairo_translate (ct2, rect->Width, -rect->Height);
339 cairo_set_source (ct2, pat);
340 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
341 cairo_fill (ct2);
342
343 /* Not sure if this is a bug, but using rect->Height - 1 avoids the seam. */
344 cairo_matrix_translate (&tempMatrix, 0, rect->Height - 1);
345
346 /* scale in -Y direction to flip along Y */
347 cairo_matrix_scale (&tempMatrix, 1.0, -1.0);
348 cairo_pattern_set_matrix (pat, &tempMatrix);
349
350 /* Draw lower right part of the texture */
351 cairo_translate (ct2, 0, rect->Height);
352 cairo_set_source (ct2, pat);
353 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
354 cairo_fill (ct2);
355 cairo_destroy(ct2);
356
357 brush->pattern = cairo_pattern_create_for_surface (texture);
358 status = gdip_get_pattern_status(brush->pattern);
359 if (status != Ok) {
360 cairo_pattern_destroy (pat);
361 cairo_surface_destroy (texture);
362 return status;
363 }
364 cairo_pattern_set_extend (brush->pattern, CAIRO_EXTEND_REPEAT);
365
366 cairo_pattern_destroy (pat);
367 cairo_surface_destroy (texture);
368
369 return gdip_get_status (cairo_status (ct));
370 }
371
372 static GpStatus
draw_clamp_texture(cairo_t * ct,GpBitmap * bitmap,GpTexture * brush)373 draw_clamp_texture (cairo_t *ct, GpBitmap *bitmap, GpTexture *brush)
374 {
375 cairo_surface_t *texture;
376 cairo_pattern_t *pat;
377 GpRect *rect = &brush->rectangle;
378 GpStatus status;
379 cairo_t *ct2;
380
381 /* Original image surface */
382 if (gdip_bitmap_ensure_surface (bitmap) == NULL)
383 return OutOfMemory;
384
385 /* Use the original as a pattern */
386 pat = cairo_pattern_create_for_surface (bitmap->surface);
387 status = gdip_get_pattern_status (pat);
388 if (status != Ok) {
389 return status;
390 }
391
392 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
393
394 /* texture surface to be created */
395 texture = cairo_surface_create_similar (
396 bitmap->surface, from_cairoformat_to_content (bitmap->cairo_format),
397 rect->Width, rect->Height);
398 if (!texture) {
399 cairo_pattern_destroy (pat);
400 return OutOfMemory;
401 }
402
403 /* Draw the texture */
404 ct2 = cairo_create (texture);
405 cairo_identity_matrix (ct2);
406 cairo_set_source (ct2, pat);
407 cairo_rectangle (ct2, 0, 0, rect->Width, rect->Height);
408 cairo_fill (ct2);
409 cairo_destroy(ct2);
410
411 brush->pattern = cairo_pattern_create_for_surface (texture);
412 status = gdip_get_pattern_status (brush->pattern);
413 if (status != Ok) {
414 cairo_pattern_destroy (pat);
415 cairo_surface_destroy (texture);
416 return status;
417 }
418
419 cairo_pattern_destroy (pat);
420 cairo_surface_destroy (texture);
421
422 return gdip_get_status (cairo_status (ct));
423 }
424
425 GpStatus
gdip_texture_setup(GpGraphics * graphics,GpBrush * brush)426 gdip_texture_setup (GpGraphics *graphics, GpBrush *brush)
427 {
428 cairo_t *ct;
429 cairo_pattern_t *pattern;
430 GpTexture *texture;
431 GpImage *img;
432 GpStatus status = Ok;
433 BOOL dispose_bitmap;
434
435 if (!graphics || !brush || !graphics->ct)
436 return InvalidParameter;
437
438 texture = (GpTexture *) brush;
439 img = texture->image;
440 if (!img)
441 return InvalidParameter;
442
443 if (img->type != ImageTypeBitmap)
444 return NotImplemented;
445
446 if (gdip_is_an_indexed_pixelformat (img->active_bitmap->pixel_format)) {
447 /* Unable to create a surface for the bitmap; it is an indexed image.
448 * Instead, it will first be converted to 32-bit RGB. */
449 img = gdip_convert_indexed_to_rgb (img);
450 if (!img)
451 return OutOfMemory;
452 if (gdip_bitmap_ensure_surface (img) == NULL)
453 return OutOfMemory;
454 dispose_bitmap = TRUE;
455 } else {
456 dispose_bitmap = FALSE;
457 }
458
459 ct = graphics->ct;
460
461 /* We create the new pattern for brush, if the brush is changed
462 * or if pattern has not been created yet. */
463 if (texture->base.changed || !texture->pattern) {
464 if (texture->pattern)
465 cairo_pattern_destroy (texture->pattern);
466
467 switch (texture->wrapMode) {
468 case WrapModeTile:
469 status = draw_tile_texture (ct, img, texture);
470 break;
471
472 case WrapModeTileFlipX:
473 status = draw_tile_flipX_texture (ct, img, texture);
474 break;
475
476 case WrapModeTileFlipY:
477 status = draw_tile_flipY_texture (ct, img, texture);
478 break;
479
480 case WrapModeTileFlipXY:
481 status = draw_tile_flipXY_texture (ct, img, texture);
482 break;
483
484 case WrapModeClamp:
485 status = draw_clamp_texture (ct, img, texture);
486 break;
487
488 default:
489 status = InvalidParameter;
490 }
491 }
492
493 if (dispose_bitmap) {
494 GdipDisposeImage((GpImage *)img);
495 }
496
497 if ((status != Ok) || (gdip_get_pattern_status(texture->pattern) != Ok)) {
498 return GenericError;
499 }
500
501 pattern = texture->pattern;
502 if (pattern != NULL) {
503 /* got something to apply ? */
504 if (!gdip_is_matrix_empty (&texture->matrix)) {
505 cairo_matrix_t product;
506
507 gdip_cairo_matrix_copy (&product, &texture->matrix);
508 cairo_matrix_invert (&product);
509 cairo_pattern_set_matrix (pattern, &product);
510 }
511 cairo_set_source (ct, pattern);
512 }
513
514 return gdip_get_status (cairo_status (ct));
515 }
516
517 GpStatus
gdip_texture_clone(GpBrush * brush,GpBrush ** clonedBrush)518 gdip_texture_clone (GpBrush *brush, GpBrush **clonedBrush)
519 {
520 GpTexture *result;
521 GpTexture *texture;
522 GpStatus status;
523
524 if (!brush || !clonedBrush)
525 return InvalidParameter;
526
527 result = gdip_texture_new ();
528 if (!result)
529 return OutOfMemory;
530
531 texture = (GpTexture *) brush;
532 result->base = texture->base;
533 result->wrapMode = texture->wrapMode;
534
535 /* Let the clone create its own pattern. */
536 result->pattern = NULL;
537 result->base.changed = TRUE;
538
539 gdip_cairo_matrix_copy (&result->matrix, &texture->matrix);
540 result->rectangle.X = texture->rectangle.X;
541 result->rectangle.Y = texture->rectangle.Y;
542 result->rectangle.Width = texture->rectangle.Width;
543 result->rectangle.Height = texture->rectangle.Height;
544
545 result->image = NULL;
546 status = GdipCloneImage (texture->image, &result->image);
547 if (status != Ok) {
548 GdipDeleteBrush ((GpBrush *) result);
549 *clonedBrush = NULL;
550 return status;
551 } else {
552 cairo_surface_reference (result->image->surface);
553 }
554
555 *clonedBrush = (GpBrush *) result;
556 return status;
557 }
558
559 GpStatus
gdip_texture_destroy(GpBrush * brush)560 gdip_texture_destroy (GpBrush *brush)
561 {
562 /* a. the NULL check for brush is done by the caller, GdipDeleteBrush */
563 /* b. brush itself is freed by the caller */
564
565 GpTexture *texture = (GpTexture *) brush;
566
567 if (texture->pattern) {
568 cairo_pattern_destroy (texture->pattern);
569 texture->pattern = NULL;
570 }
571
572 if (texture->image) {
573 GdipDisposeImage (texture->image);
574 texture->image = NULL;
575 }
576
577 return Ok;
578 }
579
580 static GpStatus
gdip_texture_create_from_cloned_image(GpImage * image,GpWrapMode wrapMode,GpTexture ** texture)581 gdip_texture_create_from_cloned_image (GpImage *image, GpWrapMode wrapMode, GpTexture **texture)
582 {
583 GpTexture *result;
584
585 result = gdip_texture_new ();
586 if (!result)
587 return OutOfMemory;
588
589 result->image = image;
590 gdip_bitmap_ensure_surface (image);
591 result->wrapMode = wrapMode;
592 result->rectangle.X = 0;
593 result->rectangle.Y = 0;
594 result->rectangle.Width = result->image->active_bitmap->width;
595 result->rectangle.Height = result->image->active_bitmap->height;
596
597 *texture = result;
598 return Ok;
599 }
600
601 /* coverity[+alloc : arg-*2] */
602 GpStatus WINGDIPAPI
GdipCreateTexture(GpImage * image,GpWrapMode wrapmode,GpTexture ** texture)603 GdipCreateTexture (GpImage *image, GpWrapMode wrapmode, GpTexture **texture)
604 {
605 GpImage *textureImage;
606 GpStatus status;
607
608 if (!gdiplusInitialized)
609 return GdiplusNotInitialized;
610
611 if (!image || !texture)
612 return InvalidParameter;
613
614 if (wrapmode > WrapModeClamp) {
615 *texture = NULL;
616 return OutOfMemory;
617 }
618
619 switch (image->type) {
620 case ImageTypeBitmap:
621 status = GdipCloneImage (image, &textureImage);
622 if (status != Ok)
623 return status;
624 break;
625 case ImageTypeMetafile:
626 status = gdip_get_bitmap_from_metafile ((GpMetafile *) image, 0, 0, &textureImage);
627 if (status != Ok)
628 return status;
629 break;
630 default:
631 return GenericError;
632 }
633
634 return gdip_texture_create_from_cloned_image (textureImage, wrapmode, texture);
635 }
636
637 /* coverity[+alloc : arg-*6] */
638 GpStatus WINGDIPAPI
GdipCreateTexture2(GpImage * image,GpWrapMode wrapmode,REAL x,REAL y,REAL width,REAL height,GpTexture ** texture)639 GdipCreateTexture2 (GpImage *image, GpWrapMode wrapmode, REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
640 {
641 if (!gdiplusInitialized)
642 return GdiplusNotInitialized;
643
644 return GdipCreateTexture2I (image, wrapmode, (INT) x, (INT) y, (INT) width, (INT) height, texture);
645 }
646
647 /* coverity[+alloc : arg-*6] */
648 GpStatus WINGDIPAPI
GdipCreateTexture2I(GpImage * image,GpWrapMode wrapmode,INT x,INT y,INT width,INT height,GpTexture ** texture)649 GdipCreateTexture2I (GpImage *image, GpWrapMode wrapmode, INT x, INT y, INT width, INT height, GpTexture **texture)
650 {
651 GpImage *textureImage;
652 GpStatus status;
653
654 if (!gdiplusInitialized)
655 return GdiplusNotInitialized;
656
657 if (!image || !texture)
658 return InvalidParameter;
659
660 if (wrapmode > WrapModeClamp) {
661 *texture = NULL;
662 return OutOfMemory;
663 }
664
665 switch (image->type) {
666 case ImageTypeBitmap: {
667 INT bmpWidth = image->active_bitmap->width;
668 INT bmpHeight = image->active_bitmap->height;
669 if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || (bmpWidth < (x + width)) || (bmpHeight < (y + height))) {
670 *texture = NULL;
671 return OutOfMemory;
672 }
673
674 status = GdipCloneBitmapAreaI (x, y, width, height, image->active_bitmap->pixel_format, image, &textureImage);
675 if (status != Ok)
676 return status;
677 break;
678 }
679 case ImageTypeMetafile:
680 status = gdip_get_bitmap_from_metafile ((GpMetafile *) image, width, height, &textureImage);
681 if (status != Ok)
682 return status;
683 break;
684 default:
685 return GenericError;
686 }
687
688 return gdip_texture_create_from_cloned_image (textureImage, wrapmode, texture);
689 }
690
691 /* coverity[+alloc : arg-*6] */
692 GpStatus WINGDIPAPI
GdipCreateTextureIA(GpImage * image,GpImageAttributes * imageAttributes,REAL x,REAL y,REAL width,REAL height,GpTexture ** texture)693 GdipCreateTextureIA (GpImage *image, GpImageAttributes *imageAttributes, REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
694 {
695 if (!gdiplusInitialized)
696 return GdiplusNotInitialized;
697
698 return GdipCreateTextureIAI (image, imageAttributes, (INT) x, (INT) y, (INT) width, (INT) height, texture);
699 }
700
701 /* coverity[+alloc : arg-*6] */
702 GpStatus WINGDIPAPI
GdipCreateTextureIAI(GpImage * image,GpImageAttributes * imageAttributes,INT x,INT y,INT width,INT height,GpTexture ** texture)703 GdipCreateTextureIAI (GpImage *image, GpImageAttributes *imageAttributes, INT x, INT y, INT width, INT height, GpTexture **texture)
704 {
705 if (!gdiplusInitialized)
706 return GdiplusNotInitialized;
707
708 /* FIXME MonoTODO: Make use of ImageAttributes parameter when
709 * ImageAttributes is implemented */
710 GpWrapMode mode = imageAttributes ? imageAttributes->wrapmode : WrapModeTile;
711 return GdipCreateTexture2I (image, mode, x, y, width, height, texture);
712 }
713
714 GpStatus WINGDIPAPI
GdipGetTextureTransform(GpTexture * texture,GpMatrix * matrix)715 GdipGetTextureTransform (GpTexture *texture, GpMatrix *matrix)
716 {
717 if (!texture || !matrix)
718 return InvalidParameter;
719
720 gdip_cairo_matrix_copy (matrix, &texture->matrix);
721 return Ok;
722 }
723
724 GpStatus WINGDIPAPI
GdipSetTextureTransform(GpTexture * texture,GDIPCONST GpMatrix * matrix)725 GdipSetTextureTransform (GpTexture *texture, GDIPCONST GpMatrix *matrix)
726 {
727 BOOL invertible;
728
729 if (!texture || !matrix)
730 return InvalidParameter;
731
732 GdipIsMatrixInvertible ((GpMatrix *) matrix, &invertible);
733 if (!invertible)
734 return InvalidParameter;
735
736 gdip_cairo_matrix_copy (&texture->matrix, matrix);
737 texture->base.changed = TRUE;
738
739 return Ok;
740 }
741
742 GpStatus WINGDIPAPI
GdipResetTextureTransform(GpTexture * texture)743 GdipResetTextureTransform (GpTexture *texture)
744 {
745 if (!texture)
746 return InvalidParameter;
747
748 cairo_matrix_init_identity (&texture->matrix);
749 texture->base.changed = TRUE;
750 return Ok;
751 }
752
753 GpStatus WINGDIPAPI
GdipMultiplyTextureTransform(GpTexture * texture,GpMatrix * matrix,GpMatrixOrder order)754 GdipMultiplyTextureTransform (GpTexture *texture, GpMatrix *matrix, GpMatrixOrder order)
755 {
756 BOOL invertible;
757
758 if (!texture)
759 return InvalidParameter;
760
761 if (!matrix)
762 return Ok;
763
764 /* the matrix MUST be invertible to be used */
765 GdipIsMatrixInvertible (matrix, &invertible);
766 if (!invertible)
767 return InvalidParameter;
768
769 if (order == MatrixOrderPrepend)
770 cairo_matrix_multiply (&texture->matrix, matrix, &texture->matrix);
771 else
772 cairo_matrix_multiply (&texture->matrix, &texture->matrix, matrix);
773
774 texture->base.changed = TRUE;
775 return Ok;
776 }
777
778 GpStatus WINGDIPAPI
GdipTranslateTextureTransform(GpTexture * texture,REAL dx,REAL dy,GpMatrixOrder order)779 GdipTranslateTextureTransform (GpTexture *texture, REAL dx, REAL dy, GpMatrixOrder order)
780 {
781 GpStatus status;
782
783 if (!texture)
784 return InvalidParameter;
785
786 status = GdipTranslateMatrix (&texture->matrix, dx, dy, order);
787 if (status != Ok)
788 return status;
789
790 texture->base.changed = TRUE;
791 return Ok;
792 }
793
794 GpStatus WINGDIPAPI
GdipScaleTextureTransform(GpTexture * texture,REAL sx,REAL sy,GpMatrixOrder order)795 GdipScaleTextureTransform (GpTexture *texture, REAL sx, REAL sy, GpMatrixOrder order)
796 {
797 GpStatus status;
798
799 if (!texture)
800 return InvalidParameter;
801
802 status = GdipScaleMatrix (&texture->matrix, sx, sy, order);
803 if (status != Ok)
804 return status;
805
806 texture->base.changed = TRUE;
807 return Ok;
808 }
809
810 GpStatus WINGDIPAPI
GdipRotateTextureTransform(GpTexture * texture,REAL angle,GpMatrixOrder order)811 GdipRotateTextureTransform (GpTexture *texture, REAL angle, GpMatrixOrder order)
812 {
813 GpStatus status;
814
815 if (!texture)
816 return InvalidParameter;
817
818 status = GdipRotateMatrix (&texture->matrix, angle, order);
819 if (status != Ok)
820 return status;
821
822 texture->base.changed = TRUE;
823 return Ok;
824 }
825
826 GpStatus WINGDIPAPI
GdipSetTextureWrapMode(GpTexture * texture,GpWrapMode wrapMode)827 GdipSetTextureWrapMode (GpTexture *texture, GpWrapMode wrapMode)
828 {
829 if (!texture)
830 return InvalidParameter;
831
832 /* ignore invalid GpWrapMode value */
833 if (wrapMode > WrapModeClamp)
834 return Ok;
835
836 texture->wrapMode = wrapMode;
837 texture->base.changed = TRUE;
838
839 return Ok;
840 }
841
842 GpStatus WINGDIPAPI
GdipGetTextureWrapMode(GpTexture * texture,GpWrapMode * wrapMode)843 GdipGetTextureWrapMode (GpTexture *texture, GpWrapMode *wrapMode)
844 {
845 if (!texture || !wrapMode)
846 return InvalidParameter;
847
848 *wrapMode = texture->wrapMode;
849 return Ok;
850 }
851
852 /* coverity[+alloc : arg-*1] */
853 GpStatus WINGDIPAPI
GdipGetTextureImage(GpTexture * texture,GpImage ** image)854 GdipGetTextureImage (GpTexture *texture, GpImage **image)
855 {
856 if (!gdiplusInitialized)
857 return GdiplusNotInitialized;
858
859 if (!texture || !image)
860 return InvalidParameter;
861
862 return GdipCloneImage (texture->image, image);
863 }
864