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