d3drm: Fix leakage of pData2 on error.
[wine] / dlls / gdiplus / brush.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "wingdi.h"
25
26 #define COBJMACROS
27 #include "objbase.h"
28 #include "olectl.h"
29 #include "ole2.h"
30
31 #include "gdiplus.h"
32 #include "gdiplus_private.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
36
37 /******************************************************************************
38  * GdipCloneBrush [GDIPLUS.@]
39  */
40 GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
41 {
42     TRACE("(%p, %p)\n", brush, clone);
43
44     if(!brush || !clone)
45         return InvalidParameter;
46
47     switch(brush->bt){
48         case BrushTypeSolidColor:
49         {
50             *clone = GdipAlloc(sizeof(GpSolidFill));
51             if (!*clone) return OutOfMemory;
52             memcpy(*clone, brush, sizeof(GpSolidFill));
53             break;
54         }
55         case BrushTypeHatchFill:
56         {
57             GpHatch *hatch = (GpHatch*)brush;
58
59             return GdipCreateHatchBrush(hatch->hatchstyle, hatch->forecol, hatch->backcol, (GpHatch**)clone);
60         }
61         case BrushTypePathGradient:{
62             GpPathGradient *src, *dest;
63             INT count;
64             GpStatus stat;
65
66             *clone = GdipAlloc(sizeof(GpPathGradient));
67             if (!*clone) return OutOfMemory;
68
69             src = (GpPathGradient*) brush,
70             dest = (GpPathGradient*) *clone;
71
72             memcpy(dest, src, sizeof(GpPathGradient));
73
74             stat = GdipClonePath(src->path, &dest->path);
75
76             if(stat != Ok){
77                 GdipFree(dest);
78                 return stat;
79             }
80
81             /* blending */
82             count = src->blendcount;
83             dest->blendcount = count;
84             dest->blendfac = GdipAlloc(count * sizeof(REAL));
85             dest->blendpos = GdipAlloc(count * sizeof(REAL));
86
87             if(!dest->blendfac || !dest->blendpos){
88                 GdipDeletePath(dest->path);
89                 GdipFree(dest->blendfac);
90                 GdipFree(dest->blendpos);
91                 GdipFree(dest);
92                 return OutOfMemory;
93             }
94
95             memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
96             memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
97
98             break;
99         }
100         case BrushTypeLinearGradient:{
101             GpLineGradient *dest, *src;
102             INT count, pcount;
103
104             dest = GdipAlloc(sizeof(GpLineGradient));
105             if(!dest)    return OutOfMemory;
106
107             src = (GpLineGradient*)brush;
108
109             memcpy(dest, src, sizeof(GpLineGradient));
110
111             count = dest->blendcount;
112             dest->blendfac = GdipAlloc(count * sizeof(REAL));
113             dest->blendpos = GdipAlloc(count * sizeof(REAL));
114             pcount = dest->pblendcount;
115             if (pcount)
116             {
117                 dest->pblendcolor = GdipAlloc(pcount * sizeof(ARGB));
118                 dest->pblendpos = GdipAlloc(pcount * sizeof(REAL));
119             }
120
121             if (!dest->blendfac || !dest->blendpos ||
122                 (pcount && (!dest->pblendcolor || !dest->pblendpos)))
123             {
124                 GdipFree(dest->blendfac);
125                 GdipFree(dest->blendpos);
126                 GdipFree(dest->pblendcolor);
127                 GdipFree(dest->pblendpos);
128                 GdipFree(dest);
129                 return OutOfMemory;
130             }
131
132             memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
133             memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
134
135             if (pcount)
136             {
137                 memcpy(dest->pblendcolor, src->pblendcolor, pcount * sizeof(ARGB));
138                 memcpy(dest->pblendpos, src->pblendpos, pcount * sizeof(REAL));
139             }
140
141             *clone = &dest->brush;
142             break;
143         }
144         case BrushTypeTextureFill:
145         {
146             GpStatus stat;
147             GpTexture *texture = (GpTexture*)brush;
148             GpTexture *new_texture;
149             UINT width, height;
150
151             stat = GdipGetImageWidth(texture->image, &width);
152             if (stat != Ok) return stat;
153             stat = GdipGetImageHeight(texture->image, &height);
154             if (stat != Ok) return stat;
155
156             stat = GdipCreateTextureIA(texture->image, texture->imageattributes, 0, 0, width, height, &new_texture);
157
158             if (stat == Ok)
159             {
160                 memcpy(new_texture->transform, texture->transform, sizeof(GpMatrix));
161                 *clone = (GpBrush*)new_texture;
162             }
163             else
164                 *clone = NULL;
165
166             return stat;
167         }
168         default:
169             ERR("not implemented for brush type %d\n", brush->bt);
170             return NotImplemented;
171     }
172
173     TRACE("<-- %p\n", *clone);
174     return Ok;
175 }
176
177 static const char HatchBrushes[][8] = {
178     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HatchStyleHorizontal */
179     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleVertical */
180     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */
181     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */
182     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleCross */
183     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */
184     { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */
185     { 0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88 }, /* HatchStyle10Percent */
186     { 0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc }, /* HatchStyle20Percent */
187     { 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc }, /* HatchStyle25Percent */
188     { 0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc }, /* HatchStyle30Percent */
189     { 0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc }, /* HatchStyle40Percent */
190     { 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc }, /* HatchStyle50Percent */
191     { 0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee }, /* HatchStyle60Percent */
192     { 0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff }, /* HatchStyle70Percent */
193     { 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff }, /* HatchStyle75Percent */
194     { 0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff }, /* HatchStyle80Percent */
195     { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff }, /* HatchStyle90Percent */
196     { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, /* HatchStyleLightDownwardDiagonal */
197     { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, /* HatchStyleLightUpwardDiagonal */
198     { 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc }, /* HatchStyleDarkDownwardDiagonal */
199     { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 }, /* HatchStyleDarkUpwardDiagonal */
200     { 0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0 }, /* HatchStyleWideDownwardDiagonal */
201     { 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1 }, /* HatchStyleWideUpwardDiagonal */
202     { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, /* HatchStyleLightVertical */
203     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleLightHorizontal */
204     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, /* HatchStyleNarrowVertical */
205     { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */
206     { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */
207     { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */
208 };
209
210 GpStatus get_hatch_data(HatchStyle hatchstyle, const char **result)
211 {
212     if (hatchstyle < sizeof(HatchBrushes) / sizeof(HatchBrushes[0]))
213     {
214         *result = HatchBrushes[hatchstyle];
215         return Ok;
216     }
217     else
218         return NotImplemented;
219 }
220
221 /******************************************************************************
222  * GdipCreateHatchBrush [GDIPLUS.@]
223  */
224 GpStatus WINGDIPAPI GdipCreateHatchBrush(HatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
225 {
226     TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
227
228     if(!brush)  return InvalidParameter;
229
230     *brush = GdipAlloc(sizeof(GpHatch));
231     if (!*brush) return OutOfMemory;
232
233     (*brush)->brush.bt = BrushTypeHatchFill;
234     (*brush)->forecol = forecol;
235     (*brush)->backcol = backcol;
236     (*brush)->hatchstyle = hatchstyle;
237     TRACE("<-- %p\n", *brush);
238
239     return Ok;
240 }
241
242 /******************************************************************************
243  * GdipCreateLineBrush [GDIPLUS.@]
244  */
245 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
246     GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
247     GpWrapMode wrap, GpLineGradient **line)
248 {
249     TRACE("(%s, %s, %x, %x, %d, %p)\n", debugstr_pointf(startpoint),
250           debugstr_pointf(endpoint), startcolor, endcolor, wrap, line);
251
252     if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
253         return InvalidParameter;
254
255     if (startpoint->X == endpoint->X && startpoint->Y == endpoint->Y)
256         return OutOfMemory;
257
258     *line = GdipAlloc(sizeof(GpLineGradient));
259     if(!*line)  return OutOfMemory;
260
261     (*line)->brush.bt = BrushTypeLinearGradient;
262
263     (*line)->startpoint.X = startpoint->X;
264     (*line)->startpoint.Y = startpoint->Y;
265     (*line)->endpoint.X = endpoint->X;
266     (*line)->endpoint.Y = endpoint->Y;
267     (*line)->startcolor = startcolor;
268     (*line)->endcolor = endcolor;
269     (*line)->wrap = wrap;
270     (*line)->gamma = FALSE;
271
272     (*line)->rect.X = (startpoint->X < endpoint->X ? startpoint->X: endpoint->X);
273     (*line)->rect.Y = (startpoint->Y < endpoint->Y ? startpoint->Y: endpoint->Y);
274     (*line)->rect.Width  = fabs(startpoint->X - endpoint->X);
275     (*line)->rect.Height = fabs(startpoint->Y - endpoint->Y);
276
277     if ((*line)->rect.Width == 0)
278     {
279         (*line)->rect.X -= (*line)->rect.Height / 2.0f;
280         (*line)->rect.Width = (*line)->rect.Height;
281     }
282     else if ((*line)->rect.Height == 0)
283     {
284         (*line)->rect.Y -= (*line)->rect.Width / 2.0f;
285         (*line)->rect.Height = (*line)->rect.Width;
286     }
287
288     (*line)->blendcount = 1;
289     (*line)->blendfac = GdipAlloc(sizeof(REAL));
290     (*line)->blendpos = GdipAlloc(sizeof(REAL));
291
292     if (!(*line)->blendfac || !(*line)->blendpos)
293     {
294         GdipFree((*line)->blendfac);
295         GdipFree((*line)->blendpos);
296         GdipFree(*line);
297         *line = NULL;
298         return OutOfMemory;
299     }
300
301     (*line)->blendfac[0] = 1.0f;
302     (*line)->blendpos[0] = 1.0f;
303
304     (*line)->pblendcolor = NULL;
305     (*line)->pblendpos = NULL;
306     (*line)->pblendcount = 0;
307
308     TRACE("<-- %p\n", *line);
309
310     return Ok;
311 }
312
313 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
314     GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
315     GpWrapMode wrap, GpLineGradient **line)
316 {
317     GpPointF stF;
318     GpPointF endF;
319
320     TRACE("(%p, %p, %x, %x, %d, %p)\n", startpoint, endpoint,
321           startcolor, endcolor, wrap, line);
322
323     if(!startpoint || !endpoint)
324         return InvalidParameter;
325
326     stF.X  = (REAL)startpoint->X;
327     stF.Y  = (REAL)startpoint->Y;
328     endF.X = (REAL)endpoint->X;
329     endF.Y = (REAL)endpoint->Y;
330
331     return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
332 }
333
334 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
335     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
336     GpLineGradient **line)
337 {
338     GpPointF start, end;
339     GpStatus stat;
340
341     TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
342           wrap, line);
343
344     if(!line || !rect)
345         return InvalidParameter;
346
347     switch (mode)
348     {
349     case LinearGradientModeHorizontal:
350         start.X = rect->X;
351         start.Y = rect->Y;
352         end.X = rect->X + rect->Width;
353         end.Y = rect->Y;
354         break;
355     case LinearGradientModeVertical:
356         start.X = rect->X;
357         start.Y = rect->Y;
358         end.X = rect->X;
359         end.Y = rect->Y + rect->Height;
360         break;
361     case LinearGradientModeForwardDiagonal:
362         start.X = rect->X;
363         start.Y = rect->Y;
364         end.X = rect->X + rect->Width;
365         end.Y = rect->Y + rect->Height;
366         break;
367     case LinearGradientModeBackwardDiagonal:
368         start.X = rect->X + rect->Width;
369         start.Y = rect->Y;
370         end.X = rect->X;
371         end.Y = rect->Y + rect->Height;
372         break;
373     default:
374         return InvalidParameter;
375     }
376
377     stat = GdipCreateLineBrush(&start, &end, startcolor, endcolor, wrap, line);
378
379     if (stat == Ok)
380         (*line)->rect = *rect;
381
382     return stat;
383 }
384
385 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
386     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
387     GpLineGradient **line)
388 {
389     GpRectF rectF;
390
391     TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
392           wrap, line);
393
394     rectF.X      = (REAL) rect->X;
395     rectF.Y      = (REAL) rect->Y;
396     rectF.Width  = (REAL) rect->Width;
397     rectF.Height = (REAL) rect->Height;
398
399     return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
400 }
401
402 /******************************************************************************
403  * GdipCreateLineBrushFromRectWithAngle [GDIPLUS.@]
404  */
405 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect,
406     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
407     GpLineGradient **line)
408 {
409     GpStatus stat;
410     LinearGradientMode mode;
411     REAL width, height, exofs, eyofs;
412     REAL sin_angle, cos_angle, sin_cos_angle;
413
414     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
415           wrap, line);
416
417     sin_angle = sinf(deg2rad(angle));
418     cos_angle = cosf(deg2rad(angle));
419     sin_cos_angle = sin_angle * cos_angle;
420
421     if (isAngleScalable)
422     {
423         width = height = 1.0;
424     }
425     else
426     {
427         width = rect->Width;
428         height = rect->Height;
429     }
430
431     if (sin_cos_angle >= 0)
432         mode = LinearGradientModeForwardDiagonal;
433     else
434         mode = LinearGradientModeBackwardDiagonal;
435
436     stat = GdipCreateLineBrushFromRect(rect, startcolor, endcolor, mode, wrap, line);
437
438     if (stat == Ok)
439     {
440         if (sin_cos_angle >= 0)
441         {
442             exofs = width * sin_cos_angle + height * cos_angle * cos_angle;
443             eyofs = width * sin_angle * sin_angle + height * sin_cos_angle;
444         }
445         else
446         {
447             exofs = width * sin_angle * sin_angle + height * sin_cos_angle;
448             eyofs = -width * sin_cos_angle + height * sin_angle * sin_angle;
449         }
450
451         if (isAngleScalable)
452         {
453             exofs = exofs * rect->Width;
454             eyofs = eyofs * rect->Height;
455         }
456
457         if (sin_angle >= 0)
458         {
459             (*line)->endpoint.X = rect->X + exofs;
460             (*line)->endpoint.Y = rect->Y + eyofs;
461         }
462         else
463         {
464             (*line)->endpoint.X = (*line)->startpoint.X;
465             (*line)->endpoint.Y = (*line)->startpoint.Y;
466             (*line)->startpoint.X = rect->X + exofs;
467             (*line)->startpoint.Y = rect->Y + eyofs;
468         }
469     }
470
471     return stat;
472 }
473
474 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect,
475     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
476     GpLineGradient **line)
477 {
478     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
479           wrap, line);
480
481     return GdipCreateLineBrushFromRectI(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
482                                         wrap, line);
483 }
484
485 static GpStatus create_path_gradient(GpPath *path, GpPathGradient **grad)
486 {
487     GpRectF bounds;
488
489     if(!path || !grad)
490         return InvalidParameter;
491
492     GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
493
494     *grad = GdipAlloc(sizeof(GpPathGradient));
495     if (!*grad)
496     {
497         return OutOfMemory;
498     }
499
500     (*grad)->blendfac = GdipAlloc(sizeof(REAL));
501     (*grad)->blendpos = GdipAlloc(sizeof(REAL));
502     if(!(*grad)->blendfac || !(*grad)->blendpos){
503         GdipFree((*grad)->blendfac);
504         GdipFree((*grad)->blendpos);
505         GdipFree(*grad);
506         *grad = NULL;
507         return OutOfMemory;
508     }
509     (*grad)->blendfac[0] = 1.0;
510     (*grad)->blendpos[0] = 1.0;
511     (*grad)->blendcount  = 1;
512
513     (*grad)->path = path;
514
515     (*grad)->brush.bt = BrushTypePathGradient;
516     (*grad)->centercolor = 0xffffffff;
517     (*grad)->wrap = WrapModeClamp;
518     (*grad)->gamma = FALSE;
519     /* FIXME: this should be set to the "centroid" of the path by default */
520     (*grad)->center.X = bounds.X + bounds.Width / 2;
521     (*grad)->center.Y = bounds.Y + bounds.Height / 2;
522     (*grad)->focus.X = 0.0;
523     (*grad)->focus.Y = 0.0;
524
525     TRACE("<-- %p\n", *grad);
526
527     return Ok;
528 }
529
530 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
531     INT count, GpWrapMode wrap, GpPathGradient **grad)
532 {
533     GpStatus stat;
534     GpPath *path;
535
536     TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
537
538     if(!points || !grad)
539         return InvalidParameter;
540
541     if(count <= 0)
542         return OutOfMemory;
543
544     stat = GdipCreatePath(FillModeAlternate, &path);
545
546     if (stat == Ok)
547     {
548         stat = GdipAddPathLine2(path, points, count);
549
550         if (stat == Ok)
551             stat = create_path_gradient(path, grad);
552
553         if (stat != Ok)
554             GdipDeletePath(path);
555     }
556
557     return stat;
558 }
559
560 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
561     INT count, GpWrapMode wrap, GpPathGradient **grad)
562 {
563     GpStatus stat;
564     GpPath *path;
565
566     TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
567
568     if(!points || !grad)
569         return InvalidParameter;
570
571     if(count <= 0)
572         return OutOfMemory;
573
574     stat = GdipCreatePath(FillModeAlternate, &path);
575
576     if (stat == Ok)
577     {
578         stat = GdipAddPathLine2I(path, points, count);
579
580         if (stat == Ok)
581             stat = create_path_gradient(path, grad);
582
583         if (stat != Ok)
584             GdipDeletePath(path);
585     }
586
587     return stat;
588 }
589
590 /******************************************************************************
591  * GdipCreatePathGradientFromPath [GDIPLUS.@]
592  */
593 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
594     GpPathGradient **grad)
595 {
596     GpStatus stat;
597     GpPath *new_path;
598
599     TRACE("(%p, %p)\n", path, grad);
600
601     if(!path || !grad)
602         return InvalidParameter;
603
604     stat = GdipClonePath((GpPath*)path, &new_path);
605
606     if (stat == Ok)
607     {
608         stat = create_path_gradient(new_path, grad);
609
610         if (stat != Ok)
611             GdipDeletePath(new_path);
612     }
613
614     return stat;
615 }
616
617 /******************************************************************************
618  * GdipCreateSolidFill [GDIPLUS.@]
619  */
620 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
621 {
622     TRACE("(%x, %p)\n", color, sf);
623
624     if(!sf)  return InvalidParameter;
625
626     *sf = GdipAlloc(sizeof(GpSolidFill));
627     if (!*sf) return OutOfMemory;
628
629     (*sf)->brush.bt = BrushTypeSolidColor;
630     (*sf)->color = color;
631
632     TRACE("<-- %p\n", *sf);
633
634     return Ok;
635 }
636
637 /******************************************************************************
638  * GdipCreateTexture [GDIPLUS.@]
639  *
640  * PARAMS
641  *  image       [I] image to use
642  *  wrapmode    [I] optional
643  *  texture     [O] pointer to the resulting texturebrush
644  *
645  * RETURNS
646  *  SUCCESS: Ok
647  *  FAILURE: element of GpStatus
648  */
649 GpStatus WINGDIPAPI GdipCreateTexture(GpImage *image, GpWrapMode wrapmode,
650         GpTexture **texture)
651 {
652     UINT width, height;
653     GpImageAttributes *attributes;
654     GpStatus stat;
655
656     TRACE("%p, %d %p\n", image, wrapmode, texture);
657
658     if (!(image && texture))
659         return InvalidParameter;
660
661     stat = GdipGetImageWidth(image, &width);
662     if (stat != Ok) return stat;
663     stat = GdipGetImageHeight(image, &height);
664     if (stat != Ok) return stat;
665
666     stat = GdipCreateImageAttributes(&attributes);
667
668     if (stat == Ok)
669     {
670         attributes->wrap = wrapmode;
671
672         stat = GdipCreateTextureIA(image, attributes, 0, 0, width, height,
673             texture);
674
675         GdipDisposeImageAttributes(attributes);
676     }
677
678     return stat;
679 }
680
681 /******************************************************************************
682  * GdipCreateTexture2 [GDIPLUS.@]
683  */
684 GpStatus WINGDIPAPI GdipCreateTexture2(GpImage *image, GpWrapMode wrapmode,
685         REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
686 {
687     GpImageAttributes *attributes;
688     GpStatus stat;
689
690     TRACE("%p %d %f %f %f %f %p\n", image, wrapmode,
691             x, y, width, height, texture);
692
693     stat = GdipCreateImageAttributes(&attributes);
694
695     if (stat == Ok)
696     {
697         attributes->wrap = wrapmode;
698
699         stat = GdipCreateTextureIA(image, attributes, x, y, width, height,
700                 texture);
701
702         GdipDisposeImageAttributes(attributes);
703     }
704
705     return stat;
706 }
707
708 /******************************************************************************
709  * GdipCreateTextureIA [GDIPLUS.@]
710  *
711  * FIXME: imageattr ignored
712  */
713 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
714     GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
715     REAL height, GpTexture **texture)
716 {
717     GpStatus status;
718     GpImage *new_image=NULL;
719
720     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %p)\n", image, imageattr, x, y, width, height,
721            texture);
722
723     if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
724         return InvalidParameter;
725
726     *texture = NULL;
727
728     if(image->type != ImageTypeBitmap){
729         FIXME("not implemented for image type %d\n", image->type);
730         return NotImplemented;
731     }
732
733     status = GdipCloneBitmapArea(x, y, width, height, PixelFormatDontCare, (GpBitmap*)image, (GpBitmap**)&new_image);
734     if (status != Ok)
735         return status;
736
737     *texture = GdipAlloc(sizeof(GpTexture));
738     if (!*texture){
739         status = OutOfMemory;
740         goto exit;
741     }
742
743     if((status = GdipCreateMatrix(&(*texture)->transform)) != Ok){
744         goto exit;
745     }
746
747     if (imageattr)
748     {
749         status = GdipCloneImageAttributes(imageattr, &(*texture)->imageattributes);
750     }
751     else
752     {
753         status = GdipCreateImageAttributes(&(*texture)->imageattributes);
754         if (status == Ok)
755             (*texture)->imageattributes->wrap = WrapModeTile;
756     }
757     if (status == Ok)
758     {
759         (*texture)->brush.bt = BrushTypeTextureFill;
760         (*texture)->image = new_image;
761     }
762
763 exit:
764     if (status == Ok)
765     {
766         TRACE("<-- %p\n", *texture);
767     }
768     else
769     {
770         if (*texture)
771         {
772             GdipDeleteMatrix((*texture)->transform);
773             GdipDisposeImageAttributes((*texture)->imageattributes);
774             GdipFree(*texture);
775             *texture = NULL;
776         }
777         GdipDisposeImage(new_image);
778         TRACE("<-- error %u\n", status);
779     }
780
781     return status;
782 }
783
784 /******************************************************************************
785  * GdipCreateTextureIAI [GDIPLUS.@]
786  */
787 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
788     INT x, INT y, INT width, INT height, GpTexture **texture)
789 {
790     TRACE("(%p, %p, %d, %d, %d, %d, %p)\n", image, imageattr, x, y, width, height,
791            texture);
792
793     return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
794 }
795
796 GpStatus WINGDIPAPI GdipCreateTexture2I(GpImage *image, GpWrapMode wrapmode,
797         INT x, INT y, INT width, INT height, GpTexture **texture)
798 {
799     GpImageAttributes *imageattr;
800     GpStatus stat;
801
802     TRACE("%p %d %d %d %d %d %p\n", image, wrapmode, x, y, width, height,
803             texture);
804
805     stat = GdipCreateImageAttributes(&imageattr);
806
807     if (stat == Ok)
808     {
809         imageattr->wrap = wrapmode;
810
811         stat = GdipCreateTextureIA(image, imageattr, x, y, width, height, texture);
812     }
813
814     return stat;
815 }
816
817 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
818 {
819     TRACE("(%p, %p)\n", brush, type);
820
821     if(!brush || !type)  return InvalidParameter;
822
823     *type = brush->bt;
824
825     return Ok;
826 }
827
828 GpStatus WINGDIPAPI GdipGetHatchBackgroundColor(GpHatch *brush, ARGB *backcol)
829 {
830     TRACE("(%p, %p)\n", brush, backcol);
831
832     if(!brush || !backcol)  return InvalidParameter;
833
834     *backcol = brush->backcol;
835
836     return Ok;
837 }
838
839 GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
840 {
841     TRACE("(%p, %p)\n", brush, forecol);
842
843     if(!brush || !forecol)  return InvalidParameter;
844
845     *forecol = brush->forecol;
846
847     return Ok;
848 }
849
850 GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, HatchStyle *hatchstyle)
851 {
852     TRACE("(%p, %p)\n", brush, hatchstyle);
853
854     if(!brush || !hatchstyle)  return InvalidParameter;
855
856     *hatchstyle = brush->hatchstyle;
857
858     return Ok;
859 }
860
861 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
862 {
863     TRACE("(%p)\n", brush);
864
865     if(!brush)  return InvalidParameter;
866
867     switch(brush->bt)
868     {
869         case BrushTypePathGradient:
870             GdipDeletePath(((GpPathGradient*) brush)->path);
871             GdipFree(((GpPathGradient*) brush)->blendfac);
872             GdipFree(((GpPathGradient*) brush)->blendpos);
873             break;
874         case BrushTypeLinearGradient:
875             GdipFree(((GpLineGradient*)brush)->blendfac);
876             GdipFree(((GpLineGradient*)brush)->blendpos);
877             GdipFree(((GpLineGradient*)brush)->pblendcolor);
878             GdipFree(((GpLineGradient*)brush)->pblendpos);
879             break;
880         case BrushTypeTextureFill:
881             GdipDeleteMatrix(((GpTexture*)brush)->transform);
882             GdipDisposeImage(((GpTexture*)brush)->image);
883             GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes);
884             GdipFree(((GpTexture*)brush)->bitmap_bits);
885             break;
886         default:
887             break;
888     }
889
890     GdipFree(brush);
891
892     return Ok;
893 }
894
895 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
896     BOOL *usinggamma)
897 {
898     TRACE("(%p, %p)\n", line, usinggamma);
899
900     if(!line || !usinggamma)
901         return InvalidParameter;
902
903     *usinggamma = line->gamma;
904
905     return Ok;
906 }
907
908 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
909 {
910     TRACE("(%p, %p)\n", brush, wrapmode);
911
912     if(!brush || !wrapmode)
913         return InvalidParameter;
914
915     *wrapmode = brush->wrap;
916
917     return Ok;
918 }
919
920 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
921     REAL *positions, INT count)
922 {
923     TRACE("(%p, %p, %p, %d)\n", brush, blend, positions, count);
924
925     if(!brush || !blend || !positions || count <= 0)
926         return InvalidParameter;
927
928     if(count < brush->blendcount)
929         return InsufficientBuffer;
930
931     memcpy(blend, brush->blendfac, count*sizeof(REAL));
932     if(brush->blendcount > 1){
933         memcpy(positions, brush->blendpos, count*sizeof(REAL));
934     }
935
936     return Ok;
937 }
938
939 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
940 {
941     TRACE("(%p, %p)\n", brush, count);
942
943     if(!brush || !count)
944         return InvalidParameter;
945
946     *count = brush->blendcount;
947
948     return Ok;
949 }
950
951 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
952     GpPointF *point)
953 {
954     TRACE("(%p, %p)\n", grad, point);
955
956     if(!grad || !point)
957         return InvalidParameter;
958
959     point->X = grad->center.X;
960     point->Y = grad->center.Y;
961
962     return Ok;
963 }
964
965 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
966     GpPoint *point)
967 {
968     GpStatus ret;
969     GpPointF ptf;
970
971     TRACE("(%p, %p)\n", grad, point);
972
973     if(!point)
974         return InvalidParameter;
975
976     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
977
978     if(ret == Ok){
979         point->X = roundr(ptf.X);
980         point->Y = roundr(ptf.Y);
981     }
982
983     return ret;
984 }
985
986 GpStatus WINGDIPAPI GdipGetPathGradientCenterColor(GpPathGradient *grad,
987     ARGB *colors)
988 {
989     static int calls;
990
991     TRACE("(%p,%p)\n", grad, colors);
992
993     if(!(calls++))
994         FIXME("not implemented\n");
995
996     return NotImplemented;
997 }
998
999 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
1000     REAL *x, REAL *y)
1001 {
1002     TRACE("(%p, %p, %p)\n", grad, x, y);
1003
1004     if(!grad || !x || !y)
1005         return InvalidParameter;
1006
1007     *x = grad->focus.X;
1008     *y = grad->focus.Y;
1009
1010     return Ok;
1011 }
1012
1013 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
1014     BOOL *gamma)
1015 {
1016     TRACE("(%p, %p)\n", grad, gamma);
1017
1018     if(!grad || !gamma)
1019         return InvalidParameter;
1020
1021     *gamma = grad->gamma;
1022
1023     return Ok;
1024 }
1025
1026 GpStatus WINGDIPAPI GdipGetPathGradientPath(GpPathGradient *grad, GpPath *path)
1027 {
1028     static int calls;
1029
1030     TRACE("(%p, %p)\n", grad, path);
1031
1032     if (!(calls++))
1033         FIXME("not implemented\n");
1034
1035     return NotImplemented;
1036 }
1037
1038 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
1039     INT *count)
1040 {
1041     TRACE("(%p, %p)\n", grad, count);
1042
1043     if(!grad || !count)
1044         return InvalidParameter;
1045
1046     *count = grad->path->pathdata.Count;
1047
1048     return Ok;
1049 }
1050
1051 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
1052 {
1053     GpStatus stat;
1054
1055     TRACE("(%p, %p)\n", brush, rect);
1056
1057     if(!brush || !rect)
1058         return InvalidParameter;
1059
1060     stat = GdipGetPathWorldBounds(brush->path, rect, NULL, NULL);
1061
1062     return stat;
1063 }
1064
1065 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
1066 {
1067     GpRectF rectf;
1068     GpStatus stat;
1069
1070     TRACE("(%p, %p)\n", brush, rect);
1071
1072     if(!brush || !rect)
1073         return InvalidParameter;
1074
1075     stat = GdipGetPathGradientRect(brush, &rectf);
1076     if(stat != Ok)  return stat;
1077
1078     rect->X = roundr(rectf.X);
1079     rect->Y = roundr(rectf.Y);
1080     rect->Width  = roundr(rectf.Width);
1081     rect->Height = roundr(rectf.Height);
1082
1083     return Ok;
1084 }
1085
1086 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
1087     *grad, ARGB *argb, INT *count)
1088 {
1089     static int calls;
1090
1091     TRACE("(%p,%p,%p)\n", grad, argb, count);
1092
1093     if(!grad || !argb || !count || (*count < grad->path->pathdata.Count))
1094         return InvalidParameter;
1095
1096     if(!(calls++))
1097         FIXME("not implemented\n");
1098
1099     return NotImplemented;
1100 }
1101
1102 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient *brush, INT *count)
1103 {
1104     static int calls;
1105
1106     TRACE("(%p, %p)\n", brush, count);
1107
1108     if (!brush || !count)
1109        return InvalidParameter;
1110
1111     if(!(calls++))
1112         FIXME("not implemented\n");
1113
1114     return NotImplemented;
1115 }
1116
1117 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
1118     GpWrapMode *wrapmode)
1119 {
1120     TRACE("(%p, %p)\n", brush, wrapmode);
1121
1122     if(!brush || !wrapmode)
1123         return InvalidParameter;
1124
1125     *wrapmode = brush->wrap;
1126
1127     return Ok;
1128 }
1129
1130 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
1131 {
1132     TRACE("(%p, %p)\n", sf, argb);
1133
1134     if(!sf || !argb)
1135         return InvalidParameter;
1136
1137     *argb = sf->color;
1138
1139     return Ok;
1140 }
1141
1142 /******************************************************************************
1143  * GdipGetTextureImage [GDIPLUS.@]
1144  */
1145 GpStatus WINGDIPAPI GdipGetTextureImage(GpTexture *brush, GpImage **image)
1146 {
1147     TRACE("(%p, %p)\n", brush, image);
1148
1149     if(!brush || !image)
1150         return InvalidParameter;
1151
1152     return GdipCloneImage(brush->image, image);
1153 }
1154
1155 /******************************************************************************
1156  * GdipGetTextureTransform [GDIPLUS.@]
1157  */
1158 GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
1159 {
1160     TRACE("(%p, %p)\n", brush, matrix);
1161
1162     if(!brush || !matrix)
1163         return InvalidParameter;
1164
1165     memcpy(matrix, brush->transform, sizeof(GpMatrix));
1166
1167     return Ok;
1168 }
1169
1170 /******************************************************************************
1171  * GdipGetTextureWrapMode [GDIPLUS.@]
1172  */
1173 GpStatus WINGDIPAPI GdipGetTextureWrapMode(GpTexture *brush, GpWrapMode *wrapmode)
1174 {
1175     TRACE("(%p, %p)\n", brush, wrapmode);
1176
1177     if(!brush || !wrapmode)
1178         return InvalidParameter;
1179
1180     *wrapmode = brush->imageattributes->wrap;
1181
1182     return Ok;
1183 }
1184
1185 /******************************************************************************
1186  * GdipMultiplyTextureTransform [GDIPLUS.@]
1187  */
1188 GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
1189     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1190 {
1191     TRACE("(%p, %p, %d)\n", brush, matrix, order);
1192
1193     if(!brush || !matrix)
1194         return InvalidParameter;
1195
1196     return GdipMultiplyMatrix(brush->transform, matrix, order);
1197 }
1198
1199 /******************************************************************************
1200  * GdipResetTextureTransform [GDIPLUS.@]
1201  */
1202 GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
1203 {
1204     TRACE("(%p)\n", brush);
1205
1206     if(!brush)
1207         return InvalidParameter;
1208
1209     return GdipSetMatrixElements(brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1210 }
1211
1212 /******************************************************************************
1213  * GdipScaleTextureTransform [GDIPLUS.@]
1214  */
1215 GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
1216     REAL sx, REAL sy, GpMatrixOrder order)
1217 {
1218     TRACE("(%p, %.2f, %.2f, %d)\n", brush, sx, sy, order);
1219
1220     if(!brush)
1221         return InvalidParameter;
1222
1223     return GdipScaleMatrix(brush->transform, sx, sy, order);
1224 }
1225
1226 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
1227     GDIPCONST REAL *factors, GDIPCONST REAL* positions, INT count)
1228 {
1229     REAL *new_blendfac, *new_blendpos;
1230
1231     TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1232
1233     if(!brush || !factors || !positions || count <= 0 ||
1234        (count >= 2 && (positions[0] != 0.0f || positions[count-1] != 1.0f)))
1235         return InvalidParameter;
1236
1237     new_blendfac = GdipAlloc(count * sizeof(REAL));
1238     new_blendpos = GdipAlloc(count * sizeof(REAL));
1239
1240     if (!new_blendfac || !new_blendpos)
1241     {
1242         GdipFree(new_blendfac);
1243         GdipFree(new_blendpos);
1244         return OutOfMemory;
1245     }
1246
1247     memcpy(new_blendfac, factors, count * sizeof(REAL));
1248     memcpy(new_blendpos, positions, count * sizeof(REAL));
1249
1250     GdipFree(brush->blendfac);
1251     GdipFree(brush->blendpos);
1252
1253     brush->blendcount = count;
1254     brush->blendfac = new_blendfac;
1255     brush->blendpos = new_blendpos;
1256
1257     return Ok;
1258 }
1259
1260 GpStatus WINGDIPAPI GdipGetLineBlend(GpLineGradient *brush, REAL *factors,
1261     REAL *positions, INT count)
1262 {
1263     TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1264
1265     if (!brush || !factors || !positions || count <= 0)
1266         return InvalidParameter;
1267
1268     if (count < brush->blendcount)
1269         return InsufficientBuffer;
1270
1271     memcpy(factors, brush->blendfac, brush->blendcount * sizeof(REAL));
1272     memcpy(positions, brush->blendpos, brush->blendcount * sizeof(REAL));
1273
1274     return Ok;
1275 }
1276
1277 GpStatus WINGDIPAPI GdipGetLineBlendCount(GpLineGradient *brush, INT *count)
1278 {
1279     TRACE("(%p, %p)\n", brush, count);
1280
1281     if (!brush || !count)
1282         return InvalidParameter;
1283
1284     *count = brush->blendcount;
1285
1286     return Ok;
1287 }
1288
1289 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
1290     BOOL usegamma)
1291 {
1292     TRACE("(%p, %d)\n", line, usegamma);
1293
1294     if(!line)
1295         return InvalidParameter;
1296
1297     line->gamma = usegamma;
1298
1299     return Ok;
1300 }
1301
1302 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
1303     REAL scale)
1304 {
1305     REAL factors[33];
1306     REAL positions[33];
1307     int num_points = 0;
1308     int i;
1309     const int precision = 16;
1310     REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1311     REAL min_erf;
1312     REAL scale_erf;
1313
1314     TRACE("(%p, %0.2f, %0.2f)\n", line, focus, scale);
1315
1316     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
1317         return InvalidParameter;
1318
1319     /* we want 2 standard deviations */
1320     erf_range = 2.0 / sqrt(2);
1321
1322     /* calculate the constants we need to normalize the error function to be
1323         between 0.0 and scale over the range we need */
1324     min_erf = erf(-erf_range);
1325     scale_erf = scale / (-2.0 * min_erf);
1326
1327     if (focus != 0.0)
1328     {
1329         positions[0] = 0.0;
1330         factors[0] = 0.0;
1331         for (i=1; i<precision; i++)
1332         {
1333             positions[i] = focus * i / precision;
1334             factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1335         }
1336         num_points += precision;
1337     }
1338
1339     positions[num_points] = focus;
1340     factors[num_points] = scale;
1341     num_points += 1;
1342
1343     if (focus != 1.0)
1344     {
1345         for (i=1; i<precision; i++)
1346         {
1347             positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1348             factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1349         }
1350         num_points += precision;
1351         positions[num_points-1] = 1.0;
1352         factors[num_points-1] = 0.0;
1353     }
1354
1355     return GdipSetLineBlend(line, factors, positions, num_points);
1356 }
1357
1358 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
1359     GpWrapMode wrap)
1360 {
1361     TRACE("(%p, %d)\n", line, wrap);
1362
1363     if(!line || wrap == WrapModeClamp)
1364         return InvalidParameter;
1365
1366     line->wrap = wrap;
1367
1368     return Ok;
1369 }
1370
1371 GpStatus WINGDIPAPI GdipSetPathGradientBlend(GpPathGradient *brush, GDIPCONST REAL *blend,
1372     GDIPCONST REAL *pos, INT count)
1373 {
1374     static int calls;
1375
1376     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1377
1378     if(!(calls++))
1379         FIXME("not implemented\n");
1380
1381     return NotImplemented;
1382 }
1383
1384 GpStatus WINGDIPAPI GdipSetPathGradientLinearBlend(GpPathGradient *brush,
1385     REAL focus, REAL scale)
1386 {
1387     static int calls;
1388
1389     TRACE("(%p,%0.2f,%0.2f)\n", brush, focus, scale);
1390
1391     if(!(calls++))
1392         FIXME("not implemented\n");
1393
1394     return NotImplemented;
1395 }
1396
1397 GpStatus WINGDIPAPI GdipSetPathGradientPresetBlend(GpPathGradient *brush,
1398     GDIPCONST ARGB *blend, GDIPCONST REAL *pos, INT count)
1399 {
1400     FIXME("(%p,%p,%p,%i): stub\n", brush, blend, pos, count);
1401     return NotImplemented;
1402 }
1403
1404 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlend(GpPathGradient *brush,
1405     ARGB *blend, REAL *pos, INT count)
1406 {
1407     FIXME("(%p,%p,%p,%i): stub\n", brush, blend, pos, count);
1408     return NotImplemented;
1409 }
1410
1411 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlendCount(GpPathGradient *brush,
1412     INT *count)
1413 {
1414     FIXME("(%p,%p): stub\n", brush, count);
1415     return NotImplemented;
1416 }
1417
1418 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1419     ARGB argb)
1420 {
1421     TRACE("(%p, %x)\n", grad, argb);
1422
1423     if(!grad)
1424         return InvalidParameter;
1425
1426     grad->centercolor = argb;
1427     return Ok;
1428 }
1429
1430 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1431     GpPointF *point)
1432 {
1433     TRACE("(%p, %s)\n", grad, debugstr_pointf(point));
1434
1435     if(!grad || !point)
1436         return InvalidParameter;
1437
1438     grad->center.X = point->X;
1439     grad->center.Y = point->Y;
1440
1441     return Ok;
1442 }
1443
1444 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1445     GpPoint *point)
1446 {
1447     GpPointF ptf;
1448
1449     TRACE("(%p, %p)\n", grad, point);
1450
1451     if(!point)
1452         return InvalidParameter;
1453
1454     ptf.X = (REAL)point->X;
1455     ptf.Y = (REAL)point->Y;
1456
1457     return GdipSetPathGradientCenterPoint(grad,&ptf);
1458 }
1459
1460 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1461     REAL x, REAL y)
1462 {
1463     TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1464
1465     if(!grad)
1466         return InvalidParameter;
1467
1468     grad->focus.X = x;
1469     grad->focus.Y = y;
1470
1471     return Ok;
1472 }
1473
1474 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1475     BOOL gamma)
1476 {
1477     TRACE("(%p, %d)\n", grad, gamma);
1478
1479     if(!grad)
1480         return InvalidParameter;
1481
1482     grad->gamma = gamma;
1483
1484     return Ok;
1485 }
1486
1487 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1488     REAL focus, REAL scale)
1489 {
1490     static int calls;
1491
1492     TRACE("(%p,%0.2f,%0.2f)\n", grad, focus, scale);
1493
1494     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
1495         return InvalidParameter;
1496
1497     if(!(calls++))
1498         FIXME("not implemented\n");
1499
1500     return NotImplemented;
1501 }
1502
1503 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1504     *grad, GDIPCONST ARGB *argb, INT *count)
1505 {
1506     static int calls;
1507
1508     TRACE("(%p,%p,%p)\n", grad, argb, count);
1509
1510     if(!grad || !argb || !count || (*count <= 0) ||
1511         (*count > grad->path->pathdata.Count))
1512         return InvalidParameter;
1513
1514     if(!(calls++))
1515         FIXME("not implemented\n");
1516
1517     return NotImplemented;
1518 }
1519
1520 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1521     GpWrapMode wrap)
1522 {
1523     TRACE("(%p, %d)\n", grad, wrap);
1524
1525     if(!grad)
1526         return InvalidParameter;
1527
1528     grad->wrap = wrap;
1529
1530     return Ok;
1531 }
1532
1533 GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
1534     GpMatrix *matrix)
1535 {
1536     static int calls;
1537
1538     TRACE("(%p,%p)\n", grad, matrix);
1539
1540     if(!(calls++))
1541         FIXME("not implemented\n");
1542
1543     return NotImplemented;
1544 }
1545
1546 GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
1547     GpMatrix *matrix)
1548 {
1549     static int calls;
1550
1551     TRACE("(%p,%p)\n", grad, matrix);
1552
1553     if(!(calls++))
1554         FIXME("not implemented\n");
1555
1556     return NotImplemented;
1557 }
1558
1559 GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
1560     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1561 {
1562     static int calls;
1563
1564     TRACE("(%p,%p,%i)\n", grad, matrix, order);
1565
1566     if(!(calls++))
1567         FIXME("not implemented\n");
1568
1569     return NotImplemented;
1570 }
1571
1572 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
1573     REAL angle, GpMatrixOrder order)
1574 {
1575     static int calls;
1576
1577     TRACE("(%p,%0.2f,%i)\n", grad, angle, order);
1578
1579     if(!(calls++))
1580         FIXME("not implemented\n");
1581
1582     return NotImplemented;
1583 }
1584
1585 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
1586     REAL sx, REAL sy, GpMatrixOrder order)
1587 {
1588     static int calls;
1589
1590     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, sx, sy, order);
1591
1592     if(!(calls++))
1593         FIXME("not implemented\n");
1594
1595     return NotImplemented;
1596 }
1597
1598 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
1599     REAL dx, REAL dy, GpMatrixOrder order)
1600 {
1601     static int calls;
1602
1603     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, dx, dy, order);
1604
1605     if(!(calls++))
1606         FIXME("not implemented\n");
1607
1608     return NotImplemented;
1609 }
1610
1611 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1612 {
1613     TRACE("(%p, %x)\n", sf, argb);
1614
1615     if(!sf)
1616         return InvalidParameter;
1617
1618     sf->color = argb;
1619     return Ok;
1620 }
1621
1622 /******************************************************************************
1623  * GdipSetTextureTransform [GDIPLUS.@]
1624  */
1625 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1626     GDIPCONST GpMatrix *matrix)
1627 {
1628     TRACE("(%p, %p)\n", texture, matrix);
1629
1630     if(!texture || !matrix)
1631         return InvalidParameter;
1632
1633     memcpy(texture->transform, matrix, sizeof(GpMatrix));
1634
1635     return Ok;
1636 }
1637
1638 /******************************************************************************
1639  * GdipSetTextureWrapMode [GDIPLUS.@]
1640  *
1641  * WrapMode not used, only stored
1642  */
1643 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1644 {
1645     TRACE("(%p, %d)\n", brush, wrapmode);
1646
1647     if(!brush)
1648         return InvalidParameter;
1649
1650     brush->imageattributes->wrap = wrapmode;
1651
1652     return Ok;
1653 }
1654
1655 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1656     ARGB color2)
1657 {
1658     TRACE("(%p, %x, %x)\n", brush, color1, color2);
1659
1660     if(!brush)
1661         return InvalidParameter;
1662
1663     brush->startcolor = color1;
1664     brush->endcolor   = color2;
1665
1666     return Ok;
1667 }
1668
1669 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1670 {
1671     TRACE("(%p, %p)\n", brush, colors);
1672
1673     if(!brush || !colors)
1674         return InvalidParameter;
1675
1676     colors[0] = brush->startcolor;
1677     colors[1] = brush->endcolor;
1678
1679     return Ok;
1680 }
1681
1682 /******************************************************************************
1683  * GdipRotateTextureTransform [GDIPLUS.@]
1684  */
1685 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
1686     GpMatrixOrder order)
1687 {
1688     TRACE("(%p, %.2f, %d)\n", brush, angle, order);
1689
1690     if(!brush)
1691         return InvalidParameter;
1692
1693     return GdipRotateMatrix(brush->transform, angle, order);
1694 }
1695
1696 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
1697     REAL scale)
1698 {
1699     REAL factors[3];
1700     REAL positions[3];
1701     int num_points = 0;
1702
1703     TRACE("(%p,%.2f,%.2f)\n", brush, focus, scale);
1704
1705     if (!brush) return InvalidParameter;
1706
1707     if (focus != 0.0)
1708     {
1709         factors[num_points] = 0.0;
1710         positions[num_points] = 0.0;
1711         num_points++;
1712     }
1713
1714     factors[num_points] = scale;
1715     positions[num_points] = focus;
1716     num_points++;
1717
1718     if (focus != 1.0)
1719     {
1720         factors[num_points] = 0.0;
1721         positions[num_points] = 1.0;
1722         num_points++;
1723     }
1724
1725     return GdipSetLineBlend(brush, factors, positions, num_points);
1726 }
1727
1728 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
1729     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
1730 {
1731     ARGB *new_color;
1732     REAL *new_pos;
1733     TRACE("(%p,%p,%p,%i)\n", brush, blend, positions, count);
1734
1735     if (!brush || !blend || !positions || count < 2 ||
1736         positions[0] != 0.0f || positions[count-1] != 1.0f)
1737     {
1738         return InvalidParameter;
1739     }
1740
1741     new_color = GdipAlloc(count * sizeof(ARGB));
1742     new_pos = GdipAlloc(count * sizeof(REAL));
1743     if (!new_color || !new_pos)
1744     {
1745         GdipFree(new_color);
1746         GdipFree(new_pos);
1747         return OutOfMemory;
1748     }
1749
1750     memcpy(new_color, blend, sizeof(ARGB) * count);
1751     memcpy(new_pos, positions, sizeof(REAL) * count);
1752
1753     GdipFree(brush->pblendcolor);
1754     GdipFree(brush->pblendpos);
1755
1756     brush->pblendcolor = new_color;
1757     brush->pblendpos = new_pos;
1758     brush->pblendcount = count;
1759
1760     return Ok;
1761 }
1762
1763 GpStatus WINGDIPAPI GdipGetLinePresetBlend(GpLineGradient *brush,
1764     ARGB *blend, REAL* positions, INT count)
1765 {
1766     if (!brush || !blend || !positions || count < 2)
1767         return InvalidParameter;
1768
1769     if (brush->pblendcount == 0)
1770         return GenericError;
1771
1772     if (count < brush->pblendcount)
1773         return InsufficientBuffer;
1774
1775     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1776     memcpy(positions, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1777
1778     return Ok;
1779 }
1780
1781 GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
1782     INT *count)
1783 {
1784     if (!brush || !count)
1785         return InvalidParameter;
1786
1787     *count = brush->pblendcount;
1788
1789     return Ok;
1790 }
1791
1792 GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
1793 {
1794     static int calls;
1795
1796     TRACE("(%p)\n", brush);
1797
1798     if(!(calls++))
1799         FIXME("not implemented\n");
1800
1801     return NotImplemented;
1802 }
1803
1804 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
1805     GDIPCONST GpMatrix *matrix)
1806 {
1807     static int calls;
1808
1809     TRACE("(%p,%p)\n", brush,  matrix);
1810
1811     if(!(calls++))
1812         FIXME("not implemented\n");
1813
1814     return NotImplemented;
1815 }
1816
1817 GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
1818 {
1819     static int calls;
1820
1821     TRACE("(%p,%p)\n", brush, matrix);
1822
1823     if(!(calls++))
1824         FIXME("not implemented\n");
1825
1826     return NotImplemented;
1827 }
1828
1829 GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
1830     GpMatrixOrder order)
1831 {
1832     static int calls;
1833
1834     TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
1835
1836     if(!(calls++))
1837         FIXME("not implemented\n");
1838
1839     return NotImplemented;
1840 }
1841
1842 GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
1843     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1844 {
1845     static int calls;
1846
1847     TRACE("(%p,%p,%u)\n", brush, matrix, order);
1848
1849     if(!(calls++))
1850         FIXME("not implemented\n");
1851
1852     return NotImplemented;
1853 }
1854
1855 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient* brush,
1856         REAL dx, REAL dy, GpMatrixOrder order)
1857 {
1858     static int calls;
1859
1860     TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
1861
1862     if(!(calls++))
1863         FIXME("not implemented\n");
1864
1865     return Ok;
1866 }
1867
1868 /******************************************************************************
1869  * GdipTranslateTextureTransform [GDIPLUS.@]
1870  */
1871 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
1872     GpMatrixOrder order)
1873 {
1874     TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
1875
1876     if(!brush)
1877         return InvalidParameter;
1878
1879     return GdipTranslateMatrix(brush->transform, dx, dy, order);
1880 }
1881
1882 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
1883 {
1884     TRACE("(%p, %p)\n", brush, rect);
1885
1886     if(!brush || !rect)
1887         return InvalidParameter;
1888
1889     *rect = brush->rect;
1890
1891     return Ok;
1892 }
1893
1894 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
1895 {
1896     GpRectF  rectF;
1897     GpStatus ret;
1898
1899     TRACE("(%p, %p)\n", brush, rect);
1900
1901     if(!rect)
1902         return InvalidParameter;
1903
1904     ret = GdipGetLineRect(brush, &rectF);
1905
1906     if(ret == Ok){
1907         rect->X      = roundr(rectF.X);
1908         rect->Y      = roundr(rectF.Y);
1909         rect->Width  = roundr(rectF.Width);
1910         rect->Height = roundr(rectF.Height);
1911     }
1912
1913     return ret;
1914 }
1915
1916 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
1917     REAL angle, GpMatrixOrder order)
1918 {
1919     static int calls;
1920
1921     TRACE("(%p,%0.2f,%u)\n", brush, angle, order);
1922
1923     if(!brush)
1924         return InvalidParameter;
1925
1926     if(!(calls++))
1927         FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
1928
1929     return NotImplemented;
1930 }