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