xref: /reactos/dll/win32/gdiplus/brush.c (revision 89f10a28)
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  * Copyright (C) 2003-2004,2007 Novell, Inc. http://www.novell.com (Ravindra (rkumar@novell.com))
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "olectl.h"
30 #include "ole2.h"
31 
32 #include "gdiplus.h"
33 #include "gdiplus_private.h"
34 #include "wine/debug.h"
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
37 
38 #ifdef __REACTOS__
39 /*
40     Unix stuff
41     Code from http://www.johndcook.com/blog/2009/01/19/stand-alone-error-function-erf/
42 */
erf(double x)43 double erf(double x)
44 {
45     const float a1 =  0.254829592f;
46     const float a2 = -0.284496736f;
47     const float a3 =  1.421413741f;
48     const float a4 = -1.453152027f;
49     const float a5 =  1.061405429f;
50     const float p  =  0.3275911f;
51     float t, y, sign;
52 
53     /* Save the sign of x */
54     sign = 1;
55     if (x < 0)
56         sign = -1;
57     x = abs(x);
58 
59     /* A & S 7.1.26 */
60     t = 1.0/(1.0 + p*x);
61     y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-x*x);
62 
63     return sign*y;
64 }
65 #endif
66 
67 /******************************************************************************
68  * GdipCloneBrush [GDIPLUS.@]
69  */
GdipCloneBrush(GpBrush * brush,GpBrush ** clone)70 GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
71 {
72     TRACE("(%p, %p)\n", brush, clone);
73 
74     if(!brush || !clone)
75         return InvalidParameter;
76 
77     switch(brush->bt){
78         case BrushTypeSolidColor:
79         {
80             *clone = heap_alloc_zero(sizeof(GpSolidFill));
81             if (!*clone) return OutOfMemory;
82             memcpy(*clone, brush, sizeof(GpSolidFill));
83             break;
84         }
85         case BrushTypeHatchFill:
86         {
87             GpHatch *hatch = (GpHatch*)brush;
88 
89             return GdipCreateHatchBrush(hatch->hatchstyle, hatch->forecol, hatch->backcol, (GpHatch**)clone);
90         }
91         case BrushTypePathGradient:{
92             GpPathGradient *src, *dest;
93             INT count, pcount;
94             GpStatus stat;
95 
96             *clone = heap_alloc_zero(sizeof(GpPathGradient));
97             if (!*clone) return OutOfMemory;
98 
99             src = (GpPathGradient*) brush,
100             dest = (GpPathGradient*) *clone;
101 
102             memcpy(dest, src, sizeof(GpPathGradient));
103 
104             stat = GdipClonePath(src->path, &dest->path);
105 
106             if(stat != Ok){
107                 heap_free(dest);
108                 return stat;
109             }
110 
111             dest->transform = src->transform;
112 
113             /* blending */
114             count = src->blendcount;
115             dest->blendcount = count;
116             dest->blendfac = heap_alloc_zero(count * sizeof(REAL));
117             dest->blendpos = heap_alloc_zero(count * sizeof(REAL));
118             dest->surroundcolors = heap_alloc_zero(dest->surroundcolorcount * sizeof(ARGB));
119             pcount = dest->pblendcount;
120             if (pcount)
121             {
122                 dest->pblendcolor = heap_alloc_zero(pcount * sizeof(ARGB));
123                 dest->pblendpos = heap_alloc_zero(pcount * sizeof(REAL));
124             }
125 
126             if(!dest->blendfac || !dest->blendpos || !dest->surroundcolors ||
127                (pcount && (!dest->pblendcolor || !dest->pblendpos))){
128                 GdipDeletePath(dest->path);
129                 heap_free(dest->blendfac);
130                 heap_free(dest->blendpos);
131                 heap_free(dest->surroundcolors);
132                 heap_free(dest->pblendcolor);
133                 heap_free(dest->pblendpos);
134                 heap_free(dest);
135                 return OutOfMemory;
136             }
137 
138             memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
139             memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
140             memcpy(dest->surroundcolors, src->surroundcolors, dest->surroundcolorcount * sizeof(ARGB));
141 
142             if (pcount)
143             {
144                 memcpy(dest->pblendcolor, src->pblendcolor, pcount * sizeof(ARGB));
145                 memcpy(dest->pblendpos, src->pblendpos, pcount * sizeof(REAL));
146             }
147 
148             break;
149         }
150         case BrushTypeLinearGradient:{
151             GpLineGradient *dest, *src;
152             INT count, pcount;
153 
154             dest = heap_alloc_zero(sizeof(GpLineGradient));
155             if(!dest)    return OutOfMemory;
156 
157             src = (GpLineGradient*)brush;
158 
159             memcpy(dest, src, sizeof(GpLineGradient));
160 
161             count = dest->blendcount;
162             dest->blendfac = heap_alloc_zero(count * sizeof(REAL));
163             dest->blendpos = heap_alloc_zero(count * sizeof(REAL));
164             pcount = dest->pblendcount;
165             if (pcount)
166             {
167                 dest->pblendcolor = heap_alloc_zero(pcount * sizeof(ARGB));
168                 dest->pblendpos = heap_alloc_zero(pcount * sizeof(REAL));
169             }
170 
171             if (!dest->blendfac || !dest->blendpos ||
172                 (pcount && (!dest->pblendcolor || !dest->pblendpos)))
173             {
174                 heap_free(dest->blendfac);
175                 heap_free(dest->blendpos);
176                 heap_free(dest->pblendcolor);
177                 heap_free(dest->pblendpos);
178                 heap_free(dest);
179                 return OutOfMemory;
180             }
181 
182             dest->transform = src->transform;
183 
184             memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
185             memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
186 
187             if (pcount)
188             {
189                 memcpy(dest->pblendcolor, src->pblendcolor, pcount * sizeof(ARGB));
190                 memcpy(dest->pblendpos, src->pblendpos, pcount * sizeof(REAL));
191             }
192 
193             *clone = &dest->brush;
194             break;
195         }
196         case BrushTypeTextureFill:
197         {
198             GpStatus stat;
199             GpTexture *texture = (GpTexture*)brush;
200             GpTexture *new_texture;
201             UINT width, height;
202 
203             stat = GdipGetImageWidth(texture->image, &width);
204             if (stat != Ok) return stat;
205             stat = GdipGetImageHeight(texture->image, &height);
206             if (stat != Ok) return stat;
207 
208             stat = GdipCreateTextureIA(texture->image, texture->imageattributes, 0, 0, width, height, &new_texture);
209 
210             if (stat == Ok)
211             {
212                 new_texture->transform = texture->transform;
213                 *clone = &new_texture->brush;
214             }
215             else
216                 *clone = NULL;
217 
218             return stat;
219         }
220         default:
221             ERR("not implemented for brush type %d\n", brush->bt);
222             return NotImplemented;
223     }
224 
225     TRACE("<-- %p\n", *clone);
226     return Ok;
227 }
228 
229 static const char HatchBrushes[][8] = {
230     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HatchStyleHorizontal */
231     { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleVertical */
232     { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */
233     { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */
234     { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleCross */
235     { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */
236     { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */
237     { 0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88 }, /* HatchStyle10Percent */
238     { 0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc }, /* HatchStyle20Percent */
239     { 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc }, /* HatchStyle25Percent */
240     { 0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc }, /* HatchStyle30Percent */
241     { 0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc }, /* HatchStyle40Percent */
242     { 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc }, /* HatchStyle50Percent */
243     { 0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee }, /* HatchStyle60Percent */
244     { 0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff }, /* HatchStyle70Percent */
245     { 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff }, /* HatchStyle75Percent */
246     { 0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff }, /* HatchStyle80Percent */
247     { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff }, /* HatchStyle90Percent */
248     { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, /* HatchStyleLightDownwardDiagonal */
249     { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, /* HatchStyleLightUpwardDiagonal */
250     { 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc }, /* HatchStyleDarkDownwardDiagonal */
251     { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 }, /* HatchStyleDarkUpwardDiagonal */
252     { 0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0 }, /* HatchStyleWideDownwardDiagonal */
253     { 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1 }, /* HatchStyleWideUpwardDiagonal */
254     { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, /* HatchStyleLightVertical */
255     { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleLightHorizontal */
256     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, /* HatchStyleNarrowVertical */
257     { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */
258     { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */
259     { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */
260 };
261 
get_hatch_data(GpHatchStyle hatchstyle,const char ** result)262 GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result)
263 {
264     if (hatchstyle < ARRAY_SIZE(HatchBrushes))
265     {
266         *result = HatchBrushes[hatchstyle];
267         return Ok;
268     }
269     else
270         return NotImplemented;
271 }
272 
273 /******************************************************************************
274  * GdipCreateHatchBrush [GDIPLUS.@]
275  */
GdipCreateHatchBrush(GpHatchStyle hatchstyle,ARGB forecol,ARGB backcol,GpHatch ** brush)276 GpStatus WINGDIPAPI GdipCreateHatchBrush(GpHatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
277 {
278     TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
279 
280     if(!brush)  return InvalidParameter;
281 
282     if(hatchstyle < HatchStyleMin || hatchstyle > HatchStyleMax)
283         return InvalidParameter;
284 
285     *brush = heap_alloc_zero(sizeof(GpHatch));
286     if (!*brush) return OutOfMemory;
287 
288     (*brush)->brush.bt = BrushTypeHatchFill;
289     (*brush)->forecol = forecol;
290     (*brush)->backcol = backcol;
291     (*brush)->hatchstyle = hatchstyle;
292     TRACE("<-- %p\n", *brush);
293 
294     return Ok;
295 }
296 
create_line_brush(const GpRectF * rect,ARGB startcolor,ARGB endcolor,GpWrapMode wrap,GpLineGradient ** line)297 static GpStatus create_line_brush(const GpRectF *rect, ARGB startcolor, ARGB endcolor,
298     GpWrapMode wrap, GpLineGradient **line)
299 {
300     *line = heap_alloc_zero(sizeof(GpLineGradient));
301     if(!*line)  return OutOfMemory;
302 
303     (*line)->brush.bt = BrushTypeLinearGradient;
304     (*line)->startcolor = startcolor;
305     (*line)->endcolor = endcolor;
306     (*line)->wrap = wrap;
307     (*line)->gamma = FALSE;
308     (*line)->rect = *rect;
309     (*line)->blendcount = 1;
310     (*line)->blendfac = heap_alloc_zero(sizeof(REAL));
311     (*line)->blendpos = heap_alloc_zero(sizeof(REAL));
312 
313     if (!(*line)->blendfac || !(*line)->blendpos)
314     {
315         heap_free((*line)->blendfac);
316         heap_free((*line)->blendpos);
317         heap_free(*line);
318         *line = NULL;
319         return OutOfMemory;
320     }
321 
322     (*line)->blendfac[0] = 1.0f;
323     (*line)->blendpos[0] = 1.0f;
324 
325     (*line)->pblendcolor = NULL;
326     (*line)->pblendpos = NULL;
327     (*line)->pblendcount = 0;
328 
329     GdipSetMatrixElements(&(*line)->transform, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
330 
331     return Ok;
332 }
333 
linegradient_init_transform(const GpPointF * startpoint,const GpPointF * endpoint,GpLineGradient * line)334 static void linegradient_init_transform(const GpPointF *startpoint, const GpPointF *endpoint, GpLineGradient *line)
335 {
336     float trans_x = line->rect.X + (line->rect.Width / 2.f);
337     float trans_y = line->rect.Y + (line->rect.Height / 2.f);
338     float dx = endpoint->X - startpoint->X;
339     float dy = endpoint->Y - startpoint->Y;
340     float t_cos, t_sin, w_ratio, h_ratio;
341     float h;
342     GpMatrix rot;
343 
344     h = sqrtf(dx * dx + dy * dy);
345 
346     t_cos = dx / h;
347     t_sin = dy / h;
348 
349     w_ratio = (fabs(t_cos) * line->rect.Width + fabs(t_sin) * line->rect.Height) / line->rect.Width;
350     h_ratio = (fabs(t_sin) * line->rect.Width + fabs(t_cos) * line->rect.Height) / line->rect.Height;
351 
352     GdipSetMatrixElements(&line->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
353 
354     GdipSetMatrixElements(&rot, t_cos, t_sin, -1.f * t_sin, t_cos, 0, 0);
355 
356     /* center about the origin */
357     GdipTranslateMatrix(&line->transform, -trans_x, -trans_y, MatrixOrderAppend);
358 
359     /* scale to normalize gradient along gradient line (?) */
360     GdipScaleMatrix(&line->transform, w_ratio, h_ratio, MatrixOrderAppend);
361 
362     /* rotate so the gradient is horizontal */
363     GdipMultiplyMatrix(&line->transform, &rot, MatrixOrderAppend);
364 
365     /* restore original offset in new coords */
366     GdipTranslateMatrix(&line->transform, trans_x, trans_y, MatrixOrderAppend);
367 }
368 
369 /******************************************************************************
370  * GdipCreateLineBrush [GDIPLUS.@]
371  */
GdipCreateLineBrush(GDIPCONST GpPointF * startpoint,GDIPCONST GpPointF * endpoint,ARGB startcolor,ARGB endcolor,GpWrapMode wrap,GpLineGradient ** line)372 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
373     GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
374     GpWrapMode wrap, GpLineGradient **line)
375 {
376     GpStatus stat;
377     GpRectF rect;
378 
379     TRACE("(%s, %s, %x, %x, %d, %p)\n", debugstr_pointf(startpoint),
380           debugstr_pointf(endpoint), startcolor, endcolor, wrap, line);
381 
382     if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
383         return InvalidParameter;
384 
385     if (startpoint->X == endpoint->X && startpoint->Y == endpoint->Y)
386         return OutOfMemory;
387 
388     rect.X = startpoint->X < endpoint->X ? startpoint->X : endpoint->X;
389     rect.Y = startpoint->Y < endpoint->Y ? startpoint->Y : endpoint->Y;
390     rect.Width = fabs(startpoint->X - endpoint->X);
391     rect.Height = fabs(startpoint->Y - endpoint->Y);
392 
393     if (rect.Width == 0.0f)
394     {
395         rect.X -= rect.Height / 2.0f;
396         rect.Width = rect.Height;
397     }
398     else if (rect.Height == 0.0f)
399     {
400         rect.Y -= rect.Width / 2.0f;
401         rect.Height = rect.Width;
402     }
403 
404     stat = create_line_brush(&rect, startcolor, endcolor, wrap, line);
405     if (stat != Ok)
406         return stat;
407 
408     linegradient_init_transform(startpoint, endpoint, *line);
409 
410     TRACE("<-- %p\n", *line);
411 
412     return Ok;
413 }
414 
GdipCreateLineBrushI(GDIPCONST GpPoint * startpoint,GDIPCONST GpPoint * endpoint,ARGB startcolor,ARGB endcolor,GpWrapMode wrap,GpLineGradient ** line)415 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
416     GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
417     GpWrapMode wrap, GpLineGradient **line)
418 {
419     GpPointF stF;
420     GpPointF endF;
421 
422     TRACE("(%p, %p, %x, %x, %d, %p)\n", startpoint, endpoint,
423           startcolor, endcolor, wrap, line);
424 
425     if(!startpoint || !endpoint)
426         return InvalidParameter;
427 
428     stF.X  = (REAL)startpoint->X;
429     stF.Y  = (REAL)startpoint->Y;
430     endF.X = (REAL)endpoint->X;
431     endF.Y = (REAL)endpoint->Y;
432 
433     return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
434 }
435 
GdipCreateLineBrushFromRect(GDIPCONST GpRectF * rect,ARGB startcolor,ARGB endcolor,LinearGradientMode mode,GpWrapMode wrap,GpLineGradient ** line)436 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
437     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
438     GpLineGradient **line)
439 {
440     float angle;
441 
442     TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
443           wrap, line);
444 
445     if(!line || !rect)
446         return InvalidParameter;
447 
448     switch (mode)
449     {
450     case LinearGradientModeHorizontal:
451         angle = 0.0f;
452         break;
453     case LinearGradientModeVertical:
454         angle = 90.0f;
455         break;
456     case LinearGradientModeForwardDiagonal:
457         angle = 45.0f;
458         break;
459     case LinearGradientModeBackwardDiagonal:
460         angle = 135.0f;
461         break;
462     default:
463         return InvalidParameter;
464     }
465 
466     return GdipCreateLineBrushFromRectWithAngle(rect, startcolor, endcolor, angle, TRUE, wrap, line);
467 }
468 
GdipCreateLineBrushFromRectI(GDIPCONST GpRect * rect,ARGB startcolor,ARGB endcolor,LinearGradientMode mode,GpWrapMode wrap,GpLineGradient ** line)469 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
470     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
471     GpLineGradient **line)
472 {
473     GpRectF rectF;
474 
475     TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
476           wrap, line);
477 
478     rectF.X      = (REAL) rect->X;
479     rectF.Y      = (REAL) rect->Y;
480     rectF.Width  = (REAL) rect->Width;
481     rectF.Height = (REAL) rect->Height;
482 
483     return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
484 }
485 
486 /******************************************************************************
487  * GdipCreateLineBrushFromRectWithAngle [GDIPLUS.@]
488  */
GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF * rect,ARGB startcolor,ARGB endcolor,REAL angle,BOOL isAngleScalable,GpWrapMode wrap,GpLineGradient ** line)489 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect,
490     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
491     GpLineGradient **line)
492 {
493     GpStatus stat;
494     REAL exofs, eyofs, far_x, far_y;
495     REAL sin_angle, cos_angle, sin_cos_angle;
496     GpPointF start, end;
497 
498     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
499           wrap, line);
500 
501     if (!rect || !line || wrap == WrapModeClamp)
502         return InvalidParameter;
503 
504     if (!rect->Width || !rect->Height)
505         return OutOfMemory;
506 
507     angle = fmodf(angle, 360);
508     if (angle < 0)
509         angle += 360;
510 
511     if (isAngleScalable)
512     {
513         float add_angle = 0;
514 
515         while(angle >= 90) {
516             angle -= 180;
517             add_angle += M_PI;
518         }
519 
520         if (angle != 90 && angle != -90)
521             angle = atan((rect->Width / rect->Height) * tan(deg2rad(angle)));
522         else
523             angle = deg2rad(angle);
524         angle += add_angle;
525     }
526     else
527     {
528         angle = deg2rad(angle);
529     }
530 
531     sin_angle = sinf(angle);
532     cos_angle = cosf(angle);
533     sin_cos_angle = sin_angle * cos_angle;
534 
535     far_x = rect->X + rect->Width;
536     far_y = rect->Y + rect->Height;
537 
538     if (angle == 0.0f)
539     {
540         start.X = min(rect->X, far_x);
541         start.Y = rect->Y;
542         end.X = max(rect->X, far_x);
543         end.Y = rect->Y;
544     }
545     else if (sin_cos_angle >= 0)
546     {
547         start.X = min(rect->X, far_x);
548         start.Y = min(rect->Y, far_y);
549         end.X = max(rect->X, far_x);
550         end.Y = max(rect->Y, far_y);
551     }
552     else
553     {
554         start.X = max(rect->X, far_x);
555         start.Y = min(rect->Y, far_y);
556         end.X = min(rect->X, far_x);
557         end.Y = max(rect->Y, far_y);
558     }
559 
560     stat = create_line_brush(rect, startcolor, endcolor, wrap, line);
561     if (stat != Ok || angle == 0.0f)
562         return stat;
563 
564     if (sin_cos_angle >= 0)
565     {
566         exofs = rect->Height * sin_cos_angle + rect->Width * cos_angle * cos_angle;
567         eyofs = rect->Height * sin_angle * sin_angle + rect->Width * sin_cos_angle;
568     }
569     else
570     {
571         exofs = rect->Width * sin_angle * sin_angle + rect->Height * sin_cos_angle;
572         eyofs = -rect->Width * sin_cos_angle + rect->Height * sin_angle * sin_angle;
573     }
574 
575     if (sin_angle >= 0)
576     {
577         end.X = rect->X + exofs;
578         end.Y = rect->Y + eyofs;
579     }
580     else
581     {
582         end.X = start.X;
583         end.Y = start.Y;
584         start.X = rect->X + exofs;
585         start.Y = rect->Y + eyofs;
586     }
587 
588     linegradient_init_transform(&start, &end, *line);
589 
590     return stat;
591 }
592 
GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect * rect,ARGB startcolor,ARGB endcolor,REAL angle,BOOL isAngleScalable,GpWrapMode wrap,GpLineGradient ** line)593 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect,
594     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
595     GpLineGradient **line)
596 {
597     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
598           wrap, line);
599 
600     return GdipCreateLineBrushFromRectI(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
601                                         wrap, line);
602 }
603 
create_path_gradient(GpPath * path,ARGB centercolor,GpPathGradient ** grad)604 static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradient **grad)
605 {
606     GpRectF bounds;
607 
608     if(!path || !grad)
609         return InvalidParameter;
610 
611     if (path->pathdata.Count < 2)
612         return OutOfMemory;
613 
614     GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
615 
616     *grad = heap_alloc_zero(sizeof(GpPathGradient));
617     if (!*grad)
618     {
619         return OutOfMemory;
620     }
621 
622     GdipSetMatrixElements(&(*grad)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
623 
624     (*grad)->blendfac = heap_alloc_zero(sizeof(REAL));
625     (*grad)->blendpos = heap_alloc_zero(sizeof(REAL));
626     (*grad)->surroundcolors = heap_alloc_zero(sizeof(ARGB));
627     if(!(*grad)->blendfac || !(*grad)->blendpos || !(*grad)->surroundcolors){
628         heap_free((*grad)->blendfac);
629         heap_free((*grad)->blendpos);
630         heap_free((*grad)->surroundcolors);
631         heap_free(*grad);
632         *grad = NULL;
633         return OutOfMemory;
634     }
635     (*grad)->blendfac[0] = 1.0;
636     (*grad)->blendpos[0] = 1.0;
637     (*grad)->blendcount  = 1;
638 
639     (*grad)->path = path;
640 
641     (*grad)->brush.bt = BrushTypePathGradient;
642     (*grad)->centercolor = centercolor;
643     (*grad)->wrap = WrapModeClamp;
644     (*grad)->gamma = FALSE;
645     /* FIXME: this should be set to the "centroid" of the path by default */
646     (*grad)->center.X = bounds.X + bounds.Width / 2;
647     (*grad)->center.Y = bounds.Y + bounds.Height / 2;
648     (*grad)->focus.X = 0.0;
649     (*grad)->focus.Y = 0.0;
650     (*grad)->surroundcolors[0] = 0xffffffff;
651     (*grad)->surroundcolorcount = 1;
652 
653     TRACE("<-- %p\n", *grad);
654 
655     return Ok;
656 }
657 
GdipCreatePathGradient(GDIPCONST GpPointF * points,INT count,GpWrapMode wrap,GpPathGradient ** grad)658 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
659     INT count, GpWrapMode wrap, GpPathGradient **grad)
660 {
661     GpStatus stat;
662     GpPath *path;
663 
664     TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
665 
666     if(!grad)
667         return InvalidParameter;
668 
669     if(!points || count <= 0)
670         return OutOfMemory;
671 
672     stat = GdipCreatePath(FillModeAlternate, &path);
673 
674     if (stat == Ok)
675     {
676         stat = GdipAddPathLine2(path, points, count);
677 
678         if (stat == Ok)
679             stat = create_path_gradient(path, 0xff000000, grad);
680 
681         if (stat != Ok)
682             GdipDeletePath(path);
683     }
684 
685     if (stat == Ok)
686         (*grad)->wrap = wrap;
687 
688     return stat;
689 }
690 
GdipCreatePathGradientI(GDIPCONST GpPoint * points,INT count,GpWrapMode wrap,GpPathGradient ** grad)691 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
692     INT count, GpWrapMode wrap, GpPathGradient **grad)
693 {
694     GpStatus stat;
695     GpPath *path;
696 
697     TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
698 
699     if(!grad)
700         return InvalidParameter;
701 
702     if(!points || count <= 0)
703         return OutOfMemory;
704 
705     stat = GdipCreatePath(FillModeAlternate, &path);
706 
707     if (stat == Ok)
708     {
709         stat = GdipAddPathLine2I(path, points, count);
710 
711         if (stat == Ok)
712             stat = create_path_gradient(path, 0xff000000, grad);
713 
714         if (stat != Ok)
715             GdipDeletePath(path);
716     }
717 
718     if (stat == Ok)
719         (*grad)->wrap = wrap;
720 
721     return stat;
722 }
723 
724 /******************************************************************************
725  * GdipCreatePathGradientFromPath [GDIPLUS.@]
726  */
GdipCreatePathGradientFromPath(GDIPCONST GpPath * path,GpPathGradient ** grad)727 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
728     GpPathGradient **grad)
729 {
730     GpStatus stat;
731     GpPath *new_path;
732 
733     TRACE("(%p, %p)\n", path, grad);
734 
735     if(!grad)
736         return InvalidParameter;
737 
738     if (!path)
739         return OutOfMemory;
740 
741     stat = GdipClonePath((GpPath*)path, &new_path);
742 
743     if (stat == Ok)
744     {
745         stat = create_path_gradient(new_path, 0xffffffff, grad);
746 
747         if (stat != Ok)
748             GdipDeletePath(new_path);
749     }
750 
751     return stat;
752 }
753 
754 /******************************************************************************
755  * GdipCreateSolidFill [GDIPLUS.@]
756  */
GdipCreateSolidFill(ARGB color,GpSolidFill ** sf)757 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
758 {
759     TRACE("(%x, %p)\n", color, sf);
760 
761     if(!sf)  return InvalidParameter;
762 
763     *sf = heap_alloc_zero(sizeof(GpSolidFill));
764     if (!*sf) return OutOfMemory;
765 
766     (*sf)->brush.bt = BrushTypeSolidColor;
767     (*sf)->color = color;
768 
769     TRACE("<-- %p\n", *sf);
770 
771     return Ok;
772 }
773 
774 /******************************************************************************
775  * GdipCreateTexture [GDIPLUS.@]
776  *
777  * PARAMS
778  *  image       [I] image to use
779  *  wrapmode    [I] optional
780  *  texture     [O] pointer to the resulting texturebrush
781  *
782  * RETURNS
783  *  SUCCESS: Ok
784  *  FAILURE: element of GpStatus
785  */
GdipCreateTexture(GpImage * image,GpWrapMode wrapmode,GpTexture ** texture)786 GpStatus WINGDIPAPI GdipCreateTexture(GpImage *image, GpWrapMode wrapmode,
787         GpTexture **texture)
788 {
789     UINT width, height;
790     GpImageAttributes *attributes;
791     GpStatus stat;
792 
793     TRACE("%p, %d %p\n", image, wrapmode, texture);
794 
795     if (!(image && texture))
796         return InvalidParameter;
797 
798     stat = GdipGetImageWidth(image, &width);
799     if (stat != Ok) return stat;
800     stat = GdipGetImageHeight(image, &height);
801     if (stat != Ok) return stat;
802 
803     stat = GdipCreateImageAttributes(&attributes);
804 
805     if (stat == Ok)
806     {
807         attributes->wrap = wrapmode;
808 
809         stat = GdipCreateTextureIA(image, attributes, 0, 0, width, height,
810             texture);
811 
812         GdipDisposeImageAttributes(attributes);
813     }
814 
815     return stat;
816 }
817 
818 /******************************************************************************
819  * GdipCreateTexture2 [GDIPLUS.@]
820  */
GdipCreateTexture2(GpImage * image,GpWrapMode wrapmode,REAL x,REAL y,REAL width,REAL height,GpTexture ** texture)821 GpStatus WINGDIPAPI GdipCreateTexture2(GpImage *image, GpWrapMode wrapmode,
822         REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
823 {
824     GpImageAttributes *attributes;
825     GpStatus stat;
826 
827     TRACE("%p %d %f %f %f %f %p\n", image, wrapmode,
828             x, y, width, height, texture);
829 
830     stat = GdipCreateImageAttributes(&attributes);
831 
832     if (stat == Ok)
833     {
834         attributes->wrap = wrapmode;
835 
836         stat = GdipCreateTextureIA(image, attributes, x, y, width, height,
837                 texture);
838 
839         GdipDisposeImageAttributes(attributes);
840     }
841 
842     return stat;
843 }
844 
845 /******************************************************************************
846  * GdipCreateTextureIA [GDIPLUS.@]
847  */
GdipCreateTextureIA(GpImage * image,GDIPCONST GpImageAttributes * imageattr,REAL x,REAL y,REAL width,REAL height,GpTexture ** texture)848 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
849     GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
850     REAL height, GpTexture **texture)
851 {
852     GpStatus status;
853     GpImage *new_image=NULL;
854 
855     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %p)\n", image, imageattr, x, y, width, height,
856            texture);
857 
858     if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
859         return InvalidParameter;
860 
861     *texture = NULL;
862 
863     if(image->type != ImageTypeBitmap){
864         FIXME("not implemented for image type %d\n", image->type);
865         return NotImplemented;
866     }
867 
868     status = GdipCloneBitmapArea(x, y, width, height, PixelFormatDontCare, (GpBitmap*)image, (GpBitmap**)&new_image);
869     if (status != Ok)
870         return status;
871 
872     *texture = heap_alloc_zero(sizeof(GpTexture));
873     if (!*texture){
874         status = OutOfMemory;
875         goto exit;
876     }
877 
878     GdipSetMatrixElements(&(*texture)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
879 
880     if (imageattr)
881     {
882         status = GdipCloneImageAttributes(imageattr, &(*texture)->imageattributes);
883     }
884     else
885     {
886         status = GdipCreateImageAttributes(&(*texture)->imageattributes);
887         if (status == Ok)
888             (*texture)->imageattributes->wrap = WrapModeTile;
889     }
890     if (status == Ok)
891     {
892         (*texture)->brush.bt = BrushTypeTextureFill;
893         (*texture)->image = new_image;
894     }
895 
896 exit:
897     if (status == Ok)
898     {
899         TRACE("<-- %p\n", *texture);
900     }
901     else
902     {
903         if (*texture)
904         {
905             GdipDisposeImageAttributes((*texture)->imageattributes);
906             heap_free(*texture);
907             *texture = NULL;
908         }
909         GdipDisposeImage(new_image);
910         TRACE("<-- error %u\n", status);
911     }
912 
913     return status;
914 }
915 
916 /******************************************************************************
917  * GdipCreateTextureIAI [GDIPLUS.@]
918  */
GdipCreateTextureIAI(GpImage * image,GDIPCONST GpImageAttributes * imageattr,INT x,INT y,INT width,INT height,GpTexture ** texture)919 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
920     INT x, INT y, INT width, INT height, GpTexture **texture)
921 {
922     TRACE("(%p, %p, %d, %d, %d, %d, %p)\n", image, imageattr, x, y, width, height,
923            texture);
924 
925     return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
926 }
927 
GdipCreateTexture2I(GpImage * image,GpWrapMode wrapmode,INT x,INT y,INT width,INT height,GpTexture ** texture)928 GpStatus WINGDIPAPI GdipCreateTexture2I(GpImage *image, GpWrapMode wrapmode,
929         INT x, INT y, INT width, INT height, GpTexture **texture)
930 {
931     GpImageAttributes *imageattr;
932     GpStatus stat;
933 
934     TRACE("%p %d %d %d %d %d %p\n", image, wrapmode, x, y, width, height,
935             texture);
936 
937     stat = GdipCreateImageAttributes(&imageattr);
938 
939     if (stat == Ok)
940     {
941         imageattr->wrap = wrapmode;
942 
943         stat = GdipCreateTextureIA(image, imageattr, x, y, width, height, texture);
944         GdipDisposeImageAttributes(imageattr);
945     }
946 
947     return stat;
948 }
949 
GdipGetBrushType(GpBrush * brush,GpBrushType * type)950 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
951 {
952     TRACE("(%p, %p)\n", brush, type);
953 
954     if(!brush || !type)  return InvalidParameter;
955 
956     *type = brush->bt;
957 
958     return Ok;
959 }
960 
GdipGetHatchBackgroundColor(GpHatch * brush,ARGB * backcol)961 GpStatus WINGDIPAPI GdipGetHatchBackgroundColor(GpHatch *brush, ARGB *backcol)
962 {
963     TRACE("(%p, %p)\n", brush, backcol);
964 
965     if(!brush || !backcol)  return InvalidParameter;
966 
967     *backcol = brush->backcol;
968 
969     return Ok;
970 }
971 
GdipGetHatchForegroundColor(GpHatch * brush,ARGB * forecol)972 GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
973 {
974     TRACE("(%p, %p)\n", brush, forecol);
975 
976     if(!brush || !forecol)  return InvalidParameter;
977 
978     *forecol = brush->forecol;
979 
980     return Ok;
981 }
982 
GdipGetHatchStyle(GpHatch * brush,GpHatchStyle * hatchstyle)983 GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, GpHatchStyle *hatchstyle)
984 {
985     TRACE("(%p, %p)\n", brush, hatchstyle);
986 
987     if(!brush || !hatchstyle)  return InvalidParameter;
988 
989     *hatchstyle = brush->hatchstyle;
990 
991     return Ok;
992 }
993 
GdipDeleteBrush(GpBrush * brush)994 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
995 {
996     TRACE("(%p)\n", brush);
997 
998     if(!brush)  return InvalidParameter;
999 
1000     switch(brush->bt)
1001     {
1002         case BrushTypePathGradient:
1003             GdipDeletePath(((GpPathGradient*) brush)->path);
1004             heap_free(((GpPathGradient*) brush)->blendfac);
1005             heap_free(((GpPathGradient*) brush)->blendpos);
1006             heap_free(((GpPathGradient*) brush)->surroundcolors);
1007             heap_free(((GpPathGradient*) brush)->pblendcolor);
1008             heap_free(((GpPathGradient*) brush)->pblendpos);
1009             break;
1010         case BrushTypeLinearGradient:
1011             heap_free(((GpLineGradient*)brush)->blendfac);
1012             heap_free(((GpLineGradient*)brush)->blendpos);
1013             heap_free(((GpLineGradient*)brush)->pblendcolor);
1014             heap_free(((GpLineGradient*)brush)->pblendpos);
1015             break;
1016         case BrushTypeTextureFill:
1017             GdipDisposeImage(((GpTexture*)brush)->image);
1018             GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes);
1019             heap_free(((GpTexture*)brush)->bitmap_bits);
1020             break;
1021         default:
1022             break;
1023     }
1024 
1025     heap_free(brush);
1026 
1027     return Ok;
1028 }
1029 
GdipGetLineGammaCorrection(GpLineGradient * line,BOOL * usinggamma)1030 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
1031     BOOL *usinggamma)
1032 {
1033     TRACE("(%p, %p)\n", line, usinggamma);
1034 
1035     if(!line || !usinggamma)
1036         return InvalidParameter;
1037 
1038     *usinggamma = line->gamma;
1039 
1040     return Ok;
1041 }
1042 
GdipGetLineWrapMode(GpLineGradient * brush,GpWrapMode * wrapmode)1043 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
1044 {
1045     TRACE("(%p, %p)\n", brush, wrapmode);
1046 
1047     if(!brush || !wrapmode || brush->brush.bt != BrushTypeLinearGradient)
1048         return InvalidParameter;
1049 
1050     *wrapmode = brush->wrap;
1051 
1052     return Ok;
1053 }
1054 
GdipGetPathGradientBlend(GpPathGradient * brush,REAL * blend,REAL * positions,INT count)1055 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
1056     REAL *positions, INT count)
1057 {
1058     TRACE("(%p, %p, %p, %d)\n", brush, blend, positions, count);
1059 
1060     if(!brush || !blend || !positions || count <= 0 || brush->brush.bt != BrushTypePathGradient)
1061         return InvalidParameter;
1062 
1063     if(count < brush->blendcount)
1064         return InsufficientBuffer;
1065 
1066     memcpy(blend, brush->blendfac, count*sizeof(REAL));
1067     if(brush->blendcount > 1){
1068         memcpy(positions, brush->blendpos, count*sizeof(REAL));
1069     }
1070 
1071     return Ok;
1072 }
1073 
GdipGetPathGradientBlendCount(GpPathGradient * brush,INT * count)1074 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
1075 {
1076     TRACE("(%p, %p)\n", brush, count);
1077 
1078     if(!brush || !count || brush->brush.bt != BrushTypePathGradient)
1079         return InvalidParameter;
1080 
1081     *count = brush->blendcount;
1082 
1083     return Ok;
1084 }
1085 
GdipGetPathGradientCenterPoint(GpPathGradient * grad,GpPointF * point)1086 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
1087     GpPointF *point)
1088 {
1089     TRACE("(%p, %p)\n", grad, point);
1090 
1091     if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1092         return InvalidParameter;
1093 
1094     point->X = grad->center.X;
1095     point->Y = grad->center.Y;
1096 
1097     return Ok;
1098 }
1099 
GdipGetPathGradientCenterPointI(GpPathGradient * grad,GpPoint * point)1100 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
1101     GpPoint *point)
1102 {
1103     GpStatus ret;
1104     GpPointF ptf;
1105 
1106     TRACE("(%p, %p)\n", grad, point);
1107 
1108     if(!point)
1109         return InvalidParameter;
1110 
1111     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
1112 
1113     if(ret == Ok){
1114         point->X = gdip_round(ptf.X);
1115         point->Y = gdip_round(ptf.Y);
1116     }
1117 
1118     return ret;
1119 }
1120 
GdipGetPathGradientCenterColor(GpPathGradient * grad,ARGB * colors)1121 GpStatus WINGDIPAPI GdipGetPathGradientCenterColor(GpPathGradient *grad,
1122     ARGB *colors)
1123 {
1124     TRACE("(%p,%p)\n", grad, colors);
1125 
1126     if (!grad || !colors || grad->brush.bt != BrushTypePathGradient)
1127         return InvalidParameter;
1128 
1129     *colors = grad->centercolor;
1130 
1131     return Ok;
1132 }
1133 
GdipGetPathGradientFocusScales(GpPathGradient * grad,REAL * x,REAL * y)1134 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
1135     REAL *x, REAL *y)
1136 {
1137     TRACE("(%p, %p, %p)\n", grad, x, y);
1138 
1139     if(!grad || !x || !y || grad->brush.bt != BrushTypePathGradient)
1140         return InvalidParameter;
1141 
1142     *x = grad->focus.X;
1143     *y = grad->focus.Y;
1144 
1145     return Ok;
1146 }
1147 
GdipGetPathGradientGammaCorrection(GpPathGradient * grad,BOOL * gamma)1148 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
1149     BOOL *gamma)
1150 {
1151     TRACE("(%p, %p)\n", grad, gamma);
1152 
1153     if(!grad || !gamma || grad->brush.bt != BrushTypePathGradient)
1154         return InvalidParameter;
1155 
1156     *gamma = grad->gamma;
1157 
1158     return Ok;
1159 }
1160 
GdipGetPathGradientPath(GpPathGradient * grad,GpPath * path)1161 GpStatus WINGDIPAPI GdipGetPathGradientPath(GpPathGradient *grad, GpPath *path)
1162 {
1163     static int calls;
1164 
1165     TRACE("(%p, %p)\n", grad, path);
1166 
1167     if (!(calls++))
1168         FIXME("not implemented\n");
1169 
1170     return NotImplemented;
1171 }
1172 
GdipGetPathGradientPointCount(GpPathGradient * grad,INT * count)1173 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
1174     INT *count)
1175 {
1176     TRACE("(%p, %p)\n", grad, count);
1177 
1178     if(!grad || !count || grad->brush.bt != BrushTypePathGradient)
1179         return InvalidParameter;
1180 
1181     *count = grad->path->pathdata.Count;
1182 
1183     return Ok;
1184 }
1185 
GdipGetPathGradientRect(GpPathGradient * brush,GpRectF * rect)1186 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
1187 {
1188     GpStatus stat;
1189 
1190     TRACE("(%p, %p)\n", brush, rect);
1191 
1192     if(!brush || !rect || brush->brush.bt != BrushTypePathGradient)
1193         return InvalidParameter;
1194 
1195     stat = GdipGetPathWorldBounds(brush->path, rect, NULL, NULL);
1196 
1197     return stat;
1198 }
1199 
GdipGetPathGradientRectI(GpPathGradient * brush,GpRect * rect)1200 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
1201 {
1202     GpRectF rectf;
1203     GpStatus stat;
1204 
1205     TRACE("(%p, %p)\n", brush, rect);
1206 
1207     if(!brush || !rect)
1208         return InvalidParameter;
1209 
1210     stat = GdipGetPathGradientRect(brush, &rectf);
1211     if(stat != Ok)  return stat;
1212 
1213     rect->X = gdip_round(rectf.X);
1214     rect->Y = gdip_round(rectf.Y);
1215     rect->Width  = gdip_round(rectf.Width);
1216     rect->Height = gdip_round(rectf.Height);
1217 
1218     return Ok;
1219 }
1220 
GdipGetPathGradientSurroundColorsWithCount(GpPathGradient * grad,ARGB * argb,INT * count)1221 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
1222     *grad, ARGB *argb, INT *count)
1223 {
1224     INT i;
1225 
1226     TRACE("(%p,%p,%p)\n", grad, argb, count);
1227 
1228     if(!grad || !argb || !count || (*count < grad->path->pathdata.Count) || grad->brush.bt != BrushTypePathGradient)
1229         return InvalidParameter;
1230 
1231     for (i=0; i<grad->path->pathdata.Count; i++)
1232     {
1233         if (i < grad->surroundcolorcount)
1234             argb[i] = grad->surroundcolors[i];
1235         else
1236             argb[i] = grad->surroundcolors[grad->surroundcolorcount-1];
1237     }
1238 
1239     *count = grad->surroundcolorcount;
1240 
1241     return Ok;
1242 }
1243 
GdipGetPathGradientSurroundColorCount(GpPathGradient * brush,INT * count)1244 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient *brush, INT *count)
1245 {
1246     TRACE("(%p, %p)\n", brush, count);
1247 
1248     if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1249        return InvalidParameter;
1250 
1251     /* Yes, this actually returns the number of points in the path (which is the
1252      * required size of a buffer to get the surround colors), rather than the
1253      * number of surround colors. The real count is returned when getting the
1254      * colors. */
1255     *count = brush->path->pathdata.Count;
1256 
1257     return Ok;
1258 }
1259 
GdipGetPathGradientWrapMode(GpPathGradient * brush,GpWrapMode * wrapmode)1260 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
1261     GpWrapMode *wrapmode)
1262 {
1263     TRACE("(%p, %p)\n", brush, wrapmode);
1264 
1265     if(!brush || !wrapmode || brush->brush.bt != BrushTypePathGradient)
1266         return InvalidParameter;
1267 
1268     *wrapmode = brush->wrap;
1269 
1270     return Ok;
1271 }
1272 
GdipGetSolidFillColor(GpSolidFill * sf,ARGB * argb)1273 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
1274 {
1275     TRACE("(%p, %p)\n", sf, argb);
1276 
1277     if(!sf || !argb)
1278         return InvalidParameter;
1279 
1280     *argb = sf->color;
1281 
1282     return Ok;
1283 }
1284 
1285 /******************************************************************************
1286  * GdipGetTextureImage [GDIPLUS.@]
1287  */
GdipGetTextureImage(GpTexture * brush,GpImage ** image)1288 GpStatus WINGDIPAPI GdipGetTextureImage(GpTexture *brush, GpImage **image)
1289 {
1290     TRACE("(%p, %p)\n", brush, image);
1291 
1292     if(!brush || !image)
1293         return InvalidParameter;
1294 
1295     return GdipCloneImage(brush->image, image);
1296 }
1297 
1298 /******************************************************************************
1299  * GdipGetTextureTransform [GDIPLUS.@]
1300  */
GdipGetTextureTransform(GpTexture * brush,GpMatrix * matrix)1301 GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
1302 {
1303     TRACE("(%p, %p)\n", brush, matrix);
1304 
1305     if(!brush || !matrix)
1306         return InvalidParameter;
1307 
1308     *matrix = brush->transform;
1309 
1310     return Ok;
1311 }
1312 
1313 /******************************************************************************
1314  * GdipGetTextureWrapMode [GDIPLUS.@]
1315  */
GdipGetTextureWrapMode(GpTexture * brush,GpWrapMode * wrapmode)1316 GpStatus WINGDIPAPI GdipGetTextureWrapMode(GpTexture *brush, GpWrapMode *wrapmode)
1317 {
1318     TRACE("(%p, %p)\n", brush, wrapmode);
1319 
1320     if(!brush || !wrapmode)
1321         return InvalidParameter;
1322 
1323     *wrapmode = brush->imageattributes->wrap;
1324 
1325     return Ok;
1326 }
1327 
1328 /******************************************************************************
1329  * GdipMultiplyTextureTransform [GDIPLUS.@]
1330  */
GdipMultiplyTextureTransform(GpTexture * brush,GDIPCONST GpMatrix * matrix,GpMatrixOrder order)1331 GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
1332     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1333 {
1334     TRACE("(%p, %p, %d)\n", brush, matrix, order);
1335 
1336     if(!brush || !matrix)
1337         return InvalidParameter;
1338 
1339     return GdipMultiplyMatrix(&brush->transform, matrix, order);
1340 }
1341 
1342 /******************************************************************************
1343  * GdipResetTextureTransform [GDIPLUS.@]
1344  */
GdipResetTextureTransform(GpTexture * brush)1345 GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
1346 {
1347     TRACE("(%p)\n", brush);
1348 
1349     if(!brush)
1350         return InvalidParameter;
1351 
1352     return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1353 }
1354 
1355 /******************************************************************************
1356  * GdipScaleTextureTransform [GDIPLUS.@]
1357  */
GdipScaleTextureTransform(GpTexture * brush,REAL sx,REAL sy,GpMatrixOrder order)1358 GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
1359     REAL sx, REAL sy, GpMatrixOrder order)
1360 {
1361     TRACE("(%p, %.2f, %.2f, %d)\n", brush, sx, sy, order);
1362 
1363     if(!brush)
1364         return InvalidParameter;
1365 
1366     return GdipScaleMatrix(&brush->transform, sx, sy, order);
1367 }
1368 
GdipSetLineBlend(GpLineGradient * brush,GDIPCONST REAL * factors,GDIPCONST REAL * positions,INT count)1369 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
1370     GDIPCONST REAL *factors, GDIPCONST REAL* positions, INT count)
1371 {
1372     REAL *new_blendfac, *new_blendpos;
1373 
1374     TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1375 
1376     if(!brush || !factors || !positions || count <= 0 || brush->brush.bt != BrushTypeLinearGradient ||
1377        (count >= 2 && (positions[0] != 0.0f || positions[count-1] != 1.0f)))
1378         return InvalidParameter;
1379 
1380     new_blendfac = heap_alloc_zero(count * sizeof(REAL));
1381     new_blendpos = heap_alloc_zero(count * sizeof(REAL));
1382 
1383     if (!new_blendfac || !new_blendpos)
1384     {
1385         heap_free(new_blendfac);
1386         heap_free(new_blendpos);
1387         return OutOfMemory;
1388     }
1389 
1390     memcpy(new_blendfac, factors, count * sizeof(REAL));
1391     memcpy(new_blendpos, positions, count * sizeof(REAL));
1392 
1393     heap_free(brush->blendfac);
1394     heap_free(brush->blendpos);
1395 
1396     brush->blendcount = count;
1397     brush->blendfac = new_blendfac;
1398     brush->blendpos = new_blendpos;
1399 
1400     return Ok;
1401 }
1402 
GdipGetLineBlend(GpLineGradient * brush,REAL * factors,REAL * positions,INT count)1403 GpStatus WINGDIPAPI GdipGetLineBlend(GpLineGradient *brush, REAL *factors,
1404     REAL *positions, INT count)
1405 {
1406     TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1407 
1408     if (!brush || !factors || !positions || count <= 0 || brush->brush.bt != BrushTypeLinearGradient)
1409         return InvalidParameter;
1410 
1411     if (count < brush->blendcount)
1412         return InsufficientBuffer;
1413 
1414     memcpy(factors, brush->blendfac, brush->blendcount * sizeof(REAL));
1415     memcpy(positions, brush->blendpos, brush->blendcount * sizeof(REAL));
1416 
1417     return Ok;
1418 }
1419 
GdipGetLineBlendCount(GpLineGradient * brush,INT * count)1420 GpStatus WINGDIPAPI GdipGetLineBlendCount(GpLineGradient *brush, INT *count)
1421 {
1422     TRACE("(%p, %p)\n", brush, count);
1423 
1424     if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
1425         return InvalidParameter;
1426 
1427     *count = brush->blendcount;
1428 
1429     return Ok;
1430 }
1431 
GdipSetLineGammaCorrection(GpLineGradient * line,BOOL usegamma)1432 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
1433     BOOL usegamma)
1434 {
1435     TRACE("(%p, %d)\n", line, usegamma);
1436 
1437     if(!line || line->brush.bt != BrushTypeLinearGradient)
1438         return InvalidParameter;
1439 
1440     line->gamma = usegamma;
1441 
1442     return Ok;
1443 }
1444 
GdipSetLineSigmaBlend(GpLineGradient * line,REAL focus,REAL scale)1445 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
1446     REAL scale)
1447 {
1448     REAL factors[33];
1449     REAL positions[33];
1450     int num_points = 0;
1451     int i;
1452     const int precision = 16;
1453     REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1454     REAL min_erf;
1455     REAL scale_erf;
1456 
1457     TRACE("(%p, %0.2f, %0.2f)\n", line, focus, scale);
1458 
1459     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || line->brush.bt != BrushTypeLinearGradient)
1460         return InvalidParameter;
1461 
1462     /* we want 2 standard deviations */
1463     erf_range = 2.0 / sqrt(2);
1464 
1465     /* calculate the constants we need to normalize the error function to be
1466         between 0.0 and scale over the range we need */
1467     min_erf = erf(-erf_range);
1468     scale_erf = scale / (-2.0 * min_erf);
1469 
1470     if (focus != 0.0)
1471     {
1472         positions[0] = 0.0;
1473         factors[0] = 0.0;
1474         for (i=1; i<precision; i++)
1475         {
1476             positions[i] = focus * i / precision;
1477             factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1478         }
1479         num_points += precision;
1480     }
1481 
1482     positions[num_points] = focus;
1483     factors[num_points] = scale;
1484     num_points += 1;
1485 
1486     if (focus != 1.0)
1487     {
1488         for (i=1; i<precision; i++)
1489         {
1490             positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1491             factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1492         }
1493         num_points += precision;
1494         positions[num_points-1] = 1.0;
1495         factors[num_points-1] = 0.0;
1496     }
1497 
1498     return GdipSetLineBlend(line, factors, positions, num_points);
1499 }
1500 
GdipSetLineWrapMode(GpLineGradient * line,GpWrapMode wrap)1501 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
1502     GpWrapMode wrap)
1503 {
1504     TRACE("(%p, %d)\n", line, wrap);
1505 
1506     if(!line || wrap == WrapModeClamp || line->brush.bt != BrushTypeLinearGradient)
1507         return InvalidParameter;
1508 
1509     line->wrap = wrap;
1510 
1511     return Ok;
1512 }
1513 
GdipSetPathGradientBlend(GpPathGradient * brush,GDIPCONST REAL * blend,GDIPCONST REAL * pos,INT count)1514 GpStatus WINGDIPAPI GdipSetPathGradientBlend(GpPathGradient *brush, GDIPCONST REAL *blend,
1515     GDIPCONST REAL *pos, INT count)
1516 {
1517     REAL *new_blendfac, *new_blendpos;
1518 
1519     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1520 
1521     if(!brush || !blend || !pos || count <= 0 || brush->brush.bt != BrushTypePathGradient ||
1522        (count >= 2 && (pos[0] != 0.0f || pos[count-1] != 1.0f)))
1523         return InvalidParameter;
1524 
1525     new_blendfac = heap_alloc_zero(count * sizeof(REAL));
1526     new_blendpos = heap_alloc_zero(count * sizeof(REAL));
1527 
1528     if (!new_blendfac || !new_blendpos)
1529     {
1530         heap_free(new_blendfac);
1531         heap_free(new_blendpos);
1532         return OutOfMemory;
1533     }
1534 
1535     memcpy(new_blendfac, blend, count * sizeof(REAL));
1536     memcpy(new_blendpos, pos, count * sizeof(REAL));
1537 
1538     heap_free(brush->blendfac);
1539     heap_free(brush->blendpos);
1540 
1541     brush->blendcount = count;
1542     brush->blendfac = new_blendfac;
1543     brush->blendpos = new_blendpos;
1544 
1545     return Ok;
1546 }
1547 
GdipSetPathGradientLinearBlend(GpPathGradient * brush,REAL focus,REAL scale)1548 GpStatus WINGDIPAPI GdipSetPathGradientLinearBlend(GpPathGradient *brush,
1549     REAL focus, REAL scale)
1550 {
1551     REAL factors[3];
1552     REAL positions[3];
1553     int num_points = 0;
1554 
1555     TRACE("(%p,%0.2f,%0.2f)\n", brush, focus, scale);
1556 
1557     if (!brush || brush->brush.bt != BrushTypePathGradient)
1558         return InvalidParameter;
1559 
1560     if (focus != 0.0)
1561     {
1562         factors[num_points] = 0.0;
1563         positions[num_points] = 0.0;
1564         num_points++;
1565     }
1566 
1567     factors[num_points] = scale;
1568     positions[num_points] = focus;
1569     num_points++;
1570 
1571     if (focus != 1.0)
1572     {
1573         factors[num_points] = 0.0;
1574         positions[num_points] = 1.0;
1575         num_points++;
1576     }
1577 
1578     return GdipSetPathGradientBlend(brush, factors, positions, num_points);
1579 }
1580 
GdipSetPathGradientPresetBlend(GpPathGradient * brush,GDIPCONST ARGB * blend,GDIPCONST REAL * pos,INT count)1581 GpStatus WINGDIPAPI GdipSetPathGradientPresetBlend(GpPathGradient *brush,
1582     GDIPCONST ARGB *blend, GDIPCONST REAL *pos, INT count)
1583 {
1584     ARGB *new_color;
1585     REAL *new_pos;
1586     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1587 
1588     if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient ||
1589         pos[0] != 0.0f || pos[count-1] != 1.0f)
1590     {
1591         return InvalidParameter;
1592     }
1593 
1594     new_color = heap_alloc_zero(count * sizeof(ARGB));
1595     new_pos = heap_alloc_zero(count * sizeof(REAL));
1596     if (!new_color || !new_pos)
1597     {
1598         heap_free(new_color);
1599         heap_free(new_pos);
1600         return OutOfMemory;
1601     }
1602 
1603     memcpy(new_color, blend, sizeof(ARGB) * count);
1604     memcpy(new_pos, pos, sizeof(REAL) * count);
1605 
1606     heap_free(brush->pblendcolor);
1607     heap_free(brush->pblendpos);
1608 
1609     brush->pblendcolor = new_color;
1610     brush->pblendpos = new_pos;
1611     brush->pblendcount = count;
1612 
1613     return Ok;
1614 }
1615 
GdipGetPathGradientPresetBlend(GpPathGradient * brush,ARGB * blend,REAL * pos,INT count)1616 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlend(GpPathGradient *brush,
1617     ARGB *blend, REAL *pos, INT count)
1618 {
1619     TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1620 
1621     if (count < 0)
1622         return OutOfMemory;
1623 
1624     if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient)
1625         return InvalidParameter;
1626 
1627     if (brush->pblendcount == 0)
1628         return GenericError;
1629 
1630     if (count != brush->pblendcount)
1631     {
1632         /* Native lines up the ends of each array, and copies the destination size. */
1633         FIXME("Braindead behavior on wrong-sized buffer not implemented.\n");
1634         return InvalidParameter;
1635     }
1636 
1637     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1638     memcpy(pos, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1639 
1640     return Ok;
1641 }
1642 
GdipGetPathGradientPresetBlendCount(GpPathGradient * brush,INT * count)1643 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlendCount(GpPathGradient *brush,
1644     INT *count)
1645 {
1646     TRACE("(%p,%p)\n", brush, count);
1647 
1648     if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1649         return InvalidParameter;
1650 
1651     *count = brush->pblendcount;
1652 
1653     return Ok;
1654 }
1655 
GdipSetPathGradientCenterColor(GpPathGradient * grad,ARGB argb)1656 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1657     ARGB argb)
1658 {
1659     TRACE("(%p, %x)\n", grad, argb);
1660 
1661     if(!grad || grad->brush.bt != BrushTypePathGradient)
1662         return InvalidParameter;
1663 
1664     grad->centercolor = argb;
1665     return Ok;
1666 }
1667 
GdipSetPathGradientCenterPoint(GpPathGradient * grad,GpPointF * point)1668 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1669     GpPointF *point)
1670 {
1671     TRACE("(%p, %s)\n", grad, debugstr_pointf(point));
1672 
1673     if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1674         return InvalidParameter;
1675 
1676     grad->center.X = point->X;
1677     grad->center.Y = point->Y;
1678 
1679     return Ok;
1680 }
1681 
GdipSetPathGradientCenterPointI(GpPathGradient * grad,GpPoint * point)1682 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1683     GpPoint *point)
1684 {
1685     GpPointF ptf;
1686 
1687     TRACE("(%p, %p)\n", grad, point);
1688 
1689     if(!point)
1690         return InvalidParameter;
1691 
1692     ptf.X = (REAL)point->X;
1693     ptf.Y = (REAL)point->Y;
1694 
1695     return GdipSetPathGradientCenterPoint(grad,&ptf);
1696 }
1697 
GdipSetPathGradientFocusScales(GpPathGradient * grad,REAL x,REAL y)1698 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1699     REAL x, REAL y)
1700 {
1701     TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1702 
1703     if(!grad || grad->brush.bt != BrushTypePathGradient)
1704         return InvalidParameter;
1705 
1706     grad->focus.X = x;
1707     grad->focus.Y = y;
1708 
1709     return Ok;
1710 }
1711 
GdipSetPathGradientGammaCorrection(GpPathGradient * grad,BOOL gamma)1712 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1713     BOOL gamma)
1714 {
1715     TRACE("(%p, %d)\n", grad, gamma);
1716 
1717     if(!grad || grad->brush.bt != BrushTypePathGradient)
1718         return InvalidParameter;
1719 
1720     grad->gamma = gamma;
1721 
1722     return Ok;
1723 }
1724 
GdipSetPathGradientPath(GpPathGradient * grad,GDIPCONST GpPath * path)1725 GpStatus WINGDIPAPI GdipSetPathGradientPath(GpPathGradient *grad, GDIPCONST GpPath *path)
1726 {
1727     static int calls;
1728 
1729     TRACE("(%p, %p)\n", grad, path);
1730 
1731     if (!(calls++))
1732         FIXME("not implemented\n");
1733 
1734     return NotImplemented;
1735 }
1736 
GdipSetPathGradientSigmaBlend(GpPathGradient * grad,REAL focus,REAL scale)1737 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1738     REAL focus, REAL scale)
1739 {
1740     REAL factors[33];
1741     REAL positions[33];
1742     int num_points = 0;
1743     int i;
1744     const int precision = 16;
1745     REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1746     REAL min_erf;
1747     REAL scale_erf;
1748 
1749     TRACE("(%p,%0.2f,%0.2f)\n", grad, focus, scale);
1750 
1751     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || grad->brush.bt != BrushTypePathGradient)
1752         return InvalidParameter;
1753 
1754     /* we want 2 standard deviations */
1755     erf_range = 2.0 / sqrt(2);
1756 
1757     /* calculate the constants we need to normalize the error function to be
1758         between 0.0 and scale over the range we need */
1759     min_erf = erf(-erf_range);
1760     scale_erf = scale / (-2.0 * min_erf);
1761 
1762     if (focus != 0.0)
1763     {
1764         positions[0] = 0.0;
1765         factors[0] = 0.0;
1766         for (i=1; i<precision; i++)
1767         {
1768             positions[i] = focus * i / precision;
1769             factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1770         }
1771         num_points += precision;
1772     }
1773 
1774     positions[num_points] = focus;
1775     factors[num_points] = scale;
1776     num_points += 1;
1777 
1778     if (focus != 1.0)
1779     {
1780         for (i=1; i<precision; i++)
1781         {
1782             positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1783             factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1784         }
1785         num_points += precision;
1786         positions[num_points-1] = 1.0;
1787         factors[num_points-1] = 0.0;
1788     }
1789 
1790     return GdipSetPathGradientBlend(grad, factors, positions, num_points);
1791 }
1792 
GdipSetPathGradientSurroundColorsWithCount(GpPathGradient * grad,GDIPCONST ARGB * argb,INT * count)1793 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1794     *grad, GDIPCONST ARGB *argb, INT *count)
1795 {
1796     ARGB *new_surroundcolors;
1797     INT i, num_colors;
1798 
1799     TRACE("(%p,%p,%p)\n", grad, argb, count);
1800 
1801     if(!grad || !argb || !count || (*count <= 0) || grad->brush.bt != BrushTypePathGradient ||
1802         (*count > grad->path->pathdata.Count))
1803         return InvalidParameter;
1804 
1805     num_colors = *count;
1806 
1807     /* If all colors are the same, only store 1 color. */
1808     if (*count > 1)
1809     {
1810         for (i=1; i < num_colors; i++)
1811             if (argb[i] != argb[i-1])
1812                 break;
1813 
1814         if (i == num_colors)
1815             num_colors = 1;
1816     }
1817 
1818     new_surroundcolors = heap_alloc_zero(num_colors * sizeof(ARGB));
1819     if (!new_surroundcolors)
1820         return OutOfMemory;
1821 
1822     memcpy(new_surroundcolors, argb, num_colors * sizeof(ARGB));
1823 
1824     heap_free(grad->surroundcolors);
1825 
1826     grad->surroundcolors = new_surroundcolors;
1827     grad->surroundcolorcount = num_colors;
1828 
1829     return Ok;
1830 }
1831 
GdipSetPathGradientWrapMode(GpPathGradient * grad,GpWrapMode wrap)1832 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1833     GpWrapMode wrap)
1834 {
1835     TRACE("(%p, %d)\n", grad, wrap);
1836 
1837     if(!grad || grad->brush.bt != BrushTypePathGradient)
1838         return InvalidParameter;
1839 
1840     grad->wrap = wrap;
1841 
1842     return Ok;
1843 }
1844 
GdipSetPathGradientTransform(GpPathGradient * grad,GpMatrix * matrix)1845 GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
1846     GpMatrix *matrix)
1847 {
1848     TRACE("(%p,%p)\n", grad, matrix);
1849 
1850     if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1851         return InvalidParameter;
1852 
1853     grad->transform = *matrix;
1854 
1855     return Ok;
1856 }
1857 
GdipGetPathGradientTransform(GpPathGradient * grad,GpMatrix * matrix)1858 GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
1859     GpMatrix *matrix)
1860 {
1861     TRACE("(%p,%p)\n", grad, matrix);
1862 
1863     if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1864         return InvalidParameter;
1865 
1866     *matrix = grad->transform;
1867 
1868     return Ok;
1869 }
1870 
GdipMultiplyPathGradientTransform(GpPathGradient * grad,GDIPCONST GpMatrix * matrix,GpMatrixOrder order)1871 GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
1872     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1873 {
1874     TRACE("(%p,%p,%i)\n", grad, matrix, order);
1875 
1876     if (!grad || grad->brush.bt != BrushTypePathGradient)
1877         return InvalidParameter;
1878 
1879     return GdipMultiplyMatrix(&grad->transform, matrix, order);
1880 }
1881 
GdipResetPathGradientTransform(GpPathGradient * grad)1882 GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
1883 {
1884     TRACE("(%p)\n", grad);
1885 
1886     if (!grad || grad->brush.bt != BrushTypePathGradient)
1887         return InvalidParameter;
1888 
1889     return GdipSetMatrixElements(&grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1890 }
1891 
GdipRotatePathGradientTransform(GpPathGradient * grad,REAL angle,GpMatrixOrder order)1892 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
1893     REAL angle, GpMatrixOrder order)
1894 {
1895     TRACE("(%p,%0.2f,%i)\n", grad, angle, order);
1896 
1897     if (!grad || grad->brush.bt != BrushTypePathGradient)
1898         return InvalidParameter;
1899 
1900     return GdipRotateMatrix(&grad->transform, angle, order);
1901 }
1902 
GdipScalePathGradientTransform(GpPathGradient * grad,REAL sx,REAL sy,GpMatrixOrder order)1903 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
1904     REAL sx, REAL sy, GpMatrixOrder order)
1905 {
1906     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, sx, sy, order);
1907 
1908     if (!grad || grad->brush.bt != BrushTypePathGradient)
1909         return InvalidParameter;
1910 
1911     return GdipScaleMatrix(&grad->transform, sx, sy, order);
1912 }
1913 
GdipTranslatePathGradientTransform(GpPathGradient * grad,REAL dx,REAL dy,GpMatrixOrder order)1914 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
1915     REAL dx, REAL dy, GpMatrixOrder order)
1916 {
1917     TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, dx, dy, order);
1918 
1919     if (!grad || grad->brush.bt != BrushTypePathGradient)
1920         return InvalidParameter;
1921 
1922     return GdipTranslateMatrix(&grad->transform, dx, dy, order);
1923 }
1924 
GdipSetSolidFillColor(GpSolidFill * sf,ARGB argb)1925 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1926 {
1927     TRACE("(%p, %x)\n", sf, argb);
1928 
1929     if(!sf)
1930         return InvalidParameter;
1931 
1932     sf->color = argb;
1933     return Ok;
1934 }
1935 
1936 /******************************************************************************
1937  * GdipSetTextureTransform [GDIPLUS.@]
1938  */
GdipSetTextureTransform(GpTexture * texture,GDIPCONST GpMatrix * matrix)1939 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1940     GDIPCONST GpMatrix *matrix)
1941 {
1942     TRACE("(%p, %p)\n", texture, matrix);
1943 
1944     if(!texture || !matrix)
1945         return InvalidParameter;
1946 
1947     texture->transform = *matrix;
1948 
1949     return Ok;
1950 }
1951 
1952 /******************************************************************************
1953  * GdipSetTextureWrapMode [GDIPLUS.@]
1954  *
1955  * WrapMode not used, only stored
1956  */
GdipSetTextureWrapMode(GpTexture * brush,GpWrapMode wrapmode)1957 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1958 {
1959     TRACE("(%p, %d)\n", brush, wrapmode);
1960 
1961     if(!brush)
1962         return InvalidParameter;
1963 
1964     brush->imageattributes->wrap = wrapmode;
1965 
1966     return Ok;
1967 }
1968 
GdipSetLineColors(GpLineGradient * brush,ARGB color1,ARGB color2)1969 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1970     ARGB color2)
1971 {
1972     TRACE("(%p, %x, %x)\n", brush, color1, color2);
1973 
1974     if(!brush || brush->brush.bt != BrushTypeLinearGradient)
1975         return InvalidParameter;
1976 
1977     brush->startcolor = color1;
1978     brush->endcolor   = color2;
1979 
1980     return Ok;
1981 }
1982 
GdipGetLineColors(GpLineGradient * brush,ARGB * colors)1983 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1984 {
1985     TRACE("(%p, %p)\n", brush, colors);
1986 
1987     if(!brush || !colors || brush->brush.bt != BrushTypeLinearGradient)
1988         return InvalidParameter;
1989 
1990     colors[0] = brush->startcolor;
1991     colors[1] = brush->endcolor;
1992 
1993     return Ok;
1994 }
1995 
1996 /******************************************************************************
1997  * GdipRotateTextureTransform [GDIPLUS.@]
1998  */
GdipRotateTextureTransform(GpTexture * brush,REAL angle,GpMatrixOrder order)1999 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
2000     GpMatrixOrder order)
2001 {
2002     TRACE("(%p, %.2f, %d)\n", brush, angle, order);
2003 
2004     if(!brush)
2005         return InvalidParameter;
2006 
2007     return GdipRotateMatrix(&brush->transform, angle, order);
2008 }
2009 
GdipSetLineLinearBlend(GpLineGradient * brush,REAL focus,REAL scale)2010 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
2011     REAL scale)
2012 {
2013     REAL factors[3];
2014     REAL positions[3];
2015     int num_points = 0;
2016 
2017     TRACE("(%p,%.2f,%.2f)\n", brush, focus, scale);
2018 
2019     if (!brush) return InvalidParameter;
2020 
2021     if (focus != 0.0)
2022     {
2023         factors[num_points] = 0.0;
2024         positions[num_points] = 0.0;
2025         num_points++;
2026     }
2027 
2028     factors[num_points] = scale;
2029     positions[num_points] = focus;
2030     num_points++;
2031 
2032     if (focus != 1.0)
2033     {
2034         factors[num_points] = 0.0;
2035         positions[num_points] = 1.0;
2036         num_points++;
2037     }
2038 
2039     return GdipSetLineBlend(brush, factors, positions, num_points);
2040 }
2041 
GdipSetLinePresetBlend(GpLineGradient * brush,GDIPCONST ARGB * blend,GDIPCONST REAL * positions,INT count)2042 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
2043     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
2044 {
2045     ARGB *new_color;
2046     REAL *new_pos;
2047     TRACE("(%p,%p,%p,%i)\n", brush, blend, positions, count);
2048 
2049     if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient ||
2050         positions[0] != 0.0f || positions[count-1] != 1.0f)
2051     {
2052         return InvalidParameter;
2053     }
2054 
2055     new_color = heap_alloc_zero(count * sizeof(ARGB));
2056     new_pos = heap_alloc_zero(count * sizeof(REAL));
2057     if (!new_color || !new_pos)
2058     {
2059         heap_free(new_color);
2060         heap_free(new_pos);
2061         return OutOfMemory;
2062     }
2063 
2064     memcpy(new_color, blend, sizeof(ARGB) * count);
2065     memcpy(new_pos, positions, sizeof(REAL) * count);
2066 
2067     heap_free(brush->pblendcolor);
2068     heap_free(brush->pblendpos);
2069 
2070     brush->pblendcolor = new_color;
2071     brush->pblendpos = new_pos;
2072     brush->pblendcount = count;
2073 
2074     return Ok;
2075 }
2076 
GdipGetLinePresetBlend(GpLineGradient * brush,ARGB * blend,REAL * positions,INT count)2077 GpStatus WINGDIPAPI GdipGetLinePresetBlend(GpLineGradient *brush,
2078     ARGB *blend, REAL* positions, INT count)
2079 {
2080     if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient)
2081         return InvalidParameter;
2082 
2083     if (brush->pblendcount == 0)
2084         return GenericError;
2085 
2086     if (count < brush->pblendcount)
2087         return InsufficientBuffer;
2088 
2089     memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
2090     memcpy(positions, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
2091 
2092     return Ok;
2093 }
2094 
GdipGetLinePresetBlendCount(GpLineGradient * brush,INT * count)2095 GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
2096     INT *count)
2097 {
2098     if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
2099         return InvalidParameter;
2100 
2101     *count = brush->pblendcount;
2102 
2103     return Ok;
2104 }
2105 
GdipResetLineTransform(GpLineGradient * brush)2106 GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
2107 {
2108     TRACE("(%p)\n", brush);
2109 
2110     if(!brush)
2111         return InvalidParameter;
2112 
2113     return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2114 }
2115 
GdipSetLineTransform(GpLineGradient * brush,GDIPCONST GpMatrix * matrix)2116 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
2117     GDIPCONST GpMatrix *matrix)
2118 {
2119     TRACE("(%p,%p)\n", brush,  matrix);
2120 
2121     if(!brush || !matrix)
2122         return InvalidParameter;
2123 
2124     brush->transform = *matrix;
2125 
2126     return Ok;
2127 }
2128 
GdipGetLineTransform(GpLineGradient * brush,GpMatrix * matrix)2129 GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
2130 {
2131     TRACE("(%p,%p)\n", brush, matrix);
2132 
2133     if(!brush || !matrix)
2134         return InvalidParameter;
2135 
2136     *matrix = brush->transform;
2137 
2138     return Ok;
2139 }
2140 
GdipScaleLineTransform(GpLineGradient * brush,REAL sx,REAL sy,GpMatrixOrder order)2141 GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
2142     GpMatrixOrder order)
2143 {
2144     TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
2145 
2146     if(!brush)
2147         return InvalidParameter;
2148 
2149     return GdipScaleMatrix(&brush->transform, sx, sy, order);
2150 }
2151 
GdipMultiplyLineTransform(GpLineGradient * brush,GDIPCONST GpMatrix * matrix,GpMatrixOrder order)2152 GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
2153     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
2154 {
2155     TRACE("(%p,%p,%u)\n", brush, matrix, order);
2156 
2157     if(!brush)
2158         return InvalidParameter;
2159 
2160     if(!matrix)
2161         return Ok;
2162 
2163     return GdipMultiplyMatrix(&brush->transform, matrix, order);
2164 }
2165 
GdipTranslateLineTransform(GpLineGradient * brush,REAL dx,REAL dy,GpMatrixOrder order)2166 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient *brush,
2167         REAL dx, REAL dy, GpMatrixOrder order)
2168 {
2169     TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
2170 
2171     if(!brush)
2172         return InvalidParameter;
2173 
2174     return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2175 }
2176 
2177 /******************************************************************************
2178  * GdipTranslateTextureTransform [GDIPLUS.@]
2179  */
GdipTranslateTextureTransform(GpTexture * brush,REAL dx,REAL dy,GpMatrixOrder order)2180 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
2181     GpMatrixOrder order)
2182 {
2183     TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
2184 
2185     if(!brush)
2186         return InvalidParameter;
2187 
2188     return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2189 }
2190 
GdipGetLineRect(GpLineGradient * brush,GpRectF * rect)2191 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
2192 {
2193     TRACE("(%p, %p)\n", brush, rect);
2194 
2195     if(!brush || !rect || brush->brush.bt != BrushTypeLinearGradient)
2196         return InvalidParameter;
2197 
2198     *rect = brush->rect;
2199 
2200     return Ok;
2201 }
2202 
GdipGetLineRectI(GpLineGradient * brush,GpRect * rect)2203 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
2204 {
2205     GpRectF  rectF;
2206     GpStatus ret;
2207 
2208     TRACE("(%p, %p)\n", brush, rect);
2209 
2210     if(!rect)
2211         return InvalidParameter;
2212 
2213     ret = GdipGetLineRect(brush, &rectF);
2214 
2215     if(ret == Ok){
2216         rect->X      = gdip_round(rectF.X);
2217         rect->Y      = gdip_round(rectF.Y);
2218         rect->Width  = gdip_round(rectF.Width);
2219         rect->Height = gdip_round(rectF.Height);
2220     }
2221 
2222     return ret;
2223 }
2224 
GdipRotateLineTransform(GpLineGradient * brush,REAL angle,GpMatrixOrder order)2225 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
2226     REAL angle, GpMatrixOrder order)
2227 {
2228     static int calls;
2229 
2230     TRACE("(%p,%0.2f,%u)\n", brush, angle, order);
2231 
2232     if(!brush || brush->brush.bt != BrushTypeLinearGradient)
2233         return InvalidParameter;
2234 
2235     if(!(calls++))
2236         FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
2237 
2238     return NotImplemented;
2239 }
2240