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