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