[GDIPLUS]
[reactos.git] / reactos / dll / win32 / gdiplus / graphics.c
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "gdiplus_private.h"
20
21 #include <winreg.h>
22 #include <shlwapi.h>
23
24 /* Mike "tamlin" Nordell 2012-09-14 for ReactOS:
25 * NOTE: Wine uses per-GpGraphics id's ('contid' starting from zero in
26 * every GpGraphics). Windows seems to use process-global id's, or at
27 * least more unique id's.
28 * This have the following implications. It:
29 * 1. fails the current gdiplus test case.
30 * 2. is not what Windows does.
31 *
32 * We therefore "obfuscate" the 'contid' a little to more match Windows'
33 * behaviour. The observable behviour should still remain the same,
34 * except for handing out more "unique" id's.
35 */
36 #define GDIP_CONTID_STEP 64
37 static volatile LONG g_priv_contid = GDIP_CONTID_STEP;
38 #define GDIP_GET_NEW_CONTID_FOR(pGpGraphics) \
39 (UINT)(InterlockedExchangeAdd(&g_priv_contid,GDIP_CONTID_STEP))
40
41 /* looks-right constants */
42 #define ANCHOR_WIDTH (2.0)
43 #define MAX_ITERS (50)
44
45 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
46 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
47 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
48 INT flags, GDIPCONST GpMatrix *matrix);
49
50 /* Converts from gdiplus path point type to gdi path point type. */
51 static BYTE convert_path_point_type(BYTE type)
52 {
53 BYTE ret;
54
55 switch(type & PathPointTypePathTypeMask){
56 case PathPointTypeBezier:
57 ret = PT_BEZIERTO;
58 break;
59 case PathPointTypeLine:
60 ret = PT_LINETO;
61 break;
62 case PathPointTypeStart:
63 ret = PT_MOVETO;
64 break;
65 default:
66 ERR("Bad point type\n");
67 return 0;
68 }
69
70 if(type & PathPointTypeCloseSubpath)
71 ret |= PT_CLOSEFIGURE;
72
73 return ret;
74 }
75
76 static COLORREF get_gdi_brush_color(const GpBrush *brush)
77 {
78 ARGB argb;
79
80 switch (brush->bt)
81 {
82 case BrushTypeSolidColor:
83 {
84 const GpSolidFill *sf = (const GpSolidFill *)brush;
85 argb = sf->color;
86 break;
87 }
88 case BrushTypeHatchFill:
89 {
90 const GpHatch *hatch = (const GpHatch *)brush;
91 argb = hatch->forecol;
92 break;
93 }
94 case BrushTypeLinearGradient:
95 {
96 const GpLineGradient *line = (const GpLineGradient *)brush;
97 argb = line->startcolor;
98 break;
99 }
100 case BrushTypePathGradient:
101 {
102 const GpPathGradient *grad = (const GpPathGradient *)brush;
103 argb = grad->centercolor;
104 break;
105 }
106 default:
107 FIXME("unhandled brush type %d\n", brush->bt);
108 argb = 0;
109 break;
110 }
111 return ARGB2COLORREF(argb);
112 }
113
114 static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
115 {
116 HBITMAP hbmp;
117 BITMAPINFOHEADER bmih;
118 DWORD *bits;
119 int x, y;
120
121 bmih.biSize = sizeof(bmih);
122 bmih.biWidth = 8;
123 bmih.biHeight = 8;
124 bmih.biPlanes = 1;
125 bmih.biBitCount = 32;
126 bmih.biCompression = BI_RGB;
127 bmih.biSizeImage = 0;
128
129 hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
130 if (hbmp)
131 {
132 const char *hatch_data;
133
134 if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
135 {
136 for (y = 0; y < 8; y++)
137 {
138 for (x = 0; x < 8; x++)
139 {
140 if (hatch_data[y] & (0x80 >> x))
141 bits[y * 8 + x] = hatch->forecol;
142 else
143 bits[y * 8 + x] = hatch->backcol;
144 }
145 }
146 }
147 else
148 {
149 FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
150
151 for (y = 0; y < 64; y++)
152 bits[y] = hatch->forecol;
153 }
154 }
155
156 return hbmp;
157 }
158
159 static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
160 {
161 switch (brush->bt)
162 {
163 case BrushTypeSolidColor:
164 {
165 const GpSolidFill *sf = (const GpSolidFill *)brush;
166 lb->lbStyle = BS_SOLID;
167 lb->lbColor = ARGB2COLORREF(sf->color);
168 lb->lbHatch = 0;
169 return Ok;
170 }
171
172 case BrushTypeHatchFill:
173 {
174 const GpHatch *hatch = (const GpHatch *)brush;
175 HBITMAP hbmp;
176
177 hbmp = create_hatch_bitmap(hatch);
178 if (!hbmp) return OutOfMemory;
179
180 lb->lbStyle = BS_PATTERN;
181 lb->lbColor = 0;
182 lb->lbHatch = (ULONG_PTR)hbmp;
183 return Ok;
184 }
185
186 default:
187 FIXME("unhandled brush type %d\n", brush->bt);
188 lb->lbStyle = BS_SOLID;
189 lb->lbColor = get_gdi_brush_color(brush);
190 lb->lbHatch = 0;
191 return Ok;
192 }
193 }
194
195 static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
196 {
197 switch (lb->lbStyle)
198 {
199 case BS_PATTERN:
200 DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
201 break;
202 }
203 return Ok;
204 }
205
206 static HBRUSH create_gdi_brush(const GpBrush *brush)
207 {
208 LOGBRUSH lb;
209 HBRUSH gdibrush;
210
211 if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
212
213 gdibrush = CreateBrushIndirect(&lb);
214 free_gdi_logbrush(&lb);
215
216 return gdibrush;
217 }
218
219 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
220 {
221 LOGBRUSH lb;
222 HPEN gdipen;
223 REAL width;
224 INT save_state, i, numdashes;
225 GpPointF pt[2];
226 DWORD dash_array[MAX_DASHLEN];
227
228 save_state = SaveDC(graphics->hdc);
229
230 EndPath(graphics->hdc);
231
232 if(pen->unit == UnitPixel){
233 width = pen->width;
234 }
235 else{
236 /* Get an estimate for the amount the pen width is affected by the world
237 * transform. (This is similar to what some of the wine drivers do.) */
238 pt[0].X = 0.0;
239 pt[0].Y = 0.0;
240 pt[1].X = 1.0;
241 pt[1].Y = 1.0;
242 GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
243 width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
244 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
245
246 width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
247 width *= graphics->scale;
248 }
249
250 if(pen->dash == DashStyleCustom){
251 numdashes = min(pen->numdashes, MAX_DASHLEN);
252
253 TRACE("dashes are: ");
254 for(i = 0; i < numdashes; i++){
255 dash_array[i] = gdip_round(width * pen->dashes[i]);
256 TRACE("%d, ", dash_array[i]);
257 }
258 TRACE("\n and the pen style is %x\n", pen->style);
259
260 create_gdi_logbrush(pen->brush, &lb);
261 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
262 numdashes, dash_array);
263 free_gdi_logbrush(&lb);
264 }
265 else
266 {
267 create_gdi_logbrush(pen->brush, &lb);
268 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
269 free_gdi_logbrush(&lb);
270 }
271
272 SelectObject(graphics->hdc, gdipen);
273
274 return save_state;
275 }
276
277 static void restore_dc(GpGraphics *graphics, INT state)
278 {
279 DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
280 RestoreDC(graphics->hdc, state);
281 }
282
283 /* This helper applies all the changes that the points listed in ptf need in
284 * order to be drawn on the device context. In the end, this should include at
285 * least:
286 * -scaling by page unit
287 * -applying world transformation
288 * -converting from float to int
289 * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
290 * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
291 * gdi to draw, and these functions would irreparably mess with line widths.
292 */
293 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
294 GpPointF *ptf, INT count)
295 {
296 REAL scale_x, scale_y;
297 GpMatrix matrix;
298 int i;
299
300 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
301 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
302
303 /* apply page scale */
304 if(graphics->unit != UnitDisplay)
305 {
306 scale_x *= graphics->scale;
307 scale_y *= graphics->scale;
308 }
309
310 matrix = graphics->worldtrans;
311 GdipScaleMatrix(&matrix, scale_x, scale_y, MatrixOrderAppend);
312 GdipTransformMatrixPoints(&matrix, ptf, count);
313
314 for(i = 0; i < count; i++){
315 pti[i].x = gdip_round(ptf[i].X);
316 pti[i].y = gdip_round(ptf[i].Y);
317 }
318 }
319
320 static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
321 HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
322 {
323 if (GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
324 GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
325 {
326 TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
327
328 StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
329 hdc, src_x, src_y, src_width, src_height, SRCCOPY);
330 }
331 else
332 {
333 BLENDFUNCTION bf;
334
335 bf.BlendOp = AC_SRC_OVER;
336 bf.BlendFlags = 0;
337 bf.SourceConstantAlpha = 255;
338 bf.AlphaFormat = AC_SRC_ALPHA;
339
340 GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
341 hdc, src_x, src_y, src_width, src_height, bf);
342 }
343 }
344
345 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
346 {
347 /* clipping region is in device coords */
348 return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
349 }
350
351 /* Draw ARGB data to the given graphics object */
352 static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
353 const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt)
354 {
355 GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
356 INT x, y;
357
358 for (y=0; y<src_height; y++)
359 {
360 for (x=0; x<src_width; x++)
361 {
362 ARGB dst_color, src_color;
363 src_color = ((ARGB*)(src + src_stride * y))[x];
364
365 if (!(src_color & 0xff000000))
366 continue;
367
368 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
369 if (fmt & PixelFormatPAlpha)
370 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over_fgpremult(dst_color, src_color));
371 else
372 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
373 }
374 }
375
376 return Ok;
377 }
378
379 static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
380 const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
381 {
382 HDC hdc;
383 HBITMAP hbitmap;
384 BITMAPINFOHEADER bih;
385 BYTE *temp_bits;
386
387 hdc = CreateCompatibleDC(0);
388
389 bih.biSize = sizeof(BITMAPINFOHEADER);
390 bih.biWidth = src_width;
391 bih.biHeight = -src_height;
392 bih.biPlanes = 1;
393 bih.biBitCount = 32;
394 bih.biCompression = BI_RGB;
395 bih.biSizeImage = 0;
396 bih.biXPelsPerMeter = 0;
397 bih.biYPelsPerMeter = 0;
398 bih.biClrUsed = 0;
399 bih.biClrImportant = 0;
400
401 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
402 (void**)&temp_bits, NULL, 0);
403
404 if ((GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
405 GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE) ||
406 fmt & PixelFormatPAlpha)
407 memcpy(temp_bits, src, src_width * src_height * 4);
408 else
409 convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
410 4 * src_width, src, src_stride);
411
412 SelectObject(hdc, hbitmap);
413 gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
414 hdc, 0, 0, src_width, src_height);
415 DeleteDC(hdc);
416 DeleteObject(hbitmap);
417
418 return Ok;
419 }
420
421 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
422 const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
423 {
424 GpStatus stat=Ok;
425
426 if (graphics->image && graphics->image->type == ImageTypeBitmap)
427 {
428 DWORD i;
429 int size;
430 RGNDATA *rgndata;
431 RECT *rects;
432 HRGN hrgn, visible_rgn;
433
434 hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
435 if (!hrgn)
436 return OutOfMemory;
437
438 stat = get_clip_hrgn(graphics, &visible_rgn);
439 if (stat != Ok)
440 {
441 DeleteObject(hrgn);
442 return stat;
443 }
444
445 if (visible_rgn)
446 {
447 CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
448 DeleteObject(visible_rgn);
449 }
450
451 if (hregion)
452 CombineRgn(hrgn, hrgn, hregion, RGN_AND);
453
454 size = GetRegionData(hrgn, 0, NULL);
455
456 rgndata = heap_alloc_zero(size);
457 if (!rgndata)
458 {
459 DeleteObject(hrgn);
460 return OutOfMemory;
461 }
462
463 GetRegionData(hrgn, size, rgndata);
464
465 rects = (RECT*)rgndata->Buffer;
466
467 for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
468 {
469 stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
470 &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
471 rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
472 src_stride, fmt);
473 }
474
475 heap_free(rgndata);
476
477 DeleteObject(hrgn);
478
479 return stat;
480 }
481 else if (graphics->image && graphics->image->type == ImageTypeMetafile)
482 {
483 ERR("This should not be used for metafiles; fix caller\n");
484 return NotImplemented;
485 }
486 else
487 {
488 HRGN hrgn;
489 int save;
490
491 stat = get_clip_hrgn(graphics, &hrgn);
492
493 if (stat != Ok)
494 return stat;
495
496 save = SaveDC(graphics->hdc);
497
498 SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
499
500 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
501
502 if (hregion)
503 ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
504
505 stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
506 src_height, src_stride, fmt);
507
508 RestoreDC(graphics->hdc, save);
509
510 DeleteObject(hrgn);
511
512 return stat;
513 }
514 }
515
516 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
517 const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
518 {
519 return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
520 }
521
522 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
523 {
524 INT start_a, end_a, final_a;
525 INT pos;
526
527 pos = gdip_round(position * 0xff);
528
529 start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
530 end_a = ((end >> 24) & 0xff) * pos;
531
532 final_a = start_a + end_a;
533
534 if (final_a < 0xff) return 0;
535
536 return (final_a / 0xff) << 24 |
537 ((((start >> 16) & 0xff) * start_a + (((end >> 16) & 0xff) * end_a)) / final_a) << 16 |
538 ((((start >> 8) & 0xff) * start_a + (((end >> 8) & 0xff) * end_a)) / final_a) << 8 |
539 (((start & 0xff) * start_a + ((end & 0xff) * end_a)) / final_a);
540 }
541
542 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
543 {
544 REAL blendfac;
545
546 /* clamp to between 0.0 and 1.0, using the wrap mode */
547 if (brush->wrap == WrapModeTile)
548 {
549 position = fmodf(position, 1.0f);
550 if (position < 0.0f) position += 1.0f;
551 }
552 else /* WrapModeFlip* */
553 {
554 position = fmodf(position, 2.0f);
555 if (position < 0.0f) position += 2.0f;
556 if (position > 1.0f) position = 2.0f - position;
557 }
558
559 if (brush->blendcount == 1)
560 blendfac = position;
561 else
562 {
563 int i=1;
564 REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
565 REAL range;
566
567 /* locate the blend positions surrounding this position */
568 while (position > brush->blendpos[i])
569 i++;
570
571 /* interpolate between the blend positions */
572 left_blendpos = brush->blendpos[i-1];
573 left_blendfac = brush->blendfac[i-1];
574 right_blendpos = brush->blendpos[i];
575 right_blendfac = brush->blendfac[i];
576 range = right_blendpos - left_blendpos;
577 blendfac = (left_blendfac * (right_blendpos - position) +
578 right_blendfac * (position - left_blendpos)) / range;
579 }
580
581 if (brush->pblendcount == 0)
582 return blend_colors(brush->startcolor, brush->endcolor, blendfac);
583 else
584 {
585 int i=1;
586 ARGB left_blendcolor, right_blendcolor;
587 REAL left_blendpos, right_blendpos;
588
589 /* locate the blend colors surrounding this position */
590 while (blendfac > brush->pblendpos[i])
591 i++;
592
593 /* interpolate between the blend colors */
594 left_blendpos = brush->pblendpos[i-1];
595 left_blendcolor = brush->pblendcolor[i-1];
596 right_blendpos = brush->pblendpos[i];
597 right_blendcolor = brush->pblendcolor[i];
598 blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
599 return blend_colors(left_blendcolor, right_blendcolor, blendfac);
600 }
601 }
602
603 static BOOL round_color_matrix(const ColorMatrix *matrix, int values[5][5])
604 {
605 /* Convert floating point color matrix to int[5][5], return TRUE if it's an identity */
606 BOOL identity = TRUE;
607 int i, j;
608
609 for (i=0; i<4; i++)
610 for (j=0; j<5; j++)
611 {
612 if (matrix->m[j][i] != (i == j ? 1.0 : 0.0))
613 identity = FALSE;
614 values[j][i] = gdip_round(matrix->m[j][i] * 256.0);
615 }
616
617 return identity;
618 }
619
620 static ARGB transform_color(ARGB color, int matrix[5][5])
621 {
622 int val[5], res[4];
623 int i, j;
624 unsigned char a, r, g, b;
625
626 val[0] = ((color >> 16) & 0xff); /* red */
627 val[1] = ((color >> 8) & 0xff); /* green */
628 val[2] = (color & 0xff); /* blue */
629 val[3] = ((color >> 24) & 0xff); /* alpha */
630 val[4] = 255; /* translation */
631
632 for (i=0; i<4; i++)
633 {
634 res[i] = 0;
635
636 for (j=0; j<5; j++)
637 res[i] += matrix[j][i] * val[j];
638 }
639
640 a = min(max(res[3] / 256, 0), 255);
641 r = min(max(res[0] / 256, 0), 255);
642 g = min(max(res[1] / 256, 0), 255);
643 b = min(max(res[2] / 256, 0), 255);
644
645 return (a << 24) | (r << 16) | (g << 8) | b;
646 }
647
648 static BOOL color_is_gray(ARGB color)
649 {
650 unsigned char r, g, b;
651
652 r = (color >> 16) & 0xff;
653 g = (color >> 8) & 0xff;
654 b = color & 0xff;
655
656 return (r == g) && (g == b);
657 }
658
659 /* returns preferred pixel format for the applied attributes */
660 PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
661 UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt)
662 {
663 UINT x, y;
664 INT i;
665
666 if (attributes->colorkeys[type].enabled ||
667 attributes->colorkeys[ColorAdjustTypeDefault].enabled)
668 {
669 const struct color_key *key;
670 BYTE min_blue, min_green, min_red;
671 BYTE max_blue, max_green, max_red;
672
673 if (!data || fmt != PixelFormat32bppARGB)
674 return PixelFormat32bppARGB;
675
676 if (attributes->colorkeys[type].enabled)
677 key = &attributes->colorkeys[type];
678 else
679 key = &attributes->colorkeys[ColorAdjustTypeDefault];
680
681 min_blue = key->low&0xff;
682 min_green = (key->low>>8)&0xff;
683 min_red = (key->low>>16)&0xff;
684
685 max_blue = key->high&0xff;
686 max_green = (key->high>>8)&0xff;
687 max_red = (key->high>>16)&0xff;
688
689 for (x=0; x<width; x++)
690 for (y=0; y<height; y++)
691 {
692 ARGB *src_color;
693 BYTE blue, green, red;
694 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
695 blue = *src_color&0xff;
696 green = (*src_color>>8)&0xff;
697 red = (*src_color>>16)&0xff;
698 if (blue >= min_blue && green >= min_green && red >= min_red &&
699 blue <= max_blue && green <= max_green && red <= max_red)
700 *src_color = 0x00000000;
701 }
702 }
703
704 if (attributes->colorremaptables[type].enabled ||
705 attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
706 {
707 const struct color_remap_table *table;
708
709 if (!data || fmt != PixelFormat32bppARGB)
710 return PixelFormat32bppARGB;
711
712 if (attributes->colorremaptables[type].enabled)
713 table = &attributes->colorremaptables[type];
714 else
715 table = &attributes->colorremaptables[ColorAdjustTypeDefault];
716
717 for (x=0; x<width; x++)
718 for (y=0; y<height; y++)
719 {
720 ARGB *src_color;
721 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
722 for (i=0; i<table->mapsize; i++)
723 {
724 if (*src_color == table->colormap[i].oldColor.Argb)
725 {
726 *src_color = table->colormap[i].newColor.Argb;
727 break;
728 }
729 }
730 }
731 }
732
733 if (attributes->colormatrices[type].enabled ||
734 attributes->colormatrices[ColorAdjustTypeDefault].enabled)
735 {
736 const struct color_matrix *colormatrices;
737 int color_matrix[5][5];
738 int gray_matrix[5][5];
739 BOOL identity;
740
741 if (!data || fmt != PixelFormat32bppARGB)
742 return PixelFormat32bppARGB;
743
744 if (attributes->colormatrices[type].enabled)
745 colormatrices = &attributes->colormatrices[type];
746 else
747 colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
748
749 identity = round_color_matrix(&colormatrices->colormatrix, color_matrix);
750
751 if (colormatrices->flags == ColorMatrixFlagsAltGray)
752 identity = (round_color_matrix(&colormatrices->graymatrix, gray_matrix) && identity);
753
754 if (!identity)
755 {
756 for (x=0; x<width; x++)
757 {
758 for (y=0; y<height; y++)
759 {
760 ARGB *src_color;
761 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
762
763 if (colormatrices->flags == ColorMatrixFlagsDefault ||
764 !color_is_gray(*src_color))
765 {
766 *src_color = transform_color(*src_color, color_matrix);
767 }
768 else if (colormatrices->flags == ColorMatrixFlagsAltGray)
769 {
770 *src_color = transform_color(*src_color, gray_matrix);
771 }
772 }
773 }
774 }
775 }
776
777 if (attributes->gamma_enabled[type] ||
778 attributes->gamma_enabled[ColorAdjustTypeDefault])
779 {
780 REAL gamma;
781
782 if (!data || fmt != PixelFormat32bppARGB)
783 return PixelFormat32bppARGB;
784
785 if (attributes->gamma_enabled[type])
786 gamma = attributes->gamma[type];
787 else
788 gamma = attributes->gamma[ColorAdjustTypeDefault];
789
790 for (x=0; x<width; x++)
791 for (y=0; y<height; y++)
792 {
793 ARGB *src_color;
794 BYTE blue, green, red;
795 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
796
797 blue = *src_color&0xff;
798 green = (*src_color>>8)&0xff;
799 red = (*src_color>>16)&0xff;
800
801 /* FIXME: We should probably use a table for this. */
802 blue = floorf(powf(blue / 255.0, gamma) * 255.0);
803 green = floorf(powf(green / 255.0, gamma) * 255.0);
804 red = floorf(powf(red / 255.0, gamma) * 255.0);
805
806 *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
807 }
808 }
809
810 return fmt;
811 }
812
813 /* Given a bitmap and its source rectangle, find the smallest rectangle in the
814 * bitmap that contains all the pixels we may need to draw it. */
815 static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
816 GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
817 GpRect *rect)
818 {
819 INT left, top, right, bottom;
820
821 switch (interpolation)
822 {
823 case InterpolationModeHighQualityBilinear:
824 case InterpolationModeHighQualityBicubic:
825 /* FIXME: Include a greater range for the prefilter? */
826 case InterpolationModeBicubic:
827 case InterpolationModeBilinear:
828 left = (INT)(floorf(srcx));
829 top = (INT)(floorf(srcy));
830 right = (INT)(ceilf(srcx+srcwidth));
831 bottom = (INT)(ceilf(srcy+srcheight));
832 break;
833 case InterpolationModeNearestNeighbor:
834 default:
835 left = gdip_round(srcx);
836 top = gdip_round(srcy);
837 right = gdip_round(srcx+srcwidth);
838 bottom = gdip_round(srcy+srcheight);
839 break;
840 }
841
842 if (wrap == WrapModeClamp)
843 {
844 if (left < 0)
845 left = 0;
846 if (top < 0)
847 top = 0;
848 if (right >= bitmap->width)
849 right = bitmap->width-1;
850 if (bottom >= bitmap->height)
851 bottom = bitmap->height-1;
852 if (bottom < top || right < left)
853 /* entirely outside image, just sample a pixel so we don't have to
854 * special-case this later */
855 left = top = right = bottom = 0;
856 }
857 else
858 {
859 /* In some cases we can make the rectangle smaller here, but the logic
860 * is hard to get right, and tiling suggests we're likely to use the
861 * entire source image. */
862 if (left < 0 || right >= bitmap->width)
863 {
864 left = 0;
865 right = bitmap->width-1;
866 }
867
868 if (top < 0 || bottom >= bitmap->height)
869 {
870 top = 0;
871 bottom = bitmap->height-1;
872 }
873 }
874
875 rect->X = left;
876 rect->Y = top;
877 rect->Width = right - left + 1;
878 rect->Height = bottom - top + 1;
879 }
880
881 static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
882 UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
883 {
884 if (attributes->wrap == WrapModeClamp)
885 {
886 if (x < 0 || y < 0 || x >= width || y >= height)
887 return attributes->outside_color;
888 }
889 else
890 {
891 /* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
892 if (x < 0)
893 x = width*2 + x % (width * 2);
894 if (y < 0)
895 y = height*2 + y % (height * 2);
896
897 if ((attributes->wrap & 1) == 1)
898 {
899 /* Flip X */
900 if ((x / width) % 2 == 0)
901 x = x % width;
902 else
903 x = width - 1 - x % width;
904 }
905 else
906 x = x % width;
907
908 if ((attributes->wrap & 2) == 2)
909 {
910 /* Flip Y */
911 if ((y / height) % 2 == 0)
912 y = y % height;
913 else
914 y = height - 1 - y % height;
915 }
916 else
917 y = y % height;
918 }
919
920 if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
921 {
922 ERR("out of range pixel requested\n");
923 return 0xffcd0084;
924 }
925
926 return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
927 }
928
929 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
930 UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
931 InterpolationMode interpolation, PixelOffsetMode offset_mode)
932 {
933 static int fixme;
934
935 switch (interpolation)
936 {
937 default:
938 if (!fixme++)
939 FIXME("Unimplemented interpolation %i\n", interpolation);
940 /* fall-through */
941 case InterpolationModeBilinear:
942 {
943 REAL leftxf, topyf;
944 INT leftx, rightx, topy, bottomy;
945 ARGB topleft, topright, bottomleft, bottomright;
946 ARGB top, bottom;
947 float x_offset;
948
949 leftxf = floorf(point->X);
950 leftx = (INT)leftxf;
951 rightx = (INT)ceilf(point->X);
952 topyf = floorf(point->Y);
953 topy = (INT)topyf;
954 bottomy = (INT)ceilf(point->Y);
955
956 if (leftx == rightx && topy == bottomy)
957 return sample_bitmap_pixel(src_rect, bits, width, height,
958 leftx, topy, attributes);
959
960 topleft = sample_bitmap_pixel(src_rect, bits, width, height,
961 leftx, topy, attributes);
962 topright = sample_bitmap_pixel(src_rect, bits, width, height,
963 rightx, topy, attributes);
964 bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
965 leftx, bottomy, attributes);
966 bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
967 rightx, bottomy, attributes);
968
969 x_offset = point->X - leftxf;
970 top = blend_colors(topleft, topright, x_offset);
971 bottom = blend_colors(bottomleft, bottomright, x_offset);
972
973 return blend_colors(top, bottom, point->Y - topyf);
974 }
975 case InterpolationModeNearestNeighbor:
976 {
977 FLOAT pixel_offset;
978 switch (offset_mode)
979 {
980 default:
981 case PixelOffsetModeNone:
982 case PixelOffsetModeHighSpeed:
983 pixel_offset = 0.5;
984 break;
985
986 case PixelOffsetModeHalf:
987 case PixelOffsetModeHighQuality:
988 pixel_offset = 0.0;
989 break;
990 }
991 return sample_bitmap_pixel(src_rect, bits, width, height,
992 floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
993 }
994
995 }
996 }
997
998 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
999 {
1000 return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
1001 }
1002
1003 static BOOL brush_can_fill_path(GpBrush *brush)
1004 {
1005 switch (brush->bt)
1006 {
1007 case BrushTypeSolidColor:
1008 return TRUE;
1009 case BrushTypeHatchFill:
1010 {
1011 GpHatch *hatch = (GpHatch*)brush;
1012 return ((hatch->forecol & 0xff000000) == 0xff000000) &&
1013 ((hatch->backcol & 0xff000000) == 0xff000000);
1014 }
1015 case BrushTypeLinearGradient:
1016 case BrushTypeTextureFill:
1017 /* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
1018 default:
1019 return FALSE;
1020 }
1021 }
1022
1023 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
1024 {
1025 switch (brush->bt)
1026 {
1027 case BrushTypeSolidColor:
1028 {
1029 GpSolidFill *fill = (GpSolidFill*)brush;
1030 HBITMAP bmp = ARGB2BMP(fill->color);
1031
1032 if (bmp)
1033 {
1034 RECT rc;
1035 /* partially transparent fill */
1036
1037 SelectClipPath(graphics->hdc, RGN_AND);
1038 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
1039 {
1040 HDC hdc = CreateCompatibleDC(NULL);
1041
1042 if (!hdc) break;
1043
1044 SelectObject(hdc, bmp);
1045 gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
1046 hdc, 0, 0, 1, 1);
1047 DeleteDC(hdc);
1048 }
1049
1050 DeleteObject(bmp);
1051 break;
1052 }
1053 /* else fall through */
1054 }
1055 default:
1056 {
1057 HBRUSH gdibrush, old_brush;
1058
1059 gdibrush = create_gdi_brush(brush);
1060 if (!gdibrush) return;
1061
1062 old_brush = SelectObject(graphics->hdc, gdibrush);
1063 FillPath(graphics->hdc);
1064 SelectObject(graphics->hdc, old_brush);
1065 DeleteObject(gdibrush);
1066 break;
1067 }
1068 }
1069 }
1070
1071 static BOOL brush_can_fill_pixels(GpBrush *brush)
1072 {
1073 switch (brush->bt)
1074 {
1075 case BrushTypeSolidColor:
1076 case BrushTypeHatchFill:
1077 case BrushTypeLinearGradient:
1078 case BrushTypeTextureFill:
1079 case BrushTypePathGradient:
1080 return TRUE;
1081 default:
1082 return FALSE;
1083 }
1084 }
1085
1086 static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
1087 DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
1088 {
1089 switch (brush->bt)
1090 {
1091 case BrushTypeSolidColor:
1092 {
1093 int x, y;
1094 GpSolidFill *fill = (GpSolidFill*)brush;
1095 for (x=0; x<fill_area->Width; x++)
1096 for (y=0; y<fill_area->Height; y++)
1097 argb_pixels[x + y*cdwStride] = fill->color;
1098 return Ok;
1099 }
1100 case BrushTypeHatchFill:
1101 {
1102 int x, y;
1103 GpHatch *fill = (GpHatch*)brush;
1104 const char *hatch_data;
1105
1106 if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
1107 return NotImplemented;
1108
1109 for (x=0; x<fill_area->Width; x++)
1110 for (y=0; y<fill_area->Height; y++)
1111 {
1112 int hx, hy;
1113
1114 /* FIXME: Account for the rendering origin */
1115 hx = (x + fill_area->X) % 8;
1116 hy = (y + fill_area->Y) % 8;
1117
1118 if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
1119 argb_pixels[x + y*cdwStride] = fill->forecol;
1120 else
1121 argb_pixels[x + y*cdwStride] = fill->backcol;
1122 }
1123
1124 return Ok;
1125 }
1126 case BrushTypeLinearGradient:
1127 {
1128 GpLineGradient *fill = (GpLineGradient*)brush;
1129 GpPointF draw_points[3], line_points[3];
1130 GpStatus stat;
1131 static const GpRectF box_1 = { 0.0, 0.0, 1.0, 1.0 };
1132 GpMatrix *world_to_gradient; /* FIXME: Store this in the brush? */
1133 int x, y;
1134
1135 draw_points[0].X = fill_area->X;
1136 draw_points[0].Y = fill_area->Y;
1137 draw_points[1].X = fill_area->X+1;
1138 draw_points[1].Y = fill_area->Y;
1139 draw_points[2].X = fill_area->X;
1140 draw_points[2].Y = fill_area->Y+1;
1141
1142 /* Transform the points to a co-ordinate space where X is the point's
1143 * position in the gradient, 0.0 being the start point and 1.0 the
1144 * end point. */
1145 stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
1146 CoordinateSpaceDevice, draw_points, 3);
1147
1148 if (stat == Ok)
1149 {
1150 line_points[0] = fill->startpoint;
1151 line_points[1] = fill->endpoint;
1152 line_points[2].X = fill->startpoint.X + (fill->startpoint.Y - fill->endpoint.Y);
1153 line_points[2].Y = fill->startpoint.Y + (fill->endpoint.X - fill->startpoint.X);
1154
1155 stat = GdipCreateMatrix3(&box_1, line_points, &world_to_gradient);
1156 }
1157
1158 if (stat == Ok)
1159 {
1160 stat = GdipInvertMatrix(world_to_gradient);
1161
1162 if (stat == Ok)
1163 stat = GdipTransformMatrixPoints(world_to_gradient, draw_points, 3);
1164
1165 GdipDeleteMatrix(world_to_gradient);
1166 }
1167
1168 if (stat == Ok)
1169 {
1170 REAL x_delta = draw_points[1].X - draw_points[0].X;
1171 REAL y_delta = draw_points[2].X - draw_points[0].X;
1172
1173 for (y=0; y<fill_area->Height; y++)
1174 {
1175 for (x=0; x<fill_area->Width; x++)
1176 {
1177 REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
1178
1179 argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
1180 }
1181 }
1182 }
1183
1184 return stat;
1185 }
1186 case BrushTypeTextureFill:
1187 {
1188 GpTexture *fill = (GpTexture*)brush;
1189 GpPointF draw_points[3];
1190 GpStatus stat;
1191 int x, y;
1192 GpBitmap *bitmap;
1193 int src_stride;
1194 GpRect src_area;
1195
1196 if (fill->image->type != ImageTypeBitmap)
1197 {
1198 FIXME("metafile texture brushes not implemented\n");
1199 return NotImplemented;
1200 }
1201
1202 bitmap = (GpBitmap*)fill->image;
1203 src_stride = sizeof(ARGB) * bitmap->width;
1204
1205 src_area.X = src_area.Y = 0;
1206 src_area.Width = bitmap->width;
1207 src_area.Height = bitmap->height;
1208
1209 draw_points[0].X = fill_area->X;
1210 draw_points[0].Y = fill_area->Y;
1211 draw_points[1].X = fill_area->X+1;
1212 draw_points[1].Y = fill_area->Y;
1213 draw_points[2].X = fill_area->X;
1214 draw_points[2].Y = fill_area->Y+1;
1215
1216 /* Transform the points to the co-ordinate space of the bitmap. */
1217 stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
1218 CoordinateSpaceDevice, draw_points, 3);
1219
1220 if (stat == Ok)
1221 {
1222 GpMatrix world_to_texture = fill->transform;
1223
1224 stat = GdipInvertMatrix(&world_to_texture);
1225 if (stat == Ok)
1226 stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
1227 }
1228
1229 if (stat == Ok && !fill->bitmap_bits)
1230 {
1231 BitmapData lockeddata;
1232
1233 fill->bitmap_bits = heap_alloc_zero(sizeof(ARGB) * bitmap->width * bitmap->height);
1234 if (!fill->bitmap_bits)
1235 stat = OutOfMemory;
1236
1237 if (stat == Ok)
1238 {
1239 lockeddata.Width = bitmap->width;
1240 lockeddata.Height = bitmap->height;
1241 lockeddata.Stride = src_stride;
1242 lockeddata.PixelFormat = PixelFormat32bppARGB;
1243 lockeddata.Scan0 = fill->bitmap_bits;
1244
1245 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
1246 PixelFormat32bppARGB, &lockeddata);
1247 }
1248
1249 if (stat == Ok)
1250 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
1251
1252 if (stat == Ok)
1253 apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
1254 bitmap->width, bitmap->height,
1255 src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
1256
1257 if (stat != Ok)
1258 {
1259 heap_free(fill->bitmap_bits);
1260 fill->bitmap_bits = NULL;
1261 }
1262 }
1263
1264 if (stat == Ok)
1265 {
1266 REAL x_dx = draw_points[1].X - draw_points[0].X;
1267 REAL x_dy = draw_points[1].Y - draw_points[0].Y;
1268 REAL y_dx = draw_points[2].X - draw_points[0].X;
1269 REAL y_dy = draw_points[2].Y - draw_points[0].Y;
1270
1271 for (y=0; y<fill_area->Height; y++)
1272 {
1273 for (x=0; x<fill_area->Width; x++)
1274 {
1275 GpPointF point;
1276 point.X = draw_points[0].X + x * x_dx + y * y_dx;
1277 point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
1278
1279 argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
1280 &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
1281 &point, fill->imageattributes, graphics->interpolation,
1282 graphics->pixeloffset);
1283 }
1284 }
1285 }
1286
1287 return stat;
1288 }
1289 case BrushTypePathGradient:
1290 {
1291 GpPathGradient *fill = (GpPathGradient*)brush;
1292 GpPath *flat_path;
1293 GpMatrix world_to_device;
1294 GpStatus stat;
1295 int i, figure_start=0;
1296 GpPointF start_point, end_point, center_point;
1297 BYTE type;
1298 REAL min_yf, max_yf, line1_xf, line2_xf;
1299 INT min_y, max_y, min_x, max_x;
1300 INT x, y;
1301 ARGB outer_color;
1302 static BOOL transform_fixme_once;
1303
1304 if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
1305 {
1306 static int once;
1307 if (!once++)
1308 FIXME("path gradient focus not implemented\n");
1309 }
1310
1311 if (fill->gamma)
1312 {
1313 static int once;
1314 if (!once++)
1315 FIXME("path gradient gamma correction not implemented\n");
1316 }
1317
1318 if (fill->blendcount)
1319 {
1320 static int once;
1321 if (!once++)
1322 FIXME("path gradient blend not implemented\n");
1323 }
1324
1325 if (fill->pblendcount)
1326 {
1327 static int once;
1328 if (!once++)
1329 FIXME("path gradient preset blend not implemented\n");
1330 }
1331
1332 if (!transform_fixme_once)
1333 {
1334 BOOL is_identity=TRUE;
1335 GdipIsMatrixIdentity(&fill->transform, &is_identity);
1336 if (!is_identity)
1337 {
1338 FIXME("path gradient transform not implemented\n");
1339 transform_fixme_once = TRUE;
1340 }
1341 }
1342
1343 stat = GdipClonePath(fill->path, &flat_path);
1344
1345 if (stat != Ok)
1346 return stat;
1347
1348 stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
1349 CoordinateSpaceWorld, &world_to_device);
1350 if (stat == Ok)
1351 {
1352 stat = GdipTransformPath(flat_path, &world_to_device);
1353
1354 if (stat == Ok)
1355 {
1356 center_point = fill->center;
1357 stat = GdipTransformMatrixPoints(&world_to_device, &center_point, 1);
1358 }
1359
1360 if (stat == Ok)
1361 stat = GdipFlattenPath(flat_path, NULL, 0.5);
1362 }
1363
1364 if (stat != Ok)
1365 {
1366 GdipDeletePath(flat_path);
1367 return stat;
1368 }
1369
1370 for (i=0; i<flat_path->pathdata.Count; i++)
1371 {
1372 int start_center_line=0, end_center_line=0;
1373 BOOL seen_start = FALSE, seen_end = FALSE, seen_center = FALSE;
1374 REAL center_distance;
1375 ARGB start_color, end_color;
1376 REAL dy, dx;
1377
1378 type = flat_path->pathdata.Types[i];
1379
1380 if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
1381 figure_start = i;
1382
1383 start_point = flat_path->pathdata.Points[i];
1384
1385 start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
1386
1387 if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
1388 {
1389 end_point = flat_path->pathdata.Points[figure_start];
1390 end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
1391 }
1392 else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
1393 {
1394 end_point = flat_path->pathdata.Points[i+1];
1395 end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
1396 }
1397 else
1398 continue;
1399
1400 outer_color = start_color;
1401
1402 min_yf = center_point.Y;
1403 if (min_yf > start_point.Y) min_yf = start_point.Y;
1404 if (min_yf > end_point.Y) min_yf = end_point.Y;
1405
1406 if (min_yf < fill_area->Y)
1407 min_y = fill_area->Y;
1408 else
1409 min_y = (INT)ceil(min_yf);
1410
1411 max_yf = center_point.Y;
1412 if (max_yf < start_point.Y) max_yf = start_point.Y;
1413 if (max_yf < end_point.Y) max_yf = end_point.Y;
1414
1415 if (max_yf > fill_area->Y + fill_area->Height)
1416 max_y = fill_area->Y + fill_area->Height;
1417 else
1418 max_y = (INT)ceil(max_yf);
1419
1420 dy = end_point.Y - start_point.Y;
1421 dx = end_point.X - start_point.X;
1422
1423 /* This is proportional to the distance from start-end line to center point. */
1424 center_distance = dy * (start_point.X - center_point.X) +
1425 dx * (center_point.Y - start_point.Y);
1426
1427 for (y=min_y; y<max_y; y++)
1428 {
1429 REAL yf = (REAL)y;
1430
1431 if (!seen_start && yf >= start_point.Y)
1432 {
1433 seen_start = TRUE;
1434 start_center_line ^= 1;
1435 }
1436 if (!seen_end && yf >= end_point.Y)
1437 {
1438 seen_end = TRUE;
1439 end_center_line ^= 1;
1440 }
1441 if (!seen_center && yf >= center_point.Y)
1442 {
1443 seen_center = TRUE;
1444 start_center_line ^= 1;
1445 end_center_line ^= 1;
1446 }
1447
1448 if (start_center_line)
1449 line1_xf = intersect_line_scanline(&start_point, &center_point, yf);
1450 else
1451 line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
1452
1453 if (end_center_line)
1454 line2_xf = intersect_line_scanline(&end_point, &center_point, yf);
1455 else
1456 line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
1457
1458 if (line1_xf < line2_xf)
1459 {
1460 min_x = (INT)ceil(line1_xf);
1461 max_x = (INT)ceil(line2_xf);
1462 }
1463 else
1464 {
1465 min_x = (INT)ceil(line2_xf);
1466 max_x = (INT)ceil(line1_xf);
1467 }
1468
1469 if (min_x < fill_area->X)
1470 min_x = fill_area->X;
1471 if (max_x > fill_area->X + fill_area->Width)
1472 max_x = fill_area->X + fill_area->Width;
1473
1474 for (x=min_x; x<max_x; x++)
1475 {
1476 REAL xf = (REAL)x;
1477 REAL distance;
1478
1479 if (start_color != end_color)
1480 {
1481 REAL blend_amount, pdy, pdx;
1482 pdy = yf - center_point.Y;
1483 pdx = xf - center_point.X;
1484 blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
1485 outer_color = blend_colors(start_color, end_color, blend_amount);
1486 }
1487
1488 distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
1489 (end_point.X - start_point.X) * (yf - start_point.Y);
1490
1491 distance = distance / center_distance;
1492
1493 argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
1494 blend_colors(outer_color, fill->centercolor, distance);
1495 }
1496 }
1497 }
1498
1499 GdipDeletePath(flat_path);
1500 return stat;
1501 }
1502 default:
1503 return NotImplemented;
1504 }
1505 }
1506
1507 /* Draws the linecap the specified color and size on the hdc. The linecap is in
1508 * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
1509 * should not be called on an hdc that has a path you care about. */
1510 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
1511 const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
1512 {
1513 HGDIOBJ oldbrush = NULL, oldpen = NULL;
1514 GpMatrix matrix;
1515 HBRUSH brush = NULL;
1516 HPEN pen = NULL;
1517 PointF ptf[4], *custptf = NULL;
1518 POINT pt[4], *custpt = NULL;
1519 BYTE *tp = NULL;
1520 REAL theta, dsmall, dbig, dx, dy = 0.0;
1521 INT i, count;
1522 LOGBRUSH lb;
1523 BOOL customstroke;
1524
1525 if((x1 == x2) && (y1 == y2))
1526 return;
1527
1528 theta = gdiplus_atan2(y2 - y1, x2 - x1);
1529
1530 customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
1531 if(!customstroke){
1532 brush = CreateSolidBrush(color);
1533 lb.lbStyle = BS_SOLID;
1534 lb.lbColor = color;
1535 lb.lbHatch = 0;
1536 pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
1537 PS_JOIN_MITER, 1, &lb, 0,
1538 NULL);
1539 oldbrush = SelectObject(graphics->hdc, brush);
1540 oldpen = SelectObject(graphics->hdc, pen);
1541 }
1542
1543 switch(cap){
1544 case LineCapFlat:
1545 break;
1546 case LineCapSquare:
1547 case LineCapSquareAnchor:
1548 case LineCapDiamondAnchor:
1549 size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
1550 if(cap == LineCapDiamondAnchor){
1551 dsmall = cos(theta + M_PI_2) * size;
1552 dbig = sin(theta + M_PI_2) * size;
1553 }
1554 else{
1555 dsmall = cos(theta + M_PI_4) * size;
1556 dbig = sin(theta + M_PI_4) * size;
1557 }
1558
1559 ptf[0].X = x2 - dsmall;
1560 ptf[1].X = x2 + dbig;
1561
1562 ptf[0].Y = y2 - dbig;
1563 ptf[3].Y = y2 + dsmall;
1564
1565 ptf[1].Y = y2 - dsmall;
1566 ptf[2].Y = y2 + dbig;
1567
1568 ptf[3].X = x2 - dbig;
1569 ptf[2].X = x2 + dsmall;
1570
1571 transform_and_round_points(graphics, pt, ptf, 4);
1572 Polygon(graphics->hdc, pt, 4);
1573
1574 break;
1575 case LineCapArrowAnchor:
1576 size = size * 4.0 / sqrt(3.0);
1577
1578 dx = cos(M_PI / 6.0 + theta) * size;
1579 dy = sin(M_PI / 6.0 + theta) * size;
1580
1581 ptf[0].X = x2 - dx;
1582 ptf[0].Y = y2 - dy;
1583
1584 dx = cos(- M_PI / 6.0 + theta) * size;
1585 dy = sin(- M_PI / 6.0 + theta) * size;
1586
1587 ptf[1].X = x2 - dx;
1588 ptf[1].Y = y2 - dy;
1589
1590 ptf[2].X = x2;
1591 ptf[2].Y = y2;
1592
1593 transform_and_round_points(graphics, pt, ptf, 3);
1594 Polygon(graphics->hdc, pt, 3);
1595
1596 break;
1597 case LineCapRoundAnchor:
1598 dx = dy = ANCHOR_WIDTH * size / 2.0;
1599
1600 ptf[0].X = x2 - dx;
1601 ptf[0].Y = y2 - dy;
1602 ptf[1].X = x2 + dx;
1603 ptf[1].Y = y2 + dy;
1604
1605 transform_and_round_points(graphics, pt, ptf, 2);
1606 Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
1607
1608 break;
1609 case LineCapTriangle:
1610 size = size / 2.0;
1611 dx = cos(M_PI_2 + theta) * size;
1612 dy = sin(M_PI_2 + theta) * size;
1613
1614 ptf[0].X = x2 - dx;
1615 ptf[0].Y = y2 - dy;
1616 ptf[1].X = x2 + dx;
1617 ptf[1].Y = y2 + dy;
1618
1619 dx = cos(theta) * size;
1620 dy = sin(theta) * size;
1621
1622 ptf[2].X = x2 + dx;
1623 ptf[2].Y = y2 + dy;
1624
1625 transform_and_round_points(graphics, pt, ptf, 3);
1626 Polygon(graphics->hdc, pt, 3);
1627
1628 break;
1629 case LineCapRound:
1630 dx = dy = size / 2.0;
1631
1632 ptf[0].X = x2 - dx;
1633 ptf[0].Y = y2 - dy;
1634 ptf[1].X = x2 + dx;
1635 ptf[1].Y = y2 + dy;
1636
1637 dx = -cos(M_PI_2 + theta) * size;
1638 dy = -sin(M_PI_2 + theta) * size;
1639
1640 ptf[2].X = x2 - dx;
1641 ptf[2].Y = y2 - dy;
1642 ptf[3].X = x2 + dx;
1643 ptf[3].Y = y2 + dy;
1644
1645 transform_and_round_points(graphics, pt, ptf, 4);
1646 Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
1647 pt[2].y, pt[3].x, pt[3].y);
1648
1649 break;
1650 case LineCapCustom:
1651 if(!custom)
1652 break;
1653
1654 count = custom->pathdata.Count;
1655 custptf = heap_alloc_zero(count * sizeof(PointF));
1656 custpt = heap_alloc_zero(count * sizeof(POINT));
1657 tp = heap_alloc_zero(count);
1658
1659 if(!custptf || !custpt || !tp)
1660 goto custend;
1661
1662 memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
1663
1664 GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1665 GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
1666 GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
1667 MatrixOrderAppend);
1668 GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
1669 GdipTransformMatrixPoints(&matrix, custptf, count);
1670
1671 transform_and_round_points(graphics, custpt, custptf, count);
1672
1673 for(i = 0; i < count; i++)
1674 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
1675
1676 if(custom->fill){
1677 BeginPath(graphics->hdc);
1678 PolyDraw(graphics->hdc, custpt, tp, count);
1679 EndPath(graphics->hdc);
1680 StrokeAndFillPath(graphics->hdc);
1681 }
1682 else
1683 PolyDraw(graphics->hdc, custpt, tp, count);
1684
1685 custend:
1686 heap_free(custptf);
1687 heap_free(custpt);
1688 heap_free(tp);
1689 break;
1690 default:
1691 break;
1692 }
1693
1694 if(!customstroke){
1695 SelectObject(graphics->hdc, oldbrush);
1696 SelectObject(graphics->hdc, oldpen);
1697 DeleteObject(brush);
1698 DeleteObject(pen);
1699 }
1700 }
1701
1702 /* Shortens the line by the given percent by changing x2, y2.
1703 * If percent is > 1.0 then the line will change direction.
1704 * If percent is negative it can lengthen the line. */
1705 static void shorten_line_percent(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL percent)
1706 {
1707 REAL dist, theta, dx, dy;
1708
1709 if((y1 == *y2) && (x1 == *x2))
1710 return;
1711
1712 dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
1713 theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
1714 dx = cos(theta) * dist;
1715 dy = sin(theta) * dist;
1716
1717 *x2 = *x2 + dx;
1718 *y2 = *y2 + dy;
1719 }
1720
1721 /* Shortens the line by the given amount by changing x2, y2.
1722 * If the amount is greater than the distance, the line will become length 0.
1723 * If the amount is negative, it can lengthen the line. */
1724 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
1725 {
1726 REAL dx, dy, percent;
1727
1728 dx = *x2 - x1;
1729 dy = *y2 - y1;
1730 if(dx == 0 && dy == 0)
1731 return;
1732
1733 percent = amt / sqrt(dx * dx + dy * dy);
1734 if(percent >= 1.0){
1735 *x2 = x1;
1736 *y2 = y1;
1737 return;
1738 }
1739
1740 shorten_line_percent(x1, y1, x2, y2, percent);
1741 }
1742
1743 /* Conducts a linear search to find the bezier points that will back off
1744 * the endpoint of the curve by a distance of amt. Linear search works
1745 * better than binary in this case because there are multiple solutions,
1746 * and binary searches often find a bad one. I don't think this is what
1747 * Windows does but short of rendering the bezier without GDI's help it's
1748 * the best we can do. If rev then work from the start of the passed points
1749 * instead of the end. */
1750 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
1751 {
1752 GpPointF origpt[4];
1753 REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
1754 INT i, first = 0, second = 1, third = 2, fourth = 3;
1755
1756 if(rev){
1757 first = 3;
1758 second = 2;
1759 third = 1;
1760 fourth = 0;
1761 }
1762
1763 origx = pt[fourth].X;
1764 origy = pt[fourth].Y;
1765 memcpy(origpt, pt, sizeof(GpPointF) * 4);
1766
1767 for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
1768 /* reset bezier points to original values */
1769 memcpy(pt, origpt, sizeof(GpPointF) * 4);
1770 /* Perform magic on bezier points. Order is important here.*/
1771 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1772 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1773 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1774 shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
1775 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1776 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1777
1778 dx = pt[fourth].X - origx;
1779 dy = pt[fourth].Y - origy;
1780
1781 diff = sqrt(dx * dx + dy * dy);
1782 percent += 0.0005 * amt;
1783 }
1784 }
1785
1786 /* Draws a combination of bezier curves and lines between points. */
1787 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
1788 GDIPCONST BYTE * types, INT count, BOOL caps)
1789 {
1790 POINT *pti = heap_alloc_zero(count * sizeof(POINT));
1791 BYTE *tp = heap_alloc_zero(count);
1792 GpPointF *ptcopy = heap_alloc_zero(count * sizeof(GpPointF));
1793 INT i, j;
1794 GpStatus status = GenericError;
1795
1796 if(!count){
1797 status = Ok;
1798 goto end;
1799 }
1800 if(!pti || !tp || !ptcopy){
1801 status = OutOfMemory;
1802 goto end;
1803 }
1804
1805 for(i = 1; i < count; i++){
1806 if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
1807 if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
1808 || !(types[i + 2] & PathPointTypeBezier)){
1809 ERR("Bad bezier points\n");
1810 goto end;
1811 }
1812 i += 2;
1813 }
1814 }
1815
1816 memcpy(ptcopy, pt, count * sizeof(GpPointF));
1817
1818 /* If we are drawing caps, go through the points and adjust them accordingly,
1819 * and draw the caps. */
1820 if(caps){
1821 switch(types[count - 1] & PathPointTypePathTypeMask){
1822 case PathPointTypeBezier:
1823 if(pen->endcap == LineCapArrowAnchor)
1824 shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
1825 else if((pen->endcap == LineCapCustom) && pen->customend)
1826 shorten_bezier_amt(&ptcopy[count - 4],
1827 pen->width * pen->customend->inset, FALSE);
1828
1829 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1830 pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
1831 pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
1832 pt[count - 1].X, pt[count - 1].Y);
1833
1834 break;
1835 case PathPointTypeLine:
1836 if(pen->endcap == LineCapArrowAnchor)
1837 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1838 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1839 pen->width);
1840 else if((pen->endcap == LineCapCustom) && pen->customend)
1841 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1842 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1843 pen->customend->inset * pen->width);
1844
1845 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1846 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
1847 pt[count - 1].Y);
1848
1849 break;
1850 default:
1851 ERR("Bad path last point\n");
1852 goto end;
1853 }
1854
1855 /* Find start of points */
1856 for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
1857 == PathPointTypeStart); j++);
1858
1859 switch(types[j] & PathPointTypePathTypeMask){
1860 case PathPointTypeBezier:
1861 if(pen->startcap == LineCapArrowAnchor)
1862 shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
1863 else if((pen->startcap == LineCapCustom) && pen->customstart)
1864 shorten_bezier_amt(&ptcopy[j - 1],
1865 pen->width * pen->customstart->inset, TRUE);
1866
1867 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
1868 pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
1869 pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
1870 pt[j - 1].X, pt[j - 1].Y);
1871
1872 break;
1873 case PathPointTypeLine:
1874 if(pen->startcap == LineCapArrowAnchor)
1875 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1876 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1877 pen->width);
1878 else if((pen->startcap == LineCapCustom) && pen->customstart)
1879 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1880 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1881 pen->customstart->inset * pen->width);
1882
1883 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
1884 pt[j].X, pt[j].Y, pt[j - 1].X,
1885 pt[j - 1].Y);
1886
1887 break;
1888 default:
1889 ERR("Bad path points\n");
1890 goto end;
1891 }
1892 }
1893
1894 transform_and_round_points(graphics, pti, ptcopy, count);
1895
1896 for(i = 0; i < count; i++){
1897 tp[i] = convert_path_point_type(types[i]);
1898 }
1899
1900 PolyDraw(graphics->hdc, pti, tp, count);
1901
1902 status = Ok;
1903
1904 end:
1905 heap_free(pti);
1906 heap_free(ptcopy);
1907 heap_free(tp);
1908
1909 return status;
1910 }
1911
1912 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
1913 {
1914 GpStatus result;
1915
1916 BeginPath(graphics->hdc);
1917 result = draw_poly(graphics, NULL, path->pathdata.Points,
1918 path->pathdata.Types, path->pathdata.Count, FALSE);
1919 EndPath(graphics->hdc);
1920 return result;
1921 }
1922
1923 typedef enum GraphicsContainerType {
1924 BEGIN_CONTAINER,
1925 SAVE_GRAPHICS
1926 } GraphicsContainerType;
1927
1928 typedef struct _GraphicsContainerItem {
1929 struct list entry;
1930 GraphicsContainer contid;
1931 GraphicsContainerType type;
1932
1933 SmoothingMode smoothing;
1934 CompositingQuality compqual;
1935 InterpolationMode interpolation;
1936 CompositingMode compmode;
1937 TextRenderingHint texthint;
1938 REAL scale;
1939 GpUnit unit;
1940 PixelOffsetMode pixeloffset;
1941 UINT textcontrast;
1942 GpMatrix worldtrans;
1943 GpRegion* clip;
1944 INT origin_x, origin_y;
1945 } GraphicsContainerItem;
1946
1947 static GpStatus init_container(GraphicsContainerItem** container,
1948 GDIPCONST GpGraphics* graphics, GraphicsContainerType type){
1949 GpStatus sts;
1950
1951 *container = heap_alloc_zero(sizeof(GraphicsContainerItem));
1952 if(!(*container))
1953 return OutOfMemory;
1954
1955 (*container)->contid = graphics->contid + 1;
1956 (*container)->type = type;
1957
1958 (*container)->smoothing = graphics->smoothing;
1959 (*container)->compqual = graphics->compqual;
1960 (*container)->interpolation = graphics->interpolation;
1961 (*container)->compmode = graphics->compmode;
1962 (*container)->texthint = graphics->texthint;
1963 (*container)->scale = graphics->scale;
1964 (*container)->unit = graphics->unit;
1965 (*container)->textcontrast = graphics->textcontrast;
1966 (*container)->pixeloffset = graphics->pixeloffset;
1967 (*container)->origin_x = graphics->origin_x;
1968 (*container)->origin_y = graphics->origin_y;
1969 (*container)->worldtrans = graphics->worldtrans;
1970
1971 sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
1972 if(sts != Ok){
1973 heap_free(*container);
1974 *container = NULL;
1975 return sts;
1976 }
1977
1978 return Ok;
1979 }
1980
1981 static void delete_container(GraphicsContainerItem* container)
1982 {
1983 GdipDeleteRegion(container->clip);
1984 heap_free(container);
1985 }
1986
1987 static GpStatus restore_container(GpGraphics* graphics,
1988 GDIPCONST GraphicsContainerItem* container){
1989 GpStatus sts;
1990 GpRegion *newClip;
1991
1992 sts = GdipCloneRegion(container->clip, &newClip);
1993 if(sts != Ok) return sts;
1994
1995 graphics->worldtrans = container->worldtrans;
1996
1997 GdipDeleteRegion(graphics->clip);
1998 graphics->clip = newClip;
1999
2000 graphics->contid = container->contid - 1;
2001
2002 graphics->smoothing = container->smoothing;
2003 graphics->compqual = container->compqual;
2004 graphics->interpolation = container->interpolation;
2005 graphics->compmode = container->compmode;
2006 graphics->texthint = container->texthint;
2007 graphics->scale = container->scale;
2008 graphics->unit = container->unit;
2009 graphics->textcontrast = container->textcontrast;
2010 graphics->pixeloffset = container->pixeloffset;
2011 graphics->origin_x = container->origin_x;
2012 graphics->origin_y = container->origin_y;
2013
2014 return Ok;
2015 }
2016
2017 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
2018 {
2019 RECT wnd_rect;
2020 GpStatus stat=Ok;
2021 GpUnit unit;
2022
2023 if(graphics->hwnd) {
2024 if(!GetClientRect(graphics->hwnd, &wnd_rect))
2025 return GenericError;
2026
2027 rect->X = wnd_rect.left;
2028 rect->Y = wnd_rect.top;
2029 rect->Width = wnd_rect.right - wnd_rect.left;
2030 rect->Height = wnd_rect.bottom - wnd_rect.top;
2031 }else if (graphics->image){
2032 stat = GdipGetImageBounds(graphics->image, rect, &unit);
2033 if (stat == Ok && unit != UnitPixel)
2034 FIXME("need to convert from unit %i\n", unit);
2035 }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
2036 HBITMAP hbmp;
2037 BITMAP bmp;
2038
2039 rect->X = 0;
2040 rect->Y = 0;
2041
2042 hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
2043 if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
2044 {
2045 rect->Width = bmp.bmWidth;
2046 rect->Height = bmp.bmHeight;
2047 }
2048 else
2049 {
2050 /* FIXME: ??? */
2051 rect->Width = 1;
2052 rect->Height = 1;
2053 }
2054 }else{
2055 rect->X = 0;
2056 rect->Y = 0;
2057 rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
2058 rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
2059 }
2060
2061 if (graphics->hdc)
2062 {
2063 POINT points[2];
2064
2065 points[0].x = rect->X;
2066 points[0].y = rect->Y;
2067 points[1].x = rect->X + rect->Width;
2068 points[1].y = rect->Y + rect->Height;
2069
2070 DPtoLP(graphics->hdc, points, sizeof(points)/sizeof(points[0]));
2071
2072 rect->X = min(points[0].x, points[1].x);
2073 rect->Y = min(points[0].y, points[1].y);
2074 rect->Width = abs(points[1].x - points[0].x);
2075 rect->Height = abs(points[1].y - points[0].y);
2076 }
2077
2078 return stat;
2079 }
2080
2081 /* on success, rgn will contain the region of the graphics object which
2082 * is visible after clipping has been applied */
2083 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
2084 {
2085 GpStatus stat;
2086 GpRectF rectf;
2087 GpRegion* tmp;
2088
2089 if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
2090 return stat;
2091
2092 if((stat = GdipCreateRegion(&tmp)) != Ok)
2093 return stat;
2094
2095 if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
2096 goto end;
2097
2098 if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
2099 goto end;
2100
2101 stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
2102
2103 end:
2104 GdipDeleteRegion(tmp);
2105 return stat;
2106 }
2107
2108 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
2109 {
2110 REAL height;
2111
2112 if (font->unit == UnitPixel)
2113 {
2114 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
2115 }
2116 else
2117 {
2118 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
2119 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
2120 else
2121 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
2122 }
2123
2124 lf->lfHeight = -(height + 0.5);
2125 lf->lfWidth = 0;
2126 lf->lfEscapement = 0;
2127 lf->lfOrientation = 0;
2128 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
2129 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
2130 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
2131 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
2132 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
2133 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
2134 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
2135 lf->lfQuality = DEFAULT_QUALITY;
2136 lf->lfPitchAndFamily = 0;
2137 strcpyW(lf->lfFaceName, font->family->FamilyName);
2138 }
2139
2140 static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
2141 GDIPCONST GpStringFormat *format, HFONT *hfont,
2142 GDIPCONST GpMatrix *matrix)
2143 {
2144 HDC hdc = CreateCompatibleDC(0);
2145 GpPointF pt[3];
2146 REAL angle, rel_width, rel_height, font_height;
2147 LOGFONTW lfw;
2148 HFONT unscaled_font;
2149 TEXTMETRICW textmet;
2150
2151 if (font->unit == UnitPixel || font->unit == UnitWorld)
2152 font_height = font->emSize;
2153 else
2154 {
2155 REAL unit_scale, res;
2156
2157 res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
2158 unit_scale = units_scale(font->unit, graphics->unit, res);
2159
2160 font_height = font->emSize * unit_scale;
2161 }
2162
2163 pt[0].X = 0.0;
2164 pt[0].Y = 0.0;
2165 pt[1].X = 1.0;
2166 pt[1].Y = 0.0;
2167 pt[2].X = 0.0;
2168 pt[2].Y = 1.0;
2169 if (matrix)
2170 {
2171 GpMatrix xform = *matrix;
2172 GdipTransformMatrixPoints(&xform, pt, 3);
2173 }
2174
2175 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
2176 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2177 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
2178 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
2179 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
2180 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
2181
2182 get_log_fontW(font, graphics, &lfw);
2183 lfw.lfHeight = -gdip_round(font_height * rel_height);
2184 unscaled_font = CreateFontIndirectW(&lfw);
2185
2186 SelectObject(hdc, unscaled_font);
2187 GetTextMetricsW(hdc, &textmet);
2188
2189 lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
2190 lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
2191
2192 *hfont = CreateFontIndirectW(&lfw);
2193
2194 DeleteDC(hdc);
2195 DeleteObject(unscaled_font);
2196 }
2197
2198 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
2199 {
2200 TRACE("(%p, %p)\n", hdc, graphics);
2201
2202 return GdipCreateFromHDC2(hdc, NULL, graphics);
2203 }
2204
2205 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
2206 {
2207 GpStatus retval;
2208 HBITMAP hbitmap;
2209 DIBSECTION dib;
2210
2211 TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
2212
2213 if(hDevice != NULL)
2214 FIXME("Don't know how to handle parameter hDevice\n");
2215
2216 if(hdc == NULL)
2217 return OutOfMemory;
2218
2219 if(graphics == NULL)
2220 return InvalidParameter;
2221
2222 *graphics = heap_alloc_zero(sizeof(GpGraphics));
2223 if(!*graphics) return OutOfMemory;
2224
2225 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2226
2227 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2228 heap_free(*graphics);
2229 return retval;
2230 }
2231
2232 hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
2233 if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
2234 dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
2235 {
2236 (*graphics)->alpha_hdc = 1;
2237 }
2238
2239 (*graphics)->hdc = hdc;
2240 (*graphics)->hwnd = WindowFromDC(hdc);
2241 (*graphics)->owndc = FALSE;
2242 (*graphics)->smoothing = SmoothingModeDefault;
2243 (*graphics)->compqual = CompositingQualityDefault;
2244 (*graphics)->interpolation = InterpolationModeBilinear;
2245 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2246 (*graphics)->compmode = CompositingModeSourceOver;
2247 (*graphics)->unit = UnitDisplay;
2248 (*graphics)->scale = 1.0;
2249 (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
2250 (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
2251 (*graphics)->busy = FALSE;
2252 (*graphics)->textcontrast = 4;
2253 list_init(&(*graphics)->containers);
2254 (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
2255
2256 TRACE("<-- %p\n", *graphics);
2257
2258 return Ok;
2259 }
2260
2261 GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
2262 {
2263 GpStatus retval;
2264
2265 *graphics = heap_alloc_zero(sizeof(GpGraphics));
2266 if(!*graphics) return OutOfMemory;
2267
2268 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2269
2270 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2271 heap_free(*graphics);
2272 return retval;
2273 }
2274
2275 (*graphics)->hdc = NULL;
2276 (*graphics)->hwnd = NULL;
2277 (*graphics)->owndc = FALSE;
2278 (*graphics)->image = image;
2279 /* We have to store the image type here because the image may be freed
2280 * before GdipDeleteGraphics is called, and metafiles need special treatment. */
2281 (*graphics)->image_type = image->type;
2282 (*graphics)->smoothing = SmoothingModeDefault;
2283 (*graphics)->compqual = CompositingQualityDefault;
2284 (*graphics)->interpolation = InterpolationModeBilinear;
2285 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2286 (*graphics)->compmode = CompositingModeSourceOver;
2287 (*graphics)->unit = UnitDisplay;
2288 (*graphics)->scale = 1.0;
2289 (*graphics)->xres = image->xres;
2290 (*graphics)->yres = image->yres;
2291 (*graphics)->busy = FALSE;
2292 (*graphics)->textcontrast = 4;
2293 list_init(&(*graphics)->containers);
2294 (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
2295
2296 TRACE("<-- %p\n", *graphics);
2297
2298 return Ok;
2299 }
2300
2301 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
2302 {
2303 GpStatus ret;
2304 HDC hdc;
2305
2306 TRACE("(%p, %p)\n", hwnd, graphics);
2307
2308 hdc = GetDC(hwnd);
2309
2310 if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
2311 {
2312 ReleaseDC(hwnd, hdc);
2313 return ret;
2314 }
2315
2316 (*graphics)->hwnd = hwnd;
2317 (*graphics)->owndc = TRUE;
2318
2319 return Ok;
2320 }
2321
2322 /* FIXME: no icm handling */
2323 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
2324 {
2325 TRACE("(%p, %p)\n", hwnd, graphics);
2326
2327 return GdipCreateFromHWND(hwnd, graphics);
2328 }
2329
2330 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
2331 UINT access, IStream **stream)
2332 {
2333 DWORD dwMode;
2334 HRESULT ret;
2335
2336 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
2337
2338 if(!stream || !filename)
2339 return InvalidParameter;
2340
2341 if(access & GENERIC_WRITE)
2342 dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
2343 else if(access & GENERIC_READ)
2344 dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
2345 else
2346 return InvalidParameter;
2347
2348 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
2349
2350 return hresult_to_status(ret);
2351 }
2352
2353 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
2354 {
2355 GraphicsContainerItem *cont, *next;
2356 GpStatus stat;
2357 TRACE("(%p)\n", graphics);
2358
2359 if(!graphics) return InvalidParameter;
2360 if(graphics->busy) return ObjectBusy;
2361
2362 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2363 {
2364 stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
2365 if (stat != Ok)
2366 return stat;
2367 }
2368
2369 if(graphics->owndc)
2370 ReleaseDC(graphics->hwnd, graphics->hdc);
2371
2372 LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
2373 list_remove(&cont->entry);
2374 delete_container(cont);
2375 }
2376
2377 GdipDeleteRegion(graphics->clip);
2378
2379 /* Native returns ObjectBusy on the second free, instead of crashing as we'd
2380 * do otherwise, but we can't have that in the test suite because it means
2381 * accessing freed memory. */
2382 graphics->busy = TRUE;
2383
2384 heap_free(graphics);
2385
2386 return Ok;
2387 }
2388
2389 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
2390 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2391 {
2392 GpStatus status;
2393 GpPath *path;
2394
2395 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2396 width, height, startAngle, sweepAngle);
2397
2398 if(!graphics || !pen || width <= 0 || height <= 0)
2399 return InvalidParameter;
2400
2401 if(graphics->busy)
2402 return ObjectBusy;
2403
2404 status = GdipCreatePath(FillModeAlternate, &path);
2405 if (status != Ok) return status;
2406
2407 status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
2408 if (status == Ok)
2409 status = GdipDrawPath(graphics, pen, path);
2410
2411 GdipDeletePath(path);
2412 return status;
2413 }
2414
2415 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
2416 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2417 {
2418 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2419 width, height, startAngle, sweepAngle);
2420
2421 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2422 }
2423
2424 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
2425 REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
2426 {
2427 GpPointF pt[4];
2428
2429 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
2430 x2, y2, x3, y3, x4, y4);
2431
2432 if(!graphics || !pen)
2433 return InvalidParameter;
2434
2435 if(graphics->busy)
2436 return ObjectBusy;
2437
2438 pt[0].X = x1;
2439 pt[0].Y = y1;
2440 pt[1].X = x2;
2441 pt[1].Y = y2;
2442 pt[2].X = x3;
2443 pt[2].Y = y3;
2444 pt[3].X = x4;
2445 pt[3].Y = y4;
2446 return GdipDrawBeziers(graphics, pen, pt, 4);
2447 }
2448
2449 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
2450 INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
2451 {
2452 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
2453 x2, y2, x3, y3, x4, y4);
2454
2455 return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
2456 }
2457
2458 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
2459 GDIPCONST GpPointF *points, INT count)
2460 {
2461 GpStatus status;
2462 GpPath *path;
2463
2464 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2465
2466 if(!graphics || !pen || !points || (count <= 0))
2467 return InvalidParameter;
2468
2469 if(graphics->busy)
2470 return ObjectBusy;
2471
2472 status = GdipCreatePath(FillModeAlternate, &path);
2473 if (status != Ok) return status;
2474
2475 status = GdipAddPathBeziers(path, points, count);
2476 if (status == Ok)
2477 status = GdipDrawPath(graphics, pen, path);
2478
2479 GdipDeletePath(path);
2480 return status;
2481 }
2482
2483 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
2484 GDIPCONST GpPoint *points, INT count)
2485 {
2486 GpPointF *pts;
2487 GpStatus ret;
2488 INT i;
2489
2490 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2491
2492 if(!graphics || !pen || !points || (count <= 0))
2493 return InvalidParameter;
2494
2495 if(graphics->busy)
2496 return ObjectBusy;
2497
2498 pts = heap_alloc_zero(sizeof(GpPointF) * count);
2499 if(!pts)
2500 return OutOfMemory;
2501
2502 for(i = 0; i < count; i++){
2503 pts[i].X = (REAL)points[i].X;
2504 pts[i].Y = (REAL)points[i].Y;
2505 }
2506
2507 ret = GdipDrawBeziers(graphics,pen,pts,count);
2508
2509 heap_free(pts);
2510
2511 return ret;
2512 }
2513
2514 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
2515 GDIPCONST GpPointF *points, INT count)
2516 {
2517 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2518
2519 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
2520 }
2521
2522 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
2523 GDIPCONST GpPoint *points, INT count)
2524 {
2525 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2526
2527 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
2528 }
2529
2530 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
2531 GDIPCONST GpPointF *points, INT count, REAL tension)
2532 {
2533 GpPath *path;
2534 GpStatus status;
2535
2536 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2537
2538 if(!graphics || !pen || !points || count <= 0)
2539 return InvalidParameter;
2540
2541 if(graphics->busy)
2542 return ObjectBusy;
2543
2544 status = GdipCreatePath(FillModeAlternate, &path);
2545 if (status != Ok) return status;
2546
2547 status = GdipAddPathClosedCurve2(path, points, count, tension);
2548 if (status == Ok)
2549 status = GdipDrawPath(graphics, pen, path);
2550
2551 GdipDeletePath(path);
2552
2553 return status;
2554 }
2555
2556 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
2557 GDIPCONST GpPoint *points, INT count, REAL tension)
2558 {
2559 GpPointF *ptf;
2560 GpStatus stat;
2561 INT i;
2562
2563 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2564
2565 if(!points || count <= 0)
2566 return InvalidParameter;
2567
2568 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
2569 if(!ptf)
2570 return OutOfMemory;
2571
2572 for(i = 0; i < count; i++){
2573 ptf[i].X = (REAL)points[i].X;
2574 ptf[i].Y = (REAL)points[i].Y;
2575 }
2576
2577 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
2578
2579 heap_free(ptf);
2580
2581 return stat;
2582 }
2583
2584 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
2585 GDIPCONST GpPointF *points, INT count)
2586 {
2587 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2588
2589 return GdipDrawCurve2(graphics,pen,points,count,1.0);
2590 }
2591
2592 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
2593 GDIPCONST GpPoint *points, INT count)
2594 {
2595 GpPointF *pointsF;
2596 GpStatus ret;
2597 INT i;
2598
2599 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2600
2601 if(!points)
2602 return InvalidParameter;
2603
2604 pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
2605 if(!pointsF)
2606 return OutOfMemory;
2607
2608 for(i = 0; i < count; i++){
2609 pointsF[i].X = (REAL)points[i].X;
2610 pointsF[i].Y = (REAL)points[i].Y;
2611 }
2612
2613 ret = GdipDrawCurve(graphics,pen,pointsF,count);
2614 heap_free(pointsF);
2615
2616 return ret;
2617 }
2618
2619 /* Approximates cardinal spline with Bezier curves. */
2620 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
2621 GDIPCONST GpPointF *points, INT count, REAL tension)
2622 {
2623 GpPath *path;
2624 GpStatus status;
2625
2626 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2627
2628 if(!graphics || !pen)
2629 return InvalidParameter;
2630
2631 if(graphics->busy)
2632 return ObjectBusy;
2633
2634 if(count < 2)
2635 return InvalidParameter;
2636
2637 status = GdipCreatePath(FillModeAlternate, &path);
2638 if (status != Ok) return status;
2639
2640 status = GdipAddPathCurve2(path, points, count, tension);
2641 if (status == Ok)
2642 status = GdipDrawPath(graphics, pen, path);
2643
2644 GdipDeletePath(path);
2645 return status;
2646 }
2647
2648 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
2649 GDIPCONST GpPoint *points, INT count, REAL tension)
2650 {
2651 GpPointF *pointsF;
2652 GpStatus ret;
2653 INT i;
2654
2655 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2656
2657 if(!points)
2658 return InvalidParameter;
2659
2660 pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
2661 if(!pointsF)
2662 return OutOfMemory;
2663
2664 for(i = 0; i < count; i++){
2665 pointsF[i].X = (REAL)points[i].X;
2666 pointsF[i].Y = (REAL)points[i].Y;
2667 }
2668
2669 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
2670 heap_free(pointsF);
2671
2672 return ret;
2673 }
2674
2675 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
2676 GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
2677 REAL tension)
2678 {
2679 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2680
2681 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2682 return InvalidParameter;
2683 }
2684
2685 return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
2686 }
2687
2688 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
2689 GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
2690 REAL tension)
2691 {
2692 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2693
2694 if(count < 0){
2695 return OutOfMemory;
2696 }
2697
2698 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2699 return InvalidParameter;
2700 }
2701
2702 return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
2703 }
2704
2705 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
2706 REAL y, REAL width, REAL height)
2707 {
2708 GpPath *path;
2709 GpStatus status;
2710
2711 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2712
2713 if(!graphics || !pen)
2714 return InvalidParameter;
2715
2716 if(graphics->busy)
2717 return ObjectBusy;
2718
2719 status = GdipCreatePath(FillModeAlternate, &path);
2720 if (status != Ok) return status;
2721
2722 status = GdipAddPathEllipse(path, x, y, width, height);
2723 if (status == Ok)
2724 status = GdipDrawPath(graphics, pen, path);
2725
2726 GdipDeletePath(path);
2727 return status;
2728 }
2729
2730 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
2731 INT y, INT width, INT height)
2732 {
2733 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2734
2735 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2736 }
2737
2738
2739 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
2740 {
2741 UINT width, height;
2742
2743 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
2744
2745 if(!graphics || !image)
2746 return InvalidParameter;
2747
2748 GdipGetImageWidth(image, &width);
2749 GdipGetImageHeight(image, &height);
2750
2751 return GdipDrawImagePointRect(graphics, image, x, y,
2752 0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
2753 }
2754
2755 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
2756 INT y)
2757 {
2758 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
2759
2760 return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
2761 }
2762
2763 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
2764 REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
2765 GpUnit srcUnit)
2766 {
2767 GpPointF points[3];
2768 REAL scale_x, scale_y, width, height;
2769
2770 TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2771
2772 if (!graphics || !image) return InvalidParameter;
2773
2774 scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
2775 scale_x *= graphics->xres / image->xres;
2776 scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
2777 scale_y *= graphics->yres / image->yres;
2778 width = srcwidth * scale_x;
2779 height = srcheight * scale_y;
2780
2781 points[0].X = points[2].X = x;
2782 points[0].Y = points[1].Y = y;
2783 points[1].X = x + width;
2784 points[2].Y = y + height;
2785
2786 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2787 srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
2788 }
2789
2790 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
2791 INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
2792 GpUnit srcUnit)
2793 {
2794 return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2795 }
2796
2797 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
2798 GDIPCONST GpPointF *dstpoints, INT count)
2799 {
2800 UINT width, height;
2801
2802 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2803
2804 if(!image)
2805 return InvalidParameter;
2806
2807 GdipGetImageWidth(image, &width);
2808 GdipGetImageHeight(image, &height);
2809
2810 return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
2811 width, height, UnitPixel, NULL, NULL, NULL);
2812 }
2813
2814 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
2815 GDIPCONST GpPoint *dstpoints, INT count)
2816 {
2817 GpPointF ptf[3];
2818
2819 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2820
2821 if (count != 3 || !dstpoints)
2822 return InvalidParameter;
2823
2824 ptf[0].X = (REAL)dstpoints[0].X;
2825 ptf[0].Y = (REAL)dstpoints[0].Y;
2826 ptf[1].X = (REAL)dstpoints[1].X;
2827 ptf[1].Y = (REAL)dstpoints[1].Y;
2828 ptf[2].X = (REAL)dstpoints[2].X;
2829 ptf[2].Y = (REAL)dstpoints[2].Y;
2830
2831 return GdipDrawImagePoints(graphics, image, ptf, count);
2832 }
2833
2834 static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
2835 unsigned int dataSize, const unsigned char *pStr, void *userdata)
2836 {
2837 GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
2838 return TRUE;
2839 }
2840
2841 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
2842 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2843 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2844 DrawImageAbort callback, VOID * callbackData)
2845 {
2846 GpPointF ptf[4];
2847 POINT pti[4];
2848 GpStatus stat;
2849
2850 TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
2851 count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2852 callbackData);
2853
2854 if (count > 3)
2855 return NotImplemented;
2856
2857 if(!graphics || !image || !points || count != 3)
2858 return InvalidParameter;
2859
2860 TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
2861 debugstr_pointf(&points[2]));
2862
2863 memcpy(ptf, points, 3 * sizeof(GpPointF));
2864
2865 /* Ensure source width/height is positive */
2866 if (srcwidth < 0)
2867 {
2868 GpPointF tmp = ptf[1];
2869 srcx = srcx + srcwidth;
2870 srcwidth = -srcwidth;
2871 ptf[2].X = ptf[2].X + ptf[1].X - ptf[0].X;
2872 ptf[2].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2873 ptf[1] = ptf[0];
2874 ptf[0] = tmp;
2875 }
2876
2877 if (srcheight < 0)
2878 {
2879 GpPointF tmp = ptf[2];
2880 srcy = srcy + srcheight;
2881 srcheight = -srcheight;
2882 ptf[1].X = ptf[1].X + ptf[2].X - ptf[0].X;
2883 ptf[1].Y = ptf[1].Y + ptf[2].Y - ptf[0].Y;
2884 ptf[2] = ptf[0];
2885 ptf[0] = tmp;
2886 }
2887
2888 ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
2889 ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2890 if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
2891 return Ok;
2892 transform_and_round_points(graphics, pti, ptf, 4);
2893
2894 TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
2895 wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
2896
2897 srcx = units_to_pixels(srcx, srcUnit, image->xres);
2898 srcy = units_to_pixels(srcy, srcUnit, image->yres);
2899 srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
2900 srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
2901 TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
2902
2903 if (image->type == ImageTypeBitmap)
2904 {
2905 GpBitmap* bitmap = (GpBitmap*)image;
2906 BOOL do_resampling = FALSE;
2907 BOOL use_software = FALSE;
2908
2909 TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
2910 graphics->xres, graphics->yres,
2911 graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
2912 graphics->scale, image->xres, image->yres, bitmap->format,
2913 imageAttributes ? imageAttributes->outside_color : 0);
2914
2915 if (ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
2916 ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
2917 srcx < 0 || srcy < 0 ||
2918 srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
2919 do_resampling = TRUE;
2920
2921 if (imageAttributes || graphics->alpha_hdc || do_resampling ||
2922 (graphics->image && graphics->image->type == ImageTypeBitmap))
2923 use_software = TRUE;
2924
2925 if (use_software)
2926 {
2927 RECT dst_area;
2928 GpRectF graphics_bounds;
2929 GpRect src_area;
2930 int i, x, y, src_stride, dst_stride;
2931 GpMatrix dst_to_src;
2932 REAL m11, m12, m21, m22, mdx, mdy;
2933 LPBYTE src_data, dst_data, dst_dyn_data=NULL;
2934 BitmapData lockeddata;
2935 InterpolationMode interpolation = graphics->interpolation;
2936 PixelOffsetMode offset_mode = graphics->pixeloffset;
2937 GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
2938 REAL x_dx, x_dy, y_dx, y_dy;
2939 static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
2940
2941 if (!imageAttributes)
2942 imageAttributes = &defaultImageAttributes;
2943
2944 dst_area.left = dst_area.right = pti[0].x;
2945 dst_area.top = dst_area.bottom = pti[0].y;
2946 for (i=1; i<4; i++)
2947 {
2948 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2949 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2950 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2951 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2952 }
2953
2954 stat = get_graphics_bounds(graphics, &graphics_bounds);
2955 if (stat != Ok) return stat;
2956
2957 if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
2958 if (graphics_bounds.Y > dst_area.top) dst_area.top = floorf(graphics_bounds.Y);
2959 if (graphics_bounds.X + graphics_bounds.Width < dst_area.right) dst_area.right = ceilf(graphics_bounds.X + graphics_bounds.Width);
2960 if (graphics_bounds.Y + graphics_bounds.Height < dst_area.bottom) dst_area.bottom = ceilf(graphics_bounds.Y + graphics_bounds.Height);
2961
2962 TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
2963
2964 if (IsRectEmpty(&dst_area)) return Ok;
2965
2966 m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2967 m21 = (ptf[2].X - ptf[0].X) / srcheight;
2968 mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2969 m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2970 m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2971 mdy = ptf[0].Y - m12 * srcx - m22 * srcy;</