[GDIPLUS] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / 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 <stdarg.h>
20 #include <math.h>
21 #include <limits.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27 #include "wine/unicode.h"
28
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
34
35 #include "winreg.h"
36 #include "shlwapi.h"
37
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
44
45 /* Mike "tamlin" Nordell 2012-09-14 for ReactOS:
46 * NOTE: Wine uses per-GpGraphics id's ('contid' starting from zero in
47 * every GpGraphics). Windows seems to use process-global id's, or at
48 * least more unique id's.
49 * This have the following implications. It:
50 * 1. fails the current gdiplus test case.
51 * 2. is not what Windows does.
52 *
53 * We therefore "obfuscate" the 'contid' a little to more match Windows'
54 * behaviour. The observable behviour should still remain the same,
55 * except for handing out more "unique" id's.
56 */
57 #define GDIP_CONTID_STEP 64
58 static volatile LONG g_priv_contid = GDIP_CONTID_STEP;
59 #define GDIP_GET_NEW_CONTID_FOR(pGpGraphics) \
60 (UINT)(InterlockedExchangeAdd(&g_priv_contid,GDIP_CONTID_STEP))
61
62
63 /* ReactOS FIXME: Inspect */
64 #define fmax max
65
66 /* looks-right constants */
67 #define ANCHOR_WIDTH (2.0)
68 #define MAX_ITERS (50)
69
70 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
71 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
72 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
73 INT flags, GDIPCONST GpMatrix *matrix);
74
75 /* Converts from gdiplus path point type to gdi path point type. */
76 static BYTE convert_path_point_type(BYTE type)
77 {
78 BYTE ret;
79
80 switch(type & PathPointTypePathTypeMask){
81 case PathPointTypeBezier:
82 ret = PT_BEZIERTO;
83 break;
84 case PathPointTypeLine:
85 ret = PT_LINETO;
86 break;
87 case PathPointTypeStart:
88 ret = PT_MOVETO;
89 break;
90 default:
91 ERR("Bad point type\n");
92 return 0;
93 }
94
95 if(type & PathPointTypeCloseSubpath)
96 ret |= PT_CLOSEFIGURE;
97
98 return ret;
99 }
100
101 static COLORREF get_gdi_brush_color(const GpBrush *brush)
102 {
103 ARGB argb;
104
105 switch (brush->bt)
106 {
107 case BrushTypeSolidColor:
108 {
109 const GpSolidFill *sf = (const GpSolidFill *)brush;
110 argb = sf->color;
111 break;
112 }
113 case BrushTypeHatchFill:
114 {
115 const GpHatch *hatch = (const GpHatch *)brush;
116 argb = hatch->forecol;
117 break;
118 }
119 case BrushTypeLinearGradient:
120 {
121 const GpLineGradient *line = (const GpLineGradient *)brush;
122 argb = line->startcolor;
123 break;
124 }
125 case BrushTypePathGradient:
126 {
127 const GpPathGradient *grad = (const GpPathGradient *)brush;
128 argb = grad->centercolor;
129 break;
130 }
131 default:
132 FIXME("unhandled brush type %d\n", brush->bt);
133 argb = 0;
134 break;
135 }
136 return ARGB2COLORREF(argb);
137 }
138
139 static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
140 {
141 HBITMAP hbmp;
142 BITMAPINFOHEADER bmih;
143 DWORD *bits;
144 int x, y;
145
146 bmih.biSize = sizeof(bmih);
147 bmih.biWidth = 8;
148 bmih.biHeight = 8;
149 bmih.biPlanes = 1;
150 bmih.biBitCount = 32;
151 bmih.biCompression = BI_RGB;
152 bmih.biSizeImage = 0;
153
154 hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
155 if (hbmp)
156 {
157 const char *hatch_data;
158
159 if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
160 {
161 for (y = 0; y < 8; y++)
162 {
163 for (x = 0; x < 8; x++)
164 {
165 if (hatch_data[y] & (0x80 >> x))
166 bits[y * 8 + x] = hatch->forecol;
167 else
168 bits[y * 8 + x] = hatch->backcol;
169 }
170 }
171 }
172 else
173 {
174 FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
175
176 for (y = 0; y < 64; y++)
177 bits[y] = hatch->forecol;
178 }
179 }
180
181 return hbmp;
182 }
183
184 static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
185 {
186 switch (brush->bt)
187 {
188 case BrushTypeSolidColor:
189 {
190 const GpSolidFill *sf = (const GpSolidFill *)brush;
191 lb->lbStyle = BS_SOLID;
192 lb->lbColor = ARGB2COLORREF(sf->color);
193 lb->lbHatch = 0;
194 return Ok;
195 }
196
197 case BrushTypeHatchFill:
198 {
199 const GpHatch *hatch = (const GpHatch *)brush;
200 HBITMAP hbmp;
201
202 hbmp = create_hatch_bitmap(hatch);
203 if (!hbmp) return OutOfMemory;
204
205 lb->lbStyle = BS_PATTERN;
206 lb->lbColor = 0;
207 lb->lbHatch = (ULONG_PTR)hbmp;
208 return Ok;
209 }
210
211 default:
212 FIXME("unhandled brush type %d\n", brush->bt);
213 lb->lbStyle = BS_SOLID;
214 lb->lbColor = get_gdi_brush_color(brush);
215 lb->lbHatch = 0;
216 return Ok;
217 }
218 }
219
220 static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
221 {
222 switch (lb->lbStyle)
223 {
224 case BS_PATTERN:
225 DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
226 break;
227 }
228 return Ok;
229 }
230
231 static HBRUSH create_gdi_brush(const GpBrush *brush)
232 {
233 LOGBRUSH lb;
234 HBRUSH gdibrush;
235
236 if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
237
238 gdibrush = CreateBrushIndirect(&lb);
239 free_gdi_logbrush(&lb);
240
241 return gdibrush;
242 }
243
244 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
245 {
246 LOGBRUSH lb;
247 HPEN gdipen;
248 REAL width;
249 INT save_state, i, numdashes;
250 GpPointF pt[2];
251 DWORD dash_array[MAX_DASHLEN];
252
253 save_state = SaveDC(graphics->hdc);
254
255 EndPath(graphics->hdc);
256
257 if(pen->unit == UnitPixel){
258 width = pen->width;
259 }
260 else{
261 /* Get an estimate for the amount the pen width is affected by the world
262 * transform. (This is similar to what some of the wine drivers do.) */
263 pt[0].X = 0.0;
264 pt[0].Y = 0.0;
265 pt[1].X = 1.0;
266 pt[1].Y = 1.0;
267 GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
268 width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
269 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
270
271 width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
272 width *= graphics->scale;
273
274 pt[0].X = 0.0;
275 pt[0].Y = 0.0;
276 pt[1].X = 1.0;
277 pt[1].Y = 1.0;
278 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, pt, 2);
279 width *= sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
280 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
281 }
282
283 if(pen->dash == DashStyleCustom){
284 numdashes = min(pen->numdashes, MAX_DASHLEN);
285
286 TRACE("dashes are: ");
287 for(i = 0; i < numdashes; i++){
288 dash_array[i] = gdip_round(width * pen->dashes[i]);
289 TRACE("%d, ", dash_array[i]);
290 }
291 TRACE("\n and the pen style is %x\n", pen->style);
292
293 create_gdi_logbrush(pen->brush, &lb);
294 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
295 numdashes, dash_array);
296 free_gdi_logbrush(&lb);
297 }
298 else
299 {
300 create_gdi_logbrush(pen->brush, &lb);
301 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
302 free_gdi_logbrush(&lb);
303 }
304
305 SelectObject(graphics->hdc, gdipen);
306
307 return save_state;
308 }
309
310 static void restore_dc(GpGraphics *graphics, INT state)
311 {
312 DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
313 RestoreDC(graphics->hdc, state);
314 }
315
316 static void round_points(POINT *pti, GpPointF *ptf, INT count)
317 {
318 int i;
319
320 for(i = 0; i < count; i++){
321 pti[i].x = gdip_round(ptf[i].X);
322 pti[i].y = gdip_round(ptf[i].Y);
323 }
324 }
325
326 static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
327 HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
328 {
329 if (GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
330 GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
331 {
332 TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
333
334 StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
335 hdc, src_x, src_y, src_width, src_height, SRCCOPY);
336 }
337 else
338 {
339 BLENDFUNCTION bf;
340
341 bf.BlendOp = AC_SRC_OVER;
342 bf.BlendFlags = 0;
343 bf.SourceConstantAlpha = 255;
344 bf.AlphaFormat = AC_SRC_ALPHA;
345
346 GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
347 hdc, src_x, src_y, src_width, src_height, bf);
348 }
349 }
350
351 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
352 {
353 GpRegion *rgn;
354 GpMatrix transform;
355 GpStatus stat;
356
357 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, &transform);
358
359 if (stat == Ok)
360 stat = GdipCloneRegion(graphics->clip, &rgn);
361
362 if (stat == Ok)
363 {
364 stat = GdipTransformRegion(rgn, &transform);
365
366 if (stat == Ok)
367 stat = GdipGetRegionHRgn(rgn, NULL, hrgn);
368
369 GdipDeleteRegion(rgn);
370 }
371
372 if (stat == Ok && graphics->gdi_clip)
373 {
374 if (*hrgn)
375 CombineRgn(*hrgn, *hrgn, graphics->gdi_clip, RGN_AND);
376 else
377 {
378 *hrgn = CreateRectRgn(0,0,0,0);
379 CombineRgn(*hrgn, graphics->gdi_clip, graphics->gdi_clip, RGN_COPY);
380 }
381 }
382
383 return stat;
384 }
385
386 /* Draw ARGB data to the given graphics object */
387 static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
388 const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt)
389 {
390 GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
391 INT x, y;
392
393 for (y=0; y<src_height; y++)
394 {
395 for (x=0; x<src_width; x++)
396 {
397 ARGB dst_color, src_color;
398 src_color = ((ARGB*)(src + src_stride * y))[x];
399
400 if (!(src_color & 0xff000000))
401 continue;
402
403 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
404 if (fmt & PixelFormatPAlpha)
405 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over_fgpremult(dst_color, src_color));
406 else
407 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
408 }
409 }
410
411 return Ok;
412 }
413
414 static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
415 const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
416 {
417 HDC hdc;
418 HBITMAP hbitmap;
419 BITMAPINFOHEADER bih;
420 BYTE *temp_bits;
421
422 hdc = CreateCompatibleDC(0);
423
424 bih.biSize = sizeof(BITMAPINFOHEADER);
425 bih.biWidth = src_width;
426 bih.biHeight = -src_height;
427 bih.biPlanes = 1;
428 bih.biBitCount = 32;
429 bih.biCompression = BI_RGB;
430 bih.biSizeImage = 0;
431 bih.biXPelsPerMeter = 0;
432 bih.biYPelsPerMeter = 0;
433 bih.biClrUsed = 0;
434 bih.biClrImportant = 0;
435
436 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
437 (void**)&temp_bits, NULL, 0);
438
439 if ((GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
440 GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE) ||
441 fmt & PixelFormatPAlpha)
442 memcpy(temp_bits, src, src_width * src_height * 4);
443 else
444 convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
445 4 * src_width, src, src_stride);
446
447 SelectObject(hdc, hbitmap);
448 gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
449 hdc, 0, 0, src_width, src_height);
450 DeleteDC(hdc);
451 DeleteObject(hbitmap);
452
453 return Ok;
454 }
455
456 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
457 const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
458 {
459 GpStatus stat=Ok;
460
461 if (graphics->image && graphics->image->type == ImageTypeBitmap)
462 {
463 DWORD i;
464 int size;
465 RGNDATA *rgndata;
466 RECT *rects;
467 HRGN hrgn, visible_rgn;
468
469 hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
470 if (!hrgn)
471 return OutOfMemory;
472
473 stat = get_clip_hrgn(graphics, &visible_rgn);
474 if (stat != Ok)
475 {
476 DeleteObject(hrgn);
477 return stat;
478 }
479
480 if (visible_rgn)
481 {
482 CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
483 DeleteObject(visible_rgn);
484 }
485
486 if (hregion)
487 CombineRgn(hrgn, hrgn, hregion, RGN_AND);
488
489 size = GetRegionData(hrgn, 0, NULL);
490
491 rgndata = heap_alloc_zero(size);
492 if (!rgndata)
493 {
494 DeleteObject(hrgn);
495 return OutOfMemory;
496 }
497
498 GetRegionData(hrgn, size, rgndata);
499
500 rects = (RECT*)rgndata->Buffer;
501
502 for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
503 {
504 stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
505 &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
506 rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
507 src_stride, fmt);
508 }
509
510 heap_free(rgndata);
511
512 DeleteObject(hrgn);
513
514 return stat;
515 }
516 else if (graphics->image && graphics->image->type == ImageTypeMetafile)
517 {
518 ERR("This should not be used for metafiles; fix caller\n");
519 return NotImplemented;
520 }
521 else
522 {
523 HRGN hrgn;
524 int save;
525
526 stat = get_clip_hrgn(graphics, &hrgn);
527
528 if (stat != Ok)
529 return stat;
530
531 save = SaveDC(graphics->hdc);
532
533 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
534
535 if (hregion)
536 ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
537
538 stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
539 src_height, src_stride, fmt);
540
541 RestoreDC(graphics->hdc, save);
542
543 DeleteObject(hrgn);
544
545 return stat;
546 }
547 }
548
549 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
550 const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
551 {
552 return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
553 }
554
555 /* NOTE: start and end pixels must be in pre-multiplied ARGB format */
556 static inline ARGB blend_colors_premult(ARGB start, ARGB end, REAL position)
557 {
558 UINT pos = position * 255.0f + 0.5f;
559 return
560 (((((start >> 24) ) << 8) + (((end >> 24) ) - ((start >> 24) )) * pos) >> 8) << 24 |
561 (((((start >> 16) & 0xff) << 8) + (((end >> 16) & 0xff) - ((start >> 16) & 0xff)) * pos) >> 8) << 16 |
562 (((((start >> 8) & 0xff) << 8) + (((end >> 8) & 0xff) - ((start >> 8) & 0xff)) * pos) >> 8) << 8 |
563 (((((start ) & 0xff) << 8) + (((end ) & 0xff) - ((start ) & 0xff)) * pos) >> 8);
564 }
565
566 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
567 {
568 INT start_a, end_a, final_a;
569 INT pos;
570
571 pos = (INT)(position * 255.0f + 0.5f);
572
573 start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
574 end_a = ((end >> 24) & 0xff) * pos;
575
576 final_a = start_a + end_a;
577
578 if (final_a < 0xff) return 0;
579
580 return (final_a / 0xff) << 24 |
581 ((((start >> 16) & 0xff) * start_a + (((end >> 16) & 0xff) * end_a)) / final_a) << 16 |
582 ((((start >> 8) & 0xff) * start_a + (((end >> 8) & 0xff) * end_a)) / final_a) << 8 |
583 (((start & 0xff) * start_a + ((end & 0xff) * end_a)) / final_a);
584 }
585
586 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
587 {
588 REAL blendfac;
589
590 /* clamp to between 0.0 and 1.0, using the wrap mode */
591 position = (position - brush->rect.X) / brush->rect.Width;
592 if (brush->wrap == WrapModeTile)
593 {
594 position = fmodf(position, 1.0f);
595 if (position < 0.0f) position += 1.0f;
596 }
597 else /* WrapModeFlip* */
598 {
599 position = fmodf(position, 2.0f);
600 if (position < 0.0f) position += 2.0f;
601 if (position > 1.0f) position = 2.0f - position;
602 }
603
604 if (brush->blendcount == 1)
605 blendfac = position;
606 else
607 {
608 int i=1;
609 REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
610 REAL range;
611
612 /* locate the blend positions surrounding this position */
613 while (position > brush->blendpos[i])
614 i++;
615
616 /* interpolate between the blend positions */
617 left_blendpos = brush->blendpos[i-1];
618 left_blendfac = brush->blendfac[i-1];
619 right_blendpos = brush->blendpos[i];
620 right_blendfac = brush->blendfac[i];
621 range = right_blendpos - left_blendpos;
622 blendfac = (left_blendfac * (right_blendpos - position) +
623 right_blendfac * (position - left_blendpos)) / range;
624 }
625
626 if (brush->pblendcount == 0)
627 return blend_colors(brush->startcolor, brush->endcolor, blendfac);
628 else
629 {
630 int i=1;
631 ARGB left_blendcolor, right_blendcolor;
632 REAL left_blendpos, right_blendpos;
633
634 /* locate the blend colors surrounding this position */
635 while (blendfac > brush->pblendpos[i])
636 i++;
637
638 /* interpolate between the blend colors */
639 left_blendpos = brush->pblendpos[i-1];
640 left_blendcolor = brush->pblendcolor[i-1];
641 right_blendpos = brush->pblendpos[i];
642 right_blendcolor = brush->pblendcolor[i];
643 blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
644 return blend_colors(left_blendcolor, right_blendcolor, blendfac);
645 }
646 }
647
648 static BOOL round_color_matrix(const ColorMatrix *matrix, int values[5][5])
649 {
650 /* Convert floating point color matrix to int[5][5], return TRUE if it's an identity */
651 BOOL identity = TRUE;
652 int i, j;
653
654 for (i=0; i<4; i++)
655 for (j=0; j<5; j++)
656 {
657 if (matrix->m[j][i] != (i == j ? 1.0 : 0.0))
658 identity = FALSE;
659 values[j][i] = gdip_round(matrix->m[j][i] * 256.0);
660 }
661
662 return identity;
663 }
664
665 static ARGB transform_color(ARGB color, int matrix[5][5])
666 {
667 int val[5], res[4];
668 int i, j;
669 unsigned char a, r, g, b;
670
671 val[0] = ((color >> 16) & 0xff); /* red */
672 val[1] = ((color >> 8) & 0xff); /* green */
673 val[2] = (color & 0xff); /* blue */
674 val[3] = ((color >> 24) & 0xff); /* alpha */
675 val[4] = 255; /* translation */
676
677 for (i=0; i<4; i++)
678 {
679 res[i] = 0;
680
681 for (j=0; j<5; j++)
682 res[i] += matrix[j][i] * val[j];
683 }
684
685 a = min(max(res[3] / 256, 0), 255);
686 r = min(max(res[0] / 256, 0), 255);
687 g = min(max(res[1] / 256, 0), 255);
688 b = min(max(res[2] / 256, 0), 255);
689
690 return (a << 24) | (r << 16) | (g << 8) | b;
691 }
692
693 static BOOL color_is_gray(ARGB color)
694 {
695 unsigned char r, g, b;
696
697 r = (color >> 16) & 0xff;
698 g = (color >> 8) & 0xff;
699 b = color & 0xff;
700
701 return (r == g) && (g == b);
702 }
703
704 /* returns preferred pixel format for the applied attributes */
705 PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
706 UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt)
707 {
708 UINT x, y;
709 INT i;
710
711 if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
712 attributes->noop[ColorAdjustTypeDefault] == IMAGEATTR_NOOP_SET) ||
713 (attributes->noop[type] == IMAGEATTR_NOOP_SET))
714 return fmt;
715
716 if (attributes->colorkeys[type].enabled ||
717 attributes->colorkeys[ColorAdjustTypeDefault].enabled)
718 {
719 const struct color_key *key;
720 BYTE min_blue, min_green, min_red;
721 BYTE max_blue, max_green, max_red;
722
723 if (!data || fmt != PixelFormat32bppARGB)
724 return PixelFormat32bppARGB;
725
726 if (attributes->colorkeys[type].enabled)
727 key = &attributes->colorkeys[type];
728 else
729 key = &attributes->colorkeys[ColorAdjustTypeDefault];
730
731 min_blue = key->low&0xff;
732 min_green = (key->low>>8)&0xff;
733 min_red = (key->low>>16)&0xff;
734
735 max_blue = key->high&0xff;
736 max_green = (key->high>>8)&0xff;
737 max_red = (key->high>>16)&0xff;
738
739 for (x=0; x<width; x++)
740 for (y=0; y<height; y++)
741 {
742 ARGB *src_color;
743 BYTE blue, green, red;
744 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
745 blue = *src_color&0xff;
746 green = (*src_color>>8)&0xff;
747 red = (*src_color>>16)&0xff;
748 if (blue >= min_blue && green >= min_green && red >= min_red &&
749 blue <= max_blue && green <= max_green && red <= max_red)
750 *src_color = 0x00000000;
751 }
752 }
753
754 if (attributes->colorremaptables[type].enabled ||
755 attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
756 {
757 const struct color_remap_table *table;
758
759 if (!data || fmt != PixelFormat32bppARGB)
760 return PixelFormat32bppARGB;
761
762 if (attributes->colorremaptables[type].enabled)
763 table = &attributes->colorremaptables[type];
764 else
765 table = &attributes->colorremaptables[ColorAdjustTypeDefault];
766
767 for (x=0; x<width; x++)
768 for (y=0; y<height; y++)
769 {
770 ARGB *src_color;
771 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
772 for (i=0; i<table->mapsize; i++)
773 {
774 if (*src_color == table->colormap[i].oldColor.Argb)
775 {
776 *src_color = table->colormap[i].newColor.Argb;
777 break;
778 }
779 }
780 }
781 }
782
783 if (attributes->colormatrices[type].enabled ||
784 attributes->colormatrices[ColorAdjustTypeDefault].enabled)
785 {
786 const struct color_matrix *colormatrices;
787 int color_matrix[5][5];
788 int gray_matrix[5][5];
789 BOOL identity;
790
791 if (!data || fmt != PixelFormat32bppARGB)
792 return PixelFormat32bppARGB;
793
794 if (attributes->colormatrices[type].enabled)
795 colormatrices = &attributes->colormatrices[type];
796 else
797 colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
798
799 identity = round_color_matrix(&colormatrices->colormatrix, color_matrix);
800
801 if (colormatrices->flags == ColorMatrixFlagsAltGray)
802 identity = (round_color_matrix(&colormatrices->graymatrix, gray_matrix) && identity);
803
804 if (!identity)
805 {
806 for (x=0; x<width; x++)
807 {
808 for (y=0; y<height; y++)
809 {
810 ARGB *src_color;
811 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
812
813 if (colormatrices->flags == ColorMatrixFlagsDefault ||
814 !color_is_gray(*src_color))
815 {
816 *src_color = transform_color(*src_color, color_matrix);
817 }
818 else if (colormatrices->flags == ColorMatrixFlagsAltGray)
819 {
820 *src_color = transform_color(*src_color, gray_matrix);
821 }
822 }
823 }
824 }
825 }
826
827 if (attributes->gamma_enabled[type] ||
828 attributes->gamma_enabled[ColorAdjustTypeDefault])
829 {
830 REAL gamma;
831
832 if (!data || fmt != PixelFormat32bppARGB)
833 return PixelFormat32bppARGB;
834
835 if (attributes->gamma_enabled[type])
836 gamma = attributes->gamma[type];
837 else
838 gamma = attributes->gamma[ColorAdjustTypeDefault];
839
840 for (x=0; x<width; x++)
841 for (y=0; y<height; y++)
842 {
843 ARGB *src_color;
844 BYTE blue, green, red;
845 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
846
847 blue = *src_color&0xff;
848 green = (*src_color>>8)&0xff;
849 red = (*src_color>>16)&0xff;
850
851 /* FIXME: We should probably use a table for this. */
852 blue = floorf(powf(blue / 255.0, gamma) * 255.0);
853 green = floorf(powf(green / 255.0, gamma) * 255.0);
854 red = floorf(powf(red / 255.0, gamma) * 255.0);
855
856 *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
857 }
858 }
859
860 return fmt;
861 }
862
863 /* Given a bitmap and its source rectangle, find the smallest rectangle in the
864 * bitmap that contains all the pixels we may need to draw it. */
865 static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
866 GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
867 GpRect *rect)
868 {
869 INT left, top, right, bottom;
870
871 switch (interpolation)
872 {
873 case InterpolationModeHighQualityBilinear:
874 case InterpolationModeHighQualityBicubic:
875 /* FIXME: Include a greater range for the prefilter? */
876 case InterpolationModeBicubic:
877 case InterpolationModeBilinear:
878 left = (INT)(floorf(srcx));
879 top = (INT)(floorf(srcy));
880 right = (INT)(ceilf(srcx+srcwidth));
881 bottom = (INT)(ceilf(srcy+srcheight));
882 break;
883 case InterpolationModeNearestNeighbor:
884 default:
885 left = gdip_round(srcx);
886 top = gdip_round(srcy);
887 right = gdip_round(srcx+srcwidth);
888 bottom = gdip_round(srcy+srcheight);
889 break;
890 }
891
892 if (wrap == WrapModeClamp)
893 {
894 if (left < 0)
895 left = 0;
896 if (top < 0)
897 top = 0;
898 if (right >= bitmap->width)
899 right = bitmap->width-1;
900 if (bottom >= bitmap->height)
901 bottom = bitmap->height-1;
902 if (bottom < top || right < left)
903 /* entirely outside image, just sample a pixel so we don't have to
904 * special-case this later */
905 left = top = right = bottom = 0;
906 }
907 else
908 {
909 /* In some cases we can make the rectangle smaller here, but the logic
910 * is hard to get right, and tiling suggests we're likely to use the
911 * entire source image. */
912 if (left < 0 || right >= bitmap->width)
913 {
914 left = 0;
915 right = bitmap->width-1;
916 }
917
918 if (top < 0 || bottom >= bitmap->height)
919 {
920 top = 0;
921 bottom = bitmap->height-1;
922 }
923 }
924
925 rect->X = left;
926 rect->Y = top;
927 rect->Width = right - left + 1;
928 rect->Height = bottom - top + 1;
929 }
930
931 static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
932 UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
933 {
934 if (attributes->wrap == WrapModeClamp)
935 {
936 if (x < 0 || y < 0 || x >= width || y >= height)
937 return attributes->outside_color;
938 }
939 else
940 {
941 /* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
942 if (x < 0)
943 x = width*2 + x % (width * 2);
944 if (y < 0)
945 y = height*2 + y % (height * 2);
946
947 if (attributes->wrap & WrapModeTileFlipX)
948 {
949 if ((x / width) % 2 == 0)
950 x = x % width;
951 else
952 x = width - 1 - x % width;
953 }
954 else
955 x = x % width;
956
957 if (attributes->wrap & WrapModeTileFlipY)
958 {
959 if ((y / height) % 2 == 0)
960 y = y % height;
961 else
962 y = height - 1 - y % height;
963 }
964 else
965 y = y % height;
966 }
967
968 if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
969 {
970 ERR("out of range pixel requested\n");
971 return 0xffcd0084;
972 }
973
974 return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
975 }
976
977 static inline int positive_ceilf(float f)
978 {
979 return f - (int)f > 0.0f ? f + 1.0f : f;
980 }
981
982 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
983 UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
984 InterpolationMode interpolation, PixelOffsetMode offset_mode)
985 {
986 static int fixme;
987
988 switch (interpolation)
989 {
990 default:
991 if (!fixme++)
992 FIXME("Unimplemented interpolation %i\n", interpolation);
993 /* fall-through */
994 case InterpolationModeBilinear:
995 {
996 REAL leftxf, topyf;
997 INT leftx, rightx, topy, bottomy;
998 ARGB topleft, topright, bottomleft, bottomright;
999 ARGB top, bottom;
1000 float x_offset;
1001
1002 leftx = (INT)point->X;
1003 leftxf = (REAL)leftx;
1004 rightx = positive_ceilf(point->X);
1005 topy = (INT)point->Y;
1006 topyf = (REAL)topy;
1007 bottomy = positive_ceilf(point->Y);
1008
1009 if (leftx == rightx && topy == bottomy)
1010 return sample_bitmap_pixel(src_rect, bits, width, height,
1011 leftx, topy, attributes);
1012
1013 topleft = sample_bitmap_pixel(src_rect, bits, width, height,
1014 leftx, topy, attributes);
1015 topright = sample_bitmap_pixel(src_rect, bits, width, height,
1016 rightx, topy, attributes);
1017 bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
1018 leftx, bottomy, attributes);
1019 bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
1020 rightx, bottomy, attributes);
1021
1022 x_offset = point->X - leftxf;
1023 top = blend_colors(topleft, topright, x_offset);
1024 bottom = blend_colors(bottomleft, bottomright, x_offset);
1025
1026 return blend_colors(top, bottom, point->Y - topyf);
1027 }
1028 case InterpolationModeNearestNeighbor:
1029 {
1030 FLOAT pixel_offset;
1031 switch (offset_mode)
1032 {
1033 default:
1034 case PixelOffsetModeNone:
1035 case PixelOffsetModeHighSpeed:
1036 pixel_offset = 0.5;
1037 break;
1038
1039 case PixelOffsetModeHalf:
1040 case PixelOffsetModeHighQuality:
1041 pixel_offset = 0.0;
1042 break;
1043 }
1044 return sample_bitmap_pixel(src_rect, bits, width, height,
1045 floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
1046 }
1047
1048 }
1049 }
1050
1051 static ARGB resample_bitmap_pixel_premult(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
1052 UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
1053 InterpolationMode interpolation, PixelOffsetMode offset_mode)
1054 {
1055 static int fixme;
1056
1057 switch (interpolation)
1058 {
1059 default:
1060 if (!fixme++)
1061 FIXME("Unimplemented interpolation %i\n", interpolation);
1062 /* fall-through */
1063 case InterpolationModeBilinear:
1064 {
1065 REAL leftxf, topyf;
1066 INT leftx, rightx, topy, bottomy;
1067 ARGB topleft, topright, bottomleft, bottomright;
1068 ARGB top, bottom;
1069 float x_offset;
1070
1071 leftx = (INT)point->X;
1072 leftxf = (REAL)leftx;
1073 rightx = positive_ceilf(point->X);
1074 topy = (INT)point->Y;
1075 topyf = (REAL)topy;
1076 bottomy = positive_ceilf(point->Y);
1077
1078 if (leftx == rightx && topy == bottomy)
1079 return sample_bitmap_pixel(src_rect, bits, width, height,
1080 leftx, topy, attributes);
1081
1082 topleft = sample_bitmap_pixel(src_rect, bits, width, height,
1083 leftx, topy, attributes);
1084 topright = sample_bitmap_pixel(src_rect, bits, width, height,
1085 rightx, topy, attributes);
1086 bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
1087 leftx, bottomy, attributes);
1088 bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
1089 rightx, bottomy, attributes);
1090
1091 x_offset = point->X - leftxf;
1092 top = blend_colors_premult(topleft, topright, x_offset);
1093 bottom = blend_colors_premult(bottomleft, bottomright, x_offset);
1094
1095 return blend_colors_premult(top, bottom, point->Y - topyf);
1096 }
1097 case InterpolationModeNearestNeighbor:
1098 {
1099 FLOAT pixel_offset;
1100 switch (offset_mode)
1101 {
1102 default:
1103 case PixelOffsetModeNone:
1104 case PixelOffsetModeHighSpeed:
1105 pixel_offset = 0.5;
1106 break;
1107
1108 case PixelOffsetModeHalf:
1109 case PixelOffsetModeHighQuality:
1110 pixel_offset = 0.0;
1111 break;
1112 }
1113 return sample_bitmap_pixel(src_rect, bits, width, height,
1114 floorf(point->X + pixel_offset), point->Y + pixel_offset, attributes);
1115 }
1116
1117 }
1118 }
1119
1120 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
1121 {
1122 return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
1123 }
1124
1125 /* is_fill is TRUE if filling regions, FALSE for drawing primitives */
1126 static BOOL brush_can_fill_path(GpBrush *brush, BOOL is_fill)
1127 {
1128 switch (brush->bt)
1129 {
1130 case BrushTypeSolidColor:
1131 {
1132 if (is_fill)
1133 return TRUE;
1134 else
1135 {
1136 /* cannot draw semi-transparent colors */
1137 return (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000;
1138 }
1139 }
1140 case BrushTypeHatchFill:
1141 {
1142 GpHatch *hatch = (GpHatch*)brush;
1143 return ((hatch->forecol & 0xff000000) == 0xff000000) &&
1144 ((hatch->backcol & 0xff000000) == 0xff000000);
1145 }
1146 case BrushTypeLinearGradient:
1147 case BrushTypeTextureFill:
1148 /* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
1149 default:
1150 return FALSE;
1151 }
1152 }
1153
1154 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
1155 {
1156 switch (brush->bt)
1157 {
1158 case BrushTypeSolidColor:
1159 {
1160 GpSolidFill *fill = (GpSolidFill*)brush;
1161 HBITMAP bmp = ARGB2BMP(fill->color);
1162
1163 if (bmp)
1164 {
1165 RECT rc;
1166 /* partially transparent fill */
1167
1168 SelectClipPath(graphics->hdc, RGN_AND);
1169 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
1170 {
1171 HDC hdc = CreateCompatibleDC(NULL);
1172
1173 if (!hdc) break;
1174
1175 SelectObject(hdc, bmp);
1176 gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
1177 hdc, 0, 0, 1, 1);
1178 DeleteDC(hdc);
1179 }
1180
1181 DeleteObject(bmp);
1182 break;
1183 }
1184 /* else fall through */
1185 }
1186 default:
1187 {
1188 HBRUSH gdibrush, old_brush;
1189
1190 gdibrush = create_gdi_brush(brush);
1191 if (!gdibrush) return;
1192
1193 old_brush = SelectObject(graphics->hdc, gdibrush);
1194 FillPath(graphics->hdc);
1195 SelectObject(graphics->hdc, old_brush);
1196 DeleteObject(gdibrush);
1197 break;
1198 }
1199 }
1200 }
1201
1202 static BOOL brush_can_fill_pixels(GpBrush *brush)
1203 {
1204 switch (brush->bt)
1205 {
1206 case BrushTypeSolidColor:
1207 case BrushTypeHatchFill:
1208 case BrushTypeLinearGradient:
1209 case BrushTypeTextureFill:
1210 case BrushTypePathGradient:
1211 return TRUE;
1212 default:
1213 return FALSE;
1214 }
1215 }
1216
1217 static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
1218 DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
1219 {
1220 switch (brush->bt)
1221 {
1222 case BrushTypeSolidColor:
1223 {
1224 int x, y;
1225 GpSolidFill *fill = (GpSolidFill*)brush;
1226 for (x=0; x<fill_area->Width; x++)
1227 for (y=0; y<fill_area->Height; y++)
1228 argb_pixels[x + y*cdwStride] = fill->color;
1229 return Ok;
1230 }
1231 case BrushTypeHatchFill:
1232 {
1233 int x, y;
1234 GpHatch *fill = (GpHatch*)brush;
1235 const char *hatch_data;
1236
1237 if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
1238 return NotImplemented;
1239
1240 for (x=0; x<fill_area->Width; x++)
1241 for (y=0; y<fill_area->Height; y++)
1242 {
1243 int hx, hy;
1244
1245 /* FIXME: Account for the rendering origin */
1246 hx = (x + fill_area->X) % 8;
1247 hy = (y + fill_area->Y) % 8;
1248
1249 if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
1250 argb_pixels[x + y*cdwStride] = fill->forecol;
1251 else
1252 argb_pixels[x + y*cdwStride] = fill->backcol;
1253 }
1254
1255 return Ok;
1256 }
1257 case BrushTypeLinearGradient:
1258 {
1259 GpLineGradient *fill = (GpLineGradient*)brush;
1260 GpPointF draw_points[3];
1261 GpStatus stat;
1262 int x, y;
1263
1264 draw_points[0].X = fill_area->X;
1265 draw_points[0].Y = fill_area->Y;
1266 draw_points[1].X = fill_area->X+1;
1267 draw_points[1].Y = fill_area->Y;
1268 draw_points[2].X = fill_area->X;
1269 draw_points[2].Y = fill_area->Y+1;
1270
1271 /* Transform the points to a co-ordinate space where X is the point's
1272 * position in the gradient, 0.0 being the start point and 1.0 the
1273 * end point. */
1274 stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
1275 WineCoordinateSpaceGdiDevice, draw_points, 3);
1276
1277 if (stat == Ok)
1278 {
1279 GpMatrix world_to_gradient = fill->transform;
1280
1281 stat = GdipInvertMatrix(&world_to_gradient);
1282 if (stat == Ok)
1283 stat = GdipTransformMatrixPoints(&world_to_gradient, draw_points, 3);
1284 }
1285
1286 if (stat == Ok)
1287 {
1288 REAL x_delta = draw_points[1].X - draw_points[0].X;
1289 REAL y_delta = draw_points[2].X - draw_points[0].X;
1290
1291 for (y=0; y<fill_area->Height; y++)
1292 {
1293 for (x=0; x<fill_area->Width; x++)
1294 {
1295 REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
1296
1297 argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
1298 }
1299 }
1300 }
1301
1302 return stat;
1303 }
1304 case BrushTypeTextureFill:
1305 {
1306 GpTexture *fill = (GpTexture*)brush;
1307 GpPointF draw_points[3];
1308 GpStatus stat;
1309 int x, y;
1310 GpBitmap *bitmap;
1311 int src_stride;
1312 GpRect src_area;
1313
1314 if (fill->image->type != ImageTypeBitmap)
1315 {
1316 FIXME("metafile texture brushes not implemented\n");
1317 return NotImplemented;
1318 }
1319
1320 bitmap = (GpBitmap*)fill->image;
1321 src_stride = sizeof(ARGB) * bitmap->width;
1322
1323 src_area.X = src_area.Y = 0;
1324 src_area.Width = bitmap->width;
1325 src_area.Height = bitmap->height;
1326
1327 draw_points[0].X = fill_area->X;
1328 draw_points[0].Y = fill_area->Y;
1329 draw_points[1].X = fill_area->X+1;
1330 draw_points[1].Y = fill_area->Y;
1331 draw_points[2].X = fill_area->X;
1332 draw_points[2].Y = fill_area->Y+1;
1333
1334 /* Transform the points to the co-ordinate space of the bitmap. */
1335 stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
1336 WineCoordinateSpaceGdiDevice, draw_points, 3);
1337
1338 if (stat == Ok)
1339 {
1340 GpMatrix world_to_texture = fill->transform;
1341
1342 stat = GdipInvertMatrix(&world_to_texture);
1343 if (stat == Ok)
1344 stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
1345 }
1346
1347 if (stat == Ok && !fill->bitmap_bits)
1348 {
1349 BitmapData lockeddata;
1350
1351 fill->bitmap_bits = heap_alloc_zero(sizeof(ARGB) * bitmap->width * bitmap->height);
1352 if (!fill->bitmap_bits)
1353 stat = OutOfMemory;
1354
1355 if (stat == Ok)
1356 {
1357 lockeddata.Width = bitmap->width;
1358 lockeddata.Height = bitmap->height;
1359 lockeddata.Stride = src_stride;
1360 lockeddata.PixelFormat = PixelFormat32bppARGB;
1361 lockeddata.Scan0 = fill->bitmap_bits;
1362
1363 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
1364 PixelFormat32bppARGB, &lockeddata);
1365 }
1366
1367 if (stat == Ok)
1368 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
1369
1370 if (stat == Ok)
1371 apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
1372 bitmap->width, bitmap->height,
1373 src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
1374
1375 if (stat != Ok)
1376 {
1377 heap_free(fill->bitmap_bits);
1378 fill->bitmap_bits = NULL;
1379 }
1380 }
1381
1382 if (stat == Ok)
1383 {
1384 REAL x_dx = draw_points[1].X - draw_points[0].X;
1385 REAL x_dy = draw_points[1].Y - draw_points[0].Y;
1386 REAL y_dx = draw_points[2].X - draw_points[0].X;
1387 REAL y_dy = draw_points[2].Y - draw_points[0].Y;
1388
1389 for (y=0; y<fill_area->Height; y++)
1390 {
1391 for (x=0; x<fill_area->Width; x++)
1392 {
1393 GpPointF point;
1394 point.X = draw_points[0].X + x * x_dx + y * y_dx;
1395 point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
1396
1397 argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
1398 &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
1399 &point, fill->imageattributes, graphics->interpolation,
1400 graphics->pixeloffset);
1401 }
1402 }
1403 }
1404
1405 return stat;
1406 }
1407 case BrushTypePathGradient:
1408 {
1409 GpPathGradient *fill = (GpPathGradient*)brush;
1410 GpPath *flat_path;
1411 GpMatrix world_to_device;
1412 GpStatus stat;
1413 int i, figure_start=0;
1414 GpPointF start_point, end_point, center_point;
1415 BYTE type;
1416 REAL min_yf, max_yf, line1_xf, line2_xf;
1417 INT min_y, max_y, min_x, max_x;
1418 INT x, y;
1419 ARGB outer_color;
1420 static BOOL transform_fixme_once;
1421
1422 if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
1423 {
1424 static int once;
1425 if (!once++)
1426 FIXME("path gradient focus not implemented\n");
1427 }
1428
1429 if (fill->gamma)
1430 {
1431 static int once;
1432 if (!once++)
1433 FIXME("path gradient gamma correction not implemented\n");
1434 }
1435
1436 if (fill->blendcount)
1437 {
1438 static int once;
1439 if (!once++)
1440 FIXME("path gradient blend not implemented\n");
1441 }
1442
1443 if (fill->pblendcount)
1444 {
1445 static int once;
1446 if (!once++)
1447 FIXME("path gradient preset blend not implemented\n");
1448 }
1449
1450 if (!transform_fixme_once)
1451 {
1452 BOOL is_identity=TRUE;
1453 GdipIsMatrixIdentity(&fill->transform, &is_identity);
1454 if (!is_identity)
1455 {
1456 FIXME("path gradient transform not implemented\n");
1457 transform_fixme_once = TRUE;
1458 }
1459 }
1460
1461 stat = GdipClonePath(fill->path, &flat_path);
1462
1463 if (stat != Ok)
1464 return stat;
1465
1466 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
1467 CoordinateSpaceWorld, &world_to_device);
1468 if (stat == Ok)
1469 {
1470 stat = GdipTransformPath(flat_path, &world_to_device);
1471
1472 if (stat == Ok)
1473 {
1474 center_point = fill->center;
1475 stat = GdipTransformMatrixPoints(&world_to_device, &center_point, 1);
1476 }
1477
1478 if (stat == Ok)
1479 stat = GdipFlattenPath(flat_path, NULL, 0.5);
1480 }
1481
1482 if (stat != Ok)
1483 {
1484 GdipDeletePath(flat_path);
1485 return stat;
1486 }
1487
1488 for (i=0; i<flat_path->pathdata.Count; i++)
1489 {
1490 int start_center_line=0, end_center_line=0;
1491 BOOL seen_start = FALSE, seen_end = FALSE, seen_center = FALSE;
1492 REAL center_distance;
1493 ARGB start_color, end_color;
1494 REAL dy, dx;
1495
1496 type = flat_path->pathdata.Types[i];
1497
1498 if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
1499 figure_start = i;
1500
1501 start_point = flat_path->pathdata.Points[i];
1502
1503 start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
1504
1505 if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
1506 {
1507 end_point = flat_path->pathdata.Points[figure_start];
1508 end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
1509 }
1510 else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
1511 {
1512 end_point = flat_path->pathdata.Points[i+1];
1513 end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
1514 }
1515 else
1516 continue;
1517
1518 outer_color = start_color;
1519
1520 min_yf = center_point.Y;
1521 if (min_yf > start_point.Y) min_yf = start_point.Y;
1522 if (min_yf > end_point.Y) min_yf = end_point.Y;
1523
1524 if (min_yf < fill_area->Y)
1525 min_y = fill_area->Y;
1526 else
1527 min_y = (INT)ceil(min_yf);
1528
1529 max_yf = center_point.Y;
1530 if (max_yf < start_point.Y) max_yf = start_point.Y;
1531 if (max_yf < end_point.Y) max_yf = end_point.Y;
1532
1533 if (max_yf > fill_area->Y + fill_area->Height)
1534 max_y = fill_area->Y + fill_area->Height;
1535 else
1536 max_y = (INT)ceil(max_yf);
1537
1538 dy = end_point.Y - start_point.Y;
1539 dx = end_point.X - start_point.X;
1540
1541 /* This is proportional to the distance from start-end line to center point. */
1542 center_distance = dy * (start_point.X - center_point.X) +
1543 dx * (center_point.Y - start_point.Y);
1544
1545 for (y=min_y; y<max_y; y++)
1546 {
1547 REAL yf = (REAL)y;
1548
1549 if (!seen_start && yf >= start_point.Y)
1550 {
1551 seen_start = TRUE;
1552 start_center_line ^= 1;
1553 }
1554 if (!seen_end && yf >= end_point.Y)
1555 {
1556 seen_end = TRUE;
1557 end_center_line ^= 1;
1558 }
1559 if (!seen_center && yf >= center_point.Y)
1560 {
1561 seen_center = TRUE;
1562 start_center_line ^= 1;
1563 end_center_line ^= 1;
1564 }
1565
1566 if (start_center_line)
1567 line1_xf = intersect_line_scanline(&start_point, &center_point, yf);
1568 else
1569 line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
1570
1571 if (end_center_line)
1572 line2_xf = intersect_line_scanline(&end_point, &center_point, yf);
1573 else
1574 line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
1575
1576 if (line1_xf < line2_xf)
1577 {
1578 min_x = (INT)ceil(line1_xf);
1579 max_x = (INT)ceil(line2_xf);
1580 }
1581 else
1582 {
1583 min_x = (INT)ceil(line2_xf);
1584 max_x = (INT)ceil(line1_xf);
1585 }
1586
1587 if (min_x < fill_area->X)
1588 min_x = fill_area->X;
1589 if (max_x > fill_area->X + fill_area->Width)
1590 max_x = fill_area->X + fill_area->Width;
1591
1592 for (x=min_x; x<max_x; x++)
1593 {
1594 REAL xf = (REAL)x;
1595 REAL distance;
1596
1597 if (start_color != end_color)
1598 {
1599 REAL blend_amount, pdy, pdx;
1600 pdy = yf - center_point.Y;
1601 pdx = xf - center_point.X;
1602
1603 if (fabs(pdx) <= 0.001 && fabs(pdy) <= 0.001)
1604 {
1605 /* Too close to center point, don't try to calculate outer color */
1606 outer_color = start_color;
1607 }
1608 else
1609 {
1610 blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
1611 outer_color = blend_colors(start_color, end_color, blend_amount);
1612 }
1613 }
1614
1615 distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
1616 (end_point.X - start_point.X) * (yf - start_point.Y);
1617
1618 distance = distance / center_distance;
1619
1620 argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
1621 blend_colors(outer_color, fill->centercolor, distance);
1622 }
1623 }
1624 }
1625
1626 GdipDeletePath(flat_path);
1627 return stat;
1628 }
1629 default:
1630 return NotImplemented;
1631 }
1632 }
1633
1634 /* Draws the linecap the specified color and size on the hdc. The linecap is in
1635 * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
1636 * should not be called on an hdc that has a path you care about. */
1637 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
1638 const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
1639 {
1640 HGDIOBJ oldbrush = NULL, oldpen = NULL;
1641 GpMatrix matrix;
1642 HBRUSH brush = NULL;
1643 HPEN pen = NULL;
1644 PointF ptf[4], *custptf = NULL;
1645 POINT pt[4], *custpt = NULL;
1646 BYTE *tp = NULL;
1647 REAL theta, dsmall, dbig, dx, dy = 0.0;
1648 INT i, count;
1649 LOGBRUSH lb;
1650 BOOL customstroke;
1651
1652 if((x1 == x2) && (y1 == y2))
1653 return;
1654
1655 theta = gdiplus_atan2(y2 - y1, x2 - x1);
1656
1657 customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
1658 if(!customstroke){
1659 brush = CreateSolidBrush(color);
1660 lb.lbStyle = BS_SOLID;
1661 lb.lbColor = color;
1662 lb.lbHatch = 0;
1663 pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
1664 PS_JOIN_MITER, 1, &lb, 0,
1665 NULL);
1666 oldbrush = SelectObject(graphics->hdc, brush);
1667 oldpen = SelectObject(graphics->hdc, pen);
1668 }
1669
1670 switch(cap){
1671 case LineCapFlat:
1672 break;
1673 case LineCapSquare:
1674 case LineCapSquareAnchor:
1675 case LineCapDiamondAnchor:
1676 size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
1677 if(cap == LineCapDiamondAnchor){
1678 dsmall = cos(theta + M_PI_2) * size;
1679 dbig = sin(theta + M_PI_2) * size;
1680 }
1681 else{
1682 dsmall = cos(theta + M_PI_4) * size;
1683 dbig = sin(theta + M_PI_4) * size;
1684 }
1685
1686 ptf[0].X = x2 - dsmall;
1687 ptf[1].X = x2 + dbig;
1688
1689 ptf[0].Y = y2 - dbig;
1690 ptf[3].Y = y2 + dsmall;
1691
1692 ptf[1].Y = y2 - dsmall;
1693 ptf[2].Y = y2 + dbig;
1694
1695 ptf[3].X = x2 - dbig;
1696 ptf[2].X = x2 + dsmall;
1697
1698 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1699
1700 round_points(pt, ptf, 3);
1701
1702 Polygon(graphics->hdc, pt, 4);
1703
1704 break;
1705 case LineCapArrowAnchor:
1706 size = size * 4.0 / sqrt(3.0);
1707
1708 dx = cos(M_PI / 6.0 + theta) * size;
1709 dy = sin(M_PI / 6.0 + theta) * size;
1710
1711 ptf[0].X = x2 - dx;
1712 ptf[0].Y = y2 - dy;
1713
1714 dx = cos(- M_PI / 6.0 + theta) * size;
1715 dy = sin(- M_PI / 6.0 + theta) * size;
1716
1717 ptf[1].X = x2 - dx;
1718 ptf[1].Y = y2 - dy;
1719
1720 ptf[2].X = x2;
1721 ptf[2].Y = y2;
1722
1723 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1724
1725 round_points(pt, ptf, 3);
1726
1727 Polygon(graphics->hdc, pt, 3);
1728
1729 break;
1730 case LineCapRoundAnchor:
1731 dx = dy = ANCHOR_WIDTH * size / 2.0;
1732
1733 ptf[0].X = x2 - dx;
1734 ptf[0].Y = y2 - dy;
1735 ptf[1].X = x2 + dx;
1736 ptf[1].Y = y2 + dy;
1737
1738 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1739
1740 round_points(pt, ptf, 3);
1741
1742 Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
1743
1744 break;
1745 case LineCapTriangle:
1746 size = size / 2.0;
1747 dx = cos(M_PI_2 + theta) * size;
1748 dy = sin(M_PI_2 + theta) * size;
1749
1750 ptf[0].X = x2 - dx;
1751 ptf[0].Y = y2 - dy;
1752 ptf[1].X = x2 + dx;
1753 ptf[1].Y = y2 + dy;
1754
1755 dx = cos(theta) * size;
1756 dy = sin(theta) * size;
1757
1758 ptf[2].X = x2 + dx;
1759 ptf[2].Y = y2 + dy;
1760
1761 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1762
1763 round_points(pt, ptf, 3);
1764
1765 Polygon(graphics->hdc, pt, 3);
1766
1767 break;
1768 case LineCapRound:
1769 dx = dy = size / 2.0;
1770
1771 ptf[0].X = x2 - dx;
1772 ptf[0].Y = y2 - dy;
1773 ptf[1].X = x2 + dx;
1774 ptf[1].Y = y2 + dy;
1775
1776 dx = -cos(M_PI_2 + theta) * size;
1777 dy = -sin(M_PI_2 + theta) * size;
1778
1779 ptf[2].X = x2 - dx;
1780 ptf[2].Y = y2 - dy;
1781 ptf[3].X = x2 + dx;
1782 ptf[3].Y = y2 + dy;
1783
1784 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1785
1786 round_points(pt, ptf, 3);
1787
1788 Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
1789 pt[2].y, pt[3].x, pt[3].y);
1790
1791 break;
1792 case LineCapCustom:
1793 if(!custom)
1794 break;
1795
1796 count = custom->pathdata.Count;
1797 custptf = heap_alloc_zero(count * sizeof(PointF));
1798 custpt = heap_alloc_zero(count * sizeof(POINT));
1799 tp = heap_alloc_zero(count);
1800
1801 if(!custptf || !custpt || !tp)
1802 goto custend;
1803
1804 memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
1805
1806 GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1807 GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
1808 GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
1809 MatrixOrderAppend);
1810 GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
1811 GdipTransformMatrixPoints(&matrix, custptf, count);
1812
1813 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1814
1815 round_points(pt, ptf, 3);
1816
1817 for(i = 0; i < count; i++)
1818 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
1819
1820 if(custom->fill){
1821 BeginPath(graphics->hdc);
1822 PolyDraw(graphics->hdc, custpt, tp, count);
1823 EndPath(graphics->hdc);
1824 StrokeAndFillPath(graphics->hdc);
1825 }
1826 else
1827 PolyDraw(graphics->hdc, custpt, tp, count);
1828
1829 custend:
1830 heap_free(custptf);
1831 heap_free(custpt);
1832 heap_free(tp);
1833 break;
1834 default:
1835 break;
1836 }
1837
1838 if(!customstroke){
1839 SelectObject(graphics->hdc, oldbrush);
1840 SelectObject(graphics->hdc, oldpen);
1841 DeleteObject(brush);
1842 DeleteObject(pen);
1843 }
1844 }
1845
1846 /* Shortens the line by the given percent by changing x2, y2.
1847 * If percent is > 1.0 then the line will change direction.
1848 * If percent is negative it can lengthen the line. */
1849 static void shorten_line_percent(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL percent)
1850 {
1851 REAL dist, theta, dx, dy;
1852
1853 if((y1 == *y2) && (x1 == *x2))
1854 return;
1855
1856 dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
1857 theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
1858 dx = cos(theta) * dist;
1859 dy = sin(theta) * dist;
1860
1861 *x2 = *x2 + dx;
1862 *y2 = *y2 + dy;
1863 }
1864
1865 /* Shortens the line by the given amount by changing x2, y2.
1866 * If the amount is greater than the distance, the line will become length 0.
1867 * If the amount is negative, it can lengthen the line. */
1868 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
1869 {
1870 REAL dx, dy, percent;
1871
1872 dx = *x2 - x1;
1873 dy = *y2 - y1;
1874 if(dx == 0 && dy == 0)
1875 return;
1876
1877 percent = amt / sqrt(dx * dx + dy * dy);
1878 if(percent >= 1.0){
1879 *x2 = x1;
1880 *y2 = y1;
1881 return;
1882 }
1883
1884 shorten_line_percent(x1, y1, x2, y2, percent);
1885 }
1886
1887 /* Conducts a linear search to find the bezier points that will back off
1888 * the endpoint of the curve by a distance of amt. Linear search works
1889 * better than binary in this case because there are multiple solutions,
1890 * and binary searches often find a bad one. I don't think this is what
1891 * Windows does but short of rendering the bezier without GDI's help it's
1892 * the best we can do. If rev then work from the start of the passed points
1893 * instead of the end. */
1894 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
1895 {
1896 GpPointF origpt[4];
1897 REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
1898 INT i, first = 0, second = 1, third = 2, fourth = 3;
1899
1900 if(rev){
1901 first = 3;
1902 second = 2;
1903 third = 1;
1904 fourth = 0;
1905 }
1906
1907 origx = pt[fourth].X;
1908 origy = pt[fourth].Y;
1909 memcpy(origpt, pt, sizeof(GpPointF) * 4);
1910
1911 for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
1912 /* reset bezier points to original values */
1913 memcpy(pt, origpt, sizeof(GpPointF) * 4);
1914 /* Perform magic on bezier points. Order is important here.*/
1915 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1916 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1917 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1918 shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
1919 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1920 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1921
1922 dx = pt[fourth].X - origx;
1923 dy = pt[fourth].Y - origy;
1924
1925 diff = sqrt(dx * dx + dy * dy);
1926 percent += 0.0005 * amt;
1927 }
1928 }
1929
1930 /* Draws a combination of bezier curves and lines between points. */
1931 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
1932 GDIPCONST BYTE * types, INT count, BOOL caps)
1933 {
1934 POINT *pti = heap_alloc_zero(count * sizeof(POINT));
1935 BYTE *tp = heap_alloc_zero(count);
1936 GpPointF *ptcopy = heap_alloc_zero(count * sizeof(GpPointF));
1937 INT i, j;
1938 GpStatus status = GenericError;
1939
1940 if(!count){
1941 status = Ok;
1942 goto end;
1943 }
1944 if(!pti || !tp || !ptcopy){
1945 status = OutOfMemory;
1946 goto end;
1947 }
1948
1949 for(i = 1; i < count; i++){
1950 if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
1951 if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
1952 || !(types[i + 2] & PathPointTypeBezier)){
1953 ERR("Bad bezier points\n");
1954 goto end;
1955 }
1956 i += 2;
1957 }
1958 }
1959
1960 memcpy(ptcopy, pt, count * sizeof(GpPointF));
1961
1962 /* If we are drawing caps, go through the points and adjust them accordingly,
1963 * and draw the caps. */
1964 if(caps){
1965 switch(types[count - 1] & PathPointTypePathTypeMask){
1966 case PathPointTypeBezier:
1967 if(pen->endcap == LineCapArrowAnchor)
1968 shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
1969 else if((pen->endcap == LineCapCustom) && pen->customend)
1970 shorten_bezier_amt(&ptcopy[count - 4],
1971 pen->width * pen->customend->inset, FALSE);
1972
1973 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1974 pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
1975 pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
1976 pt[count - 1].X, pt[count - 1].Y);
1977
1978 break;
1979 case PathPointTypeLine:
1980 if(pen->endcap == LineCapArrowAnchor)
1981 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1982 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1983 pen->width);
1984 else if((pen->endcap == LineCapCustom) && pen->customend)
1985 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1986 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1987 pen->customend->inset * pen->width);
1988
1989 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1990 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
1991 pt[count - 1].Y);
1992
1993 break;
1994 default:
1995 ERR("Bad path last point\n");
1996 goto end;
1997 }
1998
1999 /* Find start of points */
2000 for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
2001 == PathPointTypeStart); j++);
2002
2003 switch(types[j] & PathPointTypePathTypeMask){
2004 case PathPointTypeBezier:
2005 if(pen->startcap == LineCapArrowAnchor)
2006 shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
2007 else if((pen->startcap == LineCapCustom) && pen->customstart)
2008 shorten_bezier_amt(&ptcopy[j - 1],
2009 pen->width * pen->customstart->inset, TRUE);
2010
2011 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
2012 pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
2013 pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
2014 pt[j - 1].X, pt[j - 1].Y);
2015
2016 break;
2017 case PathPointTypeLine:
2018 if(pen->startcap == LineCapArrowAnchor)
2019 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
2020 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
2021 pen->width);
2022 else if((pen->startcap == LineCapCustom) && pen->customstart)
2023 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
2024 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
2025 pen->customstart->inset * pen->width);
2026
2027 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
2028 pt[j].X, pt[j].Y, pt[j - 1].X,
2029 pt[j - 1].Y);
2030
2031 break;
2032 default:
2033 ERR("Bad path points\n");
2034 goto end;
2035 }
2036 }
2037
2038 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, count);
2039
2040 round_points(pti, ptcopy, count);
2041
2042 for(i = 0; i < count; i++){
2043 tp[i] = convert_path_point_type(types[i]);
2044 }
2045
2046 PolyDraw(graphics->hdc, pti, tp, count);
2047
2048 status = Ok;
2049
2050 end:
2051 heap_free(pti);
2052 heap_free(ptcopy);
2053 heap_free(tp);
2054
2055 return status;
2056 }
2057
2058 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
2059 {
2060 GpStatus result;
2061
2062 BeginPath(graphics->hdc);
2063 result = draw_poly(graphics, NULL, path->pathdata.Points,
2064 path->pathdata.Types, path->pathdata.Count, FALSE);
2065 EndPath(graphics->hdc);
2066 return result;
2067 }
2068
2069 typedef enum GraphicsContainerType {
2070 BEGIN_CONTAINER,
2071 SAVE_GRAPHICS
2072 } GraphicsContainerType;
2073
2074 typedef struct _GraphicsContainerItem {
2075 struct list entry;
2076 GraphicsContainer contid;
2077 GraphicsContainerType type;
2078
2079 SmoothingMode smoothing;
2080 CompositingQuality compqual;
2081 InterpolationMode interpolation;
2082 CompositingMode compmode;
2083 TextRenderingHint texthint;
2084 REAL scale;
2085 GpUnit unit;
2086 PixelOffsetMode pixeloffset;
2087 UINT textcontrast;
2088 GpMatrix worldtrans;
2089 GpRegion* clip;
2090 INT origin_x, origin_y;
2091 } GraphicsContainerItem;
2092
2093 static GpStatus init_container(GraphicsContainerItem** container,
2094 GDIPCONST GpGraphics* graphics, GraphicsContainerType type){
2095 GpStatus sts;
2096
2097 *container = heap_alloc_zero(sizeof(GraphicsContainerItem));
2098 if(!(*container))
2099 return OutOfMemory;
2100
2101 (*container)->contid = graphics->contid + 1;
2102 (*container)->type = type;
2103
2104 (*container)->smoothing = graphics->smoothing;
2105 (*container)->compqual = graphics->compqual;
2106 (*container)->interpolation = graphics->interpolation;
2107 (*container)->compmode = graphics->compmode;
2108 (*container)->texthint = graphics->texthint;
2109 (*container)->scale = graphics->scale;
2110 (*container)->unit = graphics->unit;
2111 (*container)->textcontrast = graphics->textcontrast;
2112 (*container)->pixeloffset = graphics->pixeloffset;
2113 (*container)->origin_x = graphics->origin_x;
2114 (*container)->origin_y = graphics->origin_y;
2115 (*container)->worldtrans = graphics->worldtrans;
2116
2117 sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
2118 if(sts != Ok){
2119 heap_free(*container);
2120 *container = NULL;
2121 return sts;
2122 }
2123
2124 return Ok;
2125 }
2126
2127 static void delete_container(GraphicsContainerItem* container)
2128 {
2129 GdipDeleteRegion(container->clip);
2130 heap_free(container);
2131 }
2132
2133 static GpStatus restore_container(GpGraphics* graphics,
2134 GDIPCONST GraphicsContainerItem* container){
2135 GpStatus sts;
2136 GpRegion *newClip;
2137
2138 sts = GdipCloneRegion(container->clip, &newClip);
2139 if(sts != Ok) return sts;
2140
2141 graphics->worldtrans = container->worldtrans;
2142
2143 GdipDeleteRegion(graphics->clip);
2144 graphics->clip = newClip;
2145
2146 graphics->contid = container->contid - 1;
2147
2148 graphics->smoothing = container->smoothing;
2149 graphics->compqual = container->compqual;
2150 graphics->interpolation = container->interpolation;
2151 graphics->compmode = container->compmode;
2152 graphics->texthint = container->texthint;
2153 graphics->scale = container->scale;
2154 graphics->unit = container->unit;
2155 graphics->textcontrast = container->textcontrast;
2156 graphics->pixeloffset = container->pixeloffset;
2157 graphics->origin_x = container->origin_x;
2158 graphics->origin_y = container->origin_y;
2159
2160 return Ok;
2161 }
2162
2163 static GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect)
2164 {
2165 RECT wnd_rect;
2166 GpStatus stat=Ok;
2167 GpUnit unit;
2168
2169 if(graphics->hwnd) {
2170 if(!GetClientRect(graphics->hwnd, &wnd_rect))
2171 return GenericError;
2172
2173 rect->X = wnd_rect.left;
2174 rect->Y = wnd_rect.top;
2175 rect->Width = wnd_rect.right - wnd_rect.left;
2176 rect->Height = wnd_rect.bottom - wnd_rect.top;
2177 }else if (graphics->image){
2178 stat = GdipGetImageBounds(graphics->image, rect, &unit);
2179 if (stat == Ok && unit != UnitPixel)
2180 FIXME("need to convert from unit %i\n", unit);
2181 }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
2182 HBITMAP hbmp;
2183 BITMAP bmp;
2184
2185 rect->X = 0;
2186 rect->Y = 0;
2187
2188 hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
2189 if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
2190 {
2191 rect->Width = bmp.bmWidth;
2192 rect->Height = bmp.bmHeight;
2193 }
2194 else
2195 {
2196 /* FIXME: ??? */
2197 rect->Width = 1;
2198 rect->Height = 1;
2199 }
2200 }else{
2201 rect->X = 0;
2202 rect->Y = 0;
2203 rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
2204 rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
2205 }
2206
2207 return stat;
2208 }
2209
2210 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
2211 {
2212 GpStatus stat = get_graphics_device_bounds(graphics, rect);
2213
2214 if (stat == Ok && graphics->hdc)
2215 {
2216 GpPointF points[4], min_point, max_point;
2217 int i;
2218
2219 points[0].X = points[2].X = rect->X;
2220 points[0].Y = points[1].Y = rect->Y;
2221 points[1].X = points[3].X = rect->X + rect->Width;
2222 points[2].Y = points[3].Y = rect->Y + rect->Height;
2223
2224 gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
2225
2226 min_point = max_point = points[0];
2227
2228 for (i=1; i<4; i++)
2229 {
2230 if (points[i].X < min_point.X) min_point.X = points[i].X;
2231 if (points[i].Y < min_point.Y) min_point.Y = points[i].Y;
2232 if (points[i].X > max_point.X) max_point.X = points[i].X;
2233 if (points[i].Y > max_point.Y) max_point.Y = points[i].Y;
2234 }
2235
2236 rect->X = min_point.X;
2237 rect->Y = min_point.Y;
2238 rect->Width = max_point.X - min_point.X;
2239 rect->Height = max_point.Y - min_point.Y;
2240 }
2241
2242 return stat;
2243 }
2244
2245 /* on success, rgn will contain the region of the graphics object which
2246 * is visible after clipping has been applied */
2247 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
2248 {
2249 GpStatus stat;
2250 GpRectF rectf;
2251 GpRegion* tmp;
2252
2253 /* Ignore graphics image bounds for metafiles */
2254 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2255 return GdipCombineRegionRegion(rgn, graphics->clip, CombineModeReplace);
2256
2257 if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
2258 return stat;
2259
2260 if((stat = GdipCreateRegion(&tmp)) != Ok)
2261 return stat;
2262
2263 if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
2264 goto end;
2265
2266 if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
2267 goto end;
2268
2269 stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
2270
2271 end:
2272 GdipDeleteRegion(tmp);
2273 return stat;
2274 }
2275
2276 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
2277 {
2278 REAL height;
2279
2280 if (font->unit == UnitPixel)
2281 {
2282 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
2283 }
2284 else
2285 {
2286 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
2287 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
2288 else
2289 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
2290 }
2291
2292 lf->lfHeight = -(height + 0.5);
2293 lf->lfWidth = 0;
2294 lf->lfEscapement = 0;
2295 lf->lfOrientation = 0;
2296 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
2297 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
2298 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
2299 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
2300 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
2301 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
2302 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
2303 lf->lfQuality = DEFAULT_QUALITY;
2304 lf->lfPitchAndFamily = 0;
2305 strcpyW(lf->lfFaceName, font->family->FamilyName);
2306 }
2307
2308 static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
2309 GDIPCONST GpStringFormat *format, HFONT *hfont,
2310 GDIPCONST GpMatrix *matrix)
2311 {
2312 HDC hdc = CreateCompatibleDC(0);
2313 GpPointF pt[3];
2314 REAL angle, rel_width, rel_height, font_height;
2315 LOGFONTW lfw;
2316 HFONT unscaled_font;
2317 TEXTMETRICW textmet;
2318
2319 if (font->unit == UnitPixel || font->unit == UnitWorld)
2320 font_height = font->emSize;
2321 else
2322 {
2323 REAL unit_scale, res;
2324
2325 res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
2326 unit_scale = units_scale(font->unit, graphics->unit, res);
2327
2328 font_height = font->emSize * unit_scale;
2329 }
2330
2331 pt[0].X = 0.0;
2332 pt[0].Y = 0.0;
2333 pt[1].X = 1.0;
2334 pt[1].Y = 0.0;
2335 pt[2].X = 0.0;
2336 pt[2].Y = 1.0;
2337 if (matrix)
2338 {
2339 GpMatrix xform = *matrix;
2340 GdipTransformMatrixPoints(&xform, pt, 3);
2341 }
2342
2343 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
2344 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2345 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
2346 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
2347 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
2348 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
2349
2350 get_log_fontW(font, graphics, &lfw);
2351 lfw.lfHeight = -gdip_round(font_height * rel_height);
2352 unscaled_font = CreateFontIndirectW(&lfw);
2353
2354 SelectObject(hdc, unscaled_font);
2355 GetTextMetricsW(hdc, &textmet);
2356
2357 lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
2358 lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
2359
2360 *hfont = CreateFontIndirectW(&lfw);
2361
2362 DeleteDC(hdc);
2363 DeleteObject(unscaled_font);
2364 }
2365
2366 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
2367 {
2368 TRACE("(%p, %p)\n", hdc, graphics);
2369
2370 return GdipCreateFromHDC2(hdc, NULL, graphics);
2371 }
2372
2373 static void get_gdi_transform(GpGraphics *graphics, GpMatrix *matrix)
2374 {
2375 XFORM xform;
2376
2377 if (graphics->hdc == NULL)
2378 {
2379 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2380 return;
2381 }
2382
2383 GetTransform(graphics->hdc, 0x204, &xform);
2384 GdipSetMatrixElements(matrix, xform.eM11, xform.eM12, xform.eM21, xform.eM22, xform.eDx, xform.eDy);
2385 }
2386
2387 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
2388 {
2389 GpStatus retval;
2390 HBITMAP hbitmap;
2391 DIBSECTION dib;
2392
2393 TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
2394
2395 if(hDevice != NULL)
2396 FIXME("Don't know how to handle parameter hDevice\n");
2397
2398 if(hdc == NULL)
2399 return OutOfMemory;
2400
2401 if(graphics == NULL)
2402 return InvalidParameter;
2403
2404 *graphics = heap_alloc_zero(sizeof(GpGraphics));
2405 if(!*graphics) return OutOfMemory;
2406
2407 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2408
2409 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2410 heap_free(*graphics);
2411 return retval;
2412 }
2413
2414 hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
2415 if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
2416 dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
2417 {
2418 (*graphics)->alpha_hdc = 1;
2419 }
2420
2421 (*graphics)->hdc = hdc;
2422 (*graphics)->hwnd = WindowFromDC(hdc);
2423 (*graphics)->owndc = FALSE;
2424 (*graphics)->smoothing = SmoothingModeDefault;
2425 (*graphics)->compqual = CompositingQualityDefault;
2426 (*graphics)->interpolation = InterpolationModeBilinear;
2427 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2428 (*graphics)->compmode = CompositingModeSourceOver;
2429 (*graphics)->unit = UnitDisplay;
2430 (*graphics)->scale = 1.0;
2431 (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
2432 (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
2433 (*graphics)->busy = FALSE;
2434 (*graphics)->textcontrast = 4;
2435 list_init(&(*graphics)->containers);
2436 #ifdef __REACTOS__
2437 (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
2438 #else
2439 (*graphics)->contid = 0;
2440 #endif
2441 get_gdi_transform(*graphics, &(*graphics)->gdi_transform);
2442
2443 (*graphics)->gdi_clip = CreateRectRgn(0,0,0,0);
2444 if (!GetClipRgn(hdc, (*graphics)->gdi_clip))
2445 {
2446 DeleteObject((*graphics)->gdi_clip);
2447 (*graphics)->gdi_clip = NULL;
2448 }
2449
2450 TRACE("<-- %p\n", *graphics);
2451
2452 return Ok;
2453 }
2454
2455 GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
2456 {
2457 GpStatus retval;
2458
2459 *graphics = heap_alloc_zero(sizeof(GpGraphics));
2460 if(!*graphics) return OutOfMemory;
2461
2462 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2463 GdipSetMatrixElements(&(*graphics)->gdi_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2464
2465 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2466 heap_free(*graphics);
2467 return retval;
2468 }
2469
2470 (*graphics)->hdc = NULL;
2471 (*graphics)->hwnd = NULL;
2472 (*graphics)->owndc = FALSE;
2473 (*graphics)->image = image;
2474 /* We have to store the image type here because the image may be freed
2475 * before GdipDeleteGraphics is called, and metafiles need special treatment. */
2476 (*graphics)->image_type = image->type;
2477 (*graphics)->smoothing = SmoothingModeDefault;
2478 (*graphics)->compqual = CompositingQualityDefault;
2479 (*graphics)->interpolation = InterpolationModeBilinear;
2480 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2481 (*graphics)->compmode = CompositingModeSourceOver;
2482 (*graphics)->unit = UnitDisplay;
2483 (*graphics)->scale = 1.0;
2484 (*graphics)->xres = image->xres;
2485 (*graphics)->yres = image->yres;
2486 (*graphics)->busy = FALSE;
2487 (*graphics)->textcontrast = 4;
2488 list_init(&(*graphics)->containers);
2489 #ifdef __REACTOS__
2490 (*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
2491 #else
2492 (*graphics)->contid = 0;
2493 #endif
2494
2495 TRACE("<-- %p\n", *graphics);
2496
2497 return Ok;
2498 }
2499
2500 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
2501 {
2502 GpStatus ret;
2503 HDC hdc;
2504
2505 TRACE("(%p, %p)\n", hwnd, graphics);
2506
2507 hdc = GetDC(hwnd);
2508
2509 if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
2510 {
2511 ReleaseDC(hwnd, hdc);
2512 return ret;
2513 }
2514
2515 (*graphics)->hwnd = hwnd;
2516 (*graphics)->owndc = TRUE;
2517
2518 return Ok;
2519 }
2520
2521 /* FIXME: no icm handling */
2522 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
2523 {
2524 TRACE("(%p, %p)\n", hwnd, graphics);
2525
2526 return GdipCreateFromHWND(hwnd, graphics);
2527 }
2528
2529 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
2530 UINT access, IStream **stream)
2531 {
2532 DWORD dwMode;
2533 HRESULT ret;
2534
2535 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
2536
2537 if(!stream || !filename)
2538 return InvalidParameter;
2539
2540 if(access & GENERIC_WRITE)
2541 dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
2542 else if(access & GENERIC_READ)
2543 dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
2544 else
2545 return InvalidParameter;
2546
2547 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
2548
2549 return hresult_to_status(ret);
2550 }
2551
2552 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
2553 {
2554 GraphicsContainerItem *cont, *next;
2555 GpStatus stat;
2556 TRACE("(%p)\n", graphics);
2557
2558 if(!graphics) return InvalidParameter;
2559 if(graphics->busy) return ObjectBusy;
2560
2561 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2562 {
2563 stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
2564 if (stat != Ok)
2565 return stat;
2566 }
2567
2568 if(graphics->owndc)
2569 ReleaseDC(graphics->hwnd, graphics->hdc);
2570
2571 LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
2572 list_remove(&cont->entry);
2573 delete_container(cont);
2574 }
2575
2576 GdipDeleteRegion(graphics->clip);
2577
2578 DeleteObject(graphics->gdi_clip);
2579
2580 /* Native returns ObjectBusy on the second free, instead of crashing as we'd
2581 * do otherwise, but we can't have that in the test suite because it means
2582 * accessing freed memory. */
2583 graphics->busy = TRUE;
2584
2585 heap_free(graphics);
2586
2587 return Ok;
2588 }
2589
2590 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
2591 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2592 {
2593 GpStatus status;
2594 GpPath *path;
2595
2596 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2597 width, height, startAngle, sweepAngle);
2598
2599 if(!graphics || !pen || width <= 0 || height <= 0)
2600 return InvalidParameter;
2601
2602 if(graphics->busy)
2603 return ObjectBusy;
2604
2605 status = GdipCreatePath(FillModeAlternate, &path);
2606 if (status != Ok) return status;
2607
2608 status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
2609 if (status == Ok)
2610 status = GdipDrawPath(graphics, pen, path);
2611
2612 GdipDeletePath(path);
2613 return status;
2614 }
2615
2616 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
2617 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2618 {
2619 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2620 width, height, startAngle, sweepAngle);
2621
2622 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2623 }
2624
2625 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
2626 REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
2627 {
2628 GpPointF pt[4];
2629
2630 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
2631 x2, y2, x3, y3, x4, y4);
2632
2633 if(!graphics || !pen)
2634 return InvalidParameter;
2635
2636 if(graphics->busy)
2637 return ObjectBusy;
2638
2639 pt[0].X = x1;
2640 pt[0].Y = y1;
2641 pt[1].X = x2;
2642 pt[1].Y = y2;
2643 pt[2].X = x3;
2644 pt[2].Y = y3;
2645 pt[3].X = x4;
2646 pt[3].Y = y4;
2647 return GdipDrawBeziers(graphics, pen, pt, 4);
2648 }
2649
2650 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
2651 INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
2652 {
2653 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
2654 x2, y2, x3, y3, x4, y4);
2655
2656 return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
2657 }
2658
2659 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
2660 GDIPCONST GpPointF *points, INT count)
2661 {
2662 GpStatus status;
2663 GpPath *path;
2664
2665 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2666
2667 if(!graphics || !pen || !points || (count <= 0))
2668 return InvalidParameter;
2669
2670 if(graphics->busy)
2671 return ObjectBusy;
2672
2673 status = GdipCreatePath(FillModeAlternate, &path);
2674 if (status != Ok) return status;
2675
2676 status = GdipAddPathBeziers(path, points, count);
2677 if (status == Ok)
2678 status = GdipDrawPath(graphics, pen, path);
2679
2680 GdipDeletePath(path);
2681 return status;
2682 }
2683
2684 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
2685 GDIPCONST GpPoint *points, INT count)
2686 {
2687 GpPointF *pts;
2688 GpStatus ret;
2689 INT i;
2690
2691 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2692
2693 if(!graphics || !pen || !points || (count <= 0))
2694 return InvalidParameter;
2695
2696 if(graphics->busy)
2697 return ObjectBusy;
2698
2699 pts = heap_alloc_zero(sizeof(GpPointF) * count);
2700 if(!pts)
2701 return OutOfMemory;
2702
2703 for(i = 0; i < count; i++){
2704 pts[i].X = (REAL)points[i].X;
2705 pts[i].Y = (REAL)points[i].Y;
2706 }
2707
2708 ret = GdipDrawBeziers(graphics,pen,pts,count);
2709
2710 heap_free(pts);
2711
2712 return ret;
2713 }
2714
2715 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
2716 GDIPCONST GpPointF *points, INT count)
2717 {
2718 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2719
2720 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
2721 }
2722
2723 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
2724 GDIPCONST GpPoint *points, INT count)
2725 {
2726 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2727
2728 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
2729 }
2730
2731 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
2732 GDIPCONST GpPointF *points, INT count, REAL tension)
2733 {
2734 GpPath *path;
2735 GpStatus status;
2736
2737 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2738
2739 if(!graphics || !pen || !points || count <= 0)
2740 return InvalidParameter;
2741
2742 if(graphics->busy)
2743 return ObjectBusy;
2744
2745 status = GdipCreatePath(FillModeAlternate, &path);
2746 if (status != Ok) return status;
2747
2748 status = GdipAddPathClosedCurve2(path, points, count, tension);
2749 if (status == Ok)
2750 status = GdipDrawPath(graphics, pen, path);
2751
2752 GdipDeletePath(path);
2753
2754 return status;
2755 }
2756
2757 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
2758 GDIPCONST GpPoint *points, INT count, REAL tension)
2759 {
2760 GpPointF *ptf;
2761 GpStatus stat;
2762 INT i;
2763
2764 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2765
2766 if(!points || count <= 0)
2767 return InvalidParameter;
2768
2769 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
2770 if(!ptf)
2771 return OutOfMemory;
2772
2773 for(i = 0; i < count; i++){
2774 ptf[i].X = (REAL)points[i].X;
2775 ptf[i].Y = (REAL)points[i].Y;
2776 }
2777
2778 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
2779
2780 heap_free(ptf);
2781
2782 return stat;
2783 }
2784
2785 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
2786 GDIPCONST GpPointF *points, INT count)
2787 {
2788 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2789
2790 return GdipDrawCurve2(graphics,pen,points,count,1.0);
2791 }
2792
2793 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
2794 GDIPCONST GpPoint *points, INT count)
2795 {
2796 GpPointF *pointsF;
2797 GpStatus ret;
2798 INT i;
2799
2800 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2801
2802 if(!points)
2803 return InvalidParameter;
2804
2805 pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
2806 if(!pointsF)
2807 return OutOfMemory;
2808
2809 for(i = 0; i < count; i++){
2810 pointsF[i].X = (REAL)points[i].X;
2811 pointsF[i].Y = (REAL)points[i].Y;
2812 }
2813
2814 ret = GdipDrawCurve(graphics,pen,pointsF,count);
2815 heap_free(pointsF);
2816
2817 return ret;
2818 }
2819
2820 /* Approximates cardinal spline with Bezier curves. */
2821 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
2822 GDIPCONST GpPointF *points, INT count, REAL tension)
2823 {
2824 GpPath *path;
2825 GpStatus status;
2826
2827 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2828
2829 if(!graphics || !pen)
2830 return InvalidParameter;
2831
2832 if(graphics->busy)
2833 return ObjectBusy;
2834
2835 if(count < 2)
2836 return InvalidParameter;
2837
2838 status = GdipCreatePath(FillModeAlternate, &path);
2839 if (status != Ok) return status;
2840
2841 status = GdipAddPathCurve2(path, points, count, tension);
2842 if (status == Ok)
2843 status = GdipDrawPath(graphics, pen, path);
2844
2845 GdipDeletePath(path);
2846 return status;
2847 }
2848
2849 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
2850 GDIPCONST GpPoint *points, INT count, REAL tension)
2851 {
2852 GpPointF *pointsF;
2853 GpStatus ret;
2854 INT i;
2855
2856 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2857
2858 if(!points)
2859 return InvalidParameter;
2860
2861 pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
2862 if(!pointsF)
2863 return OutOfMemory;
2864
2865 for(i = 0; i < count; i++){
2866 pointsF[i].X = (REAL)points[i].X;
2867 pointsF[i].Y = (REAL)points[i].Y;
2868 }
2869
2870 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
2871 heap_free(pointsF);
2872
2873 return ret;
2874 }
2875
2876 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
2877 GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
2878 REAL tension)
2879 {
2880 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2881
2882 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2883 return InvalidParameter;
2884 }
2885
2886 return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
2887 }
2888
2889 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
2890 GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
2891 REAL tension)
2892 {
2893 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2894
2895 if(count < 0){
2896 return OutOfMemory;
2897 }
2898
2899 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2900 return InvalidParameter;
2901 }
2902
2903 return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
2904 }
2905
2906 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
2907 REAL y, REAL width, REAL height)
2908 {
2909 GpPath *path;
2910 GpStatus status;
2911
2912 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2913
2914 if(!graphics || !pen)
2915 return InvalidParameter;
2916
2917 if(graphics->busy)
2918 return ObjectBusy;
2919
2920 status = GdipCreatePath(FillModeAlternate, &path);
2921 if (status != Ok) return status;
2922
2923 status = GdipAddPathEllipse(path, x, y, width, height);
2924 if (status == Ok)
2925 status = GdipDrawPath(graphics, pen, path);
2926
2927 GdipDeletePath(path);
2928 return status;
2929 }
2930
2931 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
2932 INT y, INT width, INT height)
2933 {
2934 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2935
2936 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2937 }
2938
2939
2940 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
2941 {
2942 UINT width, height;
2943
2944 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
2945
2946 if(!graphics || !image)
2947 return InvalidParameter;
2948
2949 GdipGetImageWidth(image, &width);
2950 GdipGetImageHeight(image, &height);
2951
2952 return GdipDrawImagePointRect(graphics, image, x, y,
2953 0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
2954 }
2955
2956 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
2957 INT y)
2958 {
2959 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
2960
2961 return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
2962 }
2963
2964 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
2965 REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
2966 GpUnit srcUnit)
2967 {
2968 GpPointF points[3];
2969 REAL scale_x, scale_y, width, height;
2970
2971 TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2972
2973 if (!graphics || !image) return InvalidParameter;
2974
2975 scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
2976 scale_x *= graphics->xres / image->xres;
2977 scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
2978 scale_y *= graphics->yres / image->yres;
2979 width = srcwidth * scale_x;
2980 height = srcheight * scale_y;
2981
2982 points[0].X = points[2].X = x;
2983 points[0].Y = points[1].Y = y;
2984 points[1].X = x + width;
2985 points[2].Y = y + height;
2986
2987 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2988 srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
2989 }
2990
2991 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
2992 INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
2993 GpUnit srcUnit)
2994 {
2995 return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2996 }
2997
2998 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
2999 GDIPCONST GpPointF *dstpoints, INT count)
3000 {
3001 UINT width, height;
3002
3003 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
3004
3005 if(!image)
3006 return InvalidParameter;
3007
3008 GdipGetImageWidth(image, &width);
3009 GdipGetImageHeight(image, &height);
3010
3011 return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
3012 width, height, UnitPixel, NULL, NULL, NULL);
3013 }
3014
3015 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
3016 GDIPCONST GpPoint *dstpoints, INT count)
3017 {
3018 GpPointF ptf[3];
3019
3020 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
3021
3022 if (count != 3 || !dstpoints)
3023 return InvalidParameter;
3024
3025 ptf[0].X = (REAL)dstpoints[0].X;
3026 ptf[0].Y = (REAL)dstpoints[0].Y;
3027 ptf[1].X = (REAL)dstpoints[1].X;
3028 ptf[1].Y = (REAL)dstpoints[1].Y;
3029 ptf[2].X = (REAL)dstpoints[2].X;
3030 ptf[2].Y = (REAL)dstpoints[2].Y;
3031
3032 return GdipDrawImagePoints(graphics, image, ptf, count);
3033 }
3034
3035 static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
3036 unsigned int dataSize, const unsigned char *pStr, void *userdata)
3037 {
3038 GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
3039 return TRUE;
3040 }
3041
3042 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
3043 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
3044 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3045 DrawImageAbort callback, VOID * callbackData)
3046 {
3047 GpPointF ptf[4];
3048 POINT pti[4];
3049 GpStatus stat;
3050
3051 TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
3052 count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
3053 callbackData);
3054
3055 if (count > 3)
3056 return NotImplemented;
3057
3058 if(!graphics || !image || !points || count != 3)
3059 return InvalidParameter;
3060
3061 TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
3062 debugstr_pointf(&points[2]));
3063
3064 if (graphics->image && graphics->image->type == ImageTypeMetafile)
3065 {
3066 return METAFILE_DrawImagePointsRect((GpMetafile*)graphics->image,
3067 image, points, count, srcx, srcy, srcwidth, srcheight,
3068 srcUnit, imageAttributes, callback, callbackData);
3069 }
3070
3071 memcpy(ptf, points, 3 * sizeof(GpPointF));
3072
3073 /* Ensure source width/height is positive */
3074 if (srcwidth < 0)
3075 {
3076 GpPointF tmp = ptf[1];
3077 srcx = srcx + srcwidth;
3078 srcwidth = -srcwidth;
3079 ptf[2].X = ptf[2].X + ptf[1].X - ptf[0].X;
3080 ptf[2].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
3081 ptf[1] = ptf[0];
3082 ptf[0] = tmp;
3083 }
3084
3085 if (srcheight < 0)
3086 {
3087 GpPointF tmp = ptf[2];
3088 srcy = srcy + srcheight;
3089 srcheight = -srcheight;
3090 ptf[1].X = ptf[1].X + ptf[2].X - ptf[0].X;
3091 ptf[1].Y = ptf[1].Y + ptf[2].Y - ptf[0].Y;
3092 ptf[2] = ptf[0];
3093 ptf[0] = tmp;
3094 }
3095
3096 ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
3097 ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
3098 if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
3099 return Ok;
3100 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
3101 round_points(pti, ptf, 4);
3102
3103 TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
3104 wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
3105
3106 srcx = units_to_pixels(srcx, srcUnit, image->xres);
3107 srcy = units_to_pixels(srcy, srcUnit, image->yres);
3108 srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
3109 srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
3110 TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
3111
3112 if (image->type == ImageTypeBitmap)
3113 {
3114 GpBitmap* bitmap = (GpBitmap*)image;
3115 BOOL do_resampling = FALSE;
3116 BOOL use_software = FALSE;
3117
3118 TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
3119 graphics->xres, graphics->yres,
3120 graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
3121 graphics->scale, image->xres, image->yres, bitmap->format,
3122 imageAttributes ? imageAttributes->outside_color : 0);
3123
3124 if (ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
3125 ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
3126 srcx < 0 || srcy < 0 ||
3127 srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
3128 do_resampling = TRUE;
3129
3130 if (imageAttributes || graphics->alpha_hdc || do_resampling ||
3131 (graphics->image && graphics->image->type == ImageTypeBitmap))
3132 use_software = TRUE;
3133
3134 if (use_software)
3135 {
3136 RECT dst_area;
3137 GpRectF graphics_bounds;
3138 GpRect src_area;
3139 int i, x, y, src_stride, dst_stride;
3140 GpMatrix dst_to_src;
3141 REAL m11, m12, m21, m22, mdx, mdy;
3142 LPBYTE src_data, dst_data, dst_dyn_data=NULL;
3143 BitmapData lockeddata;
3144 InterpolationMode interpolation = graphics->interpolation;
3145 PixelOffsetMode offset_mode = graphics->pixeloffset;
3146 GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
3147 REAL x_dx, x_dy, y_dx, y_dy;
3148 static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
3149
3150 if (!imageAttributes)
3151 imageAttributes = &defaultImageAttributes;
3152
3153 dst_area.left = dst_area.right = pti[0].x;
3154 dst_area.top = dst_area.bottom = pti[0].y;
3155 for (i=1; i<4; i++)
3156 {
3157 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
3158 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
3159 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
3160 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
3161 }
3162
3163 stat = get_graphics_device_bounds(graphics, &graphics_bounds);
3164 if (stat != Ok) return stat;
3165
3166 if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
3167 if (graphics_bounds.Y > dst_area.top) dst_area.top = floorf(graphics_bounds.Y);
3168 if (graphics_bounds.X + graphics_bounds.Width < dst_area.right) dst_area.right = ceilf(graphics_bounds.X + graphics_bounds.Width);
3169 if (graphics_bounds.Y + graphics_bounds.Height < dst_area.bottom) dst_area.bottom = ceilf(graphics_bounds.Y + graphics_bounds.Height);
3170
3171 TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
3172
3173 if (IsRectEmpty(&dst_area)) return Ok;
3174
3175 m11 = (ptf[1].X - ptf[0].X) / srcwidth;
3176 m21 = (ptf[2].X - ptf[0].X) / srcheight;
3177 mdx = ptf[0].X - m11 * srcx - m21 * srcy;
3178 m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
3179 m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
3180 mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
3181
3182 GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
3183
3184 stat = GdipInvertMatrix(&dst_to_src);
3185 if (stat != Ok) return stat;
3186
3187 if (do_resampling)
3188 {
3189 get_bitmap_sample_size(interpolation, imageAttributes->wrap,
3190 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
3191 }
3192 else
3193 {
3194 /* Make sure src_area is equal in size to dst_area. */
3195 src_area.X = srcx + dst_area.left - pti[0].x;
3196 src_area.Y = srcy + dst_area.top - pti[0].y;
3197 src_area.Width = dst_area.right - dst_area.left;
3198 src_area.Height = dst_area.bottom - dst_area.top;
3199 }
3200
3201 TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
3202
3203 src_data = heap_alloc_zero(sizeof(ARGB) * src_area.Width * src_area.Height);
3204 if (!src_data)
3205 return OutOfMemory;
3206 src_stride = sizeof(ARGB) * src_area.Width;
3207
3208 /* Read the bits we need from the source bitmap into a compatible buffer. */
3209 lockeddata.Width = src_area.Width;
3210 lockeddata.Height = src_area.Height;
3211 lockeddata.Stride = src_stride;
3212 lockeddata.Scan0 = src_data;
3213 if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
3214 lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
3215 else if (imageAttributes != &defaultImageAttributes)
3216 lockeddata.PixelFormat = PixelFormat32bppARGB;
3217 else
3218 lockeddata.PixelFormat = PixelFormat32bppPARGB;
3219
3220 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
3221 lockeddata.PixelFormat, &lockeddata);
3222
3223 if (stat == Ok)
3224 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
3225
3226 if (stat != Ok)
3227 {
3228 heap_free(src_data);
3229 return stat;
3230 }
3231
3232 apply_image_attributes(imageAttributes, src_data,
3233 src_area.Width, src_area.Height,
3234 src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
3235
3236 if (do_resampling)
3237 {
3238 REAL delta_xx, delta_xy, delta_yx, delta_yy;
3239
3240 /* Transform the bits as needed to the destination. */
3241 dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
3242 if (!dst_data)
3243 {
3244 heap_free(src_data);
3245 return OutOfMemory;
3246 }
3247
3248 dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
3249
3250 GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
3251
3252 x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
3253 x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
3254 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
3255 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
3256
3257 delta_yy = dst_area.top * y_dy;
3258 delta_yx = dst_area.top * y_dx;
3259
3260 for (y=dst_area.top; y<dst_area.bottom; y++)
3261 {
3262 delta_xx = dst_area.left * x_dx;
3263 delta_xy = dst_area.left * x_dy;
3264
3265 for (x=dst_area.left; x<dst_area.right; x++)
3266 {
3267 GpPointF src_pointf;
3268 ARGB *dst_color;
3269
3270 src_pointf.X = dst_to_src_points[0].X + delta_xx + delta_yx;
3271 src_pointf.Y = dst_to_src_points[0].Y + delta_xy + delta_yy;
3272
3273 dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
3274
3275 if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
3276 {
3277 if (lockeddata.PixelFormat != PixelFormat32bppPARGB)
3278 *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3279 imageAttributes, interpolation, offset_mode);
3280 else
3281 *dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3282 imageAttributes, interpolation, offset_mode);
3283 }
3284 else
3285 *dst_color = 0;
3286
3287 delta_xx += x_dx;
3288 delta_yx += y_dx;
3289 }
3290
3291 delta_xy += x_dy;
3292 delta_yy += y_dy;
3293 }
3294 }
3295 else
3296 {
3297 dst_data = src_data;
3298 dst_stride = src_stride;
3299 }
3300
3301 gdi_transform_acquire(graphics);
3302
3303 stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
3304 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
3305 lockeddata.PixelFormat);
3306
3307 gdi_transform_release(graphics);
3308
3309 heap_free(src_data);
3310
3311 heap_free(dst_dyn_data);
3312
3313 return stat;
3314 }
3315 else
3316 {
3317 HDC hdc;
3318 BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
3319 HBITMAP hbitmap, old_hbm=NULL;
3320 HRGN hrgn;
3321 INT save_state;
3322
3323 if (!(bitmap->format == PixelFormat16bppRGB555 ||
3324 bitmap->format == PixelFormat24bppRGB ||
3325 bitmap->format == PixelFormat32bppRGB ||
3326 bitmap->format == PixelFormat32bppPARGB))
3327 {
3328 BITMAPINFOHEADER bih;
3329 BYTE *temp_bits;
3330 PixelFormat dst_format;
3331
3332 /* we can't draw a bitmap of this format directly */
3333 hdc = CreateCompatibleDC(0);
3334 temp_hdc = TRUE;
3335 temp_bitmap = TRUE;
3336
3337 bih.biSize = sizeof(BITMAPINFOHEADER);
3338 bih.biWidth = bitmap->width;
3339 bih.biHeight = -bitmap->height;
3340 bih.biPlanes = 1;
3341 bih.biBitCount = 32;
3342 bih.biCompression = BI_RGB;
3343 bih.biSizeImage = 0;
3344 bih.biXPelsPerMeter = 0;
3345 bih.biYPelsPerMeter = 0;
3346 bih.biClrUsed = 0;
3347 bih.biClrImportant = 0;
3348
3349 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
3350 (void**)&temp_bits, NULL, 0);
3351
3352 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3353 dst_format = PixelFormat32bppPARGB;
3354 else
3355 dst_format = PixelFormat32bppRGB;
3356
3357 convert_pixels(bitmap->width, bitmap->height,
3358 bitmap->width*4, temp_bits, dst_format,
3359 bitmap->stride, bitmap->bits, bitmap->format,
3360 bitmap->image.palette);
3361 }
3362 else
3363 {
3364 if (bitmap->hbitmap)
3365 hbitmap = bitmap->hbitmap;
3366 else
3367 {
3368 GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
3369 temp_bitmap = TRUE;
3370 }
3371
3372 hdc = bitmap->hdc;
3373 temp_hdc = (hdc == 0);
3374 }
3375
3376 if (temp_hdc)
3377 {
3378 if (!hdc) hdc = CreateCompatibleDC(0);
3379 old_hbm = SelectObject(hdc, hbitmap);
3380 }
3381
3382 save_state = SaveDC(graphics->hdc);
3383
3384 stat = get_clip_hrgn(graphics, &hrgn);
3385
3386 if (stat == Ok)
3387 {
3388 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
3389 DeleteObject(hrgn);
3390 }
3391
3392 gdi_transform_acquire(graphics);
3393
3394 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3395 {
3396 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
3397 hdc, srcx, srcy, srcwidth, srcheight);
3398 }
3399 else
3400 {
3401 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
3402 hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
3403 }
3404
3405 gdi_transform_release(graphics);
3406
3407 RestoreDC(graphics->hdc, save_state);
3408
3409 if (temp_hdc)
3410 {
3411 SelectObject(hdc, old_hbm);
3412 DeleteDC(hdc);
3413 }
3414
3415 if (temp_bitmap)
3416 DeleteObject(hbitmap);
3417 }
3418 }
3419 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
3420 {
3421 GpRectF rc;
3422
3423 rc.X = srcx;
3424 rc.Y = srcy;
3425 rc.Width = srcwidth;
3426 rc.Height = srcheight;
3427
3428 return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
3429 points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
3430 }
3431 else
3432 {
3433 WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
3434 return InvalidParameter;
3435 }
3436
3437 return Ok;
3438 }
3439
3440 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
3441 GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
3442 INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3443 DrawImageAbort callback, VOID * callbackData)
3444 {
3445 GpPointF pointsF[3];
3446 INT i;
3447
3448 TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
3449 srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
3450 callbackData);
3451
3452 if(!points || count!=3)
3453 return InvalidParameter;
3454
3455 for(i = 0; i < count; i++){
3456 pointsF[i].X = (REAL)points[i].X;
3457 pointsF[i].Y = (REAL)points[i].Y;
3458 }
3459
3460 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
3461 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
3462 callback, callbackData);
3463 }
3464
3465 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
3466 REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
3467 REAL srcwidth, REAL srcheight, GpUnit srcUnit,
3468 GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
3469 VOID * callbackData)
3470 {
3471 GpPointF points[3];
3472
3473 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
3474 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3475 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3476
3477 points[0].X = dstx;
3478 points[0].Y = dsty;
3479 points[1].X = dstx + dstwidth;
3480 points[1].Y = dsty;
3481 points[2].X = dstx;
3482 points[2].Y = dsty + dstheight;
3483
3484 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3485 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3486 }
3487
3488 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
3489 INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
3490 INT srcwidth, INT srcheight, GpUnit srcUnit,
3491 GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
3492 VOID * callbackData)
3493 {
3494 GpPointF points[3];
3495
3496 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
3497 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3498 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3499
3500 points[0].X = dstx;
3501 points[0].Y = dsty;
3502 points[1].X = dstx + dstwidth;
3503 points[1].Y = dsty;
3504 points[2].X = dstx;
3505 points[2].Y = dsty + dstheight;
3506
3507 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3508 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3509 }
3510
3511 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
3512 REAL x, REAL y, REAL width, REAL height)
3513 {
3514 RectF bounds;
3515 GpUnit unit;
3516 GpStatus ret;
3517
3518 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
3519
3520 if(!graphics || !image)
3521 return InvalidParameter;
3522
3523 ret = GdipGetImageBounds(image, &bounds, &unit);
3524 if(ret != Ok)
3525 return ret;
3526
3527 return GdipDrawImageRectRect(graphics, image, x, y, width, height,
3528 bounds.X, bounds.Y, bounds.Width, bounds.Height,
3529 unit, NULL, NULL, NULL);
3530 }
3531
3532 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
3533 INT x, INT y, INT width, INT height)
3534 {
3535 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
3536
3537 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
3538 }
3539
3540 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
3541 REAL y1, REAL x2, REAL y2)
3542 {
3543 GpPointF pt[2];
3544
3545 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
3546
3547 if (!pen)
3548 return InvalidParameter;
3549
3550 if (pen->unit == UnitPixel && pen->width <= 0.0)
3551 return Ok;
3552
3553 pt[0].X = x1;
3554 pt[0].Y = y1;
3555 pt[1].X = x2;
3556 pt[1].Y = y2;
3557 return GdipDrawLines(graphics, pen, pt, 2);
3558 }
3559
3560 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
3561 INT y1, INT x2, INT y2)
3562 {
3563 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
3564
3565 return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
3566 }
3567
3568 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
3569 GpPointF *points, INT count)
3570 {
3571 GpStatus status;
3572 GpPath *path;
3573
3574 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3575
3576 if(!pen || !graphics || (count < 2))
3577 return InvalidParameter;
3578
3579 if(graphics->busy)
3580 return ObjectBusy;
3581
3582 status = GdipCreatePath(FillModeAlternate, &path);
3583 if (status != Ok) return status;
3584
3585 status = GdipAddPathLine2(path, points, count);
3586 if (status == Ok)
3587 status = GdipDrawPath(graphics, pen, path);
3588
3589 GdipDeletePath(path);
3590 return status;
3591 }
3592
3593 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
3594 GpPoint *points, INT count)
3595 {
3596 GpStatus retval;
3597 GpPointF *ptf;
3598 int i;
3599
3600 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3601
3602 ptf = heap_alloc_zero(count * sizeof(GpPointF));
3603 if(!ptf) return OutOfMemory;
3604
3605 for(i = 0; i < count; i ++){
3606 ptf[i].X = (REAL) points[i].X;
3607 ptf[i].Y = (REAL) points[i].Y;
3608 }
3609
3610 retval = GdipDrawLines(graphics, pen, ptf, count);
3611
3612 heap_free(ptf);
3613 return retval;
3614 }
3615
3616 static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3617 {
3618 INT save_state;
3619 GpStatus retval;
3620 HRGN hrgn=NULL;
3621
3622 save_state = prepare_dc(graphics, pen);
3623
3624 retval = get_clip_hrgn(graphics, &hrgn);
3625
3626 if (retval != Ok)
3627 goto end;
3628
3629 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
3630
3631 gdi_transform_acquire(graphics);
3632
3633 retval = draw_poly(graphics, pen, path->pathdata.Points,
3634 path->pathdata.Types, path->pathdata.Count, TRUE);
3635
3636 gdi_transform_release(graphics);
3637
3638 end:
3639 restore_dc(graphics, save_state);
3640 DeleteObject(hrgn);
3641
3642 return retval;
3643 }
3644
3645 static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3646 {
3647 GpStatus stat;
3648 GpPath* flat_path;
3649 GpMatrix* transform;
3650 GpRectF gp_bound_rect;
3651 GpRect gp_output_area;
3652 RECT output_area;
3653 INT output_height, output_width;
3654 DWORD *output_bits, *brush_bits=NULL;
3655 int i;
3656 static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0};
3657 const BYTE *dash_pattern;
3658 INT dash_pattern_size;
3659 BYTE *dyn_dash_pattern = NULL;
3660
3661 stat = GdipClonePath(path, &flat_path);
3662
3663 if (stat != Ok)
3664 return stat;
3665
3666 stat = GdipCreateMatrix(&transform);
3667
3668 if (stat == Ok)
3669 {
3670 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
3671 CoordinateSpaceWorld, transform);
3672
3673 if (stat == Ok)
3674 stat = GdipFlattenPath(flat_path, transform, 1.0);
3675
3676 GdipDeleteMatrix(transform);
3677 }
3678
3679 /* estimate the output size in pixels, can be larger than necessary */
3680 if (stat == Ok)
3681 {
3682 output_area.left = floorf(flat_path->pathdata.Points[0].X);
3683 output_area.right = ceilf(flat_path->pathdata.Points[0].X);
3684 output_area.top = floorf(flat_path->pathdata.Points[0].Y);
3685 output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y);
3686
3687 for (i=1; i<flat_path->pathdata.Count; i++)
3688 {
3689 REAL x, y;
3690 x = flat_path->pathdata.Points[i].X;
3691 y = flat_path->pathdata.Points[i].Y;
3692
3693 if (floorf(x) < output_area.left) output_area.left = floorf(x);
3694 if (floorf(y) < output_area.top) output_area.top = floorf(y);
3695 if (ceilf(x) > output_area.right) output_area.right = ceilf(x);
3696 if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
3697 }
3698
3699 stat = get_graphics_device_bounds(graphics, &gp_bound_rect);
3700 }
3701
3702 if (stat == Ok)
3703 {
3704 output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
3705 output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
3706 output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
3707 output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
3708
3709 output_width = output_area.right - output_area.left + 1;
3710 output_height = output_area.bottom - output_area.top + 1;
3711
3712 if (output_width <= 0 || output_height <= 0)
3713 {
3714 GdipDeletePath(flat_path);
3715 return Ok;
3716 }
3717
3718 gp_output_area.X = output_area.left;
3719 gp_output_area.Y = output_area.top;
3720 gp_output_area.Width = output_width;
3721 gp_output_area.Height = output_height;
3722
3723 output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
3724 if (!output_bits)
3725 stat = OutOfMemory;
3726 }
3727
3728 if (stat == Ok)
3729 {
3730 if (pen->brush->bt != BrushTypeSolidColor)
3731 {
3732 /* allocate and draw brush output */
3733 brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
3734
3735 if (brush_bits)
3736 {
3737 stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
3738 &gp_output_area, output_width);
3739 }
3740 else
3741 stat = OutOfMemory;
3742 }
3743
3744 if (stat == Ok)
3745 {
3746 /* convert dash pattern to bool array */
3747 switch (pen->dash)
3748 {
3749 case DashStyleCustom:
3750 {
3751 dash_pattern_size = 0;
3752
3753 for (i=0; i < pen->numdashes; i++)
3754 dash_pattern_size += gdip_round(pen->dashes[i]);
3755
3756 if (dash_pattern_size != 0)
3757 {
3758 dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
3759
3760 if (dyn_dash_pattern)
3761 {
3762 int j=0;
3763 for (i=0; i < pen->numdashes; i++)
3764 {
3765 int k;
3766 for (k=0; k < gdip_round(pen->dashes[i]); k++)
3767 dyn_dash_pattern[j++] = (i&1)^1;
3768 }
3769 }
3770 else
3771 stat = OutOfMemory;
3772
3773 break;
3774 }
3775 /* else fall through */
3776 }
3777 case DashStyleSolid:
3778 default:
3779 dash_pattern = static_dash_pattern;
3780 dash_pattern_size = 1;
3781 break;
3782 case DashStyleDash:
3783 dash_pattern = static_dash_pattern;
3784 dash_pattern_size = 4;
3785 break;
3786 case DashStyleDot:
3787 dash_pattern = &static_dash_pattern[4];
3788 dash_pattern_size = 2;
3789 break;
3790 case DashStyleDashDot:
3791 dash_pattern = static_dash_pattern;
3792 dash_pattern_size = 6;
3793 break;
3794 case DashStyleDashDotDot:
3795 dash_pattern = static_dash_pattern;
3796 dash_pattern_size = 8;
3797 break;
3798 }
3799 }
3800
3801 if (stat == Ok)
3802 {
3803 /* trace path */
3804 GpPointF subpath_start = flat_path->pathdata.Points[0];
3805 INT prev_x = INT_MAX, prev_y = INT_MAX;
3806 int dash_pos = dash_pattern_size - 1;
3807
3808 for (i=0; i < flat_path->pathdata.Count; i++)
3809 {
3810 BYTE type, type2;
3811 GpPointF start_point, end_point;
3812 GpPoint start_pointi, end_pointi;
3813
3814 type = flat_path->pathdata.Types[i];
3815 if (i+1 < flat_path->pathdata.Count)
3816 type2 = flat_path->pathdata.Types[i+1];
3817 else
3818 type2 = PathPointTypeStart;
3819
3820 start_point = flat_path->pathdata.Points[i];
3821
3822 if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
3823 subpath_start = start_point;
3824
3825 if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
3826 end_point = subpath_start;
3827 else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
3828 continue;
3829 else
3830 end_point = flat_path->pathdata.Points[i+1];
3831
3832 start_pointi.X = floorf(start_point.X);
3833 start_pointi.Y = floorf(start_point.Y);
3834 end_pointi.X = floorf(end_point.X);
3835 end_pointi.Y = floorf(end_point.Y);
3836
3837 if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
3838 continue;
3839
3840 /* draw line segment */
3841 if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
3842 {
3843 INT x, y, start_y, end_y, step;
3844
3845 if (start_pointi.Y < end_pointi.Y)
3846 {
3847 step = 1;
3848 start_y = ceilf(start_point.Y) - output_area.top;
3849 end_y = end_pointi.Y - output_area.top;
3850 }
3851 else
3852 {
3853 step = -1;
3854 start_y = start_point.Y - output_area.top;
3855 end_y = ceilf(end_point.Y) - output_area.top;
3856 }
3857
3858 for (y=start_y; y != (end_y+step); y+=step)
3859 {
3860 x = gdip_round( start_point.X +
3861 (end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
3862 - output_area.left;
3863
3864 if (x == prev_x && y == prev_y)
3865 continue;
3866
3867 prev_x = x;
3868 prev_y = y;
3869 dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
3870
3871 if (!dash_pattern[dash_pos])
3872 continue;
3873
3874 if (x < 0 || x >= output_width || y < 0 || y >= output_height)
3875 continue;
3876
3877 if (brush_bits)
3878 output_bits[x + y*output_width] = brush_bits[x + y*output_width];
3879 else
3880 output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
3881 }
3882 }
3883 else
3884 {
3885 INT x, y, start_x, end_x, step;
3886
3887 if (start_pointi.X < end_pointi.X)
3888 {
3889 step = 1;
3890 start_x = ceilf(start_point.X) - output_area.left;
3891 end_x = end_pointi.X - output_area.left;
3892 }
3893 else
3894 {
3895 step = -1;
3896 start_x = start_point.X - output_area.left;
3897 end_x = ceilf(end_point.X) - output_area.left;
3898 }
3899
3900 for (x=start_x; x != (end_x+step); x+=step)
3901 {
3902 y = gdip_round( start_point.Y +
3903 (end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
3904 - output_area.top;
3905
3906 if (x == prev_x && y == prev_y)
3907 continue;
3908
3909 prev_x = x;
3910 prev_y = y;
3911 dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
3912
3913 if (!dash_pattern[dash_pos])
3914 continue;
3915
3916 if (x < 0 || x >= output_width || y < 0 || y >= output_height)
3917 continue;
3918
3919 if (brush_bits)
3920 output_bits[x + y*output_width] = brush_bits[x + y*output_width];
3921 else
3922 output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
3923 }
3924 }
3925 }
3926 }
3927
3928 /* draw output image */
3929 if (stat == Ok)
3930 {
3931 gdi_transform_acquire(graphics);
3932
3933 stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
3934 (BYTE*)output_bits, output_width, output_height, output_width * 4,
3935 PixelFormat32bppARGB);
3936
3937 gdi_transform_release(graphics);
3938 }
3939
3940 heap_free(brush_bits);
3941 heap_free(dyn_dash_pattern);
3942 heap_free(output_bits);
3943 }
3944
3945 GdipDeletePath(flat_path);
3946
3947 return stat;
3948 }
3949
3950 static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3951 {
3952 GpStatus stat;
3953 GpPath *wide_path;
3954 GpMatrix *transform=NULL;
3955 REAL flatness=1.0;
3956
3957 /* Check if the final pen thickness in pixels is too thin. */
3958 if (pen->unit == UnitPixel)
3959 {
3960 if (pen->width < 1.415)
3961 return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
3962 }
3963 else
3964 {
3965 GpPointF points[3] = {{0,0}, {1,0}, {0,1}};
3966
3967 points[1].X = pen->width;
3968 points[2].Y = pen->width;
3969
3970 stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice,
3971 CoordinateSpaceWorld, points, 3);
3972
3973 if (stat != Ok)
3974 return stat;
3975
3976 if (((points[1].X-points[0].X)*(points[1].X-points[0].X) +
3977 (points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) &&
3978 ((points[2].X-points[0].X)*(points[2].X-points[0].X) +
3979 (points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001))
3980 return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
3981 }
3982
3983 stat = GdipClonePath(path, &wide_path);
3984
3985 if (stat != Ok)
3986 return stat;
3987
3988 if (pen->unit == UnitPixel)
3989 {
3990 /* We have to transform this to device coordinates to get the widths right. */
3991 stat = GdipCreateMatrix(&transform);
3992
3993 if (stat == Ok)
3994 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
3995 CoordinateSpaceWorld, transform);
3996 }
3997 else
3998 {
3999 /* Set flatness based on the final coordinate space */
4000 GpMatrix t;
4001
4002 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
4003 CoordinateSpaceWorld, &t);
4004
4005 if (stat != Ok)
4006 return stat;
4007
4008 flatness = 1.0/sqrt(fmax(
4009 t.matrix[0] * t.matrix[0] + t.matrix[1] * t.matrix[1],
4010 t.matrix[2] * t.matrix[2] + t.matrix[3] * t.matrix[3]));
4011 }
4012
4013 if (stat == Ok)
4014 stat = GdipWidenPath(wide_path, pen, transform, flatness);
4015
4016 if (pen->unit == UnitPixel)
4017 {
4018 /* Transform the path back to world coordinates */
4019 if (stat == Ok)
4020 stat = GdipInvertMatrix(transform);
4021
4022 if (stat == Ok)
4023 stat = GdipTransformPath(wide_path, transform);
4024 }
4025
4026 /* Actually draw the path */
4027 if (stat == Ok)
4028 stat = GdipFillPath(graphics, pen->brush, wide_path);
4029
4030 GdipDeleteMatrix(transform);
4031
4032 GdipDeletePath(wide_path);
4033
4034 return stat;
4035 }
4036
4037 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
4038 {
4039 GpStatus retval;
4040
4041 TRACE("(%p, %p, %p)\n", graphics, pen, path);
4042
4043 if(!pen || !graphics)
4044 return InvalidParameter;
4045
4046 if(graphics->busy)
4047 return ObjectBusy;
4048
4049 if (path->pathdata.Count == 0)
4050 return Ok;
4051
4052 if (graphics->image && graphics->image->type == ImageTypeMetafile)
4053 retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path);
4054 else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE))
4055 retval = SOFTWARE_GdipDrawPath(graphics, pen, path);
4056 else
4057 retval = GDI32_GdipDrawPath(graphics, pen, path);
4058
4059 return retval;
4060 }
4061
4062 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
4063 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
4064 {
4065 GpStatus status;
4066 GpPath *path;
4067
4068 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
4069 width, height, startAngle, sweepAngle);
4070
4071 if(!graphics || !pen)
4072 return InvalidParameter;
4073
4074 if(graphics->busy)
4075 return ObjectBusy;
4076
4077 status = GdipCreatePath(FillModeAlternate, &path);
4078 if (status != Ok) return status;
4079
4080 status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
4081 if (status == Ok)
4082 status = GdipDrawPath(graphics, pen, path);
4083
4084 GdipDeletePath(path);
4085 return status;
4086 }
4087
4088 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
4089 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
4090 {
4091 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
4092 width, height, startAngle, sweepAngle);
4093
4094 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
4095 }
4096
4097 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
4098 REAL y, REAL width, REAL height)
4099 {
4100 GpStatus status;
4101 GpPath *path;
4102
4103 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
4104
4105 if(!pen || !graphics)
4106 return InvalidParameter;
4107
4108 if(graphics->busy)
4109 return ObjectBusy;
4110
4111 status = GdipCreatePath(FillModeAlternate, &path);
4112 if (status != Ok) return status;
4113
4114 status = GdipAddPathRectangle(path, x, y, width, height);
4115 if (status == Ok)
4116 status = GdipDrawPath(graphics, pen, path);
4117
4118 GdipDeletePath(path);
4119 return status;
4120 }
4121
4122 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
4123 INT y, INT width, INT height)
4124 {
4125 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
4126
4127 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
4128 }
4129
4130 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
4131 GDIPCONST GpRectF* rects, INT count)
4132 {
4133 GpStatus status;
4134 GpPath *path;
4135
4136 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
4137
4138 if(!graphics || !pen || !rects || count < 1)
4139 return InvalidParameter;
4140
4141 if(graphics->busy)
4142 return ObjectBusy;
4143
4144 status = GdipCreatePath(FillModeAlternate, &path);
4145 if (status != Ok) return status;
4146
4147 status = GdipAddPathRectangles(path, rects, count);
4148 if (status == Ok)
4149 status = GdipDrawPath(graphics, pen, path);
4150
4151 GdipDeletePath(path);
4152 return status;
4153 }
4154
4155 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
4156 GDIPCONST GpRect* rects, INT count)
4157 {
4158 GpRectF *rectsF;
4159 GpStatus ret;
4160 INT i;
4161
4162 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
4163
4164 if(!rects || count<=0)
4165 return InvalidParameter;
4166
4167 rectsF = heap_alloc_zero(sizeof(GpRectF) * count);
4168 if(!rectsF)
4169 return OutOfMemory;
4170
4171 for(i = 0;i < count;i++){
4172 rectsF[i].X = (REAL)rects[i].X;
4173 rectsF[i].Y = (REAL)rects[i].Y;
4174 rectsF[i].Width = (REAL)rects[i].Width;
4175 rectsF[i].Height = (REAL)rects[i].Height;
4176 }
4177
4178 ret = GdipDrawRectangles(graphics, pen, rectsF, count);
4179 heap_free(rectsF);
4180
4181 return ret;
4182 }
4183
4184 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
4185 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
4186 {
4187 GpPath *path;
4188 GpStatus status;
4189
4190 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
4191 count, tension, fill);
4192
4193 if(!graphics || !brush || !points)
4194 return InvalidParameter;
4195
4196 if(graphics->busy)
4197 return ObjectBusy;
4198
4199 if(count == 1) /* Do nothing */
4200 return Ok;
4201
4202 status = GdipCreatePath(fill, &path);
4203 if (status != Ok) return status;
4204
4205 status = GdipAddPathClosedCurve2(path, points, count, tension);
4206 if (status == Ok)
4207 status = GdipFillPath(graphics, brush, path);
4208
4209 GdipDeletePath(path);
4210 return status;
4211 }
4212
4213 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
4214 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
4215 {
4216 GpPointF *ptf;
4217 GpStatus stat;
4218 INT i;
4219
4220 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
4221 count, tension, fill);
4222
4223 if(!points || count == 0)
4224 return InvalidParameter;
4225
4226 if(count == 1) /* Do nothing */
4227 return Ok;
4228
4229 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
4230 if(!ptf)
4231 return OutOfMemory;
4232
4233 for(i = 0;i < count;i++){
4234 ptf[i].X = (REAL)points[i].X;
4235 ptf[i].Y = (REAL)points[i].Y;
4236 }
4237
4238 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
4239
4240 heap_free(ptf);
4241
4242 return stat;
4243 }
4244
4245 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
4246 GDIPCONST GpPointF *points, INT count)
4247 {
4248 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4249 return GdipFillClosedCurve2(graphics, brush, points, count,
4250 0.5f, FillModeAlternate);
4251 }
4252
4253 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
4254 GDIPCONST GpPoint *points, INT count)
4255 {
4256 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4257 return GdipFillClosedCurve2I(graphics, brush, points, count,
4258 0.5f, FillModeAlternate);
4259 }
4260
4261 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
4262 REAL y, REAL width, REAL height)
4263 {
4264 GpStatus stat;
4265 GpPath *path;
4266
4267 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
4268
4269 if(!graphics || !brush)
4270 return InvalidParameter;
4271
4272 if(graphics->busy)
4273 return ObjectBusy;
4274
4275 stat = GdipCreatePath(FillModeAlternate, &path);
4276
4277 if (stat == Ok)
4278 {
4279 stat = GdipAddPathEllipse(path, x, y, width, height);
4280
4281 if (stat == Ok)
4282 stat = GdipFillPath(graphics, brush, path);
4283
4284 GdipDeletePath(path);
4285 }
4286
4287 return stat;
4288 }
4289
4290 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
4291 INT y, INT width, INT height)
4292 {
4293 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
4294
4295 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
4296 }
4297
4298 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
4299 {
4300 INT save_state;
4301 GpStatus retval;
4302 HRGN hrgn=NULL;
4303
4304 if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
4305 return NotImplemented;
4306
4307 save_state = SaveDC(graphics->hdc);
4308 EndPath(graphics->hdc);
4309 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
4310 : WINDING));
4311
4312 retval = get_clip_hrgn(graphics, &hrgn);
4313
4314 if (retval != Ok)
4315 goto end;
4316
4317 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
4318
4319 gdi_transform_acquire(graphics);
4320
4321 BeginPath(graphics->hdc);
4322 retval = draw_poly(graphics, NULL, path->pathdata.Points,
4323 path->pathdata.Types, path->pathdata.Count, FALSE);
4324
4325 if(retval == Ok)
4326 {
4327 EndPath(graphics->hdc);
4328 brush_fill_path(graphics, brush);
4329 }
4330
4331 gdi_transform_release(graphics);
4332
4333 end:
4334 RestoreDC(graphics->hdc, save_state);
4335 DeleteObject(hrgn);
4336
4337 return retval;
4338 }
4339
4340 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
4341 {
4342 GpStatus stat;
4343 GpRegion *rgn;
4344
4345 if (!brush_can_fill_pixels(brush))
4346 return NotImplemented;
4347
4348 /* FIXME: This could probably be done more efficiently without regions. */
4349
4350 stat = GdipCreateRegionPath(path, &rgn);
4351
4352 if (stat == Ok)
4353 {
4354 stat = GdipFillRegion(graphics, brush, rgn);
4355
4356 GdipDeleteRegion(rgn);
4357 }
4358
4359 return stat;
4360 }
4361
4362 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
4363 {
4364 GpStatus stat = NotImplemented;
4365
4366 TRACE("(%p, %p, %p)\n", graphics, brush, path);
4367
4368 if(!brush || !graphics || !path)
4369 return InvalidParameter;
4370
4371 if(graphics->busy)
4372 return ObjectBusy;
4373
4374 if (graphics->image && graphics->image->type == ImageTypeMetafile)
4375 return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path);
4376
4377 if (!graphics->image && !graphics->alpha_hdc)
4378 stat = GDI32_GdipFillPath(graphics, brush, path);
4379
4380 if (stat == NotImplemented)
4381 stat = SOFTWARE_GdipFillPath(graphics, brush, path);
4382
4383 if (stat == NotImplemented)
4384 {
4385 FIXME("Not implemented for brushtype %i\n", brush->bt);
4386 stat = Ok;
4387 }
4388
4389 return stat;
4390 }
4391
4392 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
4393 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
4394 {
4395 GpStatus stat;
4396 GpPath *path;
4397
4398 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
4399 graphics, brush, x, y, width, height, startAngle, sweepAngle);
4400
4401 if(!graphics || !brush)
4402 return InvalidParameter;
4403
4404 if(graphics->busy)
4405 return ObjectBusy;
4406
4407 stat = GdipCreatePath(FillModeAlternate, &path);
4408
4409 if (stat == Ok)
4410 {
4411 stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
4412
4413 if (stat == Ok)
4414 stat = GdipFillPath(graphics, brush, path);
4415
4416 GdipDeletePath(path);
4417 }
4418
4419 return stat;
4420 }
4421
4422 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
4423 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
4424 {
4425 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
4426 graphics, brush, x, y, width, height, startAngle, sweepAngle);
4427
4428 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
4429 }
4430
4431 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
4432 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
4433 {
4434 GpStatus stat;
4435 GpPath *path;
4436
4437 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
4438
4439 if(!graphics || !brush || !points || !count)
4440 return InvalidParameter;
4441
4442 if(graphics->busy)
4443 return ObjectBusy;
4444
4445 stat = GdipCreatePath(fillMode, &path);
4446
4447 if (stat == Ok)
4448 {
4449 stat = GdipAddPathPolygon(path, points, count);
4450
4451 if (stat == Ok)
4452 stat = GdipFillPath(graphics, brush, path);
4453
4454 GdipDeletePath(path);
4455 }
4456
4457 return stat;
4458 }
4459
4460 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
4461 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
4462 {
4463 GpStatus stat;
4464 GpPath *path;
4465
4466 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
4467
4468 if(!graphics || !brush || !points || !count)
4469 return InvalidParameter;
4470
4471 if(graphics->busy)
4472 return ObjectBusy;
4473
4474 stat = GdipCreatePath(fillMode, &path);
4475
4476 if (stat == Ok)
4477 {
4478 stat = GdipAddPathPolygonI(path, points, count);
4479
4480 if (stat == Ok)
4481 stat = GdipFillPath(graphics, brush, path);
4482
4483 GdipDeletePath(path);
4484 }
4485
4486 return stat;
4487 }
4488
4489 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
4490 GDIPCONST GpPointF *points, INT count)
4491 {
4492 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4493
4494 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
4495 }
4496
4497 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
4498 GDIPCONST GpPoint *points, INT count)
4499 {
4500 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4501
4502 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
4503 }
4504
4505 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
4506 REAL x, REAL y, REAL width, REAL height)
4507 {
4508 GpRectF rect;
4509
4510 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
4511
4512 rect.X = x;
4513 rect.Y = y;
4514 rect.Width = width;
4515 rect.Height = height;
4516
4517 return GdipFillRectangles(graphics, brush, &rect, 1);
4518 }
4519
4520 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
4521 INT x, INT y, INT width, INT height)
4522 {
4523 GpRectF rect;
4524
4525 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
4526
4527 rect.X = (REAL)x;
4528 rect.Y = (REAL)y;
4529 rect.Width = (REAL)width;
4530 rect.Height = (REAL)height;
4531
4532 return GdipFillRectangles(graphics, brush, &rect, 1);
4533 }
4534
4535 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
4536 INT count)
4537 {
4538 GpStatus status;
4539 GpPath *path;
4540
4541 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
4542
4543 if(!graphics || !brush || !rects || count <= 0)
4544 return InvalidParameter;
4545
4546 if (graphics->image && graphics->image->type == ImageTypeMetafile)
4547 {
4548 status = METAFILE_FillRectangles((GpMetafile*)graphics->image, brush, rects, count);
4549 /* FIXME: Add gdi32 drawing. */
4550 return status;
4551 }
4552
4553 status = GdipCreatePath(FillModeAlternate, &path);
4554 if (status != Ok) return status;
4555
4556 status = GdipAddPathRectangles(path, rects, count);
4557 if (status == Ok)
4558 status = GdipFillPath(graphics, brush, path);
4559
4560 GdipDeletePath(path);
4561 return status;
4562 }
4563
4564 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
4565 INT count)
4566 {
4567 GpRectF *rectsF;
4568 GpStatus ret;
4569 INT i;
4570
4571 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
4572
4573 if(!rects || count <= 0)
4574 return InvalidParameter;
4575
4576 rectsF = heap_alloc_zero(sizeof(GpRectF)*count);
4577 if(!rectsF)
4578 return OutOfMemory;
4579
4580 for(i = 0; i < count; i++){
4581 rectsF[i].X = (REAL)rects[i].X;
4582 rectsF[i].Y = (REAL)rects[i].Y;
4583 rectsF[i].Width = (REAL)rects[i].Width;
4584 rectsF[i].Height = (REAL)rects[i].Height;
4585 }
4586
4587 ret = GdipFillRectangles(graphics,brush,rectsF,count);
4588 heap_free(rectsF);
4589
4590 return ret;
4591 }
4592
4593 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
4594 GpRegion* region)
4595 {
4596 INT save_state;
4597 GpStatus status;
4598 HRGN hrgn;
4599 RECT rc;
4600
4601 if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
4602 return NotImplemented;
4603
4604 save_state = SaveDC(graphics->hdc);
4605 EndPath(graphics->hdc);
4606
4607 hrgn = NULL;
4608 status = get_clip_hrgn(graphics, &hrgn);
4609 if (status != Ok)
4610 {
4611 RestoreDC(graphics->hdc, save_state);
4612 return status;
4613 }
4614
4615 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
4616 DeleteObject(hrgn);
4617
4618 status = GdipGetRegionHRgn(region, graphics, &hrgn);
4619 if (status != Ok)
4620 {
4621 RestoreDC(graphics->hdc, save_state);
4622 return status;
4623 }
4624
4625 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
4626 DeleteObject(hrgn);
4627
4628 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
4629 {
4630 BeginPath(graphics->hdc);
4631 Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
4632 EndPath(graphics->hdc);
4633
4634 brush_fill_path(graphics, brush);
4635 }
4636
4637 RestoreDC(graphics->hdc, save_state);
4638
4639
4640 return Ok;
4641 }
4642
4643 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
4644 GpRegion* region)
4645 {
4646 GpStatus stat;
4647 GpRegion *temp_region;
4648 GpMatrix world_to_device;
4649 GpRectF graphics_bounds;
4650 DWORD *pixel_data;
4651 HRGN hregion;
4652 RECT bound_rect;
4653 GpRect gp_bound_rect;
4654
4655 if (!brush_can_fill_pixels(brush))
4656 return NotImplemented;
4657
4658 stat = gdi_transform_acquire(graphics);
4659
4660 if (stat == Ok)
4661 stat = get_graphics_device_bounds(graphics, &graphics_bounds);
4662
4663 if (stat == Ok)
4664 stat = GdipCloneRegion(region, &temp_region);
4665
4666 if (stat == Ok)
4667 {
4668 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
4669 CoordinateSpaceWorld, &world_to_device);
4670
4671 if (stat == Ok)
4672 stat = GdipTransformRegion(temp_region, &world_to_device);
4673
4674 if (stat == Ok)
4675 stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
4676
4677 if (stat == Ok)
4678 stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
4679
4680 GdipDeleteRegion(temp_region);
4681 }
4682
4683 if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
4684 {
4685 DeleteObject(hregion);
4686 gdi_transform_release(graphics);
4687 return Ok;
4688 }
4689
4690 if (stat == Ok)
4691 {
4692 gp_bound_rect.X = bound_rect.left;
4693 gp_bound_rect.Y = bound_rect.top;
4694 gp_bound_rect.Width = bound_rect.right - bound_rect.left;
4695 gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
4696
4697 pixel_data = heap_alloc_zero(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
4698 if (!pixel_data)
4699 stat = OutOfMemory;
4700
4701 if (stat == Ok)
4702 {
4703 stat = brush_fill_pixels(graphics, brush, pixel_data,
4704 &gp_bound_rect, gp_bound_rect.Width);
4705
4706 if (stat == Ok)
4707 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
4708 gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
4709 gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion,
4710 PixelFormat32bppARGB);
4711
4712 heap_free(pixel_data);
4713 }
4714
4715 DeleteObject(hregion);
4716 }
4717
4718 gdi_transform_release(graphics);
4719
4720 return stat;
4721 }
4722
4723 /*****************************************************************************
4724 * GdipFillRegion [GDIPLUS.@]
4725 */
4726 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
4727 GpRegion* region)
4728 {
4729 GpStatus stat = NotImplemented;
4730
4731 TRACE("(%p, %p, %p)\n", graphics, brush, region);
4732
4733 if (!(graphics && brush && region))
4734 return InvalidParameter;
4735
4736 if(graphics->busy)
4737 return ObjectBusy;
4738
4739 if (!graphics->image && !graphics->alpha_hdc)
4740 stat = GDI32_GdipFillRegion(graphics, brush, region);
4741
4742 if (stat == NotImplemented)
4743 stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
4744
4745 if (stat == NotImplemented)
4746 {
4747 FIXME("not implemented for brushtype %i\n", brush->bt);
4748 stat = Ok;
4749 }
4750
4751 return stat;
4752 }
4753
4754 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
4755 {
4756 TRACE("(%p,%u)\n", graphics, intention);
4757
4758 if(!graphics)
4759 return InvalidParameter;
4760
4761 if(graphics->busy)
4762 return ObjectBusy;
4763
4764 /* We have no internal operation queue, so there's no need to clear it. */
4765
4766 if (graphics->hdc)
4767 GdiFlush();
4768
4769 return Ok;
4770 }
4771
4772 /*****************************************************************************
4773 * GdipGetClipBounds [GDIPLUS.@]
4774 */
4775 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
4776 {
4777 GpStatus status;
4778 GpRegion *clip;
4779
4780 TRACE("(%p, %p)\n", graphics, rect);
4781
4782 if(!graphics)
4783 return InvalidParameter;
4784
4785 if(graphics->busy)
4786 return ObjectBusy;
4787
4788 status = GdipCreateRegion(&clip);
4789 if (status != Ok) return status;
4790
4791 status = GdipGetClip(graphics, clip);
4792 if (status == Ok)
4793 status = GdipGetRegionBounds(clip, graphics, rect);
4794
4795 GdipDeleteRegion(clip);
4796 return status;
4797 }
4798
4799 /*****************************************************************************
4800 * GdipGetClipBoundsI [GDIPLUS.@]
4801 */
4802 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
4803 {
4804 TRACE("(%p, %p)\n", graphics, rect);
4805
4806 if(!graphics)
4807 return InvalidParameter;
4808
4809 if(graphics->busy)
4810 return ObjectBusy;
4811
4812 return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
4813 }
4814
4815 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
4816 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
4817 CompositingMode *mode)
4818 {
4819 TRACE("(%p, %p)\n", graphics, mode);
4820
4821 if(!graphics || !mode)
4822 return InvalidParameter;
4823
4824 if(graphics->busy)
4825 return ObjectBusy;
4826
4827 *mode = graphics->compmode;
4828
4829 return Ok;
4830 }
4831
4832 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
4833 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
4834 CompositingQuality *quality)
4835 {
4836 TRACE("(%p, %p)\n", graphics, quality);
4837
4838 if(!graphics || !quality)
4839 return InvalidParameter;
4840
4841 if(graphics->busy)
4842 return ObjectBusy;
4843
4844 *quality = graphics->compqual;
4845
4846 return Ok;
4847 }
4848
4849 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
4850 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
4851 InterpolationMode *mode)
4852 {
4853 TRACE("(%p, %p)\n", graphics, mode);
4854
4855 if(!graphics || !mode)
4856 return InvalidParameter;
4857
4858 if(graphics->busy)
4859 return ObjectBusy;
4860
4861 *mode = graphics->interpolation;
4862
4863 return Ok;
4864 }
4865
4866 /* FIXME: Need to handle color depths less than 24bpp */
4867 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
4868 {
4869 FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
4870
4871 if(!graphics || !argb)
4872 return InvalidParameter;
4873
4874 if(graphics->busy)
4875 return ObjectBusy;
4876
4877 return Ok;
4878 }
4879
4880 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
4881 {
4882 TRACE("(%p, %p)\n", graphics, scale);
4883
4884 if(!graphics || !scale)
4885 return InvalidParameter;
4886
4887 if(graphics->busy)
4888 return ObjectBusy;
4889
4890 *scale = graphics->scale;
4891
4892 return Ok;
4893 }
4894
4895 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
4896 {
4897 TRACE("(%p, %p)\n", graphics, unit);
4898
4899 if(!graphics || !unit)
4900 return InvalidParameter;
4901
4902 if(graphics->busy)
4903 return ObjectBusy;
4904
4905 *unit = graphics->unit;
4906
4907 return Ok;
4908 }
4909
4910 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
4911 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4912 *mode)
4913 {
4914 TRACE("(%p, %p)\n", graphics, mode);
4915
4916 if(!graphics || !mode)
4917 return InvalidParameter;
4918
4919 if(graphics->busy)
4920 return ObjectBusy;
4921
4922 *mode = graphics->pixeloffset;
4923
4924 return Ok;
4925 }
4926
4927 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
4928 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
4929 {
4930 TRACE("(%p, %p)\n", graphics, mode);
4931
4932 if(!graphics || !mode)
4933 return InvalidParameter;
4934
4935 if(graphics->busy)
4936 return ObjectBusy;
4937
4938 *mode = graphics->smoothing;
4939
4940 return Ok;
4941 }
4942
4943 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
4944 {
4945 TRACE("(%p, %p)\n", graphics, contrast);
4946
4947 if(!graphics || !contrast)
4948 return InvalidParameter;
4949
4950 *contrast = graphics->textcontrast;
4951
4952 return Ok;
4953 }
4954
4955 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
4956 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
4957 TextRenderingHint *hint)
4958 {
4959 TRACE("(%p, %p)\n", graphics, hint);
4960
4961 if(!graphics || !hint)
4962 return InvalidParameter;
4963
4964 if(graphics->busy)
4965 return ObjectBusy;
4966
4967 *hint = graphics->texthint;
4968
4969 return Ok;
4970 }
4971
4972 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
4973 {
4974 GpRegion *clip_rgn;
4975 GpStatus stat;
4976 GpMatrix device_to_world;
4977
4978 TRACE("(%p, %p)\n", graphics, rect);
4979
4980 if(!graphics || !rect)
4981 return InvalidParameter;
4982
4983 if(graphics->busy)
4984 return ObjectBusy;
4985
4986 /* intersect window and graphics clipping regions */
4987 if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
4988 return stat;
4989
4990 if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
4991 goto cleanup;
4992
4993 /* transform to world coordinates */
4994 if((stat = get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world)) != Ok)
4995 goto cleanup;
4996
4997 if((stat = GdipTransformRegion(clip_rgn, &device_to_world)) != Ok)
4998 goto cleanup;
4999
5000 /* get bounds of the region */
5001 stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
5002
5003 cleanup:
5004 GdipDeleteRegion(clip_rgn);
5005
5006 return stat;
5007 }
5008
5009 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
5010 {
5011 GpRectF rectf;
5012 GpStatus stat;
5013
5014 TRACE("(%p, %p)\n", graphics, rect);
5015
5016 if(!graphics || !rect)
5017 return InvalidParameter;
5018
5019 if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
5020 {
5021 rect->X = gdip_round(rectf.X);
5022 rect->Y = gdip_round(rectf.Y);
5023 rect->Width = gdip_round(rectf.Width);
5024 rect->Height = gdip_round(rectf.Height);
5025 }
5026
5027 return stat;
5028 }
5029
5030 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
5031 {
5032 TRACE("(%p, %p)\n", graphics, matrix);
5033
5034 if(!graphics || !matrix)
5035 return InvalidParameter;
5036
5037 if(graphics->busy)
5038 return ObjectBusy;
5039
5040 *matrix = graphics->worldtrans;
5041 return Ok;
5042 }
5043
5044 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
5045 {
5046 GpSolidFill *brush;
5047 GpStatus stat;
5048 GpRectF wnd_rect;
5049
5050 TRACE("(%p, %x)\n", graphics, color);
5051
5052 if(!graphics)
5053 return InvalidParameter;
5054
5055 if(graphics->busy)
5056 return ObjectBusy;
5057
5058 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5059 return METAFILE_GraphicsClear((GpMetafile*)graphics->image, color);
5060
5061 if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
5062 return stat;
5063
5064 if((stat = GdipGetVisibleClipBounds(graphics, &wnd_rect)) != Ok){
5065 GdipDeleteBrush((GpBrush*)brush);
5066 return stat;
5067 }
5068
5069 GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
5070 wnd_rect.Width, wnd_rect.Height);
5071
5072 GdipDeleteBrush((GpBrush*)brush);
5073
5074 return Ok;
5075 }
5076
5077 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
5078 {
5079 TRACE("(%p, %p)\n", graphics, res);
5080
5081 if(!graphics || !res)
5082 return InvalidParameter;
5083
5084 return GdipIsEmptyRegion(graphics->clip, graphics, res);
5085 }
5086
5087 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
5088 {
5089 GpStatus stat;
5090 GpRegion* rgn;
5091 GpPointF pt;
5092
5093 TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
5094
5095 if(!graphics || !result)
5096 return InvalidParameter;
5097
5098 if(graphics->busy)
5099 return ObjectBusy;
5100
5101 pt.X = x;
5102 pt.Y = y;
5103 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
5104 CoordinateSpaceWorld, &pt, 1)) != Ok)
5105 return stat;
5106
5107 if((stat = GdipCreateRegion(&rgn)) != Ok)
5108 return stat;
5109
5110 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
5111 goto cleanup;
5112
5113 stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
5114
5115 cleanup:
5116 GdipDeleteRegion(rgn);
5117 return stat;
5118 }
5119
5120 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
5121 {
5122 return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
5123 }
5124
5125 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
5126 {
5127 GpStatus stat;
5128 GpRegion* rgn;
5129 GpPointF pts[2];
5130
5131 TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
5132
5133 if(!graphics || !result)
5134 return InvalidParameter;
5135
5136 if(graphics->busy)
5137 return ObjectBusy;
5138
5139 pts[0].X = x;
5140 pts[0].Y = y;
5141 pts[1].X = x + width;
5142 pts[1].Y = y + height;
5143
5144 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
5145 CoordinateSpaceWorld, pts, 2)) != Ok)
5146 return stat;
5147
5148 pts[1].X -= pts[0].X;
5149 pts[1].Y -= pts[0].Y;
5150
5151 if((stat = GdipCreateRegion(&rgn)) != Ok)
5152 return stat;
5153
5154 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
5155 goto cleanup;
5156
5157 stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
5158
5159 cleanup:
5160 GdipDeleteRegion(rgn);
5161 return stat;
5162 }
5163
5164 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
5165 {
5166 return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
5167 }
5168
5169 GpStatus gdip_format_string(HDC hdc,
5170 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
5171 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
5172 gdip_format_string_callback callback, void *user_data)
5173 {
5174 WCHAR* stringdup;
5175 int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
5176 nheight, lineend, lineno = 0;
5177 RectF bounds;
5178 StringAlignment halign;
5179 GpStatus stat = Ok;
5180 SIZE size;
5181 HotkeyPrefix hkprefix;
5182 INT *hotkeyprefix_offsets=NULL;
5183 INT hotkeyprefix_count=0;
5184 INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
5185 BOOL seen_prefix = FALSE;
5186
5187 if(length == -1) length = lstrlenW(string);
5188
5189 stringdup = heap_alloc_zero((length + 1) * sizeof(WCHAR));
5190 if(!stringdup) return OutOfMemory;
5191
5192 if (!format)
5193 format = &default_drawstring_format;
5194
5195 nwidth = rect->Width;
5196 nheight = rect->Height;
5197 if (ignore_empty_clip)
5198 {
5199 if (!nwidth) nwidth = INT_MAX;
5200 if (!nheight) nheight = INT_MAX;
5201 }
5202
5203 hkprefix = format->hkprefix;
5204
5205 if (hkprefix == HotkeyPrefixShow)
5206 {
5207 for (i=0; i<length; i++)
5208 {
5209 if (string[i] == '&')
5210 hotkeyprefix_count++;
5211 }
5212 }
5213
5214 if (hotkeyprefix_count)
5215 hotkeyprefix_offsets = heap_alloc_zero(sizeof(INT) * hotkeyprefix_count);
5216
5217 hotkeyprefix_count = 0;
5218
5219 for(i = 0, j = 0; i < length; i++){
5220 /* FIXME: This makes the indexes passed to callback inaccurate. */
5221 if(!isprintW(string[i]) && (string[i] != '\n'))
5222 continue;
5223
5224 /* FIXME: tabs should be handled using tabstops from stringformat */
5225 if (string[i] == '\t')
5226 continue;
5227
5228 if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
5229 hotkeyprefix_offsets[hotkeyprefix_count++] = j;
5230 else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
5231 {
5232 seen_prefix = TRUE;
5233 continue;
5234 }
5235
5236 seen_prefix = FALSE;
5237
5238 stringdup[j] = string[i];
5239 j++;
5240 }
5241
5242 length = j;
5243
5244 halign = format->align;
5245
5246 while(sum < length){
5247 GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
5248 nwidth, &fit, NULL, &size);
5249 fitcpy = fit;
5250
5251 if(fit == 0)
5252 break;
5253
5254 for(lret = 0; lret < fit; lret++)
5255 if(*(stringdup + sum + lret) == '\n')
5256 break;
5257
5258 /* Line break code (may look strange, but it imitates windows). */
5259 if(lret < fit)
5260 lineend = fit = lret; /* this is not an off-by-one error */
5261 else if(fit < (length - sum)){
5262 if(*(stringdup + sum + fit) == ' ')
5263 while(*(stringdup + sum + fit) == ' ')
5264 fit++;
5265 else
5266 while(*(stringdup + sum + fit - 1) != ' '){
5267 fit--;
5268
5269 if(*(stringdup + sum + fit) == '\t')
5270 break;
5271
5272 if(fit == 0){
5273 fit = fitcpy;
5274 break;
5275 }
5276 }
5277 lineend = fit;
5278 while(*(stringdup + sum + lineend - 1) == ' ' ||
5279 *(stringdup + sum + lineend - 1) == '\t')
5280 lineend--;
5281 }
5282 else
5283 lineend = fit;
5284
5285 GetTextExtentExPointW(hdc, stringdup + sum, lineend,
5286 nwidth, &j, NULL, &size);
5287
5288 bounds.Width = size.cx;
5289
5290 if(height + size.cy > nheight)
5291 {
5292 if (format->attr & StringFormatFlagsLineLimit)
5293 break;
5294 bounds.Height = nheight - (height + size.cy);
5295 }
5296 else
5297 bounds.Height = size.cy;
5298
5299 bounds.Y = rect->Y + height;
5300
5301 switch (halign)
5302 {
5303 case StringAlignmentNear:
5304 default:
5305 bounds.X = rect->X;
5306 break;
5307 case StringAlignmentCenter:
5308 bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
5309 break;
5310 case StringAlignmentFar:
5311 bounds.X = rect->X + rect->Width - bounds.Width;
5312 break;
5313 }
5314
5315 for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
5316 if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
5317 break;
5318
5319 stat = callback(hdc, stringdup, sum, lineend,
5320 font, rect, format, lineno, &bounds,
5321 &hotkeyprefix_offsets[hotkeyprefix_pos],
5322 hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
5323
5324 if (stat != Ok)
5325 break;
5326
5327 sum += fit + (lret < fitcpy ? 1 : 0);
5328 height += size.cy;
5329 lineno++;
5330
5331 hotkeyprefix_pos = hotkeyprefix_end_pos;
5332
5333 if(height > nheight)
5334 break;
5335
5336 /* Stop if this was a linewrap (but not if it was a linebreak). */
5337 if ((lret == fitcpy) && (format->attr & StringFormatFlagsNoWrap))
5338 break;
5339 }
5340
5341 heap_free(stringdup);
5342 heap_free(hotkeyprefix_offsets);
5343
5344 return stat;
5345 }
5346
5347 struct measure_ranges_args {
5348 GpRegion **regions;
5349 REAL rel_width, rel_height;
5350 };
5351
5352 static GpStatus measure_ranges_callback(HDC hdc,
5353 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
5354 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
5355 INT lineno, const RectF *bounds, INT *underlined_indexes,
5356 INT underlined_index_count, void *user_data)
5357 {
5358 int i;
5359 GpStatus stat = Ok;
5360 struct measure_ranges_args *args = user_data;
5361
5362 for (i=0; i<format->range_count; i++)
5363 {
5364 INT range_start = max(index, format->character_ranges[i].First);
5365 INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
5366 if (range_start < range_end)
5367 {
5368 GpRectF range_rect;
5369 SIZE range_size;
5370
5371 range_rect.Y = bounds->Y / args->rel_height;
5372 range_rect.Height = bounds->Height / args->rel_height;
5373
5374 GetTextExtentExPointW(hdc, string + index, range_start - index,
5375 INT_MAX, NULL, NULL, &range_size);
5376 range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
5377
5378 GetTextExtentExPointW(hdc, string + index, range_end - index,
5379 INT_MAX, NULL, NULL, &range_size);
5380 range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
5381
5382 stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
5383 if (stat != Ok)
5384 break;
5385 }
5386 }
5387
5388 return stat;
5389 }
5390
5391 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
5392 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
5393 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
5394 INT regionCount, GpRegion** regions)
5395 {
5396 GpStatus stat;
5397 int i;
5398 HFONT gdifont, oldfont;
5399 struct measure_ranges_args args;
5400 HDC hdc, temp_hdc=NULL;
5401 GpPointF pt[3];
5402 RectF scaled_rect;
5403 REAL margin_x;
5404
5405 TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
5406 length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
5407
5408 if (!(graphics && string && font && layoutRect && stringFormat && regions))
5409 return InvalidParameter;
5410
5411 if (regionCount < stringFormat->range_count)
5412 return InvalidParameter;
5413
5414 if(!graphics->hdc)
5415 {
5416 hdc = temp_hdc = CreateCompatibleDC(0);
5417 if (!temp_hdc) return OutOfMemory;
5418 }
5419 else
5420 hdc = graphics->hdc;
5421
5422 if (stringFormat->attr)
5423 TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
5424
5425 pt[0].X = 0.0;
5426 pt[0].Y = 0.0;
5427 pt[1].X = 1.0;
5428 pt[1].Y = 0.0;
5429 pt[2].X = 0.0;
5430 pt[2].Y = 1.0;
5431 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
5432 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5433 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5434 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5435 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5436
5437 margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
5438 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5439
5440 scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
5441 scaled_rect.Y = layoutRect->Y * args.rel_height;
5442 scaled_rect.Width = layoutRect->Width * args.rel_width;
5443 scaled_rect.Height = layoutRect->Height * args.rel_height;
5444
5445 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5446 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5447
5448 get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
5449 oldfont = SelectObject(hdc, gdifont);
5450
5451 for (i=0; i<stringFormat->range_count; i++)
5452 {
5453 stat = GdipSetEmpty(regions[i]);
5454 if (stat != Ok)
5455 return stat;
5456 }
5457
5458 args.regions = regions;
5459
5460 gdi_transform_acquire(graphics);
5461
5462 stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
5463 (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
5464
5465 gdi_transform_release(graphics);
5466
5467 SelectObject(hdc, oldfont);
5468 DeleteObject(gdifont);
5469
5470 if (temp_hdc)
5471 DeleteDC(temp_hdc);
5472
5473 return stat;
5474 }
5475
5476 struct measure_string_args {
5477 RectF *bounds;
5478 INT *codepointsfitted;
5479 INT *linesfilled;
5480 REAL rel_width, rel_height;
5481 };
5482
5483 static GpStatus measure_string_callback(HDC hdc,
5484 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
5485 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
5486 INT lineno, const RectF *bounds, INT *underlined_indexes,
5487 INT underlined_index_count, void *user_data)
5488 {
5489 struct measure_string_args *args = user_data;
5490 REAL new_width, new_height;
5491
5492 new_width = bounds->Width / args->rel_width;
5493 new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
5494
5495 if (new_width > args->bounds->Width)
5496 args->bounds->Width = new_width;
5497
5498 if (new_height > args->bounds->Height)
5499 args->bounds->Height = new_height;
5500
5501 if (args->codepointsfitted)
5502 *args->codepointsfitted = index + length;
5503
5504 if (args->linesfilled)
5505 (*args->linesfilled)++;
5506
5507 return Ok;
5508 }
5509
5510 /* Find the smallest rectangle that bounds the text when it is printed in rect
5511 * according to the format options listed in format. If rect has 0 width and
5512 * height, then just find the smallest rectangle that bounds the text when it's
5513 * printed at location (rect->X, rect-Y). */
5514 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
5515 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
5516 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
5517 INT *codepointsfitted, INT *linesfilled)
5518 {
5519 HFONT oldfont, gdifont;
5520 struct measure_string_args args;
5521 HDC temp_hdc=NULL, hdc;
5522 GpPointF pt[3];
5523 RectF scaled_rect;
5524 REAL margin_x;
5525 INT lines, glyphs;
5526
5527 TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
5528 debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
5529 bounds, codepointsfitted, linesfilled);
5530
5531 if(!graphics || !string || !font || !rect || !bounds)
5532 return InvalidParameter;
5533
5534 if(!graphics->hdc)
5535 {
5536 hdc = temp_hdc = CreateCompatibleDC(0);
5537 if (!temp_hdc) return OutOfMemory;
5538 }
5539 else
5540 hdc = graphics->hdc;
5541
5542 if(linesfilled) *linesfilled = 0;
5543 if(codepointsfitted) *codepointsfitted = 0;
5544
5545 if(format)
5546 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
5547
5548 pt[0].X = 0.0;
5549 pt[0].Y = 0.0;
5550 pt[1].X = 1.0;
5551 pt[1].Y = 0.0;
5552 pt[2].X = 0.0;
5553 pt[2].Y = 1.0;
5554 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
5555 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5556 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5557 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5558 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5559
5560 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
5561 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5562
5563 scaled_rect.X = (rect->X + margin_x) * args.rel_width;
5564 scaled_rect.Y = rect->Y * args.rel_height;
5565 scaled_rect.Width = rect->Width * args.rel_width;
5566 scaled_rect.Height = rect->Height * args.rel_height;
5567 if (scaled_rect.Width >= 0.5)
5568 {
5569 scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
5570 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
5571 }
5572
5573 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5574 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5575
5576 get_font_hfont(graphics, font, format, &gdifont, NULL);
5577 oldfont = SelectObject(hdc, gdifont);
5578
5579 bounds->X = rect->X;
5580 bounds->Y = rect->Y;
5581 bounds->Width = 0.0;
5582 bounds->Height = 0.0;
5583
5584 args.bounds = bounds;
5585 args.codepointsfitted = &glyphs;
5586 args.linesfilled = &lines;
5587 lines = glyphs = 0;
5588
5589 gdi_transform_acquire(graphics);
5590
5591 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
5592 measure_string_callback, &args);
5593
5594 gdi_transform_release(graphics);
5595
5596 if (linesfilled) *linesfilled = lines;
5597 if (codepointsfitted) *codepointsfitted = glyphs;
5598
5599 if (lines)
5600 bounds->Width += margin_x * 2.0;
5601
5602 SelectObject(hdc, oldfont);
5603 DeleteObject(gdifont);
5604
5605 if (temp_hdc)
5606 DeleteDC(temp_hdc);
5607
5608 return Ok;
5609 }
5610
5611 struct draw_string_args {
5612 GpGraphics *graphics;
5613 GDIPCONST GpBrush *brush;
5614 REAL x, y, rel_width, rel_height, ascent;
5615 };
5616
5617 static GpStatus draw_string_callback(HDC hdc,
5618 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
5619 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
5620 INT lineno, const RectF *bounds, INT *underlined_indexes,
5621 INT underlined_index_count, void *user_data)
5622 {
5623 struct draw_string_args *args = user_data;
5624 PointF position;
5625 GpStatus stat;
5626
5627 position.X = args->x + bounds->X / args->rel_width;
5628 position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
5629
5630 stat = draw_driver_string(args->graphics, &string[index], length, font, format,
5631 args->brush, &position,
5632 DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
5633
5634 if (stat == Ok && underlined_index_count)
5635 {
5636 OUTLINETEXTMETRICW otm;
5637 REAL underline_y, underline_height;
5638 int i;
5639
5640 GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
5641
5642 underline_height = otm.otmsUnderscoreSize / args->rel_height;
5643 underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
5644
5645 for (i=0; i<underlined_index_count; i++)
5646 {
5647 REAL start_x, end_x;
5648 SIZE text_size;
5649 INT ofs = underlined_indexes[i] - index;
5650
5651 GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
5652 start_x = text_size.cx / args->rel_width;
5653
5654 GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
5655 end_x = text_size.cx / args->rel_width;
5656
5657 GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
5658 }
5659 }
5660
5661 return stat;
5662 }
5663
5664 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
5665 INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
5666 GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
5667 {
5668 HRGN rgn = NULL;
5669 HFONT gdifont;
5670 GpPointF pt[3], rectcpy[4];
5671 POINT corners[4];
5672 REAL rel_width, rel_height, margin_x;
5673 INT save_state, format_flags = 0;
5674 REAL offsety = 0.0;
5675 struct draw_string_args args;
5676 RectF scaled_rect;
5677 HDC hdc, temp_hdc=NULL;
5678 TEXTMETRICW textmetric;
5679
5680 TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
5681 length, font, debugstr_rectf(rect), format, brush);
5682
5683 if(!graphics || !string || !font || !brush || !rect)
5684 return InvalidParameter;
5685
5686 if(graphics->hdc)
5687 {
5688 hdc = graphics->hdc;
5689 }
5690 else
5691 {
5692 hdc = temp_hdc = CreateCompatibleDC(0);
5693 }
5694
5695 if(format){
5696 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
5697
5698 format_flags = format->attr;
5699
5700 /* Should be no need to explicitly test for StringAlignmentNear as
5701 * that is default behavior if no alignment is passed. */
5702 if(format->line_align != StringAlignmentNear){
5703 RectF bounds, in_rect = *rect;
5704 in_rect.Height = 0.0; /* avoid height clipping */
5705 GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0);
5706
5707 TRACE("bounds %s\n", debugstr_rectf(&bounds));
5708
5709 if(format->line_align == StringAlignmentCenter)
5710 offsety = (rect->Height - bounds.Height) / 2;
5711 else if(format->line_align == StringAlignmentFar)
5712 offsety = (rect->Height - bounds.Height);
5713 }
5714 TRACE("line align %d, offsety %f\n", format->line_align, offsety);
5715 }
5716
5717 save_state = SaveDC(hdc);
5718
5719 pt[0].X = 0.0;
5720 pt[0].Y = 0.0;
5721 pt[1].X = 1.0;
5722 pt[1].Y = 0.0;
5723 pt[2].X = 0.0;
5724 pt[2].Y = 1.0;
5725 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
5726 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5727 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5728 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5729 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5730
5731 rectcpy[3].X = rectcpy[0].X = rect->X;
5732 rectcpy[1].Y = rectcpy[0].Y = rect->Y;
5733 rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
5734 rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
5735 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4);
5736 round_points(corners, rectcpy, 4);
5737
5738 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
5739 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5740
5741 scaled_rect.X = margin_x * rel_width;
5742 scaled_rect.Y = 0.0;
5743 scaled_rect.Width = rel_width * rect->Width;
5744 scaled_rect.Height = rel_height * rect->Height;
5745 if (scaled_rect.Width >= 0.5)
5746 {
5747 scaled_rect.Width -= margin_x * 2.0 * rel_width;
5748 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
5749 }
5750
5751 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5752 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5753
5754 if (!(format_flags & StringFormatFlagsNoClip) &&
5755 scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 &&
5756 rect->Width > 0.0 && rect->Height > 0.0)
5757 {
5758 /* FIXME: If only the width or only the height is 0, we should probably still clip */
5759 rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
5760 SelectClipRgn(hdc, rgn);
5761 }
5762
5763 get_font_hfont(graphics, font, format, &gdifont, NULL);
5764 SelectObject(hdc, gdifont);
5765
5766 args.graphics = graphics;
5767 args.brush = brush;
5768
5769 args.x = rect->X;
5770 args.y = rect->Y + offsety;
5771
5772 args.rel_width = rel_width;
5773 args.rel_height = rel_height;
5774
5775 gdi_transform_acquire(graphics);
5776
5777 GetTextMetricsW(hdc, &textmetric);
5778 args.ascent = textmetric.tmAscent / rel_height;
5779
5780 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
5781 draw_string_callback, &args);
5782
5783 gdi_transform_release(graphics);
5784
5785 DeleteObject(rgn);
5786 DeleteObject(gdifont);
5787
5788 RestoreDC(hdc, save_state);
5789
5790 DeleteDC(temp_hdc);
5791
5792 return Ok;
5793 }
5794
5795 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
5796 {
5797 TRACE("(%p)\n", graphics);
5798
5799 if(!graphics)
5800 return InvalidParameter;
5801
5802 if(graphics->busy)
5803 return ObjectBusy;
5804
5805 return GdipSetInfinite(graphics->clip);
5806 }
5807
5808 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
5809 {
5810 GpStatus stat;
5811
5812 TRACE("(%p)\n", graphics);
5813
5814 if(!graphics)
5815 return InvalidParameter;
5816
5817 if(graphics->busy)
5818 return ObjectBusy;
5819
5820 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5821 stat = METAFILE_ResetWorldTransform((GpMetafile*)graphics->image);
5822
5823 if (stat != Ok)
5824 return stat;
5825 }
5826
5827 return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
5828 }
5829
5830 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
5831 GpMatrixOrder order)
5832 {
5833 GpStatus stat;
5834
5835 TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
5836
5837 if(!graphics)
5838 return InvalidParameter;
5839
5840 if(graphics->busy)
5841 return ObjectBusy;
5842
5843 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5844 stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order);
5845
5846 if (stat != Ok)
5847 return stat;
5848 }
5849
5850 return GdipRotateMatrix(&graphics->worldtrans, angle, order);
5851 }
5852
5853 static GpStatus begin_container(GpGraphics *graphics,
5854 GraphicsContainerType type, GraphicsContainer *state)
5855 {
5856 GraphicsContainerItem *container;
5857 GpStatus sts;
5858
5859 if(!graphics || !state)
5860 return InvalidParameter;
5861
5862 sts = init_container(&container, graphics, type);
5863 if(sts != Ok)
5864 return sts;
5865
5866 list_add_head(&graphics->containers, &container->entry);
5867 *state = graphics->contid = container->contid;
5868
5869 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5870 if (type == BEGIN_CONTAINER)
5871 METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid);
5872 else
5873 METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid);
5874 }
5875
5876 return Ok;
5877 }
5878
5879 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
5880 {
5881 TRACE("(%p, %p)\n", graphics, state);
5882 return begin_container(graphics, SAVE_GRAPHICS, state);
5883 }
5884
5885 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
5886 GraphicsContainer *state)
5887 {
5888 TRACE("(%p, %p)\n", graphics, state);
5889 return begin_container(graphics, BEGIN_CONTAINER, state);
5890 }
5891
5892 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
5893 {
5894 GraphicsContainerItem *container;
5895 GpMatrix transform;
5896 GpStatus stat;
5897 GpRectF scaled_srcrect;
5898 REAL scale_x, scale_y;
5899
5900 TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state);
5901
5902 if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state)
5903 return InvalidParameter;
5904
5905 stat = init_container(&container, graphics, BEGIN_CONTAINER);
5906 if(stat != Ok)
5907 return stat;
5908
5909 list_add_head(&graphics->containers, &container->entry);
5910 *state = graphics->contid = container->contid;
5911
5912 scale_x = units_to_pixels(1.0, unit, graphics->xres);
5913 scale_y = units_to_pixels(1.0, unit, graphics->yres);
5914
5915 scaled_srcrect.X = scale_x * srcrect->X;
5916 scaled_srcrect.Y = scale_y * srcrect->Y;
5917 scaled_srcrect.Width = scale_x * srcrect->Width;
5918 scaled_srcrect.Height = scale_y * srcrect->Height;
5919
5920 transform.matrix[0] = dstrect->Width / scaled_srcrect.Width;
5921 transform.matrix[1] = 0.0;
5922 transform.matrix[2] = 0.0;
5923 transform.matrix[3] = dstrect->Height / scaled_srcrect.Height;
5924 transform.matrix[4] = dstrect->X - scaled_srcrect.X;
5925 transform.matrix[5] = dstrect->Y - scaled_srcrect.Y;
5926
5927 GdipMultiplyMatrix(&graphics->worldtrans, &transform, MatrixOrderPrepend);
5928
5929 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5930 METAFILE_BeginContainer((GpMetafile*)graphics->image, dstrect, srcrect, unit, container->contid);
5931 }
5932
5933 return Ok;
5934 }
5935
5936 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
5937 {
5938 GpRectF dstrectf, srcrectf;
5939
5940 TRACE("(%p, %p, %p, %d, %p)\n", graphics, dstrect, srcrect, unit, state);
5941
5942 if (!dstrect || !srcrect)
5943 return InvalidParameter;
5944
5945 dstrectf.X = dstrect->X;
5946 dstrectf.Y = dstrect->Y;
5947 dstrectf.Width = dstrect->Width;
5948 dstrectf.Height = dstrect->Height;
5949
5950 srcrectf.X = srcrect->X;
5951 srcrectf.Y = srcrect->Y;
5952 srcrectf.Width = srcrect->Width;
5953 srcrectf.Height = srcrect->Height;
5954
5955 return GdipBeginContainer(graphics, &dstrectf, &srcrectf, unit, state);
5956 }
5957
5958 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
5959 {
5960 FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
5961 return NotImplemented;
5962 }
5963
5964 static GpStatus end_container(GpGraphics *graphics, GraphicsContainerType type,
5965 GraphicsContainer state)
5966 {
5967 GpStatus sts;
5968 GraphicsContainerItem *container, *container2;
5969
5970 if(!graphics)
5971 return InvalidParameter;
5972
5973 LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
5974 if(container->contid == state && container->type == type)
5975 break;
5976 }
5977
5978 /* did not find a matching container */
5979 if(&container->entry == &graphics->containers)
5980 return Ok;
5981
5982 sts = restore_container(graphics, container);
5983 if(sts != Ok)
5984 return sts;
5985
5986 /* remove all of the containers on top of the found container */
5987 LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
5988 if(container->contid == state)
5989 break;
5990 list_remove(&container->entry);
5991 delete_container(container);
5992 }
5993
5994 list_remove(&container->entry);
5995 delete_container(container);
5996
5997 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5998 if (type == BEGIN_CONTAINER)
5999 METAFILE_EndContainer((GpMetafile*)graphics->image, state);
6000 else
6001 METAFILE_RestoreGraphics((GpMetafile*)graphics->image, state);
6002 }
6003
6004 return Ok;
6005 }
6006
6007 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
6008 {
6009 TRACE("(%p, %x)\n", graphics, state);
6010 return end_container(graphics, BEGIN_CONTAINER, state);
6011 }
6012
6013 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
6014 {
6015 TRACE("(%p, %x)\n", graphics, state);
6016 return end_container(graphics, SAVE_GRAPHICS, state);
6017 }
6018
6019 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
6020 REAL sy, GpMatrixOrder order)
6021 {
6022 GpStatus stat;
6023
6024 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
6025
6026 if(!graphics)
6027 return InvalidParameter;
6028
6029 if(graphics->busy)
6030 return ObjectBusy;
6031
6032 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6033 stat = METAFILE_ScaleWorldTransform((GpMetafile*)graphics->image, sx, sy, order);
6034
6035 if (stat != Ok)
6036 return stat;
6037 }
6038
6039 return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
6040 }
6041
6042 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
6043 CombineMode mode)
6044 {
6045 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
6046
6047 if(!graphics || !srcgraphics)
6048 return InvalidParameter;
6049
6050 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
6051 }
6052
6053 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
6054 CompositingMode mode)
6055 {
6056 TRACE("(%p, %d)\n", graphics, mode);
6057
6058 if(!graphics)
6059 return InvalidParameter;
6060
6061 if(graphics->busy)
6062 return ObjectBusy;
6063
6064 if(graphics->compmode == mode)
6065 return Ok;
6066
6067 if(graphics->image && graphics->image->type == ImageTypeMetafile)
6068 {
6069 GpStatus stat;
6070
6071 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6072 EmfPlusRecordTypeSetCompositingMode, mode);
6073 if(stat != Ok)
6074 return stat;
6075 }
6076
6077 graphics->compmode = mode;
6078
6079 return Ok;
6080 }
6081
6082 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
6083 CompositingQuality quality)
6084 {
6085 TRACE("(%p, %d)\n", graphics, quality);
6086
6087 if(!graphics)
6088 return InvalidParameter;
6089
6090 if(graphics->busy)
6091 return ObjectBusy;
6092
6093 if(graphics->compqual == quality)
6094 return Ok;
6095
6096 if(graphics->image && graphics->image->type == ImageTypeMetafile)
6097 {
6098 GpStatus stat;
6099
6100 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6101 EmfPlusRecordTypeSetCompositingQuality, quality);
6102 if(stat != Ok)
6103 return stat;
6104 }
6105
6106 graphics->compqual = quality;
6107
6108 return Ok;
6109 }
6110
6111 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
6112 InterpolationMode mode)
6113 {
6114 TRACE("(%p, %d)\n", graphics, mode);
6115
6116 if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
6117 return InvalidParameter;
6118
6119 if(graphics->busy)
6120 return ObjectBusy;
6121
6122 if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
6123 mode = InterpolationModeBilinear;
6124
6125 if (mode == InterpolationModeHighQuality)
6126 mode = InterpolationModeHighQualityBicubic;
6127
6128 if (mode == graphics->interpolation)
6129 return Ok;
6130
6131 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6132 {
6133 GpStatus stat;
6134
6135 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6136 EmfPlusRecordTypeSetInterpolationMode, mode);
6137 if (stat != Ok)
6138 return stat;
6139 }
6140
6141 graphics->interpolation = mode;
6142
6143 return Ok;
6144 }
6145
6146 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
6147 {
6148 GpStatus stat;
6149
6150 TRACE("(%p, %.2f)\n", graphics, scale);
6151
6152 if(!graphics || (scale <= 0.0))
6153 return InvalidParameter;
6154
6155 if(graphics->busy)
6156 return ObjectBusy;
6157
6158 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6159 {
6160 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, graphics->unit, scale);
6161 if (stat != Ok)
6162 return stat;
6163 }
6164
6165 graphics->scale = scale;
6166
6167 return Ok;
6168 }
6169
6170 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
6171 {
6172 GpStatus stat;
6173
6174 TRACE("(%p, %d)\n", graphics, unit);
6175
6176 if(!graphics)
6177 return InvalidParameter;
6178
6179 if(graphics->busy)
6180 return ObjectBusy;
6181
6182 if(unit == UnitWorld)
6183 return InvalidParameter;
6184
6185 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6186 {
6187 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, unit, graphics->scale);
6188 if (stat != Ok)
6189 return stat;
6190 }
6191
6192 graphics->unit = unit;
6193
6194 return Ok;
6195 }
6196
6197 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
6198 mode)
6199 {
6200 TRACE("(%p, %d)\n", graphics, mode);
6201
6202 if(!graphics)
6203 return InvalidParameter;
6204
6205 if(graphics->busy)
6206 return ObjectBusy;
6207
6208 if(graphics->pixeloffset == mode)
6209 return Ok;
6210
6211 if(graphics->image && graphics->image->type == ImageTypeMetafile)
6212 {
6213 GpStatus stat;
6214
6215 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6216 EmfPlusRecordTypeSetPixelOffsetMode, mode);
6217 if(stat != Ok)
6218 return stat;
6219 }
6220
6221 graphics->pixeloffset = mode;
6222
6223 return Ok;
6224 }
6225
6226 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
6227 {
6228 static int calls;
6229
6230 TRACE("(%p,%i,%i)\n", graphics, x, y);
6231
6232 if (!(calls++))
6233 FIXME("value is unused in rendering\n");
6234
6235 if (!graphics)
6236 return InvalidParameter;
6237
6238 graphics->origin_x = x;
6239 graphics->origin_y = y;
6240
6241 return Ok;
6242 }
6243
6244 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
6245 {
6246 TRACE("(%p,%p,%p)\n", graphics, x, y);
6247
6248 if (!graphics || !x || !y)
6249 return InvalidParameter;
6250
6251 *x = graphics->origin_x;
6252 *y = graphics->origin_y;
6253
6254 return Ok;
6255 }
6256
6257 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
6258 {
6259 TRACE("(%p, %d)\n", graphics, mode);
6260
6261 if(!graphics)
6262 return InvalidParameter;
6263
6264 if(graphics->busy)
6265 return ObjectBusy;
6266
6267 if(graphics->smoothing == mode)
6268 return Ok;
6269
6270 if(graphics->image && graphics->image->type == ImageTypeMetafile) {
6271 GpStatus stat;
6272 BOOL antialias = (mode != SmoothingModeDefault &&
6273 mode != SmoothingModeNone && mode != SmoothingModeHighSpeed);
6274
6275 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6276 EmfPlusRecordTypeSetAntiAliasMode, (mode << 1) + antialias);
6277 if(stat != Ok)
6278 return stat;
6279 }
6280
6281 graphics->smoothing = mode;
6282
6283 return Ok;
6284 }
6285
6286 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
6287 {
6288 TRACE("(%p, %d)\n", graphics, contrast);
6289
6290 if(!graphics)
6291 return InvalidParameter;
6292
6293 graphics->textcontrast = contrast;
6294
6295 return Ok;
6296 }
6297
6298 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
6299 TextRenderingHint hint)
6300 {
6301 TRACE("(%p, %d)\n", graphics, hint);
6302
6303 if(!graphics || hint > TextRenderingHintClearTypeGridFit)
6304 return InvalidParameter;
6305
6306 if(graphics->busy)
6307 return ObjectBusy;
6308
6309 if(graphics->texthint == hint)
6310 return Ok;
6311
6312 if(graphics->image && graphics->image->type == ImageTypeMetafile) {
6313 GpStatus stat;
6314
6315 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6316 EmfPlusRecordTypeSetTextRenderingHint, hint);
6317 if(stat != Ok)
6318 return stat;
6319 }
6320
6321 graphics->texthint = hint;
6322
6323 return Ok;
6324 }
6325
6326 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
6327 {
6328 GpStatus stat;
6329
6330 TRACE("(%p, %p)\n", graphics, matrix);
6331
6332 if(!graphics || !matrix)
6333 return InvalidParameter;
6334
6335 if(graphics->busy)
6336 return ObjectBusy;
6337
6338 TRACE("%f,%f,%f,%f,%f,%f\n",
6339 matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
6340 matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
6341
6342 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6343 stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix);
6344
6345 if (stat != Ok)
6346 return stat;
6347 }
6348
6349 graphics->worldtrans = *matrix;
6350
6351 return Ok;
6352 }
6353
6354 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
6355 REAL dy, GpMatrixOrder order)
6356 {
6357 GpStatus stat;
6358
6359 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
6360
6361 if(!graphics)
6362 return InvalidParameter;
6363
6364 if(graphics->busy)
6365 return ObjectBusy;
6366
6367 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6368 stat = METAFILE_TranslateWorldTransform((GpMetafile*)graphics->image, dx, dy, order);
6369
6370 if (stat != Ok)
6371 return stat;
6372 }
6373
6374 return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
6375 }
6376
6377 /*****************************************************************************
6378 * GdipSetClipHrgn [GDIPLUS.@]
6379 */
6380 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
6381 {
6382 GpRegion *region;
6383 GpStatus status;
6384 GpMatrix transform;
6385
6386 TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
6387
6388 if(!graphics)
6389 return InvalidParameter;
6390
6391 if(graphics->busy)
6392 return ObjectBusy;
6393
6394 /* hrgn is in gdi32 device units */
6395 status = GdipCreateRegionHrgn(hrgn, &region);
6396
6397 if (status == Ok)
6398 {
6399 status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform);
6400
6401 if (status == Ok)
6402 status = GdipTransformRegion(region, &transform);
6403
6404 if (status == Ok)
6405 status = GdipCombineRegionRegion(graphics->clip, region, mode);
6406
6407 GdipDeleteRegion(region);
6408 }
6409 return status;
6410 }
6411
6412 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
6413 {
6414 GpStatus status;
6415 GpPath *clip_path;
6416
6417 TRACE("(%p, %p, %d)\n", graphics, path, mode);
6418
6419 if(!graphics)
6420 return InvalidParameter;
6421
6422 if(graphics->busy)
6423 return ObjectBusy;
6424
6425 status = GdipClonePath(path, &clip_path);
6426 if (status == Ok)
6427 {
6428 GpMatrix world_to_device;
6429
6430 get_graphics_transform(graphics, CoordinateSpaceDevice,
6431 CoordinateSpaceWorld, &world_to_device);
6432 status = GdipTransformPath(clip_path, &world_to_device);
6433 if (status == Ok)
6434 GdipCombineRegionPath(graphics->clip, clip_path, mode);
6435
6436 GdipDeletePath(clip_path);
6437 }
6438 return status;
6439 }
6440
6441 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
6442 REAL width, REAL height,
6443 CombineMode mode)
6444 {
6445 GpStatus status;
6446 GpRectF rect;
6447 GpRegion *region;
6448
6449 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
6450
6451 if(!graphics)
6452 return InvalidParameter;
6453
6454 if(graphics->busy)
6455 return ObjectBusy;
6456
6457 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6458 {
6459 status = METAFILE_SetClipRect((GpMetafile*)graphics->image, x, y, width, height, mode);
6460 if (status != Ok)
6461 return status;
6462 }
6463
6464 rect.X = x;
6465 rect.Y = y;
6466 rect.Width = width;
6467 rect.Height = height;
6468 status = GdipCreateRegionRect(&rect, &region);
6469 if (status == Ok)
6470 {
6471 GpMatrix world_to_device;
6472
6473 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
6474 status = GdipTransformRegion(region, &world_to_device);
6475 if (status == Ok)
6476 status = GdipCombineRegionRegion(graphics->clip, region, mode);
6477
6478 GdipDeleteRegion(region);
6479 }
6480 return status;
6481 }
6482
6483 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
6484 INT width, INT height,
6485 CombineMode mode)
6486 {
6487 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
6488
6489 if(!graphics)
6490 return InvalidParameter;
6491
6492 if(graphics->busy)
6493 return ObjectBusy;
6494
6495 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
6496 }
6497
6498 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
6499 CombineMode mode)
6500 {
6501 GpStatus status;
6502 GpRegion *clip;
6503
6504 TRACE("(%p, %p, %d)\n", graphics, region, mode);
6505
6506 if(!graphics || !region)
6507 return InvalidParameter;
6508
6509 if(graphics->busy)
6510 return ObjectBusy;
6511
6512 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6513 {
6514 status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode);
6515 if (status != Ok)
6516 return status;
6517 }
6518
6519 status = GdipCloneRegion(region, &clip);
6520 if (status == Ok)
6521 {
6522 GpMatrix world_to_device;
6523
6524 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
6525 status = GdipTransformRegion(clip, &world_to_device);
6526 if (status == Ok)
6527 status = GdipCombineRegionRegion(graphics->clip, clip, mode);
6528
6529 GdipDeleteRegion(clip);
6530 }
6531 return status;
6532 }
6533
6534 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
6535 INT count)
6536 {
6537 GpStatus status;
6538 GpPath* path;
6539
6540 TRACE("(%p, %p, %d)\n", graphics, points, count);
6541
6542 if(!graphics || !pen || count<=0)
6543 return InvalidParameter;
6544
6545 if(graphics->busy)
6546 return ObjectBusy;
6547
6548 status = GdipCreatePath(FillModeAlternate, &path);
6549 if (status != Ok) return status;
6550
6551 status = GdipAddPathPolygon(path, points, count);
6552 if (status == Ok)
6553 status = GdipDrawPath(graphics, pen, path);
6554
6555 GdipDeletePath(path);
6556
6557 return status;
6558 }
6559
6560 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
6561 INT count)
6562 {
6563 GpStatus ret;
6564 GpPointF *ptf;
6565 INT i;
6566
6567 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
6568
6569 if(count<=0) return InvalidParameter;
6570 ptf = heap_alloc_zero(sizeof(GpPointF) * count);
6571
6572 for(i = 0;i < count; i++){
6573 ptf[i].X = (REAL)points[i].X;
6574 ptf[i].Y = (REAL)points[i].Y;
6575 }
6576
6577 ret = GdipDrawPolygon(graphics,pen,ptf,count);
6578 heap_free(ptf);
6579
6580 return ret;
6581 }
6582
6583 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
6584 {
6585 TRACE("(%p, %p)\n", graphics, dpi);
6586
6587 if(!graphics || !dpi)
6588 return InvalidParameter;
6589
6590 if(graphics->busy)
6591 return ObjectBusy;
6592
6593 *dpi = graphics->xres;
6594 return Ok;
6595 }
6596
6597 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
6598 {
6599 TRACE("(%p, %p)\n", graphics, dpi);
6600
6601 if(!graphics || !dpi)
6602 return InvalidParameter;
6603
6604 if(graphics->busy)
6605 return ObjectBusy;
6606
6607 *dpi = graphics->yres;
6608 return Ok;
6609 }
6610
6611 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
6612 GpMatrixOrder order)
6613 {
6614 GpMatrix m;
6615 GpStatus ret;
6616
6617 TRACE("(%p, %p, %d)\n", graphics, matrix, order);
6618
6619 if(!graphics || !matrix)
6620 return InvalidParameter;
6621
6622 if(graphics->busy)
6623 return ObjectBusy;
6624
6625 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6626 ret = METAFILE_MultiplyWorldTransform((GpMetafile*)graphics->image, matrix, order);
6627
6628 if (ret != Ok)
6629 return ret;
6630 }
6631
6632 m = graphics->worldtrans;
6633
6634 ret = GdipMultiplyMatrix(&m, matrix, order);
6635 if(ret == Ok)
6636 graphics->worldtrans = m;
6637
6638 return ret;
6639 }
6640
6641 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
6642 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
6643
6644 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
6645 {
6646 GpStatus stat=Ok;
6647
6648 TRACE("(%p, %p)\n", graphics, hdc);
6649
6650 if(!graphics || !hdc)
6651 return InvalidParameter;
6652
6653 if(graphics->busy)
6654 return ObjectBusy;
6655
6656 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6657 {
6658 stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
6659 }
6660 else if (!graphics->hdc ||
6661 (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
6662 {
6663 /* Create a fake HDC and fill it with a constant color. */
6664 HDC temp_hdc;
6665 HBITMAP hbitmap;
6666 GpRectF bounds;
6667 BITMAPINFOHEADER bmih;
6668 int i;
6669
6670 stat = get_graphics_bounds(graphics, &bounds);
6671 if (stat != Ok)
6672 return stat;
6673
6674 graphics->temp_hbitmap_width = bounds.Width;
6675 graphics->temp_hbitmap_height = bounds.Height;
6676
6677 bmih.biSize = sizeof(bmih);
6678 bmih.biWidth = graphics->temp_hbitmap_width;
6679 bmih.biHeight = -graphics->temp_hbitmap_height;
6680 bmih.biPlanes = 1;
6681 bmih.biBitCount = 32;
6682 bmih.biCompression = BI_RGB;
6683 bmih.biSizeImage = 0;
6684 bmih.biXPelsPerMeter = 0;
6685 bmih.biYPelsPerMeter = 0;
6686 bmih.biClrUsed = 0;
6687 bmih.biClrImportant = 0;
6688
6689 hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
6690 (void**)&graphics->temp_bits, NULL, 0);
6691 if (!hbitmap)
6692 return GenericError;
6693
6694 temp_hdc = CreateCompatibleDC(0);
6695 if (!temp_hdc)
6696 {
6697 DeleteObject(hbitmap);
6698 return GenericError;
6699 }
6700
6701 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
6702 ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
6703
6704 SelectObject(temp_hdc, hbitmap);
6705
6706 graphics->temp_hbitmap = hbitmap;
6707 *hdc = graphics->temp_hdc = temp_hdc;
6708 }
6709 else
6710 {
6711 *hdc = graphics->hdc;
6712 }
6713
6714 if (stat == Ok)
6715 graphics->busy = TRUE;
6716
6717 return stat;
6718 }
6719
6720 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
6721 {
6722 GpStatus stat=Ok;
6723
6724 TRACE("(%p, %p)\n", graphics, hdc);
6725
6726 if(!graphics || !hdc || !graphics->busy)
6727 return InvalidParameter;
6728
6729 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6730 {
6731 stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
6732 }
6733 else if (graphics->temp_hdc == hdc)
6734 {
6735 DWORD* pos;
6736 int i;
6737
6738 /* Find the pixels that have changed, and mark them as opaque. */
6739 pos = (DWORD*)graphics->temp_bits;
6740 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
6741 {
6742 if (*pos != DC_BACKGROUND_KEY)
6743 {
6744 *pos |= 0xff000000;
6745 }
6746 pos++;
6747 }
6748
6749 /* Write the changed pixels to the real target. */
6750 alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
6751 graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
6752 graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB);
6753
6754 /* Clean up. */
6755 DeleteDC(graphics->temp_hdc);
6756 DeleteObject(graphics->temp_hbitmap);
6757 graphics->temp_hdc = NULL;
6758 graphics->temp_hbitmap = NULL;
6759 }
6760 else if (hdc != graphics->hdc)
6761 {
6762 stat = InvalidParameter;
6763 }
6764
6765 if (stat == Ok)
6766 graphics->busy = FALSE;
6767
6768 return stat;
6769 }
6770
6771 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
6772 {
6773 GpRegion *clip;
6774 GpStatus status;
6775 GpMatrix device_to_world;
6776
6777 TRACE("(%p, %p)\n", graphics, region);
6778
6779 if(!graphics || !region)
6780 return InvalidParameter;
6781
6782 if(graphics->busy)
6783 return ObjectBusy;
6784
6785 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
6786 return status;
6787
6788 get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
6789 status = GdipTransformRegion(clip, &device_to_world);
6790 if (status != Ok)
6791 {
6792 GdipDeleteRegion(clip);
6793 return status;
6794 }
6795
6796 /* free everything except root node and header */
6797 delete_element(&region->node);
6798 memcpy(region, clip, sizeof(GpRegion));
6799 heap_free(clip);
6800
6801 return Ok;
6802 }
6803
6804 GpStatus gdi_transform_acquire(GpGraphics *graphics)
6805 {
6806 if (graphics->gdi_transform_acquire_count == 0 && graphics->hdc)
6807 {
6808 graphics->gdi_transform_save = SaveDC(graphics->hdc);
6809 SetGraphicsMode(graphics->hdc, GM_COMPATIBLE);
6810 SetMapMode(graphics->hdc, MM_TEXT);
6811 SetWindowOrgEx(graphics->hdc, 0, 0, NULL);
6812 SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
6813 }
6814 graphics->gdi_transform_acquire_count++;
6815 return Ok;
6816 }
6817
6818 GpStatus gdi_transform_release(GpGraphics *graphics)
6819 {
6820 if (graphics->gdi_transform_acquire_count <= 0)
6821 {
6822 ERR("called without matching gdi_transform_acquire\n");
6823 return GenericError;
6824 }
6825 if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
6826 {
6827 RestoreDC(graphics->hdc, graphics->gdi_transform_save);
6828 }
6829 graphics->gdi_transform_acquire_count--;
6830 return Ok;
6831 }
6832
6833 GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
6834 GpCoordinateSpace src_space, GpMatrix *matrix)
6835 {
6836 GpStatus stat = Ok;
6837 REAL scale_x, scale_y;
6838
6839 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
6840
6841 if (dst_space != src_space)
6842 {
6843 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
6844 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
6845
6846 if(graphics->unit != UnitDisplay)
6847 {
6848 scale_x *= graphics->scale;
6849 scale_y *= graphics->scale;
6850 }
6851
6852 if (dst_space < src_space)
6853 {
6854 /* transform towards world space */
6855 switch ((int)src_space)
6856 {
6857 case WineCoordinateSpaceGdiDevice:
6858 {
6859 GpMatrix gdixform;
6860 gdixform = graphics->gdi_transform;
6861 stat = GdipInvertMatrix(&gdixform);
6862 if (stat != Ok)
6863 break;
6864 GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
6865 if (dst_space == CoordinateSpaceDevice)
6866 break;
6867 /* else fall-through */
6868 }
6869 case CoordinateSpaceDevice:
6870 GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
6871 if (dst_space == CoordinateSpacePage)
6872 break;
6873 /* else fall-through */
6874 case CoordinateSpacePage:
6875 {
6876 GpMatrix inverted_transform = graphics->worldtrans;
6877 stat = GdipInvertMatrix(&inverted_transform);
6878 if (stat == Ok)
6879 GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
6880 break;
6881 }
6882 }
6883 }
6884 else
6885 {
6886 /* transform towards device space */
6887 switch ((int)src_space)
6888 {
6889 case CoordinateSpaceWorld:
6890 GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
6891 if (dst_space == CoordinateSpacePage)
6892 break;
6893 /* else fall-through */
6894 case CoordinateSpacePage:
6895 GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
6896 if (dst_space == CoordinateSpaceDevice)
6897 break;
6898 /* else fall-through */
6899 case CoordinateSpaceDevice:
6900 {
6901 GdipMultiplyMatrix(matrix, &graphics->gdi_transform, MatrixOrderAppend);
6902 break;
6903 }
6904 }
6905 }
6906 }
6907 return stat;
6908 }
6909
6910 GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
6911 GpCoordinateSpace src_space, GpPointF *points, INT count)
6912 {
6913 GpMatrix matrix;
6914 GpStatus stat;
6915
6916 stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
6917 if (stat != Ok) return stat;
6918
6919 return GdipTransformMatrixPoints(&matrix, points, count);
6920 }
6921
6922 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
6923 GpCoordinateSpace src_space, GpPointF *points, INT count)
6924 {
6925 if(!graphics || !points || count <= 0 ||
6926 dst_space < 0 || dst_space > CoordinateSpaceDevice ||
6927 src_space < 0 || src_space > CoordinateSpaceDevice)
6928 return InvalidParameter;
6929
6930 if(graphics->busy)
6931 return ObjectBusy;
6932
6933 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
6934
6935 if (src_space == dst_space) return Ok;
6936
6937 return gdip_transform_points(graphics, dst_space, src_space, points, count);
6938 }
6939
6940 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
6941 GpCoordinateSpace src_space, GpPoint *points, INT count)
6942 {
6943 GpPointF *pointsF;
6944 GpStatus ret;
6945 INT i;
6946
6947 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
6948
6949 if(count <= 0)
6950 return InvalidParameter;
6951
6952 pointsF = heap_alloc_zero(sizeof(GpPointF) * count);
6953 if(!pointsF)
6954 return OutOfMemory;
6955
6956 for(i = 0; i < count; i++){
6957 pointsF[i].X = (REAL)points[i].X;
6958 pointsF[i].Y = (REAL)points[i].Y;
6959 }
6960
6961 ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
6962
6963 if(ret == Ok)
6964 for(i = 0; i < count; i++){
6965 points[i].X = gdip_round(pointsF[i].X);
6966 points[i].Y = gdip_round(pointsF[i].Y);
6967 }
6968 heap_free(pointsF);
6969
6970 return ret;
6971 }
6972
6973 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
6974 {
6975 static int calls;
6976
6977 TRACE("\n");
6978
6979 if (!calls++)
6980 FIXME("stub\n");
6981
6982 return NULL;
6983 }
6984
6985 /*****************************************************************************
6986 * GdipTranslateClip [GDIPLUS.@]
6987 */
6988 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
6989 {
6990 TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
6991
6992 if(!graphics)
6993 return InvalidParameter;
6994
6995 if(graphics->busy)
6996 return ObjectBusy;
6997
6998 return GdipTranslateRegion(graphics->clip, dx, dy);
6999 }
7000
7001 /*****************************************************************************
7002 * GdipTranslateClipI [GDIPLUS.@]
7003 */
7004 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
7005 {
7006 TRACE("(%p, %d, %d)\n", graphics, dx, dy);
7007
7008 if(!graphics)
7009 return InvalidParameter;
7010
7011 if(graphics->busy)
7012 return ObjectBusy;
7013
7014 return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
7015 }
7016
7017
7018 /*****************************************************************************
7019 * GdipMeasureDriverString [GDIPLUS.@]
7020 */
7021 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7022 GDIPCONST GpFont *font, GDIPCONST PointF *positions,
7023 INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
7024 {
7025 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
7026 HFONT hfont;
7027 HDC hdc;
7028 REAL min_x, min_y, max_x, max_y, x, y;
7029 int i;
7030 TEXTMETRICW textmetric;
7031 const WORD *glyph_indices;
7032 WORD *dynamic_glyph_indices=NULL;
7033 REAL rel_width, rel_height, ascent, descent;
7034 GpPointF pt[3];
7035
7036 TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
7037
7038 if (!graphics || !text || !font || !positions || !boundingBox)
7039 return InvalidParameter;
7040
7041 if (length == -1)
7042 length = strlenW(text);
7043
7044 if (length == 0)
7045 {
7046 boundingBox->X = 0.0;
7047 boundingBox->Y = 0.0;
7048 boundingBox->Width = 0.0;
7049 boundingBox->Height = 0.0;
7050 }
7051
7052 if (flags & unsupported_flags)
7053 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
7054
7055 get_font_hfont(graphics, font, NULL, &hfont, matrix);
7056
7057 hdc = CreateCompatibleDC(0);
7058 SelectObject(hdc, hfont);
7059
7060 GetTextMetricsW(hdc, &textmetric);
7061
7062 pt[0].X = 0.0;
7063 pt[0].Y = 0.0;
7064 pt[1].X = 1.0;
7065 pt[1].Y = 0.0;
7066 pt[2].X = 0.0;
7067 pt[2].Y = 1.0;
7068 if (matrix)
7069 {
7070 GpMatrix xform = *matrix;
7071 GdipTransformMatrixPoints(&xform, pt, 3);
7072 }
7073 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
7074 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
7075 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
7076 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
7077 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
7078
7079 if (flags & DriverStringOptionsCmapLookup)
7080 {
7081 glyph_indices = dynamic_glyph_indices = heap_alloc_zero(sizeof(WORD) * length);
7082 if (!glyph_indices)
7083 {
7084 DeleteDC(hdc);
7085 DeleteObject(hfont);
7086 return OutOfMemory;
7087 }
7088
7089 GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
7090 }
7091 else
7092 glyph_indices = text;
7093
7094 min_x = max_x = x = positions[0].X;
7095 min_y = max_y = y = positions[0].Y;
7096
7097 ascent = textmetric.tmAscent / rel_height;
7098 descent = textmetric.tmDescent / rel_height;
7099
7100 for (i=0; i<length; i++)
7101 {
7102 int char_width;
7103 ABC abc;
7104
7105 if (!(flags & DriverStringOptionsRealizedAdvance))
7106 {
7107 x = positions[i].X;
7108 y = positions[i].Y;
7109 }
7110
7111 GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
7112 char_width = abc.abcA + abc.abcB + abc.abcC;
7113
7114 if (min_y > y - ascent) min_y = y - ascent;
7115 if (max_y < y + descent) max_y = y + descent;
7116 if (min_x > x) min_x = x;
7117
7118 x += char_width / rel_width;
7119
7120 if (max_x < x) max_x = x;
7121 }
7122
7123 heap_free(dynamic_glyph_indices);
7124 DeleteDC(hdc);
7125 DeleteObject(hfont);
7126
7127 boundingBox->X = min_x;
7128 boundingBox->Y = min_y;
7129 boundingBox->Width = max_x - min_x;
7130 boundingBox->Height = max_y - min_y;
7131
7132 return Ok;
7133 }
7134
7135 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7136 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
7137 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
7138 INT flags, GDIPCONST GpMatrix *matrix)
7139 {
7140 static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
7141 INT save_state;
7142 GpPointF pt;
7143 HFONT hfont;
7144 UINT eto_flags=0;
7145 GpStatus status;
7146 HRGN hrgn;
7147
7148 if (flags & unsupported_flags)
7149 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
7150
7151 if (!(flags & DriverStringOptionsCmapLookup))
7152 eto_flags |= ETO_GLYPH_INDEX;
7153
7154 save_state = SaveDC(graphics->hdc);
7155 SetBkMode(graphics->hdc, TRANSPARENT);
7156 SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
7157
7158 status = get_clip_hrgn(graphics, &hrgn);
7159
7160 if (status == Ok)
7161 {
7162 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
7163 DeleteObject(hrgn);
7164 }
7165
7166 pt = positions[0];
7167 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &pt, 1);
7168
7169 get_font_hfont(graphics, font, format, &hfont, matrix);
7170 SelectObject(graphics->hdc, hfont);
7171
7172 SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
7173
7174 gdi_transform_acquire(graphics);
7175
7176 ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
7177
7178 gdi_transform_release(graphics);
7179
7180 RestoreDC(graphics->hdc, save_state);
7181
7182 DeleteObject(hfont);
7183
7184 return Ok;
7185 }
7186
7187 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7188 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
7189 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
7190 INT flags, GDIPCONST GpMatrix *matrix)
7191 {
7192 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
7193 GpStatus stat;
7194 PointF *real_positions, real_position;
7195 POINT *pti;
7196 HFONT hfont;
7197 HDC hdc;
7198 int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
7199 DWORD max_glyphsize=0;
7200 GLYPHMETRICS glyphmetrics;
7201 static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
7202 BYTE *glyph_mask;
7203 BYTE *text_mask;
7204 int text_mask_stride;
7205 BYTE *pixel_data;
7206 int pixel_data_stride;
7207 GpRect pixel_area;
7208 UINT ggo_flags = GGO_GRAY8_BITMAP;
7209
7210 if (length <= 0)
7211 return Ok;
7212
7213 if (!(flags & DriverStringOptionsCmapLookup))
7214 ggo_flags |= GGO_GLYPH_INDEX;
7215
7216 if (flags & unsupported_flags)
7217 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
7218
7219 pti = heap_alloc_zero(sizeof(POINT) * length);
7220 if (!pti)
7221 return OutOfMemory;
7222
7223 if (flags & DriverStringOptionsRealizedAdvance)
7224 {
7225 real_position = positions[0];
7226
7227 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1);
7228 round_points(pti, &real_position, 1);
7229 }
7230 else
7231 {
7232 real_positions = heap_alloc_zero(sizeof(PointF) * length);
7233 if (!real_positions)
7234 {
7235 heap_free(pti);
7236 return OutOfMemory;
7237 }
7238
7239 memcpy(real_positions, positions, sizeof(PointF) * length);
7240
7241 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length);
7242 round_points(pti, real_positions, length);
7243
7244 heap_free(real_positions);
7245 }
7246
7247 get_font_hfont(graphics, font, format, &hfont, matrix);
7248
7249 hdc = CreateCompatibleDC(0);
7250 SelectObject(hdc, hfont);
7251
7252 /* Get the boundaries of the text to be drawn */
7253 for (i=0; i<length; i++)
7254 {
7255 DWORD glyphsize;
7256 int left, top, right, bottom;
7257
7258 glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
7259 &glyphmetrics, 0, NULL, &identity);
7260
7261 if (glyphsize == GDI_ERROR)
7262 {
7263 ERR("GetGlyphOutlineW failed\n");
7264 heap_free(pti);
7265 DeleteDC(hdc);
7266 DeleteObject(hfont);
7267 return GenericError;
7268 }
7269
7270 if (glyphsize > max_glyphsize)
7271 max_glyphsize = glyphsize;
7272
7273 if (glyphsize != 0)
7274 {
7275 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
7276 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
7277 right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
7278 bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
7279
7280 if (left < min_x) min_x = left;
7281 if (top < min_y) min_y = top;
7282 if (right > max_x) max_x = right;
7283 if (bottom > max_y) max_y = bottom;
7284 }
7285
7286 if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
7287 {
7288 pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
7289 pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
7290 }
7291 }
7292
7293 if (max_glyphsize == 0)
7294 /* Nothing to draw. */
7295 return Ok;
7296
7297 glyph_mask = heap_alloc_zero(max_glyphsize);
7298 text_mask = heap_alloc_zero((max_x - min_x) * (max_y - min_y));
7299 text_mask_stride = max_x - min_x;
7300
7301 if (!(glyph_mask && text_mask))
7302 {
7303 heap_free(glyph_mask);
7304 heap_free(text_mask);
7305 heap_free(pti);
7306 DeleteDC(hdc);
7307 DeleteObject(hfont);
7308 return OutOfMemory;
7309 }
7310
7311 /* Generate a mask for the text */
7312 for (i=0; i<length; i++)
7313 {
7314 DWORD ret;
7315 int left, top, stride;
7316
7317 ret = GetGlyphOutlineW(hdc, text[i], ggo_flags,
7318 &glyphmetrics, max_glyphsize, glyph_mask, &identity);
7319
7320 if (ret == GDI_ERROR || ret == 0)
7321 continue; /* empty glyph */
7322
7323 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
7324 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
7325 stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
7326
7327 for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
7328 {
7329 BYTE *glyph_val = glyph_mask + y * stride;
7330 BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
7331 for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
7332 {
7333 *text_val = min(64, *text_val + *glyph_val);
7334 glyph_val++;
7335 text_val++;
7336 }
7337 }
7338 }
7339
7340 heap_free(pti);
7341 DeleteDC(hdc);
7342 DeleteObject(hfont);
7343 heap_free(glyph_mask);
7344
7345 /* get the brush data */
7346 pixel_data = heap_alloc_zero(4 * (max_x - min_x) * (max_y - min_y));
7347 if (!pixel_data)
7348 {
7349 heap_free(text_mask);
7350 return OutOfMemory;
7351 }
7352
7353 pixel_area.X = min_x;
7354 pixel_area.Y = min_y;
7355 pixel_area.Width = max_x - min_x;
7356 pixel_area.Height = max_y - min_y;
7357 pixel_data_stride = pixel_area.Width * 4;
7358
7359 stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
7360 if (stat != Ok)
7361 {
7362 heap_free(text_mask);
7363 heap_free(pixel_data);
7364 return stat;
7365 }
7366
7367 /* multiply the brush data by the mask */
7368 for (y=0; y<pixel_area.Height; y++)
7369 {
7370 BYTE *text_val = text_mask + text_mask_stride * y;
7371 BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
7372 for (x=0; x<pixel_area.Width; x++)
7373 {
7374 *pixel_val = (*pixel_val) * (*text_val) / 64;
7375 text_val++;
7376 pixel_val+=4;
7377 }
7378 }
7379
7380 heap_free(text_mask);
7381
7382 gdi_transform_acquire(graphics);
7383
7384 /* draw the result */
7385 stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
7386 pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
7387
7388 gdi_transform_release(graphics);
7389
7390 heap_free(pixel_data);
7391
7392 return stat;
7393 }
7394
7395 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7396 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
7397 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
7398 INT flags, GDIPCONST GpMatrix *matrix)
7399 {
7400 GpStatus stat = NotImplemented;
7401
7402 if (length == -1)
7403 length = strlenW(text);
7404
7405 if (graphics->hdc && !graphics->alpha_hdc &&
7406 ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
7407 brush->bt == BrushTypeSolidColor &&
7408 (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
7409 stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
7410 brush, positions, flags, matrix);
7411 if (stat == NotImplemented)
7412 stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
7413 brush, positions, flags, matrix);
7414 return stat;
7415 }
7416
7417 /*****************************************************************************
7418 * GdipDrawDriverString [GDIPLUS.@]
7419 */
7420 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7421 GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
7422 GDIPCONST PointF *positions, INT flags,
7423 GDIPCONST GpMatrix *matrix )
7424 {
7425 TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
7426
7427 if (!graphics || !text || !font || !brush || !positions)
7428 return InvalidParameter;
7429
7430 return draw_driver_string(graphics, text, length, font, NULL,
7431 brush, positions, flags, matrix);
7432 }
7433
7434 /*****************************************************************************
7435 * GdipIsVisibleClipEmpty [GDIPLUS.@]
7436 */
7437 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
7438 {
7439 GpStatus stat;
7440 GpRegion* rgn;
7441
7442 TRACE("(%p, %p)\n", graphics, res);
7443
7444 if((stat = GdipCreateRegion(&rgn)) != Ok)
7445 return stat;
7446
7447 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
7448 goto cleanup;
7449
7450 stat = GdipIsEmptyRegion(rgn, graphics, res);
7451
7452 cleanup:
7453 GdipDeleteRegion(rgn);
7454 return stat;
7455 }
7456
7457 GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
7458 {
7459 static int calls;
7460
7461 TRACE("(%p) stub\n", graphics);
7462
7463 if(!(calls++))
7464 FIXME("not implemented\n");
7465
7466 return NotImplemented;
7467 }
7468
7469 GpStatus WINGDIPAPI GdipGraphicsSetAbort(GpGraphics *graphics, GdiplusAbort *pabort)
7470 {
7471 TRACE("(%p, %p)\n", graphics, pabort);
7472
7473 if (!graphics)
7474 return InvalidParameter;
7475
7476 if (pabort)
7477 FIXME("Abort callback is not supported.\n");
7478
7479 return Ok;
7480 }