gdiplus: Implement transform matrix for line gradient brushes.
[wine.git] / dlls / gdiplus / brush.c
blob201fadf11a4e6a7e8203c1f631f2def8020993f6
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(HatchStyle hatchstyle, const char **result)
235 if (hatchstyle < sizeof(HatchBrushes) / sizeof(HatchBrushes[0]))
237 *result = HatchBrushes[hatchstyle];
238 return Ok;
240 else
241 return NotImplemented;
244 /******************************************************************************
245 * GdipCreateHatchBrush [GDIPLUS.@]
247 GpStatus WINGDIPAPI GdipCreateHatchBrush(HatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
249 TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
251 if(!brush) return InvalidParameter;
253 *brush = heap_alloc_zero(sizeof(GpHatch));
254 if (!*brush) return OutOfMemory;
256 (*brush)->brush.bt = BrushTypeHatchFill;
257 (*brush)->forecol = forecol;
258 (*brush)->backcol = backcol;
259 (*brush)->hatchstyle = hatchstyle;
260 TRACE("<-- %p\n", *brush);
262 return Ok;
265 static void linegradient_init_transform(GpLineGradient *line)
267 float trans_x = line->rect.X + (line->rect.Width / 2.f);
268 float trans_y = line->rect.Y + (line->rect.Height / 2.f);
269 float dx = line->endpoint.X - line->startpoint.X;
270 float dy = line->endpoint.Y - line->startpoint.Y;
271 float t_cos, t_sin, w_ratio, h_ratio;
272 float h;
273 GpMatrix rot;
275 h = sqrtf(dx * dx + dy * dy);
277 t_cos = dx / h;
278 t_sin = dy / h;
280 w_ratio = (fabs(t_cos) * line->rect.Width + fabs(t_sin) * line->rect.Height) / line->rect.Width;
281 h_ratio = (fabs(t_sin) * line->rect.Width + fabs(t_cos) * line->rect.Height) / line->rect.Height;
283 GdipSetMatrixElements(&line->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
285 GdipSetMatrixElements(&rot, t_cos, t_sin, -1.f * t_sin, t_cos, 0, 0);
287 /* center about the origin */
288 GdipTranslateMatrix(&line->transform, -trans_x, -trans_y, MatrixOrderAppend);
290 /* scale to normalize gradient along gradient line (?) */
291 GdipScaleMatrix(&line->transform, w_ratio, h_ratio, MatrixOrderAppend);
293 /* rotate so the gradient is horizontal */
294 GdipMultiplyMatrix(&line->transform, &rot, MatrixOrderAppend);
296 /* restore original offset in new coords */
297 GdipTranslateMatrix(&line->transform, trans_x, trans_y, MatrixOrderAppend);
300 /******************************************************************************
301 * GdipCreateLineBrush [GDIPLUS.@]
303 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
304 GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
305 GpWrapMode wrap, GpLineGradient **line)
307 TRACE("(%s, %s, %x, %x, %d, %p)\n", debugstr_pointf(startpoint),
308 debugstr_pointf(endpoint), startcolor, endcolor, wrap, line);
310 if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
311 return InvalidParameter;
313 if (startpoint->X == endpoint->X && startpoint->Y == endpoint->Y)
314 return OutOfMemory;
316 *line = heap_alloc_zero(sizeof(GpLineGradient));
317 if(!*line) return OutOfMemory;
319 (*line)->brush.bt = BrushTypeLinearGradient;
321 (*line)->startpoint.X = startpoint->X;
322 (*line)->startpoint.Y = startpoint->Y;
323 (*line)->endpoint.X = endpoint->X;
324 (*line)->endpoint.Y = endpoint->Y;
325 (*line)->startcolor = startcolor;
326 (*line)->endcolor = endcolor;
327 (*line)->wrap = wrap;
328 (*line)->gamma = FALSE;
330 (*line)->rect.X = (startpoint->X < endpoint->X ? startpoint->X: endpoint->X);
331 (*line)->rect.Y = (startpoint->Y < endpoint->Y ? startpoint->Y: endpoint->Y);
332 (*line)->rect.Width = fabs(startpoint->X - endpoint->X);
333 (*line)->rect.Height = fabs(startpoint->Y - endpoint->Y);
335 if ((*line)->rect.Width == 0)
337 (*line)->rect.X -= (*line)->rect.Height / 2.0f;
338 (*line)->rect.Width = (*line)->rect.Height;
340 else if ((*line)->rect.Height == 0)
342 (*line)->rect.Y -= (*line)->rect.Width / 2.0f;
343 (*line)->rect.Height = (*line)->rect.Width;
346 (*line)->blendcount = 1;
347 (*line)->blendfac = heap_alloc_zero(sizeof(REAL));
348 (*line)->blendpos = heap_alloc_zero(sizeof(REAL));
350 if (!(*line)->blendfac || !(*line)->blendpos)
352 heap_free((*line)->blendfac);
353 heap_free((*line)->blendpos);
354 heap_free(*line);
355 *line = NULL;
356 return OutOfMemory;
359 (*line)->blendfac[0] = 1.0f;
360 (*line)->blendpos[0] = 1.0f;
362 (*line)->pblendcolor = NULL;
363 (*line)->pblendpos = NULL;
364 (*line)->pblendcount = 0;
366 linegradient_init_transform(*line);
368 TRACE("<-- %p\n", *line);
370 return Ok;
373 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
374 GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
375 GpWrapMode wrap, GpLineGradient **line)
377 GpPointF stF;
378 GpPointF endF;
380 TRACE("(%p, %p, %x, %x, %d, %p)\n", startpoint, endpoint,
381 startcolor, endcolor, wrap, line);
383 if(!startpoint || !endpoint)
384 return InvalidParameter;
386 stF.X = (REAL)startpoint->X;
387 stF.Y = (REAL)startpoint->Y;
388 endF.X = (REAL)endpoint->X;
389 endF.Y = (REAL)endpoint->Y;
391 return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
394 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
395 ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
396 GpLineGradient **line)
398 GpPointF start, end;
399 GpStatus stat;
400 float far_x, far_y;
402 TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
403 wrap, line);
405 if(!line || !rect)
406 return InvalidParameter;
408 far_x = rect->X + rect->Width;
409 far_y = rect->Y + rect->Height;
411 switch (mode)
413 case LinearGradientModeHorizontal:
414 start.X = min(rect->X, far_x);
415 start.Y = rect->Y;
416 end.X = max(rect->X, far_x);
417 end.Y = rect->Y;
418 break;
419 case LinearGradientModeVertical:
420 start.X = rect->X;
421 start.Y = min(rect->Y, far_y);
422 end.X = rect->X;
423 end.Y = max(rect->Y, far_y);
424 break;
425 case LinearGradientModeForwardDiagonal:
426 start.X = min(rect->X, far_x);
427 start.Y = min(rect->Y, far_y);
428 end.X = max(rect->X, far_x);
429 end.Y = max(rect->Y, far_y);
430 break;
431 case LinearGradientModeBackwardDiagonal:
432 start.X = max(rect->X, far_x);
433 start.Y = min(rect->Y, far_y);
434 end.X = min(rect->X, far_x);
435 end.Y = max(rect->Y, far_y);
436 break;
437 default:
438 return InvalidParameter;
441 stat = GdipCreateLineBrush(&start, &end, startcolor, endcolor, wrap, line);
443 if (stat == Ok)
444 (*line)->rect = *rect;
446 return stat;
449 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
450 ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
451 GpLineGradient **line)
453 GpRectF rectF;
455 TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
456 wrap, line);
458 rectF.X = (REAL) rect->X;
459 rectF.Y = (REAL) rect->Y;
460 rectF.Width = (REAL) rect->Width;
461 rectF.Height = (REAL) rect->Height;
463 return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
466 /******************************************************************************
467 * GdipCreateLineBrushFromRectWithAngle [GDIPLUS.@]
469 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect,
470 ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
471 GpLineGradient **line)
473 GpStatus stat;
474 LinearGradientMode mode;
475 REAL exofs, eyofs;
476 REAL sin_angle, cos_angle, sin_cos_angle;
478 TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
479 wrap, line);
481 if (!rect || !rect->Width || !rect->Height)
482 return InvalidParameter;
484 angle = fmodf(angle, 360);
485 if (angle < 0)
486 angle += 360;
488 if (isAngleScalable)
490 float add_angle = 0;
492 while(angle >= 90) {
493 angle -= 180;
494 add_angle += M_PI;
497 if (angle != 90 && angle != -90)
498 angle = atan((rect->Width / rect->Height) * tan(deg2rad(angle)));
499 else
500 angle = deg2rad(angle);
501 angle += add_angle;
503 else
505 angle = deg2rad(angle);
508 sin_angle = sinf(angle);
509 cos_angle = cosf(angle);
510 sin_cos_angle = sin_angle * cos_angle;
512 if (sin_cos_angle >= 0)
513 mode = LinearGradientModeForwardDiagonal;
514 else
515 mode = LinearGradientModeBackwardDiagonal;
517 stat = GdipCreateLineBrushFromRect(rect, startcolor, endcolor, mode, wrap, line);
519 if (stat == Ok)
521 if (sin_cos_angle >= 0)
523 exofs = rect->Height * sin_cos_angle + rect->Width * cos_angle * cos_angle;
524 eyofs = rect->Height * sin_angle * sin_angle + rect->Width * sin_cos_angle;
526 else
528 exofs = rect->Width * sin_angle * sin_angle + rect->Height * sin_cos_angle;
529 eyofs = -rect->Width * sin_cos_angle + rect->Height * sin_angle * sin_angle;
532 if (sin_angle >= 0)
534 (*line)->endpoint.X = rect->X + exofs;
535 (*line)->endpoint.Y = rect->Y + eyofs;
537 else
539 (*line)->endpoint.X = (*line)->startpoint.X;
540 (*line)->endpoint.Y = (*line)->startpoint.Y;
541 (*line)->startpoint.X = rect->X + exofs;
542 (*line)->startpoint.Y = rect->Y + eyofs;
545 linegradient_init_transform(*line);
548 return stat;
551 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect,
552 ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
553 GpLineGradient **line)
555 TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
556 wrap, line);
558 return GdipCreateLineBrushFromRectI(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
559 wrap, line);
562 static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradient **grad)
564 GpRectF bounds;
566 if(!path || !grad)
567 return InvalidParameter;
569 if (path->pathdata.Count < 2)
570 return OutOfMemory;
572 GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
574 *grad = heap_alloc_zero(sizeof(GpPathGradient));
575 if (!*grad)
577 return OutOfMemory;
580 GdipSetMatrixElements(&(*grad)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
582 (*grad)->blendfac = heap_alloc_zero(sizeof(REAL));
583 (*grad)->blendpos = heap_alloc_zero(sizeof(REAL));
584 (*grad)->surroundcolors = heap_alloc_zero(sizeof(ARGB));
585 if(!(*grad)->blendfac || !(*grad)->blendpos || !(*grad)->surroundcolors){
586 heap_free((*grad)->blendfac);
587 heap_free((*grad)->blendpos);
588 heap_free((*grad)->surroundcolors);
589 heap_free(*grad);
590 *grad = NULL;
591 return OutOfMemory;
593 (*grad)->blendfac[0] = 1.0;
594 (*grad)->blendpos[0] = 1.0;
595 (*grad)->blendcount = 1;
597 (*grad)->path = path;
599 (*grad)->brush.bt = BrushTypePathGradient;
600 (*grad)->centercolor = centercolor;
601 (*grad)->wrap = WrapModeClamp;
602 (*grad)->gamma = FALSE;
603 /* FIXME: this should be set to the "centroid" of the path by default */
604 (*grad)->center.X = bounds.X + bounds.Width / 2;
605 (*grad)->center.Y = bounds.Y + bounds.Height / 2;
606 (*grad)->focus.X = 0.0;
607 (*grad)->focus.Y = 0.0;
608 (*grad)->surroundcolors[0] = 0xffffffff;
609 (*grad)->surroundcolorcount = 1;
611 TRACE("<-- %p\n", *grad);
613 return Ok;
616 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
617 INT count, GpWrapMode wrap, GpPathGradient **grad)
619 GpStatus stat;
620 GpPath *path;
622 TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
624 if(!grad)
625 return InvalidParameter;
627 if(!points || count <= 0)
628 return OutOfMemory;
630 stat = GdipCreatePath(FillModeAlternate, &path);
632 if (stat == Ok)
634 stat = GdipAddPathLine2(path, points, count);
636 if (stat == Ok)
637 stat = create_path_gradient(path, 0xff000000, grad);
639 if (stat != Ok)
640 GdipDeletePath(path);
643 if (stat == Ok)
644 (*grad)->wrap = wrap;
646 return stat;
649 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
650 INT count, GpWrapMode wrap, GpPathGradient **grad)
652 GpStatus stat;
653 GpPath *path;
655 TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
657 if(!grad)
658 return InvalidParameter;
660 if(!points || count <= 0)
661 return OutOfMemory;
663 stat = GdipCreatePath(FillModeAlternate, &path);
665 if (stat == Ok)
667 stat = GdipAddPathLine2I(path, points, count);
669 if (stat == Ok)
670 stat = create_path_gradient(path, 0xff000000, grad);
672 if (stat != Ok)
673 GdipDeletePath(path);
676 if (stat == Ok)
677 (*grad)->wrap = wrap;
679 return stat;
682 /******************************************************************************
683 * GdipCreatePathGradientFromPath [GDIPLUS.@]
685 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
686 GpPathGradient **grad)
688 GpStatus stat;
689 GpPath *new_path;
691 TRACE("(%p, %p)\n", path, grad);
693 if(!grad)
694 return InvalidParameter;
696 if (!path)
697 return OutOfMemory;
699 stat = GdipClonePath((GpPath*)path, &new_path);
701 if (stat == Ok)
703 stat = create_path_gradient(new_path, 0xffffffff, grad);
705 if (stat != Ok)
706 GdipDeletePath(new_path);
709 return stat;
712 /******************************************************************************
713 * GdipCreateSolidFill [GDIPLUS.@]
715 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
717 TRACE("(%x, %p)\n", color, sf);
719 if(!sf) return InvalidParameter;
721 *sf = heap_alloc_zero(sizeof(GpSolidFill));
722 if (!*sf) return OutOfMemory;
724 (*sf)->brush.bt = BrushTypeSolidColor;
725 (*sf)->color = color;
727 TRACE("<-- %p\n", *sf);
729 return Ok;
732 /******************************************************************************
733 * GdipCreateTexture [GDIPLUS.@]
735 * PARAMS
736 * image [I] image to use
737 * wrapmode [I] optional
738 * texture [O] pointer to the resulting texturebrush
740 * RETURNS
741 * SUCCESS: Ok
742 * FAILURE: element of GpStatus
744 GpStatus WINGDIPAPI GdipCreateTexture(GpImage *image, GpWrapMode wrapmode,
745 GpTexture **texture)
747 UINT width, height;
748 GpImageAttributes *attributes;
749 GpStatus stat;
751 TRACE("%p, %d %p\n", image, wrapmode, texture);
753 if (!(image && texture))
754 return InvalidParameter;
756 stat = GdipGetImageWidth(image, &width);
757 if (stat != Ok) return stat;
758 stat = GdipGetImageHeight(image, &height);
759 if (stat != Ok) return stat;
761 stat = GdipCreateImageAttributes(&attributes);
763 if (stat == Ok)
765 attributes->wrap = wrapmode;
767 stat = GdipCreateTextureIA(image, attributes, 0, 0, width, height,
768 texture);
770 GdipDisposeImageAttributes(attributes);
773 return stat;
776 /******************************************************************************
777 * GdipCreateTexture2 [GDIPLUS.@]
779 GpStatus WINGDIPAPI GdipCreateTexture2(GpImage *image, GpWrapMode wrapmode,
780 REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
782 GpImageAttributes *attributes;
783 GpStatus stat;
785 TRACE("%p %d %f %f %f %f %p\n", image, wrapmode,
786 x, y, width, height, texture);
788 stat = GdipCreateImageAttributes(&attributes);
790 if (stat == Ok)
792 attributes->wrap = wrapmode;
794 stat = GdipCreateTextureIA(image, attributes, x, y, width, height,
795 texture);
797 GdipDisposeImageAttributes(attributes);
800 return stat;
803 /******************************************************************************
804 * GdipCreateTextureIA [GDIPLUS.@]
806 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
807 GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
808 REAL height, GpTexture **texture)
810 GpStatus status;
811 GpImage *new_image=NULL;
813 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %p)\n", image, imageattr, x, y, width, height,
814 texture);
816 if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
817 return InvalidParameter;
819 *texture = NULL;
821 if(image->type != ImageTypeBitmap){
822 FIXME("not implemented for image type %d\n", image->type);
823 return NotImplemented;
826 status = GdipCloneBitmapArea(x, y, width, height, PixelFormatDontCare, (GpBitmap*)image, (GpBitmap**)&new_image);
827 if (status != Ok)
828 return status;
830 *texture = heap_alloc_zero(sizeof(GpTexture));
831 if (!*texture){
832 status = OutOfMemory;
833 goto exit;
836 GdipSetMatrixElements(&(*texture)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
838 if (imageattr)
840 status = GdipCloneImageAttributes(imageattr, &(*texture)->imageattributes);
842 else
844 status = GdipCreateImageAttributes(&(*texture)->imageattributes);
845 if (status == Ok)
846 (*texture)->imageattributes->wrap = WrapModeTile;
848 if (status == Ok)
850 (*texture)->brush.bt = BrushTypeTextureFill;
851 (*texture)->image = new_image;
854 exit:
855 if (status == Ok)
857 TRACE("<-- %p\n", *texture);
859 else
861 if (*texture)
863 GdipDisposeImageAttributes((*texture)->imageattributes);
864 heap_free(*texture);
865 *texture = NULL;
867 GdipDisposeImage(new_image);
868 TRACE("<-- error %u\n", status);
871 return status;
874 /******************************************************************************
875 * GdipCreateTextureIAI [GDIPLUS.@]
877 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
878 INT x, INT y, INT width, INT height, GpTexture **texture)
880 TRACE("(%p, %p, %d, %d, %d, %d, %p)\n", image, imageattr, x, y, width, height,
881 texture);
883 return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
886 GpStatus WINGDIPAPI GdipCreateTexture2I(GpImage *image, GpWrapMode wrapmode,
887 INT x, INT y, INT width, INT height, GpTexture **texture)
889 GpImageAttributes *imageattr;
890 GpStatus stat;
892 TRACE("%p %d %d %d %d %d %p\n", image, wrapmode, x, y, width, height,
893 texture);
895 stat = GdipCreateImageAttributes(&imageattr);
897 if (stat == Ok)
899 imageattr->wrap = wrapmode;
901 stat = GdipCreateTextureIA(image, imageattr, x, y, width, height, texture);
902 GdipDisposeImageAttributes(imageattr);
905 return stat;
908 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
910 TRACE("(%p, %p)\n", brush, type);
912 if(!brush || !type) return InvalidParameter;
914 *type = brush->bt;
916 return Ok;
919 GpStatus WINGDIPAPI GdipGetHatchBackgroundColor(GpHatch *brush, ARGB *backcol)
921 TRACE("(%p, %p)\n", brush, backcol);
923 if(!brush || !backcol) return InvalidParameter;
925 *backcol = brush->backcol;
927 return Ok;
930 GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
932 TRACE("(%p, %p)\n", brush, forecol);
934 if(!brush || !forecol) return InvalidParameter;
936 *forecol = brush->forecol;
938 return Ok;
941 GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, HatchStyle *hatchstyle)
943 TRACE("(%p, %p)\n", brush, hatchstyle);
945 if(!brush || !hatchstyle) return InvalidParameter;
947 *hatchstyle = brush->hatchstyle;
949 return Ok;
952 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
954 TRACE("(%p)\n", brush);
956 if(!brush) return InvalidParameter;
958 switch(brush->bt)
960 case BrushTypePathGradient:
961 GdipDeletePath(((GpPathGradient*) brush)->path);
962 heap_free(((GpPathGradient*) brush)->blendfac);
963 heap_free(((GpPathGradient*) brush)->blendpos);
964 heap_free(((GpPathGradient*) brush)->surroundcolors);
965 heap_free(((GpPathGradient*) brush)->pblendcolor);
966 heap_free(((GpPathGradient*) brush)->pblendpos);
967 break;
968 case BrushTypeLinearGradient:
969 heap_free(((GpLineGradient*)brush)->blendfac);
970 heap_free(((GpLineGradient*)brush)->blendpos);
971 heap_free(((GpLineGradient*)brush)->pblendcolor);
972 heap_free(((GpLineGradient*)brush)->pblendpos);
973 break;
974 case BrushTypeTextureFill:
975 GdipDisposeImage(((GpTexture*)brush)->image);
976 GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes);
977 heap_free(((GpTexture*)brush)->bitmap_bits);
978 break;
979 default:
980 break;
983 heap_free(brush);
985 return Ok;
988 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
989 BOOL *usinggamma)
991 TRACE("(%p, %p)\n", line, usinggamma);
993 if(!line || !usinggamma)
994 return InvalidParameter;
996 *usinggamma = line->gamma;
998 return Ok;
1001 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
1003 TRACE("(%p, %p)\n", brush, wrapmode);
1005 if(!brush || !wrapmode || brush->brush.bt != BrushTypeLinearGradient)
1006 return InvalidParameter;
1008 *wrapmode = brush->wrap;
1010 return Ok;
1013 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
1014 REAL *positions, INT count)
1016 TRACE("(%p, %p, %p, %d)\n", brush, blend, positions, count);
1018 if(!brush || !blend || !positions || count <= 0 || brush->brush.bt != BrushTypePathGradient)
1019 return InvalidParameter;
1021 if(count < brush->blendcount)
1022 return InsufficientBuffer;
1024 memcpy(blend, brush->blendfac, count*sizeof(REAL));
1025 if(brush->blendcount > 1){
1026 memcpy(positions, brush->blendpos, count*sizeof(REAL));
1029 return Ok;
1032 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
1034 TRACE("(%p, %p)\n", brush, count);
1036 if(!brush || !count || brush->brush.bt != BrushTypePathGradient)
1037 return InvalidParameter;
1039 *count = brush->blendcount;
1041 return Ok;
1044 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
1045 GpPointF *point)
1047 TRACE("(%p, %p)\n", grad, point);
1049 if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1050 return InvalidParameter;
1052 point->X = grad->center.X;
1053 point->Y = grad->center.Y;
1055 return Ok;
1058 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
1059 GpPoint *point)
1061 GpStatus ret;
1062 GpPointF ptf;
1064 TRACE("(%p, %p)\n", grad, point);
1066 if(!point)
1067 return InvalidParameter;
1069 ret = GdipGetPathGradientCenterPoint(grad,&ptf);
1071 if(ret == Ok){
1072 point->X = gdip_round(ptf.X);
1073 point->Y = gdip_round(ptf.Y);
1076 return ret;
1079 GpStatus WINGDIPAPI GdipGetPathGradientCenterColor(GpPathGradient *grad,
1080 ARGB *colors)
1082 TRACE("(%p,%p)\n", grad, colors);
1084 if (!grad || !colors || grad->brush.bt != BrushTypePathGradient)
1085 return InvalidParameter;
1087 *colors = grad->centercolor;
1089 return Ok;
1092 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
1093 REAL *x, REAL *y)
1095 TRACE("(%p, %p, %p)\n", grad, x, y);
1097 if(!grad || !x || !y || grad->brush.bt != BrushTypePathGradient)
1098 return InvalidParameter;
1100 *x = grad->focus.X;
1101 *y = grad->focus.Y;
1103 return Ok;
1106 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
1107 BOOL *gamma)
1109 TRACE("(%p, %p)\n", grad, gamma);
1111 if(!grad || !gamma || grad->brush.bt != BrushTypePathGradient)
1112 return InvalidParameter;
1114 *gamma = grad->gamma;
1116 return Ok;
1119 GpStatus WINGDIPAPI GdipGetPathGradientPath(GpPathGradient *grad, GpPath *path)
1121 static int calls;
1123 TRACE("(%p, %p)\n", grad, path);
1125 if (!(calls++))
1126 FIXME("not implemented\n");
1128 return NotImplemented;
1131 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
1132 INT *count)
1134 TRACE("(%p, %p)\n", grad, count);
1136 if(!grad || !count || grad->brush.bt != BrushTypePathGradient)
1137 return InvalidParameter;
1139 *count = grad->path->pathdata.Count;
1141 return Ok;
1144 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
1146 GpStatus stat;
1148 TRACE("(%p, %p)\n", brush, rect);
1150 if(!brush || !rect || brush->brush.bt != BrushTypePathGradient)
1151 return InvalidParameter;
1153 stat = GdipGetPathWorldBounds(brush->path, rect, NULL, NULL);
1155 return stat;
1158 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
1160 GpRectF rectf;
1161 GpStatus stat;
1163 TRACE("(%p, %p)\n", brush, rect);
1165 if(!brush || !rect)
1166 return InvalidParameter;
1168 stat = GdipGetPathGradientRect(brush, &rectf);
1169 if(stat != Ok) return stat;
1171 rect->X = gdip_round(rectf.X);
1172 rect->Y = gdip_round(rectf.Y);
1173 rect->Width = gdip_round(rectf.Width);
1174 rect->Height = gdip_round(rectf.Height);
1176 return Ok;
1179 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
1180 *grad, ARGB *argb, INT *count)
1182 INT i;
1184 TRACE("(%p,%p,%p)\n", grad, argb, count);
1186 if(!grad || !argb || !count || (*count < grad->path->pathdata.Count) || grad->brush.bt != BrushTypePathGradient)
1187 return InvalidParameter;
1189 for (i=0; i<grad->path->pathdata.Count; i++)
1191 if (i < grad->surroundcolorcount)
1192 argb[i] = grad->surroundcolors[i];
1193 else
1194 argb[i] = grad->surroundcolors[grad->surroundcolorcount-1];
1197 *count = grad->surroundcolorcount;
1199 return Ok;
1202 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient *brush, INT *count)
1204 TRACE("(%p, %p)\n", brush, count);
1206 if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1207 return InvalidParameter;
1209 /* Yes, this actually returns the number of points in the path (which is the
1210 * required size of a buffer to get the surround colors), rather than the
1211 * number of surround colors. The real count is returned when getting the
1212 * colors. */
1213 *count = brush->path->pathdata.Count;
1215 return Ok;
1218 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
1219 GpWrapMode *wrapmode)
1221 TRACE("(%p, %p)\n", brush, wrapmode);
1223 if(!brush || !wrapmode || brush->brush.bt != BrushTypePathGradient)
1224 return InvalidParameter;
1226 *wrapmode = brush->wrap;
1228 return Ok;
1231 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
1233 TRACE("(%p, %p)\n", sf, argb);
1235 if(!sf || !argb)
1236 return InvalidParameter;
1238 *argb = sf->color;
1240 return Ok;
1243 /******************************************************************************
1244 * GdipGetTextureImage [GDIPLUS.@]
1246 GpStatus WINGDIPAPI GdipGetTextureImage(GpTexture *brush, GpImage **image)
1248 TRACE("(%p, %p)\n", brush, image);
1250 if(!brush || !image)
1251 return InvalidParameter;
1253 return GdipCloneImage(brush->image, image);
1256 /******************************************************************************
1257 * GdipGetTextureTransform [GDIPLUS.@]
1259 GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
1261 TRACE("(%p, %p)\n", brush, matrix);
1263 if(!brush || !matrix)
1264 return InvalidParameter;
1266 *matrix = brush->transform;
1268 return Ok;
1271 /******************************************************************************
1272 * GdipGetTextureWrapMode [GDIPLUS.@]
1274 GpStatus WINGDIPAPI GdipGetTextureWrapMode(GpTexture *brush, GpWrapMode *wrapmode)
1276 TRACE("(%p, %p)\n", brush, wrapmode);
1278 if(!brush || !wrapmode)
1279 return InvalidParameter;
1281 *wrapmode = brush->imageattributes->wrap;
1283 return Ok;
1286 /******************************************************************************
1287 * GdipMultiplyTextureTransform [GDIPLUS.@]
1289 GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
1290 GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1292 TRACE("(%p, %p, %d)\n", brush, matrix, order);
1294 if(!brush || !matrix)
1295 return InvalidParameter;
1297 return GdipMultiplyMatrix(&brush->transform, matrix, order);
1300 /******************************************************************************
1301 * GdipResetTextureTransform [GDIPLUS.@]
1303 GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
1305 TRACE("(%p)\n", brush);
1307 if(!brush)
1308 return InvalidParameter;
1310 return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1313 /******************************************************************************
1314 * GdipScaleTextureTransform [GDIPLUS.@]
1316 GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
1317 REAL sx, REAL sy, GpMatrixOrder order)
1319 TRACE("(%p, %.2f, %.2f, %d)\n", brush, sx, sy, order);
1321 if(!brush)
1322 return InvalidParameter;
1324 return GdipScaleMatrix(&brush->transform, sx, sy, order);
1327 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
1328 GDIPCONST REAL *factors, GDIPCONST REAL* positions, INT count)
1330 REAL *new_blendfac, *new_blendpos;
1332 TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1334 if(!brush || !factors || !positions || count <= 0 || brush->brush.bt != BrushTypeLinearGradient ||
1335 (count >= 2 && (positions[0] != 0.0f || positions[count-1] != 1.0f)))
1336 return InvalidParameter;
1338 new_blendfac = heap_alloc_zero(count * sizeof(REAL));
1339 new_blendpos = heap_alloc_zero(count * sizeof(REAL));
1341 if (!new_blendfac || !new_blendpos)
1343 heap_free(new_blendfac);
1344 heap_free(new_blendpos);
1345 return OutOfMemory;
1348 memcpy(new_blendfac, factors, count * sizeof(REAL));
1349 memcpy(new_blendpos, positions, count * sizeof(REAL));
1351 heap_free(brush->blendfac);
1352 heap_free(brush->blendpos);
1354 brush->blendcount = count;
1355 brush->blendfac = new_blendfac;
1356 brush->blendpos = new_blendpos;
1358 return Ok;
1361 GpStatus WINGDIPAPI GdipGetLineBlend(GpLineGradient *brush, REAL *factors,
1362 REAL *positions, INT count)
1364 TRACE("(%p, %p, %p, %i)\n", brush, factors, positions, count);
1366 if (!brush || !factors || !positions || count <= 0 || brush->brush.bt != BrushTypeLinearGradient)
1367 return InvalidParameter;
1369 if (count < brush->blendcount)
1370 return InsufficientBuffer;
1372 memcpy(factors, brush->blendfac, brush->blendcount * sizeof(REAL));
1373 memcpy(positions, brush->blendpos, brush->blendcount * sizeof(REAL));
1375 return Ok;
1378 GpStatus WINGDIPAPI GdipGetLineBlendCount(GpLineGradient *brush, INT *count)
1380 TRACE("(%p, %p)\n", brush, count);
1382 if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
1383 return InvalidParameter;
1385 *count = brush->blendcount;
1387 return Ok;
1390 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
1391 BOOL usegamma)
1393 TRACE("(%p, %d)\n", line, usegamma);
1395 if(!line || line->brush.bt != BrushTypeLinearGradient)
1396 return InvalidParameter;
1398 line->gamma = usegamma;
1400 return Ok;
1403 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
1404 REAL scale)
1406 REAL factors[33];
1407 REAL positions[33];
1408 int num_points = 0;
1409 int i;
1410 const int precision = 16;
1411 REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1412 REAL min_erf;
1413 REAL scale_erf;
1415 TRACE("(%p, %0.2f, %0.2f)\n", line, focus, scale);
1417 if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || line->brush.bt != BrushTypeLinearGradient)
1418 return InvalidParameter;
1420 /* we want 2 standard deviations */
1421 erf_range = 2.0 / sqrt(2);
1423 /* calculate the constants we need to normalize the error function to be
1424 between 0.0 and scale over the range we need */
1425 min_erf = erf(-erf_range);
1426 scale_erf = scale / (-2.0 * min_erf);
1428 if (focus != 0.0)
1430 positions[0] = 0.0;
1431 factors[0] = 0.0;
1432 for (i=1; i<precision; i++)
1434 positions[i] = focus * i / precision;
1435 factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1437 num_points += precision;
1440 positions[num_points] = focus;
1441 factors[num_points] = scale;
1442 num_points += 1;
1444 if (focus != 1.0)
1446 for (i=1; i<precision; i++)
1448 positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1449 factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1451 num_points += precision;
1452 positions[num_points-1] = 1.0;
1453 factors[num_points-1] = 0.0;
1456 return GdipSetLineBlend(line, factors, positions, num_points);
1459 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
1460 GpWrapMode wrap)
1462 TRACE("(%p, %d)\n", line, wrap);
1464 if(!line || wrap == WrapModeClamp || line->brush.bt != BrushTypeLinearGradient)
1465 return InvalidParameter;
1467 line->wrap = wrap;
1469 return Ok;
1472 GpStatus WINGDIPAPI GdipSetPathGradientBlend(GpPathGradient *brush, GDIPCONST REAL *blend,
1473 GDIPCONST REAL *pos, INT count)
1475 REAL *new_blendfac, *new_blendpos;
1477 TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1479 if(!brush || !blend || !pos || count <= 0 || brush->brush.bt != BrushTypePathGradient ||
1480 (count >= 2 && (pos[0] != 0.0f || pos[count-1] != 1.0f)))
1481 return InvalidParameter;
1483 new_blendfac = heap_alloc_zero(count * sizeof(REAL));
1484 new_blendpos = heap_alloc_zero(count * sizeof(REAL));
1486 if (!new_blendfac || !new_blendpos)
1488 heap_free(new_blendfac);
1489 heap_free(new_blendpos);
1490 return OutOfMemory;
1493 memcpy(new_blendfac, blend, count * sizeof(REAL));
1494 memcpy(new_blendpos, pos, count * sizeof(REAL));
1496 heap_free(brush->blendfac);
1497 heap_free(brush->blendpos);
1499 brush->blendcount = count;
1500 brush->blendfac = new_blendfac;
1501 brush->blendpos = new_blendpos;
1503 return Ok;
1506 GpStatus WINGDIPAPI GdipSetPathGradientLinearBlend(GpPathGradient *brush,
1507 REAL focus, REAL scale)
1509 REAL factors[3];
1510 REAL positions[3];
1511 int num_points = 0;
1513 TRACE("(%p,%0.2f,%0.2f)\n", brush, focus, scale);
1515 if (!brush || brush->brush.bt != BrushTypePathGradient)
1516 return InvalidParameter;
1518 if (focus != 0.0)
1520 factors[num_points] = 0.0;
1521 positions[num_points] = 0.0;
1522 num_points++;
1525 factors[num_points] = scale;
1526 positions[num_points] = focus;
1527 num_points++;
1529 if (focus != 1.0)
1531 factors[num_points] = 0.0;
1532 positions[num_points] = 1.0;
1533 num_points++;
1536 return GdipSetPathGradientBlend(brush, factors, positions, num_points);
1539 GpStatus WINGDIPAPI GdipSetPathGradientPresetBlend(GpPathGradient *brush,
1540 GDIPCONST ARGB *blend, GDIPCONST REAL *pos, INT count)
1542 ARGB *new_color;
1543 REAL *new_pos;
1544 TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1546 if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient ||
1547 pos[0] != 0.0f || pos[count-1] != 1.0f)
1549 return InvalidParameter;
1552 new_color = heap_alloc_zero(count * sizeof(ARGB));
1553 new_pos = heap_alloc_zero(count * sizeof(REAL));
1554 if (!new_color || !new_pos)
1556 heap_free(new_color);
1557 heap_free(new_pos);
1558 return OutOfMemory;
1561 memcpy(new_color, blend, sizeof(ARGB) * count);
1562 memcpy(new_pos, pos, sizeof(REAL) * count);
1564 heap_free(brush->pblendcolor);
1565 heap_free(brush->pblendpos);
1567 brush->pblendcolor = new_color;
1568 brush->pblendpos = new_pos;
1569 brush->pblendcount = count;
1571 return Ok;
1574 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlend(GpPathGradient *brush,
1575 ARGB *blend, REAL *pos, INT count)
1577 TRACE("(%p,%p,%p,%i)\n", brush, blend, pos, count);
1579 if (count < 0)
1580 return OutOfMemory;
1582 if (!brush || !blend || !pos || count < 2 || brush->brush.bt != BrushTypePathGradient)
1583 return InvalidParameter;
1585 if (brush->pblendcount == 0)
1586 return GenericError;
1588 if (count != brush->pblendcount)
1590 /* Native lines up the ends of each array, and copies the destination size. */
1591 FIXME("Braindead behavior on wrong-sized buffer not implemented.\n");
1592 return InvalidParameter;
1595 memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
1596 memcpy(pos, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
1598 return Ok;
1601 GpStatus WINGDIPAPI GdipGetPathGradientPresetBlendCount(GpPathGradient *brush,
1602 INT *count)
1604 TRACE("(%p,%p)\n", brush, count);
1606 if (!brush || !count || brush->brush.bt != BrushTypePathGradient)
1607 return InvalidParameter;
1609 *count = brush->pblendcount;
1611 return Ok;
1614 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1615 ARGB argb)
1617 TRACE("(%p, %x)\n", grad, argb);
1619 if(!grad || grad->brush.bt != BrushTypePathGradient)
1620 return InvalidParameter;
1622 grad->centercolor = argb;
1623 return Ok;
1626 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1627 GpPointF *point)
1629 TRACE("(%p, %s)\n", grad, debugstr_pointf(point));
1631 if(!grad || !point || grad->brush.bt != BrushTypePathGradient)
1632 return InvalidParameter;
1634 grad->center.X = point->X;
1635 grad->center.Y = point->Y;
1637 return Ok;
1640 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1641 GpPoint *point)
1643 GpPointF ptf;
1645 TRACE("(%p, %p)\n", grad, point);
1647 if(!point)
1648 return InvalidParameter;
1650 ptf.X = (REAL)point->X;
1651 ptf.Y = (REAL)point->Y;
1653 return GdipSetPathGradientCenterPoint(grad,&ptf);
1656 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1657 REAL x, REAL y)
1659 TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1661 if(!grad || grad->brush.bt != BrushTypePathGradient)
1662 return InvalidParameter;
1664 grad->focus.X = x;
1665 grad->focus.Y = y;
1667 return Ok;
1670 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1671 BOOL gamma)
1673 TRACE("(%p, %d)\n", grad, gamma);
1675 if(!grad || grad->brush.bt != BrushTypePathGradient)
1676 return InvalidParameter;
1678 grad->gamma = gamma;
1680 return Ok;
1683 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1684 REAL focus, REAL scale)
1686 REAL factors[33];
1687 REAL positions[33];
1688 int num_points = 0;
1689 int i;
1690 const int precision = 16;
1691 REAL erf_range; /* we use values erf(-erf_range) through erf(+erf_range) */
1692 REAL min_erf;
1693 REAL scale_erf;
1695 TRACE("(%p,%0.2f,%0.2f)\n", grad, focus, scale);
1697 if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0 || grad->brush.bt != BrushTypePathGradient)
1698 return InvalidParameter;
1700 /* we want 2 standard deviations */
1701 erf_range = 2.0 / sqrt(2);
1703 /* calculate the constants we need to normalize the error function to be
1704 between 0.0 and scale over the range we need */
1705 min_erf = erf(-erf_range);
1706 scale_erf = scale / (-2.0 * min_erf);
1708 if (focus != 0.0)
1710 positions[0] = 0.0;
1711 factors[0] = 0.0;
1712 for (i=1; i<precision; i++)
1714 positions[i] = focus * i / precision;
1715 factors[i] = scale_erf * (erf(2 * erf_range * i / precision - erf_range) - min_erf);
1717 num_points += precision;
1720 positions[num_points] = focus;
1721 factors[num_points] = scale;
1722 num_points += 1;
1724 if (focus != 1.0)
1726 for (i=1; i<precision; i++)
1728 positions[i+num_points-1] = (focus + ((1.0-focus) * i / precision));
1729 factors[i+num_points-1] = scale_erf * (erf(erf_range - 2 * erf_range * i / precision) - min_erf);
1731 num_points += precision;
1732 positions[num_points-1] = 1.0;
1733 factors[num_points-1] = 0.0;
1736 return GdipSetPathGradientBlend(grad, factors, positions, num_points);
1739 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1740 *grad, GDIPCONST ARGB *argb, INT *count)
1742 ARGB *new_surroundcolors;
1743 INT i, num_colors;
1745 TRACE("(%p,%p,%p)\n", grad, argb, count);
1747 if(!grad || !argb || !count || (*count <= 0) || grad->brush.bt != BrushTypePathGradient ||
1748 (*count > grad->path->pathdata.Count))
1749 return InvalidParameter;
1751 num_colors = *count;
1753 /* If all colors are the same, only store 1 color. */
1754 if (*count > 1)
1756 for (i=1; i < num_colors; i++)
1757 if (argb[i] != argb[i-1])
1758 break;
1760 if (i == num_colors)
1761 num_colors = 1;
1764 new_surroundcolors = heap_alloc_zero(num_colors * sizeof(ARGB));
1765 if (!new_surroundcolors)
1766 return OutOfMemory;
1768 memcpy(new_surroundcolors, argb, num_colors * sizeof(ARGB));
1770 heap_free(grad->surroundcolors);
1772 grad->surroundcolors = new_surroundcolors;
1773 grad->surroundcolorcount = num_colors;
1775 return Ok;
1778 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1779 GpWrapMode wrap)
1781 TRACE("(%p, %d)\n", grad, wrap);
1783 if(!grad || grad->brush.bt != BrushTypePathGradient)
1784 return InvalidParameter;
1786 grad->wrap = wrap;
1788 return Ok;
1791 GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad,
1792 GpMatrix *matrix)
1794 TRACE("(%p,%p)\n", grad, matrix);
1796 if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1797 return InvalidParameter;
1799 grad->transform = *matrix;
1801 return Ok;
1804 GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad,
1805 GpMatrix *matrix)
1807 TRACE("(%p,%p)\n", grad, matrix);
1809 if (!grad || !matrix || grad->brush.bt != BrushTypePathGradient)
1810 return InvalidParameter;
1812 *matrix = grad->transform;
1814 return Ok;
1817 GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad,
1818 GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1820 TRACE("(%p,%p,%i)\n", grad, matrix, order);
1822 if (!grad || grad->brush.bt != BrushTypePathGradient)
1823 return InvalidParameter;
1825 return GdipMultiplyMatrix(&grad->transform, matrix, order);
1828 GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad)
1830 TRACE("(%p)\n", grad);
1832 if (!grad || grad->brush.bt != BrushTypePathGradient)
1833 return InvalidParameter;
1835 return GdipSetMatrixElements(&grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1838 GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad,
1839 REAL angle, GpMatrixOrder order)
1841 TRACE("(%p,%0.2f,%i)\n", grad, angle, order);
1843 if (!grad || grad->brush.bt != BrushTypePathGradient)
1844 return InvalidParameter;
1846 return GdipRotateMatrix(&grad->transform, angle, order);
1849 GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad,
1850 REAL sx, REAL sy, GpMatrixOrder order)
1852 TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, sx, sy, order);
1854 if (!grad || grad->brush.bt != BrushTypePathGradient)
1855 return InvalidParameter;
1857 return GdipScaleMatrix(&grad->transform, sx, sy, order);
1860 GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad,
1861 REAL dx, REAL dy, GpMatrixOrder order)
1863 TRACE("(%p,%0.2f,%0.2f,%i)\n", grad, dx, dy, order);
1865 if (!grad || grad->brush.bt != BrushTypePathGradient)
1866 return InvalidParameter;
1868 return GdipTranslateMatrix(&grad->transform, dx, dy, order);
1871 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1873 TRACE("(%p, %x)\n", sf, argb);
1875 if(!sf)
1876 return InvalidParameter;
1878 sf->color = argb;
1879 return Ok;
1882 /******************************************************************************
1883 * GdipSetTextureTransform [GDIPLUS.@]
1885 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1886 GDIPCONST GpMatrix *matrix)
1888 TRACE("(%p, %p)\n", texture, matrix);
1890 if(!texture || !matrix)
1891 return InvalidParameter;
1893 texture->transform = *matrix;
1895 return Ok;
1898 /******************************************************************************
1899 * GdipSetTextureWrapMode [GDIPLUS.@]
1901 * WrapMode not used, only stored
1903 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1905 TRACE("(%p, %d)\n", brush, wrapmode);
1907 if(!brush)
1908 return InvalidParameter;
1910 brush->imageattributes->wrap = wrapmode;
1912 return Ok;
1915 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1916 ARGB color2)
1918 TRACE("(%p, %x, %x)\n", brush, color1, color2);
1920 if(!brush || brush->brush.bt != BrushTypeLinearGradient)
1921 return InvalidParameter;
1923 brush->startcolor = color1;
1924 brush->endcolor = color2;
1926 return Ok;
1929 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1931 TRACE("(%p, %p)\n", brush, colors);
1933 if(!brush || !colors || brush->brush.bt != BrushTypeLinearGradient)
1934 return InvalidParameter;
1936 colors[0] = brush->startcolor;
1937 colors[1] = brush->endcolor;
1939 return Ok;
1942 /******************************************************************************
1943 * GdipRotateTextureTransform [GDIPLUS.@]
1945 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
1946 GpMatrixOrder order)
1948 TRACE("(%p, %.2f, %d)\n", brush, angle, order);
1950 if(!brush)
1951 return InvalidParameter;
1953 return GdipRotateMatrix(&brush->transform, angle, order);
1956 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
1957 REAL scale)
1959 REAL factors[3];
1960 REAL positions[3];
1961 int num_points = 0;
1963 TRACE("(%p,%.2f,%.2f)\n", brush, focus, scale);
1965 if (!brush) return InvalidParameter;
1967 if (focus != 0.0)
1969 factors[num_points] = 0.0;
1970 positions[num_points] = 0.0;
1971 num_points++;
1974 factors[num_points] = scale;
1975 positions[num_points] = focus;
1976 num_points++;
1978 if (focus != 1.0)
1980 factors[num_points] = 0.0;
1981 positions[num_points] = 1.0;
1982 num_points++;
1985 return GdipSetLineBlend(brush, factors, positions, num_points);
1988 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
1989 GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
1991 ARGB *new_color;
1992 REAL *new_pos;
1993 TRACE("(%p,%p,%p,%i)\n", brush, blend, positions, count);
1995 if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient ||
1996 positions[0] != 0.0f || positions[count-1] != 1.0f)
1998 return InvalidParameter;
2001 new_color = heap_alloc_zero(count * sizeof(ARGB));
2002 new_pos = heap_alloc_zero(count * sizeof(REAL));
2003 if (!new_color || !new_pos)
2005 heap_free(new_color);
2006 heap_free(new_pos);
2007 return OutOfMemory;
2010 memcpy(new_color, blend, sizeof(ARGB) * count);
2011 memcpy(new_pos, positions, sizeof(REAL) * count);
2013 heap_free(brush->pblendcolor);
2014 heap_free(brush->pblendpos);
2016 brush->pblendcolor = new_color;
2017 brush->pblendpos = new_pos;
2018 brush->pblendcount = count;
2020 return Ok;
2023 GpStatus WINGDIPAPI GdipGetLinePresetBlend(GpLineGradient *brush,
2024 ARGB *blend, REAL* positions, INT count)
2026 if (!brush || !blend || !positions || count < 2 || brush->brush.bt != BrushTypeLinearGradient)
2027 return InvalidParameter;
2029 if (brush->pblendcount == 0)
2030 return GenericError;
2032 if (count < brush->pblendcount)
2033 return InsufficientBuffer;
2035 memcpy(blend, brush->pblendcolor, sizeof(ARGB) * brush->pblendcount);
2036 memcpy(positions, brush->pblendpos, sizeof(REAL) * brush->pblendcount);
2038 return Ok;
2041 GpStatus WINGDIPAPI GdipGetLinePresetBlendCount(GpLineGradient *brush,
2042 INT *count)
2044 if (!brush || !count || brush->brush.bt != BrushTypeLinearGradient)
2045 return InvalidParameter;
2047 *count = brush->pblendcount;
2049 return Ok;
2052 GpStatus WINGDIPAPI GdipResetLineTransform(GpLineGradient *brush)
2054 TRACE("(%p)\n", brush);
2056 if(!brush)
2057 return InvalidParameter;
2059 return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2062 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
2063 GDIPCONST GpMatrix *matrix)
2065 TRACE("(%p,%p)\n", brush, matrix);
2067 if(!brush || !matrix)
2068 return InvalidParameter;
2070 brush->transform = *matrix;
2072 return Ok;
2075 GpStatus WINGDIPAPI GdipGetLineTransform(GpLineGradient *brush, GpMatrix *matrix)
2077 TRACE("(%p,%p)\n", brush, matrix);
2079 if(!brush || !matrix)
2080 return InvalidParameter;
2082 *matrix = brush->transform;
2084 return Ok;
2087 GpStatus WINGDIPAPI GdipScaleLineTransform(GpLineGradient *brush, REAL sx, REAL sy,
2088 GpMatrixOrder order)
2090 TRACE("(%p,%0.2f,%0.2f,%u)\n", brush, sx, sy, order);
2092 if(!brush)
2093 return InvalidParameter;
2095 return GdipScaleMatrix(&brush->transform, sx, sy, order);
2098 GpStatus WINGDIPAPI GdipMultiplyLineTransform(GpLineGradient *brush,
2099 GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
2101 TRACE("(%p,%p,%u)\n", brush, matrix, order);
2103 if(!brush)
2104 return InvalidParameter;
2106 if(!matrix)
2107 return Ok;
2109 return GdipMultiplyMatrix(&brush->transform, matrix, order);
2112 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient *brush,
2113 REAL dx, REAL dy, GpMatrixOrder order)
2115 TRACE("(%p,%f,%f,%d)\n", brush, dx, dy, order);
2117 if(!brush)
2118 return InvalidParameter;
2120 return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2123 /******************************************************************************
2124 * GdipTranslateTextureTransform [GDIPLUS.@]
2126 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
2127 GpMatrixOrder order)
2129 TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
2131 if(!brush)
2132 return InvalidParameter;
2134 return GdipTranslateMatrix(&brush->transform, dx, dy, order);
2137 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
2139 TRACE("(%p, %p)\n", brush, rect);
2141 if(!brush || !rect || brush->brush.bt != BrushTypeLinearGradient)
2142 return InvalidParameter;
2144 *rect = brush->rect;
2146 return Ok;
2149 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
2151 GpRectF rectF;
2152 GpStatus ret;
2154 TRACE("(%p, %p)\n", brush, rect);
2156 if(!rect)
2157 return InvalidParameter;
2159 ret = GdipGetLineRect(brush, &rectF);
2161 if(ret == Ok){
2162 rect->X = gdip_round(rectF.X);
2163 rect->Y = gdip_round(rectF.Y);
2164 rect->Width = gdip_round(rectF.Width);
2165 rect->Height = gdip_round(rectF.Height);
2168 return ret;
2171 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
2172 REAL angle, GpMatrixOrder order)
2174 static int calls;
2176 TRACE("(%p,%0.2f,%u)\n", brush, angle, order);
2178 if(!brush || brush->brush.bt != BrushTypeLinearGradient)
2179 return InvalidParameter;
2181 if(!(calls++))
2182 FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
2184 return NotImplemented;