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