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