wined3d: Make the alphaop == colorop matching a bit smarter.
[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 GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
38 {
39     if(!brush || !clone)
40         return InvalidParameter;
41
42     switch(brush->bt){
43         case BrushTypeSolidColor:
44             *clone = GdipAlloc(sizeof(GpSolidFill));
45             if (!*clone) return OutOfMemory;
46
47             memcpy(*clone, brush, sizeof(GpSolidFill));
48
49             (*clone)->gdibrush = CreateBrushIndirect(&(*clone)->lb);
50             break;
51         case BrushTypePathGradient:{
52             GpPathGradient *src, *dest;
53             INT count;
54
55             *clone = GdipAlloc(sizeof(GpPathGradient));
56             if (!*clone) return OutOfMemory;
57
58             src = (GpPathGradient*) brush,
59             dest = (GpPathGradient*) *clone;
60             count = src->pathdata.Count;
61
62             memcpy(dest, src, sizeof(GpPathGradient));
63
64             dest->pathdata.Count = count;
65             dest->pathdata.Points = GdipAlloc(count * sizeof(PointF));
66             dest->pathdata.Types = GdipAlloc(count);
67
68             if(!dest->pathdata.Points || !dest->pathdata.Types){
69                 GdipFree(dest->pathdata.Points);
70                 GdipFree(dest->pathdata.Types);
71                 GdipFree(dest);
72                 return OutOfMemory;
73             }
74
75             memcpy(dest->pathdata.Points, src->pathdata.Points, count * sizeof(PointF));
76             memcpy(dest->pathdata.Types, src->pathdata.Types, count);
77
78             /* blending */
79             count = src->blendcount;
80             dest->blendcount = count;
81             dest->blendfac = GdipAlloc(count * sizeof(REAL));
82             dest->blendpos = GdipAlloc(count * sizeof(REAL));
83
84             if(!dest->blendfac || !dest->blendpos){
85                 GdipFree(dest->pathdata.Points);
86                 GdipFree(dest->pathdata.Types);
87                 GdipFree(dest->blendfac);
88                 GdipFree(dest->blendpos);
89                 GdipFree(dest);
90                 return OutOfMemory;
91             }
92
93             memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
94             memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
95
96             break;
97         }
98         case BrushTypeLinearGradient:
99             *clone = GdipAlloc(sizeof(GpLineGradient));
100             if(!*clone)    return OutOfMemory;
101
102             memcpy(*clone, brush, sizeof(GpLineGradient));
103
104             (*clone)->gdibrush = CreateSolidBrush((*clone)->lb.lbColor);
105             break;
106         case BrushTypeTextureFill:
107             *clone = GdipAlloc(sizeof(GpTexture));
108             if(!*clone)    return OutOfMemory;
109
110             memcpy(*clone, brush, sizeof(GpTexture));
111
112             (*clone)->gdibrush = CreateBrushIndirect(&(*clone)->lb);
113             break;
114         default:
115             ERR("not implemented for brush type %d\n", brush->bt);
116             return NotImplemented;
117     }
118
119     return Ok;
120 }
121
122 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
123     GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
124     GpWrapMode wrap, GpLineGradient **line)
125 {
126     COLORREF col = ARGB2COLORREF(startcolor);
127
128     if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
129         return InvalidParameter;
130
131     *line = GdipAlloc(sizeof(GpLineGradient));
132     if(!*line)  return OutOfMemory;
133
134     (*line)->brush.lb.lbStyle = BS_SOLID;
135     (*line)->brush.lb.lbColor = col;
136     (*line)->brush.lb.lbHatch = 0;
137     (*line)->brush.gdibrush = CreateSolidBrush(col);
138     (*line)->brush.bt = BrushTypeLinearGradient;
139
140     (*line)->startpoint.X = startpoint->X;
141     (*line)->startpoint.Y = startpoint->Y;
142     (*line)->endpoint.X = endpoint->X;
143     (*line)->endpoint.Y = endpoint->Y;
144     (*line)->startcolor = startcolor;
145     (*line)->endcolor = endcolor;
146     (*line)->wrap = wrap;
147     (*line)->gamma = FALSE;
148
149     return Ok;
150 }
151
152 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
153     GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
154     GpWrapMode wrap, GpLineGradient **line)
155 {
156     GpPointF stF;
157     GpPointF endF;
158
159     if(!startpoint || !endpoint)
160         return InvalidParameter;
161
162     stF.X  = (REAL)startpoint->X;
163     stF.Y  = (REAL)startpoint->Y;
164     endF.X = (REAL)endpoint->X;
165     endF.X = (REAL)endpoint->Y;
166
167     return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
168 }
169
170 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
171     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
172     GpLineGradient **line)
173 {
174     GpPointF start, end;
175
176     if(!line || !rect)
177         return InvalidParameter;
178
179     start.X = rect->X;
180     start.Y = rect->Y;
181     end.X = rect->X + rect->Width;
182     end.Y = rect->Y + rect->Height;
183
184     return GdipCreateLineBrush(&start, &end, startcolor, endcolor, wrap, line);
185 }
186
187 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
188     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
189     GpLineGradient **line)
190 {
191     GpRectF rectF;
192
193     rectF.X      = (REAL) rect->X;
194     rectF.Y      = (REAL) rect->Y;
195     rectF.Width  = (REAL) rect->Width;
196     rectF.Height = (REAL) rect->Height;
197
198     return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
199 }
200
201 /* FIXME: angle value completely ignored. Don't know how to use it since native
202           always set Brush rectangle to rect (independetly of this angle).
203           Maybe it's used only on drawing.  */
204 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect,
205     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
206     GpLineGradient **line)
207 {
208     return GdipCreateLineBrushFromRect(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
209                                        wrap, line);
210 }
211
212 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect,
213     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
214     GpLineGradient **line)
215 {
216     return GdipCreateLineBrushFromRectI(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
217                                         wrap, line);
218 }
219
220 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
221     INT count, GpWrapMode wrap, GpPathGradient **grad)
222 {
223     COLORREF col = ARGB2COLORREF(0xffffffff);
224
225     if(!points || !grad)
226         return InvalidParameter;
227
228     if(count <= 0)
229         return OutOfMemory;
230
231     *grad = GdipAlloc(sizeof(GpPathGradient));
232     if (!*grad) return OutOfMemory;
233
234     (*grad)->blendfac = GdipAlloc(sizeof(REAL));
235     if(!(*grad)->blendfac){
236         GdipFree(*grad);
237         return OutOfMemory;
238     }
239     (*grad)->blendfac[0] = 1.0;
240     (*grad)->blendpos    = NULL;
241     (*grad)->blendcount  = 1;
242
243     (*grad)->pathdata.Count = count;
244     (*grad)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
245     (*grad)->pathdata.Types = GdipAlloc(count);
246
247     if(!(*grad)->pathdata.Points || !(*grad)->pathdata.Types){
248         GdipFree((*grad)->pathdata.Points);
249         GdipFree((*grad)->pathdata.Types);
250         GdipFree(*grad);
251         return OutOfMemory;
252     }
253
254     memcpy((*grad)->pathdata.Points, points, count * sizeof(PointF));
255     memset((*grad)->pathdata.Types, PathPointTypeLine, count);
256
257     (*grad)->brush.lb.lbStyle = BS_SOLID;
258     (*grad)->brush.lb.lbColor = col;
259     (*grad)->brush.lb.lbHatch = 0;
260
261     (*grad)->brush.gdibrush = CreateSolidBrush(col);
262     (*grad)->brush.bt = BrushTypePathGradient;
263     (*grad)->centercolor = 0xffffffff;
264     (*grad)->wrap = wrap;
265     (*grad)->gamma = FALSE;
266     (*grad)->center.X = 0.0;
267     (*grad)->center.Y = 0.0;
268     (*grad)->focus.X = 0.0;
269     (*grad)->focus.Y = 0.0;
270
271     return Ok;
272 }
273
274 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
275     INT count, GpWrapMode wrap, GpPathGradient **grad)
276 {
277     GpPointF *pointsF;
278     GpStatus ret;
279     INT i;
280
281     if(!points || !grad)
282         return InvalidParameter;
283
284     if(count <= 0)
285         return OutOfMemory;
286
287     pointsF = GdipAlloc(sizeof(GpPointF) * count);
288     if(!pointsF)
289         return OutOfMemory;
290
291     for(i = 0; i < count; i++){
292         pointsF[i].X = (REAL)points[i].X;
293         pointsF[i].Y = (REAL)points[i].Y;
294     }
295
296     ret = GdipCreatePathGradient(pointsF, count, wrap, grad);
297     GdipFree(pointsF);
298
299     return ret;
300 }
301
302 /* FIXME: path gradient brushes not truly supported (drawn as solid brushes) */
303 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
304     GpPathGradient **grad)
305 {
306     COLORREF col = ARGB2COLORREF(0xffffffff);
307
308     if(!path || !grad)
309         return InvalidParameter;
310
311     *grad = GdipAlloc(sizeof(GpPathGradient));
312     if (!*grad) return OutOfMemory;
313
314     (*grad)->blendfac = GdipAlloc(sizeof(REAL));
315     if(!(*grad)->blendfac){
316         GdipFree(*grad);
317         return OutOfMemory;
318     }
319     (*grad)->blendfac[0] = 1.0;
320     (*grad)->blendpos    = NULL;
321     (*grad)->blendcount  = 1;
322
323     (*grad)->pathdata.Count = path->pathdata.Count;
324     (*grad)->pathdata.Points = GdipAlloc(path->pathdata.Count * sizeof(PointF));
325     (*grad)->pathdata.Types = GdipAlloc(path->pathdata.Count);
326
327     if(!(*grad)->pathdata.Points || !(*grad)->pathdata.Types){
328         GdipFree((*grad)->pathdata.Points);
329         GdipFree((*grad)->pathdata.Types);
330         GdipFree(*grad);
331         return OutOfMemory;
332     }
333
334     memcpy((*grad)->pathdata.Points, path->pathdata.Points,
335            path->pathdata.Count * sizeof(PointF));
336     memcpy((*grad)->pathdata.Types, path->pathdata.Types, path->pathdata.Count);
337
338     (*grad)->brush.lb.lbStyle = BS_SOLID;
339     (*grad)->brush.lb.lbColor = col;
340     (*grad)->brush.lb.lbHatch = 0;
341
342     (*grad)->brush.gdibrush = CreateSolidBrush(col);
343     (*grad)->brush.bt = BrushTypePathGradient;
344     (*grad)->centercolor = 0xffffffff;
345     (*grad)->wrap = WrapModeClamp;
346     (*grad)->gamma = FALSE;
347     /* FIXME: this should be set to the "centroid" of the path by default */
348     (*grad)->center.X = 0.0;
349     (*grad)->center.Y = 0.0;
350     (*grad)->focus.X = 0.0;
351     (*grad)->focus.Y = 0.0;
352
353     return Ok;
354 }
355
356 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
357 {
358     COLORREF col = ARGB2COLORREF(color);
359
360     if(!sf)  return InvalidParameter;
361
362     *sf = GdipAlloc(sizeof(GpSolidFill));
363     if (!*sf) return OutOfMemory;
364
365     (*sf)->brush.lb.lbStyle = BS_SOLID;
366     (*sf)->brush.lb.lbColor = col;
367     (*sf)->brush.lb.lbHatch = 0;
368
369     (*sf)->brush.gdibrush = CreateSolidBrush(col);
370     (*sf)->brush.bt = BrushTypeSolidColor;
371     (*sf)->color = color;
372
373     return Ok;
374 }
375
376 /* FIXME: imageattr ignored */
377 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
378     GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
379     REAL height, GpTexture **texture)
380 {
381     HDC hdc;
382     OLE_HANDLE hbm;
383     HBITMAP old = NULL;
384     BITMAPINFO bmi;
385     BITMAPINFOHEADER *bmih;
386     INT n_x, n_y, n_width, n_height, abs_height, stride, image_stride, i, bytespp;
387     BOOL bm_is_selected;
388     BYTE *dibits, *buff, *textbits;
389
390     if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
391         return InvalidParameter;
392
393     if(image->type != ImageTypeBitmap){
394         FIXME("not implemented for image type %d\n", image->type);
395         return NotImplemented;
396     }
397
398     n_x = roundr(x);
399     n_y = roundr(y);
400     n_width = roundr(width);
401     n_height = roundr(height);
402
403     if(n_x + n_width > ((GpBitmap*)image)->width ||
404        n_y + n_height > ((GpBitmap*)image)->height)
405         return InvalidParameter;
406
407     IPicture_get_Handle(image->picture, &hbm);
408     if(!hbm)   return GenericError;
409     IPicture_get_CurDC(image->picture, &hdc);
410     bm_is_selected = (hdc != 0);
411
412     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
413     bmi.bmiHeader.biBitCount = 0;
414
415     if(!bm_is_selected){
416         hdc = CreateCompatibleDC(0);
417         old = SelectObject(hdc, (HBITMAP)hbm);
418     }
419
420     /* fill out bmi */
421     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
422
423     bytespp = bmi.bmiHeader.biBitCount / 8;
424     abs_height = abs(bmi.bmiHeader.biHeight);
425
426     if(n_x > bmi.bmiHeader.biWidth || n_x + n_width > bmi.bmiHeader.biWidth ||
427        n_y > abs_height || n_y + n_height > abs_height)
428         return InvalidParameter;
429
430     dibits = GdipAlloc(bmi.bmiHeader.biSizeImage);
431
432     if(dibits)  /* this is not a good place to error out */
433         GetDIBits(hdc, (HBITMAP)hbm, 0, abs_height, dibits, &bmi, DIB_RGB_COLORS);
434
435     if(!bm_is_selected){
436         SelectObject(hdc, old);
437         DeleteDC(hdc);
438     }
439
440     if(!dibits)
441         return OutOfMemory;
442
443     image_stride = (bmi.bmiHeader.biWidth * bytespp + 3) & ~3;
444     stride = (n_width * bytespp + 3) & ~3;
445     buff = GdipAlloc(sizeof(BITMAPINFOHEADER) + stride * n_height);
446     if(!buff){
447         GdipFree(dibits);
448         return OutOfMemory;
449     }
450
451     bmih = (BITMAPINFOHEADER*)buff;
452     textbits = (BYTE*) (bmih + 1);
453     bmih->biSize = sizeof(BITMAPINFOHEADER);
454     bmih->biWidth = n_width;
455     bmih->biHeight = n_height;
456     bmih->biCompression = BI_RGB;
457     bmih->biSizeImage = stride * n_height;
458     bmih->biBitCount = bmi.bmiHeader.biBitCount;
459     bmih->biClrUsed = 0;
460     bmih->biPlanes = 1;
461
462     /* image is flipped */
463     if(bmi.bmiHeader.biHeight > 0){
464         dibits += bmi.bmiHeader.biSizeImage;
465         image_stride *= -1;
466         textbits += stride * (n_height - 1);
467         stride *= -1;
468     }
469
470     for(i = 0; i < n_height; i++)
471         memcpy(&textbits[i * stride],
472                &dibits[n_x * bytespp + (n_y + i) * image_stride],
473                abs(stride));
474
475     *texture = GdipAlloc(sizeof(GpTexture));
476     if (!*texture) return OutOfMemory;
477
478     (*texture)->brush.lb.lbStyle = BS_DIBPATTERNPT;
479     (*texture)->brush.lb.lbColor = DIB_RGB_COLORS;
480     (*texture)->brush.lb.lbHatch = (ULONG_PTR)buff;
481
482     (*texture)->brush.gdibrush = CreateBrushIndirect(&(*texture)->brush.lb);
483     (*texture)->brush.bt = BrushTypeTextureFill;
484
485     GdipFree(dibits);
486     GdipFree(buff);
487
488     return Ok;
489 }
490
491 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
492     INT x, INT y, INT width, INT height, GpTexture **texture)
493 {
494     return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
495 }
496
497 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
498 {
499     if(!brush || !type)  return InvalidParameter;
500
501     *type = brush->bt;
502
503     return Ok;
504 }
505
506 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
507 {
508     if(!brush)  return InvalidParameter;
509
510     switch(brush->bt)
511     {
512         case BrushTypePathGradient:
513             GdipFree(((GpPathGradient*) brush)->pathdata.Points);
514             GdipFree(((GpPathGradient*) brush)->pathdata.Types);
515             GdipFree(((GpPathGradient*) brush)->blendfac);
516             GdipFree(((GpPathGradient*) brush)->blendpos);
517             break;
518         case BrushTypeSolidColor:
519         case BrushTypeLinearGradient:
520         case BrushTypeTextureFill:
521         default:
522             break;
523     }
524
525     DeleteObject(brush->gdibrush);
526     GdipFree(brush);
527
528     return Ok;
529 }
530
531 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
532     BOOL *usinggamma)
533 {
534     if(!line)
535         return InvalidParameter;
536
537     *usinggamma = line->gamma;
538
539     return Ok;
540 }
541
542 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
543 {
544     if(!brush || !wrapmode)
545         return InvalidParameter;
546
547     *wrapmode = brush->wrap;
548
549     return Ok;
550 }
551
552 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
553     REAL *positions, INT count)
554 {
555     if(!brush || !blend || !positions || count <= 0)
556         return InvalidParameter;
557
558     if(count < brush->blendcount)
559         return InsufficientBuffer;
560
561     memcpy(blend, brush->blendfac, count*sizeof(REAL));
562     if(brush->blendcount > 1){
563         memcpy(positions, brush->blendpos, count*sizeof(REAL));
564     }
565
566     return Ok;
567 }
568
569 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
570 {
571     if(!brush || !count)
572         return InvalidParameter;
573
574     *count = brush->blendcount;
575
576     return Ok;
577 }
578
579 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
580     GpPointF *point)
581 {
582     if(!grad || !point)
583         return InvalidParameter;
584
585     point->X = grad->center.X;
586     point->Y = grad->center.Y;
587
588     return Ok;
589 }
590
591 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
592     GpPoint *point)
593 {
594     GpStatus ret;
595     GpPointF ptf;
596
597     if(!point)
598         return InvalidParameter;
599
600     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
601
602     if(ret == Ok){
603         point->X = roundr(ptf.X);
604         point->Y = roundr(ptf.Y);
605     }
606
607     return ret;
608 }
609
610 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
611     REAL *x, REAL *y)
612 {
613     if(!grad || !x || !y)
614         return InvalidParameter;
615
616     *x = grad->focus.X;
617     *y = grad->focus.Y;
618
619     return Ok;
620 }
621
622 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
623     BOOL *gamma)
624 {
625     if(!grad || !gamma)
626         return InvalidParameter;
627
628     *gamma = grad->gamma;
629
630     return Ok;
631 }
632
633 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
634     INT *count)
635 {
636     if(!grad || !count)
637         return InvalidParameter;
638
639     *count = grad->pathdata.Count;
640
641     return Ok;
642 }
643
644 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
645 {
646     GpRectF r;
647     GpPath* path;
648     GpStatus stat;
649
650     if(!brush || !rect)
651         return InvalidParameter;
652
653     stat = GdipCreatePath2(brush->pathdata.Points, brush->pathdata.Types,
654                            brush->pathdata.Count, FillModeAlternate, &path);
655     if(stat != Ok)  return stat;
656
657     stat = GdipGetPathWorldBounds(path, &r, NULL, NULL);
658     if(stat != Ok){
659         GdipDeletePath(path);
660         return stat;
661     }
662
663     memcpy(rect, &r, sizeof(GpRectF));
664
665     GdipDeletePath(path);
666
667     return Ok;
668 }
669
670 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
671 {
672     GpRectF rectf;
673     GpStatus stat;
674
675     if(!brush || !rect)
676         return InvalidParameter;
677
678     stat = GdipGetPathGradientRect(brush, &rectf);
679     if(stat != Ok)  return stat;
680
681     rect->X = roundr(rectf.X);
682     rect->Y = roundr(rectf.Y);
683     rect->Width  = roundr(rectf.Width);
684     rect->Height = roundr(rectf.Height);
685
686     return Ok;
687 }
688
689 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
690     *grad, ARGB *argb, INT *count)
691 {
692     static int calls;
693
694     if(!grad || !argb || !count || (*count < grad->pathdata.Count))
695         return InvalidParameter;
696
697     if(!(calls++))
698         FIXME("not implemented\n");
699
700     return NotImplemented;
701 }
702
703 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
704     GpWrapMode *wrapmode)
705 {
706     if(!brush || !wrapmode)
707         return InvalidParameter;
708
709     *wrapmode = brush->wrap;
710
711     return Ok;
712 }
713
714 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
715 {
716     if(!sf || !argb)
717         return InvalidParameter;
718
719     *argb = sf->color;
720
721     return Ok;
722 }
723
724 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
725     GDIPCONST REAL *blend, GDIPCONST REAL* positions, INT count)
726 {
727     static int calls;
728
729     if(!brush || !blend || !positions || count <= 0)
730         return InvalidParameter;
731
732     if(!(calls++))
733         FIXME("not implemented\n");
734
735     return Ok;
736 }
737
738 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
739     BOOL usegamma)
740 {
741     if(!line)
742         return InvalidParameter;
743
744     line->gamma = usegamma;
745
746     return Ok;
747 }
748
749 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
750     REAL scale)
751 {
752     static int calls;
753
754     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
755         return InvalidParameter;
756
757     if(!(calls++))
758         FIXME("not implemented\n");
759
760     return NotImplemented;
761 }
762
763 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
764     GpWrapMode wrap)
765 {
766     if(!line || wrap == WrapModeClamp)
767         return InvalidParameter;
768
769     line->wrap = wrap;
770
771     return Ok;
772 }
773
774 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
775     ARGB argb)
776 {
777     if(!grad)
778         return InvalidParameter;
779
780     grad->centercolor = argb;
781     grad->brush.lb.lbColor = ARGB2COLORREF(argb);
782
783     DeleteObject(grad->brush.gdibrush);
784     grad->brush.gdibrush = CreateSolidBrush(grad->brush.lb.lbColor);
785
786     return Ok;
787 }
788
789 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
790     GpPointF *point)
791 {
792     if(!grad || !point)
793         return InvalidParameter;
794
795     grad->center.X = point->X;
796     grad->center.Y = point->Y;
797
798     return Ok;
799 }
800
801 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
802     GpPoint *point)
803 {
804     GpPointF ptf;
805
806     if(!point)
807         return InvalidParameter;
808
809     ptf.X = (REAL)point->X;
810     ptf.Y = (REAL)point->Y;
811
812     return GdipSetPathGradientCenterPoint(grad,&ptf);
813 }
814
815 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
816     REAL x, REAL y)
817 {
818     if(!grad)
819         return InvalidParameter;
820
821     grad->focus.X = x;
822     grad->focus.Y = y;
823
824     return Ok;
825 }
826
827 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
828     BOOL gamma)
829 {
830     if(!grad)
831         return InvalidParameter;
832
833     grad->gamma = gamma;
834
835     return Ok;
836 }
837
838 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
839     REAL focus, REAL scale)
840 {
841     static int calls;
842
843     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
844         return InvalidParameter;
845
846     if(!(calls++))
847         FIXME("not implemented\n");
848
849     return NotImplemented;
850 }
851
852 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
853     *grad, ARGB *argb, INT *count)
854 {
855     static int calls;
856
857     if(!grad || !argb || !count || (*count <= 0) ||
858         (*count > grad->pathdata.Count))
859         return InvalidParameter;
860
861     if(!(calls++))
862         FIXME("not implemented\n");
863
864     return NotImplemented;
865 }
866
867 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
868     GpWrapMode wrap)
869 {
870     if(!grad)
871         return InvalidParameter;
872
873     grad->wrap = wrap;
874
875     return Ok;
876 }
877
878 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
879 {
880     if(!sf)
881         return InvalidParameter;
882
883     sf->color = argb;
884     sf->brush.lb.lbColor = ARGB2COLORREF(argb);
885
886     DeleteObject(sf->brush.gdibrush);
887     sf->brush.gdibrush = CreateSolidBrush(sf->brush.lb.lbColor);
888
889     return Ok;
890 }
891
892 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
893     GDIPCONST GpMatrix *matrix)
894 {
895     static int calls;
896
897     if(!texture || !matrix)
898         return InvalidParameter;
899
900     if(!(calls++))
901         FIXME("not implemented\n");
902
903     return Ok;
904 }
905
906 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
907     ARGB color2)
908 {
909     if(!brush)
910         return InvalidParameter;
911
912     brush->startcolor = color1;
913     brush->endcolor   = color2;
914
915     return Ok;
916 }
917
918 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
919 {
920     if(!brush || !colors)
921         return InvalidParameter;
922
923     colors[0] = brush->startcolor;
924     colors[1] = brush->endcolor;
925
926     return Ok;
927 }
928
929 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
930     REAL scale)
931 {
932     static int calls;
933
934     if(!(calls++))
935         FIXME("not implemented\n");
936
937     return NotImplemented;
938 }
939
940 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
941     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
942 {
943     static int calls;
944
945     if(!(calls++))
946         FIXME("not implemented\n");
947
948     return NotImplemented;
949 }
950
951 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
952     GDIPCONST GpMatrix *matrix)
953 {
954     static int calls;
955
956     if(!(calls++))
957         FIXME("not implemented\n");
958
959     return NotImplemented;
960 }
961
962 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
963 {
964     if(!brush || !rect)
965         return InvalidParameter;
966
967     rect->X = (brush->startpoint.X < brush->endpoint.X ? brush->startpoint.X: brush->endpoint.X);
968     rect->Y = (brush->startpoint.Y < brush->endpoint.Y ? brush->startpoint.Y: brush->endpoint.Y);
969
970     rect->Width  = fabs(brush->startpoint.X - brush->endpoint.X);
971     rect->Height = fabs(brush->startpoint.Y - brush->endpoint.Y);
972
973     return Ok;
974 }
975
976 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
977 {
978     GpRectF  rectF;
979     GpStatus ret;
980
981     if(!rect)
982         return InvalidParameter;
983
984     ret = GdipGetLineRect(brush, &rectF);
985
986     if(ret == Ok){
987         rect->X      = roundr(rectF.X);
988         rect->Y      = roundr(rectF.Y);
989         rect->Width  = roundr(rectF.Width);
990         rect->Height = roundr(rectF.Height);
991     }
992
993     return ret;
994 }