quartz: Make dwSamplesProcessed a longlong.
[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 GdipGetPathGradientFocusScales(GpPathGradient *grad,
492     REAL *x, REAL *y)
493 {
494     if(!grad || !x || !y)
495         return InvalidParameter;
496
497     *x = grad->focus.X;
498     *y = grad->focus.Y;
499
500     return Ok;
501 }
502
503 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
504     BOOL *gamma)
505 {
506     if(!grad || !gamma)
507         return InvalidParameter;
508
509     *gamma = grad->gamma;
510
511     return Ok;
512 }
513
514 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
515     INT *count)
516 {
517     if(!grad || !count)
518         return InvalidParameter;
519
520     *count = grad->pathdata.Count;
521
522     return Ok;
523 }
524
525 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
526     *grad, ARGB *argb, INT *count)
527 {
528     static int calls;
529
530     if(!grad || !argb || !count || (*count < grad->pathdata.Count))
531         return InvalidParameter;
532
533     if(!(calls++))
534         FIXME("not implemented\n");
535
536     return NotImplemented;
537 }
538
539 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
540 {
541     if(!sf || !argb)
542         return InvalidParameter;
543
544     *argb = sf->color;
545
546     return Ok;
547 }
548
549 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
550     GDIPCONST REAL *blend, GDIPCONST REAL* positions, INT count)
551 {
552     static int calls;
553
554     if(!brush || !blend || !positions || count <= 0)
555         return InvalidParameter;
556
557     if(!(calls++))
558         FIXME("not implemented\n");
559
560     return Ok;
561 }
562
563 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
564     BOOL usegamma)
565 {
566     if(!line)
567         return InvalidParameter;
568
569     line->gamma = usegamma;
570
571     return Ok;
572 }
573
574 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
575     REAL scale)
576 {
577     static int calls;
578
579     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
580         return InvalidParameter;
581
582     if(!(calls++))
583         FIXME("not implemented\n");
584
585     return NotImplemented;
586 }
587
588 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
589     GpWrapMode wrap)
590 {
591     if(!line || wrap == WrapModeClamp)
592         return InvalidParameter;
593
594     line->wrap = wrap;
595
596     return Ok;
597 }
598
599 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
600     ARGB argb)
601 {
602     if(!grad)
603         return InvalidParameter;
604
605     grad->centercolor = argb;
606     grad->brush.lb.lbColor = ARGB2COLORREF(argb);
607
608     DeleteObject(grad->brush.gdibrush);
609     grad->brush.gdibrush = CreateSolidBrush(grad->brush.lb.lbColor);
610
611     return Ok;
612 }
613
614 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
615     GpPointF *point)
616 {
617     if(!grad || !point)
618         return InvalidParameter;
619
620     grad->center.X = point->X;
621     grad->center.Y = point->Y;
622
623     return Ok;
624 }
625
626 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
627     REAL x, REAL y)
628 {
629     if(!grad)
630         return InvalidParameter;
631
632     grad->focus.X = x;
633     grad->focus.Y = y;
634
635     return Ok;
636 }
637
638 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
639     BOOL gamma)
640 {
641     if(!grad)
642         return InvalidParameter;
643
644     grad->gamma = gamma;
645
646     return Ok;
647 }
648
649 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
650     REAL focus, REAL scale)
651 {
652     static int calls;
653
654     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
655         return InvalidParameter;
656
657     if(!(calls++))
658         FIXME("not implemented\n");
659
660     return NotImplemented;
661 }
662
663 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
664     *grad, ARGB *argb, INT *count)
665 {
666     static int calls;
667
668     if(!grad || !argb || !count || (*count <= 0) ||
669         (*count > grad->pathdata.Count))
670         return InvalidParameter;
671
672     if(!(calls++))
673         FIXME("not implemented\n");
674
675     return NotImplemented;
676 }
677
678 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
679     GpWrapMode wrap)
680 {
681     if(!grad)
682         return InvalidParameter;
683
684     grad->wrap = wrap;
685
686     return Ok;
687 }
688
689 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
690 {
691     if(!sf)
692         return InvalidParameter;
693
694     sf->color = argb;
695     sf->brush.lb.lbColor = ARGB2COLORREF(argb);
696
697     DeleteObject(sf->brush.gdibrush);
698     sf->brush.gdibrush = CreateSolidBrush(sf->brush.lb.lbColor);
699
700     return Ok;
701 }
702
703 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
704     GDIPCONST GpMatrix *matrix)
705 {
706     static int calls;
707
708     if(!texture || !matrix)
709         return InvalidParameter;
710
711     if(!(calls++))
712         FIXME("not implemented\n");
713
714     return Ok;
715 }
716
717 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
718     ARGB color2)
719 {
720     if(!brush)
721         return InvalidParameter;
722
723     brush->startcolor = color1;
724     brush->endcolor   = color2;
725
726     return Ok;
727 }
728
729 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
730 {
731     if(!brush || !colors)
732         return InvalidParameter;
733
734     colors[0] = brush->startcolor;
735     colors[1] = brush->endcolor;
736
737     return Ok;
738 }
739
740 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
741     REAL scale)
742 {
743     static int calls;
744
745     if(!(calls++))
746         FIXME("not implemented\n");
747
748     return NotImplemented;
749 }
750
751 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
752     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
753 {
754     static int calls;
755
756     if(!(calls++))
757         FIXME("not implemented\n");
758
759     return NotImplemented;
760 }
761
762 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
763     GDIPCONST GpMatrix *matrix)
764 {
765     static int calls;
766
767     if(!(calls++))
768         FIXME("not implemented\n");
769
770     return NotImplemented;
771 }
772
773 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
774 {
775     if(!brush || !rect)
776         return InvalidParameter;
777
778     rect->X = (brush->startpoint.X < brush->endpoint.X ? brush->startpoint.X: brush->endpoint.X);
779     rect->Y = (brush->startpoint.Y < brush->endpoint.Y ? brush->startpoint.Y: brush->endpoint.Y);
780
781     rect->Width  = fabs(brush->startpoint.X - brush->endpoint.X);
782     rect->Height = fabs(brush->startpoint.Y - brush->endpoint.Y);
783
784     return Ok;
785 }
786
787 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
788 {
789     GpRectF  rectF;
790     GpStatus ret;
791
792     ret = GdipGetLineRect(brush, &rectF);
793
794     if(ret == Ok){
795         rect->X      = roundr(rectF.X);
796         rect->Y      = roundr(rectF.Y);
797         rect->Width  = roundr(rectF.Width);
798         rect->Height = roundr(rectF.Height);
799     }
800
801     return ret;
802 }