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