d3d9: Translate E_INVALIDARG to D3DERR_INVALIDCALL in d3d9_surface_LockRect().
[wine.git] / dlls / gdiplus / graphics.c
blob3216a3ea4354d3876bb996fe861925bb72742ed4
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
19 #include <stdarg.h>
20 #include <math.h>
21 #include <limits.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27 #include "wine/unicode.h"
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
35 #include "winreg.h"
36 #include "shlwapi.h"
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
45 /* looks-right constants */
46 #define ANCHOR_WIDTH (2.0)
47 #define MAX_ITERS (50)
49 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
50 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
51 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
52 INT flags, GDIPCONST GpMatrix *matrix);
54 /* Converts from gdiplus path point type to gdi path point type. */
55 static BYTE convert_path_point_type(BYTE type)
57 BYTE ret;
59 switch(type & PathPointTypePathTypeMask){
60 case PathPointTypeBezier:
61 ret = PT_BEZIERTO;
62 break;
63 case PathPointTypeLine:
64 ret = PT_LINETO;
65 break;
66 case PathPointTypeStart:
67 ret = PT_MOVETO;
68 break;
69 default:
70 ERR("Bad point type\n");
71 return 0;
74 if(type & PathPointTypeCloseSubpath)
75 ret |= PT_CLOSEFIGURE;
77 return ret;
80 static COLORREF get_gdi_brush_color(const GpBrush *brush)
82 ARGB argb;
84 switch (brush->bt)
86 case BrushTypeSolidColor:
88 const GpSolidFill *sf = (const GpSolidFill *)brush;
89 argb = sf->color;
90 break;
92 case BrushTypeHatchFill:
94 const GpHatch *hatch = (const GpHatch *)brush;
95 argb = hatch->forecol;
96 break;
98 case BrushTypeLinearGradient:
100 const GpLineGradient *line = (const GpLineGradient *)brush;
101 argb = line->startcolor;
102 break;
104 case BrushTypePathGradient:
106 const GpPathGradient *grad = (const GpPathGradient *)brush;
107 argb = grad->centercolor;
108 break;
110 default:
111 FIXME("unhandled brush type %d\n", brush->bt);
112 argb = 0;
113 break;
115 return ARGB2COLORREF(argb);
118 static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
120 HBITMAP hbmp;
121 BITMAPINFOHEADER bmih;
122 DWORD *bits;
123 int x, y;
125 bmih.biSize = sizeof(bmih);
126 bmih.biWidth = 8;
127 bmih.biHeight = 8;
128 bmih.biPlanes = 1;
129 bmih.biBitCount = 32;
130 bmih.biCompression = BI_RGB;
131 bmih.biSizeImage = 0;
133 hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
134 if (hbmp)
136 const char *hatch_data;
138 if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
140 for (y = 0; y < 8; y++)
142 for (x = 0; x < 8; x++)
144 if (hatch_data[y] & (0x80 >> x))
145 bits[y * 8 + x] = hatch->forecol;
146 else
147 bits[y * 8 + x] = hatch->backcol;
151 else
153 FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
155 for (y = 0; y < 64; y++)
156 bits[y] = hatch->forecol;
160 return hbmp;
163 static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
165 switch (brush->bt)
167 case BrushTypeSolidColor:
169 const GpSolidFill *sf = (const GpSolidFill *)brush;
170 lb->lbStyle = BS_SOLID;
171 lb->lbColor = ARGB2COLORREF(sf->color);
172 lb->lbHatch = 0;
173 return Ok;
176 case BrushTypeHatchFill:
178 const GpHatch *hatch = (const GpHatch *)brush;
179 HBITMAP hbmp;
181 hbmp = create_hatch_bitmap(hatch);
182 if (!hbmp) return OutOfMemory;
184 lb->lbStyle = BS_PATTERN;
185 lb->lbColor = 0;
186 lb->lbHatch = (ULONG_PTR)hbmp;
187 return Ok;
190 default:
191 FIXME("unhandled brush type %d\n", brush->bt);
192 lb->lbStyle = BS_SOLID;
193 lb->lbColor = get_gdi_brush_color(brush);
194 lb->lbHatch = 0;
195 return Ok;
199 static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
201 switch (lb->lbStyle)
203 case BS_PATTERN:
204 DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
205 break;
207 return Ok;
210 static HBRUSH create_gdi_brush(const GpBrush *brush)
212 LOGBRUSH lb;
213 HBRUSH gdibrush;
215 if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
217 gdibrush = CreateBrushIndirect(&lb);
218 free_gdi_logbrush(&lb);
220 return gdibrush;
223 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
225 LOGBRUSH lb;
226 HPEN gdipen;
227 REAL width;
228 INT save_state, i, numdashes;
229 GpPointF pt[2];
230 DWORD dash_array[MAX_DASHLEN];
232 save_state = SaveDC(graphics->hdc);
234 EndPath(graphics->hdc);
236 if(pen->unit == UnitPixel){
237 width = pen->width;
239 else{
240 /* Get an estimate for the amount the pen width is affected by the world
241 * transform. (This is similar to what some of the wine drivers do.) */
242 pt[0].X = 0.0;
243 pt[0].Y = 0.0;
244 pt[1].X = 1.0;
245 pt[1].Y = 1.0;
246 GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
247 width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
248 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
250 width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
251 width *= graphics->scale;
253 pt[0].X = 0.0;
254 pt[0].Y = 0.0;
255 pt[1].X = 1.0;
256 pt[1].Y = 1.0;
257 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, pt, 2);
258 width *= sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
259 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
262 if(pen->dash == DashStyleCustom){
263 numdashes = min(pen->numdashes, MAX_DASHLEN);
265 TRACE("dashes are: ");
266 for(i = 0; i < numdashes; i++){
267 dash_array[i] = gdip_round(width * pen->dashes[i]);
268 TRACE("%d, ", dash_array[i]);
270 TRACE("\n and the pen style is %x\n", pen->style);
272 create_gdi_logbrush(pen->brush, &lb);
273 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
274 numdashes, dash_array);
275 free_gdi_logbrush(&lb);
277 else
279 create_gdi_logbrush(pen->brush, &lb);
280 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
281 free_gdi_logbrush(&lb);
284 SelectObject(graphics->hdc, gdipen);
286 return save_state;
289 static void restore_dc(GpGraphics *graphics, INT state)
291 DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
292 RestoreDC(graphics->hdc, state);
295 static void round_points(POINT *pti, GpPointF *ptf, INT count)
297 int i;
299 for(i = 0; i < count; i++){
300 pti[i].x = gdip_round(ptf[i].X);
301 pti[i].y = gdip_round(ptf[i].Y);
305 static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
306 HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
308 if (GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
309 GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
311 TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
313 StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
314 hdc, src_x, src_y, src_width, src_height, SRCCOPY);
316 else
318 BLENDFUNCTION bf;
320 bf.BlendOp = AC_SRC_OVER;
321 bf.BlendFlags = 0;
322 bf.SourceConstantAlpha = 255;
323 bf.AlphaFormat = AC_SRC_ALPHA;
325 GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
326 hdc, src_x, src_y, src_width, src_height, bf);
330 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
332 GpRegion *rgn;
333 GpMatrix transform;
334 GpStatus stat;
335 BOOL identity;
337 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, &transform);
339 if (stat == Ok)
340 stat = GdipIsMatrixIdentity(&transform, &identity);
342 if (stat == Ok)
343 stat = GdipCloneRegion(graphics->clip, &rgn);
345 if (stat == Ok)
347 if (!identity)
348 stat = GdipTransformRegion(rgn, &transform);
350 if (stat == Ok)
351 stat = GdipGetRegionHRgn(rgn, NULL, hrgn);
353 GdipDeleteRegion(rgn);
356 if (stat == Ok && graphics->gdi_clip)
358 if (*hrgn)
359 CombineRgn(*hrgn, *hrgn, graphics->gdi_clip, RGN_AND);
360 else
362 *hrgn = CreateRectRgn(0,0,0,0);
363 CombineRgn(*hrgn, graphics->gdi_clip, graphics->gdi_clip, RGN_COPY);
367 return stat;
370 /* Draw ARGB data to the given graphics object */
371 static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
372 const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt)
374 GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
375 INT x, y;
377 for (y=0; y<src_height; y++)
379 for (x=0; x<src_width; x++)
381 ARGB dst_color, src_color;
382 src_color = ((ARGB*)(src + src_stride * y))[x];
384 if (!(src_color & 0xff000000))
385 continue;
387 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
388 if (fmt & PixelFormatPAlpha)
389 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over_fgpremult(dst_color, src_color));
390 else
391 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
395 return Ok;
398 static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
399 const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
401 HDC hdc;
402 HBITMAP hbitmap;
403 BITMAPINFOHEADER bih;
404 BYTE *temp_bits;
406 hdc = CreateCompatibleDC(0);
408 bih.biSize = sizeof(BITMAPINFOHEADER);
409 bih.biWidth = src_width;
410 bih.biHeight = -src_height;
411 bih.biPlanes = 1;
412 bih.biBitCount = 32;
413 bih.biCompression = BI_RGB;
414 bih.biSizeImage = 0;
415 bih.biXPelsPerMeter = 0;
416 bih.biYPelsPerMeter = 0;
417 bih.biClrUsed = 0;
418 bih.biClrImportant = 0;
420 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
421 (void**)&temp_bits, NULL, 0);
423 if ((GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
424 GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE) ||
425 fmt & PixelFormatPAlpha)
426 memcpy(temp_bits, src, src_width * src_height * 4);
427 else
428 convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
429 4 * src_width, src, src_stride);
431 SelectObject(hdc, hbitmap);
432 gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
433 hdc, 0, 0, src_width, src_height);
434 DeleteDC(hdc);
435 DeleteObject(hbitmap);
437 return Ok;
440 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
441 const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
443 GpStatus stat=Ok;
445 if (graphics->image && graphics->image->type == ImageTypeBitmap)
447 DWORD i;
448 int size;
449 RGNDATA *rgndata;
450 RECT *rects;
451 HRGN hrgn, visible_rgn;
453 hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
454 if (!hrgn)
455 return OutOfMemory;
457 stat = get_clip_hrgn(graphics, &visible_rgn);
458 if (stat != Ok)
460 DeleteObject(hrgn);
461 return stat;
464 if (visible_rgn)
466 CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
467 DeleteObject(visible_rgn);
470 if (hregion)
471 CombineRgn(hrgn, hrgn, hregion, RGN_AND);
473 size = GetRegionData(hrgn, 0, NULL);
475 rgndata = heap_alloc_zero(size);
476 if (!rgndata)
478 DeleteObject(hrgn);
479 return OutOfMemory;
482 GetRegionData(hrgn, size, rgndata);
484 rects = (RECT*)rgndata->Buffer;
486 for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
488 stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
489 &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
490 rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
491 src_stride, fmt);
494 heap_free(rgndata);
496 DeleteObject(hrgn);
498 return stat;
500 else if (graphics->image && graphics->image->type == ImageTypeMetafile)
502 ERR("This should not be used for metafiles; fix caller\n");
503 return NotImplemented;
505 else
507 HRGN hrgn;
508 int save;
510 stat = get_clip_hrgn(graphics, &hrgn);
512 if (stat != Ok)
513 return stat;
515 save = SaveDC(graphics->hdc);
517 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
519 if (hregion)
520 ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
522 stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
523 src_height, src_stride, fmt);
525 RestoreDC(graphics->hdc, save);
527 DeleteObject(hrgn);
529 return stat;
533 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
534 const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
536 return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
539 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
541 INT start_a, end_a, final_a;
542 INT pos;
544 pos = gdip_round(position * 0xff);
546 start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
547 end_a = ((end >> 24) & 0xff) * pos;
549 final_a = start_a + end_a;
551 if (final_a < 0xff) return 0;
553 return (final_a / 0xff) << 24 |
554 ((((start >> 16) & 0xff) * start_a + (((end >> 16) & 0xff) * end_a)) / final_a) << 16 |
555 ((((start >> 8) & 0xff) * start_a + (((end >> 8) & 0xff) * end_a)) / final_a) << 8 |
556 (((start & 0xff) * start_a + ((end & 0xff) * end_a)) / final_a);
559 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
561 REAL blendfac;
563 /* clamp to between 0.0 and 1.0, using the wrap mode */
564 position = (position - brush->rect.X) / brush->rect.Width;
565 if (brush->wrap == WrapModeTile)
567 position = fmodf(position, 1.0f);
568 if (position < 0.0f) position += 1.0f;
570 else /* WrapModeFlip* */
572 position = fmodf(position, 2.0f);
573 if (position < 0.0f) position += 2.0f;
574 if (position > 1.0f) position = 2.0f - position;
577 if (brush->blendcount == 1)
578 blendfac = position;
579 else
581 int i=1;
582 REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
583 REAL range;
585 /* locate the blend positions surrounding this position */
586 while (position > brush->blendpos[i])
587 i++;
589 /* interpolate between the blend positions */
590 left_blendpos = brush->blendpos[i-1];
591 left_blendfac = brush->blendfac[i-1];
592 right_blendpos = brush->blendpos[i];
593 right_blendfac = brush->blendfac[i];
594 range = right_blendpos - left_blendpos;
595 blendfac = (left_blendfac * (right_blendpos - position) +
596 right_blendfac * (position - left_blendpos)) / range;
599 if (brush->pblendcount == 0)
600 return blend_colors(brush->startcolor, brush->endcolor, blendfac);
601 else
603 int i=1;
604 ARGB left_blendcolor, right_blendcolor;
605 REAL left_blendpos, right_blendpos;
607 /* locate the blend colors surrounding this position */
608 while (blendfac > brush->pblendpos[i])
609 i++;
611 /* interpolate between the blend colors */
612 left_blendpos = brush->pblendpos[i-1];
613 left_blendcolor = brush->pblendcolor[i-1];
614 right_blendpos = brush->pblendpos[i];
615 right_blendcolor = brush->pblendcolor[i];
616 blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
617 return blend_colors(left_blendcolor, right_blendcolor, blendfac);
621 static BOOL round_color_matrix(const ColorMatrix *matrix, int values[5][5])
623 /* Convert floating point color matrix to int[5][5], return TRUE if it's an identity */
624 BOOL identity = TRUE;
625 int i, j;
627 for (i=0; i<4; i++)
628 for (j=0; j<5; j++)
630 if (matrix->m[j][i] != (i == j ? 1.0 : 0.0))
631 identity = FALSE;
632 values[j][i] = gdip_round(matrix->m[j][i] * 256.0);
635 return identity;
638 static ARGB transform_color(ARGB color, int matrix[5][5])
640 int val[5], res[4];
641 int i, j;
642 unsigned char a, r, g, b;
644 val[0] = ((color >> 16) & 0xff); /* red */
645 val[1] = ((color >> 8) & 0xff); /* green */
646 val[2] = (color & 0xff); /* blue */
647 val[3] = ((color >> 24) & 0xff); /* alpha */
648 val[4] = 255; /* translation */
650 for (i=0; i<4; i++)
652 res[i] = 0;
654 for (j=0; j<5; j++)
655 res[i] += matrix[j][i] * val[j];
658 a = min(max(res[3] / 256, 0), 255);
659 r = min(max(res[0] / 256, 0), 255);
660 g = min(max(res[1] / 256, 0), 255);
661 b = min(max(res[2] / 256, 0), 255);
663 return (a << 24) | (r << 16) | (g << 8) | b;
666 static BOOL color_is_gray(ARGB color)
668 unsigned char r, g, b;
670 r = (color >> 16) & 0xff;
671 g = (color >> 8) & 0xff;
672 b = color & 0xff;
674 return (r == g) && (g == b);
677 /* returns preferred pixel format for the applied attributes */
678 PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
679 UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt)
681 UINT x, y;
682 INT i;
684 if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
685 attributes->noop[ColorAdjustTypeDefault] == IMAGEATTR_NOOP_SET) ||
686 (attributes->noop[type] == IMAGEATTR_NOOP_SET))
687 return fmt;
689 if (attributes->colorkeys[type].enabled ||
690 attributes->colorkeys[ColorAdjustTypeDefault].enabled)
692 const struct color_key *key;
693 BYTE min_blue, min_green, min_red;
694 BYTE max_blue, max_green, max_red;
696 if (!data || fmt != PixelFormat32bppARGB)
697 return PixelFormat32bppARGB;
699 if (attributes->colorkeys[type].enabled)
700 key = &attributes->colorkeys[type];
701 else
702 key = &attributes->colorkeys[ColorAdjustTypeDefault];
704 min_blue = key->low&0xff;
705 min_green = (key->low>>8)&0xff;
706 min_red = (key->low>>16)&0xff;
708 max_blue = key->high&0xff;
709 max_green = (key->high>>8)&0xff;
710 max_red = (key->high>>16)&0xff;
712 for (x=0; x<width; x++)
713 for (y=0; y<height; y++)
715 ARGB *src_color;
716 BYTE blue, green, red;
717 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
718 blue = *src_color&0xff;
719 green = (*src_color>>8)&0xff;
720 red = (*src_color>>16)&0xff;
721 if (blue >= min_blue && green >= min_green && red >= min_red &&
722 blue <= max_blue && green <= max_green && red <= max_red)
723 *src_color = 0x00000000;
727 if (attributes->colorremaptables[type].enabled ||
728 attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
730 const struct color_remap_table *table;
732 if (!data || fmt != PixelFormat32bppARGB)
733 return PixelFormat32bppARGB;
735 if (attributes->colorremaptables[type].enabled)
736 table = &attributes->colorremaptables[type];
737 else
738 table = &attributes->colorremaptables[ColorAdjustTypeDefault];
740 for (x=0; x<width; x++)
741 for (y=0; y<height; y++)
743 ARGB *src_color;
744 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
745 for (i=0; i<table->mapsize; i++)
747 if (*src_color == table->colormap[i].oldColor.Argb)
749 *src_color = table->colormap[i].newColor.Argb;
750 break;
756 if (attributes->colormatrices[type].enabled ||
757 attributes->colormatrices[ColorAdjustTypeDefault].enabled)
759 const struct color_matrix *colormatrices;
760 int color_matrix[5][5];
761 int gray_matrix[5][5];
762 BOOL identity;
764 if (!data || fmt != PixelFormat32bppARGB)
765 return PixelFormat32bppARGB;
767 if (attributes->colormatrices[type].enabled)
768 colormatrices = &attributes->colormatrices[type];
769 else
770 colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
772 identity = round_color_matrix(&colormatrices->colormatrix, color_matrix);
774 if (colormatrices->flags == ColorMatrixFlagsAltGray)
775 identity = (round_color_matrix(&colormatrices->graymatrix, gray_matrix) && identity);
777 if (!identity)
779 for (x=0; x<width; x++)
781 for (y=0; y<height; y++)
783 ARGB *src_color;
784 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
786 if (colormatrices->flags == ColorMatrixFlagsDefault ||
787 !color_is_gray(*src_color))
789 *src_color = transform_color(*src_color, color_matrix);
791 else if (colormatrices->flags == ColorMatrixFlagsAltGray)
793 *src_color = transform_color(*src_color, gray_matrix);
800 if (attributes->gamma_enabled[type] ||
801 attributes->gamma_enabled[ColorAdjustTypeDefault])
803 REAL gamma;
805 if (!data || fmt != PixelFormat32bppARGB)
806 return PixelFormat32bppARGB;
808 if (attributes->gamma_enabled[type])
809 gamma = attributes->gamma[type];
810 else
811 gamma = attributes->gamma[ColorAdjustTypeDefault];
813 for (x=0; x<width; x++)
814 for (y=0; y<height; y++)
816 ARGB *src_color;
817 BYTE blue, green, red;
818 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
820 blue = *src_color&0xff;
821 green = (*src_color>>8)&0xff;
822 red = (*src_color>>16)&0xff;
824 /* FIXME: We should probably use a table for this. */
825 blue = floorf(powf(blue / 255.0, gamma) * 255.0);
826 green = floorf(powf(green / 255.0, gamma) * 255.0);
827 red = floorf(powf(red / 255.0, gamma) * 255.0);
829 *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
833 return fmt;
836 /* Given a bitmap and its source rectangle, find the smallest rectangle in the
837 * bitmap that contains all the pixels we may need to draw it. */
838 static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
839 GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
840 GpRect *rect)
842 INT left, top, right, bottom;
844 switch (interpolation)
846 case InterpolationModeHighQualityBilinear:
847 case InterpolationModeHighQualityBicubic:
848 /* FIXME: Include a greater range for the prefilter? */
849 case InterpolationModeBicubic:
850 case InterpolationModeBilinear:
851 left = (INT)(floorf(srcx));
852 top = (INT)(floorf(srcy));
853 right = (INT)(ceilf(srcx+srcwidth));
854 bottom = (INT)(ceilf(srcy+srcheight));
855 break;
856 case InterpolationModeNearestNeighbor:
857 default:
858 left = gdip_round(srcx);
859 top = gdip_round(srcy);
860 right = gdip_round(srcx+srcwidth);
861 bottom = gdip_round(srcy+srcheight);
862 break;
865 if (wrap == WrapModeClamp)
867 if (left < 0)
868 left = 0;
869 if (top < 0)
870 top = 0;
871 if (right >= bitmap->width)
872 right = bitmap->width-1;
873 if (bottom >= bitmap->height)
874 bottom = bitmap->height-1;
875 if (bottom < top || right < left)
876 /* entirely outside image, just sample a pixel so we don't have to
877 * special-case this later */
878 left = top = right = bottom = 0;
880 else
882 /* In some cases we can make the rectangle smaller here, but the logic
883 * is hard to get right, and tiling suggests we're likely to use the
884 * entire source image. */
885 if (left < 0 || right >= bitmap->width)
887 left = 0;
888 right = bitmap->width-1;
891 if (top < 0 || bottom >= bitmap->height)
893 top = 0;
894 bottom = bitmap->height-1;
898 rect->X = left;
899 rect->Y = top;
900 rect->Width = right - left + 1;
901 rect->Height = bottom - top + 1;
904 static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
905 UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
907 if (attributes->wrap == WrapModeClamp)
909 if (x < 0 || y < 0 || x >= width || y >= height)
910 return attributes->outside_color;
912 else
914 /* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
915 if (x < 0)
916 x = width*2 + x % (width * 2);
917 if (y < 0)
918 y = height*2 + y % (height * 2);
920 if (attributes->wrap & WrapModeTileFlipX)
922 if ((x / width) % 2 == 0)
923 x = x % width;
924 else
925 x = width - 1 - x % width;
927 else
928 x = x % width;
930 if (attributes->wrap & WrapModeTileFlipY)
932 if ((y / height) % 2 == 0)
933 y = y % height;
934 else
935 y = height - 1 - y % height;
937 else
938 y = y % height;
941 if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
943 ERR("out of range pixel requested\n");
944 return 0xffcd0084;
947 return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
950 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
951 UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
952 InterpolationMode interpolation, PixelOffsetMode offset_mode)
954 static int fixme;
956 switch (interpolation)
958 default:
959 if (!fixme++)
960 FIXME("Unimplemented interpolation %i\n", interpolation);
961 /* fall-through */
962 case InterpolationModeBilinear:
964 REAL leftxf, topyf;
965 INT leftx, rightx, topy, bottomy;
966 ARGB topleft, topright, bottomleft, bottomright;
967 ARGB top, bottom;
968 float x_offset;
970 leftxf = floorf(point->X);
971 leftx = (INT)leftxf;
972 rightx = (INT)ceilf(point->X);
973 topyf = floorf(point->Y);
974 topy = (INT)topyf;
975 bottomy = (INT)ceilf(point->Y);
977 if (leftx == rightx && topy == bottomy)
978 return sample_bitmap_pixel(src_rect, bits, width, height,
979 leftx, topy, attributes);
981 topleft = sample_bitmap_pixel(src_rect, bits, width, height,
982 leftx, topy, attributes);
983 topright = sample_bitmap_pixel(src_rect, bits, width, height,
984 rightx, topy, attributes);
985 bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
986 leftx, bottomy, attributes);
987 bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
988 rightx, bottomy, attributes);
990 x_offset = point->X - leftxf;
991 top = blend_colors(topleft, topright, x_offset);
992 bottom = blend_colors(bottomleft, bottomright, x_offset);
994 return blend_colors(top, bottom, point->Y - topyf);
996 case InterpolationModeNearestNeighbor:
998 FLOAT pixel_offset;
999 switch (offset_mode)
1001 default:
1002 case PixelOffsetModeNone:
1003 case PixelOffsetModeHighSpeed:
1004 pixel_offset = 0.5;
1005 break;
1007 case PixelOffsetModeHalf:
1008 case PixelOffsetModeHighQuality:
1009 pixel_offset = 0.0;
1010 break;
1012 return sample_bitmap_pixel(src_rect, bits, width, height,
1013 floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
1019 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
1021 return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
1024 /* is_fill is TRUE if filling regions, FALSE for drawing primitives */
1025 static BOOL brush_can_fill_path(GpBrush *brush, BOOL is_fill)
1027 switch (brush->bt)
1029 case BrushTypeSolidColor:
1031 if (is_fill)
1032 return TRUE;
1033 else
1035 /* cannot draw semi-transparent colors */
1036 return (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000;
1039 case BrushTypeHatchFill:
1041 GpHatch *hatch = (GpHatch*)brush;
1042 return ((hatch->forecol & 0xff000000) == 0xff000000) &&
1043 ((hatch->backcol & 0xff000000) == 0xff000000);
1045 case BrushTypeLinearGradient:
1046 case BrushTypeTextureFill:
1047 /* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
1048 default:
1049 return FALSE;
1053 static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush)
1055 GpStatus status = Ok;
1056 switch (brush->bt)
1058 case BrushTypeSolidColor:
1060 GpSolidFill *fill = (GpSolidFill*)brush;
1061 HBITMAP bmp = ARGB2BMP(fill->color);
1063 if (bmp)
1065 RECT rc;
1066 /* partially transparent fill */
1068 if (!SelectClipPath(graphics->hdc, RGN_AND))
1070 status = GenericError;
1071 DeleteObject(bmp);
1072 break;
1074 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
1076 HDC hdc = CreateCompatibleDC(NULL);
1078 if (!hdc)
1080 status = OutOfMemory;
1081 DeleteObject(bmp);
1082 break;
1085 SelectObject(hdc, bmp);
1086 gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
1087 hdc, 0, 0, 1, 1);
1088 DeleteDC(hdc);
1091 DeleteObject(bmp);
1092 break;
1094 /* else fall through */
1096 default:
1098 HBRUSH gdibrush, old_brush;
1100 gdibrush = create_gdi_brush(brush);
1101 if (!gdibrush)
1103 status = OutOfMemory;
1104 break;
1107 old_brush = SelectObject(graphics->hdc, gdibrush);
1108 FillPath(graphics->hdc);
1109 SelectObject(graphics->hdc, old_brush);
1110 DeleteObject(gdibrush);
1111 break;
1115 return status;
1118 static BOOL brush_can_fill_pixels(GpBrush *brush)
1120 switch (brush->bt)
1122 case BrushTypeSolidColor:
1123 case BrushTypeHatchFill:
1124 case BrushTypeLinearGradient:
1125 case BrushTypeTextureFill:
1126 case BrushTypePathGradient:
1127 return TRUE;
1128 default:
1129 return FALSE;
1133 static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
1134 DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
1136 switch (brush->bt)
1138 case BrushTypeSolidColor:
1140 int x, y;
1141 GpSolidFill *fill = (GpSolidFill*)brush;
1142 for (x=0; x<fill_area->Width; x++)
1143 for (y=0; y<fill_area->Height; y++)
1144 argb_pixels[x + y*cdwStride] = fill->color;
1145 return Ok;
1147 case BrushTypeHatchFill:
1149 int x, y;
1150 GpHatch *fill = (GpHatch*)brush;
1151 const char *hatch_data;
1153 if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
1154 return NotImplemented;
1156 for (x=0; x<fill_area->Width; x++)
1157 for (y=0; y<fill_area->Height; y++)
1159 int hx, hy;
1161 /* FIXME: Account for the rendering origin */
1162 hx = (x + fill_area->X) % 8;
1163 hy = (y + fill_area->Y) % 8;
1165 if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
1166 argb_pixels[x + y*cdwStride] = fill->forecol;
1167 else
1168 argb_pixels[x + y*cdwStride] = fill->backcol;
1171 return Ok;
1173 case BrushTypeLinearGradient:
1175 GpLineGradient *fill = (GpLineGradient*)brush;
1176 GpPointF draw_points[3];
1177 GpStatus stat;
1178 int x, y;
1180 draw_points[0].X = fill_area->X;
1181 draw_points[0].Y = fill_area->Y;
1182 draw_points[1].X = fill_area->X+1;
1183 draw_points[1].Y = fill_area->Y;
1184 draw_points[2].X = fill_area->X;
1185 draw_points[2].Y = fill_area->Y+1;
1187 /* Transform the points to a co-ordinate space where X is the point's
1188 * position in the gradient, 0.0 being the start point and 1.0 the
1189 * end point. */
1190 stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
1191 WineCoordinateSpaceGdiDevice, draw_points, 3);
1193 if (stat == Ok)
1195 GpMatrix world_to_gradient = fill->transform;
1197 stat = GdipInvertMatrix(&world_to_gradient);
1198 if (stat == Ok)
1199 stat = GdipTransformMatrixPoints(&world_to_gradient, draw_points, 3);
1202 if (stat == Ok)
1204 REAL x_delta = draw_points[1].X - draw_points[0].X;
1205 REAL y_delta = draw_points[2].X - draw_points[0].X;
1207 for (y=0; y<fill_area->Height; y++)
1209 for (x=0; x<fill_area->Width; x++)
1211 REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
1213 argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
1218 return stat;
1220 case BrushTypeTextureFill:
1222 GpTexture *fill = (GpTexture*)brush;
1223 GpPointF draw_points[3];
1224 GpStatus stat;
1225 int x, y;
1226 GpBitmap *bitmap;
1227 int src_stride;
1228 GpRect src_area;
1230 if (fill->image->type != ImageTypeBitmap)
1232 FIXME("metafile texture brushes not implemented\n");
1233 return NotImplemented;
1236 bitmap = (GpBitmap*)fill->image;
1237 src_stride = sizeof(ARGB) * bitmap->width;
1239 src_area.X = src_area.Y = 0;
1240 src_area.Width = bitmap->width;
1241 src_area.Height = bitmap->height;
1243 draw_points[0].X = fill_area->X;
1244 draw_points[0].Y = fill_area->Y;
1245 draw_points[1].X = fill_area->X+1;
1246 draw_points[1].Y = fill_area->Y;
1247 draw_points[2].X = fill_area->X;
1248 draw_points[2].Y = fill_area->Y+1;
1250 /* Transform the points to the co-ordinate space of the bitmap. */
1251 stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
1252 WineCoordinateSpaceGdiDevice, draw_points, 3);
1254 if (stat == Ok)
1256 GpMatrix world_to_texture = fill->transform;
1258 stat = GdipInvertMatrix(&world_to_texture);
1259 if (stat == Ok)
1260 stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
1263 if (stat == Ok && !fill->bitmap_bits)
1265 BitmapData lockeddata;
1267 fill->bitmap_bits = heap_alloc_zero(sizeof(ARGB) * bitmap->width * bitmap->height);
1268 if (!fill->bitmap_bits)
1269 stat = OutOfMemory;
1271 if (stat == Ok)
1273 lockeddata.Width = bitmap->width;
1274 lockeddata.Height = bitmap->height;
1275 lockeddata.Stride = src_stride;
1276 lockeddata.PixelFormat = PixelFormat32bppARGB;
1277 lockeddata.Scan0 = fill->bitmap_bits;
1279 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
1280 PixelFormat32bppARGB, &lockeddata);
1283 if (stat == Ok)
1284 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
1286 if (stat == Ok)
1287 apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
1288 bitmap->width, bitmap->height,
1289 src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
1291 if (stat != Ok)
1293 heap_free(fill->bitmap_bits);
1294 fill->bitmap_bits = NULL;
1298 if (stat == Ok)
1300 REAL x_dx = draw_points[1].X - draw_points[0].X;
1301 REAL x_dy = draw_points[1].Y - draw_points[0].Y;
1302 REAL y_dx = draw_points[2].X - draw_points[0].X;
1303 REAL y_dy = draw_points[2].Y - draw_points[0].Y;
1305 for (y=0; y<fill_area->Height; y++)
1307 for (x=0; x<fill_area->Width; x++)
1309 GpPointF point;
1310 point.X = draw_points[0].X + x * x_dx + y * y_dx;
1311 point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
1313 argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
1314 &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
1315 &point, fill->imageattributes, graphics->interpolation,
1316 graphics->pixeloffset);
1321 return stat;
1323 case BrushTypePathGradient:
1325 GpPathGradient *fill = (GpPathGradient*)brush;
1326 GpPath *flat_path;
1327 GpMatrix world_to_device;
1328 GpStatus stat;
1329 int i, figure_start=0;
1330 GpPointF start_point, end_point, center_point;
1331 BYTE type;
1332 REAL min_yf, max_yf, line1_xf, line2_xf;
1333 INT min_y, max_y, min_x, max_x;
1334 INT x, y;
1335 ARGB outer_color;
1336 static BOOL transform_fixme_once;
1338 if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
1340 static int once;
1341 if (!once++)
1342 FIXME("path gradient focus not implemented\n");
1345 if (fill->gamma)
1347 static int once;
1348 if (!once++)
1349 FIXME("path gradient gamma correction not implemented\n");
1352 if (fill->blendcount)
1354 static int once;
1355 if (!once++)
1356 FIXME("path gradient blend not implemented\n");
1359 if (fill->pblendcount)
1361 static int once;
1362 if (!once++)
1363 FIXME("path gradient preset blend not implemented\n");
1366 if (!transform_fixme_once)
1368 BOOL is_identity=TRUE;
1369 GdipIsMatrixIdentity(&fill->transform, &is_identity);
1370 if (!is_identity)
1372 FIXME("path gradient transform not implemented\n");
1373 transform_fixme_once = TRUE;
1377 stat = GdipClonePath(fill->path, &flat_path);
1379 if (stat != Ok)
1380 return stat;
1382 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
1383 CoordinateSpaceWorld, &world_to_device);
1384 if (stat == Ok)
1386 stat = GdipTransformPath(flat_path, &world_to_device);
1388 if (stat == Ok)
1390 center_point = fill->center;
1391 stat = GdipTransformMatrixPoints(&world_to_device, &center_point, 1);
1394 if (stat == Ok)
1395 stat = GdipFlattenPath(flat_path, NULL, 0.5);
1398 if (stat != Ok)
1400 GdipDeletePath(flat_path);
1401 return stat;
1404 for (i=0; i<flat_path->pathdata.Count; i++)
1406 int start_center_line=0, end_center_line=0;
1407 BOOL seen_start = FALSE, seen_end = FALSE, seen_center = FALSE;
1408 REAL center_distance;
1409 ARGB start_color, end_color;
1410 REAL dy, dx;
1412 type = flat_path->pathdata.Types[i];
1414 if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
1415 figure_start = i;
1417 start_point = flat_path->pathdata.Points[i];
1419 start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
1421 if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
1423 end_point = flat_path->pathdata.Points[figure_start];
1424 end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
1426 else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
1428 end_point = flat_path->pathdata.Points[i+1];
1429 end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
1431 else
1432 continue;
1434 outer_color = start_color;
1436 min_yf = center_point.Y;
1437 if (min_yf > start_point.Y) min_yf = start_point.Y;
1438 if (min_yf > end_point.Y) min_yf = end_point.Y;
1440 if (min_yf < fill_area->Y)
1441 min_y = fill_area->Y;
1442 else
1443 min_y = (INT)ceil(min_yf);
1445 max_yf = center_point.Y;
1446 if (max_yf < start_point.Y) max_yf = start_point.Y;
1447 if (max_yf < end_point.Y) max_yf = end_point.Y;
1449 if (max_yf > fill_area->Y + fill_area->Height)
1450 max_y = fill_area->Y + fill_area->Height;
1451 else
1452 max_y = (INT)ceil(max_yf);
1454 dy = end_point.Y - start_point.Y;
1455 dx = end_point.X - start_point.X;
1457 /* This is proportional to the distance from start-end line to center point. */
1458 center_distance = dy * (start_point.X - center_point.X) +
1459 dx * (center_point.Y - start_point.Y);
1461 for (y=min_y; y<max_y; y++)
1463 REAL yf = (REAL)y;
1465 if (!seen_start && yf >= start_point.Y)
1467 seen_start = TRUE;
1468 start_center_line ^= 1;
1470 if (!seen_end && yf >= end_point.Y)
1472 seen_end = TRUE;
1473 end_center_line ^= 1;
1475 if (!seen_center && yf >= center_point.Y)
1477 seen_center = TRUE;
1478 start_center_line ^= 1;
1479 end_center_line ^= 1;
1482 if (start_center_line)
1483 line1_xf = intersect_line_scanline(&start_point, &center_point, yf);
1484 else
1485 line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
1487 if (end_center_line)
1488 line2_xf = intersect_line_scanline(&end_point, &center_point, yf);
1489 else
1490 line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
1492 if (line1_xf < line2_xf)
1494 min_x = (INT)ceil(line1_xf);
1495 max_x = (INT)ceil(line2_xf);
1497 else
1499 min_x = (INT)ceil(line2_xf);
1500 max_x = (INT)ceil(line1_xf);
1503 if (min_x < fill_area->X)
1504 min_x = fill_area->X;
1505 if (max_x > fill_area->X + fill_area->Width)
1506 max_x = fill_area->X + fill_area->Width;
1508 for (x=min_x; x<max_x; x++)
1510 REAL xf = (REAL)x;
1511 REAL distance;
1513 if (start_color != end_color)
1515 REAL blend_amount, pdy, pdx;
1516 pdy = yf - center_point.Y;
1517 pdx = xf - center_point.X;
1519 if (fabs(pdx) <= 0.001 && fabs(pdy) <= 0.001)
1521 /* Too close to center point, don't try to calculate outer color */
1522 outer_color = start_color;
1524 else
1526 blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
1527 outer_color = blend_colors(start_color, end_color, blend_amount);
1531 distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
1532 (end_point.X - start_point.X) * (yf - start_point.Y);
1534 distance = distance / center_distance;
1536 argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
1537 blend_colors(outer_color, fill->centercolor, distance);
1542 GdipDeletePath(flat_path);
1543 return stat;
1545 default:
1546 return NotImplemented;
1550 /* Draws the linecap the specified color and size on the hdc. The linecap is in
1551 * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
1552 * should not be called on an hdc that has a path you care about. */
1553 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
1554 const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
1556 HGDIOBJ oldbrush = NULL, oldpen = NULL;
1557 GpMatrix matrix;
1558 HBRUSH brush = NULL;
1559 HPEN pen = NULL;
1560 PointF ptf[4], *custptf = NULL;
1561 POINT pt[4], *custpt = NULL;
1562 BYTE *tp = NULL;
1563 REAL theta, dsmall, dbig, dx, dy = 0.0;
1564 INT i, count;
1565 LOGBRUSH lb;
1566 BOOL customstroke;
1568 if((x1 == x2) && (y1 == y2))
1569 return;
1571 theta = gdiplus_atan2(y2 - y1, x2 - x1);
1573 customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
1574 if(!customstroke){
1575 brush = CreateSolidBrush(color);
1576 lb.lbStyle = BS_SOLID;
1577 lb.lbColor = color;
1578 lb.lbHatch = 0;
1579 pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
1580 PS_JOIN_MITER, 1, &lb, 0,
1581 NULL);
1582 oldbrush = SelectObject(graphics->hdc, brush);
1583 oldpen = SelectObject(graphics->hdc, pen);
1586 switch(cap){
1587 case LineCapFlat:
1588 break;
1589 case LineCapSquare:
1590 case LineCapSquareAnchor:
1591 case LineCapDiamondAnchor:
1592 size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
1593 if(cap == LineCapDiamondAnchor){
1594 dsmall = cos(theta + M_PI_2) * size;
1595 dbig = sin(theta + M_PI_2) * size;
1597 else{
1598 dsmall = cos(theta + M_PI_4) * size;
1599 dbig = sin(theta + M_PI_4) * size;
1602 ptf[0].X = x2 - dsmall;
1603 ptf[1].X = x2 + dbig;
1605 ptf[0].Y = y2 - dbig;
1606 ptf[3].Y = y2 + dsmall;
1608 ptf[1].Y = y2 - dsmall;
1609 ptf[2].Y = y2 + dbig;
1611 ptf[3].X = x2 - dbig;
1612 ptf[2].X = x2 + dsmall;
1614 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
1616 round_points(pt, ptf, 4);
1618 Polygon(graphics->hdc, pt, 4);
1620 break;
1621 case LineCapArrowAnchor:
1622 size = size * 4.0 / sqrt(3.0);
1624 dx = cos(M_PI / 6.0 + theta) * size;
1625 dy = sin(M_PI / 6.0 + theta) * size;
1627 ptf[0].X = x2 - dx;
1628 ptf[0].Y = y2 - dy;
1630 dx = cos(- M_PI / 6.0 + theta) * size;
1631 dy = sin(- M_PI / 6.0 + theta) * size;
1633 ptf[1].X = x2 - dx;
1634 ptf[1].Y = y2 - dy;
1636 ptf[2].X = x2;
1637 ptf[2].Y = y2;
1639 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1641 round_points(pt, ptf, 3);
1643 Polygon(graphics->hdc, pt, 3);
1645 break;
1646 case LineCapRoundAnchor:
1647 dx = dy = ANCHOR_WIDTH * size / 2.0;
1649 ptf[0].X = x2 - dx;
1650 ptf[0].Y = y2 - dy;
1651 ptf[1].X = x2 + dx;
1652 ptf[1].Y = y2 + dy;
1654 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 2);
1656 round_points(pt, ptf, 2);
1658 Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
1660 break;
1661 case LineCapTriangle:
1662 size = size / 2.0;
1663 dx = cos(M_PI_2 + theta) * size;
1664 dy = sin(M_PI_2 + theta) * size;
1666 ptf[0].X = x2 - dx;
1667 ptf[0].Y = y2 - dy;
1668 ptf[1].X = x2 + dx;
1669 ptf[1].Y = y2 + dy;
1671 dx = cos(theta) * size;
1672 dy = sin(theta) * size;
1674 ptf[2].X = x2 + dx;
1675 ptf[2].Y = y2 + dy;
1677 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
1679 round_points(pt, ptf, 3);
1681 Polygon(graphics->hdc, pt, 3);
1683 break;
1684 case LineCapRound:
1685 dx = dy = size / 2.0;
1687 ptf[0].X = x2 - dx;
1688 ptf[0].Y = y2 - dy;
1689 ptf[1].X = x2 + dx;
1690 ptf[1].Y = y2 + dy;
1692 dx = -cos(M_PI_2 + theta) * size;
1693 dy = -sin(M_PI_2 + theta) * size;
1695 ptf[2].X = x2 - dx;
1696 ptf[2].Y = y2 - dy;
1697 ptf[3].X = x2 + dx;
1698 ptf[3].Y = y2 + dy;
1700 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
1702 round_points(pt, ptf, 4);
1704 Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
1705 pt[2].y, pt[3].x, pt[3].y);
1707 break;
1708 case LineCapCustom:
1709 if(!custom)
1710 break;
1712 if (custom->type == CustomLineCapTypeAdjustableArrow)
1714 GpAdjustableArrowCap *arrow = (GpAdjustableArrowCap *)custom;
1715 if (arrow->cap.fill && arrow->height <= 0.0)
1716 break;
1719 count = custom->pathdata.Count;
1720 custptf = heap_alloc_zero(count * sizeof(PointF));
1721 custpt = heap_alloc_zero(count * sizeof(POINT));
1722 tp = heap_alloc_zero(count);
1724 if(!custptf || !custpt || !tp)
1725 goto custend;
1727 memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
1729 GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1730 GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
1731 GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
1732 MatrixOrderAppend);
1733 GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
1734 GdipTransformMatrixPoints(&matrix, custptf, count);
1736 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, custptf, count);
1738 round_points(custpt, custptf, count);
1740 for(i = 0; i < count; i++)
1741 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
1743 if(custom->fill){
1744 BeginPath(graphics->hdc);
1745 PolyDraw(graphics->hdc, custpt, tp, count);
1746 EndPath(graphics->hdc);
1747 StrokeAndFillPath(graphics->hdc);
1749 else
1750 PolyDraw(graphics->hdc, custpt, tp, count);
1752 custend:
1753 heap_free(custptf);
1754 heap_free(custpt);
1755 heap_free(tp);
1756 break;
1757 default:
1758 break;
1761 if(!customstroke){
1762 SelectObject(graphics->hdc, oldbrush);
1763 SelectObject(graphics->hdc, oldpen);
1764 DeleteObject(brush);
1765 DeleteObject(pen);
1769 /* Shortens the line by the given percent by changing x2, y2.
1770 * If percent is > 1.0 then the line will change direction.
1771 * If percent is negative it can lengthen the line. */
1772 static void shorten_line_percent(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL percent)
1774 REAL dist, theta, dx, dy;
1776 if((y1 == *y2) && (x1 == *x2))
1777 return;
1779 dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
1780 theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
1781 dx = cos(theta) * dist;
1782 dy = sin(theta) * dist;
1784 *x2 = *x2 + dx;
1785 *y2 = *y2 + dy;
1788 /* Shortens the line by the given amount by changing x2, y2.
1789 * If the amount is greater than the distance, the line will become length 0.
1790 * If the amount is negative, it can lengthen the line. */
1791 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
1793 REAL dx, dy, percent;
1795 dx = *x2 - x1;
1796 dy = *y2 - y1;
1797 if(dx == 0 && dy == 0)
1798 return;
1800 percent = amt / sqrt(dx * dx + dy * dy);
1801 if(percent >= 1.0){
1802 *x2 = x1;
1803 *y2 = y1;
1804 return;
1807 shorten_line_percent(x1, y1, x2, y2, percent);
1810 /* Conducts a linear search to find the bezier points that will back off
1811 * the endpoint of the curve by a distance of amt. Linear search works
1812 * better than binary in this case because there are multiple solutions,
1813 * and binary searches often find a bad one. I don't think this is what
1814 * Windows does but short of rendering the bezier without GDI's help it's
1815 * the best we can do. If rev then work from the start of the passed points
1816 * instead of the end. */
1817 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
1819 GpPointF origpt[4];
1820 REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
1821 INT i, first = 0, second = 1, third = 2, fourth = 3;
1823 if(rev){
1824 first = 3;
1825 second = 2;
1826 third = 1;
1827 fourth = 0;
1830 origx = pt[fourth].X;
1831 origy = pt[fourth].Y;
1832 memcpy(origpt, pt, sizeof(GpPointF) * 4);
1834 for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
1835 /* reset bezier points to original values */
1836 memcpy(pt, origpt, sizeof(GpPointF) * 4);
1837 /* Perform magic on bezier points. Order is important here.*/
1838 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1839 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1840 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1841 shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
1842 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1843 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1845 dx = pt[fourth].X - origx;
1846 dy = pt[fourth].Y - origy;
1848 diff = sqrt(dx * dx + dy * dy);
1849 percent += 0.0005 * amt;
1853 /* Draws a combination of bezier curves and lines between points. */
1854 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
1855 GDIPCONST BYTE * types, INT count, BOOL caps)
1857 POINT *pti = heap_alloc_zero(count * sizeof(POINT));
1858 BYTE *tp = heap_alloc_zero(count);
1859 GpPointF *ptcopy = heap_alloc_zero(count * sizeof(GpPointF));
1860 INT i, j;
1861 GpStatus status = GenericError;
1863 if(!count){
1864 status = Ok;
1865 goto end;
1867 if(!pti || !tp || !ptcopy){
1868 status = OutOfMemory;
1869 goto end;
1872 for(i = 1; i < count; i++){
1873 if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
1874 if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
1875 || !(types[i + 2] & PathPointTypeBezier)){
1876 ERR("Bad bezier points\n");
1877 goto end;
1879 i += 2;
1883 memcpy(ptcopy, pt, count * sizeof(GpPointF));
1885 /* If we are drawing caps, go through the points and adjust them accordingly,
1886 * and draw the caps. */
1887 if(caps){
1888 switch(types[count - 1] & PathPointTypePathTypeMask){
1889 case PathPointTypeBezier:
1890 if(pen->endcap == LineCapArrowAnchor)
1891 shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
1892 else if((pen->endcap == LineCapCustom) && pen->customend)
1893 shorten_bezier_amt(&ptcopy[count - 4],
1894 pen->width * pen->customend->inset, FALSE);
1896 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1897 pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
1898 pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
1899 pt[count - 1].X, pt[count - 1].Y);
1901 break;
1902 case PathPointTypeLine:
1903 if(pen->endcap == LineCapArrowAnchor)
1904 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1905 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1906 pen->width);
1907 else if((pen->endcap == LineCapCustom) && pen->customend)
1908 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1909 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1910 pen->customend->inset * pen->width);
1912 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1913 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
1914 pt[count - 1].Y);
1916 break;
1917 default:
1918 ERR("Bad path last point\n");
1919 goto end;
1922 /* Find start of points */
1923 for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
1924 == PathPointTypeStart); j++);
1926 switch(types[j] & PathPointTypePathTypeMask){
1927 case PathPointTypeBezier:
1928 if(pen->startcap == LineCapArrowAnchor)
1929 shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
1930 else if((pen->startcap == LineCapCustom) && pen->customstart)
1931 shorten_bezier_amt(&ptcopy[j - 1],
1932 pen->width * pen->customstart->inset, TRUE);
1934 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
1935 pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
1936 pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
1937 pt[j - 1].X, pt[j - 1].Y);
1939 break;
1940 case PathPointTypeLine:
1941 if(pen->startcap == LineCapArrowAnchor)
1942 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1943 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1944 pen->width);
1945 else if((pen->startcap == LineCapCustom) && pen->customstart)
1946 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1947 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1948 pen->customstart->inset * pen->width);
1950 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
1951 pt[j].X, pt[j].Y, pt[j - 1].X,
1952 pt[j - 1].Y);
1954 break;
1955 default:
1956 ERR("Bad path points\n");
1957 goto end;
1961 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, count);
1963 round_points(pti, ptcopy, count);
1965 for(i = 0; i < count; i++){
1966 tp[i] = convert_path_point_type(types[i]);
1969 PolyDraw(graphics->hdc, pti, tp, count);
1971 status = Ok;
1973 end:
1974 heap_free(pti);
1975 heap_free(ptcopy);
1976 heap_free(tp);
1978 return status;
1981 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
1983 GpStatus result;
1985 BeginPath(graphics->hdc);
1986 result = draw_poly(graphics, NULL, path->pathdata.Points,
1987 path->pathdata.Types, path->pathdata.Count, FALSE);
1988 EndPath(graphics->hdc);
1989 return result;
1992 typedef enum GraphicsContainerType {
1993 BEGIN_CONTAINER,
1994 SAVE_GRAPHICS
1995 } GraphicsContainerType;
1997 typedef struct _GraphicsContainerItem {
1998 struct list entry;
1999 GraphicsContainer contid;
2000 GraphicsContainerType type;
2002 SmoothingMode smoothing;
2003 CompositingQuality compqual;
2004 InterpolationMode interpolation;
2005 CompositingMode compmode;
2006 TextRenderingHint texthint;
2007 REAL scale;
2008 GpUnit unit;
2009 PixelOffsetMode pixeloffset;
2010 UINT textcontrast;
2011 GpMatrix worldtrans;
2012 GpRegion* clip;
2013 INT origin_x, origin_y;
2014 } GraphicsContainerItem;
2016 static GpStatus init_container(GraphicsContainerItem** container,
2017 GDIPCONST GpGraphics* graphics, GraphicsContainerType type){
2018 GpStatus sts;
2020 *container = heap_alloc_zero(sizeof(GraphicsContainerItem));
2021 if(!(*container))
2022 return OutOfMemory;
2024 (*container)->contid = graphics->contid + 1;
2025 (*container)->type = type;
2027 (*container)->smoothing = graphics->smoothing;
2028 (*container)->compqual = graphics->compqual;
2029 (*container)->interpolation = graphics->interpolation;
2030 (*container)->compmode = graphics->compmode;
2031 (*container)->texthint = graphics->texthint;
2032 (*container)->scale = graphics->scale;
2033 (*container)->unit = graphics->unit;
2034 (*container)->textcontrast = graphics->textcontrast;
2035 (*container)->pixeloffset = graphics->pixeloffset;
2036 (*container)->origin_x = graphics->origin_x;
2037 (*container)->origin_y = graphics->origin_y;
2038 (*container)->worldtrans = graphics->worldtrans;
2040 sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
2041 if(sts != Ok){
2042 heap_free(*container);
2043 *container = NULL;
2044 return sts;
2047 return Ok;
2050 static void delete_container(GraphicsContainerItem* container)
2052 GdipDeleteRegion(container->clip);
2053 heap_free(container);
2056 static GpStatus restore_container(GpGraphics* graphics,
2057 GDIPCONST GraphicsContainerItem* container){
2058 GpStatus sts;
2059 GpRegion *newClip;
2061 sts = GdipCloneRegion(container->clip, &newClip);
2062 if(sts != Ok) return sts;
2064 graphics->worldtrans = container->worldtrans;
2066 GdipDeleteRegion(graphics->clip);
2067 graphics->clip = newClip;
2069 graphics->contid = container->contid - 1;
2071 graphics->smoothing = container->smoothing;
2072 graphics->compqual = container->compqual;
2073 graphics->interpolation = container->interpolation;
2074 graphics->compmode = container->compmode;
2075 graphics->texthint = container->texthint;
2076 graphics->scale = container->scale;
2077 graphics->unit = container->unit;
2078 graphics->textcontrast = container->textcontrast;
2079 graphics->pixeloffset = container->pixeloffset;
2080 graphics->origin_x = container->origin_x;
2081 graphics->origin_y = container->origin_y;
2083 return Ok;
2086 static GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect)
2088 RECT wnd_rect;
2089 GpStatus stat=Ok;
2090 GpUnit unit;
2092 if(graphics->hwnd) {
2093 if(!GetClientRect(graphics->hwnd, &wnd_rect))
2094 return GenericError;
2096 rect->X = wnd_rect.left;
2097 rect->Y = wnd_rect.top;
2098 rect->Width = wnd_rect.right - wnd_rect.left;
2099 rect->Height = wnd_rect.bottom - wnd_rect.top;
2100 }else if (graphics->image){
2101 stat = GdipGetImageBounds(graphics->image, rect, &unit);
2102 if (stat == Ok && unit != UnitPixel)
2103 FIXME("need to convert from unit %i\n", unit);
2104 }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
2105 HBITMAP hbmp;
2106 BITMAP bmp;
2108 rect->X = 0;
2109 rect->Y = 0;
2111 hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
2112 if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
2114 rect->Width = bmp.bmWidth;
2115 rect->Height = bmp.bmHeight;
2117 else
2119 /* FIXME: ??? */
2120 rect->Width = 1;
2121 rect->Height = 1;
2123 }else{
2124 rect->X = 0;
2125 rect->Y = 0;
2126 rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
2127 rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
2130 return stat;
2133 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
2135 GpStatus stat = get_graphics_device_bounds(graphics, rect);
2137 if (stat == Ok && graphics->hdc)
2139 GpPointF points[4], min_point, max_point;
2140 int i;
2142 points[0].X = points[2].X = rect->X;
2143 points[0].Y = points[1].Y = rect->Y;
2144 points[1].X = points[3].X = rect->X + rect->Width;
2145 points[2].Y = points[3].Y = rect->Y + rect->Height;
2147 gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
2149 min_point = max_point = points[0];
2151 for (i=1; i<4; i++)
2153 if (points[i].X < min_point.X) min_point.X = points[i].X;
2154 if (points[i].Y < min_point.Y) min_point.Y = points[i].Y;
2155 if (points[i].X > max_point.X) max_point.X = points[i].X;
2156 if (points[i].Y > max_point.Y) max_point.Y = points[i].Y;
2159 rect->X = min_point.X;
2160 rect->Y = min_point.Y;
2161 rect->Width = max_point.X - min_point.X;
2162 rect->Height = max_point.Y - min_point.Y;
2165 return stat;
2168 /* on success, rgn will contain the region of the graphics object which
2169 * is visible after clipping has been applied */
2170 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
2172 GpStatus stat;
2173 GpRectF rectf;
2174 GpRegion* tmp;
2176 /* Ignore graphics image bounds for metafiles */
2177 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2178 return GdipCombineRegionRegion(rgn, graphics->clip, CombineModeReplace);
2180 if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
2181 return stat;
2183 if((stat = GdipCreateRegion(&tmp)) != Ok)
2184 return stat;
2186 if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
2187 goto end;
2189 if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
2190 goto end;
2192 stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
2194 end:
2195 GdipDeleteRegion(tmp);
2196 return stat;
2199 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
2201 REAL height;
2203 if (font->unit == UnitPixel)
2205 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
2207 else
2209 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
2210 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
2211 else
2212 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
2215 lf->lfHeight = -(height + 0.5);
2216 lf->lfWidth = 0;
2217 lf->lfEscapement = 0;
2218 lf->lfOrientation = 0;
2219 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
2220 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
2221 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
2222 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
2223 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
2224 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
2225 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
2226 lf->lfQuality = DEFAULT_QUALITY;
2227 lf->lfPitchAndFamily = 0;
2228 strcpyW(lf->lfFaceName, font->family->FamilyName);
2231 static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
2232 GDIPCONST GpStringFormat *format, HFONT *hfont,
2233 GDIPCONST GpMatrix *matrix)
2235 HDC hdc = CreateCompatibleDC(0);
2236 GpPointF pt[3];
2237 REAL angle, rel_width, rel_height, font_height;
2238 LOGFONTW lfw;
2239 HFONT unscaled_font;
2240 TEXTMETRICW textmet;
2242 if (font->unit == UnitPixel || font->unit == UnitWorld)
2243 font_height = font->emSize;
2244 else
2246 REAL unit_scale, res;
2248 res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
2249 unit_scale = units_scale(font->unit, graphics->unit, res);
2251 font_height = font->emSize * unit_scale;
2254 pt[0].X = 0.0;
2255 pt[0].Y = 0.0;
2256 pt[1].X = 1.0;
2257 pt[1].Y = 0.0;
2258 pt[2].X = 0.0;
2259 pt[2].Y = 1.0;
2260 if (matrix)
2262 GpMatrix xform = *matrix;
2263 GdipTransformMatrixPoints(&xform, pt, 3);
2266 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
2267 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2268 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
2269 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
2270 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
2271 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
2273 get_log_fontW(font, graphics, &lfw);
2274 lfw.lfHeight = -gdip_round(font_height * rel_height);
2275 unscaled_font = CreateFontIndirectW(&lfw);
2277 SelectObject(hdc, unscaled_font);
2278 GetTextMetricsW(hdc, &textmet);
2280 lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
2281 lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
2283 *hfont = CreateFontIndirectW(&lfw);
2285 DeleteDC(hdc);
2286 DeleteObject(unscaled_font);
2289 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
2291 TRACE("(%p, %p)\n", hdc, graphics);
2293 return GdipCreateFromHDC2(hdc, NULL, graphics);
2296 static void get_gdi_transform(GpGraphics *graphics, GpMatrix *matrix)
2298 XFORM xform;
2300 if (graphics->hdc == NULL)
2302 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2303 return;
2306 GetTransform(graphics->hdc, 0x204, &xform);
2307 GdipSetMatrixElements(matrix, xform.eM11, xform.eM12, xform.eM21, xform.eM22, xform.eDx, xform.eDy);
2310 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
2312 GpStatus retval;
2313 HBITMAP hbitmap;
2314 DIBSECTION dib;
2316 TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
2318 if(hDevice != NULL)
2319 FIXME("Don't know how to handle parameter hDevice\n");
2321 if(hdc == NULL)
2322 return OutOfMemory;
2324 if(graphics == NULL)
2325 return InvalidParameter;
2327 *graphics = heap_alloc_zero(sizeof(GpGraphics));
2328 if(!*graphics) return OutOfMemory;
2330 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2332 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2333 heap_free(*graphics);
2334 return retval;
2337 hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
2338 if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
2339 dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
2341 (*graphics)->alpha_hdc = 1;
2344 (*graphics)->hdc = hdc;
2345 (*graphics)->hwnd = WindowFromDC(hdc);
2346 (*graphics)->owndc = FALSE;
2347 (*graphics)->smoothing = SmoothingModeDefault;
2348 (*graphics)->compqual = CompositingQualityDefault;
2349 (*graphics)->interpolation = InterpolationModeBilinear;
2350 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2351 (*graphics)->compmode = CompositingModeSourceOver;
2352 (*graphics)->unit = UnitDisplay;
2353 (*graphics)->scale = 1.0;
2354 (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
2355 (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
2356 (*graphics)->busy = FALSE;
2357 (*graphics)->textcontrast = 4;
2358 list_init(&(*graphics)->containers);
2359 (*graphics)->contid = 0;
2360 get_gdi_transform(*graphics, &(*graphics)->gdi_transform);
2362 (*graphics)->gdi_clip = CreateRectRgn(0,0,0,0);
2363 if (!GetClipRgn(hdc, (*graphics)->gdi_clip))
2365 DeleteObject((*graphics)->gdi_clip);
2366 (*graphics)->gdi_clip = NULL;
2369 TRACE("<-- %p\n", *graphics);
2371 return Ok;
2374 GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
2376 GpStatus retval;
2378 *graphics = heap_alloc_zero(sizeof(GpGraphics));
2379 if(!*graphics) return OutOfMemory;
2381 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2382 GdipSetMatrixElements(&(*graphics)->gdi_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2384 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2385 heap_free(*graphics);
2386 return retval;
2389 (*graphics)->hdc = NULL;
2390 (*graphics)->hwnd = NULL;
2391 (*graphics)->owndc = FALSE;
2392 (*graphics)->image = image;
2393 /* We have to store the image type here because the image may be freed
2394 * before GdipDeleteGraphics is called, and metafiles need special treatment. */
2395 (*graphics)->image_type = image->type;
2396 (*graphics)->smoothing = SmoothingModeDefault;
2397 (*graphics)->compqual = CompositingQualityDefault;
2398 (*graphics)->interpolation = InterpolationModeBilinear;
2399 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2400 (*graphics)->compmode = CompositingModeSourceOver;
2401 (*graphics)->unit = UnitDisplay;
2402 (*graphics)->scale = 1.0;
2403 (*graphics)->xres = image->xres;
2404 (*graphics)->yres = image->yres;
2405 (*graphics)->busy = FALSE;
2406 (*graphics)->textcontrast = 4;
2407 list_init(&(*graphics)->containers);
2408 (*graphics)->contid = 0;
2410 TRACE("<-- %p\n", *graphics);
2412 return Ok;
2415 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
2417 GpStatus ret;
2418 HDC hdc;
2420 TRACE("(%p, %p)\n", hwnd, graphics);
2422 hdc = GetDC(hwnd);
2424 if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
2426 ReleaseDC(hwnd, hdc);
2427 return ret;
2430 (*graphics)->hwnd = hwnd;
2431 (*graphics)->owndc = TRUE;
2433 return Ok;
2436 /* FIXME: no icm handling */
2437 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
2439 TRACE("(%p, %p)\n", hwnd, graphics);
2441 return GdipCreateFromHWND(hwnd, graphics);
2444 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
2445 UINT access, IStream **stream)
2447 DWORD dwMode;
2448 HRESULT ret;
2450 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
2452 if(!stream || !filename)
2453 return InvalidParameter;
2455 if(access & GENERIC_WRITE)
2456 dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
2457 else if(access & GENERIC_READ)
2458 dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
2459 else
2460 return InvalidParameter;
2462 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
2464 return hresult_to_status(ret);
2467 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
2469 GraphicsContainerItem *cont, *next;
2470 GpStatus stat;
2471 TRACE("(%p)\n", graphics);
2473 if(!graphics) return InvalidParameter;
2474 if(graphics->busy) return ObjectBusy;
2476 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2478 stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
2479 if (stat != Ok)
2480 return stat;
2483 if(graphics->owndc)
2484 ReleaseDC(graphics->hwnd, graphics->hdc);
2486 LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
2487 list_remove(&cont->entry);
2488 delete_container(cont);
2491 GdipDeleteRegion(graphics->clip);
2493 DeleteObject(graphics->gdi_clip);
2495 /* Native returns ObjectBusy on the second free, instead of crashing as we'd
2496 * do otherwise, but we can't have that in the test suite because it means
2497 * accessing freed memory. */
2498 graphics->busy = TRUE;
2500 heap_free(graphics);
2502 return Ok;
2505 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
2506 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2508 GpStatus status;
2509 GpPath *path;
2511 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2512 width, height, startAngle, sweepAngle);
2514 if(!graphics || !pen || width <= 0 || height <= 0)
2515 return InvalidParameter;
2517 if(graphics->busy)
2518 return ObjectBusy;
2520 status = GdipCreatePath(FillModeAlternate, &path);
2521 if (status != Ok) return status;
2523 status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
2524 if (status == Ok)
2525 status = GdipDrawPath(graphics, pen, path);
2527 GdipDeletePath(path);
2528 return status;
2531 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
2532 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2534 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2535 width, height, startAngle, sweepAngle);
2537 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2540 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
2541 REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
2543 GpPointF pt[4];
2545 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
2546 x2, y2, x3, y3, x4, y4);
2548 if(!graphics || !pen)
2549 return InvalidParameter;
2551 if(graphics->busy)
2552 return ObjectBusy;
2554 pt[0].X = x1;
2555 pt[0].Y = y1;
2556 pt[1].X = x2;
2557 pt[1].Y = y2;
2558 pt[2].X = x3;
2559 pt[2].Y = y3;
2560 pt[3].X = x4;
2561 pt[3].Y = y4;
2562 return GdipDrawBeziers(graphics, pen, pt, 4);
2565 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
2566 INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
2568 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
2569 x2, y2, x3, y3, x4, y4);
2571 return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
2574 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
2575 GDIPCONST GpPointF *points, INT count)
2577 GpStatus status;
2578 GpPath *path;
2580 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2582 if(!graphics || !pen || !points || (count <= 0))
2583 return InvalidParameter;
2585 if(graphics->busy)
2586 return ObjectBusy;
2588 status = GdipCreatePath(FillModeAlternate, &path);
2589 if (status != Ok) return status;
2591 status = GdipAddPathBeziers(path, points, count);
2592 if (status == Ok)
2593 status = GdipDrawPath(graphics, pen, path);
2595 GdipDeletePath(path);
2596 return status;
2599 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
2600 GDIPCONST GpPoint *points, INT count)
2602 GpPointF *pts;
2603 GpStatus ret;
2604 INT i;
2606 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2608 if(!graphics || !pen || !points || (count <= 0))
2609 return InvalidParameter;
2611 if(graphics->busy)
2612 return ObjectBusy;
2614 pts = heap_alloc_zero(sizeof(GpPointF) * count);
2615 if(!pts)
2616 return OutOfMemory;
2618 for(i = 0; i < count; i++){
2619 pts[i].X = (REAL)points[i].X;
2620 pts[i].Y = (REAL)points[i].Y;
2623 ret = GdipDrawBeziers(graphics,pen,pts,count);
2625 heap_free(pts);
2627 return ret;
2630 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
2631 GDIPCONST GpPointF *points, INT count)
2633 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2635 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
2638 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
2639 GDIPCONST GpPoint *points, INT count)
2641 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2643 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
2646 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
2647 GDIPCONST GpPointF *points, INT count, REAL tension)
2649 GpPath *path;
2650 GpStatus status;
2652 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2654 if(!graphics || !pen || !points || count <= 0)
2655 return InvalidParameter;
2657 if(graphics->busy)
2658 return ObjectBusy;
2660 status = GdipCreatePath(FillModeAlternate, &path);
2661 if (status != Ok) return status;
2663 status = GdipAddPathClosedCurve2(path, points, count, tension);
2664 if (status == Ok)
2665 status = GdipDrawPath(graphics, pen, path);
2667 GdipDeletePath(path);
2669 return status;
2672 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
2673 GDIPCONST GpPoint *points, INT count, REAL tension)
2675 GpPointF *ptf;
2676 GpStatus stat;
2677 INT i;
2679 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2681 if(!points || count <= 0)
2682 return InvalidParameter;
2684 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
2685 if(!ptf)
2686 return OutOfMemory;
2688 for(i = 0; i < count; i++){
2689 ptf[i].X = (REAL)points[i].X;
2690 ptf[i].Y = (REAL)points[i].Y;
2693 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
2695 heap_free(ptf);
2697 return stat;
2700 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
2701 GDIPCONST GpPointF *points, INT count)
2703 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2705 return GdipDrawCurve2(graphics,pen,points,count,1.0);
2708 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
2709 GDIPCONST GpPoint *points, INT count)
2711 GpPointF *pointsF;
2712 GpStatus ret;
2713 INT i;
2715 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2717 if(!points)
2718 return InvalidParameter;
2720 pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
2721 if(!pointsF)
2722 return OutOfMemory;
2724 for(i = 0; i < count; i++){
2725 pointsF[i].X = (REAL)points[i].X;
2726 pointsF[i].Y = (REAL)points[i].Y;
2729 ret = GdipDrawCurve(graphics,pen,pointsF,count);
2730 heap_free(pointsF);
2732 return ret;
2735 /* Approximates cardinal spline with Bezier curves. */
2736 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
2737 GDIPCONST GpPointF *points, INT count, REAL tension)
2739 GpPath *path;
2740 GpStatus status;
2742 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2744 if(!graphics || !pen)
2745 return InvalidParameter;
2747 if(graphics->busy)
2748 return ObjectBusy;
2750 if(count < 2)
2751 return InvalidParameter;
2753 status = GdipCreatePath(FillModeAlternate, &path);
2754 if (status != Ok) return status;
2756 status = GdipAddPathCurve2(path, points, count, tension);
2757 if (status == Ok)
2758 status = GdipDrawPath(graphics, pen, path);
2760 GdipDeletePath(path);
2761 return status;
2764 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
2765 GDIPCONST GpPoint *points, INT count, REAL tension)
2767 GpPointF *pointsF;
2768 GpStatus ret;
2769 INT i;
2771 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2773 if(!points)
2774 return InvalidParameter;
2776 pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
2777 if(!pointsF)
2778 return OutOfMemory;
2780 for(i = 0; i < count; i++){
2781 pointsF[i].X = (REAL)points[i].X;
2782 pointsF[i].Y = (REAL)points[i].Y;
2785 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
2786 heap_free(pointsF);
2788 return ret;
2791 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
2792 GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
2793 REAL tension)
2795 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2797 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2798 return InvalidParameter;
2801 return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
2804 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
2805 GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
2806 REAL tension)
2808 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2810 if(count < 0){
2811 return OutOfMemory;
2814 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2815 return InvalidParameter;
2818 return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
2821 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
2822 REAL y, REAL width, REAL height)
2824 GpPath *path;
2825 GpStatus status;
2827 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2829 if(!graphics || !pen)
2830 return InvalidParameter;
2832 if(graphics->busy)
2833 return ObjectBusy;
2835 status = GdipCreatePath(FillModeAlternate, &path);
2836 if (status != Ok) return status;
2838 status = GdipAddPathEllipse(path, x, y, width, height);
2839 if (status == Ok)
2840 status = GdipDrawPath(graphics, pen, path);
2842 GdipDeletePath(path);
2843 return status;
2846 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
2847 INT y, INT width, INT height)
2849 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2851 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2855 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
2857 UINT width, height;
2859 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
2861 if(!graphics || !image)
2862 return InvalidParameter;
2864 GdipGetImageWidth(image, &width);
2865 GdipGetImageHeight(image, &height);
2867 return GdipDrawImagePointRect(graphics, image, x, y,
2868 0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
2871 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
2872 INT y)
2874 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
2876 return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
2879 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
2880 REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
2881 GpUnit srcUnit)
2883 GpPointF points[3];
2884 REAL scale_x, scale_y, width, height;
2886 TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2888 if (!graphics || !image) return InvalidParameter;
2890 scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
2891 scale_x *= graphics->xres / image->xres;
2892 scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
2893 scale_y *= graphics->yres / image->yres;
2894 width = srcwidth * scale_x;
2895 height = srcheight * scale_y;
2897 points[0].X = points[2].X = x;
2898 points[0].Y = points[1].Y = y;
2899 points[1].X = x + width;
2900 points[2].Y = y + height;
2902 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2903 srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
2906 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
2907 INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
2908 GpUnit srcUnit)
2910 return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2913 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
2914 GDIPCONST GpPointF *dstpoints, INT count)
2916 UINT width, height;
2918 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2920 if(!image)
2921 return InvalidParameter;
2923 GdipGetImageWidth(image, &width);
2924 GdipGetImageHeight(image, &height);
2926 return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
2927 width, height, UnitPixel, NULL, NULL, NULL);
2930 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
2931 GDIPCONST GpPoint *dstpoints, INT count)
2933 GpPointF ptf[3];
2935 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2937 if (count != 3 || !dstpoints)
2938 return InvalidParameter;
2940 ptf[0].X = (REAL)dstpoints[0].X;
2941 ptf[0].Y = (REAL)dstpoints[0].Y;
2942 ptf[1].X = (REAL)dstpoints[1].X;
2943 ptf[1].Y = (REAL)dstpoints[1].Y;
2944 ptf[2].X = (REAL)dstpoints[2].X;
2945 ptf[2].Y = (REAL)dstpoints[2].Y;
2947 return GdipDrawImagePoints(graphics, image, ptf, count);
2950 static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
2951 unsigned int dataSize, const unsigned char *pStr, void *userdata)
2953 GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
2954 return TRUE;
2957 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
2958 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2959 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2960 DrawImageAbort callback, VOID * callbackData)
2962 GpPointF ptf[4];
2963 POINT pti[4];
2964 GpStatus stat;
2966 TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
2967 count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2968 callbackData);
2970 if (count > 3)
2971 return NotImplemented;
2973 if(!graphics || !image || !points || count != 3)
2974 return InvalidParameter;
2976 TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
2977 debugstr_pointf(&points[2]));
2979 if (graphics->image && graphics->image->type == ImageTypeMetafile)
2981 return METAFILE_DrawImagePointsRect((GpMetafile*)graphics->image,
2982 image, points, count, srcx, srcy, srcwidth, srcheight,
2983 srcUnit, imageAttributes, callback, callbackData);
2986 memcpy(ptf, points, 3 * sizeof(GpPointF));
2988 /* Ensure source width/height is positive */
2989 if (srcwidth < 0)
2991 GpPointF tmp = ptf[1];
2992 srcx = srcx + srcwidth;
2993 srcwidth = -srcwidth;
2994 ptf[2].X = ptf[2].X + ptf[1].X - ptf[0].X;
2995 ptf[2].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2996 ptf[1] = ptf[0];
2997 ptf[0] = tmp;
3000 if (srcheight < 0)
3002 GpPointF tmp = ptf[2];
3003 srcy = srcy + srcheight;
3004 srcheight = -srcheight;
3005 ptf[1].X = ptf[1].X + ptf[2].X - ptf[0].X;
3006 ptf[1].Y = ptf[1].Y + ptf[2].Y - ptf[0].Y;
3007 ptf[2] = ptf[0];
3008 ptf[0] = tmp;
3011 ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
3012 ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
3013 if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
3014 return Ok;
3015 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
3016 round_points(pti, ptf, 4);
3018 TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
3019 wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
3021 srcx = units_to_pixels(srcx, srcUnit, image->xres);
3022 srcy = units_to_pixels(srcy, srcUnit, image->yres);
3023 srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
3024 srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
3025 TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
3027 if (image->type == ImageTypeBitmap)
3029 GpBitmap* bitmap = (GpBitmap*)image;
3030 BOOL do_resampling = FALSE;
3031 BOOL use_software = FALSE;
3033 TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
3034 graphics->xres, graphics->yres,
3035 graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
3036 graphics->scale, image->xres, image->yres, bitmap->format,
3037 imageAttributes ? imageAttributes->outside_color : 0);
3039 if (ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
3040 ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
3041 srcx < 0 || srcy < 0 ||
3042 srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
3043 do_resampling = TRUE;
3045 if (imageAttributes || graphics->alpha_hdc || do_resampling ||
3046 (graphics->image && graphics->image->type == ImageTypeBitmap))
3047 use_software = TRUE;
3049 if (use_software)
3051 RECT dst_area;
3052 GpRectF graphics_bounds;
3053 GpRect src_area;
3054 int i, x, y, src_stride, dst_stride;
3055 GpMatrix dst_to_src;
3056 REAL m11, m12, m21, m22, mdx, mdy;
3057 LPBYTE src_data, dst_data, dst_dyn_data=NULL;
3058 BitmapData lockeddata;
3059 InterpolationMode interpolation = graphics->interpolation;
3060 PixelOffsetMode offset_mode = graphics->pixeloffset;
3061 GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
3062 REAL x_dx, x_dy, y_dx, y_dy;
3063 static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
3065 if (!imageAttributes)
3066 imageAttributes = &defaultImageAttributes;
3068 dst_area.left = dst_area.right = pti[0].x;
3069 dst_area.top = dst_area.bottom = pti[0].y;
3070 for (i=1; i<4; i++)
3072 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
3073 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
3074 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
3075 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
3078 stat = get_graphics_device_bounds(graphics, &graphics_bounds);
3079 if (stat != Ok) return stat;
3081 if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
3082 if (graphics_bounds.Y > dst_area.top) dst_area.top = floorf(graphics_bounds.Y);
3083 if (graphics_bounds.X + graphics_bounds.Width < dst_area.right) dst_area.right = ceilf(graphics_bounds.X + graphics_bounds.Width);
3084 if (graphics_bounds.Y + graphics_bounds.Height < dst_area.bottom) dst_area.bottom = ceilf(graphics_bounds.Y + graphics_bounds.Height);
3086 TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
3088 if (IsRectEmpty(&dst_area)) return Ok;
3090 m11 = (ptf[1].X - ptf[0].X) / srcwidth;
3091 m21 = (ptf[2].X - ptf[0].X) / srcheight;
3092 mdx = ptf[0].X - m11 * srcx - m21 * srcy;
3093 m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
3094 m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
3095 mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
3097 GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
3099 stat = GdipInvertMatrix(&dst_to_src);
3100 if (stat != Ok) return stat;
3102 if (do_resampling)
3104 get_bitmap_sample_size(interpolation, imageAttributes->wrap,
3105 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
3107 else
3109 /* Make sure src_area is equal in size to dst_area. */
3110 src_area.X = srcx + dst_area.left - pti[0].x;
3111 src_area.Y = srcy + dst_area.top - pti[0].y;
3112 src_area.Width = dst_area.right - dst_area.left;
3113 src_area.Height = dst_area.bottom - dst_area.top;
3116 TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
3118 src_data = heap_alloc_zero(sizeof(ARGB) * src_area.Width * src_area.Height);
3119 if (!src_data)
3120 return OutOfMemory;
3121 src_stride = sizeof(ARGB) * src_area.Width;
3123 /* Read the bits we need from the source bitmap into a compatible buffer. */
3124 lockeddata.Width = src_area.Width;
3125 lockeddata.Height = src_area.Height;
3126 lockeddata.Stride = src_stride;
3127 lockeddata.Scan0 = src_data;
3128 if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
3129 lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
3130 else
3131 lockeddata.PixelFormat = PixelFormat32bppARGB;
3133 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
3134 lockeddata.PixelFormat, &lockeddata);
3136 if (stat == Ok)
3137 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
3139 if (stat != Ok)
3141 heap_free(src_data);
3142 return stat;
3145 apply_image_attributes(imageAttributes, src_data,
3146 src_area.Width, src_area.Height,
3147 src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
3149 if (do_resampling)
3151 /* Transform the bits as needed to the destination. */
3152 dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
3153 if (!dst_data)
3155 heap_free(src_data);
3156 return OutOfMemory;
3159 dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
3161 GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
3163 x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
3164 x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
3165 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
3166 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
3168 for (x=dst_area.left; x<dst_area.right; x++)
3170 for (y=dst_area.top; y<dst_area.bottom; y++)
3172 GpPointF src_pointf;
3173 ARGB *dst_color;
3175 src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
3176 src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
3178 dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
3180 if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
3181 *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3182 imageAttributes, interpolation, offset_mode);
3183 else
3184 *dst_color = 0;
3188 else
3190 dst_data = src_data;
3191 dst_stride = src_stride;
3194 gdi_transform_acquire(graphics);
3196 stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
3197 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
3198 lockeddata.PixelFormat);
3200 gdi_transform_release(graphics);
3202 heap_free(src_data);
3204 heap_free(dst_dyn_data);
3206 return stat;
3208 else
3210 HDC hdc;
3211 BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
3212 HBITMAP hbitmap, old_hbm=NULL;
3213 HRGN hrgn;
3214 INT save_state;
3216 if (!(bitmap->format == PixelFormat16bppRGB555 ||
3217 bitmap->format == PixelFormat24bppRGB ||
3218 bitmap->format == PixelFormat32bppRGB ||
3219 bitmap->format == PixelFormat32bppPARGB))
3221 BITMAPINFOHEADER bih;
3222 BYTE *temp_bits;
3223 PixelFormat dst_format;
3225 /* we can't draw a bitmap of this format directly */
3226 hdc = CreateCompatibleDC(0);
3227 temp_hdc = TRUE;
3228 temp_bitmap = TRUE;
3230 bih.biSize = sizeof(BITMAPINFOHEADER);
3231 bih.biWidth = bitmap->width;
3232 bih.biHeight = -bitmap->height;
3233 bih.biPlanes = 1;
3234 bih.biBitCount = 32;
3235 bih.biCompression = BI_RGB;
3236 bih.biSizeImage = 0;
3237 bih.biXPelsPerMeter = 0;
3238 bih.biYPelsPerMeter = 0;
3239 bih.biClrUsed = 0;
3240 bih.biClrImportant = 0;
3242 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
3243 (void**)&temp_bits, NULL, 0);
3245 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3246 dst_format = PixelFormat32bppPARGB;
3247 else
3248 dst_format = PixelFormat32bppRGB;
3250 convert_pixels(bitmap->width, bitmap->height,
3251 bitmap->width*4, temp_bits, dst_format,
3252 bitmap->stride, bitmap->bits, bitmap->format,
3253 bitmap->image.palette);
3255 else
3257 if (bitmap->hbitmap)
3258 hbitmap = bitmap->hbitmap;
3259 else
3261 GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
3262 temp_bitmap = TRUE;
3265 hdc = bitmap->hdc;
3266 temp_hdc = (hdc == 0);
3269 if (temp_hdc)
3271 if (!hdc) hdc = CreateCompatibleDC(0);
3272 old_hbm = SelectObject(hdc, hbitmap);
3275 save_state = SaveDC(graphics->hdc);
3277 stat = get_clip_hrgn(graphics, &hrgn);
3279 if (stat == Ok)
3281 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
3282 DeleteObject(hrgn);
3285 gdi_transform_acquire(graphics);
3287 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3289 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
3290 hdc, srcx, srcy, srcwidth, srcheight);
3292 else
3294 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
3295 hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
3298 gdi_transform_release(graphics);
3300 RestoreDC(graphics->hdc, save_state);
3302 if (temp_hdc)
3304 SelectObject(hdc, old_hbm);
3305 DeleteDC(hdc);
3308 if (temp_bitmap)
3309 DeleteObject(hbitmap);
3312 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
3314 GpRectF rc;
3316 rc.X = srcx;
3317 rc.Y = srcy;
3318 rc.Width = srcwidth;
3319 rc.Height = srcheight;
3321 return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
3322 points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
3324 else
3326 WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
3327 return InvalidParameter;
3330 return Ok;
3333 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
3334 GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
3335 INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3336 DrawImageAbort callback, VOID * callbackData)
3338 GpPointF pointsF[3];
3339 INT i;
3341 TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
3342 srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
3343 callbackData);
3345 if(!points || count!=3)
3346 return InvalidParameter;
3348 for(i = 0; i < count; i++){
3349 pointsF[i].X = (REAL)points[i].X;
3350 pointsF[i].Y = (REAL)points[i].Y;
3353 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
3354 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
3355 callback, callbackData);
3358 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
3359 REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
3360 REAL srcwidth, REAL srcheight, GpUnit srcUnit,
3361 GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
3362 VOID * callbackData)
3364 GpPointF points[3];
3366 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
3367 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3368 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3370 points[0].X = dstx;
3371 points[0].Y = dsty;
3372 points[1].X = dstx + dstwidth;
3373 points[1].Y = dsty;
3374 points[2].X = dstx;
3375 points[2].Y = dsty + dstheight;
3377 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3378 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3381 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
3382 INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
3383 INT srcwidth, INT srcheight, GpUnit srcUnit,
3384 GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
3385 VOID * callbackData)
3387 GpPointF points[3];
3389 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
3390 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3391 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3393 points[0].X = dstx;
3394 points[0].Y = dsty;
3395 points[1].X = dstx + dstwidth;
3396 points[1].Y = dsty;
3397 points[2].X = dstx;
3398 points[2].Y = dsty + dstheight;
3400 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3401 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3404 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
3405 REAL x, REAL y, REAL width, REAL height)
3407 RectF bounds;
3408 GpUnit unit;
3409 GpStatus ret;
3411 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
3413 if(!graphics || !image)
3414 return InvalidParameter;
3416 ret = GdipGetImageBounds(image, &bounds, &unit);
3417 if(ret != Ok)
3418 return ret;
3420 return GdipDrawImageRectRect(graphics, image, x, y, width, height,
3421 bounds.X, bounds.Y, bounds.Width, bounds.Height,
3422 unit, NULL, NULL, NULL);
3425 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
3426 INT x, INT y, INT width, INT height)
3428 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
3430 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
3433 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
3434 REAL y1, REAL x2, REAL y2)
3436 GpPointF pt[2];
3438 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
3440 if (!pen)
3441 return InvalidParameter;
3443 if (pen->unit == UnitPixel && pen->width <= 0.0)
3444 return Ok;
3446 pt[0].X = x1;
3447 pt[0].Y = y1;
3448 pt[1].X = x2;
3449 pt[1].Y = y2;
3450 return GdipDrawLines(graphics, pen, pt, 2);
3453 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
3454 INT y1, INT x2, INT y2)
3456 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
3458 return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
3461 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
3462 GpPointF *points, INT count)
3464 GpStatus status;
3465 GpPath *path;
3467 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3469 if(!pen || !graphics || (count < 2))
3470 return InvalidParameter;
3472 if(graphics->busy)
3473 return ObjectBusy;
3475 status = GdipCreatePath(FillModeAlternate, &path);
3476 if (status != Ok) return status;
3478 status = GdipAddPathLine2(path, points, count);
3479 if (status == Ok)
3480 status = GdipDrawPath(graphics, pen, path);
3482 GdipDeletePath(path);
3483 return status;
3486 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
3487 GpPoint *points, INT count)
3489 GpStatus retval;
3490 GpPointF *ptf;
3491 int i;
3493 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3495 ptf = heap_alloc_zero(count * sizeof(GpPointF));
3496 if(!ptf) return OutOfMemory;
3498 for(i = 0; i < count; i ++){
3499 ptf[i].X = (REAL) points[i].X;
3500 ptf[i].Y = (REAL) points[i].Y;
3503 retval = GdipDrawLines(graphics, pen, ptf, count);
3505 heap_free(ptf);
3506 return retval;
3509 static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3511 INT save_state;
3512 GpStatus retval;
3513 HRGN hrgn=NULL;
3515 save_state = prepare_dc(graphics, pen);
3517 retval = get_clip_hrgn(graphics, &hrgn);
3519 if (retval != Ok)
3520 goto end;
3522 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
3524 gdi_transform_acquire(graphics);
3526 retval = draw_poly(graphics, pen, path->pathdata.Points,
3527 path->pathdata.Types, path->pathdata.Count, TRUE);
3529 gdi_transform_release(graphics);
3531 end:
3532 restore_dc(graphics, save_state);
3533 DeleteObject(hrgn);
3535 return retval;
3538 static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3540 GpStatus stat;
3541 GpPath* flat_path;
3542 GpMatrix* transform;
3543 GpRectF gp_bound_rect;
3544 GpRect gp_output_area;
3545 RECT output_area;
3546 INT output_height, output_width;
3547 DWORD *output_bits, *brush_bits=NULL;
3548 int i;
3549 static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0};
3550 const BYTE *dash_pattern;
3551 INT dash_pattern_size;
3552 BYTE *dyn_dash_pattern = NULL;
3554 stat = GdipClonePath(path, &flat_path);
3556 if (stat != Ok)
3557 return stat;
3559 stat = GdipCreateMatrix(&transform);
3561 if (stat == Ok)
3563 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
3564 CoordinateSpaceWorld, transform);
3566 if (stat == Ok)
3567 stat = GdipFlattenPath(flat_path, transform, 1.0);
3569 GdipDeleteMatrix(transform);
3572 /* estimate the output size in pixels, can be larger than necessary */
3573 if (stat == Ok)
3575 output_area.left = floorf(flat_path->pathdata.Points[0].X);
3576 output_area.right = ceilf(flat_path->pathdata.Points[0].X);
3577 output_area.top = floorf(flat_path->pathdata.Points[0].Y);
3578 output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y);
3580 for (i=1; i<flat_path->pathdata.Count; i++)
3582 REAL x, y;
3583 x = flat_path->pathdata.Points[i].X;
3584 y = flat_path->pathdata.Points[i].Y;
3586 if (floorf(x) < output_area.left) output_area.left = floorf(x);
3587 if (floorf(y) < output_area.top) output_area.top = floorf(y);
3588 if (ceilf(x) > output_area.right) output_area.right = ceilf(x);
3589 if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
3592 stat = get_graphics_device_bounds(graphics, &gp_bound_rect);
3595 if (stat == Ok)
3597 output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
3598 output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
3599 output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
3600 output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
3602 output_width = output_area.right - output_area.left + 1;
3603 output_height = output_area.bottom - output_area.top + 1;
3605 if (output_width <= 0 || output_height <= 0)
3607 GdipDeletePath(flat_path);
3608 return Ok;
3611 gp_output_area.X = output_area.left;
3612 gp_output_area.Y = output_area.top;
3613 gp_output_area.Width = output_width;
3614 gp_output_area.Height = output_height;
3616 output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
3617 if (!output_bits)
3618 stat = OutOfMemory;
3621 if (stat == Ok)
3623 if (pen->brush->bt != BrushTypeSolidColor)
3625 /* allocate and draw brush output */
3626 brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
3628 if (brush_bits)
3630 stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
3631 &gp_output_area, output_width);
3633 else
3634 stat = OutOfMemory;
3637 if (stat == Ok)
3639 /* convert dash pattern to bool array */
3640 switch (pen->dash)
3642 case DashStyleCustom:
3644 dash_pattern_size = 0;
3646 for (i=0; i < pen->numdashes; i++)
3647 dash_pattern_size += gdip_round(pen->dashes[i]);
3649 if (dash_pattern_size != 0)
3651 dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
3653 if (dyn_dash_pattern)
3655 int j=0;
3656 for (i=0; i < pen->numdashes; i++)
3658 int k;
3659 for (k=0; k < gdip_round(pen->dashes[i]); k++)
3660 dyn_dash_pattern[j++] = (i&1)^1;
3663 else
3664 stat = OutOfMemory;
3666 break;
3668 /* else fall through */
3670 case DashStyleSolid:
3671 default:
3672 dash_pattern = static_dash_pattern;
3673 dash_pattern_size = 1;
3674 break;
3675 case DashStyleDash:
3676 dash_pattern = static_dash_pattern;
3677 dash_pattern_size = 4;
3678 break;
3679 case DashStyleDot:
3680 dash_pattern = &static_dash_pattern[4];
3681 dash_pattern_size = 2;
3682 break;
3683 case DashStyleDashDot:
3684 dash_pattern = static_dash_pattern;
3685 dash_pattern_size = 6;
3686 break;
3687 case DashStyleDashDotDot:
3688 dash_pattern = static_dash_pattern;
3689 dash_pattern_size = 8;
3690 break;
3694 if (stat == Ok)
3696 /* trace path */
3697 GpPointF subpath_start = flat_path->pathdata.Points[0];
3698 INT prev_x = INT_MAX, prev_y = INT_MAX;
3699 int dash_pos = dash_pattern_size - 1;
3701 for (i=0; i < flat_path->pathdata.Count; i++)
3703 BYTE type, type2;
3704 GpPointF start_point, end_point;
3705 GpPoint start_pointi, end_pointi;
3707 type = flat_path->pathdata.Types[i];
3708 if (i+1 < flat_path->pathdata.Count)
3709 type2 = flat_path->pathdata.Types[i+1];
3710 else
3711 type2 = PathPointTypeStart;
3713 start_point = flat_path->pathdata.Points[i];
3715 if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
3716 subpath_start = start_point;
3718 if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
3719 end_point = subpath_start;
3720 else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
3721 continue;
3722 else
3723 end_point = flat_path->pathdata.Points[i+1];
3725 start_pointi.X = floorf(start_point.X);
3726 start_pointi.Y = floorf(start_point.Y);
3727 end_pointi.X = floorf(end_point.X);
3728 end_pointi.Y = floorf(end_point.Y);
3730 if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
3731 continue;
3733 /* draw line segment */
3734 if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
3736 INT x, y, start_y, end_y, step;
3738 if (start_pointi.Y < end_pointi.Y)
3740 step = 1;
3741 start_y = ceilf(start_point.Y) - output_area.top;
3742 end_y = end_pointi.Y - output_area.top;
3744 else
3746 step = -1;
3747 start_y = start_point.Y - output_area.top;
3748 end_y = ceilf(end_point.Y) - output_area.top;
3751 for (y=start_y; y != (end_y+step); y+=step)
3753 x = gdip_round( start_point.X +
3754 (end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
3755 - output_area.left;
3757 if (x == prev_x && y == prev_y)
3758 continue;
3760 prev_x = x;
3761 prev_y = y;
3762 dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
3764 if (!dash_pattern[dash_pos])
3765 continue;
3767 if (x < 0 || x >= output_width || y < 0 || y >= output_height)
3768 continue;
3770 if (brush_bits)
3771 output_bits[x + y*output_width] = brush_bits[x + y*output_width];
3772 else
3773 output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
3776 else
3778 INT x, y, start_x, end_x, step;
3780 if (start_pointi.X < end_pointi.X)
3782 step = 1;
3783 start_x = ceilf(start_point.X) - output_area.left;
3784 end_x = end_pointi.X - output_area.left;
3786 else
3788 step = -1;
3789 start_x = start_point.X - output_area.left;
3790 end_x = ceilf(end_point.X) - output_area.left;
3793 for (x=start_x; x != (end_x+step); x+=step)
3795 y = gdip_round( start_point.Y +
3796 (end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
3797 - output_area.top;
3799 if (x == prev_x && y == prev_y)
3800 continue;
3802 prev_x = x;
3803 prev_y = y;
3804 dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
3806 if (!dash_pattern[dash_pos])
3807 continue;
3809 if (x < 0 || x >= output_width || y < 0 || y >= output_height)
3810 continue;
3812 if (brush_bits)
3813 output_bits[x + y*output_width] = brush_bits[x + y*output_width];
3814 else
3815 output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
3821 /* draw output image */
3822 if (stat == Ok)
3824 gdi_transform_acquire(graphics);
3826 stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
3827 (BYTE*)output_bits, output_width, output_height, output_width * 4,
3828 PixelFormat32bppARGB);
3830 gdi_transform_release(graphics);
3833 heap_free(brush_bits);
3834 heap_free(dyn_dash_pattern);
3835 heap_free(output_bits);
3838 GdipDeletePath(flat_path);
3840 return stat;
3843 static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3845 GpStatus stat;
3846 GpPath *wide_path;
3847 GpMatrix *transform=NULL;
3848 REAL flatness=1.0;
3850 /* Check if the final pen thickness in pixels is too thin. */
3851 if (pen->unit == UnitPixel)
3853 if (pen->width < 1.415)
3854 return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
3856 else
3858 GpPointF points[3] = {{0,0}, {1,0}, {0,1}};
3860 points[1].X = pen->width;
3861 points[2].Y = pen->width;
3863 stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice,
3864 CoordinateSpaceWorld, points, 3);
3866 if (stat != Ok)
3867 return stat;
3869 if (((points[1].X-points[0].X)*(points[1].X-points[0].X) +
3870 (points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) &&
3871 ((points[2].X-points[0].X)*(points[2].X-points[0].X) +
3872 (points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001))
3873 return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
3876 stat = GdipClonePath(path, &wide_path);
3878 if (stat != Ok)
3879 return stat;
3881 if (pen->unit == UnitPixel)
3883 /* We have to transform this to device coordinates to get the widths right. */
3884 stat = GdipCreateMatrix(&transform);
3886 if (stat == Ok)
3887 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
3888 CoordinateSpaceWorld, transform);
3890 else
3892 /* Set flatness based on the final coordinate space */
3893 GpMatrix t;
3895 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
3896 CoordinateSpaceWorld, &t);
3898 if (stat != Ok)
3899 return stat;
3901 flatness = 1.0/sqrt(fmax(
3902 t.matrix[0] * t.matrix[0] + t.matrix[1] * t.matrix[1],
3903 t.matrix[2] * t.matrix[2] + t.matrix[3] * t.matrix[3]));
3906 if (stat == Ok)
3907 stat = GdipWidenPath(wide_path, pen, transform, flatness);
3909 if (pen->unit == UnitPixel)
3911 /* Transform the path back to world coordinates */
3912 if (stat == Ok)
3913 stat = GdipInvertMatrix(transform);
3915 if (stat == Ok)
3916 stat = GdipTransformPath(wide_path, transform);
3919 /* Actually draw the path */
3920 if (stat == Ok)
3921 stat = GdipFillPath(graphics, pen->brush, wide_path);
3923 GdipDeleteMatrix(transform);
3925 GdipDeletePath(wide_path);
3927 return stat;
3930 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3932 GpStatus retval;
3934 TRACE("(%p, %p, %p)\n", graphics, pen, path);
3936 if(!pen || !graphics)
3937 return InvalidParameter;
3939 if(graphics->busy)
3940 return ObjectBusy;
3942 if (path->pathdata.Count == 0)
3943 return Ok;
3945 if (graphics->image && graphics->image->type == ImageTypeMetafile)
3946 retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path);
3947 else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE))
3948 retval = SOFTWARE_GdipDrawPath(graphics, pen, path);
3949 else
3950 retval = GDI32_GdipDrawPath(graphics, pen, path);
3952 return retval;
3955 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
3956 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3958 GpStatus status;
3959 GpPath *path;
3961 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
3962 width, height, startAngle, sweepAngle);
3964 if(!graphics || !pen)
3965 return InvalidParameter;
3967 if(graphics->busy)
3968 return ObjectBusy;
3970 status = GdipCreatePath(FillModeAlternate, &path);
3971 if (status != Ok) return status;
3973 status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
3974 if (status == Ok)
3975 status = GdipDrawPath(graphics, pen, path);
3977 GdipDeletePath(path);
3978 return status;
3981 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
3982 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3984 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
3985 width, height, startAngle, sweepAngle);
3987 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3990 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
3991 REAL y, REAL width, REAL height)
3993 GpStatus status;
3994 GpPath *path;
3996 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
3998 if(!pen || !graphics)
3999 return InvalidParameter;
4001 if(graphics->busy)
4002 return ObjectBusy;
4004 status = GdipCreatePath(FillModeAlternate, &path);
4005 if (status != Ok) return status;
4007 status = GdipAddPathRectangle(path, x, y, width, height);
4008 if (status == Ok)
4009 status = GdipDrawPath(graphics, pen, path);
4011 GdipDeletePath(path);
4012 return status;
4015 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
4016 INT y, INT width, INT height)
4018 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
4020 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
4023 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
4024 GDIPCONST GpRectF* rects, INT count)
4026 GpStatus status;
4027 GpPath *path;
4029 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
4031 if(!graphics || !pen || !rects || count < 1)
4032 return InvalidParameter;
4034 if(graphics->busy)
4035 return ObjectBusy;
4037 status = GdipCreatePath(FillModeAlternate, &path);
4038 if (status != Ok) return status;
4040 status = GdipAddPathRectangles(path, rects, count);
4041 if (status == Ok)
4042 status = GdipDrawPath(graphics, pen, path);
4044 GdipDeletePath(path);
4045 return status;
4048 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
4049 GDIPCONST GpRect* rects, INT count)
4051 GpRectF *rectsF;
4052 GpStatus ret;
4053 INT i;
4055 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
4057 if(!rects || count<=0)
4058 return InvalidParameter;
4060 rectsF = heap_alloc_zero(sizeof(GpRectF) * count);
4061 if(!rectsF)
4062 return OutOfMemory;
4064 for(i = 0;i < count;i++){
4065 rectsF[i].X = (REAL)rects[i].X;
4066 rectsF[i].Y = (REAL)rects[i].Y;
4067 rectsF[i].Width = (REAL)rects[i].Width;
4068 rectsF[i].Height = (REAL)rects[i].Height;
4071 ret = GdipDrawRectangles(graphics, pen, rectsF, count);
4072 heap_free(rectsF);
4074 return ret;
4077 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
4078 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
4080 GpPath *path;
4081 GpStatus status;
4083 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
4084 count, tension, fill);
4086 if(!graphics || !brush || !points)
4087 return InvalidParameter;
4089 if(graphics->busy)
4090 return ObjectBusy;
4092 if(count == 1) /* Do nothing */
4093 return Ok;
4095 status = GdipCreatePath(fill, &path);
4096 if (status != Ok) return status;
4098 status = GdipAddPathClosedCurve2(path, points, count, tension);
4099 if (status == Ok)
4100 status = GdipFillPath(graphics, brush, path);
4102 GdipDeletePath(path);
4103 return status;
4106 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
4107 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
4109 GpPointF *ptf;
4110 GpStatus stat;
4111 INT i;
4113 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
4114 count, tension, fill);
4116 if(!points || count == 0)
4117 return InvalidParameter;
4119 if(count == 1) /* Do nothing */
4120 return Ok;
4122 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
4123 if(!ptf)
4124 return OutOfMemory;
4126 for(i = 0;i < count;i++){
4127 ptf[i].X = (REAL)points[i].X;
4128 ptf[i].Y = (REAL)points[i].Y;
4131 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
4133 heap_free(ptf);
4135 return stat;
4138 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
4139 GDIPCONST GpPointF *points, INT count)
4141 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4142 return GdipFillClosedCurve2(graphics, brush, points, count,
4143 0.5f, FillModeAlternate);
4146 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
4147 GDIPCONST GpPoint *points, INT count)
4149 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4150 return GdipFillClosedCurve2I(graphics, brush, points, count,
4151 0.5f, FillModeAlternate);
4154 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
4155 REAL y, REAL width, REAL height)
4157 GpStatus stat;
4158 GpPath *path;
4160 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
4162 if(!graphics || !brush)
4163 return InvalidParameter;
4165 if(graphics->busy)
4166 return ObjectBusy;
4168 stat = GdipCreatePath(FillModeAlternate, &path);
4170 if (stat == Ok)
4172 stat = GdipAddPathEllipse(path, x, y, width, height);
4174 if (stat == Ok)
4175 stat = GdipFillPath(graphics, brush, path);
4177 GdipDeletePath(path);
4180 return stat;
4183 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
4184 INT y, INT width, INT height)
4186 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
4188 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
4191 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
4193 INT save_state;
4194 GpStatus retval;
4195 HRGN hrgn=NULL;
4197 if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
4198 return NotImplemented;
4200 save_state = SaveDC(graphics->hdc);
4201 EndPath(graphics->hdc);
4202 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
4203 : WINDING));
4205 retval = get_clip_hrgn(graphics, &hrgn);
4207 if (retval != Ok)
4208 goto end;
4210 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
4212 gdi_transform_acquire(graphics);
4214 BeginPath(graphics->hdc);
4215 retval = draw_poly(graphics, NULL, path->pathdata.Points,
4216 path->pathdata.Types, path->pathdata.Count, FALSE);
4218 if(retval == Ok)
4220 EndPath(graphics->hdc);
4221 retval = brush_fill_path(graphics, brush);
4224 gdi_transform_release(graphics);
4226 end:
4227 RestoreDC(graphics->hdc, save_state);
4228 DeleteObject(hrgn);
4230 return retval;
4233 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
4235 GpStatus stat;
4236 GpRegion *rgn;
4238 if (!brush_can_fill_pixels(brush))
4239 return NotImplemented;
4241 /* FIXME: This could probably be done more efficiently without regions. */
4243 stat = GdipCreateRegionPath(path, &rgn);
4245 if (stat == Ok)
4247 stat = GdipFillRegion(graphics, brush, rgn);
4249 GdipDeleteRegion(rgn);
4252 return stat;
4255 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
4257 GpStatus stat = NotImplemented;
4259 TRACE("(%p, %p, %p)\n", graphics, brush, path);
4261 if(!brush || !graphics || !path)
4262 return InvalidParameter;
4264 if(graphics->busy)
4265 return ObjectBusy;
4267 if (!path->pathdata.Count)
4268 return Ok;
4270 if (graphics->image && graphics->image->type == ImageTypeMetafile)
4271 return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path);
4273 if (!graphics->image && !graphics->alpha_hdc)
4274 stat = GDI32_GdipFillPath(graphics, brush, path);
4276 if (stat == NotImplemented)
4277 stat = SOFTWARE_GdipFillPath(graphics, brush, path);
4279 if (stat == NotImplemented)
4281 FIXME("Not implemented for brushtype %i\n", brush->bt);
4282 stat = Ok;
4285 return stat;
4288 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
4289 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
4291 GpStatus stat;
4292 GpPath *path;
4294 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
4295 graphics, brush, x, y, width, height, startAngle, sweepAngle);
4297 if(!graphics || !brush)
4298 return InvalidParameter;
4300 if(graphics->busy)
4301 return ObjectBusy;
4303 stat = GdipCreatePath(FillModeAlternate, &path);
4305 if (stat == Ok)
4307 stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
4309 if (stat == Ok)
4310 stat = GdipFillPath(graphics, brush, path);
4312 GdipDeletePath(path);
4315 return stat;
4318 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
4319 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
4321 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
4322 graphics, brush, x, y, width, height, startAngle, sweepAngle);
4324 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
4327 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
4328 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
4330 GpStatus stat;
4331 GpPath *path;
4333 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
4335 if(!graphics || !brush || !points || !count)
4336 return InvalidParameter;
4338 if(graphics->busy)
4339 return ObjectBusy;
4341 stat = GdipCreatePath(fillMode, &path);
4343 if (stat == Ok)
4345 stat = GdipAddPathPolygon(path, points, count);
4347 if (stat == Ok)
4348 stat = GdipFillPath(graphics, brush, path);
4350 GdipDeletePath(path);
4353 return stat;
4356 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
4357 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
4359 GpStatus stat;
4360 GpPath *path;
4362 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
4364 if(!graphics || !brush || !points || !count)
4365 return InvalidParameter;
4367 if(graphics->busy)
4368 return ObjectBusy;
4370 stat = GdipCreatePath(fillMode, &path);
4372 if (stat == Ok)
4374 stat = GdipAddPathPolygonI(path, points, count);
4376 if (stat == Ok)
4377 stat = GdipFillPath(graphics, brush, path);
4379 GdipDeletePath(path);
4382 return stat;
4385 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
4386 GDIPCONST GpPointF *points, INT count)
4388 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4390 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
4393 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
4394 GDIPCONST GpPoint *points, INT count)
4396 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
4398 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
4401 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
4402 REAL x, REAL y, REAL width, REAL height)
4404 GpRectF rect;
4406 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
4408 rect.X = x;
4409 rect.Y = y;
4410 rect.Width = width;
4411 rect.Height = height;
4413 return GdipFillRectangles(graphics, brush, &rect, 1);
4416 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
4417 INT x, INT y, INT width, INT height)
4419 GpRectF rect;
4421 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
4423 rect.X = (REAL)x;
4424 rect.Y = (REAL)y;
4425 rect.Width = (REAL)width;
4426 rect.Height = (REAL)height;
4428 return GdipFillRectangles(graphics, brush, &rect, 1);
4431 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
4432 INT count)
4434 GpStatus status;
4435 GpPath *path;
4437 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
4439 if(!graphics || !brush || !rects || count <= 0)
4440 return InvalidParameter;
4442 if (graphics->image && graphics->image->type == ImageTypeMetafile)
4444 status = METAFILE_FillRectangles((GpMetafile*)graphics->image, brush, rects, count);
4445 /* FIXME: Add gdi32 drawing. */
4446 return status;
4449 status = GdipCreatePath(FillModeAlternate, &path);
4450 if (status != Ok) return status;
4452 status = GdipAddPathRectangles(path, rects, count);
4453 if (status == Ok)
4454 status = GdipFillPath(graphics, brush, path);
4456 GdipDeletePath(path);
4457 return status;
4460 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
4461 INT count)
4463 GpRectF *rectsF;
4464 GpStatus ret;
4465 INT i;
4467 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
4469 if(!rects || count <= 0)
4470 return InvalidParameter;
4472 rectsF = heap_alloc_zero(sizeof(GpRectF)*count);
4473 if(!rectsF)
4474 return OutOfMemory;
4476 for(i = 0; i < count; i++){
4477 rectsF[i].X = (REAL)rects[i].X;
4478 rectsF[i].Y = (REAL)rects[i].Y;
4479 rectsF[i].Width = (REAL)rects[i].Width;
4480 rectsF[i].Height = (REAL)rects[i].Height;
4483 ret = GdipFillRectangles(graphics,brush,rectsF,count);
4484 heap_free(rectsF);
4486 return ret;
4489 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
4490 GpRegion* region)
4492 INT save_state;
4493 GpStatus status;
4494 HRGN hrgn;
4495 RECT rc;
4497 if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
4498 return NotImplemented;
4500 save_state = SaveDC(graphics->hdc);
4501 EndPath(graphics->hdc);
4503 hrgn = NULL;
4504 status = get_clip_hrgn(graphics, &hrgn);
4505 if (status != Ok)
4507 RestoreDC(graphics->hdc, save_state);
4508 return status;
4511 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
4512 DeleteObject(hrgn);
4514 status = GdipGetRegionHRgn(region, graphics, &hrgn);
4515 if (status != Ok)
4517 RestoreDC(graphics->hdc, save_state);
4518 return status;
4521 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
4522 DeleteObject(hrgn);
4524 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
4526 BeginPath(graphics->hdc);
4527 Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
4528 EndPath(graphics->hdc);
4530 status = brush_fill_path(graphics, brush);
4533 RestoreDC(graphics->hdc, save_state);
4536 return status;
4539 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
4540 GpRegion* region)
4542 GpStatus stat;
4543 GpRegion *temp_region;
4544 GpMatrix world_to_device;
4545 GpRectF graphics_bounds;
4546 DWORD *pixel_data;
4547 HRGN hregion;
4548 RECT bound_rect;
4549 GpRect gp_bound_rect;
4551 if (!brush_can_fill_pixels(brush))
4552 return NotImplemented;
4554 stat = gdi_transform_acquire(graphics);
4556 if (stat == Ok)
4557 stat = get_graphics_device_bounds(graphics, &graphics_bounds);
4559 if (stat == Ok)
4560 stat = GdipCloneRegion(region, &temp_region);
4562 if (stat == Ok)
4564 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
4565 CoordinateSpaceWorld, &world_to_device);
4567 if (stat == Ok)
4568 stat = GdipTransformRegion(temp_region, &world_to_device);
4570 if (stat == Ok)
4571 stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
4573 if (stat == Ok)
4574 stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
4576 GdipDeleteRegion(temp_region);
4579 if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
4581 DeleteObject(hregion);
4582 gdi_transform_release(graphics);
4583 return Ok;
4586 if (stat == Ok)
4588 gp_bound_rect.X = bound_rect.left;
4589 gp_bound_rect.Y = bound_rect.top;
4590 gp_bound_rect.Width = bound_rect.right - bound_rect.left;
4591 gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
4593 pixel_data = heap_alloc_zero(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
4594 if (!pixel_data)
4595 stat = OutOfMemory;
4597 if (stat == Ok)
4599 stat = brush_fill_pixels(graphics, brush, pixel_data,
4600 &gp_bound_rect, gp_bound_rect.Width);
4602 if (stat == Ok)
4603 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
4604 gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
4605 gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion,
4606 PixelFormat32bppARGB);
4608 heap_free(pixel_data);
4611 DeleteObject(hregion);
4614 gdi_transform_release(graphics);
4616 return stat;
4619 /*****************************************************************************
4620 * GdipFillRegion [GDIPLUS.@]
4622 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
4623 GpRegion* region)
4625 GpStatus stat = NotImplemented;
4627 TRACE("(%p, %p, %p)\n", graphics, brush, region);
4629 if (!(graphics && brush && region))
4630 return InvalidParameter;
4632 if(graphics->busy)
4633 return ObjectBusy;
4635 if (!graphics->image && !graphics->alpha_hdc)
4636 stat = GDI32_GdipFillRegion(graphics, brush, region);
4638 if (stat == NotImplemented)
4639 stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
4641 if (stat == NotImplemented)
4643 FIXME("not implemented for brushtype %i\n", brush->bt);
4644 stat = Ok;
4647 return stat;
4650 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
4652 TRACE("(%p,%u)\n", graphics, intention);
4654 if(!graphics)
4655 return InvalidParameter;
4657 if(graphics->busy)
4658 return ObjectBusy;
4660 /* We have no internal operation queue, so there's no need to clear it. */
4662 if (graphics->hdc)
4663 GdiFlush();
4665 return Ok;
4668 /*****************************************************************************
4669 * GdipGetClipBounds [GDIPLUS.@]
4671 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
4673 GpStatus status;
4674 GpRegion *clip;
4676 TRACE("(%p, %p)\n", graphics, rect);
4678 if(!graphics)
4679 return InvalidParameter;
4681 if(graphics->busy)
4682 return ObjectBusy;
4684 status = GdipCreateRegion(&clip);
4685 if (status != Ok) return status;
4687 status = GdipGetClip(graphics, clip);
4688 if (status == Ok)
4689 status = GdipGetRegionBounds(clip, graphics, rect);
4691 GdipDeleteRegion(clip);
4692 return status;
4695 /*****************************************************************************
4696 * GdipGetClipBoundsI [GDIPLUS.@]
4698 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
4700 TRACE("(%p, %p)\n", graphics, rect);
4702 if(!graphics)
4703 return InvalidParameter;
4705 if(graphics->busy)
4706 return ObjectBusy;
4708 return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
4711 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
4712 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
4713 CompositingMode *mode)
4715 TRACE("(%p, %p)\n", graphics, mode);
4717 if(!graphics || !mode)
4718 return InvalidParameter;
4720 if(graphics->busy)
4721 return ObjectBusy;
4723 *mode = graphics->compmode;
4725 return Ok;
4728 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
4729 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
4730 CompositingQuality *quality)
4732 TRACE("(%p, %p)\n", graphics, quality);
4734 if(!graphics || !quality)
4735 return InvalidParameter;
4737 if(graphics->busy)
4738 return ObjectBusy;
4740 *quality = graphics->compqual;
4742 return Ok;
4745 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
4746 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
4747 InterpolationMode *mode)
4749 TRACE("(%p, %p)\n", graphics, mode);
4751 if(!graphics || !mode)
4752 return InvalidParameter;
4754 if(graphics->busy)
4755 return ObjectBusy;
4757 *mode = graphics->interpolation;
4759 return Ok;
4762 /* FIXME: Need to handle color depths less than 24bpp */
4763 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
4765 FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
4767 if(!graphics || !argb)
4768 return InvalidParameter;
4770 if(graphics->busy)
4771 return ObjectBusy;
4773 return Ok;
4776 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
4778 TRACE("(%p, %p)\n", graphics, scale);
4780 if(!graphics || !scale)
4781 return InvalidParameter;
4783 if(graphics->busy)
4784 return ObjectBusy;
4786 *scale = graphics->scale;
4788 return Ok;
4791 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
4793 TRACE("(%p, %p)\n", graphics, unit);
4795 if(!graphics || !unit)
4796 return InvalidParameter;
4798 if(graphics->busy)
4799 return ObjectBusy;
4801 *unit = graphics->unit;
4803 return Ok;
4806 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
4807 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4808 *mode)
4810 TRACE("(%p, %p)\n", graphics, mode);
4812 if(!graphics || !mode)
4813 return InvalidParameter;
4815 if(graphics->busy)
4816 return ObjectBusy;
4818 *mode = graphics->pixeloffset;
4820 return Ok;
4823 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
4824 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
4826 TRACE("(%p, %p)\n", graphics, mode);
4828 if(!graphics || !mode)
4829 return InvalidParameter;
4831 if(graphics->busy)
4832 return ObjectBusy;
4834 *mode = graphics->smoothing;
4836 return Ok;
4839 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
4841 TRACE("(%p, %p)\n", graphics, contrast);
4843 if(!graphics || !contrast)
4844 return InvalidParameter;
4846 *contrast = graphics->textcontrast;
4848 return Ok;
4851 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
4852 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
4853 TextRenderingHint *hint)
4855 TRACE("(%p, %p)\n", graphics, hint);
4857 if(!graphics || !hint)
4858 return InvalidParameter;
4860 if(graphics->busy)
4861 return ObjectBusy;
4863 *hint = graphics->texthint;
4865 return Ok;
4868 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
4870 GpRegion *clip_rgn;
4871 GpStatus stat;
4872 GpMatrix device_to_world;
4874 TRACE("(%p, %p)\n", graphics, rect);
4876 if(!graphics || !rect)
4877 return InvalidParameter;
4879 if(graphics->busy)
4880 return ObjectBusy;
4882 /* intersect window and graphics clipping regions */
4883 if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
4884 return stat;
4886 if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
4887 goto cleanup;
4889 /* transform to world coordinates */
4890 if((stat = get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world)) != Ok)
4891 goto cleanup;
4893 if((stat = GdipTransformRegion(clip_rgn, &device_to_world)) != Ok)
4894 goto cleanup;
4896 /* get bounds of the region */
4897 stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
4899 cleanup:
4900 GdipDeleteRegion(clip_rgn);
4902 return stat;
4905 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
4907 GpRectF rectf;
4908 GpStatus stat;
4910 TRACE("(%p, %p)\n", graphics, rect);
4912 if(!graphics || !rect)
4913 return InvalidParameter;
4915 if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
4917 rect->X = gdip_round(rectf.X);
4918 rect->Y = gdip_round(rectf.Y);
4919 rect->Width = gdip_round(rectf.Width);
4920 rect->Height = gdip_round(rectf.Height);
4923 return stat;
4926 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4928 TRACE("(%p, %p)\n", graphics, matrix);
4930 if(!graphics || !matrix)
4931 return InvalidParameter;
4933 if(graphics->busy)
4934 return ObjectBusy;
4936 *matrix = graphics->worldtrans;
4937 return Ok;
4940 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
4942 GpSolidFill *brush;
4943 GpStatus stat;
4944 GpRectF wnd_rect;
4946 TRACE("(%p, %x)\n", graphics, color);
4948 if(!graphics)
4949 return InvalidParameter;
4951 if(graphics->busy)
4952 return ObjectBusy;
4954 if (graphics->image && graphics->image->type == ImageTypeMetafile)
4955 return METAFILE_GraphicsClear((GpMetafile*)graphics->image, color);
4957 if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
4958 return stat;
4960 if((stat = GdipGetVisibleClipBounds(graphics, &wnd_rect)) != Ok){
4961 GdipDeleteBrush((GpBrush*)brush);
4962 return stat;
4965 GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
4966 wnd_rect.Width, wnd_rect.Height);
4968 GdipDeleteBrush((GpBrush*)brush);
4970 return Ok;
4973 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
4975 TRACE("(%p, %p)\n", graphics, res);
4977 if(!graphics || !res)
4978 return InvalidParameter;
4980 return GdipIsEmptyRegion(graphics->clip, graphics, res);
4983 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
4985 GpStatus stat;
4986 GpRegion* rgn;
4987 GpPointF pt;
4989 TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
4991 if(!graphics || !result)
4992 return InvalidParameter;
4994 if(graphics->busy)
4995 return ObjectBusy;
4997 pt.X = x;
4998 pt.Y = y;
4999 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
5000 CoordinateSpaceWorld, &pt, 1)) != Ok)
5001 return stat;
5003 if((stat = GdipCreateRegion(&rgn)) != Ok)
5004 return stat;
5006 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
5007 goto cleanup;
5009 stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
5011 cleanup:
5012 GdipDeleteRegion(rgn);
5013 return stat;
5016 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
5018 return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
5021 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
5023 GpStatus stat;
5024 GpRegion* rgn;
5025 GpPointF pts[2];
5027 TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
5029 if(!graphics || !result)
5030 return InvalidParameter;
5032 if(graphics->busy)
5033 return ObjectBusy;
5035 pts[0].X = x;
5036 pts[0].Y = y;
5037 pts[1].X = x + width;
5038 pts[1].Y = y + height;
5040 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
5041 CoordinateSpaceWorld, pts, 2)) != Ok)
5042 return stat;
5044 pts[1].X -= pts[0].X;
5045 pts[1].Y -= pts[0].Y;
5047 if((stat = GdipCreateRegion(&rgn)) != Ok)
5048 return stat;
5050 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
5051 goto cleanup;
5053 stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
5055 cleanup:
5056 GdipDeleteRegion(rgn);
5057 return stat;
5060 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
5062 return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
5065 GpStatus gdip_format_string(HDC hdc,
5066 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
5067 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
5068 gdip_format_string_callback callback, void *user_data)
5070 WCHAR* stringdup;
5071 int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
5072 nheight, lineend, lineno = 0;
5073 RectF bounds;
5074 StringAlignment halign;
5075 GpStatus stat = Ok;
5076 SIZE size;
5077 HotkeyPrefix hkprefix;
5078 INT *hotkeyprefix_offsets=NULL;
5079 INT hotkeyprefix_count=0;
5080 INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
5081 BOOL seen_prefix = FALSE;
5083 if(length == -1) length = lstrlenW(string);
5085 stringdup = heap_alloc_zero((length + 1) * sizeof(WCHAR));
5086 if(!stringdup) return OutOfMemory;
5088 if (!format)
5089 format = &default_drawstring_format;
5091 nwidth = rect->Width;
5092 nheight = rect->Height;
5093 if (ignore_empty_clip)
5095 if (!nwidth) nwidth = INT_MAX;
5096 if (!nheight) nheight = INT_MAX;
5099 hkprefix = format->hkprefix;
5101 if (hkprefix == HotkeyPrefixShow)
5103 for (i=0; i<length; i++)
5105 if (string[i] == '&')
5106 hotkeyprefix_count++;
5110 if (hotkeyprefix_count)
5111 hotkeyprefix_offsets = heap_alloc_zero(sizeof(INT) * hotkeyprefix_count);
5113 hotkeyprefix_count = 0;
5115 for(i = 0, j = 0; i < length; i++){
5116 /* FIXME: This makes the indexes passed to callback inaccurate. */
5117 if(!isprintW(string[i]) && (string[i] != '\n'))
5118 continue;
5120 /* FIXME: tabs should be handled using tabstops from stringformat */
5121 if (string[i] == '\t')
5122 continue;
5124 if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
5125 hotkeyprefix_offsets[hotkeyprefix_count++] = j;
5126 else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
5128 seen_prefix = TRUE;
5129 continue;
5132 seen_prefix = FALSE;
5134 stringdup[j] = string[i];
5135 j++;
5138 length = j;
5140 halign = format->align;
5142 while(sum < length){
5143 GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
5144 nwidth, &fit, NULL, &size);
5145 fitcpy = fit;
5147 if(fit == 0)
5148 break;
5150 for(lret = 0; lret < fit; lret++)
5151 if(*(stringdup + sum + lret) == '\n')
5152 break;
5154 /* Line break code (may look strange, but it imitates windows). */
5155 if(lret < fit)
5156 lineend = fit = lret; /* this is not an off-by-one error */
5157 else if(fit < (length - sum)){
5158 if(*(stringdup + sum + fit) == ' ')
5159 while(*(stringdup + sum + fit) == ' ')
5160 fit++;
5161 else
5162 while(*(stringdup + sum + fit - 1) != ' '){
5163 fit--;
5165 if(*(stringdup + sum + fit) == '\t')
5166 break;
5168 if(fit == 0){
5169 fit = fitcpy;
5170 break;
5173 lineend = fit;
5174 while(*(stringdup + sum + lineend - 1) == ' ' ||
5175 *(stringdup + sum + lineend - 1) == '\t')
5176 lineend--;
5178 else
5179 lineend = fit;
5181 GetTextExtentExPointW(hdc, stringdup + sum, lineend,
5182 nwidth, &j, NULL, &size);
5184 bounds.Width = size.cx;
5186 if(height + size.cy > nheight)
5188 if (format->attr & StringFormatFlagsLineLimit)
5189 break;
5190 bounds.Height = nheight - (height + size.cy);
5192 else
5193 bounds.Height = size.cy;
5195 bounds.Y = rect->Y + height;
5197 switch (halign)
5199 case StringAlignmentNear:
5200 default:
5201 bounds.X = rect->X;
5202 break;
5203 case StringAlignmentCenter:
5204 bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
5205 break;
5206 case StringAlignmentFar:
5207 bounds.X = rect->X + rect->Width - bounds.Width;
5208 break;
5211 for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
5212 if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
5213 break;
5215 stat = callback(hdc, stringdup, sum, lineend,
5216 font, rect, format, lineno, &bounds,
5217 &hotkeyprefix_offsets[hotkeyprefix_pos],
5218 hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
5220 if (stat != Ok)
5221 break;
5223 sum += fit + (lret < fitcpy ? 1 : 0);
5224 height += size.cy;
5225 lineno++;
5227 hotkeyprefix_pos = hotkeyprefix_end_pos;
5229 if(height > nheight)
5230 break;
5232 /* Stop if this was a linewrap (but not if it was a linebreak). */
5233 if ((lret == fitcpy) && (format->attr & StringFormatFlagsNoWrap))
5234 break;
5237 heap_free(stringdup);
5238 heap_free(hotkeyprefix_offsets);
5240 return stat;
5243 struct measure_ranges_args {
5244 GpRegion **regions;
5245 REAL rel_width, rel_height;
5248 static GpStatus measure_ranges_callback(HDC hdc,
5249 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
5250 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
5251 INT lineno, const RectF *bounds, INT *underlined_indexes,
5252 INT underlined_index_count, void *user_data)
5254 int i;
5255 GpStatus stat = Ok;
5256 struct measure_ranges_args *args = user_data;
5258 for (i=0; i<format->range_count; i++)
5260 INT range_start = max(index, format->character_ranges[i].First);
5261 INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
5262 if (range_start < range_end)
5264 GpRectF range_rect;
5265 SIZE range_size;
5267 range_rect.Y = bounds->Y / args->rel_height;
5268 range_rect.Height = bounds->Height / args->rel_height;
5270 GetTextExtentExPointW(hdc, string + index, range_start - index,
5271 INT_MAX, NULL, NULL, &range_size);
5272 range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
5274 GetTextExtentExPointW(hdc, string + index, range_end - index,
5275 INT_MAX, NULL, NULL, &range_size);
5276 range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
5278 stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
5279 if (stat != Ok)
5280 break;
5284 return stat;
5287 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
5288 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
5289 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
5290 INT regionCount, GpRegion** regions)
5292 GpStatus stat;
5293 int i;
5294 HFONT gdifont, oldfont;
5295 struct measure_ranges_args args;
5296 HDC hdc, temp_hdc=NULL;
5297 GpPointF pt[3];
5298 RectF scaled_rect;
5299 REAL margin_x;
5301 TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_wn(string, length),
5302 length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
5304 if (!(graphics && string && font && layoutRect && stringFormat && regions))
5305 return InvalidParameter;
5307 if (regionCount < stringFormat->range_count)
5308 return InvalidParameter;
5310 if(!graphics->hdc)
5312 hdc = temp_hdc = CreateCompatibleDC(0);
5313 if (!temp_hdc) return OutOfMemory;
5315 else
5316 hdc = graphics->hdc;
5318 if (stringFormat->attr)
5319 TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
5321 pt[0].X = 0.0;
5322 pt[0].Y = 0.0;
5323 pt[1].X = 1.0;
5324 pt[1].Y = 0.0;
5325 pt[2].X = 0.0;
5326 pt[2].Y = 1.0;
5327 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
5328 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5329 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5330 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5331 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5333 margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
5334 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5336 scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
5337 scaled_rect.Y = layoutRect->Y * args.rel_height;
5338 scaled_rect.Width = layoutRect->Width * args.rel_width;
5339 scaled_rect.Height = layoutRect->Height * args.rel_height;
5341 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5342 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5344 get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
5345 oldfont = SelectObject(hdc, gdifont);
5347 for (i=0; i<stringFormat->range_count; i++)
5349 stat = GdipSetEmpty(regions[i]);
5350 if (stat != Ok)
5351 return stat;
5354 args.regions = regions;
5356 gdi_transform_acquire(graphics);
5358 stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
5359 (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
5361 gdi_transform_release(graphics);
5363 SelectObject(hdc, oldfont);
5364 DeleteObject(gdifont);
5366 if (temp_hdc)
5367 DeleteDC(temp_hdc);
5369 return stat;
5372 struct measure_string_args {
5373 RectF *bounds;
5374 INT *codepointsfitted;
5375 INT *linesfilled;
5376 REAL rel_width, rel_height;
5379 static GpStatus measure_string_callback(HDC hdc,
5380 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
5381 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
5382 INT lineno, const RectF *bounds, INT *underlined_indexes,
5383 INT underlined_index_count, void *user_data)
5385 struct measure_string_args *args = user_data;
5386 REAL new_width, new_height;
5388 new_width = bounds->Width / args->rel_width;
5389 new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
5391 if (new_width > args->bounds->Width)
5392 args->bounds->Width = new_width;
5394 if (new_height > args->bounds->Height)
5395 args->bounds->Height = new_height;
5397 if (args->codepointsfitted)
5398 *args->codepointsfitted = index + length;
5400 if (args->linesfilled)
5401 (*args->linesfilled)++;
5403 return Ok;
5406 /* Find the smallest rectangle that bounds the text when it is printed in rect
5407 * according to the format options listed in format. If rect has 0 width and
5408 * height, then just find the smallest rectangle that bounds the text when it's
5409 * printed at location (rect->X, rect-Y). */
5410 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
5411 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
5412 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
5413 INT *codepointsfitted, INT *linesfilled)
5415 HFONT oldfont, gdifont;
5416 struct measure_string_args args;
5417 HDC temp_hdc=NULL, hdc;
5418 GpPointF pt[3];
5419 RectF scaled_rect;
5420 REAL margin_x;
5421 INT lines, glyphs;
5423 TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
5424 debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
5425 bounds, codepointsfitted, linesfilled);
5427 if(!graphics || !string || !font || !rect || !bounds)
5428 return InvalidParameter;
5430 if(!graphics->hdc)
5432 hdc = temp_hdc = CreateCompatibleDC(0);
5433 if (!temp_hdc) return OutOfMemory;
5435 else
5436 hdc = graphics->hdc;
5438 if(linesfilled) *linesfilled = 0;
5439 if(codepointsfitted) *codepointsfitted = 0;
5441 if(format)
5442 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
5444 pt[0].X = 0.0;
5445 pt[0].Y = 0.0;
5446 pt[1].X = 1.0;
5447 pt[1].Y = 0.0;
5448 pt[2].X = 0.0;
5449 pt[2].Y = 1.0;
5450 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
5451 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5452 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5453 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5454 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5456 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
5457 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5459 scaled_rect.X = (rect->X + margin_x) * args.rel_width;
5460 scaled_rect.Y = rect->Y * args.rel_height;
5461 scaled_rect.Width = rect->Width * args.rel_width;
5462 scaled_rect.Height = rect->Height * args.rel_height;
5463 if (scaled_rect.Width >= 0.5)
5465 scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
5466 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
5469 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5470 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5472 get_font_hfont(graphics, font, format, &gdifont, NULL);
5473 oldfont = SelectObject(hdc, gdifont);
5475 bounds->X = rect->X;
5476 bounds->Y = rect->Y;
5477 bounds->Width = 0.0;
5478 bounds->Height = 0.0;
5480 args.bounds = bounds;
5481 args.codepointsfitted = &glyphs;
5482 args.linesfilled = &lines;
5483 lines = glyphs = 0;
5485 gdi_transform_acquire(graphics);
5487 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
5488 measure_string_callback, &args);
5490 gdi_transform_release(graphics);
5492 if (linesfilled) *linesfilled = lines;
5493 if (codepointsfitted) *codepointsfitted = glyphs;
5495 if (lines)
5496 bounds->Width += margin_x * 2.0;
5498 SelectObject(hdc, oldfont);
5499 DeleteObject(gdifont);
5501 if (temp_hdc)
5502 DeleteDC(temp_hdc);
5504 return Ok;
5507 struct draw_string_args {
5508 GpGraphics *graphics;
5509 GDIPCONST GpBrush *brush;
5510 REAL x, y, rel_width, rel_height, ascent;
5513 static GpStatus draw_string_callback(HDC hdc,
5514 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
5515 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
5516 INT lineno, const RectF *bounds, INT *underlined_indexes,
5517 INT underlined_index_count, void *user_data)
5519 struct draw_string_args *args = user_data;
5520 PointF position;
5521 GpStatus stat;
5523 position.X = args->x + bounds->X / args->rel_width;
5524 position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
5526 stat = draw_driver_string(args->graphics, &string[index], length, font, format,
5527 args->brush, &position,
5528 DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
5530 if (stat == Ok && underlined_index_count)
5532 OUTLINETEXTMETRICW otm;
5533 REAL underline_y, underline_height;
5534 int i;
5536 GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
5538 underline_height = otm.otmsUnderscoreSize / args->rel_height;
5539 underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
5541 for (i=0; i<underlined_index_count; i++)
5543 REAL start_x, end_x;
5544 SIZE text_size;
5545 INT ofs = underlined_indexes[i] - index;
5547 GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
5548 start_x = text_size.cx / args->rel_width;
5550 GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
5551 end_x = text_size.cx / args->rel_width;
5553 GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
5557 return stat;
5560 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
5561 INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
5562 GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
5564 HRGN rgn = NULL;
5565 HFONT gdifont;
5566 GpPointF pt[3], rectcpy[4];
5567 POINT corners[4];
5568 REAL rel_width, rel_height, margin_x;
5569 INT save_state, format_flags = 0;
5570 REAL offsety = 0.0;
5571 struct draw_string_args args;
5572 RectF scaled_rect;
5573 HDC hdc, temp_hdc=NULL;
5574 TEXTMETRICW textmetric;
5576 TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
5577 length, font, debugstr_rectf(rect), format, brush);
5579 if(!graphics || !string || !font || !brush || !rect)
5580 return InvalidParameter;
5582 if(graphics->hdc)
5584 hdc = graphics->hdc;
5586 else
5588 hdc = temp_hdc = CreateCompatibleDC(0);
5591 if(format){
5592 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
5594 format_flags = format->attr;
5596 /* Should be no need to explicitly test for StringAlignmentNear as
5597 * that is default behavior if no alignment is passed. */
5598 if(format->line_align != StringAlignmentNear){
5599 RectF bounds, in_rect = *rect;
5600 in_rect.Height = 0.0; /* avoid height clipping */
5601 GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0);
5603 TRACE("bounds %s\n", debugstr_rectf(&bounds));
5605 if(format->line_align == StringAlignmentCenter)
5606 offsety = (rect->Height - bounds.Height) / 2;
5607 else if(format->line_align == StringAlignmentFar)
5608 offsety = (rect->Height - bounds.Height);
5610 TRACE("line align %d, offsety %f\n", format->line_align, offsety);
5613 save_state = SaveDC(hdc);
5615 pt[0].X = 0.0;
5616 pt[0].Y = 0.0;
5617 pt[1].X = 1.0;
5618 pt[1].Y = 0.0;
5619 pt[2].X = 0.0;
5620 pt[2].Y = 1.0;
5621 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
5622 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5623 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5624 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5625 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5627 rectcpy[3].X = rectcpy[0].X = rect->X;
5628 rectcpy[1].Y = rectcpy[0].Y = rect->Y;
5629 rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
5630 rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
5631 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4);
5632 round_points(corners, rectcpy, 4);
5634 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
5635 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5637 scaled_rect.X = margin_x * rel_width;
5638 scaled_rect.Y = 0.0;
5639 scaled_rect.Width = rel_width * rect->Width;
5640 scaled_rect.Height = rel_height * rect->Height;
5641 if (scaled_rect.Width >= 0.5)
5643 scaled_rect.Width -= margin_x * 2.0 * rel_width;
5644 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
5647 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5648 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5650 if (!(format_flags & StringFormatFlagsNoClip) &&
5651 scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 &&
5652 rect->Width > 0.0 && rect->Height > 0.0)
5654 /* FIXME: If only the width or only the height is 0, we should probably still clip */
5655 rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
5656 SelectClipRgn(hdc, rgn);
5659 get_font_hfont(graphics, font, format, &gdifont, NULL);
5660 SelectObject(hdc, gdifont);
5662 args.graphics = graphics;
5663 args.brush = brush;
5665 args.x = rect->X;
5666 args.y = rect->Y + offsety;
5668 args.rel_width = rel_width;
5669 args.rel_height = rel_height;
5671 gdi_transform_acquire(graphics);
5673 GetTextMetricsW(hdc, &textmetric);
5674 args.ascent = textmetric.tmAscent / rel_height;
5676 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
5677 draw_string_callback, &args);
5679 gdi_transform_release(graphics);
5681 DeleteObject(rgn);
5682 DeleteObject(gdifont);
5684 RestoreDC(hdc, save_state);
5686 DeleteDC(temp_hdc);
5688 return Ok;
5691 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
5693 TRACE("(%p)\n", graphics);
5695 if(!graphics)
5696 return InvalidParameter;
5698 if(graphics->busy)
5699 return ObjectBusy;
5701 return GdipSetInfinite(graphics->clip);
5704 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
5706 GpStatus stat;
5708 TRACE("(%p)\n", graphics);
5710 if(!graphics)
5711 return InvalidParameter;
5713 if(graphics->busy)
5714 return ObjectBusy;
5716 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5717 stat = METAFILE_ResetWorldTransform((GpMetafile*)graphics->image);
5719 if (stat != Ok)
5720 return stat;
5723 return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
5726 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
5727 GpMatrixOrder order)
5729 GpStatus stat;
5731 TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
5733 if(!graphics)
5734 return InvalidParameter;
5736 if(graphics->busy)
5737 return ObjectBusy;
5739 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5740 stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order);
5742 if (stat != Ok)
5743 return stat;
5746 return GdipRotateMatrix(&graphics->worldtrans, angle, order);
5749 static GpStatus begin_container(GpGraphics *graphics,
5750 GraphicsContainerType type, GraphicsContainer *state)
5752 GraphicsContainerItem *container;
5753 GpStatus sts;
5755 if(!graphics || !state)
5756 return InvalidParameter;
5758 sts = init_container(&container, graphics, type);
5759 if(sts != Ok)
5760 return sts;
5762 list_add_head(&graphics->containers, &container->entry);
5763 *state = graphics->contid = container->contid;
5765 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5766 if (type == BEGIN_CONTAINER)
5767 METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid);
5768 else
5769 METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid);
5772 return Ok;
5775 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
5777 TRACE("(%p, %p)\n", graphics, state);
5778 return begin_container(graphics, SAVE_GRAPHICS, state);
5781 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
5782 GraphicsContainer *state)
5784 TRACE("(%p, %p)\n", graphics, state);
5785 return begin_container(graphics, BEGIN_CONTAINER, state);
5788 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
5790 GraphicsContainerItem *container;
5791 GpMatrix transform;
5792 GpStatus stat;
5793 GpRectF scaled_srcrect;
5794 REAL scale_x, scale_y;
5796 TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state);
5798 if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state)
5799 return InvalidParameter;
5801 stat = init_container(&container, graphics, BEGIN_CONTAINER);
5802 if(stat != Ok)
5803 return stat;
5805 list_add_head(&graphics->containers, &container->entry);
5806 *state = graphics->contid = container->contid;
5808 scale_x = units_to_pixels(1.0, unit, graphics->xres);
5809 scale_y = units_to_pixels(1.0, unit, graphics->yres);
5811 scaled_srcrect.X = scale_x * srcrect->X;
5812 scaled_srcrect.Y = scale_y * srcrect->Y;
5813 scaled_srcrect.Width = scale_x * srcrect->Width;
5814 scaled_srcrect.Height = scale_y * srcrect->Height;
5816 transform.matrix[0] = dstrect->Width / scaled_srcrect.Width;
5817 transform.matrix[1] = 0.0;
5818 transform.matrix[2] = 0.0;
5819 transform.matrix[3] = dstrect->Height / scaled_srcrect.Height;
5820 transform.matrix[4] = dstrect->X - scaled_srcrect.X;
5821 transform.matrix[5] = dstrect->Y - scaled_srcrect.Y;
5823 GdipMultiplyMatrix(&graphics->worldtrans, &transform, MatrixOrderPrepend);
5825 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5826 METAFILE_BeginContainer((GpMetafile*)graphics->image, dstrect, srcrect, unit, container->contid);
5829 return Ok;
5832 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
5834 GpRectF dstrectf, srcrectf;
5836 TRACE("(%p, %p, %p, %d, %p)\n", graphics, dstrect, srcrect, unit, state);
5838 if (!dstrect || !srcrect)
5839 return InvalidParameter;
5841 dstrectf.X = dstrect->X;
5842 dstrectf.Y = dstrect->Y;
5843 dstrectf.Width = dstrect->Width;
5844 dstrectf.Height = dstrect->Height;
5846 srcrectf.X = srcrect->X;
5847 srcrectf.Y = srcrect->Y;
5848 srcrectf.Width = srcrect->Width;
5849 srcrectf.Height = srcrect->Height;
5851 return GdipBeginContainer(graphics, &dstrectf, &srcrectf, unit, state);
5854 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
5856 FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
5857 return NotImplemented;
5860 static GpStatus end_container(GpGraphics *graphics, GraphicsContainerType type,
5861 GraphicsContainer state)
5863 GpStatus sts;
5864 GraphicsContainerItem *container, *container2;
5866 if(!graphics)
5867 return InvalidParameter;
5869 LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
5870 if(container->contid == state && container->type == type)
5871 break;
5874 /* did not find a matching container */
5875 if(&container->entry == &graphics->containers)
5876 return Ok;
5878 sts = restore_container(graphics, container);
5879 if(sts != Ok)
5880 return sts;
5882 /* remove all of the containers on top of the found container */
5883 LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
5884 if(container->contid == state)
5885 break;
5886 list_remove(&container->entry);
5887 delete_container(container);
5890 list_remove(&container->entry);
5891 delete_container(container);
5893 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5894 if (type == BEGIN_CONTAINER)
5895 METAFILE_EndContainer((GpMetafile*)graphics->image, state);
5896 else
5897 METAFILE_RestoreGraphics((GpMetafile*)graphics->image, state);
5900 return Ok;
5903 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
5905 TRACE("(%p, %x)\n", graphics, state);
5906 return end_container(graphics, BEGIN_CONTAINER, state);
5909 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
5911 TRACE("(%p, %x)\n", graphics, state);
5912 return end_container(graphics, SAVE_GRAPHICS, state);
5915 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
5916 REAL sy, GpMatrixOrder order)
5918 GpStatus stat;
5920 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
5922 if(!graphics)
5923 return InvalidParameter;
5925 if(graphics->busy)
5926 return ObjectBusy;
5928 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
5929 stat = METAFILE_ScaleWorldTransform((GpMetafile*)graphics->image, sx, sy, order);
5931 if (stat != Ok)
5932 return stat;
5935 return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
5938 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
5939 CombineMode mode)
5941 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
5943 if(!graphics || !srcgraphics)
5944 return InvalidParameter;
5946 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
5949 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
5950 CompositingMode mode)
5952 TRACE("(%p, %d)\n", graphics, mode);
5954 if(!graphics)
5955 return InvalidParameter;
5957 if(graphics->busy)
5958 return ObjectBusy;
5960 if(graphics->compmode == mode)
5961 return Ok;
5963 if(graphics->image && graphics->image->type == ImageTypeMetafile)
5965 GpStatus stat;
5967 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
5968 EmfPlusRecordTypeSetCompositingMode, mode);
5969 if(stat != Ok)
5970 return stat;
5973 graphics->compmode = mode;
5975 return Ok;
5978 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
5979 CompositingQuality quality)
5981 TRACE("(%p, %d)\n", graphics, quality);
5983 if(!graphics)
5984 return InvalidParameter;
5986 if(graphics->busy)
5987 return ObjectBusy;
5989 if(graphics->compqual == quality)
5990 return Ok;
5992 if(graphics->image && graphics->image->type == ImageTypeMetafile)
5994 GpStatus stat;
5996 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
5997 EmfPlusRecordTypeSetCompositingQuality, quality);
5998 if(stat != Ok)
5999 return stat;
6002 graphics->compqual = quality;
6004 return Ok;
6007 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
6008 InterpolationMode mode)
6010 TRACE("(%p, %d)\n", graphics, mode);
6012 if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
6013 return InvalidParameter;
6015 if(graphics->busy)
6016 return ObjectBusy;
6018 if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
6019 mode = InterpolationModeBilinear;
6021 if (mode == InterpolationModeHighQuality)
6022 mode = InterpolationModeHighQualityBicubic;
6024 if (mode == graphics->interpolation)
6025 return Ok;
6027 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6029 GpStatus stat;
6031 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6032 EmfPlusRecordTypeSetInterpolationMode, mode);
6033 if (stat != Ok)
6034 return stat;
6037 graphics->interpolation = mode;
6039 return Ok;
6042 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
6044 GpStatus stat;
6046 TRACE("(%p, %.2f)\n", graphics, scale);
6048 if(!graphics || (scale <= 0.0))
6049 return InvalidParameter;
6051 if(graphics->busy)
6052 return ObjectBusy;
6054 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6056 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, graphics->unit, scale);
6057 if (stat != Ok)
6058 return stat;
6061 graphics->scale = scale;
6063 return Ok;
6066 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
6068 GpStatus stat;
6070 TRACE("(%p, %d)\n", graphics, unit);
6072 if(!graphics)
6073 return InvalidParameter;
6075 if(graphics->busy)
6076 return ObjectBusy;
6078 if(unit == UnitWorld)
6079 return InvalidParameter;
6081 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6083 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, unit, graphics->scale);
6084 if (stat != Ok)
6085 return stat;
6088 graphics->unit = unit;
6090 return Ok;
6093 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
6094 mode)
6096 TRACE("(%p, %d)\n", graphics, mode);
6098 if(!graphics)
6099 return InvalidParameter;
6101 if(graphics->busy)
6102 return ObjectBusy;
6104 if(graphics->pixeloffset == mode)
6105 return Ok;
6107 if(graphics->image && graphics->image->type == ImageTypeMetafile)
6109 GpStatus stat;
6111 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6112 EmfPlusRecordTypeSetPixelOffsetMode, mode);
6113 if(stat != Ok)
6114 return stat;
6117 graphics->pixeloffset = mode;
6119 return Ok;
6122 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
6124 static int calls;
6126 TRACE("(%p,%i,%i)\n", graphics, x, y);
6128 if (!(calls++))
6129 FIXME("value is unused in rendering\n");
6131 if (!graphics)
6132 return InvalidParameter;
6134 graphics->origin_x = x;
6135 graphics->origin_y = y;
6137 return Ok;
6140 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
6142 TRACE("(%p,%p,%p)\n", graphics, x, y);
6144 if (!graphics || !x || !y)
6145 return InvalidParameter;
6147 *x = graphics->origin_x;
6148 *y = graphics->origin_y;
6150 return Ok;
6153 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
6155 TRACE("(%p, %d)\n", graphics, mode);
6157 if(!graphics)
6158 return InvalidParameter;
6160 if(graphics->busy)
6161 return ObjectBusy;
6163 if(graphics->smoothing == mode)
6164 return Ok;
6166 if(graphics->image && graphics->image->type == ImageTypeMetafile) {
6167 GpStatus stat;
6168 BOOL antialias = (mode != SmoothingModeDefault &&
6169 mode != SmoothingModeNone && mode != SmoothingModeHighSpeed);
6171 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6172 EmfPlusRecordTypeSetAntiAliasMode, (mode << 1) + antialias);
6173 if(stat != Ok)
6174 return stat;
6177 graphics->smoothing = mode;
6179 return Ok;
6182 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
6184 TRACE("(%p, %d)\n", graphics, contrast);
6186 if(!graphics)
6187 return InvalidParameter;
6189 graphics->textcontrast = contrast;
6191 return Ok;
6194 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
6195 TextRenderingHint hint)
6197 TRACE("(%p, %d)\n", graphics, hint);
6199 if(!graphics || hint > TextRenderingHintClearTypeGridFit)
6200 return InvalidParameter;
6202 if(graphics->busy)
6203 return ObjectBusy;
6205 if(graphics->texthint == hint)
6206 return Ok;
6208 if(graphics->image && graphics->image->type == ImageTypeMetafile) {
6209 GpStatus stat;
6211 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
6212 EmfPlusRecordTypeSetTextRenderingHint, hint);
6213 if(stat != Ok)
6214 return stat;
6217 graphics->texthint = hint;
6219 return Ok;
6222 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
6224 GpStatus stat;
6226 TRACE("(%p, %p)\n", graphics, matrix);
6228 if(!graphics || !matrix)
6229 return InvalidParameter;
6231 if(graphics->busy)
6232 return ObjectBusy;
6234 TRACE("%f,%f,%f,%f,%f,%f\n",
6235 matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
6236 matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
6238 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6239 stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix);
6241 if (stat != Ok)
6242 return stat;
6245 graphics->worldtrans = *matrix;
6247 return Ok;
6250 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
6251 REAL dy, GpMatrixOrder order)
6253 GpStatus stat;
6255 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
6257 if(!graphics)
6258 return InvalidParameter;
6260 if(graphics->busy)
6261 return ObjectBusy;
6263 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6264 stat = METAFILE_TranslateWorldTransform((GpMetafile*)graphics->image, dx, dy, order);
6266 if (stat != Ok)
6267 return stat;
6270 return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
6273 /*****************************************************************************
6274 * GdipSetClipHrgn [GDIPLUS.@]
6276 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
6278 GpRegion *region;
6279 GpStatus status;
6280 GpMatrix transform;
6282 TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
6284 if(!graphics)
6285 return InvalidParameter;
6287 if(graphics->busy)
6288 return ObjectBusy;
6290 /* hrgn is in gdi32 device units */
6291 status = GdipCreateRegionHrgn(hrgn, &region);
6293 if (status == Ok)
6295 status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform);
6297 if (status == Ok)
6298 status = GdipTransformRegion(region, &transform);
6300 if (status == Ok)
6301 status = GdipCombineRegionRegion(graphics->clip, region, mode);
6303 GdipDeleteRegion(region);
6305 return status;
6308 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
6310 GpStatus status;
6311 GpPath *clip_path;
6313 TRACE("(%p, %p, %d)\n", graphics, path, mode);
6315 if(!graphics)
6316 return InvalidParameter;
6318 if(graphics->busy)
6319 return ObjectBusy;
6321 status = GdipClonePath(path, &clip_path);
6322 if (status == Ok)
6324 GpMatrix world_to_device;
6326 get_graphics_transform(graphics, CoordinateSpaceDevice,
6327 CoordinateSpaceWorld, &world_to_device);
6328 status = GdipTransformPath(clip_path, &world_to_device);
6329 if (status == Ok)
6330 GdipCombineRegionPath(graphics->clip, clip_path, mode);
6332 GdipDeletePath(clip_path);
6334 return status;
6337 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
6338 REAL width, REAL height,
6339 CombineMode mode)
6341 GpStatus status;
6342 GpRectF rect;
6343 GpRegion *region;
6345 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
6347 if(!graphics)
6348 return InvalidParameter;
6350 if(graphics->busy)
6351 return ObjectBusy;
6353 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6355 status = METAFILE_SetClipRect((GpMetafile*)graphics->image, x, y, width, height, mode);
6356 if (status != Ok)
6357 return status;
6360 rect.X = x;
6361 rect.Y = y;
6362 rect.Width = width;
6363 rect.Height = height;
6364 status = GdipCreateRegionRect(&rect, &region);
6365 if (status == Ok)
6367 GpMatrix world_to_device;
6368 BOOL identity;
6370 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
6371 status = GdipIsMatrixIdentity(&world_to_device, &identity);
6372 if (status == Ok && !identity)
6373 status = GdipTransformRegion(region, &world_to_device);
6374 if (status == Ok)
6375 status = GdipCombineRegionRegion(graphics->clip, region, mode);
6377 GdipDeleteRegion(region);
6379 return status;
6382 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
6383 INT width, INT height,
6384 CombineMode mode)
6386 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
6388 if(!graphics)
6389 return InvalidParameter;
6391 if(graphics->busy)
6392 return ObjectBusy;
6394 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
6397 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
6398 CombineMode mode)
6400 GpStatus status;
6401 GpRegion *clip;
6403 TRACE("(%p, %p, %d)\n", graphics, region, mode);
6405 if(!graphics || !region)
6406 return InvalidParameter;
6408 if(graphics->busy)
6409 return ObjectBusy;
6411 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6413 status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode);
6414 if (status != Ok)
6415 return status;
6418 status = GdipCloneRegion(region, &clip);
6419 if (status == Ok)
6421 GpMatrix world_to_device;
6422 BOOL identity;
6424 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
6425 status = GdipIsMatrixIdentity(&world_to_device, &identity);
6426 if (status == Ok && !identity)
6427 status = GdipTransformRegion(clip, &world_to_device);
6428 if (status == Ok)
6429 status = GdipCombineRegionRegion(graphics->clip, clip, mode);
6431 GdipDeleteRegion(clip);
6433 return status;
6436 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
6437 INT count)
6439 GpStatus status;
6440 GpPath* path;
6442 TRACE("(%p, %p, %d)\n", graphics, points, count);
6444 if(!graphics || !pen || count<=0)
6445 return InvalidParameter;
6447 if(graphics->busy)
6448 return ObjectBusy;
6450 status = GdipCreatePath(FillModeAlternate, &path);
6451 if (status != Ok) return status;
6453 status = GdipAddPathPolygon(path, points, count);
6454 if (status == Ok)
6455 status = GdipDrawPath(graphics, pen, path);
6457 GdipDeletePath(path);
6459 return status;
6462 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
6463 INT count)
6465 GpStatus ret;
6466 GpPointF *ptf;
6467 INT i;
6469 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
6471 if(count<=0) return InvalidParameter;
6472 ptf = heap_alloc_zero(sizeof(GpPointF) * count);
6474 for(i = 0;i < count; i++){
6475 ptf[i].X = (REAL)points[i].X;
6476 ptf[i].Y = (REAL)points[i].Y;
6479 ret = GdipDrawPolygon(graphics,pen,ptf,count);
6480 heap_free(ptf);
6482 return ret;
6485 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
6487 TRACE("(%p, %p)\n", graphics, dpi);
6489 if(!graphics || !dpi)
6490 return InvalidParameter;
6492 if(graphics->busy)
6493 return ObjectBusy;
6495 *dpi = graphics->xres;
6496 return Ok;
6499 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
6501 TRACE("(%p, %p)\n", graphics, dpi);
6503 if(!graphics || !dpi)
6504 return InvalidParameter;
6506 if(graphics->busy)
6507 return ObjectBusy;
6509 *dpi = graphics->yres;
6510 return Ok;
6513 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
6514 GpMatrixOrder order)
6516 GpMatrix m;
6517 GpStatus ret;
6519 TRACE("(%p, %p, %d)\n", graphics, matrix, order);
6521 if(!graphics || !matrix)
6522 return InvalidParameter;
6524 if(graphics->busy)
6525 return ObjectBusy;
6527 if (graphics->image && graphics->image->type == ImageTypeMetafile) {
6528 ret = METAFILE_MultiplyWorldTransform((GpMetafile*)graphics->image, matrix, order);
6530 if (ret != Ok)
6531 return ret;
6534 m = graphics->worldtrans;
6536 ret = GdipMultiplyMatrix(&m, matrix, order);
6537 if(ret == Ok)
6538 graphics->worldtrans = m;
6540 return ret;
6543 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
6544 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
6546 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
6548 GpStatus stat=Ok;
6550 TRACE("(%p, %p)\n", graphics, hdc);
6552 if(!graphics || !hdc)
6553 return InvalidParameter;
6555 if(graphics->busy)
6556 return ObjectBusy;
6558 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6560 stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
6562 else if (!graphics->hdc ||
6563 (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
6565 /* Create a fake HDC and fill it with a constant color. */
6566 HDC temp_hdc;
6567 HBITMAP hbitmap;
6568 GpRectF bounds;
6569 BITMAPINFOHEADER bmih;
6570 int i;
6572 stat = get_graphics_bounds(graphics, &bounds);
6573 if (stat != Ok)
6574 return stat;
6576 graphics->temp_hbitmap_width = bounds.Width;
6577 graphics->temp_hbitmap_height = bounds.Height;
6579 bmih.biSize = sizeof(bmih);
6580 bmih.biWidth = graphics->temp_hbitmap_width;
6581 bmih.biHeight = -graphics->temp_hbitmap_height;
6582 bmih.biPlanes = 1;
6583 bmih.biBitCount = 32;
6584 bmih.biCompression = BI_RGB;
6585 bmih.biSizeImage = 0;
6586 bmih.biXPelsPerMeter = 0;
6587 bmih.biYPelsPerMeter = 0;
6588 bmih.biClrUsed = 0;
6589 bmih.biClrImportant = 0;
6591 hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
6592 (void**)&graphics->temp_bits, NULL, 0);
6593 if (!hbitmap)
6594 return GenericError;
6596 temp_hdc = CreateCompatibleDC(0);
6597 if (!temp_hdc)
6599 DeleteObject(hbitmap);
6600 return GenericError;
6603 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
6604 ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
6606 SelectObject(temp_hdc, hbitmap);
6608 graphics->temp_hbitmap = hbitmap;
6609 *hdc = graphics->temp_hdc = temp_hdc;
6611 else
6613 *hdc = graphics->hdc;
6616 if (stat == Ok)
6617 graphics->busy = TRUE;
6619 return stat;
6622 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
6624 GpStatus stat=Ok;
6626 TRACE("(%p, %p)\n", graphics, hdc);
6628 if(!graphics || !hdc || !graphics->busy)
6629 return InvalidParameter;
6631 if (graphics->image && graphics->image->type == ImageTypeMetafile)
6633 stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
6635 else if (graphics->temp_hdc == hdc)
6637 DWORD* pos;
6638 int i;
6640 /* Find the pixels that have changed, and mark them as opaque. */
6641 pos = (DWORD*)graphics->temp_bits;
6642 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
6644 if (*pos != DC_BACKGROUND_KEY)
6646 *pos |= 0xff000000;
6648 pos++;
6651 /* Write the changed pixels to the real target. */
6652 alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
6653 graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
6654 graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB);
6656 /* Clean up. */
6657 DeleteDC(graphics->temp_hdc);
6658 DeleteObject(graphics->temp_hbitmap);
6659 graphics->temp_hdc = NULL;
6660 graphics->temp_hbitmap = NULL;
6662 else if (hdc != graphics->hdc)
6664 stat = InvalidParameter;
6667 if (stat == Ok)
6668 graphics->busy = FALSE;
6670 return stat;
6673 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
6675 GpRegion *clip;
6676 GpStatus status;
6677 GpMatrix device_to_world;
6679 TRACE("(%p, %p)\n", graphics, region);
6681 if(!graphics || !region)
6682 return InvalidParameter;
6684 if(graphics->busy)
6685 return ObjectBusy;
6687 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
6688 return status;
6690 get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
6691 status = GdipTransformRegion(clip, &device_to_world);
6692 if (status != Ok)
6694 GdipDeleteRegion(clip);
6695 return status;
6698 /* free everything except root node and header */
6699 delete_element(&region->node);
6700 memcpy(region, clip, sizeof(GpRegion));
6701 heap_free(clip);
6703 return Ok;
6706 GpStatus gdi_transform_acquire(GpGraphics *graphics)
6708 if (graphics->gdi_transform_acquire_count == 0 && graphics->hdc)
6710 graphics->gdi_transform_save = SaveDC(graphics->hdc);
6711 SetGraphicsMode(graphics->hdc, GM_COMPATIBLE);
6712 SetMapMode(graphics->hdc, MM_TEXT);
6713 SetWindowOrgEx(graphics->hdc, 0, 0, NULL);
6714 SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
6716 graphics->gdi_transform_acquire_count++;
6717 return Ok;
6720 GpStatus gdi_transform_release(GpGraphics *graphics)
6722 if (graphics->gdi_transform_acquire_count <= 0)
6724 ERR("called without matching gdi_transform_acquire\n");
6725 return GenericError;
6727 if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
6729 RestoreDC(graphics->hdc, graphics->gdi_transform_save);
6731 graphics->gdi_transform_acquire_count--;
6732 return Ok;
6735 GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
6736 GpCoordinateSpace src_space, GpMatrix *matrix)
6738 GpStatus stat = Ok;
6739 REAL scale_x, scale_y;
6741 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
6743 if (dst_space != src_space)
6745 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
6746 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
6748 if(graphics->unit != UnitDisplay)
6750 scale_x *= graphics->scale;
6751 scale_y *= graphics->scale;
6754 if (dst_space < src_space)
6756 /* transform towards world space */
6757 switch ((int)src_space)
6759 case WineCoordinateSpaceGdiDevice:
6761 GpMatrix gdixform;
6762 gdixform = graphics->gdi_transform;
6763 stat = GdipInvertMatrix(&gdixform);
6764 if (stat != Ok)
6765 break;
6766 GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
6767 if (dst_space == CoordinateSpaceDevice)
6768 break;
6769 /* else fall-through */
6771 case CoordinateSpaceDevice:
6772 GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
6773 if (dst_space == CoordinateSpacePage)
6774 break;
6775 /* else fall-through */
6776 case CoordinateSpacePage:
6778 GpMatrix inverted_transform = graphics->worldtrans;
6779 stat = GdipInvertMatrix(&inverted_transform);
6780 if (stat == Ok)
6781 GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
6782 break;
6786 else
6788 /* transform towards device space */
6789 switch ((int)src_space)
6791 case CoordinateSpaceWorld:
6792 GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
6793 if (dst_space == CoordinateSpacePage)
6794 break;
6795 /* else fall-through */
6796 case CoordinateSpacePage:
6797 GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
6798 if (dst_space == CoordinateSpaceDevice)
6799 break;
6800 /* else fall-through */
6801 case CoordinateSpaceDevice:
6803 GdipMultiplyMatrix(matrix, &graphics->gdi_transform, MatrixOrderAppend);
6804 break;
6809 return stat;
6812 GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
6813 GpCoordinateSpace src_space, GpPointF *points, INT count)
6815 GpMatrix matrix;
6816 GpStatus stat;
6818 stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
6819 if (stat != Ok) return stat;
6821 return GdipTransformMatrixPoints(&matrix, points, count);
6824 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
6825 GpCoordinateSpace src_space, GpPointF *points, INT count)
6827 if(!graphics || !points || count <= 0 ||
6828 dst_space < 0 || dst_space > CoordinateSpaceDevice ||
6829 src_space < 0 || src_space > CoordinateSpaceDevice)
6830 return InvalidParameter;
6832 if(graphics->busy)
6833 return ObjectBusy;
6835 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
6837 if (src_space == dst_space) return Ok;
6839 return gdip_transform_points(graphics, dst_space, src_space, points, count);
6842 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
6843 GpCoordinateSpace src_space, GpPoint *points, INT count)
6845 GpPointF *pointsF;
6846 GpStatus ret;
6847 INT i;
6849 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
6851 if(count <= 0)
6852 return InvalidParameter;
6854 pointsF = heap_alloc_zero(sizeof(GpPointF) * count);
6855 if(!pointsF)
6856 return OutOfMemory;
6858 for(i = 0; i < count; i++){
6859 pointsF[i].X = (REAL)points[i].X;
6860 pointsF[i].Y = (REAL)points[i].Y;
6863 ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
6865 if(ret == Ok)
6866 for(i = 0; i < count; i++){
6867 points[i].X = gdip_round(pointsF[i].X);
6868 points[i].Y = gdip_round(pointsF[i].Y);
6870 heap_free(pointsF);
6872 return ret;
6875 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
6877 static int calls;
6879 TRACE("\n");
6881 if (!calls++)
6882 FIXME("stub\n");
6884 return NULL;
6887 /*****************************************************************************
6888 * GdipTranslateClip [GDIPLUS.@]
6890 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
6892 TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
6894 if(!graphics)
6895 return InvalidParameter;
6897 if(graphics->busy)
6898 return ObjectBusy;
6900 return GdipTranslateRegion(graphics->clip, dx, dy);
6903 /*****************************************************************************
6904 * GdipTranslateClipI [GDIPLUS.@]
6906 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
6908 TRACE("(%p, %d, %d)\n", graphics, dx, dy);
6910 if(!graphics)
6911 return InvalidParameter;
6913 if(graphics->busy)
6914 return ObjectBusy;
6916 return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
6920 /*****************************************************************************
6921 * GdipMeasureDriverString [GDIPLUS.@]
6923 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6924 GDIPCONST GpFont *font, GDIPCONST PointF *positions,
6925 INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
6927 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
6928 HFONT hfont;
6929 HDC hdc;
6930 REAL min_x, min_y, max_x, max_y, x, y;
6931 int i;
6932 TEXTMETRICW textmetric;
6933 const WORD *glyph_indices;
6934 WORD *dynamic_glyph_indices=NULL;
6935 REAL rel_width, rel_height, ascent, descent;
6936 GpPointF pt[3];
6938 TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
6940 if (!graphics || !text || !font || !positions || !boundingBox)
6941 return InvalidParameter;
6943 if (length == -1)
6944 length = strlenW(text);
6946 if (length == 0)
6948 boundingBox->X = 0.0;
6949 boundingBox->Y = 0.0;
6950 boundingBox->Width = 0.0;
6951 boundingBox->Height = 0.0;
6954 if (flags & unsupported_flags)
6955 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6957 get_font_hfont(graphics, font, NULL, &hfont, matrix);
6959 hdc = CreateCompatibleDC(0);
6960 SelectObject(hdc, hfont);
6962 GetTextMetricsW(hdc, &textmetric);
6964 pt[0].X = 0.0;
6965 pt[0].Y = 0.0;
6966 pt[1].X = 1.0;
6967 pt[1].Y = 0.0;
6968 pt[2].X = 0.0;
6969 pt[2].Y = 1.0;
6970 if (matrix)
6972 GpMatrix xform = *matrix;
6973 GdipTransformMatrixPoints(&xform, pt, 3);
6975 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
6976 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
6977 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
6978 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
6979 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
6981 if (flags & DriverStringOptionsCmapLookup)
6983 glyph_indices = dynamic_glyph_indices = heap_alloc_zero(sizeof(WORD) * length);
6984 if (!glyph_indices)
6986 DeleteDC(hdc);
6987 DeleteObject(hfont);
6988 return OutOfMemory;
6991 GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
6993 else
6994 glyph_indices = text;
6996 min_x = max_x = x = positions[0].X;
6997 min_y = max_y = y = positions[0].Y;
6999 ascent = textmetric.tmAscent / rel_height;
7000 descent = textmetric.tmDescent / rel_height;
7002 for (i=0; i<length; i++)
7004 int char_width;
7005 ABC abc;
7007 if (!(flags & DriverStringOptionsRealizedAdvance))
7009 x = positions[i].X;
7010 y = positions[i].Y;
7013 GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
7014 char_width = abc.abcA + abc.abcB + abc.abcC;
7016 if (min_y > y - ascent) min_y = y - ascent;
7017 if (max_y < y + descent) max_y = y + descent;
7018 if (min_x > x) min_x = x;
7020 x += char_width / rel_width;
7022 if (max_x < x) max_x = x;
7025 heap_free(dynamic_glyph_indices);
7026 DeleteDC(hdc);
7027 DeleteObject(hfont);
7029 boundingBox->X = min_x;
7030 boundingBox->Y = min_y;
7031 boundingBox->Width = max_x - min_x;
7032 boundingBox->Height = max_y - min_y;
7034 return Ok;
7037 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7038 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
7039 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
7040 INT flags, GDIPCONST GpMatrix *matrix)
7042 static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
7043 INT save_state;
7044 GpPointF pt;
7045 HFONT hfont;
7046 UINT eto_flags=0;
7047 GpStatus status;
7048 HRGN hrgn;
7050 if (flags & unsupported_flags)
7051 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
7053 if (!(flags & DriverStringOptionsCmapLookup))
7054 eto_flags |= ETO_GLYPH_INDEX;
7056 save_state = SaveDC(graphics->hdc);
7057 SetBkMode(graphics->hdc, TRANSPARENT);
7058 SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
7060 status = get_clip_hrgn(graphics, &hrgn);
7062 if (status == Ok)
7064 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
7065 DeleteObject(hrgn);
7068 pt = positions[0];
7069 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &pt, 1);
7071 get_font_hfont(graphics, font, format, &hfont, matrix);
7072 SelectObject(graphics->hdc, hfont);
7074 SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
7076 gdi_transform_acquire(graphics);
7078 ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
7080 gdi_transform_release(graphics);
7082 RestoreDC(graphics->hdc, save_state);
7084 DeleteObject(hfont);
7086 return Ok;
7089 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7090 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
7091 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
7092 INT flags, GDIPCONST GpMatrix *matrix)
7094 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
7095 GpStatus stat;
7096 PointF *real_positions, real_position;
7097 POINT *pti;
7098 HFONT hfont;
7099 HDC hdc;
7100 int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
7101 DWORD max_glyphsize=0;
7102 GLYPHMETRICS glyphmetrics;
7103 static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
7104 BYTE *glyph_mask;
7105 BYTE *text_mask;
7106 int text_mask_stride;
7107 BYTE *pixel_data;
7108 int pixel_data_stride;
7109 GpRect pixel_area;
7110 UINT ggo_flags = GGO_GRAY8_BITMAP;
7112 if (length <= 0)
7113 return Ok;
7115 if (!(flags & DriverStringOptionsCmapLookup))
7116 ggo_flags |= GGO_GLYPH_INDEX;
7118 if (flags & unsupported_flags)
7119 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
7121 pti = heap_alloc_zero(sizeof(POINT) * length);
7122 if (!pti)
7123 return OutOfMemory;
7125 if (flags & DriverStringOptionsRealizedAdvance)
7127 real_position = positions[0];
7129 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1);
7130 round_points(pti, &real_position, 1);
7132 else
7134 real_positions = heap_alloc_zero(sizeof(PointF) * length);
7135 if (!real_positions)
7137 heap_free(pti);
7138 return OutOfMemory;
7141 memcpy(real_positions, positions, sizeof(PointF) * length);
7143 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length);
7144 round_points(pti, real_positions, length);
7146 heap_free(real_positions);
7149 get_font_hfont(graphics, font, format, &hfont, matrix);
7151 hdc = CreateCompatibleDC(0);
7152 SelectObject(hdc, hfont);
7154 /* Get the boundaries of the text to be drawn */
7155 for (i=0; i<length; i++)
7157 DWORD glyphsize;
7158 int left, top, right, bottom;
7160 glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
7161 &glyphmetrics, 0, NULL, &identity);
7163 if (glyphsize == GDI_ERROR)
7165 ERR("GetGlyphOutlineW failed\n");
7166 heap_free(pti);
7167 DeleteDC(hdc);
7168 DeleteObject(hfont);
7169 return GenericError;
7172 if (glyphsize > max_glyphsize)
7173 max_glyphsize = glyphsize;
7175 if (glyphsize != 0)
7177 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
7178 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
7179 right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
7180 bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
7182 if (left < min_x) min_x = left;
7183 if (top < min_y) min_y = top;
7184 if (right > max_x) max_x = right;
7185 if (bottom > max_y) max_y = bottom;
7188 if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
7190 pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
7191 pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
7195 if (max_glyphsize == 0)
7196 /* Nothing to draw. */
7197 return Ok;
7199 glyph_mask = heap_alloc_zero(max_glyphsize);
7200 text_mask = heap_alloc_zero((max_x - min_x) * (max_y - min_y));
7201 text_mask_stride = max_x - min_x;
7203 if (!(glyph_mask && text_mask))
7205 heap_free(glyph_mask);
7206 heap_free(text_mask);
7207 heap_free(pti);
7208 DeleteDC(hdc);
7209 DeleteObject(hfont);
7210 return OutOfMemory;
7213 /* Generate a mask for the text */
7214 for (i=0; i<length; i++)
7216 DWORD ret;
7217 int left, top, stride;
7219 ret = GetGlyphOutlineW(hdc, text[i], ggo_flags,
7220 &glyphmetrics, max_glyphsize, glyph_mask, &identity);
7222 if (ret == GDI_ERROR || ret == 0)
7223 continue; /* empty glyph */
7225 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
7226 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
7227 stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
7229 for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
7231 BYTE *glyph_val = glyph_mask + y * stride;
7232 BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
7233 for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
7235 *text_val = min(64, *text_val + *glyph_val);
7236 glyph_val++;
7237 text_val++;
7242 heap_free(pti);
7243 DeleteDC(hdc);
7244 DeleteObject(hfont);
7245 heap_free(glyph_mask);
7247 /* get the brush data */
7248 pixel_data = heap_alloc_zero(4 * (max_x - min_x) * (max_y - min_y));
7249 if (!pixel_data)
7251 heap_free(text_mask);
7252 return OutOfMemory;
7255 pixel_area.X = min_x;
7256 pixel_area.Y = min_y;
7257 pixel_area.Width = max_x - min_x;
7258 pixel_area.Height = max_y - min_y;
7259 pixel_data_stride = pixel_area.Width * 4;
7261 stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
7262 if (stat != Ok)
7264 heap_free(text_mask);
7265 heap_free(pixel_data);
7266 return stat;
7269 /* multiply the brush data by the mask */
7270 for (y=0; y<pixel_area.Height; y++)
7272 BYTE *text_val = text_mask + text_mask_stride * y;
7273 BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
7274 for (x=0; x<pixel_area.Width; x++)
7276 *pixel_val = (*pixel_val) * (*text_val) / 64;
7277 text_val++;
7278 pixel_val+=4;
7282 heap_free(text_mask);
7284 gdi_transform_acquire(graphics);
7286 /* draw the result */
7287 stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
7288 pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
7290 gdi_transform_release(graphics);
7292 heap_free(pixel_data);
7294 return stat;
7297 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7298 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
7299 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
7300 INT flags, GDIPCONST GpMatrix *matrix)
7302 GpStatus stat = NotImplemented;
7304 if (length == -1)
7305 length = strlenW(text);
7307 if (graphics->hdc && !graphics->alpha_hdc &&
7308 ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
7309 brush->bt == BrushTypeSolidColor &&
7310 (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
7311 stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
7312 brush, positions, flags, matrix);
7313 if (stat == NotImplemented)
7314 stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
7315 brush, positions, flags, matrix);
7316 return stat;
7319 /*****************************************************************************
7320 * GdipDrawDriverString [GDIPLUS.@]
7322 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
7323 GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
7324 GDIPCONST PointF *positions, INT flags,
7325 GDIPCONST GpMatrix *matrix )
7327 TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
7329 if (!graphics || !text || !font || !brush || !positions)
7330 return InvalidParameter;
7332 return draw_driver_string(graphics, text, length, font, NULL,
7333 brush, positions, flags, matrix);
7336 /*****************************************************************************
7337 * GdipIsVisibleClipEmpty [GDIPLUS.@]
7339 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
7341 GpStatus stat;
7342 GpRegion* rgn;
7344 TRACE("(%p, %p)\n", graphics, res);
7346 if((stat = GdipCreateRegion(&rgn)) != Ok)
7347 return stat;
7349 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
7350 goto cleanup;
7352 stat = GdipIsEmptyRegion(rgn, graphics, res);
7354 cleanup:
7355 GdipDeleteRegion(rgn);
7356 return stat;
7359 GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
7361 static int calls;
7363 TRACE("(%p) stub\n", graphics);
7365 if(!(calls++))
7366 FIXME("not implemented\n");
7368 return NotImplemented;
7371 GpStatus WINGDIPAPI GdipGraphicsSetAbort(GpGraphics *graphics, GdiplusAbort *pabort)
7373 TRACE("(%p, %p)\n", graphics, pabort);
7375 if (!graphics)
7376 return InvalidParameter;
7378 if (pabort)
7379 FIXME("Abort callback is not supported.\n");
7381 return Ok;