winhttp: Get rid of send_request_t.
[wine.git] / dlls / gdiplus / brush.c
blob3aeb654086071af936f95e0ae2f4fcca323f0039
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 * Copyright (C) 2003-2004,2007 Novell, Inc. http://www.novell.com (Ravindra (rkumar@novell.com))
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.
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.
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
20 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wingdi.h"
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "olectl.h"
30 #include "ole2.h"
32 #include "gdiplus.h"
33 #include "gdiplus_private.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
38 /******************************************************************************
39 * GdipCloneBrush [GDIPLUS.@]
41 GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
43 TRACE("(%p, %p)\n", brush, clone);
45 if(!brush || !clone)
46 return InvalidParameter;
48 switch(brush->bt){
49 case BrushTypeSolidColor:
51 *clone = heap_alloc_zero(sizeof(GpSolidFill));
52 if (!*clone) return OutOfMemory;
53 memcpy(*clone, brush, sizeof(GpSolidFill));
54 break;
56 case BrushTypeHatchFill:
58 GpHatch *hatch = (GpHatch*)brush;
60 return GdipCreateHatchBrush(hatch->hatchstyle, hatch->forecol, hatch->backcol, (GpHatch**)clone);
62 case BrushTypePathGradient:{
63 GpPathGradient *src, *dest;
64 INT count, pcount;
65 GpStatus stat;
67 *clone = heap_alloc_zero(sizeof(GpPathGradient));
68 if (!*clone) return OutOfMemory;
70 src = (GpPathGradient*) brush,
71 dest = (GpPathGradient*) *clone;
73 memcpy(dest, src, sizeof(GpPathGradient));
75 stat = GdipClonePath(src->path, &dest->path);
77 if(stat != Ok){
78 heap_free(dest);
79 return stat;
82 dest->transform = src->transform;
84 /* blending */
85 count = src->blendcount;
86 dest->blendcount = count;
87 dest->blendfac = heap_alloc_zero(count * sizeof(REAL));
88 dest->blendpos = heap_alloc_zero(count * sizeof(REAL));
89 dest->surroundcolors = heap_alloc_zero(dest->surroundcolorcount * sizeof(ARGB));
90 pcount = dest->pblendcount;
91 if (pcount)
93 dest->pblendcolor = heap_alloc_zero(pcount * sizeof(ARGB));
94 dest->pblendpos = heap_alloc_zero(pcount * sizeof(REAL));
97 if(!dest->blendfac || !dest->blendpos || !dest->surroundcolors ||
98 (pcount && (!dest->pblendcolor || !dest->pblendpos))){
99 GdipDeletePath(dest->path);
100 heap_free(dest->blendfac);
101 heap_free(dest->blendpos);
102 heap_free(dest->surroundcolors);
103 heap_free(dest->pblendcolor);
104 heap_free(dest->pblendpos);
105 heap_free(dest);
106 return OutOfMemory;
109 memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
110 memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
111 memcpy(dest->surroundcolors, src->surroundcolors, dest->surroundcolorcount * sizeof(ARGB));
113 if (pcount)
115 memcpy(dest->pblendcolor, src->pblendcolor, pcount * sizeof(ARGB));
116 memcpy(dest->pblendpos, src->pblendpos, pcount * sizeof(REAL));
119 break;
121 case BrushTypeLinearGradient:{
122 GpLineGradient *dest, *src;
123 INT count, pcount;
125 dest = heap_alloc_zero(sizeof(GpLineGradient));
126 if(!dest) return OutOfMemory;
128 src = (GpLineGradient*)brush;
130 memcpy(dest, src, sizeof(GpLineGradient));
132 count = dest->blendcount;
133 dest->blendfac = heap_alloc_zero(count * sizeof(REAL));
134 dest->blendpos = heap_alloc_zero(count * sizeof(REAL));
135 pcount = dest->pblendcount;
136 if (pcount)
138 dest->pblendcolor = heap_alloc_zero(pcount * sizeof(ARGB));
139 dest->pblendpos = heap_alloc_zero(pcount * sizeof(REAL));
142 if (!dest->blendfac || !dest->blendpos ||
143 (pcount && (!dest->pblendcolor || !dest->pblendpos)))
145 heap_free(dest->blendfac);
146 heap_free(dest->blendpos);
147 heap_free(dest->pblendcolor);
148 heap_free(dest->pblendpos);
149 heap_free(dest);
150 return OutOfMemory;
153 dest->transform = src->transform;
155 memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
156 memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
158 if (pcount)
160 memcpy(dest->pblendcolor, src->pblendcolor, pcount * sizeof(ARGB));
161 memcpy(dest->pblendpos, src->pblendpos, pcount * sizeof(REAL));
164 *clone = &dest->brush;
165 break;
167 case BrushTypeTextureFill:
169 GpStatus stat;
170 GpTexture *texture = (GpTexture*)brush;
171 GpTexture *new_texture;
172 UINT width, height;
174 stat = GdipGetImageWidth(texture->image, &width);
175 if (stat != Ok) return stat;
176 stat = GdipGetImageHeight(texture->image, &height);
177 if (stat != Ok) return stat;
179 stat = GdipCreateTextureIA(texture->image, texture->imageattributes, 0, 0, width, height, &new_texture);
181 if (stat == Ok)
183 new_texture->transform = texture->transform;
184 *clone = &new_texture->brush;
186 else
187 *clone = NULL;
189 return stat;
191 default:
192 ERR("not implemented for brush type %d\n", brush->bt);
193 return NotImplemented;
196 TRACE("<-- %p\n", *clone);
197 return Ok;
200 static const char HatchBrushes[][8] = {
201 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HatchStyleHorizontal */
202 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleVertical */
203 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */
204 { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */
205 { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleCross */
206 { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */
207 { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */
208 { 0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88 }, /* HatchStyle10Percent */
209 { 0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc }, /* HatchStyle20Percent */
210 { 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc }, /* HatchStyle25Percent */
211 { 0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc }, /* HatchStyle30Percent */
212 { 0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc }, /* HatchStyle40Percent */
213 { 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc }, /* HatchStyle50Percent */
214 { 0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee }, /* HatchStyle60Percent */
215 { 0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff }, /* HatchStyle70Percent */
216 { 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff }, /* HatchStyle75Percent */
217 { 0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff }, /* HatchStyle80Percent */
218 { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff }, /* HatchStyle90Percent */
219 { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, /* HatchStyleLightDownwardDiagonal */
220 { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, /* HatchStyleLightUpwardDiagonal */
221 { 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc }, /* HatchStyleDarkDownwardDiagonal */
222 { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 }, /* HatchStyleDarkUpwardDiagonal */
223 { 0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0 }, /* HatchStyleWideDownwardDiagonal */
224 { 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1 }, /* HatchStyleWideUpwardDiagonal */
225 { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, /* HatchStyleLightVertical */
226 { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleLightHorizontal */
227 { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, /* HatchStyleNarrowVertical */
228 { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */
229 { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */
230 { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */
233 GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result)
235 if (hatchstyle < ARRAY_SIZE(HatchBrushes))
237 *result = HatchBrushes[hatchstyle];
238 return Ok;
240 else
241 return NotImplemented;
244 /******************************************************************************
245 * GdipCreateHatchBrush [GDIPLUS.@]
247 GpStatus WINGDIPAPI GdipCreateHatchBrush(GpHatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
249 TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
251 if(!brush) return InvalidParameter;
253 if(hatchstyle < HatchStyleMin || hatchstyle > HatchStyleMax)
254 return InvalidParameter;
256 *brush = heap_alloc_zero(sizeof(GpHatch));
257 if (!*brush) return OutOfMemory;
259 (*brush)->brush.bt = BrushTypeHatchFill;
260 (*brush)->forecol = forecol;
261 (*brush)->backcol = backcol;
262 (*brush)->hatchstyle = hatchstyle;
263 TRACE("<-- %p\n", *brush);
265 return Ok;
268 static void linegradient_init_transform(GpLineGradient *line)
270 float trans_x = line->rect.X + (line->rect.Width / 2.f);
271 float trans_y = line->rect.Y + (line->rect.Height / 2.f);
272 float dx = line->endpoint.X - line->startpoint.X;
273 float dy = line->endpoint.Y - line->startpoint.Y;
274 float t_cos, t_sin, w_ratio, h_ratio;
275 float h;
276 GpMatrix rot;
278 h = sqrtf(dx * dx + dy * dy);
280 t_cos = dx / h;
281 t_sin = dy / h;
283 w_ratio = (fabs(t_cos) * line->rect.Width + fabs(t_sin) * line->rect.Height) / line->rect.Width;
284 h_ratio = (fabs(t_sin) * line->rect.Width + fabs(t_cos) * line->rect.Height) / line->rect.Height;
286 GdipSetMatrixElements(&line->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
288 GdipSetMatrixElements(&rot, t_cos, t_sin, -1.f * t_sin, t_cos, 0, 0);
290 /* center about the origin */
291 GdipTranslateMatrix(&line->transform, -trans_x, -trans_y, MatrixOrderAppend);
293 /* scale to normalize gradient along gradient line (?) */
294 GdipScaleMatrix(&line->transform, w_ratio, h_ratio, MatrixOrderAppend);
296 /* rotate so the gradient is horizontal */
297 GdipMultiplyMatrix(&line->transform, &rot, MatrixOrderAppend);
299 /* restore original offset in new coords */
300 GdipTranslateMatrix(&line->transform, trans_x, trans_y, MatrixOrderAppend);
303 /******************************************************************************
304 * GdipCreateLineBrush [GDIPLUS.@]
306 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
307 GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
308 GpWrapMode wrap, GpLineGradient **line)
310 TRACE("(%s, %s, %x, %x, %d, %p)\n", debugstr_pointf(startpoint),
311 debugstr_pointf(endpoint), startcolor, endcolor, wrap, line);
313 if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
314 return InvalidParameter;
316 if (startpoint->X == endpoint->X && startpoint->Y == endpoint->Y)
317 return OutOfMemory;
319 *line = heap_alloc_zero(sizeof(GpLineGradient));
320 if(!*line) return OutOfMemory;
322 (*line)->brush.bt = BrushTypeLinearGradient;
324 (*line)->startpoint.X = startpoint->X;
325 (*line)->startpoint.Y = startpoint->Y;
326 (*line)->endpoint.X = endpoint->X;
327 (*line)->endpoint.Y = endpoint->Y;
328 (*line)->startcolor = startcolor;
329 (*line)->endcolor = endcolor;
330 (*line)->wrap = wrap;
331 (*line)->gamma = FALSE;
333 (*line)->rect.X = (startpoint->X < endpoint->X ? startpoint->X: endpoint->X);
334 (*line)->rect.Y = (startpoint->Y < endpoint->Y ? startpoint->Y: endpoint->Y);
335 (*line)->rect.Width = fabs(startpoint->X - endpoint->X);
336 (*line)->rect.Height = fabs(startpoint->Y - endpoint->Y);
338 if ((*line)->rect.Width == 0)
340 (*line)->rect.X -= (*line)->rect.Height / 2.0f;
341 (*line)->rect.Width = (*line)->rect.Height;
343 else if ((*line)->rect.Height == 0)
345 (*line)->rect.Y -= (*line)->rect.Width / 2.0f;
346 (*line)->rect.Height = (*line)->rect.Width;
349 (*line)->blendcount = 1;
350 (*line)->blendfac = heap_alloc_zero(sizeof(REAL));
351 (*line)->blendpos = heap_alloc_zero(sizeof(REAL));
353 if (!(*line)->blendfac || !(*line)->blendpos)
355 heap_free((*line)->blendfac);
356 heap_free((*line)->blendpos);
357 heap_free(*line);
358 *line = NULL;
359 return OutOfMemory;
362 (*line)->blendfac[0] = 1.0f;
363 (*line)->blendpos[0] = 1.0f;
365 (*line)->pblendcolor = NULL;
366 (*line)->pblendpos = NULL;
367 (*line)->pblendcount = 0;
369 linegradient_init_transform(*line);
371 TRACE("<-- %p\n", *line);
373 return Ok;
376 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
377 GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
378 GpWrapMode wrap, GpLineGradient **line)
380 GpPointF stF;
381 GpPointF endF;
383 TRACE("(%p, %p, %x, %x, %d, %p)\n", startpoint, endpoint,
384 startcolor, endcolor, wrap, line);
386 if(!startpoint || !endpoint)
387 return InvalidParameter;
389 stF.X = (REAL)startpoint->X;
390 stF.Y = (REAL)startpoint->Y;
391 endF.X = (REAL)endpoint->X;
392 endF.Y = (REAL)endpoint->Y;
394 return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
397 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
398 ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
399 GpLineGradient **line)
401 GpPointF start, end;
402 GpStatus stat;
403 float far_x, far_y;
405 TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
406 wrap, line);
408 if(!line || !rect)
409 return InvalidParameter;
411 far_x = rect->X + rect->Width;
412 far_y = rect->Y + rect->Height;
414 switch (mode)
416 case LinearGradientModeHorizontal:
417 start.X = min(rect->X, far_x);
418 start.Y = rect->Y;
419 end.X = max(rect->X, far_x);
420 end.Y = rect->Y;
421 break;
422 case LinearGradientModeVertical:
423 start.X = rect->X;
424 start.Y = min(rect->Y, far_y);
425 end.X = rect->X;
426 end.Y = max(rect->Y, far_y);
427 break;
428 case LinearGradientModeForwardDiagonal:
429 start.X = min(rect->X, far_x);
430 start.Y = min(rect->Y, far_y);
431 end.X = max(rect->X, far_x);
432 end.Y = max(rect->Y, far_y);
433 break;
434 case LinearGradientModeBackwardDiagonal:
435 start.X = max(rect->X, far_x);
436 start.Y = min(rect->Y, far_y);
437 end.X = min(rect->X, far_x);
438 end.Y = max(rect->Y, far_y);
439 break;
440 default:
441 return InvalidParameter;
444 stat = GdipCreateLineBrush(&start, &end, startcolor, endcolor, wrap, line);
446 if (stat == Ok)
447 (*line)->rect = *rect;
449 return stat;
452 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
453 ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
454 GpLineGradient **line)
456 GpRectF rectF;
458 TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
459 wrap, line);
461 rectF.X = (REAL) rect->X;
462 rectF.Y = (REAL) rect->Y;
463 rectF.Width = (REAL) rect->Width;
464 rectF.Height = (REAL) rect->Height;
466 return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
469 /******************************************************************************
470 * GdipCreateLineBrushFromRectWithAngle [GDIPLUS.@]
472 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect,
473 ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
474 GpLineGradient **line)
476 GpStatus stat;
477 LinearGradientMode mode;
478 REAL exofs, eyofs;
479 REAL sin_angle, cos_angle, sin_cos_angle;
481 TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
482 wrap, line);
484 if (!rect || !line || wrap == WrapModeClamp)
485 return InvalidParameter;
487 if (!rect->Width || !rect->Height)
488 return OutOfMemory;
490 angle = fmodf(angle, 360);
491 if (angle < 0)
492 angle += 360;
494 if (isAngleScalable)
496 float add_angle = 0;
498 while(angle >= 90) {
499 angle -= 180;
500 add_angle += M_PI;
503 if (angle != 90 && angle != -90)
504 angle = atan((rect->Width / rect->Height) * tan(deg2rad(angle)));
505 else
506 angle = deg2rad(angle);
507 angle += add_angle;
509 else
511 angle = deg2rad(angle);
514 sin_angle = sinf(angle);
515 cos_angle = cosf(angle);
516 sin_cos_angle = sin_angle * cos_angle;
518 if (sin_cos_angle >= 0)
519 mode = LinearGradientModeForwardDiagonal;
520 else
521 mode = LinearGradientModeBackwardDiagonal;
523 stat = GdipCreateLineBrushFromRect(rect, startcolor, endcolor, mode, wrap, line);
525 if (stat == Ok)
527 if (sin_cos_angle >= 0)
529 exofs = rect->Height * sin_cos_angle + rect->Width * cos_angle * cos_angle;
530 eyofs = rect->Height * sin_angle * sin_angle + rect->Width * sin_cos_angle;
532 else
534 exofs = rect->Width * sin_angle * sin_angle + rect->Height * sin_cos_angle;
535 eyofs = -rect->Width * sin_cos_angle + rect->Height * sin_angle * sin_angle;
538 if (sin_angle >= 0)
540 (*line)->endpoint.X = rect->X + exofs;
541 (*line)->endpoint.Y = rect->Y + eyofs;
543 else
545 (*line)->endpoint.X = (*line)->startpoint.X;
546 (*line)->endpoint.Y = (*line)->startpoint.Y;
547 (*line)->startpoint.X = rect->X + exofs;
548 (*line)->startpoint.Y = rect->Y + eyofs;
551 linegradient_init_transform(*line);
554 return stat;
557 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect,
558 ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
559 GpLineGradient **line)
561 TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
562 wrap, line);
564 return GdipCreateLineBrushFromRectI(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
565 wrap, line);
568 static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradient **grad)
570 GpRectF bounds;
572 if(!path || !grad)
573 return InvalidParameter;
575 if (path->pathdata.Count < 2)
576 return OutOfMemory;
578 GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
580 *grad = heap_alloc_zero(sizeof(GpPathGradient));
581 if (!*grad)
583 return OutOfMemory;
586 GdipSetMatrixElements(&(*grad)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
588 (*grad)->blendfac = heap_alloc_zero(sizeof(REAL));
589 (*grad)->blendpos = heap_alloc_zero(sizeof(REAL));
590 (*grad)->surroundcolors = heap_alloc_zero(sizeof(ARGB));
591 if(!(*grad)->blendfac || !(*grad)->blendpos || !(*grad)->surroundcolors){
592 heap_free((*grad)->blendfac);
593 heap_free((*grad)->blendpos);
594 heap_free((*grad)->surroundcolors);
595 heap_free(*grad);
596 *grad = NULL;
597 return OutOfMemory;
599 (*grad)->blendfac[0] = 1.0;
600 (*grad)->blendpos[0] = 1.0;
601 (*grad)->blendcount = 1;
603 (*grad)->path = path;
605 (*grad)->brush.bt = BrushTypePathGradient;
606 (*grad)->centercolor = centercolor;
607 (*grad)->wrap = WrapModeClamp;
608 (*grad)->gamma = FALSE;
609 /* FIXME: this should be set to the "centroid" of the path by default */
610 (*grad)->center.X = bounds.X + bounds.Width / 2;
611 (*grad)->center.Y = bounds.Y + bounds.Height / 2;
612 (*grad)->focus.X = 0.0;
613 (*grad)->focus.Y = 0.0;
614 (*grad)->surroundcolors[0] = 0xffffffff;
615 (*grad)->surroundcolorcount = 1;
617 TRACE("<-- %p\n", *grad);
619 return Ok;
622 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
623 INT count, GpWrapMode wrap, GpPathGradient **grad)
625 GpStatus stat;
626 GpPath *path;
628 TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
630 if(!grad)
631 return InvalidParameter;
633 if(!points || count <= 0)
634 return OutOfMemory;
636 stat = GdipCreatePath(FillModeAlternate, &path);
638 if (stat == Ok)
640 stat = GdipAddPathLine2(path, points, count);
642 if (stat == Ok)
643 stat = create_path_gradient(path, 0xff000000, grad);
645 if (stat != Ok)
646 GdipDeletePath(path);
649 if (stat == Ok)
650 (*grad)->wrap = wrap;
652 return stat;
655 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
656 INT count, GpWrapMode wrap, GpPathGradient **grad)
658 GpStatus stat;
659 GpPath *path;
661 TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
663 if(!grad)
664 return InvalidParameter;
666 if(!points || count <= 0)
667 return OutOfMemory;
669 stat = GdipCreatePath(FillModeAlternate, &path);
671 if (stat == Ok)
673 stat = GdipAddPathLine2I(path, points, count);
675 if (stat == Ok)
676 stat = create_path_gradient(path, 0xff000000, grad);
678 if (stat != Ok)
679 GdipDeletePath(path);
682 if (stat == Ok)
683 (*grad)->wrap = wrap;
685 return stat;
688 /******************************************************************************
689 * GdipCreatePathGradientFromPath [GDIPLUS.@]
691 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
692 GpPathGradient **grad)
694 GpStatus stat;
695 GpPath *new_path;
697 TRACE("(%p, %p)\n", path, grad);
699 if(!grad)
700 return InvalidParameter;
702 if (!path)
703 return OutOfMemory;
705 stat = GdipClonePath((GpPath*)path, &new_path);
707 if (stat == Ok)
709 stat = create_path_gradient(new_path, 0xffffffff, grad);
711 if (stat != Ok)
712 GdipDeletePath(new_path);
715 return stat;
718 /******************************************************************************
719 * GdipCreateSolidFill [GDIPLUS.@]
721 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
723 TRACE("(%x, %p)\n", color, sf);
725 if(!sf) return InvalidParameter;
727 *sf = heap_alloc_zero(sizeof(GpSolidFill));
728 if (!*sf) return OutOfMemory;
730 (*sf)->brush.bt = BrushTypeSolidColor;
731 (*sf)->color = color;
733 TRACE("<-- %p\n", *sf);
735 return Ok;
738 /******************************************************************************
739 * GdipCreateTexture [GDIPLUS.@]
741 * PARAMS
742 * image [I] image to use
743 * wrapmode [I] optional
744 * texture [O] pointer to the resulting texturebrush
746 * RETURNS
747 * SUCCESS: Ok
748 * FAILURE: element of GpStatus
750 GpStatus WINGDIPAPI GdipCreateTexture(GpImage *image, GpWrapMode wrapmode,
751 GpTexture **texture)
753 UINT width, height;
754 GpImageAttributes *attributes;
755 GpStatus stat;
757 TRACE("%p, %d %p\n", image, wrapmode, texture);
759 if (!(image && texture))
760 return InvalidParameter;
762 stat = GdipGetImageWidth(image, &width);
763 if (stat != Ok) return stat;
764 stat = GdipGetImageHeight(image, &height);
765 if (stat != Ok) return stat;
767 stat = GdipCreateImageAttributes(&attributes);
769 if (stat == Ok)
771 attributes->wrap = wrapmode;
773 stat = GdipCreateTextureIA(image, attributes, 0, 0, width, height,
774 texture);
776 GdipDisposeImageAttributes(attributes);
779 return stat;
782 /******************************************************************************
783 * GdipCreateTexture2 [GDIPLUS.@]
785 GpStatus WINGDIPAPI GdipCreateTexture2(GpImage *image, GpWrapMode wrapmode,
786 REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
788 GpImageAttributes *attributes;
789 GpStatus stat;
791 TRACE("%p %d %f %f %f %f %p\n", image, wrapmode,
792 x, y, width, height, texture);
794 stat = GdipCreateImageAttributes(&attributes);
796 if (stat == Ok)
798 attributes->wrap = wrapmode;
800 stat = GdipCreateTextureIA(image, attributes, x, y, width, height,
801 texture);
803 GdipDisposeImageAttributes(attributes);
806 return stat;
809 /******************************************************************************
810 * GdipCreateTextureIA [GDIPLUS.@]
812 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
813 GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
814 REAL height, GpTexture **texture)
816 GpStatus status;
817 GpImage *new_image=NULL;
819 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %p)\n", image, imageattr, x, y, width, height,
820 texture);
822 if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
823 return InvalidParameter;
825 *texture = NULL;
827 if(image->type != ImageTypeBitmap){
828 FIXME("not implemented for image type %d\n", image->type);
829 return NotImplemented;
832 status = GdipCloneBitmapArea(x, y, width, height, PixelFormatDontCare, (GpBitmap*)image, (GpBitmap**)&new_image);
833 if (status != Ok)
834 return status;
836 *texture = heap_alloc_zero(sizeof(GpTexture));
837 if (!*texture){
838 status = OutOfMemory;
839 goto exit;
842 GdipSetMatrixElements(&(*texture)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
844 if (imageattr)
846 status = GdipCloneImageAttributes(imageattr, &(*texture)->imageattributes);
848 else
850 status = GdipCreateImageAttributes(&(*texture)->imageattributes);
851 if (status == Ok)
852 (*texture)->imageattributes->wrap = WrapModeTile;
854 if (status == Ok)
856 (*texture)->brush.bt = BrushTypeTextureFill;
857 (*texture)->image = new_image;
860 exit:
861 if (status == Ok)
863 TRACE("<-- %p\n", *texture);
865 else
867 if (*texture)
869 GdipDisposeImageAttributes((*texture)->imageattributes);
870 heap_free(*texture);
871 *texture = NULL;
873 GdipDisposeImage(new_image);
874 TRACE("<-- error %u\n", status);
877 return status;
880 /******************************************************************************
881 * GdipCreateTextureIAI [GDIPLUS.@]
883 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
884 INT x, INT y, INT width, INT height, GpTexture **texture)
886 TRACE("(%p, %p, %d, %d, %d, %d, %p)\n", image, imageattr, x, y, width, height,
887 texture);
889 return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
892 GpStatus WINGDIPAPI GdipCreateTexture2I(GpImage *image, GpWrapMode wrapmode,
893 INT x, INT y, INT width, INT height, GpTexture **texture)
895 GpImageAttributes *imageattr;
896 GpStatus stat;
898 TRACE("%p %d %d %d %d %d %p\n", image, wrapmode, x, y, width, height,
899 texture);
901 stat = GdipCreateImageAttributes(&imageattr);
903 if (stat == Ok)
905 imageattr->wrap = wrapmode;
907 stat = GdipCreateTextureIA(image, imageattr, x, y, width, height, texture);
908 GdipDisposeImageAttributes(imageattr);
911 return stat;
914 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
916 TRACE("(%p, %p)\n", brush, type);
918 if(!brush || !type) return InvalidParameter;
920 *type = brush->bt;
922 return Ok;
925 GpStatus WINGDIPAPI GdipGetHatchBackgroundColor(GpHatch *brush, ARGB *backcol)
927 TRACE("(%p, %p)\n", brush, backcol);
929 if(!brush || !backcol) return InvalidParameter;
931 *backcol = brush->backcol;
933 return Ok;
936 GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
938 TRACE("(%p, %p)\n", brush, forecol);
940 if(!brush || !forecol) return InvalidParameter;
942 *forecol = brush->forecol;
944 return Ok;
947 GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, GpHatchStyle *hatchstyle)
949 TRACE("(%p, %p)\n", brush, hatchstyle);
951 if(!brush || !hatchstyle) return InvalidParameter;
953 *hatchstyle = brush->hatchstyle;
955 return Ok;
958 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
960 TRACE("(%p)\n", brush);
962 if(!brush) return InvalidParameter;
964 switch(brush->bt)
966 case BrushTypePathGradient:
967 GdipDeletePath(((GpPathGradient*) brush)->path);
968 heap_free(((GpPathGradient*) brush)->blendfac);
969 heap_free(((GpPathGradient*) brush)->blendpos);
970 heap_free(((GpPathGradient*) brush)->surroundcolors);
971 heap_free(((GpPathGradient*) brush)->pblendcolor);
972 heap_free(((GpPathGradient*) brush)->pblendpos);
973 break;
974 case BrushTypeLinearGradient:
975 heap_free(((GpLineGradient*)brush)->blendfac);
976 heap_free(((GpLineGradient*)brush)->blendpos);
977 heap_free(((GpLineGradient*)brush)->pblendcolor);
978 heap_free(((GpLineGradient*)brush)->pblendpos);
979 break;
980 case BrushTypeTextureFill:
981 GdipDisposeImage(((GpTexture*)brush)->image);
982 GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes);
983 heap_free(((GpTexture*)brush)->bitmap_bits);
984 break;
985 default:
986 break;
989 heap_free(brush);
991 return Ok;
994 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
995 BOOL *usinggamma)
997 TRACE("(%p, %p)\n", line, usinggamma);
999 if(!line || !usinggamma)
1000 return InvalidParameter;
1002 *usinggamma = line->gamma;
1004 return Ok;
1007 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
1009 TRACE("(%p, %p)\n", brush, wrapmode);
1011 if(!brush || !wrapmode || brush->brush.bt != BrushTypeLinearGradient)
1012 return InvalidParameter;
1014 *wrapmode = brush->wrap;
1016 return Ok;
1019 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
1020 REAL *positions, INT count)
1022 TRACE("(%p, %p, %p, %d)\n", brush, blend, positions, count);
1024 if(!brush || !blend || !positions || count <= 0 || brush->brush.bt != BrushTypePathGradient)
1025 return InvalidParameter;
1027 if(count < brush->blendcount)
1028 return InsufficientBuffer;
1030 memcpy(blend, brush->blendfac, count*sizeof(REAL));
1031 if(brush->blendcount > 1){
1032 memcpy(positions, brush->blendpos, count*sizeof(REAL));
1035 return Ok;
1038 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
1040 TRACE("(%p, %p)\n", brush, count);
1042 if(!brush || !count || brush->brush.bt != BrushTypePathGradient)
1043 return InvalidParameter;
1045 *count = brush->blendcount;
1047 return Ok;
1050 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
1051 GpPointF *point)
1053 TRACE("(%p, %p)\n", grad, point);
1055 if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1056 return InvalidParameter;
1058 point->X = grad->center.X;
1059 point->Y = grad->center.Y;
1061 return Ok;
1064 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
1065 GpPoint *point)
1067 GpStatus ret;
1068 GpPointF ptf;
1070 TRACE("(%p, %p)\n", grad, point);
1072 if(!point)
1073 return InvalidParameter;
1075 ret = GdipGetPathGradientCenterPoint(grad,&ptf);
1077 if(ret == Ok){
1078 point->X = gdip_round(ptf.X);
1079 point->Y = gdip_round(ptf.Y);
1082 return ret;
1085 GpStatus WINGDIPAPI GdipGetPathGradientCenterColor(GpPathGradient *grad,
1086 ARGB *colors)
1088 TRACE("(%p,%p)\n", grad, colors);
1090 if (!grad || !colors || grad->brush.bt != BrushTypePathGradient)
1091 return InvalidParameter;
1093 *colors = grad->centercolor;
1095 return Ok;
1098 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
1099 REAL *x, REAL *y)
1101 TRACE("(%p, %p, %p)\n", grad, x, y);
1103 if(!grad || !x || !y || grad->brush.bt != BrushTypePathGradient)
1104 return InvalidParameter;
1106 *x = grad->focus.X;
1107 *y = grad->focus.Y;
1109 return Ok;
1112 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
1113 BOOL *gamma)
1115 TRACE("(%p, %p)\n", grad, gamma);
1117 if(!grad || !gamma || grad->brush.bt != BrushTypePathGradient)
1118 return InvalidParameter;
1120 *gamma = grad->gamma;
1122 return Ok;
1125 GpStatus WINGDIPAPI GdipGetPathGradientPath(GpPathGradient *grad, GpPath *path)
1127 static int calls;
1129 TRACE("(%p, %p)\n", grad, path);
1131 if (!(calls++))
1132 FIXME("not implemented\n");
1134 return NotImplemented;
1137 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
1138 INT *count)
1140 TRACE("(%p, %p)\n", grad, count);
1142 if(!grad || !count || grad->brush.bt != BrushTypePathGradient)
1143 return InvalidParameter;
1145 *count = grad->path->pathdata.Count;
1147 return Ok;
1150 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
1152 GpStatus stat;
1154 TRACE("(%p, %p)\n", brush, rect);
1156 if(!brush || !rect || brush->brush.bt != BrushTypePathGradient)
1157 return InvalidParameter;
1159 stat = GdipGetPathWorldBounds(brush->path, rect, NULL, NULL);
1161 return stat;
1164 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
1166 GpRectF rectf;
1167 GpStatus stat;
1169 TRACE("(%p, %p)\n", brush, rect);
1171 if(!brush || !rect)
1172 return InvalidParameter;
1174 stat = GdipGetPathGradientRect(brush, &rectf);
1175 if(stat != Ok) return stat;
1177 rect->X = gdip_round(rectf.X);
1178 rect->Y = gdip_round(rectf.Y);
1179 rect->Width = gdip_round(rectf.Width);
1180 rect->Height = gdip_round(rectf.Height);
1182 return Ok;
1185 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
1186 *grad, ARGB *argb, INT *count)
1188 INT i;
1190 TRACE("(%p,%p,%p)\n", grad, argb, count);
1192 if(!grad || !argb || !count || (*count < grad->path->pathdata.Count) || grad->brush.bt != BrushTypePathGradient)
1193 return InvalidParameter;
1195 for (i=0; i<grad->path->pathdata.Count; i++)
1197 if (i < grad->surroundcolorcount)
1198 argb[i] = grad->surroundcolors[i];
1199 else
1200 argb[i] = grad->surroundcolors[grad->surroundcolorcount-1];
1203 *count = grad->surroundcolorcount;
1205 return Ok;
1208 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient *brush, INT *count)
1210 TRACE("(%p, %p)\n", brush, count);
1212 if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1213 return InvalidParameter;
1215 /* Yes, this actually returns the number of points in the path (which is the
1216 * required size of a buffer to get the surround colors), rather than the
1217 * number of surround colors. The real count is returned when getting the
1218 * colors. */
1219 *count = brush->path->pathdata.Count;
1221 return Ok;
1224 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
1225 GpWrapMode *wrapmode)
1227 TRACE("(%p, %p)\n", brush, wrapmode);
1229 if(!brush || !wrapmode || brush->brush.bt != BrushTypePathGradient)
1230 return InvalidParameter;
1232 *wrapmode = brush->wrap;
1234 return Ok;
1237 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
1239 TRACE("(%p, %p)\n", sf, argb);
1241 if(!sf || !argb)
1242 return InvalidParameter;
1244 *argb = sf->color;
1246 return Ok;
1249 /******************************************************************************
1250 * GdipGetTextureImage [GDIPLUS.@]
1252 GpStatus WINGDIPAPI GdipGetTextureImage(GpTexture *brush, GpImage **image)
1254 TRACE("(%p, %p)\n", brush, image);
1256 if(!brush || !image)
1257 return InvalidParameter;
1259 return GdipCloneImage(brush->image, image);
1262 /******************************************************************************
1263 * GdipGetTextureTransform [GDIPLUS.@]
1265 GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
1267 TRACE("(%p, %p)\n", brush, matrix);
1269 if(!brush || !matrix)
1270 return InvalidParameter;
1272 *matrix = brush->transform;
1274 return Ok;
1277 /******************************************************************************
1278 * GdipGetTextureWrapMode [GDIPLUS.@]
1280 GpStatus WINGDIPAPI GdipGetTextureWrapMode(GpTexture *brush, GpWrapMode *wrapmode)
1282 TRACE("(%p, %p)\n", brush, wrapmode);
1284 if(!brush || !wrapmode)
1285 return InvalidParameter;
1287 *wrapmode = brush->imageattributes->wrap;
1289 return Ok;
1292 /******************************************************************************
1293 * GdipMultiplyTextureTransform [GDIPLUS.@]
1295 GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
1296 GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1298 TRACE("(%p, %p, %d)\n", brush, matrix, order);
1300 if(!brush || !matrix)
1301 return InvalidParameter;
1303 return GdipMultiplyMatrix(&brush->transform, matrix, order);
1306 /******************************************************************************
1307 * GdipResetTextureTransform [GDIPLUS.@]
1309 GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
1311 TRACE("(%p)\n", brush);
1313 if(!brush)
1314 return InvalidParameter;
1316 return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1319 /******************************************************************************
1320 * GdipScaleTextureTransform [GDIPLUS.@]
1322 GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
1323 REAL sx, REAL sy, GpMatrixOrder order)
1325 TRACE("(%p, %.2f, %.2f, %d)\n", brush, sx, sy, order);
1327 if(!brush)
1328 return InvalidParameter;
1330 return GdipScaleMatrix(&brush->transform, sx, sy, order);
1333 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
1334 GDIPCONST REAL *factors, GDIPCONST REAL* positions, INT count)
1336 REAL *new_blendfac, *new_blendpos;
1338 TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1340 if(!brush || !factors || !positions || count <= 0 || brush->brush.bt != BrushTypeLinearGradient ||
1341 (count >= 2 && (positions[0] != 0.0f || positions[count-1] != 1.0f)))
1342 return InvalidParameter;
1344 new_blendfac = heap_alloc_zero(count * sizeof(REAL));
1345 new_blendpos = heap_alloc_zero(count * sizeof(REAL));
1347 if (!new_blendfac || !new_blendpos)
1349 heap_free(new_blendfac);
1350 heap_free(new_blendpos);
1351 return OutOfMemory;
1354 memcpy(new_blendfac, factors, count * sizeof(REAL));
1355 memcpy(new_blendpos, positions, count * sizeof(REAL));
1357 heap_free(brush->blendfac);
1358 heap_free(brush->blendpos);
1360 brush->blendcount = count;
1361 brush->blendfac = new_blendfac;
1362 brush->blendpos = new_blendpos;
1364 return Ok;
1367 GpStatus WINGDIPAPI GdipGetLineBlend(GpLineGradient *brush, REAL *factors,
1368 REAL *positions, INT count)
1370 TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1372 if (!brush || !factors || !positions || count <= 0 || brush->brush.bt != BrushTypeLinearGradient)
1373 return InvalidParameter;
1375 if (count < brush->blendcount)
1376 return InsufficientBuffer;
1378 memcpy(factors, brush->blendfac, brush->blendcount * sizeof(REAL));
1379 memcpy(positions, brush->blendpos, brush->blendcount * sizeof(REAL));
1381 return Ok;
1384 GpStatus WINGDIPAPI GdipGetLineBlendCount(GpLineGradient *brush, INT *count)
1386 TRACE("(%p, %p)\n", brush, count);
1388 if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
1389 return InvalidParameter;
1391 *count = brush->blendcount;
1393 return Ok;
1396 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
1397 BOOL usegamma)
1399 TRACE("(%p, %d)\n", line, usegamma);
1401 if(!line || line->brush.bt != BrushTypeLinearGradient)
1402 return InvalidParameter;
1404 line->gamma = usegamma;
1406 return Ok;
1409 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
1410 REAL scale)
1412 REAL factors[33];
1413 REAL positions[33];
1414 int num_points = 0;
1415 int i;
1416 const int precision = 16;
1417 REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1418 REAL min_erf;
1419 REAL scale_erf;
1421 TRACE("(%p, %0.2f, %0.2f)\n", line, focus, scale);
1423 if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || line->brush.bt != BrushTypeLinearGradient)
1424 return InvalidParameter;
1426 /* we want 2 standard deviations */
1427 erf_range = 2.0 / sqrt(2);
1429 /* calculate the constants we need to normalize the error function to be
1430 between 0.0 and scale over the range we need */
1431 min_erf = erf(-erf_range);
1432 scale_erf = scale / (-2.0 * min_erf);
1434 if (focus != 0.0)
1436 positions[0] = 0.0;
1437 factors[0] = 0.0;
1438 for (i=1; i<precision; i++)
1440 positions[i] = focus * i / precision;
1441 factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1443 num_points += precision;
1446 positions[num_points] = focus;
1447 factors[num_points] = scale;
1448 num_points += 1;
1450 if (focus != 1.0)
1452 for (i=1; i<precision; i++)
1454 positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1455 factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1457 num_points += precision;
1458 positions[num_points-1] = 1.0;
1459 factors[num_points-1] = 0.0;
1462 return GdipSetLineBlend(line, factors, positions, num_points);
1465 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
1466 GpWrapMode wrap)
1468 TRACE("(%p, %d)\n", line, wrap);
1470 if(!line || wrap == WrapModeClamp || line->brush.bt != BrushTypeLinearGradient)
1471 return InvalidParameter;
1473 line->wrap = wrap;
1475 return Ok;
1478 GpStatus WINGDIPAPI GdipSetPathGradientBlend(GpPathGradient *brush, GDIPCONST REAL *blend,
1479 GDIPCONST REAL *pos, INT count)
1481 REAL *new_blendfac, *new_blendpos;
1483 TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1485 if(!brush || !blend || !pos || count <= 0 || brush->brush.bt != BrushTypePathGradient ||
1486 (count >= 2 && (pos[0] != 0.0f || pos[count-1] != 1.0f)))
1487 return InvalidParameter;
1489 new_blendfac = heap_alloc_zero(count * sizeof(REAL));
1490 new_blendpos = heap_alloc_zero(count * sizeof(REAL));
1492 if (!new_blendfac || !new_blendpos)
1494 heap_free(new_blendfac);
1495 heap_free(new_blendpos);
1496 return OutOfMemory;
1499 memcpy(new_blendfac, blend, count * sizeof(REAL));
1500 memcpy(new_blendpos, pos, count * sizeof(REAL));
1502 heap_free(brush->blendfac);
1503 heap_free(brush->blendpos);
1505 brush->blendcount = count;
1506 brush->blendfac = new_blendfac;
1507 brush->blendpos = new_blendpos;
1509 return Ok;
1512 GpStatus WINGDIPAPI GdipSetPathGradientLinearBlend(GpPathGradient *brush,
1513 REAL focus, REAL scale)
1515 REAL factors[3];
1516 REAL positions[3];
1517 int num_points = 0;
1519 TRACE("(%p,%0.2f,%0.2f)\n", brush, focus, scale);
1521 if (!brush || brush->brush.bt != BrushTypePathGradient)
1522 return InvalidParameter;
1524 if (focus != 0.0)
1526 factors[num_points] = 0.0;
1527 positions[num_points] = 0.0;
1528 num_points++;
1531 factors[num_points] = scale;
1532 positions[num_points] = focus;
1533 num_points++;
1535 if (focus != 1.0)
1537 factors[num_points] = 0.0;
1538 positions[num_points] = 1.0;
1539 num_points++;
1542 return GdipSetPathGradientBlend(brush, factors, positions, num_points);
1545 GpStatus WINGDIPAPI GdipSetPathGradientPresetBlend(GpPathGradient *brush,
1546 GDIPCONST ARGB *blend, GDIPCONST REAL *pos, INT count)
1548 ARGB *new_color;
1549 REAL *new_pos;
1550 TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1552 if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient ||
1553 pos[0] != 0.0f || pos[count-1] != 1.0f)
1555 return InvalidParameter;
1558 new_color = heap_alloc_zero(count * sizeof(ARGB));
1559 new_pos = heap_alloc_zero(count * sizeof(REAL));
1560 if (!new_color || !new_pos)
1562 heap_free(new_color);
1563 heap_free(new_pos);
1564 return OutOfMemory;
1567 memcpy(new_color, blend, sizeof(ARGB) * count);
1568 memcpy(new_pos, pos, sizeof(REAL) * count);
1570 heap_free(brush->pblendcolor);
1571 heap_free(brush->pblendpos);
1573 brush->pblendcolor = new_color;
1574 brush->pblendpos = new_pos;
1575 brush->pblendcount = count;
1577 return Ok;
1580 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlend(GpPathGradient *brush,
1581 ARGB *blend, REAL *pos, INT count)
1583 TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1585 if (count < 0)
1586 return OutOfMemory;
1588 if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient)
1589 return InvalidParameter;
1591 if (brush->pblendcount == 0)
1592 return GenericError;
1594 if (count != brush->pblendcount)
1596 /* Native lines up the ends of each array, and copies the destination size. */
1597 FIXME("Braindead behavior on wrong-sized buffer not implemented.\n");
1598 return InvalidParameter;
1601 memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1602 memcpy(pos, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1604 return Ok;
1607 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlendCount(GpPathGradient *brush,
1608 INT *count)
1610 TRACE("(%p,%p)\n", brush, count);
1612 if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1613 return InvalidParameter;
1615 *count = brush->pblendcount;
1617 return Ok;
1620 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1621 ARGB argb)
1623 TRACE("(%p, %x)\n", grad, argb);
1625 if(!grad || grad->brush.bt != BrushTypePathGradient)
1626 return InvalidParameter;
1628 grad->centercolor = argb;
1629 return Ok;
1632 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1633 GpPointF *point)
1635 TRACE("(%p, %s)\n", grad, debugstr_pointf(point));
1637 if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1638 return InvalidParameter;
1640 grad->center.X = point->X;
1641 grad->center.Y = point->Y;
1643 return Ok;
1646 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1647 GpPoint *point)
1649 GpPointF ptf;
1651 TRACE("(%p, %p)\n", grad, point);
1653 if(!point)
1654 return InvalidParameter;
1656 ptf.X = (REAL)point->X;
1657 ptf.Y = (REAL)point->Y;
1659 return GdipSetPathGradientCenterPoint(grad,&ptf);
1662 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1663 REAL x, REAL y)
1665 TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1667 if(!grad || grad->brush.bt != BrushTypePathGradient)
1668 return InvalidParameter;
1670 grad->focus.X = x;
1671 grad->focus.Y = y;
1673 return Ok;
1676 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1677 BOOL gamma)
1679 TRACE("(%p, %d)\n", grad, gamma);
1681 if(!grad || grad->brush.bt != BrushTypePathGradient)
1682 return InvalidParameter;
1684 grad->gamma = gamma;
1686 return Ok;
1689 GpStatus WINGDIPAPI GdipSetPathGradientPath(GpPathGradient *grad, GDIPCONST GpPath *path)
1691 static int calls;
1693 TRACE("(%p, %p)\n", grad, path);
1695 if (!(calls++))
1696 FIXME("not implemented\n");
1698 return NotImplemented;
1701 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1702 REAL focus, REAL scale)
1704 REAL factors[33];
1705 REAL positions[33];
1706 int num_points = 0;
1707 int i;
1708 const int precision = 16;
1709 REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1710 REAL min_erf;
1711 REAL scale_erf;
1713 TRACE("(%p,%0.2f,%0.2f)\n", grad, focus, scale);
1715 if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || grad->brush.bt != BrushTypePathGradient)
1716 return InvalidParameter;
1718 /* we want 2 standard deviations */
1719 erf_range = 2.0 / sqrt(2);
1721 /* calculate the constants we need to normalize the error function to be
1722 between 0.0 and scale over the range we need */
1723 min_erf = erf(-erf_range);
1724 scale_erf = scale / (-2.0 * min_erf);
1726 if (focus != 0.0)
1728 positions[0] = 0.0;
1729 factors[0] = 0.0;
1730 for (i=1; i<precision; i++)
1732 positions[i] = focus * i / precision;
1733 factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1735 num_points += precision;
1738 positions[num_points] = focus;
1739 factors[num_points] = scale;
1740 num_points += 1;
1742 if (focus != 1.0)
1744 for (i=1; i<precision; i++)
1746 positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1747 factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1749 num_points += precision;
1750 positions[num_points-1] = 1.0;
1751 factors[num_points-1] = 0.0;
1754 return GdipSetPathGradientBlend(grad, factors, positions, num_points);
1757 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1758 *grad, GDIPCONST ARGB *argb, INT *count)
1760 ARGB *new_surroundcolors;
1761 INT i, num_colors;
1763 TRACE("(%p,%p,%p)\n", grad, argb, count);
1765 if(!grad || !argb || !count || (*count <= 0) || grad->brush.bt != BrushTypePathGradient ||
1766 (*count > grad->path->pathdata.Count))
1767 return InvalidParameter;
1769 num_colors = *count;
1771 /* If all colors are the same, only store 1 color. */
1772 if (*count > 1)
1774 for (i=1; i < num_colors; i++)
1775 if (argb[i] != argb[i-1])
1776 break;
1778 if (i == num_colors)
1779 num_colors = 1;
1782 new_surroundcolors = heap_alloc_zero(num_colors * sizeof(ARGB));
1783 if (!new_surroundcolors)
1784 return OutOfMemory;
1786 memcpy(new_surroundcolors, argb, num_colors * sizeof(ARGB));
1788 heap_free(grad->surroundcolors);
1790 grad->surroundcolors = new_surroundcolors;
1791 grad->surroundcolorcount = num_colors;
1793 return Ok;
1796 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1797 GpWrapMode wrap)
1799 TRACE("(%p, %d)\n", grad, wrap);
1801 if(!grad || grad->brush.bt != BrushTypePathGradient)
1802 return InvalidParameter;
1804 grad->wrap = wrap;
1806 return Ok;
1809 GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
1810 GpMatrix *matrix)
1812 TRACE("(%p,%p)\n", grad, matrix);
1814 if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1815 return InvalidParameter;
1817 grad->transform = *matrix;
1819 return Ok;
1822 GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
1823 GpMatrix *matrix)
1825 TRACE("(%p,%p)\n", grad, matrix);
1827 if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1828 return InvalidParameter;
1830 *matrix = grad->transform;
1832 return Ok;
1835 GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
1836 GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1838 TRACE("(%p,%p,%i)\n", grad, matrix, order);
1840 if (!grad || grad->brush.bt != BrushTypePathGradient)
1841 return InvalidParameter;
1843 return GdipMultiplyMatrix(&grad->transform, matrix, order);
1846 GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
1848 TRACE("(%p)\n", grad);
1850 if (!grad || grad->brush.bt != BrushTypePathGradient)
1851 return InvalidParameter;
1853 return GdipSetMatrixElements(&grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1856 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
1857 REAL angle, GpMatrixOrder order)
1859 TRACE("(%p,%0.2f,%i)\n", grad, angle, order);
1861 if (!grad || grad->brush.bt != BrushTypePathGradient)
1862 return InvalidParameter;
1864 return GdipRotateMatrix(&grad->transform, angle, order);
1867 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
1868 REAL sx, REAL sy, GpMatrixOrder order)
1870 TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, sx, sy, order);
1872 if (!grad || grad->brush.bt != BrushTypePathGradient)
1873 return InvalidParameter;
1875 return GdipScaleMatrix(&grad->transform, sx, sy, order);
1878 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
1879 REAL dx, REAL dy, GpMatrixOrder order)
1881 TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, dx, dy, order);
1883 if (!grad || grad->brush.bt != BrushTypePathGradient)
1884 return InvalidParameter;
1886 return GdipTranslateMatrix(&grad->transform, dx, dy, order);
1889 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1891 TRACE("(%p, %x)\n", sf, argb);
1893 if(!sf)
1894 return InvalidParameter;
1896 sf->color = argb;
1897 return Ok;
1900 /******************************************************************************
1901 * GdipSetTextureTransform [GDIPLUS.@]
1903 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1904 GDIPCONST GpMatrix *matrix)
1906 TRACE("(%p, %p)\n", texture, matrix);
1908 if(!texture || !matrix)
1909 return InvalidParameter;
1911 texture->transform = *matrix;
1913 return Ok;
1916 /******************************************************************************
1917 * GdipSetTextureWrapMode [GDIPLUS.@]
1919 * WrapMode not used, only stored
1921 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1923 TRACE("(%p, %d)\n", brush, wrapmode);
1925 if(!brush)
1926 return InvalidParameter;
1928 brush->imageattributes->wrap = wrapmode;
1930 return Ok;
1933 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1934 ARGB color2)
1936 TRACE("(%p, %x, %x)\n", brush, color1, color2);
1938 if(!brush || brush->brush.bt != BrushTypeLinearGradient)
1939 return InvalidParameter;
1941 brush->startcolor = color1;
1942 brush->endcolor = color2;
1944 return Ok;
1947 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1949 TRACE("(%p, %p)\n", brush, colors);
1951 if(!brush || !colors || brush->brush.bt != BrushTypeLinearGradient)
1952 return InvalidParameter;
1954 colors[0] = brush->startcolor;
1955 colors[1] = brush->endcolor;
1957 return Ok;
1960 /******************************************************************************
1961 * GdipRotateTextureTransform [GDIPLUS.@]
1963 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
1964 GpMatrixOrder order)
1966 TRACE("(%p, %.2f, %d)\n", brush, angle, order);
1968 if(!brush)
1969 return InvalidParameter;
1971 return GdipRotateMatrix(&brush->transform, angle, order);
1974 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
1975 REAL scale)
1977 REAL factors[3];
1978 REAL positions[3];
1979 int num_points = 0;
1981 TRACE("(%p,%.2f,%.2f)\n", brush, focus, scale);
1983 if (!brush) return InvalidParameter;
1985 if (focus != 0.0)
1987 factors[num_points] = 0.0;
1988 positions[num_points] = 0.0;
1989 num_points++;
1992 factors[num_points] = scale;
1993 positions[num_points] = focus;
1994 num_points++;
1996 if (focus != 1.0)
1998 factors[num_points] = 0.0;
1999 positions[num_points] = 1.0;
2000 num_points++;
2003 return GdipSetLineBlend(brush, factors, positions, num_points);
2006 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
2007 GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
2009 ARGB *new_color;
2010 REAL *new_pos;
2011 TRACE("(%p,%p,%p,%i)\n", brush, blend, positions, count);
2013 if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient ||
2014 positions[0] != 0.0f || positions[count-1] != 1.0f)
2016 return InvalidParameter;
2019 new_color = heap_alloc_zero(count * sizeof(ARGB));
2020 new_pos = heap_alloc_zero(count * sizeof(REAL));
2021 if (!new_color || !new_pos)
2023 heap_free(new_color);
2024 heap_free(new_pos);
2025 return OutOfMemory;
2028 memcpy(new_color, blend, sizeof(ARGB) * count);
2029 memcpy(new_pos, positions, sizeof(REAL) * count);
2031 heap_free(brush->pblendcolor);
2032 heap_free(brush->pblendpos);
2034 brush->pblendcolor = new_color;
2035 brush->pblendpos = new_pos;
2036 brush->pblendcount = count;
2038 return Ok;
2041 GpStatus WINGDIPAPI GdipGetLinePresetBlend(GpLineGradient *brush,
2042 ARGB *blend, REAL* positions, INT count)
2044 if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient)
2045 return InvalidParameter;
2047 if (brush->pblendcount == 0)
2048 return GenericError;
2050 if (count < brush->pblendcount)
2051 return InsufficientBuffer;
2053 memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
2054 memcpy(positions, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
2056 return Ok;
2059 GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
2060 INT *count)
2062 if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
2063 return InvalidParameter;
2065 *count = brush->pblendcount;
2067 return Ok;
2070 GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
2072 TRACE("(%p)\n", brush);
2074 if(!brush)
2075 return InvalidParameter;
2077 return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2080 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
2081 GDIPCONST GpMatrix *matrix)
2083 TRACE("(%p,%p)\n", brush, matrix);
2085 if(!brush || !matrix)
2086 return InvalidParameter;
2088 brush->transform = *matrix;
2090 return Ok;
2093 GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
2095 TRACE("(%p,%p)\n", brush, matrix);
2097 if(!brush || !matrix)
2098 return InvalidParameter;
2100 *matrix = brush->transform;
2102 return Ok;
2105 GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
2106 GpMatrixOrder order)
2108 TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
2110 if(!brush)
2111 return InvalidParameter;
2113 return GdipScaleMatrix(&brush->transform, sx, sy, order);
2116 GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
2117 GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
2119 TRACE("(%p,%p,%u)\n", brush, matrix, order);
2121 if(!brush)
2122 return InvalidParameter;
2124 if(!matrix)
2125 return Ok;
2127 return GdipMultiplyMatrix(&brush->transform, matrix, order);
2130 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient *brush,
2131 REAL dx, REAL dy, GpMatrixOrder order)
2133 TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
2135 if(!brush)
2136 return InvalidParameter;
2138 return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2141 /******************************************************************************
2142 * GdipTranslateTextureTransform [GDIPLUS.@]
2144 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
2145 GpMatrixOrder order)
2147 TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
2149 if(!brush)
2150 return InvalidParameter;
2152 return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2155 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
2157 TRACE("(%p, %p)\n", brush, rect);
2159 if(!brush || !rect || brush->brush.bt != BrushTypeLinearGradient)
2160 return InvalidParameter;
2162 *rect = brush->rect;
2164 return Ok;
2167 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
2169 GpRectF rectF;
2170 GpStatus ret;
2172 TRACE("(%p, %p)\n", brush, rect);
2174 if(!rect)
2175 return InvalidParameter;
2177 ret = GdipGetLineRect(brush, &rectF);
2179 if(ret == Ok){
2180 rect->X = gdip_round(rectF.X);
2181 rect->Y = gdip_round(rectF.Y);
2182 rect->Width = gdip_round(rectF.Width);
2183 rect->Height = gdip_round(rectF.Height);
2186 return ret;
2189 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
2190 REAL angle, GpMatrixOrder order)
2192 static int calls;
2194 TRACE("(%p,%0.2f,%u)\n", brush, angle, order);
2196 if(!brush || brush->brush.bt != BrushTypeLinearGradient)
2197 return InvalidParameter;
2199 if(!(calls++))
2200 FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
2202 return NotImplemented;