gdi32: Fix a test failure in 2k.
[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             break;
79         }
80         case BrushTypeLinearGradient:
81             *clone = GdipAlloc(sizeof(GpLineGradient));
82             if(!*clone)    return OutOfMemory;
83
84             memcpy(*clone, brush, sizeof(GpLineGradient));
85
86             (*clone)->gdibrush = CreateSolidBrush((*clone)->lb.lbColor);
87             break;
88         case BrushTypeTextureFill:
89             *clone = GdipAlloc(sizeof(GpTexture));
90             if(!*clone)    return OutOfMemory;
91
92             memcpy(*clone, brush, sizeof(GpTexture));
93
94             (*clone)->gdibrush = CreateBrushIndirect(&(*clone)->lb);
95             break;
96         default:
97             ERR("not implemented for brush type %d\n", brush->bt);
98             return NotImplemented;
99     }
100
101     return Ok;
102 }
103
104 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
105     GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
106     GpWrapMode wrap, GpLineGradient **line)
107 {
108     COLORREF col = ARGB2COLORREF(startcolor);
109
110     if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
111         return InvalidParameter;
112
113     *line = GdipAlloc(sizeof(GpLineGradient));
114     if(!*line)  return OutOfMemory;
115
116     (*line)->brush.lb.lbStyle = BS_SOLID;
117     (*line)->brush.lb.lbColor = col;
118     (*line)->brush.lb.lbHatch = 0;
119     (*line)->brush.gdibrush = CreateSolidBrush(col);
120     (*line)->brush.bt = BrushTypeLinearGradient;
121
122     (*line)->startpoint.X = startpoint->X;
123     (*line)->startpoint.Y = startpoint->Y;
124     (*line)->endpoint.X = endpoint->X;
125     (*line)->endpoint.Y = endpoint->Y;
126     (*line)->startcolor = startcolor;
127     (*line)->endcolor = endcolor;
128     (*line)->wrap = wrap;
129     (*line)->gamma = FALSE;
130
131     return Ok;
132 }
133
134 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
135     GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
136     GpWrapMode wrap, GpLineGradient **line)
137 {
138     GpPointF stF;
139     GpPointF endF;
140
141     if(!startpoint || !endpoint)
142         return InvalidParameter;
143
144     stF.X  = (REAL)startpoint->X;
145     stF.Y  = (REAL)startpoint->Y;
146     endF.X = (REAL)endpoint->X;
147     endF.X = (REAL)endpoint->Y;
148
149     return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
150 }
151
152 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
153     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
154     GpLineGradient **line)
155 {
156     GpPointF start, end;
157
158     if(!line || !rect)
159         return InvalidParameter;
160
161     start.X = rect->X;
162     start.Y = rect->Y;
163     end.X = rect->X + rect->Width;
164     end.Y = rect->Y + rect->Height;
165
166     return GdipCreateLineBrush(&start, &end, startcolor, endcolor, wrap, line);
167 }
168
169 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
170     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
171     GpLineGradient **line)
172 {
173     GpRectF rectF;
174
175     rectF.X      = (REAL) rect->X;
176     rectF.Y      = (REAL) rect->Y;
177     rectF.Width  = (REAL) rect->Width;
178     rectF.Height = (REAL) rect->Height;
179
180     return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
181 }
182
183 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
184     INT count, GpWrapMode wrap, GpPathGradient **grad)
185 {
186     COLORREF col = ARGB2COLORREF(0xffffffff);
187
188     if(!points || !grad)
189         return InvalidParameter;
190
191     if(count <= 0)
192         return OutOfMemory;
193
194     *grad = GdipAlloc(sizeof(GpPathGradient));
195     if (!*grad) return OutOfMemory;
196
197     (*grad)->pathdata.Count = count;
198     (*grad)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
199     (*grad)->pathdata.Types = GdipAlloc(count);
200
201     if(!(*grad)->pathdata.Points || !(*grad)->pathdata.Types){
202         GdipFree((*grad)->pathdata.Points);
203         GdipFree((*grad)->pathdata.Types);
204         GdipFree(*grad);
205         return OutOfMemory;
206     }
207
208     memcpy((*grad)->pathdata.Points, points, count * sizeof(PointF));
209     memset((*grad)->pathdata.Types, PathPointTypeLine, count);
210
211     (*grad)->brush.lb.lbStyle = BS_SOLID;
212     (*grad)->brush.lb.lbColor = col;
213     (*grad)->brush.lb.lbHatch = 0;
214
215     (*grad)->brush.gdibrush = CreateSolidBrush(col);
216     (*grad)->brush.bt = BrushTypePathGradient;
217     (*grad)->centercolor = 0xffffffff;
218     (*grad)->wrap = wrap;
219     (*grad)->gamma = FALSE;
220     (*grad)->center.X = 0.0;
221     (*grad)->center.Y = 0.0;
222     (*grad)->focus.X = 0.0;
223     (*grad)->focus.Y = 0.0;
224
225     return Ok;
226 }
227
228 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
229     INT count, GpWrapMode wrap, GpPathGradient **grad)
230 {
231     GpPointF *pointsF;
232     GpStatus ret;
233     INT i;
234
235     if(!points || !grad)
236         return InvalidParameter;
237
238     if(count <= 0)
239         return OutOfMemory;
240
241     pointsF = GdipAlloc(sizeof(GpPointF) * count);
242     if(!pointsF)
243         return OutOfMemory;
244
245     for(i = 0; i < count; i++){
246         pointsF[i].X = (REAL)points[i].X;
247         pointsF[i].Y = (REAL)points[i].Y;
248     }
249
250     ret = GdipCreatePathGradient(pointsF, count, wrap, grad);
251     GdipFree(pointsF);
252
253     return ret;
254 }
255
256 /* FIXME: path gradient brushes not truly supported (drawn as solid brushes) */
257 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
258     GpPathGradient **grad)
259 {
260     COLORREF col = ARGB2COLORREF(0xffffffff);
261
262     if(!path || !grad)
263         return InvalidParameter;
264
265     *grad = GdipAlloc(sizeof(GpPathGradient));
266     if (!*grad) return OutOfMemory;
267
268     (*grad)->pathdata.Count = path->pathdata.Count;
269     (*grad)->pathdata.Points = GdipAlloc(path->pathdata.Count * sizeof(PointF));
270     (*grad)->pathdata.Types = GdipAlloc(path->pathdata.Count);
271
272     if(!(*grad)->pathdata.Points || !(*grad)->pathdata.Types){
273         GdipFree((*grad)->pathdata.Points);
274         GdipFree((*grad)->pathdata.Types);
275         GdipFree(*grad);
276         return OutOfMemory;
277     }
278
279     memcpy((*grad)->pathdata.Points, path->pathdata.Points,
280            path->pathdata.Count * sizeof(PointF));
281     memcpy((*grad)->pathdata.Types, path->pathdata.Types, path->pathdata.Count);
282
283     (*grad)->brush.lb.lbStyle = BS_SOLID;
284     (*grad)->brush.lb.lbColor = col;
285     (*grad)->brush.lb.lbHatch = 0;
286
287     (*grad)->brush.gdibrush = CreateSolidBrush(col);
288     (*grad)->brush.bt = BrushTypePathGradient;
289     (*grad)->centercolor = 0xffffffff;
290     (*grad)->wrap = WrapModeClamp;
291     (*grad)->gamma = FALSE;
292     /* FIXME: this should be set to the "centroid" of the path by default */
293     (*grad)->center.X = 0.0;
294     (*grad)->center.Y = 0.0;
295     (*grad)->focus.X = 0.0;
296     (*grad)->focus.Y = 0.0;
297
298     return Ok;
299 }
300
301 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
302 {
303     COLORREF col = ARGB2COLORREF(color);
304
305     if(!sf)  return InvalidParameter;
306
307     *sf = GdipAlloc(sizeof(GpSolidFill));
308     if (!*sf) return OutOfMemory;
309
310     (*sf)->brush.lb.lbStyle = BS_SOLID;
311     (*sf)->brush.lb.lbColor = col;
312     (*sf)->brush.lb.lbHatch = 0;
313
314     (*sf)->brush.gdibrush = CreateSolidBrush(col);
315     (*sf)->brush.bt = BrushTypeSolidColor;
316     (*sf)->color = color;
317
318     return Ok;
319 }
320
321 /* FIXME: imageattr ignored */
322 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
323     GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
324     REAL height, GpTexture **texture)
325 {
326     HDC hdc;
327     OLE_HANDLE hbm;
328     HBITMAP old = NULL;
329     BITMAPINFO bmi;
330     BITMAPINFOHEADER *bmih;
331     INT n_x, n_y, n_width, n_height, abs_height, stride, image_stride, i, bytespp;
332     BOOL bm_is_selected;
333     BYTE *dibits, *buff, *textbits;
334
335     if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
336         return InvalidParameter;
337
338     if(image->type != ImageTypeBitmap){
339         FIXME("not implemented for image type %d\n", image->type);
340         return NotImplemented;
341     }
342
343     n_x = roundr(x);
344     n_y = roundr(y);
345     n_width = roundr(width);
346     n_height = roundr(height);
347
348     if(n_x + n_width > ((GpBitmap*)image)->width ||
349        n_y + n_height > ((GpBitmap*)image)->height)
350         return InvalidParameter;
351
352     IPicture_get_Handle(image->picture, &hbm);
353     if(!hbm)   return GenericError;
354     IPicture_get_CurDC(image->picture, &hdc);
355     bm_is_selected = (hdc != 0);
356
357     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
358     bmi.bmiHeader.biBitCount = 0;
359
360     if(!bm_is_selected){
361         hdc = CreateCompatibleDC(0);
362         old = SelectObject(hdc, (HBITMAP)hbm);
363     }
364
365     /* fill out bmi */
366     GetDIBits(hdc, (HBITMAP)hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS);
367
368     bytespp = bmi.bmiHeader.biBitCount / 8;
369     abs_height = abs(bmi.bmiHeader.biHeight);
370
371     if(n_x > bmi.bmiHeader.biWidth || n_x + n_width > bmi.bmiHeader.biWidth ||
372        n_y > abs_height || n_y + n_height > abs_height)
373         return InvalidParameter;
374
375     dibits = GdipAlloc(bmi.bmiHeader.biSizeImage);
376
377     if(dibits)  /* this is not a good place to error out */
378         GetDIBits(hdc, (HBITMAP)hbm, 0, abs_height, dibits, &bmi, DIB_RGB_COLORS);
379
380     if(!bm_is_selected){
381         SelectObject(hdc, old);
382         DeleteDC(hdc);
383     }
384
385     if(!dibits)
386         return OutOfMemory;
387
388     image_stride = (bmi.bmiHeader.biWidth * bytespp + 3) & ~3;
389     stride = (n_width * bytespp + 3) & ~3;
390     buff = GdipAlloc(sizeof(BITMAPINFOHEADER) + stride * n_height);
391     if(!buff){
392         GdipFree(dibits);
393         return OutOfMemory;
394     }
395
396     bmih = (BITMAPINFOHEADER*)buff;
397     textbits = (BYTE*) (bmih + 1);
398     bmih->biSize = sizeof(BITMAPINFOHEADER);
399     bmih->biWidth = n_width;
400     bmih->biHeight = n_height;
401     bmih->biCompression = BI_RGB;
402     bmih->biSizeImage = stride * n_height;
403     bmih->biBitCount = bmi.bmiHeader.biBitCount;
404     bmih->biClrUsed = 0;
405     bmih->biPlanes = 1;
406
407     /* image is flipped */
408     if(bmi.bmiHeader.biHeight > 0){
409         dibits += bmi.bmiHeader.biSizeImage;
410         image_stride *= -1;
411         textbits += stride * (n_height - 1);
412         stride *= -1;
413     }
414
415     for(i = 0; i < n_height; i++)
416         memcpy(&textbits[i * stride],
417                &dibits[n_x * bytespp + (n_y + i) * image_stride],
418                abs(stride));
419
420     *texture = GdipAlloc(sizeof(GpTexture));
421     if (!*texture) return OutOfMemory;
422
423     (*texture)->brush.lb.lbStyle = BS_DIBPATTERNPT;
424     (*texture)->brush.lb.lbColor = DIB_RGB_COLORS;
425     (*texture)->brush.lb.lbHatch = (ULONG_PTR)buff;
426
427     (*texture)->brush.gdibrush = CreateBrushIndirect(&(*texture)->brush.lb);
428     (*texture)->brush.bt = BrushTypeTextureFill;
429
430     GdipFree(dibits);
431     GdipFree(buff);
432
433     return Ok;
434 }
435
436 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
437 {
438     if(!brush || !type)  return InvalidParameter;
439
440     *type = brush->bt;
441
442     return Ok;
443 }
444
445 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
446 {
447     if(!brush)  return InvalidParameter;
448
449     switch(brush->bt)
450     {
451         case BrushTypePathGradient:
452             GdipFree(((GpPathGradient*) brush)->pathdata.Points);
453             GdipFree(((GpPathGradient*) brush)->pathdata.Types);
454             break;
455         case BrushTypeSolidColor:
456         case BrushTypeLinearGradient:
457         case BrushTypeTextureFill:
458         default:
459             break;
460     }
461
462     DeleteObject(brush->gdibrush);
463     GdipFree(brush);
464
465     return Ok;
466 }
467
468 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
469     BOOL *usinggamma)
470 {
471     if(!line)
472         return InvalidParameter;
473
474     *usinggamma = line->gamma;
475
476     return Ok;
477 }
478
479 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
480     GpPointF *point)
481 {
482     if(!grad || !point)
483         return InvalidParameter;
484
485     point->X = grad->center.X;
486     point->Y = grad->center.Y;
487
488     return Ok;
489 }
490
491 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
492     GpPoint *point)
493 {
494     GpStatus ret;
495     GpPointF ptf;
496
497     if(!point)
498         return InvalidParameter;
499
500     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
501
502     if(ret == Ok){
503         point->X = roundr(ptf.X);
504         point->Y = roundr(ptf.Y);
505     }
506
507     return ret;
508 }
509
510 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
511     REAL *x, REAL *y)
512 {
513     if(!grad || !x || !y)
514         return InvalidParameter;
515
516     *x = grad->focus.X;
517     *y = grad->focus.Y;
518
519     return Ok;
520 }
521
522 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
523     BOOL *gamma)
524 {
525     if(!grad || !gamma)
526         return InvalidParameter;
527
528     *gamma = grad->gamma;
529
530     return Ok;
531 }
532
533 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
534     INT *count)
535 {
536     if(!grad || !count)
537         return InvalidParameter;
538
539     *count = grad->pathdata.Count;
540
541     return Ok;
542 }
543
544 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
545     *grad, ARGB *argb, INT *count)
546 {
547     static int calls;
548
549     if(!grad || !argb || !count || (*count < grad->pathdata.Count))
550         return InvalidParameter;
551
552     if(!(calls++))
553         FIXME("not implemented\n");
554
555     return NotImplemented;
556 }
557
558 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
559 {
560     if(!sf || !argb)
561         return InvalidParameter;
562
563     *argb = sf->color;
564
565     return Ok;
566 }
567
568 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
569     GDIPCONST REAL *blend, GDIPCONST REAL* positions, INT count)
570 {
571     static int calls;
572
573     if(!brush || !blend || !positions || count <= 0)
574         return InvalidParameter;
575
576     if(!(calls++))
577         FIXME("not implemented\n");
578
579     return Ok;
580 }
581
582 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
583     BOOL usegamma)
584 {
585     if(!line)
586         return InvalidParameter;
587
588     line->gamma = usegamma;
589
590     return Ok;
591 }
592
593 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
594     REAL scale)
595 {
596     static int calls;
597
598     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
599         return InvalidParameter;
600
601     if(!(calls++))
602         FIXME("not implemented\n");
603
604     return NotImplemented;
605 }
606
607 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
608     GpWrapMode wrap)
609 {
610     if(!line || wrap == WrapModeClamp)
611         return InvalidParameter;
612
613     line->wrap = wrap;
614
615     return Ok;
616 }
617
618 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
619     ARGB argb)
620 {
621     if(!grad)
622         return InvalidParameter;
623
624     grad->centercolor = argb;
625     grad->brush.lb.lbColor = ARGB2COLORREF(argb);
626
627     DeleteObject(grad->brush.gdibrush);
628     grad->brush.gdibrush = CreateSolidBrush(grad->brush.lb.lbColor);
629
630     return Ok;
631 }
632
633 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
634     GpPointF *point)
635 {
636     if(!grad || !point)
637         return InvalidParameter;
638
639     grad->center.X = point->X;
640     grad->center.Y = point->Y;
641
642     return Ok;
643 }
644
645 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
646     GpPoint *point)
647 {
648     GpPointF ptf;
649
650     if(!point)
651         return InvalidParameter;
652
653     ptf.X = (REAL)point->X;
654     ptf.Y = (REAL)point->Y;
655
656     return GdipSetPathGradientCenterPoint(grad,&ptf);
657 }
658
659 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
660     REAL x, REAL y)
661 {
662     if(!grad)
663         return InvalidParameter;
664
665     grad->focus.X = x;
666     grad->focus.Y = y;
667
668     return Ok;
669 }
670
671 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
672     BOOL gamma)
673 {
674     if(!grad)
675         return InvalidParameter;
676
677     grad->gamma = gamma;
678
679     return Ok;
680 }
681
682 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
683     REAL focus, REAL scale)
684 {
685     static int calls;
686
687     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
688         return InvalidParameter;
689
690     if(!(calls++))
691         FIXME("not implemented\n");
692
693     return NotImplemented;
694 }
695
696 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
697     *grad, ARGB *argb, INT *count)
698 {
699     static int calls;
700
701     if(!grad || !argb || !count || (*count <= 0) ||
702         (*count > grad->pathdata.Count))
703         return InvalidParameter;
704
705     if(!(calls++))
706         FIXME("not implemented\n");
707
708     return NotImplemented;
709 }
710
711 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
712     GpWrapMode wrap)
713 {
714     if(!grad)
715         return InvalidParameter;
716
717     grad->wrap = wrap;
718
719     return Ok;
720 }
721
722 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
723 {
724     if(!sf)
725         return InvalidParameter;
726
727     sf->color = argb;
728     sf->brush.lb.lbColor = ARGB2COLORREF(argb);
729
730     DeleteObject(sf->brush.gdibrush);
731     sf->brush.gdibrush = CreateSolidBrush(sf->brush.lb.lbColor);
732
733     return Ok;
734 }
735
736 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
737     GDIPCONST GpMatrix *matrix)
738 {
739     static int calls;
740
741     if(!texture || !matrix)
742         return InvalidParameter;
743
744     if(!(calls++))
745         FIXME("not implemented\n");
746
747     return Ok;
748 }
749
750 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
751     ARGB color2)
752 {
753     if(!brush)
754         return InvalidParameter;
755
756     brush->startcolor = color1;
757     brush->endcolor   = color2;
758
759     return Ok;
760 }
761
762 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
763 {
764     if(!brush || !colors)
765         return InvalidParameter;
766
767     colors[0] = brush->startcolor;
768     colors[1] = brush->endcolor;
769
770     return Ok;
771 }
772
773 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
774     REAL scale)
775 {
776     static int calls;
777
778     if(!(calls++))
779         FIXME("not implemented\n");
780
781     return NotImplemented;
782 }
783
784 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
785     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
786 {
787     static int calls;
788
789     if(!(calls++))
790         FIXME("not implemented\n");
791
792     return NotImplemented;
793 }
794
795 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
796     GDIPCONST GpMatrix *matrix)
797 {
798     static int calls;
799
800     if(!(calls++))
801         FIXME("not implemented\n");
802
803     return NotImplemented;
804 }
805
806 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
807 {
808     if(!brush || !rect)
809         return InvalidParameter;
810
811     rect->X = (brush->startpoint.X < brush->endpoint.X ? brush->startpoint.X: brush->endpoint.X);
812     rect->Y = (brush->startpoint.Y < brush->endpoint.Y ? brush->startpoint.Y: brush->endpoint.Y);
813
814     rect->Width  = fabs(brush->startpoint.X - brush->endpoint.X);
815     rect->Height = fabs(brush->startpoint.Y - brush->endpoint.Y);
816
817     return Ok;
818 }
819
820 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
821 {
822     GpRectF  rectF;
823     GpStatus ret;
824
825     ret = GdipGetLineRect(brush, &rectF);
826
827     if(ret == Ok){
828         rect->X      = roundr(rectF.X);
829         rect->Y      = roundr(rectF.Y);
830         rect->Width  = roundr(rectF.Width);
831         rect->Height = roundr(rectF.Height);
832     }
833
834     return ret;
835 }