gdiplus: Avoid needless gotos.
[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(!grad)
582         return InvalidParameter;
583
584     if(!points || 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(!grad)
615         return InvalidParameter;
616
617     if(!points || 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(!grad)
651         return InvalidParameter;
652
653     if (!path)
654         return OutOfMemory;
655
656     stat = GdipClonePath((GpPath*)path, &new_path);
657
658     if (stat == Ok)
659     {
660         stat = create_path_gradient(new_path, 0xffffffff, grad);
661
662         if (stat != Ok)
663             GdipDeletePath(new_path);
664     }
665
666     return stat;
667 }
668
669 /******************************************************************************
670  * GdipCreateSolidFill [GDIPLUS.@]
671  */
672 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
673 {
674     TRACE("(%x, %p)\n", color, sf);
675
676     if(!sf)  return InvalidParameter;
677
678     *sf = GdipAlloc(sizeof(GpSolidFill));
679     if (!*sf) return OutOfMemory;
680
681     (*sf)->brush.bt = BrushTypeSolidColor;
682     (*sf)->color = color;
683
684     TRACE("<-- %p\n", *sf);
685
686     return Ok;
687 }
688
689 /******************************************************************************
690  * GdipCreateTexture [GDIPLUS.@]
691  *
692  * PARAMS
693  *  image       [I] image to use
694  *  wrapmode    [I] optional
695  *  texture     [O] pointer to the resulting texturebrush
696  *
697  * RETURNS
698  *  SUCCESS: Ok
699  *  FAILURE: element of GpStatus
700  */
701 GpStatus WINGDIPAPI GdipCreateTexture(GpImage *image, GpWrapMode wrapmode,
702         GpTexture **texture)
703 {
704     UINT width, height;
705     GpImageAttributes *attributes;
706     GpStatus stat;
707
708     TRACE("%p, %d %p\n", image, wrapmode, texture);
709
710     if (!(image && texture))
711         return InvalidParameter;
712
713     stat = GdipGetImageWidth(image, &width);
714     if (stat != Ok) return stat;
715     stat = GdipGetImageHeight(image, &height);
716     if (stat != Ok) return stat;
717
718     stat = GdipCreateImageAttributes(&attributes);
719
720     if (stat == Ok)
721     {
722         attributes->wrap = wrapmode;
723
724         stat = GdipCreateTextureIA(image, attributes, 0, 0, width, height,
725             texture);
726
727         GdipDisposeImageAttributes(attributes);
728     }
729
730     return stat;
731 }
732
733 /******************************************************************************
734  * GdipCreateTexture2 [GDIPLUS.@]
735  */
736 GpStatus WINGDIPAPI GdipCreateTexture2(GpImage *image, GpWrapMode wrapmode,
737         REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
738 {
739     GpImageAttributes *attributes;
740     GpStatus stat;
741
742     TRACE("%p %d %f %f %f %f %p\n", image, wrapmode,
743             x, y, width, height, texture);
744
745     stat = GdipCreateImageAttributes(&attributes);
746
747     if (stat == Ok)
748     {
749         attributes->wrap = wrapmode;
750
751         stat = GdipCreateTextureIA(image, attributes, x, y, width, height,
752                 texture);
753
754         GdipDisposeImageAttributes(attributes);
755     }
756
757     return stat;
758 }
759
760 /******************************************************************************
761  * GdipCreateTextureIA [GDIPLUS.@]
762  *
763  * FIXME: imageattr ignored
764  */
765 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
766     GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
767     REAL height, GpTexture **texture)
768 {
769     GpStatus status;
770     GpImage *new_image=NULL;
771
772     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %p)\n", image, imageattr, x, y, width, height,
773            texture);
774
775     if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
776         return InvalidParameter;
777
778     *texture = NULL;
779
780     if(image->type != ImageTypeBitmap){
781         FIXME("not implemented for image type %d\n", image->type);
782         return NotImplemented;
783     }
784
785     status = GdipCloneBitmapArea(x, y, width, height, PixelFormatDontCare, (GpBitmap*)image, (GpBitmap**)&new_image);
786     if (status != Ok)
787         return status;
788
789     *texture = GdipAlloc(sizeof(GpTexture));
790     if (!*texture){
791         status = OutOfMemory;
792         goto exit;
793     }
794
795     if((status = GdipCreateMatrix(&(*texture)->transform)) != Ok){
796         goto exit;
797     }
798
799     if (imageattr)
800     {
801         status = GdipCloneImageAttributes(imageattr, &(*texture)->imageattributes);
802     }
803     else
804     {
805         status = GdipCreateImageAttributes(&(*texture)->imageattributes);
806         if (status == Ok)
807             (*texture)->imageattributes->wrap = WrapModeTile;
808     }
809     if (status == Ok)
810     {
811         (*texture)->brush.bt = BrushTypeTextureFill;
812         (*texture)->image = new_image;
813     }
814
815 exit:
816     if (status == Ok)
817     {
818         TRACE("<-- %p\n", *texture);
819     }
820     else
821     {
822         if (*texture)
823         {
824             GdipDeleteMatrix((*texture)->transform);
825             GdipDisposeImageAttributes((*texture)->imageattributes);
826             GdipFree(*texture);
827             *texture = NULL;
828         }
829         GdipDisposeImage(new_image);
830         TRACE("<-- error %u\n", status);
831     }
832
833     return status;
834 }
835
836 /******************************************************************************
837  * GdipCreateTextureIAI [GDIPLUS.@]
838  */
839 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
840     INT x, INT y, INT width, INT height, GpTexture **texture)
841 {
842     TRACE("(%p, %p, %d, %d, %d, %d, %p)\n", image, imageattr, x, y, width, height,
843            texture);
844
845     return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
846 }
847
848 GpStatus WINGDIPAPI GdipCreateTexture2I(GpImage *image, GpWrapMode wrapmode,
849         INT x, INT y, INT width, INT height, GpTexture **texture)
850 {
851     GpImageAttributes *imageattr;
852     GpStatus stat;
853
854     TRACE("%p %d %d %d %d %d %p\n", image, wrapmode, x, y, width, height,
855             texture);
856
857     stat = GdipCreateImageAttributes(&imageattr);
858
859     if (stat == Ok)
860     {
861         imageattr->wrap = wrapmode;
862
863         stat = GdipCreateTextureIA(image, imageattr, x, y, width, height, texture);
864     }
865
866     return stat;
867 }
868
869 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
870 {
871     TRACE("(%p, %p)\n", brush, type);
872
873     if(!brush || !type)  return InvalidParameter;
874
875     *type = brush->bt;
876
877     return Ok;
878 }
879
880 GpStatus WINGDIPAPI GdipGetHatchBackgroundColor(GpHatch *brush, ARGB *backcol)
881 {
882     TRACE("(%p, %p)\n", brush, backcol);
883
884     if(!brush || !backcol)  return InvalidParameter;
885
886     *backcol = brush->backcol;
887
888     return Ok;
889 }
890
891 GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
892 {
893     TRACE("(%p, %p)\n", brush, forecol);
894
895     if(!brush || !forecol)  return InvalidParameter;
896
897     *forecol = brush->forecol;
898
899     return Ok;
900 }
901
902 GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, HatchStyle *hatchstyle)
903 {
904     TRACE("(%p, %p)\n", brush, hatchstyle);
905
906     if(!brush || !hatchstyle)  return InvalidParameter;
907
908     *hatchstyle = brush->hatchstyle;
909
910     return Ok;
911 }
912
913 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
914 {
915     TRACE("(%p)\n", brush);
916
917     if(!brush)  return InvalidParameter;
918
919     switch(brush->bt)
920     {
921         case BrushTypePathGradient:
922             GdipDeletePath(((GpPathGradient*) brush)->path);
923             GdipDeleteMatrix(((GpPathGradient*) brush)->transform);
924             GdipFree(((GpPathGradient*) brush)->blendfac);
925             GdipFree(((GpPathGradient*) brush)->blendpos);
926             GdipFree(((GpPathGradient*) brush)->surroundcolors);
927             GdipFree(((GpPathGradient*) brush)->pblendcolor);
928             GdipFree(((GpPathGradient*) brush)->pblendpos);
929             break;
930         case BrushTypeLinearGradient:
931             GdipFree(((GpLineGradient*)brush)->blendfac);
932             GdipFree(((GpLineGradient*)brush)->blendpos);
933             GdipFree(((GpLineGradient*)brush)->pblendcolor);
934             GdipFree(((GpLineGradient*)brush)->pblendpos);
935             break;
936         case BrushTypeTextureFill:
937             GdipDeleteMatrix(((GpTexture*)brush)->transform);
938             GdipDisposeImage(((GpTexture*)brush)->image);
939             GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes);
940             GdipFree(((GpTexture*)brush)->bitmap_bits);
941             break;
942         default:
943             break;
944     }
945
946     GdipFree(brush);
947
948     return Ok;
949 }
950
951 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
952     BOOL *usinggamma)
953 {
954     TRACE("(%p, %p)\n", line, usinggamma);
955
956     if(!line || !usinggamma)
957         return InvalidParameter;
958
959     *usinggamma = line->gamma;
960
961     return Ok;
962 }
963
964 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
965 {
966     TRACE("(%p, %p)\n", brush, wrapmode);
967
968     if(!brush || !wrapmode)
969         return InvalidParameter;
970
971     *wrapmode = brush->wrap;
972
973     return Ok;
974 }
975
976 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
977     REAL *positions, INT count)
978 {
979     TRACE("(%p, %p, %p, %d)\n", brush, blend, positions, count);
980
981     if(!brush || !blend || !positions || count <= 0)
982         return InvalidParameter;
983
984     if(count < brush->blendcount)
985         return InsufficientBuffer;
986
987     memcpy(blend, brush->blendfac, count*sizeof(REAL));
988     if(brush->blendcount > 1){
989         memcpy(positions, brush->blendpos, count*sizeof(REAL));
990     }
991
992     return Ok;
993 }
994
995 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
996 {
997     TRACE("(%p, %p)\n", brush, count);
998
999     if(!brush || !count)
1000         return InvalidParameter;
1001
1002     *count = brush->blendcount;
1003
1004     return Ok;
1005 }
1006
1007 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
1008     GpPointF *point)
1009 {
1010     TRACE("(%p, %p)\n", grad, point);
1011
1012     if(!grad || !point)
1013         return InvalidParameter;
1014
1015     point->X = grad->center.X;
1016     point->Y = grad->center.Y;
1017
1018     return Ok;
1019 }
1020
1021 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
1022     GpPoint *point)
1023 {
1024     GpStatus ret;
1025     GpPointF ptf;
1026
1027     TRACE("(%p, %p)\n", grad, point);
1028
1029     if(!point)
1030         return InvalidParameter;
1031
1032     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
1033
1034     if(ret == Ok){
1035         point->X = roundr(ptf.X);
1036         point->Y = roundr(ptf.Y);
1037     }
1038
1039     return ret;
1040 }
1041
1042 GpStatus WINGDIPAPI GdipGetPathGradientCenterColor(GpPathGradient *grad,
1043     ARGB *colors)
1044 {
1045     TRACE("(%p,%p)\n", grad, colors);
1046
1047     if (!grad || !colors)
1048         return InvalidParameter;
1049
1050     *colors = grad->centercolor;
1051
1052     return Ok;
1053 }
1054
1055 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
1056     REAL *x, REAL *y)
1057 {
1058     TRACE("(%p, %p, %p)\n", grad, x, y);
1059
1060     if(!grad || !x || !y)
1061         return InvalidParameter;
1062
1063     *x = grad->focus.X;
1064     *y = grad->focus.Y;
1065
1066     return Ok;
1067 }
1068
1069 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
1070     BOOL *gamma)
1071 {
1072     TRACE("(%p, %p)\n", grad, gamma);
1073
1074     if(!grad || !gamma)
1075         return InvalidParameter;
1076
1077     *gamma = grad->gamma;
1078
1079     return Ok;
1080 }
1081
1082 GpStatus WINGDIPAPI GdipGetPathGradientPath(GpPathGradient *grad, GpPath *path)
1083 {
1084     static int calls;
1085
1086     TRACE("(%p, %p)\n", grad, path);
1087
1088     if (!(calls++))
1089         FIXME("not implemented\n");
1090
1091     return NotImplemented;
1092 }
1093
1094 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
1095     INT *count)
1096 {
1097     TRACE("(%p, %p)\n", grad, count);
1098
1099     if(!grad || !count)
1100         return InvalidParameter;
1101
1102     *count = grad->path->pathdata.Count;
1103
1104     return Ok;
1105 }
1106
1107 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
1108 {
1109     GpStatus stat;
1110
1111     TRACE("(%p, %p)\n", brush, rect);
1112
1113     if(!brush || !rect)
1114         return InvalidParameter;
1115
1116     stat = GdipGetPathWorldBounds(brush->path, rect, NULL, NULL);
1117
1118     return stat;
1119 }
1120
1121 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
1122 {
1123     GpRectF rectf;
1124     GpStatus stat;
1125
1126     TRACE("(%p, %p)\n", brush, rect);
1127
1128     if(!brush || !rect)
1129         return InvalidParameter;
1130
1131     stat = GdipGetPathGradientRect(brush, &rectf);
1132     if(stat != Ok)  return stat;
1133
1134     rect->X = roundr(rectf.X);
1135     rect->Y = roundr(rectf.Y);
1136     rect->Width  = roundr(rectf.Width);
1137     rect->Height = roundr(rectf.Height);
1138
1139     return Ok;
1140 }
1141
1142 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
1143     *grad, ARGB *argb, INT *count)
1144 {
1145     INT i;
1146
1147     TRACE("(%p,%p,%p)\n", grad, argb, count);
1148
1149     if(!grad || !argb || !count || (*count < grad->path->pathdata.Count))
1150         return InvalidParameter;
1151
1152     for (i=0; i<grad->path->pathdata.Count; i++)
1153     {
1154         if (i < grad->surroundcolorcount)
1155             argb[i] = grad->surroundcolors[i];
1156         else
1157             argb[i] = grad->surroundcolors[grad->surroundcolorcount-1];
1158     }
1159
1160     *count = grad->surroundcolorcount;
1161
1162     return Ok;
1163 }
1164
1165 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient *brush, INT *count)
1166 {
1167     TRACE("(%p, %p)\n", brush, count);
1168
1169     if (!brush || !count)
1170        return InvalidParameter;
1171
1172     /* Yes, this actually returns the number of points in the path (which is the
1173      * required size of a buffer to get the surround colors), rather than the
1174      * number of surround colors. The real count is returned when getting the
1175      * colors. */
1176     *count = brush->path->pathdata.Count;
1177
1178     return Ok;
1179 }
1180
1181 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
1182     GpWrapMode *wrapmode)
1183 {
1184     TRACE("(%p, %p)\n", brush, wrapmode);
1185
1186     if(!brush || !wrapmode)
1187         return InvalidParameter;
1188
1189     *wrapmode = brush->wrap;
1190
1191     return Ok;
1192 }
1193
1194 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
1195 {
1196     TRACE("(%p, %p)\n", sf, argb);
1197
1198     if(!sf || !argb)
1199         return InvalidParameter;
1200
1201     *argb = sf->color;
1202
1203     return Ok;
1204 }
1205
1206 /******************************************************************************
1207  * GdipGetTextureImage [GDIPLUS.@]
1208  */
1209 GpStatus WINGDIPAPI GdipGetTextureImage(GpTexture *brush, GpImage **image)
1210 {
1211     TRACE("(%p, %p)\n", brush, image);
1212
1213     if(!brush || !image)
1214         return InvalidParameter;
1215
1216     return GdipCloneImage(brush->image, image);
1217 }
1218
1219 /******************************************************************************
1220  * GdipGetTextureTransform [GDIPLUS.@]
1221  */
1222 GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
1223 {
1224     TRACE("(%p, %p)\n", brush, matrix);
1225
1226     if(!brush || !matrix)
1227         return InvalidParameter;
1228
1229     memcpy(matrix, brush->transform, sizeof(GpMatrix));
1230
1231     return Ok;
1232 }
1233
1234 /******************************************************************************
1235  * GdipGetTextureWrapMode [GDIPLUS.@]
1236  */
1237 GpStatus WINGDIPAPI GdipGetTextureWrapMode(GpTexture *brush, GpWrapMode *wrapmode)
1238 {
1239     TRACE("(%p, %p)\n", brush, wrapmode);
1240
1241     if(!brush || !wrapmode)
1242         return InvalidParameter;
1243
1244     *wrapmode = brush->imageattributes->wrap;
1245
1246     return Ok;
1247 }
1248
1249 /******************************************************************************
1250  * GdipMultiplyTextureTransform [GDIPLUS.@]
1251  */
1252 GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
1253     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1254 {
1255     TRACE("(%p, %p, %d)\n", brush, matrix, order);
1256
1257     if(!brush || !matrix)
1258         return InvalidParameter;
1259
1260     return GdipMultiplyMatrix(brush->transform, matrix, order);
1261 }
1262
1263 /******************************************************************************
1264  * GdipResetTextureTransform [GDIPLUS.@]
1265  */
1266 GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
1267 {
1268     TRACE("(%p)\n", brush);
1269
1270     if(!brush)
1271         return InvalidParameter;
1272
1273     return GdipSetMatrixElements(brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1274 }
1275
1276 /******************************************************************************
1277  * GdipScaleTextureTransform [GDIPLUS.@]
1278  */
1279 GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
1280     REAL sx, REAL sy, GpMatrixOrder order)
1281 {
1282     TRACE("(%p, %.2f, %.2f, %d)\n", brush, sx, sy, order);
1283
1284     if(!brush)
1285         return InvalidParameter;
1286
1287     return GdipScaleMatrix(brush->transform, sx, sy, order);
1288 }
1289
1290 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
1291     GDIPCONST REAL *factors, GDIPCONST REAL* positions, INT count)
1292 {
1293     REAL *new_blendfac, *new_blendpos;
1294
1295     TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1296
1297     if(!brush || !factors || !positions || count <= 0 ||
1298        (count >= 2 && (positions[0] != 0.0f || positions[count-1] != 1.0f)))
1299         return InvalidParameter;
1300
1301     new_blendfac = GdipAlloc(count * sizeof(REAL));
1302     new_blendpos = GdipAlloc(count * sizeof(REAL));
1303
1304     if (!new_blendfac || !new_blendpos)
1305     {
1306         GdipFree(new_blendfac);
1307         GdipFree(new_blendpos);
1308         return OutOfMemory;
1309     }
1310
1311     memcpy(new_blendfac, factors, count * sizeof(REAL));
1312     memcpy(new_blendpos, positions, count * sizeof(REAL));
1313
1314     GdipFree(brush->blendfac);
1315     GdipFree(brush->blendpos);
1316
1317     brush->blendcount = count;
1318     brush->blendfac = new_blendfac;
1319     brush->blendpos = new_blendpos;
1320
1321     return Ok;
1322 }
1323
1324 GpStatus WINGDIPAPI GdipGetLineBlend(GpLineGradient *brush, REAL *factors,
1325     REAL *positions, INT count)
1326 {
1327     TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1328
1329     if (!brush || !factors || !positions || count <= 0)
1330         return InvalidParameter;
1331
1332     if (count < brush->blendcount)
1333         return InsufficientBuffer;
1334
1335     memcpy(factors, brush->blendfac, brush->blendcount * sizeof(REAL));
1336     memcpy(positions, brush->blendpos, brush->blendcount * sizeof(REAL));
1337
1338     return Ok;
1339 }
1340
1341 GpStatus WINGDIPAPI GdipGetLineBlendCount(GpLineGradient *brush, INT *count)
1342 {
1343     TRACE("(%p, %p)\n", brush, count);
1344
1345     if (!brush || !count)
1346         return InvalidParameter;
1347
1348     *count = brush->blendcount;
1349
1350     return Ok;
1351 }
1352
1353 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
1354     BOOL usegamma)
1355 {
1356     TRACE("(%p, %d)\n", line, usegamma);
1357
1358     if(!line)
1359         return InvalidParameter;
1360
1361     line->gamma = usegamma;
1362
1363     return Ok;
1364 }
1365
1366 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
1367     REAL scale)
1368 {
1369     REAL factors[33];
1370     REAL positions[33];
1371     int num_points = 0;
1372     int i;
1373     const int precision = 16;
1374     REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1375     REAL min_erf;
1376     REAL scale_erf;
1377
1378     TRACE("(%p, %0.2f, %0.2f)\n", line, focus, scale);
1379
1380     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
1381         return InvalidParameter;
1382
1383     /* we want 2 standard deviations */
1384     erf_range = 2.0 / sqrt(2);
1385
1386     /* calculate the constants we need to normalize the error function to be
1387         between 0.0 and scale over the range we need */
1388     min_erf = erf(-erf_range);
1389     scale_erf = scale / (-2.0 * min_erf);
1390
1391     if (focus != 0.0)
1392     {
1393         positions[0] = 0.0;
1394         factors[0] = 0.0;
1395         for (i=1; i<precision; i++)
1396         {
1397             positions[i] = focus * i / precision;
1398             factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1399         }
1400         num_points += precision;
1401     }
1402
1403     positions[num_points] = focus;
1404     factors[num_points] = scale;
1405     num_points += 1;
1406
1407     if (focus != 1.0)
1408     {
1409         for (i=1; i<precision; i++)
1410         {
1411             positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1412             factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1413         }
1414         num_points += precision;
1415         positions[num_points-1] = 1.0;
1416         factors[num_points-1] = 0.0;
1417     }
1418
1419     return GdipSetLineBlend(line, factors, positions, num_points);
1420 }
1421
1422 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
1423     GpWrapMode wrap)
1424 {
1425     TRACE("(%p, %d)\n", line, wrap);
1426
1427     if(!line || wrap == WrapModeClamp)
1428         return InvalidParameter;
1429
1430     line->wrap = wrap;
1431
1432     return Ok;
1433 }
1434
1435 GpStatus WINGDIPAPI GdipSetPathGradientBlend(GpPathGradient *brush, GDIPCONST REAL *blend,
1436     GDIPCONST REAL *pos, INT count)
1437 {
1438     REAL *new_blendfac, *new_blendpos;
1439
1440     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1441
1442     if(!brush || !blend || !pos || count <= 0 ||
1443        (count >= 2 && (pos[0] != 0.0f || pos[count-1] != 1.0f)))
1444         return InvalidParameter;
1445
1446     new_blendfac = GdipAlloc(count * sizeof(REAL));
1447     new_blendpos = GdipAlloc(count * sizeof(REAL));
1448
1449     if (!new_blendfac || !new_blendpos)
1450     {
1451         GdipFree(new_blendfac);
1452         GdipFree(new_blendpos);
1453         return OutOfMemory;
1454     }
1455
1456     memcpy(new_blendfac, blend, count * sizeof(REAL));
1457     memcpy(new_blendpos, pos, count * sizeof(REAL));
1458
1459     GdipFree(brush->blendfac);
1460     GdipFree(brush->blendpos);
1461
1462     brush->blendcount = count;
1463     brush->blendfac = new_blendfac;
1464     brush->blendpos = new_blendpos;
1465
1466     return Ok;
1467 }
1468
1469 GpStatus WINGDIPAPI GdipSetPathGradientLinearBlend(GpPathGradient *brush,
1470     REAL focus, REAL scale)
1471 {
1472     REAL factors[3];
1473     REAL positions[3];
1474     int num_points = 0;
1475
1476     TRACE("(%p,%0.2f,%0.2f)\n", brush, focus, scale);
1477
1478     if (!brush) return InvalidParameter;
1479
1480     if (focus != 0.0)
1481     {
1482         factors[num_points] = 0.0;
1483         positions[num_points] = 0.0;
1484         num_points++;
1485     }
1486
1487     factors[num_points] = scale;
1488     positions[num_points] = focus;
1489     num_points++;
1490
1491     if (focus != 1.0)
1492     {
1493         factors[num_points] = 0.0;
1494         positions[num_points] = 1.0;
1495         num_points++;
1496     }
1497
1498     return GdipSetPathGradientBlend(brush, factors, positions, num_points);
1499 }
1500
1501 GpStatus WINGDIPAPI GdipSetPathGradientPresetBlend(GpPathGradient *brush,
1502     GDIPCONST ARGB *blend, GDIPCONST REAL *pos, INT count)
1503 {
1504     ARGB *new_color;
1505     REAL *new_pos;
1506     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1507
1508     if (!brush || !blend || !pos || count < 2 ||
1509         pos[0] != 0.0f || pos[count-1] != 1.0f)
1510     {
1511         return InvalidParameter;
1512     }
1513
1514     new_color = GdipAlloc(count * sizeof(ARGB));
1515     new_pos = GdipAlloc(count * sizeof(REAL));
1516     if (!new_color || !new_pos)
1517     {
1518         GdipFree(new_color);
1519         GdipFree(new_pos);
1520         return OutOfMemory;
1521     }
1522
1523     memcpy(new_color, blend, sizeof(ARGB) * count);
1524     memcpy(new_pos, pos, sizeof(REAL) * count);
1525
1526     GdipFree(brush->pblendcolor);
1527     GdipFree(brush->pblendpos);
1528
1529     brush->pblendcolor = new_color;
1530     brush->pblendpos = new_pos;
1531     brush->pblendcount = count;
1532
1533     return Ok;
1534 }
1535
1536 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlend(GpPathGradient *brush,
1537     ARGB *blend, REAL *pos, INT count)
1538 {
1539     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1540
1541     if (count < 0)
1542         return OutOfMemory;
1543
1544     if (!brush || !blend || !pos || count < 2)
1545         return InvalidParameter;
1546
1547     if (brush->pblendcount == 0)
1548         return GenericError;
1549
1550     if (count != brush->pblendcount)
1551     {
1552         /* Native lines up the ends of each array, and copies the destination size. */
1553         FIXME("Braindead behavior on wrong-sized buffer not implemented.\n");
1554         return InvalidParameter;
1555     }
1556
1557     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1558     memcpy(pos, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1559
1560     return Ok;
1561 }
1562
1563 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlendCount(GpPathGradient *brush,
1564     INT *count)
1565 {
1566     TRACE("(%p,%p)\n", brush, count);
1567
1568     if (!brush || !count)
1569         return InvalidParameter;
1570
1571     *count = brush->pblendcount;
1572
1573     return Ok;
1574 }
1575
1576 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1577     ARGB argb)
1578 {
1579     TRACE("(%p, %x)\n", grad, argb);
1580
1581     if(!grad)
1582         return InvalidParameter;
1583
1584     grad->centercolor = argb;
1585     return Ok;
1586 }
1587
1588 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1589     GpPointF *point)
1590 {
1591     TRACE("(%p, %s)\n", grad, debugstr_pointf(point));
1592
1593     if(!grad || !point)
1594         return InvalidParameter;
1595
1596     grad->center.X = point->X;
1597     grad->center.Y = point->Y;
1598
1599     return Ok;
1600 }
1601
1602 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1603     GpPoint *point)
1604 {
1605     GpPointF ptf;
1606
1607     TRACE("(%p, %p)\n", grad, point);
1608
1609     if(!point)
1610         return InvalidParameter;
1611
1612     ptf.X = (REAL)point->X;
1613     ptf.Y = (REAL)point->Y;
1614
1615     return GdipSetPathGradientCenterPoint(grad,&ptf);
1616 }
1617
1618 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1619     REAL x, REAL y)
1620 {
1621     TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1622
1623     if(!grad)
1624         return InvalidParameter;
1625
1626     grad->focus.X = x;
1627     grad->focus.Y = y;
1628
1629     return Ok;
1630 }
1631
1632 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1633     BOOL gamma)
1634 {
1635     TRACE("(%p, %d)\n", grad, gamma);
1636
1637     if(!grad)
1638         return InvalidParameter;
1639
1640     grad->gamma = gamma;
1641
1642     return Ok;
1643 }
1644
1645 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1646     REAL focus, REAL scale)
1647 {
1648     REAL factors[33];
1649     REAL positions[33];
1650     int num_points = 0;
1651     int i;
1652     const int precision = 16;
1653     REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1654     REAL min_erf;
1655     REAL scale_erf;
1656
1657     TRACE("(%p,%0.2f,%0.2f)\n", grad, focus, scale);
1658
1659     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
1660         return InvalidParameter;
1661
1662     /* we want 2 standard deviations */
1663     erf_range = 2.0 / sqrt(2);
1664
1665     /* calculate the constants we need to normalize the error function to be
1666         between 0.0 and scale over the range we need */
1667     min_erf = erf(-erf_range);
1668     scale_erf = scale / (-2.0 * min_erf);
1669
1670     if (focus != 0.0)
1671     {
1672         positions[0] = 0.0;
1673         factors[0] = 0.0;
1674         for (i=1; i<precision; i++)
1675         {
1676             positions[i] = focus * i / precision;
1677             factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1678         }
1679         num_points += precision;
1680     }
1681
1682     positions[num_points] = focus;
1683     factors[num_points] = scale;
1684     num_points += 1;
1685
1686     if (focus != 1.0)
1687     {
1688         for (i=1; i<precision; i++)
1689         {
1690             positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1691             factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1692         }
1693         num_points += precision;
1694         positions[num_points-1] = 1.0;
1695         factors[num_points-1] = 0.0;
1696     }
1697
1698     return GdipSetPathGradientBlend(grad, factors, positions, num_points);
1699 }
1700
1701 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1702     *grad, GDIPCONST ARGB *argb, INT *count)
1703 {
1704     ARGB *new_surroundcolors;
1705     INT i, num_colors;
1706
1707     TRACE("(%p,%p,%p)\n", grad, argb, count);
1708
1709     if(!grad || !argb || !count || (*count <= 0) ||
1710         (*count > grad->path->pathdata.Count))
1711         return InvalidParameter;
1712
1713     num_colors = *count;
1714
1715     /* If all colors are the same, only store 1 color. */
1716     if (*count > 1)
1717     {
1718         for (i=1; i < num_colors; i++)
1719             if (argb[i] != argb[i-1])
1720                 break;
1721
1722         if (i == num_colors)
1723             num_colors = 1;
1724     }
1725
1726     new_surroundcolors = GdipAlloc(num_colors * sizeof(ARGB));
1727     if (!new_surroundcolors)
1728         return OutOfMemory;
1729
1730     memcpy(new_surroundcolors, argb, num_colors * sizeof(ARGB));
1731
1732     GdipFree(grad->surroundcolors);
1733
1734     grad->surroundcolors = new_surroundcolors;
1735     grad->surroundcolorcount = num_colors;
1736
1737     return Ok;
1738 }
1739
1740 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1741     GpWrapMode wrap)
1742 {
1743     TRACE("(%p, %d)\n", grad, wrap);
1744
1745     if(!grad)
1746         return InvalidParameter;
1747
1748     grad->wrap = wrap;
1749
1750     return Ok;
1751 }
1752
1753 GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
1754     GpMatrix *matrix)
1755 {
1756     TRACE("(%p,%p)\n", grad, matrix);
1757
1758     if (!grad || !matrix)
1759         return InvalidParameter;
1760
1761     memcpy(grad->transform, matrix, sizeof(GpMatrix));
1762
1763     return Ok;
1764 }
1765
1766 GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
1767     GpMatrix *matrix)
1768 {
1769     TRACE("(%p,%p)\n", grad, matrix);
1770
1771     if (!grad || !matrix)
1772         return InvalidParameter;
1773
1774     memcpy(matrix, grad->transform, sizeof(GpMatrix));
1775
1776     return Ok;
1777 }
1778
1779 GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
1780     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1781 {
1782     TRACE("(%p,%p,%i)\n", grad, matrix, order);
1783
1784     if (!grad)
1785         return InvalidParameter;
1786
1787     return GdipMultiplyMatrix(grad->transform, matrix, order);
1788 }
1789
1790 GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
1791 {
1792     TRACE("(%p)\n", grad);
1793
1794     if (!grad)
1795         return InvalidParameter;
1796
1797     return GdipSetMatrixElements(grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1798 }
1799
1800 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
1801     REAL angle, GpMatrixOrder order)
1802 {
1803     TRACE("(%p,%0.2f,%i)\n", grad, angle, order);
1804
1805     if (!grad)
1806         return InvalidParameter;
1807
1808     return GdipRotateMatrix(grad->transform, angle, order);
1809 }
1810
1811 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
1812     REAL sx, REAL sy, GpMatrixOrder order)
1813 {
1814     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, sx, sy, order);
1815
1816     if (!grad)
1817         return InvalidParameter;
1818
1819     return GdipScaleMatrix(grad->transform, sx, sy, order);
1820 }
1821
1822 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
1823     REAL dx, REAL dy, GpMatrixOrder order)
1824 {
1825     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, dx, dy, order);
1826
1827     if (!grad)
1828         return InvalidParameter;
1829
1830     return GdipTranslateMatrix(grad->transform, dx, dy, order);
1831 }
1832
1833 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1834 {
1835     TRACE("(%p, %x)\n", sf, argb);
1836
1837     if(!sf)
1838         return InvalidParameter;
1839
1840     sf->color = argb;
1841     return Ok;
1842 }
1843
1844 /******************************************************************************
1845  * GdipSetTextureTransform [GDIPLUS.@]
1846  */
1847 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1848     GDIPCONST GpMatrix *matrix)
1849 {
1850     TRACE("(%p, %p)\n", texture, matrix);
1851
1852     if(!texture || !matrix)
1853         return InvalidParameter;
1854
1855     memcpy(texture->transform, matrix, sizeof(GpMatrix));
1856
1857     return Ok;
1858 }
1859
1860 /******************************************************************************
1861  * GdipSetTextureWrapMode [GDIPLUS.@]
1862  *
1863  * WrapMode not used, only stored
1864  */
1865 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1866 {
1867     TRACE("(%p, %d)\n", brush, wrapmode);
1868
1869     if(!brush)
1870         return InvalidParameter;
1871
1872     brush->imageattributes->wrap = wrapmode;
1873
1874     return Ok;
1875 }
1876
1877 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1878     ARGB color2)
1879 {
1880     TRACE("(%p, %x, %x)\n", brush, color1, color2);
1881
1882     if(!brush)
1883         return InvalidParameter;
1884
1885     brush->startcolor = color1;
1886     brush->endcolor   = color2;
1887
1888     return Ok;
1889 }
1890
1891 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1892 {
1893     TRACE("(%p, %p)\n", brush, colors);
1894
1895     if(!brush || !colors)
1896         return InvalidParameter;
1897
1898     colors[0] = brush->startcolor;
1899     colors[1] = brush->endcolor;
1900
1901     return Ok;
1902 }
1903
1904 /******************************************************************************
1905  * GdipRotateTextureTransform [GDIPLUS.@]
1906  */
1907 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
1908     GpMatrixOrder order)
1909 {
1910     TRACE("(%p, %.2f, %d)\n", brush, angle, order);
1911
1912     if(!brush)
1913         return InvalidParameter;
1914
1915     return GdipRotateMatrix(brush->transform, angle, order);
1916 }
1917
1918 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
1919     REAL scale)
1920 {
1921     REAL factors[3];
1922     REAL positions[3];
1923     int num_points = 0;
1924
1925     TRACE("(%p,%.2f,%.2f)\n", brush, focus, scale);
1926
1927     if (!brush) return InvalidParameter;
1928
1929     if (focus != 0.0)
1930     {
1931         factors[num_points] = 0.0;
1932         positions[num_points] = 0.0;
1933         num_points++;
1934     }
1935
1936     factors[num_points] = scale;
1937     positions[num_points] = focus;
1938     num_points++;
1939
1940     if (focus != 1.0)
1941     {
1942         factors[num_points] = 0.0;
1943         positions[num_points] = 1.0;
1944         num_points++;
1945     }
1946
1947     return GdipSetLineBlend(brush, factors, positions, num_points);
1948 }
1949
1950 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
1951     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
1952 {
1953     ARGB *new_color;
1954     REAL *new_pos;
1955     TRACE("(%p,%p,%p,%i)\n", brush, blend, positions, count);
1956
1957     if (!brush || !blend || !positions || count < 2 ||
1958         positions[0] != 0.0f || positions[count-1] != 1.0f)
1959     {
1960         return InvalidParameter;
1961     }
1962
1963     new_color = GdipAlloc(count * sizeof(ARGB));
1964     new_pos = GdipAlloc(count * sizeof(REAL));
1965     if (!new_color || !new_pos)
1966     {
1967         GdipFree(new_color);
1968         GdipFree(new_pos);
1969         return OutOfMemory;
1970     }
1971
1972     memcpy(new_color, blend, sizeof(ARGB) * count);
1973     memcpy(new_pos, positions, sizeof(REAL) * count);
1974
1975     GdipFree(brush->pblendcolor);
1976     GdipFree(brush->pblendpos);
1977
1978     brush->pblendcolor = new_color;
1979     brush->pblendpos = new_pos;
1980     brush->pblendcount = count;
1981
1982     return Ok;
1983 }
1984
1985 GpStatus WINGDIPAPI GdipGetLinePresetBlend(GpLineGradient *brush,
1986     ARGB *blend, REAL* positions, INT count)
1987 {
1988     if (!brush || !blend || !positions || count < 2)
1989         return InvalidParameter;
1990
1991     if (brush->pblendcount == 0)
1992         return GenericError;
1993
1994     if (count < brush->pblendcount)
1995         return InsufficientBuffer;
1996
1997     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1998     memcpy(positions, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1999
2000     return Ok;
2001 }
2002
2003 GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
2004     INT *count)
2005 {
2006     if (!brush || !count)
2007         return InvalidParameter;
2008
2009     *count = brush->pblendcount;
2010
2011     return Ok;
2012 }
2013
2014 GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
2015 {
2016     static int calls;
2017
2018     TRACE("(%p)\n", brush);
2019
2020     if(!(calls++))
2021         FIXME("not implemented\n");
2022
2023     return NotImplemented;
2024 }
2025
2026 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
2027     GDIPCONST GpMatrix *matrix)
2028 {
2029     static int calls;
2030
2031     TRACE("(%p,%p)\n", brush,  matrix);
2032
2033     if(!(calls++))
2034         FIXME("not implemented\n");
2035
2036     return NotImplemented;
2037 }
2038
2039 GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
2040 {
2041     static int calls;
2042
2043     TRACE("(%p,%p)\n", brush, matrix);
2044
2045     if(!(calls++))
2046         FIXME("not implemented\n");
2047
2048     return NotImplemented;
2049 }
2050
2051 GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
2052     GpMatrixOrder order)
2053 {
2054     static int calls;
2055
2056     TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
2057
2058     if(!(calls++))
2059         FIXME("not implemented\n");
2060
2061     return NotImplemented;
2062 }
2063
2064 GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
2065     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
2066 {
2067     static int calls;
2068
2069     TRACE("(%p,%p,%u)\n", brush, matrix, order);
2070
2071     if(!(calls++))
2072         FIXME("not implemented\n");
2073
2074     return NotImplemented;
2075 }
2076
2077 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient* brush,
2078         REAL dx, REAL dy, GpMatrixOrder order)
2079 {
2080     static int calls;
2081
2082     TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
2083
2084     if(!(calls++))
2085         FIXME("not implemented\n");
2086
2087     return Ok;
2088 }
2089
2090 /******************************************************************************
2091  * GdipTranslateTextureTransform [GDIPLUS.@]
2092  */
2093 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
2094     GpMatrixOrder order)
2095 {
2096     TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
2097
2098     if(!brush)
2099         return InvalidParameter;
2100
2101     return GdipTranslateMatrix(brush->transform, dx, dy, order);
2102 }
2103
2104 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
2105 {
2106     TRACE("(%p, %p)\n", brush, rect);
2107
2108     if(!brush || !rect)
2109         return InvalidParameter;
2110
2111     *rect = brush->rect;
2112
2113     return Ok;
2114 }
2115
2116 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
2117 {
2118     GpRectF  rectF;
2119     GpStatus ret;
2120
2121     TRACE("(%p, %p)\n", brush, rect);
2122
2123     if(!rect)
2124         return InvalidParameter;
2125
2126     ret = GdipGetLineRect(brush, &rectF);
2127
2128     if(ret == Ok){
2129         rect->X      = roundr(rectF.X);
2130         rect->Y      = roundr(rectF.Y);
2131         rect->Width  = roundr(rectF.Width);
2132         rect->Height = roundr(rectF.Height);
2133     }
2134
2135     return ret;
2136 }
2137
2138 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
2139     REAL angle, GpMatrixOrder order)
2140 {
2141     static int calls;
2142
2143     TRACE("(%p,%0.2f,%u)\n", brush, angle, order);
2144
2145     if(!brush)
2146         return InvalidParameter;
2147
2148     if(!(calls++))
2149         FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
2150
2151     return NotImplemented;
2152 }