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