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