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