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