winhttp: Add a read-ahead buffer to allow WinHttpQueryDataAvailable to return the...
[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 || brush->brush.bt != BrushTypeLinearGradient)
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 || brush->brush.bt != BrushTypePathGradient)
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 || brush->brush.bt != BrushTypePathGradient)
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 || grad->brush.bt != BrushTypePathGradient)
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 || grad->brush.bt != BrushTypePathGradient)
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 || grad->brush.bt != BrushTypePathGradient)
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 || grad->brush.bt != BrushTypePathGradient)
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 || grad->brush.bt != BrushTypePathGradient)
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 || brush->brush.bt != BrushTypePathGradient)
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) || grad->brush.bt != BrushTypePathGradient)
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 || brush->brush.bt != BrushTypePathGradient)
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 || brush->brush.bt != BrushTypePathGradient)
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 || brush->brush.bt != BrushTypeLinearGradient ||
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 || brush->brush.bt != BrushTypeLinearGradient)
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 || brush->brush.bt != BrushTypeLinearGradient)
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 || line->brush.bt != BrushTypeLinearGradient)
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 || line->brush.bt != BrushTypeLinearGradient)
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 || line->brush.bt != BrushTypeLinearGradient)
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 || brush->brush.bt != BrushTypePathGradient ||
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 || brush->brush.bt != BrushTypePathGradient)
1460         return InvalidParameter;
1461
1462     if (focus != 0.0)
1463     {
1464         factors[num_points] = 0.0;
1465         positions[num_points] = 0.0;
1466         num_points++;
1467     }
1468
1469     factors[num_points] = scale;
1470     positions[num_points] = focus;
1471     num_points++;
1472
1473     if (focus != 1.0)
1474     {
1475         factors[num_points] = 0.0;
1476         positions[num_points] = 1.0;
1477         num_points++;
1478     }
1479
1480     return GdipSetPathGradientBlend(brush, factors, positions, num_points);
1481 }
1482
1483 GpStatus WINGDIPAPI GdipSetPathGradientPresetBlend(GpPathGradient *brush,
1484     GDIPCONST ARGB *blend, GDIPCONST REAL *pos, INT count)
1485 {
1486     ARGB *new_color;
1487     REAL *new_pos;
1488     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1489
1490     if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient ||
1491         pos[0] != 0.0f || pos[count-1] != 1.0f)
1492     {
1493         return InvalidParameter;
1494     }
1495
1496     new_color = GdipAlloc(count * sizeof(ARGB));
1497     new_pos = GdipAlloc(count * sizeof(REAL));
1498     if (!new_color || !new_pos)
1499     {
1500         GdipFree(new_color);
1501         GdipFree(new_pos);
1502         return OutOfMemory;
1503     }
1504
1505     memcpy(new_color, blend, sizeof(ARGB) * count);
1506     memcpy(new_pos, pos, sizeof(REAL) * count);
1507
1508     GdipFree(brush->pblendcolor);
1509     GdipFree(brush->pblendpos);
1510
1511     brush->pblendcolor = new_color;
1512     brush->pblendpos = new_pos;
1513     brush->pblendcount = count;
1514
1515     return Ok;
1516 }
1517
1518 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlend(GpPathGradient *brush,
1519     ARGB *blend, REAL *pos, INT count)
1520 {
1521     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1522
1523     if (count < 0)
1524         return OutOfMemory;
1525
1526     if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient)
1527         return InvalidParameter;
1528
1529     if (brush->pblendcount == 0)
1530         return GenericError;
1531
1532     if (count != brush->pblendcount)
1533     {
1534         /* Native lines up the ends of each array, and copies the destination size. */
1535         FIXME("Braindead behavior on wrong-sized buffer not implemented.\n");
1536         return InvalidParameter;
1537     }
1538
1539     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1540     memcpy(pos, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1541
1542     return Ok;
1543 }
1544
1545 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlendCount(GpPathGradient *brush,
1546     INT *count)
1547 {
1548     TRACE("(%p,%p)\n", brush, count);
1549
1550     if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1551         return InvalidParameter;
1552
1553     *count = brush->pblendcount;
1554
1555     return Ok;
1556 }
1557
1558 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1559     ARGB argb)
1560 {
1561     TRACE("(%p, %x)\n", grad, argb);
1562
1563     if(!grad || grad->brush.bt != BrushTypePathGradient)
1564         return InvalidParameter;
1565
1566     grad->centercolor = argb;
1567     return Ok;
1568 }
1569
1570 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1571     GpPointF *point)
1572 {
1573     TRACE("(%p, %s)\n", grad, debugstr_pointf(point));
1574
1575     if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1576         return InvalidParameter;
1577
1578     grad->center.X = point->X;
1579     grad->center.Y = point->Y;
1580
1581     return Ok;
1582 }
1583
1584 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1585     GpPoint *point)
1586 {
1587     GpPointF ptf;
1588
1589     TRACE("(%p, %p)\n", grad, point);
1590
1591     if(!point)
1592         return InvalidParameter;
1593
1594     ptf.X = (REAL)point->X;
1595     ptf.Y = (REAL)point->Y;
1596
1597     return GdipSetPathGradientCenterPoint(grad,&ptf);
1598 }
1599
1600 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1601     REAL x, REAL y)
1602 {
1603     TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1604
1605     if(!grad || grad->brush.bt != BrushTypePathGradient)
1606         return InvalidParameter;
1607
1608     grad->focus.X = x;
1609     grad->focus.Y = y;
1610
1611     return Ok;
1612 }
1613
1614 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1615     BOOL gamma)
1616 {
1617     TRACE("(%p, %d)\n", grad, gamma);
1618
1619     if(!grad || grad->brush.bt != BrushTypePathGradient)
1620         return InvalidParameter;
1621
1622     grad->gamma = gamma;
1623
1624     return Ok;
1625 }
1626
1627 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1628     REAL focus, REAL scale)
1629 {
1630     REAL factors[33];
1631     REAL positions[33];
1632     int num_points = 0;
1633     int i;
1634     const int precision = 16;
1635     REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1636     REAL min_erf;
1637     REAL scale_erf;
1638
1639     TRACE("(%p,%0.2f,%0.2f)\n", grad, focus, scale);
1640
1641     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || grad->brush.bt != BrushTypePathGradient)
1642         return InvalidParameter;
1643
1644     /* we want 2 standard deviations */
1645     erf_range = 2.0 / sqrt(2);
1646
1647     /* calculate the constants we need to normalize the error function to be
1648         between 0.0 and scale over the range we need */
1649     min_erf = erf(-erf_range);
1650     scale_erf = scale / (-2.0 * min_erf);
1651
1652     if (focus != 0.0)
1653     {
1654         positions[0] = 0.0;
1655         factors[0] = 0.0;
1656         for (i=1; i<precision; i++)
1657         {
1658             positions[i] = focus * i / precision;
1659             factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1660         }
1661         num_points += precision;
1662     }
1663
1664     positions[num_points] = focus;
1665     factors[num_points] = scale;
1666     num_points += 1;
1667
1668     if (focus != 1.0)
1669     {
1670         for (i=1; i<precision; i++)
1671         {
1672             positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1673             factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1674         }
1675         num_points += precision;
1676         positions[num_points-1] = 1.0;
1677         factors[num_points-1] = 0.0;
1678     }
1679
1680     return GdipSetPathGradientBlend(grad, factors, positions, num_points);
1681 }
1682
1683 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1684     *grad, GDIPCONST ARGB *argb, INT *count)
1685 {
1686     ARGB *new_surroundcolors;
1687     INT i, num_colors;
1688
1689     TRACE("(%p,%p,%p)\n", grad, argb, count);
1690
1691     if(!grad || !argb || !count || (*count <= 0) || grad->brush.bt != BrushTypePathGradient ||
1692         (*count > grad->path->pathdata.Count))
1693         return InvalidParameter;
1694
1695     num_colors = *count;
1696
1697     /* If all colors are the same, only store 1 color. */
1698     if (*count > 1)
1699     {
1700         for (i=1; i < num_colors; i++)
1701             if (argb[i] != argb[i-1])
1702                 break;
1703
1704         if (i == num_colors)
1705             num_colors = 1;
1706     }
1707
1708     new_surroundcolors = GdipAlloc(num_colors * sizeof(ARGB));
1709     if (!new_surroundcolors)
1710         return OutOfMemory;
1711
1712     memcpy(new_surroundcolors, argb, num_colors * sizeof(ARGB));
1713
1714     GdipFree(grad->surroundcolors);
1715
1716     grad->surroundcolors = new_surroundcolors;
1717     grad->surroundcolorcount = num_colors;
1718
1719     return Ok;
1720 }
1721
1722 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1723     GpWrapMode wrap)
1724 {
1725     TRACE("(%p, %d)\n", grad, wrap);
1726
1727     if(!grad || grad->brush.bt != BrushTypePathGradient)
1728         return InvalidParameter;
1729
1730     grad->wrap = wrap;
1731
1732     return Ok;
1733 }
1734
1735 GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
1736     GpMatrix *matrix)
1737 {
1738     TRACE("(%p,%p)\n", grad, matrix);
1739
1740     if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1741         return InvalidParameter;
1742
1743     grad->transform = *matrix;
1744
1745     return Ok;
1746 }
1747
1748 GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
1749     GpMatrix *matrix)
1750 {
1751     TRACE("(%p,%p)\n", grad, matrix);
1752
1753     if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1754         return InvalidParameter;
1755
1756     *matrix = grad->transform;
1757
1758     return Ok;
1759 }
1760
1761 GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
1762     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1763 {
1764     TRACE("(%p,%p,%i)\n", grad, matrix, order);
1765
1766     if (!grad || grad->brush.bt != BrushTypePathGradient)
1767         return InvalidParameter;
1768
1769     return GdipMultiplyMatrix(&grad->transform, matrix, order);
1770 }
1771
1772 GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
1773 {
1774     TRACE("(%p)\n", grad);
1775
1776     if (!grad || grad->brush.bt != BrushTypePathGradient)
1777         return InvalidParameter;
1778
1779     return GdipSetMatrixElements(&grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1780 }
1781
1782 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
1783     REAL angle, GpMatrixOrder order)
1784 {
1785     TRACE("(%p,%0.2f,%i)\n", grad, angle, order);
1786
1787     if (!grad || grad->brush.bt != BrushTypePathGradient)
1788         return InvalidParameter;
1789
1790     return GdipRotateMatrix(&grad->transform, angle, order);
1791 }
1792
1793 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
1794     REAL sx, REAL sy, GpMatrixOrder order)
1795 {
1796     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, sx, sy, order);
1797
1798     if (!grad || grad->brush.bt != BrushTypePathGradient)
1799         return InvalidParameter;
1800
1801     return GdipScaleMatrix(&grad->transform, sx, sy, order);
1802 }
1803
1804 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
1805     REAL dx, REAL dy, GpMatrixOrder order)
1806 {
1807     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, dx, dy, order);
1808
1809     if (!grad || grad->brush.bt != BrushTypePathGradient)
1810         return InvalidParameter;
1811
1812     return GdipTranslateMatrix(&grad->transform, dx, dy, order);
1813 }
1814
1815 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1816 {
1817     TRACE("(%p, %x)\n", sf, argb);
1818
1819     if(!sf)
1820         return InvalidParameter;
1821
1822     sf->color = argb;
1823     return Ok;
1824 }
1825
1826 /******************************************************************************
1827  * GdipSetTextureTransform [GDIPLUS.@]
1828  */
1829 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1830     GDIPCONST GpMatrix *matrix)
1831 {
1832     TRACE("(%p, %p)\n", texture, matrix);
1833
1834     if(!texture || !matrix)
1835         return InvalidParameter;
1836
1837     texture->transform = *matrix;
1838
1839     return Ok;
1840 }
1841
1842 /******************************************************************************
1843  * GdipSetTextureWrapMode [GDIPLUS.@]
1844  *
1845  * WrapMode not used, only stored
1846  */
1847 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1848 {
1849     TRACE("(%p, %d)\n", brush, wrapmode);
1850
1851     if(!brush)
1852         return InvalidParameter;
1853
1854     brush->imageattributes->wrap = wrapmode;
1855
1856     return Ok;
1857 }
1858
1859 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1860     ARGB color2)
1861 {
1862     TRACE("(%p, %x, %x)\n", brush, color1, color2);
1863
1864     if(!brush || brush->brush.bt != BrushTypeLinearGradient)
1865         return InvalidParameter;
1866
1867     brush->startcolor = color1;
1868     brush->endcolor   = color2;
1869
1870     return Ok;
1871 }
1872
1873 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1874 {
1875     TRACE("(%p, %p)\n", brush, colors);
1876
1877     if(!brush || !colors || brush->brush.bt != BrushTypeLinearGradient)
1878         return InvalidParameter;
1879
1880     colors[0] = brush->startcolor;
1881     colors[1] = brush->endcolor;
1882
1883     return Ok;
1884 }
1885
1886 /******************************************************************************
1887  * GdipRotateTextureTransform [GDIPLUS.@]
1888  */
1889 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
1890     GpMatrixOrder order)
1891 {
1892     TRACE("(%p, %.2f, %d)\n", brush, angle, order);
1893
1894     if(!brush)
1895         return InvalidParameter;
1896
1897     return GdipRotateMatrix(&brush->transform, angle, order);
1898 }
1899
1900 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
1901     REAL scale)
1902 {
1903     REAL factors[3];
1904     REAL positions[3];
1905     int num_points = 0;
1906
1907     TRACE("(%p,%.2f,%.2f)\n", brush, focus, scale);
1908
1909     if (!brush) return InvalidParameter;
1910
1911     if (focus != 0.0)
1912     {
1913         factors[num_points] = 0.0;
1914         positions[num_points] = 0.0;
1915         num_points++;
1916     }
1917
1918     factors[num_points] = scale;
1919     positions[num_points] = focus;
1920     num_points++;
1921
1922     if (focus != 1.0)
1923     {
1924         factors[num_points] = 0.0;
1925         positions[num_points] = 1.0;
1926         num_points++;
1927     }
1928
1929     return GdipSetLineBlend(brush, factors, positions, num_points);
1930 }
1931
1932 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
1933     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
1934 {
1935     ARGB *new_color;
1936     REAL *new_pos;
1937     TRACE("(%p,%p,%p,%i)\n", brush, blend, positions, count);
1938
1939     if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient ||
1940         positions[0] != 0.0f || positions[count-1] != 1.0f)
1941     {
1942         return InvalidParameter;
1943     }
1944
1945     new_color = GdipAlloc(count * sizeof(ARGB));
1946     new_pos = GdipAlloc(count * sizeof(REAL));
1947     if (!new_color || !new_pos)
1948     {
1949         GdipFree(new_color);
1950         GdipFree(new_pos);
1951         return OutOfMemory;
1952     }
1953
1954     memcpy(new_color, blend, sizeof(ARGB) * count);
1955     memcpy(new_pos, positions, sizeof(REAL) * count);
1956
1957     GdipFree(brush->pblendcolor);
1958     GdipFree(brush->pblendpos);
1959
1960     brush->pblendcolor = new_color;
1961     brush->pblendpos = new_pos;
1962     brush->pblendcount = count;
1963
1964     return Ok;
1965 }
1966
1967 GpStatus WINGDIPAPI GdipGetLinePresetBlend(GpLineGradient *brush,
1968     ARGB *blend, REAL* positions, INT count)
1969 {
1970     if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient)
1971         return InvalidParameter;
1972
1973     if (brush->pblendcount == 0)
1974         return GenericError;
1975
1976     if (count < brush->pblendcount)
1977         return InsufficientBuffer;
1978
1979     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1980     memcpy(positions, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1981
1982     return Ok;
1983 }
1984
1985 GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
1986     INT *count)
1987 {
1988     if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
1989         return InvalidParameter;
1990
1991     *count = brush->pblendcount;
1992
1993     return Ok;
1994 }
1995
1996 GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
1997 {
1998     static int calls;
1999
2000     TRACE("(%p)\n", brush);
2001
2002     if(!(calls++))
2003         FIXME("not implemented\n");
2004
2005     return NotImplemented;
2006 }
2007
2008 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
2009     GDIPCONST GpMatrix *matrix)
2010 {
2011     static int calls;
2012
2013     TRACE("(%p,%p)\n", brush,  matrix);
2014
2015     if(!(calls++))
2016         FIXME("not implemented\n");
2017
2018     return NotImplemented;
2019 }
2020
2021 GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
2022 {
2023     static int calls;
2024
2025     TRACE("(%p,%p)\n", brush, matrix);
2026
2027     if(!(calls++))
2028         FIXME("not implemented\n");
2029
2030     return NotImplemented;
2031 }
2032
2033 GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
2034     GpMatrixOrder order)
2035 {
2036     static int calls;
2037
2038     TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
2039
2040     if(!(calls++))
2041         FIXME("not implemented\n");
2042
2043     return NotImplemented;
2044 }
2045
2046 GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
2047     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
2048 {
2049     static int calls;
2050
2051     TRACE("(%p,%p,%u)\n", brush, matrix, order);
2052
2053     if(!(calls++))
2054         FIXME("not implemented\n");
2055
2056     return NotImplemented;
2057 }
2058
2059 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient* brush,
2060         REAL dx, REAL dy, GpMatrixOrder order)
2061 {
2062     static int calls;
2063
2064     TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
2065
2066     if(!(calls++))
2067         FIXME("not implemented\n");
2068
2069     return Ok;
2070 }
2071
2072 /******************************************************************************
2073  * GdipTranslateTextureTransform [GDIPLUS.@]
2074  */
2075 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
2076     GpMatrixOrder order)
2077 {
2078     TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
2079
2080     if(!brush)
2081         return InvalidParameter;
2082
2083     return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2084 }
2085
2086 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
2087 {
2088     TRACE("(%p, %p)\n", brush, rect);
2089
2090     if(!brush || !rect || brush->brush.bt != BrushTypeLinearGradient)
2091         return InvalidParameter;
2092
2093     *rect = brush->rect;
2094
2095     return Ok;
2096 }
2097
2098 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
2099 {
2100     GpRectF  rectF;
2101     GpStatus ret;
2102
2103     TRACE("(%p, %p)\n", brush, rect);
2104
2105     if(!rect)
2106         return InvalidParameter;
2107
2108     ret = GdipGetLineRect(brush, &rectF);
2109
2110     if(ret == Ok){
2111         rect->X      = gdip_round(rectF.X);
2112         rect->Y      = gdip_round(rectF.Y);
2113         rect->Width  = gdip_round(rectF.Width);
2114         rect->Height = gdip_round(rectF.Height);
2115     }
2116
2117     return ret;
2118 }
2119
2120 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
2121     REAL angle, GpMatrixOrder order)
2122 {
2123     static int calls;
2124
2125     TRACE("(%p,%0.2f,%u)\n", brush, angle, order);
2126
2127     if(!brush || brush->brush.bt != BrushTypeLinearGradient)
2128         return InvalidParameter;
2129
2130     if(!(calls++))
2131         FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
2132
2133     return NotImplemented;
2134 }