gdiplus: Access bitmaps by row in alpha_blend_bmp_pixels.
[wine.git] / dlls / gdiplus / graphics.c
blob6d86e3a0d839203726a108f3526516c1116b7beb
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 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
55 GpCoordinateSpace src_space, GpMatrix *matrix);
57 /* Converts from gdiplus path point type to gdi path point type. */
58 static BYTE convert_path_point_type(BYTE type)
60 BYTE ret;
62 switch(type & PathPointTypePathTypeMask){
63 case PathPointTypeBezier:
64 ret = PT_BEZIERTO;
65 break;
66 case PathPointTypeLine:
67 ret = PT_LINETO;
68 break;
69 case PathPointTypeStart:
70 ret = PT_MOVETO;
71 break;
72 default:
73 ERR("Bad point type\n");
74 return 0;
77 if(type & PathPointTypeCloseSubpath)
78 ret |= PT_CLOSEFIGURE;
80 return ret;
83 static COLORREF get_gdi_brush_color(const GpBrush *brush)
85 ARGB argb;
87 switch (brush->bt)
89 case BrushTypeSolidColor:
91 const GpSolidFill *sf = (const GpSolidFill *)brush;
92 argb = sf->color;
93 break;
95 case BrushTypeHatchFill:
97 const GpHatch *hatch = (const GpHatch *)brush;
98 argb = hatch->forecol;
99 break;
101 case BrushTypeLinearGradient:
103 const GpLineGradient *line = (const GpLineGradient *)brush;
104 argb = line->startcolor;
105 break;
107 case BrushTypePathGradient:
109 const GpPathGradient *grad = (const GpPathGradient *)brush;
110 argb = grad->centercolor;
111 break;
113 default:
114 FIXME("unhandled brush type %d\n", brush->bt);
115 argb = 0;
116 break;
118 return ARGB2COLORREF(argb);
121 static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
123 HBITMAP hbmp;
124 BITMAPINFOHEADER bmih;
125 DWORD *bits;
126 int x, y;
128 bmih.biSize = sizeof(bmih);
129 bmih.biWidth = 8;
130 bmih.biHeight = 8;
131 bmih.biPlanes = 1;
132 bmih.biBitCount = 32;
133 bmih.biCompression = BI_RGB;
134 bmih.biSizeImage = 0;
136 hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
137 if (hbmp)
139 const char *hatch_data;
141 if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
143 for (y = 0; y < 8; y++)
145 for (x = 0; x < 8; x++)
147 if (hatch_data[y] & (0x80 >> x))
148 bits[y * 8 + x] = hatch->forecol;
149 else
150 bits[y * 8 + x] = hatch->backcol;
154 else
156 FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
158 for (y = 0; y < 64; y++)
159 bits[y] = hatch->forecol;
163 return hbmp;
166 static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
168 switch (brush->bt)
170 case BrushTypeSolidColor:
172 const GpSolidFill *sf = (const GpSolidFill *)brush;
173 lb->lbStyle = BS_SOLID;
174 lb->lbColor = ARGB2COLORREF(sf->color);
175 lb->lbHatch = 0;
176 return Ok;
179 case BrushTypeHatchFill:
181 const GpHatch *hatch = (const GpHatch *)brush;
182 HBITMAP hbmp;
184 hbmp = create_hatch_bitmap(hatch);
185 if (!hbmp) return OutOfMemory;
187 lb->lbStyle = BS_PATTERN;
188 lb->lbColor = 0;
189 lb->lbHatch = (ULONG_PTR)hbmp;
190 return Ok;
193 default:
194 FIXME("unhandled brush type %d\n", brush->bt);
195 lb->lbStyle = BS_SOLID;
196 lb->lbColor = get_gdi_brush_color(brush);
197 lb->lbHatch = 0;
198 return Ok;
202 static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
204 switch (lb->lbStyle)
206 case BS_PATTERN:
207 DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
208 break;
210 return Ok;
213 static HBRUSH create_gdi_brush(const GpBrush *brush)
215 LOGBRUSH lb;
216 HBRUSH gdibrush;
218 if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
220 gdibrush = CreateBrushIndirect(&lb);
221 free_gdi_logbrush(&lb);
223 return gdibrush;
226 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
228 LOGBRUSH lb;
229 HPEN gdipen;
230 REAL width;
231 INT save_state, i, numdashes;
232 GpPointF pt[2];
233 DWORD dash_array[MAX_DASHLEN];
235 save_state = SaveDC(graphics->hdc);
237 EndPath(graphics->hdc);
239 if(pen->unit == UnitPixel){
240 width = pen->width;
242 else{
243 /* Get an estimate for the amount the pen width is affected by the world
244 * transform. (This is similar to what some of the wine drivers do.) */
245 pt[0].X = 0.0;
246 pt[0].Y = 0.0;
247 pt[1].X = 1.0;
248 pt[1].Y = 1.0;
249 GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
250 width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
251 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
253 width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
256 if(pen->dash == DashStyleCustom){
257 numdashes = min(pen->numdashes, MAX_DASHLEN);
259 TRACE("dashes are: ");
260 for(i = 0; i < numdashes; i++){
261 dash_array[i] = gdip_round(width * pen->dashes[i]);
262 TRACE("%d, ", dash_array[i]);
264 TRACE("\n and the pen style is %x\n", pen->style);
266 create_gdi_logbrush(pen->brush, &lb);
267 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
268 numdashes, dash_array);
269 free_gdi_logbrush(&lb);
271 else
273 create_gdi_logbrush(pen->brush, &lb);
274 gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
275 free_gdi_logbrush(&lb);
278 SelectObject(graphics->hdc, gdipen);
280 return save_state;
283 static void restore_dc(GpGraphics *graphics, INT state)
285 DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
286 RestoreDC(graphics->hdc, state);
289 /* This helper applies all the changes that the points listed in ptf need in
290 * order to be drawn on the device context. In the end, this should include at
291 * least:
292 * -scaling by page unit
293 * -applying world transformation
294 * -converting from float to int
295 * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
296 * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
297 * gdi to draw, and these functions would irreparably mess with line widths.
299 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
300 GpPointF *ptf, INT count)
302 REAL scale_x, scale_y;
303 GpMatrix matrix;
304 int i;
306 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
307 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
309 /* apply page scale */
310 if(graphics->unit != UnitDisplay)
312 scale_x *= graphics->scale;
313 scale_y *= graphics->scale;
316 matrix = graphics->worldtrans;
317 GdipScaleMatrix(&matrix, scale_x, scale_y, MatrixOrderAppend);
318 GdipTransformMatrixPoints(&matrix, ptf, count);
320 for(i = 0; i < count; i++){
321 pti[i].x = gdip_round(ptf[i].X);
322 pti[i].y = gdip_round(ptf[i].Y);
326 static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
327 HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
329 if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
331 TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
333 StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
334 hdc, src_x, src_y, src_width, src_height, SRCCOPY);
336 else
338 BLENDFUNCTION bf;
340 bf.BlendOp = AC_SRC_OVER;
341 bf.BlendFlags = 0;
342 bf.SourceConstantAlpha = 255;
343 bf.AlphaFormat = AC_SRC_ALPHA;
345 GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
346 hdc, src_x, src_y, src_width, src_height, bf);
350 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
352 /* clipping region is in device coords */
353 return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
356 /* Draw non-premultiplied ARGB data to the given graphics object */
357 static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
358 const BYTE *src, INT src_width, INT src_height, INT src_stride)
360 GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
361 INT x, y;
363 for (y=0; y<src_height; y++)
365 for (x=0; x<src_width; x++)
367 ARGB dst_color, src_color;
368 src_color = ((ARGB*)(src + src_stride * y))[x];
370 if (!(src_color & 0xff000000))
371 continue;
373 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
374 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
378 return Ok;
381 static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
382 const BYTE *src, INT src_width, INT src_height, INT src_stride)
384 HDC hdc;
385 HBITMAP hbitmap;
386 BITMAPINFOHEADER bih;
387 BYTE *temp_bits;
389 hdc = CreateCompatibleDC(0);
391 bih.biSize = sizeof(BITMAPINFOHEADER);
392 bih.biWidth = src_width;
393 bih.biHeight = -src_height;
394 bih.biPlanes = 1;
395 bih.biBitCount = 32;
396 bih.biCompression = BI_RGB;
397 bih.biSizeImage = 0;
398 bih.biXPelsPerMeter = 0;
399 bih.biYPelsPerMeter = 0;
400 bih.biClrUsed = 0;
401 bih.biClrImportant = 0;
403 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
404 (void**)&temp_bits, NULL, 0);
406 if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
407 memcpy(temp_bits, src, src_width * src_height * 4);
408 else
409 convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
410 4 * src_width, src, src_stride);
412 SelectObject(hdc, hbitmap);
413 gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
414 hdc, 0, 0, src_width, src_height);
415 DeleteDC(hdc);
416 DeleteObject(hbitmap);
418 return Ok;
421 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
422 const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion)
424 GpStatus stat=Ok;
426 if (graphics->image && graphics->image->type == ImageTypeBitmap)
428 DWORD i;
429 int size;
430 RGNDATA *rgndata;
431 RECT *rects;
432 HRGN hrgn, visible_rgn;
434 hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
435 if (!hrgn)
436 return OutOfMemory;
438 stat = get_clip_hrgn(graphics, &visible_rgn);
439 if (stat != Ok)
441 DeleteObject(hrgn);
442 return stat;
445 if (visible_rgn)
447 CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
448 DeleteObject(visible_rgn);
451 if (hregion)
452 CombineRgn(hrgn, hrgn, hregion, RGN_AND);
454 size = GetRegionData(hrgn, 0, NULL);
456 rgndata = GdipAlloc(size);
457 if (!rgndata)
459 DeleteObject(hrgn);
460 return OutOfMemory;
463 GetRegionData(hrgn, size, rgndata);
465 rects = (RECT*)rgndata->Buffer;
467 for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
469 stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
470 &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
471 rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
472 src_stride);
475 GdipFree(rgndata);
477 DeleteObject(hrgn);
479 return stat;
481 else if (graphics->image && graphics->image->type == ImageTypeMetafile)
483 ERR("This should not be used for metafiles; fix caller\n");
484 return NotImplemented;
486 else
488 HRGN hrgn;
489 int save;
491 stat = get_clip_hrgn(graphics, &hrgn);
493 if (stat != Ok)
494 return stat;
496 save = SaveDC(graphics->hdc);
498 if (hrgn)
499 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
501 if (hregion)
502 ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
504 stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
505 src_height, src_stride);
507 RestoreDC(graphics->hdc, save);
509 DeleteObject(hrgn);
511 return stat;
515 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
516 const BYTE *src, INT src_width, INT src_height, INT src_stride)
518 return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL);
521 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
523 ARGB result=0;
524 ARGB i;
525 INT a1, a2, a3;
527 a1 = (start >> 24) & 0xff;
528 a2 = (end >> 24) & 0xff;
530 a3 = (int)(a1*(1.0f - position)+a2*(position));
532 result |= a3 << 24;
534 for (i=0xff; i<=0xff0000; i = i << 8)
535 result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i;
536 return result;
539 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
541 REAL blendfac;
543 /* clamp to between 0.0 and 1.0, using the wrap mode */
544 if (brush->wrap == WrapModeTile)
546 position = fmodf(position, 1.0f);
547 if (position < 0.0f) position += 1.0f;
549 else /* WrapModeFlip* */
551 position = fmodf(position, 2.0f);
552 if (position < 0.0f) position += 2.0f;
553 if (position > 1.0f) position = 2.0f - position;
556 if (brush->blendcount == 1)
557 blendfac = position;
558 else
560 int i=1;
561 REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
562 REAL range;
564 /* locate the blend positions surrounding this position */
565 while (position > brush->blendpos[i])
566 i++;
568 /* interpolate between the blend positions */
569 left_blendpos = brush->blendpos[i-1];
570 left_blendfac = brush->blendfac[i-1];
571 right_blendpos = brush->blendpos[i];
572 right_blendfac = brush->blendfac[i];
573 range = right_blendpos - left_blendpos;
574 blendfac = (left_blendfac * (right_blendpos - position) +
575 right_blendfac * (position - left_blendpos)) / range;
578 if (brush->pblendcount == 0)
579 return blend_colors(brush->startcolor, brush->endcolor, blendfac);
580 else
582 int i=1;
583 ARGB left_blendcolor, right_blendcolor;
584 REAL left_blendpos, right_blendpos;
586 /* locate the blend colors surrounding this position */
587 while (blendfac > brush->pblendpos[i])
588 i++;
590 /* interpolate between the blend colors */
591 left_blendpos = brush->pblendpos[i-1];
592 left_blendcolor = brush->pblendcolor[i-1];
593 right_blendpos = brush->pblendpos[i];
594 right_blendcolor = brush->pblendcolor[i];
595 blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
596 return blend_colors(left_blendcolor, right_blendcolor, blendfac);
600 static BOOL round_color_matrix(const ColorMatrix *matrix, int values[5][5])
602 /* Convert floating point color matrix to int[5][5], return TRUE if it's an identity */
603 BOOL identity = TRUE;
604 int i, j;
606 for (i=0; i<4; i++)
607 for (j=0; j<5; j++)
609 if (matrix->m[j][i] != (i == j ? 1.0 : 0.0))
610 identity = FALSE;
611 values[j][i] = gdip_round(matrix->m[j][i] * 256.0);
614 return identity;
617 static ARGB transform_color(ARGB color, int matrix[5][5])
619 int val[5], res[4];
620 int i, j;
621 unsigned char a, r, g, b;
623 val[0] = ((color >> 16) & 0xff); /* red */
624 val[1] = ((color >> 8) & 0xff); /* green */
625 val[2] = (color & 0xff); /* blue */
626 val[3] = ((color >> 24) & 0xff); /* alpha */
627 val[4] = 255; /* translation */
629 for (i=0; i<4; i++)
631 res[i] = 0;
633 for (j=0; j<5; j++)
634 res[i] += matrix[j][i] * val[j];
637 a = min(max(res[3] / 256, 0), 255);
638 r = min(max(res[0] / 256, 0), 255);
639 g = min(max(res[1] / 256, 0), 255);
640 b = min(max(res[2] / 256, 0), 255);
642 return (a << 24) | (r << 16) | (g << 8) | b;
645 static BOOL color_is_gray(ARGB color)
647 unsigned char r, g, b;
649 r = (color >> 16) & 0xff;
650 g = (color >> 8) & 0xff;
651 b = color & 0xff;
653 return (r == g) && (g == b);
656 static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
657 UINT width, UINT height, INT stride, ColorAdjustType type)
659 UINT x, y;
660 INT i;
662 if (attributes->colorkeys[type].enabled ||
663 attributes->colorkeys[ColorAdjustTypeDefault].enabled)
665 const struct color_key *key;
666 BYTE min_blue, min_green, min_red;
667 BYTE max_blue, max_green, max_red;
669 if (attributes->colorkeys[type].enabled)
670 key = &attributes->colorkeys[type];
671 else
672 key = &attributes->colorkeys[ColorAdjustTypeDefault];
674 min_blue = key->low&0xff;
675 min_green = (key->low>>8)&0xff;
676 min_red = (key->low>>16)&0xff;
678 max_blue = key->high&0xff;
679 max_green = (key->high>>8)&0xff;
680 max_red = (key->high>>16)&0xff;
682 for (x=0; x<width; x++)
683 for (y=0; y<height; y++)
685 ARGB *src_color;
686 BYTE blue, green, red;
687 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
688 blue = *src_color&0xff;
689 green = (*src_color>>8)&0xff;
690 red = (*src_color>>16)&0xff;
691 if (blue >= min_blue && green >= min_green && red >= min_red &&
692 blue <= max_blue && green <= max_green && red <= max_red)
693 *src_color = 0x00000000;
697 if (attributes->colorremaptables[type].enabled ||
698 attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
700 const struct color_remap_table *table;
702 if (attributes->colorremaptables[type].enabled)
703 table = &attributes->colorremaptables[type];
704 else
705 table = &attributes->colorremaptables[ColorAdjustTypeDefault];
707 for (x=0; x<width; x++)
708 for (y=0; y<height; y++)
710 ARGB *src_color;
711 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
712 for (i=0; i<table->mapsize; i++)
714 if (*src_color == table->colormap[i].oldColor.Argb)
716 *src_color = table->colormap[i].newColor.Argb;
717 break;
723 if (attributes->colormatrices[type].enabled ||
724 attributes->colormatrices[ColorAdjustTypeDefault].enabled)
726 const struct color_matrix *colormatrices;
727 int color_matrix[5][5];
728 int gray_matrix[5][5];
729 BOOL identity;
731 if (attributes->colormatrices[type].enabled)
732 colormatrices = &attributes->colormatrices[type];
733 else
734 colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
736 identity = round_color_matrix(&colormatrices->colormatrix, color_matrix);
738 if (colormatrices->flags == ColorMatrixFlagsAltGray)
739 identity = (round_color_matrix(&colormatrices->graymatrix, gray_matrix) && identity);
741 if (!identity)
743 for (x=0; x<width; x++)
745 for (y=0; y<height; y++)
747 ARGB *src_color;
748 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
750 if (colormatrices->flags == ColorMatrixFlagsDefault ||
751 !color_is_gray(*src_color))
753 *src_color = transform_color(*src_color, color_matrix);
755 else if (colormatrices->flags == ColorMatrixFlagsAltGray)
757 *src_color = transform_color(*src_color, gray_matrix);
764 if (attributes->gamma_enabled[type] ||
765 attributes->gamma_enabled[ColorAdjustTypeDefault])
767 REAL gamma;
769 if (attributes->gamma_enabled[type])
770 gamma = attributes->gamma[type];
771 else
772 gamma = attributes->gamma[ColorAdjustTypeDefault];
774 for (x=0; x<width; x++)
775 for (y=0; y<height; y++)
777 ARGB *src_color;
778 BYTE blue, green, red;
779 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
781 blue = *src_color&0xff;
782 green = (*src_color>>8)&0xff;
783 red = (*src_color>>16)&0xff;
785 /* FIXME: We should probably use a table for this. */
786 blue = floorf(powf(blue / 255.0, gamma) * 255.0);
787 green = floorf(powf(green / 255.0, gamma) * 255.0);
788 red = floorf(powf(red / 255.0, gamma) * 255.0);
790 *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
795 /* Given a bitmap and its source rectangle, find the smallest rectangle in the
796 * bitmap that contains all the pixels we may need to draw it. */
797 static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
798 GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
799 GpRect *rect)
801 INT left, top, right, bottom;
803 switch (interpolation)
805 case InterpolationModeHighQualityBilinear:
806 case InterpolationModeHighQualityBicubic:
807 /* FIXME: Include a greater range for the prefilter? */
808 case InterpolationModeBicubic:
809 case InterpolationModeBilinear:
810 left = (INT)(floorf(srcx));
811 top = (INT)(floorf(srcy));
812 right = (INT)(ceilf(srcx+srcwidth));
813 bottom = (INT)(ceilf(srcy+srcheight));
814 break;
815 case InterpolationModeNearestNeighbor:
816 default:
817 left = gdip_round(srcx);
818 top = gdip_round(srcy);
819 right = gdip_round(srcx+srcwidth);
820 bottom = gdip_round(srcy+srcheight);
821 break;
824 if (wrap == WrapModeClamp)
826 if (left < 0)
827 left = 0;
828 if (top < 0)
829 top = 0;
830 if (right >= bitmap->width)
831 right = bitmap->width-1;
832 if (bottom >= bitmap->height)
833 bottom = bitmap->height-1;
834 if (bottom < top || right < left)
835 /* entirely outside image, just sample a pixel so we don't have to
836 * special-case this later */
837 left = top = right = bottom = 0;
839 else
841 /* In some cases we can make the rectangle smaller here, but the logic
842 * is hard to get right, and tiling suggests we're likely to use the
843 * entire source image. */
844 if (left < 0 || right >= bitmap->width)
846 left = 0;
847 right = bitmap->width-1;
850 if (top < 0 || bottom >= bitmap->height)
852 top = 0;
853 bottom = bitmap->height-1;
857 rect->X = left;
858 rect->Y = top;
859 rect->Width = right - left + 1;
860 rect->Height = bottom - top + 1;
863 static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
864 UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
866 if (attributes->wrap == WrapModeClamp)
868 if (x < 0 || y < 0 || x >= width || y >= height)
869 return attributes->outside_color;
871 else
873 /* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
874 if (x < 0)
875 x = width*2 + x % (width * 2);
876 if (y < 0)
877 y = height*2 + y % (height * 2);
879 if ((attributes->wrap & 1) == 1)
881 /* Flip X */
882 if ((x / width) % 2 == 0)
883 x = x % width;
884 else
885 x = width - 1 - x % width;
887 else
888 x = x % width;
890 if ((attributes->wrap & 2) == 2)
892 /* Flip Y */
893 if ((y / height) % 2 == 0)
894 y = y % height;
895 else
896 y = height - 1 - y % height;
898 else
899 y = y % height;
902 if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
904 ERR("out of range pixel requested\n");
905 return 0xffcd0084;
908 return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
911 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
912 UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
913 InterpolationMode interpolation, PixelOffsetMode offset_mode)
915 static int fixme;
917 switch (interpolation)
919 default:
920 if (!fixme++)
921 FIXME("Unimplemented interpolation %i\n", interpolation);
922 /* fall-through */
923 case InterpolationModeBilinear:
925 REAL leftxf, topyf;
926 INT leftx, rightx, topy, bottomy;
927 ARGB topleft, topright, bottomleft, bottomright;
928 ARGB top, bottom;
929 float x_offset;
931 leftxf = floorf(point->X);
932 leftx = (INT)leftxf;
933 rightx = (INT)ceilf(point->X);
934 topyf = floorf(point->Y);
935 topy = (INT)topyf;
936 bottomy = (INT)ceilf(point->Y);
938 if (leftx == rightx && topy == bottomy)
939 return sample_bitmap_pixel(src_rect, bits, width, height,
940 leftx, topy, attributes);
942 topleft = sample_bitmap_pixel(src_rect, bits, width, height,
943 leftx, topy, attributes);
944 topright = sample_bitmap_pixel(src_rect, bits, width, height,
945 rightx, topy, attributes);
946 bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
947 leftx, bottomy, attributes);
948 bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
949 rightx, bottomy, attributes);
951 x_offset = point->X - leftxf;
952 top = blend_colors(topleft, topright, x_offset);
953 bottom = blend_colors(bottomleft, bottomright, x_offset);
955 return blend_colors(top, bottom, point->Y - topyf);
957 case InterpolationModeNearestNeighbor:
959 FLOAT pixel_offset;
960 switch (offset_mode)
962 default:
963 case PixelOffsetModeNone:
964 case PixelOffsetModeHighSpeed:
965 pixel_offset = 0.5;
966 break;
968 case PixelOffsetModeHalf:
969 case PixelOffsetModeHighQuality:
970 pixel_offset = 0.0;
971 break;
973 return sample_bitmap_pixel(src_rect, bits, width, height,
974 floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
980 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
982 return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
985 static BOOL brush_can_fill_path(GpBrush *brush)
987 switch (brush->bt)
989 case BrushTypeSolidColor:
990 return TRUE;
991 case BrushTypeHatchFill:
993 GpHatch *hatch = (GpHatch*)brush;
994 return ((hatch->forecol & 0xff000000) == 0xff000000) &&
995 ((hatch->backcol & 0xff000000) == 0xff000000);
997 case BrushTypeLinearGradient:
998 case BrushTypeTextureFill:
999 /* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
1000 default:
1001 return FALSE;
1005 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
1007 switch (brush->bt)
1009 case BrushTypeSolidColor:
1011 GpSolidFill *fill = (GpSolidFill*)brush;
1012 HBITMAP bmp = ARGB2BMP(fill->color);
1014 if (bmp)
1016 RECT rc;
1017 /* partially transparent fill */
1019 SelectClipPath(graphics->hdc, RGN_AND);
1020 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
1022 HDC hdc = CreateCompatibleDC(NULL);
1024 if (!hdc) break;
1026 SelectObject(hdc, bmp);
1027 gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
1028 hdc, 0, 0, 1, 1);
1029 DeleteDC(hdc);
1032 DeleteObject(bmp);
1033 break;
1035 /* else fall through */
1037 default:
1039 HBRUSH gdibrush, old_brush;
1041 gdibrush = create_gdi_brush(brush);
1042 if (!gdibrush) return;
1044 old_brush = SelectObject(graphics->hdc, gdibrush);
1045 FillPath(graphics->hdc);
1046 SelectObject(graphics->hdc, old_brush);
1047 DeleteObject(gdibrush);
1048 break;
1053 static BOOL brush_can_fill_pixels(GpBrush *brush)
1055 switch (brush->bt)
1057 case BrushTypeSolidColor:
1058 case BrushTypeHatchFill:
1059 case BrushTypeLinearGradient:
1060 case BrushTypeTextureFill:
1061 case BrushTypePathGradient:
1062 return TRUE;
1063 default:
1064 return FALSE;
1068 static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
1069 DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
1071 switch (brush->bt)
1073 case BrushTypeSolidColor:
1075 int x, y;
1076 GpSolidFill *fill = (GpSolidFill*)brush;
1077 for (x=0; x<fill_area->Width; x++)
1078 for (y=0; y<fill_area->Height; y++)
1079 argb_pixels[x + y*cdwStride] = fill->color;
1080 return Ok;
1082 case BrushTypeHatchFill:
1084 int x, y;
1085 GpHatch *fill = (GpHatch*)brush;
1086 const char *hatch_data;
1088 if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
1089 return NotImplemented;
1091 for (x=0; x<fill_area->Width; x++)
1092 for (y=0; y<fill_area->Height; y++)
1094 int hx, hy;
1096 /* FIXME: Account for the rendering origin */
1097 hx = (x + fill_area->X) % 8;
1098 hy = (y + fill_area->Y) % 8;
1100 if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
1101 argb_pixels[x + y*cdwStride] = fill->forecol;
1102 else
1103 argb_pixels[x + y*cdwStride] = fill->backcol;
1106 return Ok;
1108 case BrushTypeLinearGradient:
1110 GpLineGradient *fill = (GpLineGradient*)brush;
1111 GpPointF draw_points[3], line_points[3];
1112 GpStatus stat;
1113 static const GpRectF box_1 = { 0.0, 0.0, 1.0, 1.0 };
1114 GpMatrix *world_to_gradient; /* FIXME: Store this in the brush? */
1115 int x, y;
1117 draw_points[0].X = fill_area->X;
1118 draw_points[0].Y = fill_area->Y;
1119 draw_points[1].X = fill_area->X+1;
1120 draw_points[1].Y = fill_area->Y;
1121 draw_points[2].X = fill_area->X;
1122 draw_points[2].Y = fill_area->Y+1;
1124 /* Transform the points to a co-ordinate space where X is the point's
1125 * position in the gradient, 0.0 being the start point and 1.0 the
1126 * end point. */
1127 stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
1128 CoordinateSpaceDevice, draw_points, 3);
1130 if (stat == Ok)
1132 line_points[0] = fill->startpoint;
1133 line_points[1] = fill->endpoint;
1134 line_points[2].X = fill->startpoint.X + (fill->startpoint.Y - fill->endpoint.Y);
1135 line_points[2].Y = fill->startpoint.Y + (fill->endpoint.X - fill->startpoint.X);
1137 stat = GdipCreateMatrix3(&box_1, line_points, &world_to_gradient);
1140 if (stat == Ok)
1142 stat = GdipInvertMatrix(world_to_gradient);
1144 if (stat == Ok)
1145 stat = GdipTransformMatrixPoints(world_to_gradient, draw_points, 3);
1147 GdipDeleteMatrix(world_to_gradient);
1150 if (stat == Ok)
1152 REAL x_delta = draw_points[1].X - draw_points[0].X;
1153 REAL y_delta = draw_points[2].X - draw_points[0].X;
1155 for (y=0; y<fill_area->Height; y++)
1157 for (x=0; x<fill_area->Width; x++)
1159 REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
1161 argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
1166 return stat;
1168 case BrushTypeTextureFill:
1170 GpTexture *fill = (GpTexture*)brush;
1171 GpPointF draw_points[3];
1172 GpStatus stat;
1173 int x, y;
1174 GpBitmap *bitmap;
1175 int src_stride;
1176 GpRect src_area;
1178 if (fill->image->type != ImageTypeBitmap)
1180 FIXME("metafile texture brushes not implemented\n");
1181 return NotImplemented;
1184 bitmap = (GpBitmap*)fill->image;
1185 src_stride = sizeof(ARGB) * bitmap->width;
1187 src_area.X = src_area.Y = 0;
1188 src_area.Width = bitmap->width;
1189 src_area.Height = bitmap->height;
1191 draw_points[0].X = fill_area->X;
1192 draw_points[0].Y = fill_area->Y;
1193 draw_points[1].X = fill_area->X+1;
1194 draw_points[1].Y = fill_area->Y;
1195 draw_points[2].X = fill_area->X;
1196 draw_points[2].Y = fill_area->Y+1;
1198 /* Transform the points to the co-ordinate space of the bitmap. */
1199 stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
1200 CoordinateSpaceDevice, draw_points, 3);
1202 if (stat == Ok)
1204 GpMatrix world_to_texture = fill->transform;
1206 stat = GdipInvertMatrix(&world_to_texture);
1207 if (stat == Ok)
1208 stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
1211 if (stat == Ok && !fill->bitmap_bits)
1213 BitmapData lockeddata;
1215 fill->bitmap_bits = GdipAlloc(sizeof(ARGB) * bitmap->width * bitmap->height);
1216 if (!fill->bitmap_bits)
1217 stat = OutOfMemory;
1219 if (stat == Ok)
1221 lockeddata.Width = bitmap->width;
1222 lockeddata.Height = bitmap->height;
1223 lockeddata.Stride = src_stride;
1224 lockeddata.PixelFormat = PixelFormat32bppARGB;
1225 lockeddata.Scan0 = fill->bitmap_bits;
1227 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
1228 PixelFormat32bppARGB, &lockeddata);
1231 if (stat == Ok)
1232 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
1234 if (stat == Ok)
1235 apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
1236 bitmap->width, bitmap->height,
1237 src_stride, ColorAdjustTypeBitmap);
1239 if (stat != Ok)
1241 GdipFree(fill->bitmap_bits);
1242 fill->bitmap_bits = NULL;
1246 if (stat == Ok)
1248 REAL x_dx = draw_points[1].X - draw_points[0].X;
1249 REAL x_dy = draw_points[1].Y - draw_points[0].Y;
1250 REAL y_dx = draw_points[2].X - draw_points[0].X;
1251 REAL y_dy = draw_points[2].Y - draw_points[0].Y;
1253 for (y=0; y<fill_area->Height; y++)
1255 for (x=0; x<fill_area->Width; x++)
1257 GpPointF point;
1258 point.X = draw_points[0].X + x * x_dx + y * y_dx;
1259 point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
1261 argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
1262 &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
1263 &point, fill->imageattributes, graphics->interpolation,
1264 graphics->pixeloffset);
1269 return stat;
1271 case BrushTypePathGradient:
1273 GpPathGradient *fill = (GpPathGradient*)brush;
1274 GpPath *flat_path;
1275 GpMatrix world_to_device;
1276 GpStatus stat;
1277 int i, figure_start=0;
1278 GpPointF start_point, end_point, center_point;
1279 BYTE type;
1280 REAL min_yf, max_yf, line1_xf, line2_xf;
1281 INT min_y, max_y, min_x, max_x;
1282 INT x, y;
1283 ARGB outer_color;
1284 static BOOL transform_fixme_once;
1286 if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
1288 static int once;
1289 if (!once++)
1290 FIXME("path gradient focus not implemented\n");
1293 if (fill->gamma)
1295 static int once;
1296 if (!once++)
1297 FIXME("path gradient gamma correction not implemented\n");
1300 if (fill->blendcount)
1302 static int once;
1303 if (!once++)
1304 FIXME("path gradient blend not implemented\n");
1307 if (fill->pblendcount)
1309 static int once;
1310 if (!once++)
1311 FIXME("path gradient preset blend not implemented\n");
1314 if (!transform_fixme_once)
1316 BOOL is_identity=TRUE;
1317 GdipIsMatrixIdentity(&fill->transform, &is_identity);
1318 if (!is_identity)
1320 FIXME("path gradient transform not implemented\n");
1321 transform_fixme_once = TRUE;
1325 stat = GdipClonePath(fill->path, &flat_path);
1327 if (stat != Ok)
1328 return stat;
1330 stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
1331 CoordinateSpaceWorld, &world_to_device);
1332 if (stat == Ok)
1334 stat = GdipTransformPath(flat_path, &world_to_device);
1336 if (stat == Ok)
1338 center_point = fill->center;
1339 stat = GdipTransformMatrixPoints(&world_to_device, &center_point, 1);
1342 if (stat == Ok)
1343 stat = GdipFlattenPath(flat_path, NULL, 0.5);
1346 if (stat != Ok)
1348 GdipDeletePath(flat_path);
1349 return stat;
1352 for (i=0; i<flat_path->pathdata.Count; i++)
1354 int start_center_line=0, end_center_line=0;
1355 BOOL seen_start = FALSE, seen_end = FALSE, seen_center = FALSE;
1356 REAL center_distance;
1357 ARGB start_color, end_color;
1358 REAL dy, dx;
1360 type = flat_path->pathdata.Types[i];
1362 if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
1363 figure_start = i;
1365 start_point = flat_path->pathdata.Points[i];
1367 start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
1369 if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
1371 end_point = flat_path->pathdata.Points[figure_start];
1372 end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
1374 else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
1376 end_point = flat_path->pathdata.Points[i+1];
1377 end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
1379 else
1380 continue;
1382 outer_color = start_color;
1384 min_yf = center_point.Y;
1385 if (min_yf > start_point.Y) min_yf = start_point.Y;
1386 if (min_yf > end_point.Y) min_yf = end_point.Y;
1388 if (min_yf < fill_area->Y)
1389 min_y = fill_area->Y;
1390 else
1391 min_y = (INT)ceil(min_yf);
1393 max_yf = center_point.Y;
1394 if (max_yf < start_point.Y) max_yf = start_point.Y;
1395 if (max_yf < end_point.Y) max_yf = end_point.Y;
1397 if (max_yf > fill_area->Y + fill_area->Height)
1398 max_y = fill_area->Y + fill_area->Height;
1399 else
1400 max_y = (INT)ceil(max_yf);
1402 dy = end_point.Y - start_point.Y;
1403 dx = end_point.X - start_point.X;
1405 /* This is proportional to the distance from start-end line to center point. */
1406 center_distance = dy * (start_point.X - center_point.X) +
1407 dx * (center_point.Y - start_point.Y);
1409 for (y=min_y; y<max_y; y++)
1411 REAL yf = (REAL)y;
1413 if (!seen_start && yf >= start_point.Y)
1415 seen_start = TRUE;
1416 start_center_line ^= 1;
1418 if (!seen_end && yf >= end_point.Y)
1420 seen_end = TRUE;
1421 end_center_line ^= 1;
1423 if (!seen_center && yf >= center_point.Y)
1425 seen_center = TRUE;
1426 start_center_line ^= 1;
1427 end_center_line ^= 1;
1430 if (start_center_line)
1431 line1_xf = intersect_line_scanline(&start_point, &center_point, yf);
1432 else
1433 line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
1435 if (end_center_line)
1436 line2_xf = intersect_line_scanline(&end_point, &center_point, yf);
1437 else
1438 line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
1440 if (line1_xf < line2_xf)
1442 min_x = (INT)ceil(line1_xf);
1443 max_x = (INT)ceil(line2_xf);
1445 else
1447 min_x = (INT)ceil(line2_xf);
1448 max_x = (INT)ceil(line1_xf);
1451 if (min_x < fill_area->X)
1452 min_x = fill_area->X;
1453 if (max_x > fill_area->X + fill_area->Width)
1454 max_x = fill_area->X + fill_area->Width;
1456 for (x=min_x; x<max_x; x++)
1458 REAL xf = (REAL)x;
1459 REAL distance;
1461 if (start_color != end_color)
1463 REAL blend_amount, pdy, pdx;
1464 pdy = yf - center_point.Y;
1465 pdx = xf - center_point.X;
1466 blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
1467 outer_color = blend_colors(start_color, end_color, blend_amount);
1470 distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
1471 (end_point.X - start_point.X) * (yf - start_point.Y);
1473 distance = distance / center_distance;
1475 argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
1476 blend_colors(outer_color, fill->centercolor, distance);
1481 GdipDeletePath(flat_path);
1482 return stat;
1484 default:
1485 return NotImplemented;
1489 /* Draws the linecap the specified color and size on the hdc. The linecap is in
1490 * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
1491 * should not be called on an hdc that has a path you care about. */
1492 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
1493 const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
1495 HGDIOBJ oldbrush = NULL, oldpen = NULL;
1496 GpMatrix matrix;
1497 HBRUSH brush = NULL;
1498 HPEN pen = NULL;
1499 PointF ptf[4], *custptf = NULL;
1500 POINT pt[4], *custpt = NULL;
1501 BYTE *tp = NULL;
1502 REAL theta, dsmall, dbig, dx, dy = 0.0;
1503 INT i, count;
1504 LOGBRUSH lb;
1505 BOOL customstroke;
1507 if((x1 == x2) && (y1 == y2))
1508 return;
1510 theta = gdiplus_atan2(y2 - y1, x2 - x1);
1512 customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
1513 if(!customstroke){
1514 brush = CreateSolidBrush(color);
1515 lb.lbStyle = BS_SOLID;
1516 lb.lbColor = color;
1517 lb.lbHatch = 0;
1518 pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
1519 PS_JOIN_MITER, 1, &lb, 0,
1520 NULL);
1521 oldbrush = SelectObject(graphics->hdc, brush);
1522 oldpen = SelectObject(graphics->hdc, pen);
1525 switch(cap){
1526 case LineCapFlat:
1527 break;
1528 case LineCapSquare:
1529 case LineCapSquareAnchor:
1530 case LineCapDiamondAnchor:
1531 size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
1532 if(cap == LineCapDiamondAnchor){
1533 dsmall = cos(theta + M_PI_2) * size;
1534 dbig = sin(theta + M_PI_2) * size;
1536 else{
1537 dsmall = cos(theta + M_PI_4) * size;
1538 dbig = sin(theta + M_PI_4) * size;
1541 ptf[0].X = x2 - dsmall;
1542 ptf[1].X = x2 + dbig;
1544 ptf[0].Y = y2 - dbig;
1545 ptf[3].Y = y2 + dsmall;
1547 ptf[1].Y = y2 - dsmall;
1548 ptf[2].Y = y2 + dbig;
1550 ptf[3].X = x2 - dbig;
1551 ptf[2].X = x2 + dsmall;
1553 transform_and_round_points(graphics, pt, ptf, 4);
1554 Polygon(graphics->hdc, pt, 4);
1556 break;
1557 case LineCapArrowAnchor:
1558 size = size * 4.0 / sqrt(3.0);
1560 dx = cos(M_PI / 6.0 + theta) * size;
1561 dy = sin(M_PI / 6.0 + theta) * size;
1563 ptf[0].X = x2 - dx;
1564 ptf[0].Y = y2 - dy;
1566 dx = cos(- M_PI / 6.0 + theta) * size;
1567 dy = sin(- M_PI / 6.0 + theta) * size;
1569 ptf[1].X = x2 - dx;
1570 ptf[1].Y = y2 - dy;
1572 ptf[2].X = x2;
1573 ptf[2].Y = y2;
1575 transform_and_round_points(graphics, pt, ptf, 3);
1576 Polygon(graphics->hdc, pt, 3);
1578 break;
1579 case LineCapRoundAnchor:
1580 dx = dy = ANCHOR_WIDTH * size / 2.0;
1582 ptf[0].X = x2 - dx;
1583 ptf[0].Y = y2 - dy;
1584 ptf[1].X = x2 + dx;
1585 ptf[1].Y = y2 + dy;
1587 transform_and_round_points(graphics, pt, ptf, 2);
1588 Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
1590 break;
1591 case LineCapTriangle:
1592 size = size / 2.0;
1593 dx = cos(M_PI_2 + theta) * size;
1594 dy = sin(M_PI_2 + theta) * size;
1596 ptf[0].X = x2 - dx;
1597 ptf[0].Y = y2 - dy;
1598 ptf[1].X = x2 + dx;
1599 ptf[1].Y = y2 + dy;
1601 dx = cos(theta) * size;
1602 dy = sin(theta) * size;
1604 ptf[2].X = x2 + dx;
1605 ptf[2].Y = y2 + dy;
1607 transform_and_round_points(graphics, pt, ptf, 3);
1608 Polygon(graphics->hdc, pt, 3);
1610 break;
1611 case LineCapRound:
1612 dx = dy = size / 2.0;
1614 ptf[0].X = x2 - dx;
1615 ptf[0].Y = y2 - dy;
1616 ptf[1].X = x2 + dx;
1617 ptf[1].Y = y2 + dy;
1619 dx = -cos(M_PI_2 + theta) * size;
1620 dy = -sin(M_PI_2 + theta) * size;
1622 ptf[2].X = x2 - dx;
1623 ptf[2].Y = y2 - dy;
1624 ptf[3].X = x2 + dx;
1625 ptf[3].Y = y2 + dy;
1627 transform_and_round_points(graphics, pt, ptf, 4);
1628 Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
1629 pt[2].y, pt[3].x, pt[3].y);
1631 break;
1632 case LineCapCustom:
1633 if(!custom)
1634 break;
1636 count = custom->pathdata.Count;
1637 custptf = GdipAlloc(count * sizeof(PointF));
1638 custpt = GdipAlloc(count * sizeof(POINT));
1639 tp = GdipAlloc(count);
1641 if(!custptf || !custpt || !tp)
1642 goto custend;
1644 memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
1646 GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1647 GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
1648 GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
1649 MatrixOrderAppend);
1650 GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
1651 GdipTransformMatrixPoints(&matrix, custptf, count);
1653 transform_and_round_points(graphics, custpt, custptf, count);
1655 for(i = 0; i < count; i++)
1656 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
1658 if(custom->fill){
1659 BeginPath(graphics->hdc);
1660 PolyDraw(graphics->hdc, custpt, tp, count);
1661 EndPath(graphics->hdc);
1662 StrokeAndFillPath(graphics->hdc);
1664 else
1665 PolyDraw(graphics->hdc, custpt, tp, count);
1667 custend:
1668 GdipFree(custptf);
1669 GdipFree(custpt);
1670 GdipFree(tp);
1671 break;
1672 default:
1673 break;
1676 if(!customstroke){
1677 SelectObject(graphics->hdc, oldbrush);
1678 SelectObject(graphics->hdc, oldpen);
1679 DeleteObject(brush);
1680 DeleteObject(pen);
1684 /* Shortens the line by the given percent by changing x2, y2.
1685 * If percent is > 1.0 then the line will change direction.
1686 * If percent is negative it can lengthen the line. */
1687 static void shorten_line_percent(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL percent)
1689 REAL dist, theta, dx, dy;
1691 if((y1 == *y2) && (x1 == *x2))
1692 return;
1694 dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
1695 theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
1696 dx = cos(theta) * dist;
1697 dy = sin(theta) * dist;
1699 *x2 = *x2 + dx;
1700 *y2 = *y2 + dy;
1703 /* Shortens the line by the given amount by changing x2, y2.
1704 * If the amount is greater than the distance, the line will become length 0.
1705 * If the amount is negative, it can lengthen the line. */
1706 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
1708 REAL dx, dy, percent;
1710 dx = *x2 - x1;
1711 dy = *y2 - y1;
1712 if(dx == 0 && dy == 0)
1713 return;
1715 percent = amt / sqrt(dx * dx + dy * dy);
1716 if(percent >= 1.0){
1717 *x2 = x1;
1718 *y2 = y1;
1719 return;
1722 shorten_line_percent(x1, y1, x2, y2, percent);
1725 /* Conducts a linear search to find the bezier points that will back off
1726 * the endpoint of the curve by a distance of amt. Linear search works
1727 * better than binary in this case because there are multiple solutions,
1728 * and binary searches often find a bad one. I don't think this is what
1729 * Windows does but short of rendering the bezier without GDI's help it's
1730 * the best we can do. If rev then work from the start of the passed points
1731 * instead of the end. */
1732 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
1734 GpPointF origpt[4];
1735 REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
1736 INT i, first = 0, second = 1, third = 2, fourth = 3;
1738 if(rev){
1739 first = 3;
1740 second = 2;
1741 third = 1;
1742 fourth = 0;
1745 origx = pt[fourth].X;
1746 origy = pt[fourth].Y;
1747 memcpy(origpt, pt, sizeof(GpPointF) * 4);
1749 for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
1750 /* reset bezier points to original values */
1751 memcpy(pt, origpt, sizeof(GpPointF) * 4);
1752 /* Perform magic on bezier points. Order is important here.*/
1753 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1754 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1755 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1756 shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
1757 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
1758 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
1760 dx = pt[fourth].X - origx;
1761 dy = pt[fourth].Y - origy;
1763 diff = sqrt(dx * dx + dy * dy);
1764 percent += 0.0005 * amt;
1768 /* Draws a combination of bezier curves and lines between points. */
1769 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
1770 GDIPCONST BYTE * types, INT count, BOOL caps)
1772 POINT *pti = GdipAlloc(count * sizeof(POINT));
1773 BYTE *tp = GdipAlloc(count);
1774 GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
1775 INT i, j;
1776 GpStatus status = GenericError;
1778 if(!count){
1779 status = Ok;
1780 goto end;
1782 if(!pti || !tp || !ptcopy){
1783 status = OutOfMemory;
1784 goto end;
1787 for(i = 1; i < count; i++){
1788 if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
1789 if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
1790 || !(types[i + 2] & PathPointTypeBezier)){
1791 ERR("Bad bezier points\n");
1792 goto end;
1794 i += 2;
1798 memcpy(ptcopy, pt, count * sizeof(GpPointF));
1800 /* If we are drawing caps, go through the points and adjust them accordingly,
1801 * and draw the caps. */
1802 if(caps){
1803 switch(types[count - 1] & PathPointTypePathTypeMask){
1804 case PathPointTypeBezier:
1805 if(pen->endcap == LineCapArrowAnchor)
1806 shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
1807 else if((pen->endcap == LineCapCustom) && pen->customend)
1808 shorten_bezier_amt(&ptcopy[count - 4],
1809 pen->width * pen->customend->inset, FALSE);
1811 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1812 pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
1813 pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
1814 pt[count - 1].X, pt[count - 1].Y);
1816 break;
1817 case PathPointTypeLine:
1818 if(pen->endcap == LineCapArrowAnchor)
1819 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1820 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1821 pen->width);
1822 else if((pen->endcap == LineCapCustom) && pen->customend)
1823 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
1824 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
1825 pen->customend->inset * pen->width);
1827 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
1828 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
1829 pt[count - 1].Y);
1831 break;
1832 default:
1833 ERR("Bad path last point\n");
1834 goto end;
1837 /* Find start of points */
1838 for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
1839 == PathPointTypeStart); j++);
1841 switch(types[j] & PathPointTypePathTypeMask){
1842 case PathPointTypeBezier:
1843 if(pen->startcap == LineCapArrowAnchor)
1844 shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
1845 else if((pen->startcap == LineCapCustom) && pen->customstart)
1846 shorten_bezier_amt(&ptcopy[j - 1],
1847 pen->width * pen->customstart->inset, TRUE);
1849 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
1850 pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
1851 pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
1852 pt[j - 1].X, pt[j - 1].Y);
1854 break;
1855 case PathPointTypeLine:
1856 if(pen->startcap == LineCapArrowAnchor)
1857 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1858 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1859 pen->width);
1860 else if((pen->startcap == LineCapCustom) && pen->customstart)
1861 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1862 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1863 pen->customstart->inset * pen->width);
1865 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
1866 pt[j].X, pt[j].Y, pt[j - 1].X,
1867 pt[j - 1].Y);
1869 break;
1870 default:
1871 ERR("Bad path points\n");
1872 goto end;
1876 transform_and_round_points(graphics, pti, ptcopy, count);
1878 for(i = 0; i < count; i++){
1879 tp[i] = convert_path_point_type(types[i]);
1882 PolyDraw(graphics->hdc, pti, tp, count);
1884 status = Ok;
1886 end:
1887 GdipFree(pti);
1888 GdipFree(ptcopy);
1889 GdipFree(tp);
1891 return status;
1894 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
1896 GpStatus result;
1898 BeginPath(graphics->hdc);
1899 result = draw_poly(graphics, NULL, path->pathdata.Points,
1900 path->pathdata.Types, path->pathdata.Count, FALSE);
1901 EndPath(graphics->hdc);
1902 return result;
1905 typedef struct _GraphicsContainerItem {
1906 struct list entry;
1907 GraphicsContainer contid;
1909 SmoothingMode smoothing;
1910 CompositingQuality compqual;
1911 InterpolationMode interpolation;
1912 CompositingMode compmode;
1913 TextRenderingHint texthint;
1914 REAL scale;
1915 GpUnit unit;
1916 PixelOffsetMode pixeloffset;
1917 UINT textcontrast;
1918 GpMatrix worldtrans;
1919 GpRegion* clip;
1920 INT origin_x, origin_y;
1921 } GraphicsContainerItem;
1923 static GpStatus init_container(GraphicsContainerItem** container,
1924 GDIPCONST GpGraphics* graphics){
1925 GpStatus sts;
1927 *container = GdipAlloc(sizeof(GraphicsContainerItem));
1928 if(!(*container))
1929 return OutOfMemory;
1931 (*container)->contid = graphics->contid + 1;
1933 (*container)->smoothing = graphics->smoothing;
1934 (*container)->compqual = graphics->compqual;
1935 (*container)->interpolation = graphics->interpolation;
1936 (*container)->compmode = graphics->compmode;
1937 (*container)->texthint = graphics->texthint;
1938 (*container)->scale = graphics->scale;
1939 (*container)->unit = graphics->unit;
1940 (*container)->textcontrast = graphics->textcontrast;
1941 (*container)->pixeloffset = graphics->pixeloffset;
1942 (*container)->origin_x = graphics->origin_x;
1943 (*container)->origin_y = graphics->origin_y;
1944 (*container)->worldtrans = graphics->worldtrans;
1946 sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
1947 if(sts != Ok){
1948 GdipFree(*container);
1949 *container = NULL;
1950 return sts;
1953 return Ok;
1956 static void delete_container(GraphicsContainerItem* container)
1958 GdipDeleteRegion(container->clip);
1959 GdipFree(container);
1962 static GpStatus restore_container(GpGraphics* graphics,
1963 GDIPCONST GraphicsContainerItem* container){
1964 GpStatus sts;
1965 GpRegion *newClip;
1967 sts = GdipCloneRegion(container->clip, &newClip);
1968 if(sts != Ok) return sts;
1970 graphics->worldtrans = container->worldtrans;
1972 GdipDeleteRegion(graphics->clip);
1973 graphics->clip = newClip;
1975 graphics->contid = container->contid - 1;
1977 graphics->smoothing = container->smoothing;
1978 graphics->compqual = container->compqual;
1979 graphics->interpolation = container->interpolation;
1980 graphics->compmode = container->compmode;
1981 graphics->texthint = container->texthint;
1982 graphics->scale = container->scale;
1983 graphics->unit = container->unit;
1984 graphics->textcontrast = container->textcontrast;
1985 graphics->pixeloffset = container->pixeloffset;
1986 graphics->origin_x = container->origin_x;
1987 graphics->origin_y = container->origin_y;
1989 return Ok;
1992 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
1994 RECT wnd_rect;
1995 GpStatus stat=Ok;
1996 GpUnit unit;
1998 if(graphics->hwnd) {
1999 if(!GetClientRect(graphics->hwnd, &wnd_rect))
2000 return GenericError;
2002 rect->X = wnd_rect.left;
2003 rect->Y = wnd_rect.top;
2004 rect->Width = wnd_rect.right - wnd_rect.left;
2005 rect->Height = wnd_rect.bottom - wnd_rect.top;
2006 }else if (graphics->image){
2007 stat = GdipGetImageBounds(graphics->image, rect, &unit);
2008 if (stat == Ok && unit != UnitPixel)
2009 FIXME("need to convert from unit %i\n", unit);
2010 }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
2011 HBITMAP hbmp;
2012 BITMAP bmp;
2014 rect->X = 0;
2015 rect->Y = 0;
2017 hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
2018 if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
2020 rect->Width = bmp.bmWidth;
2021 rect->Height = bmp.bmHeight;
2023 else
2025 /* FIXME: ??? */
2026 rect->Width = 1;
2027 rect->Height = 1;
2029 }else{
2030 rect->X = 0;
2031 rect->Y = 0;
2032 rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
2033 rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
2036 if (graphics->hdc)
2038 POINT points[2];
2040 points[0].x = rect->X;
2041 points[0].y = rect->Y;
2042 points[1].x = rect->X + rect->Width;
2043 points[1].y = rect->Y + rect->Height;
2045 DPtoLP(graphics->hdc, points, sizeof(points)/sizeof(points[0]));
2047 rect->X = min(points[0].x, points[1].x);
2048 rect->Y = min(points[0].y, points[1].y);
2049 rect->Width = abs(points[1].x - points[0].x);
2050 rect->Height = abs(points[1].y - points[0].y);
2053 return stat;
2056 /* on success, rgn will contain the region of the graphics object which
2057 * is visible after clipping has been applied */
2058 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
2060 GpStatus stat;
2061 GpRectF rectf;
2062 GpRegion* tmp;
2064 if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
2065 return stat;
2067 if((stat = GdipCreateRegion(&tmp)) != Ok)
2068 return stat;
2070 if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
2071 goto end;
2073 if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
2074 goto end;
2076 stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
2078 end:
2079 GdipDeleteRegion(tmp);
2080 return stat;
2083 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
2085 REAL height;
2087 if (font->unit == UnitPixel)
2089 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
2091 else
2093 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
2094 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
2095 else
2096 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
2099 lf->lfHeight = -(height + 0.5);
2100 lf->lfWidth = 0;
2101 lf->lfEscapement = 0;
2102 lf->lfOrientation = 0;
2103 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
2104 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
2105 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
2106 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
2107 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
2108 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
2109 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
2110 lf->lfQuality = DEFAULT_QUALITY;
2111 lf->lfPitchAndFamily = 0;
2112 strcpyW(lf->lfFaceName, font->family->FamilyName);
2115 static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
2116 GDIPCONST GpStringFormat *format, HFONT *hfont,
2117 GDIPCONST GpMatrix *matrix)
2119 HDC hdc = CreateCompatibleDC(0);
2120 GpPointF pt[3];
2121 REAL angle, rel_width, rel_height, font_height;
2122 LOGFONTW lfw;
2123 HFONT unscaled_font;
2124 TEXTMETRICW textmet;
2126 if (font->unit == UnitPixel)
2127 font_height = font->emSize;
2128 else
2130 REAL unit_scale, res;
2132 res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
2133 unit_scale = units_scale(font->unit, graphics->unit, res);
2135 font_height = font->emSize * unit_scale;
2138 pt[0].X = 0.0;
2139 pt[0].Y = 0.0;
2140 pt[1].X = 1.0;
2141 pt[1].Y = 0.0;
2142 pt[2].X = 0.0;
2143 pt[2].Y = 1.0;
2144 if (matrix)
2146 GpMatrix xform = *matrix;
2147 GdipTransformMatrixPoints(&xform, pt, 3);
2149 if (graphics)
2150 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
2151 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2152 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
2153 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
2154 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
2155 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
2157 get_log_fontW(font, graphics, &lfw);
2158 lfw.lfHeight = -gdip_round(font_height * rel_height);
2159 unscaled_font = CreateFontIndirectW(&lfw);
2161 SelectObject(hdc, unscaled_font);
2162 GetTextMetricsW(hdc, &textmet);
2164 lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
2165 lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
2167 *hfont = CreateFontIndirectW(&lfw);
2169 DeleteDC(hdc);
2170 DeleteObject(unscaled_font);
2173 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
2175 TRACE("(%p, %p)\n", hdc, graphics);
2177 return GdipCreateFromHDC2(hdc, NULL, graphics);
2180 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
2182 GpStatus retval;
2183 HBITMAP hbitmap;
2184 DIBSECTION dib;
2186 TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
2188 if(hDevice != NULL)
2189 FIXME("Don't know how to handle parameter hDevice\n");
2191 if(hdc == NULL)
2192 return OutOfMemory;
2194 if(graphics == NULL)
2195 return InvalidParameter;
2197 *graphics = GdipAlloc(sizeof(GpGraphics));
2198 if(!*graphics) return OutOfMemory;
2200 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2202 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2203 GdipFree(*graphics);
2204 return retval;
2207 hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
2208 if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
2209 dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
2211 (*graphics)->alpha_hdc = 1;
2214 (*graphics)->hdc = hdc;
2215 (*graphics)->hwnd = WindowFromDC(hdc);
2216 (*graphics)->owndc = FALSE;
2217 (*graphics)->smoothing = SmoothingModeDefault;
2218 (*graphics)->compqual = CompositingQualityDefault;
2219 (*graphics)->interpolation = InterpolationModeBilinear;
2220 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2221 (*graphics)->compmode = CompositingModeSourceOver;
2222 (*graphics)->unit = UnitDisplay;
2223 (*graphics)->scale = 1.0;
2224 (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
2225 (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
2226 (*graphics)->busy = FALSE;
2227 (*graphics)->textcontrast = 4;
2228 list_init(&(*graphics)->containers);
2229 (*graphics)->contid = 0;
2231 TRACE("<-- %p\n", *graphics);
2233 return Ok;
2236 GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
2238 GpStatus retval;
2240 *graphics = GdipAlloc(sizeof(GpGraphics));
2241 if(!*graphics) return OutOfMemory;
2243 GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2245 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
2246 GdipFree(*graphics);
2247 return retval;
2250 (*graphics)->hdc = NULL;
2251 (*graphics)->hwnd = NULL;
2252 (*graphics)->owndc = FALSE;
2253 (*graphics)->image = image;
2254 /* We have to store the image type here because the image may be freed
2255 * before GdipDeleteGraphics is called, and metafiles need special treatment. */
2256 (*graphics)->image_type = image->type;
2257 (*graphics)->smoothing = SmoothingModeDefault;
2258 (*graphics)->compqual = CompositingQualityDefault;
2259 (*graphics)->interpolation = InterpolationModeBilinear;
2260 (*graphics)->pixeloffset = PixelOffsetModeDefault;
2261 (*graphics)->compmode = CompositingModeSourceOver;
2262 (*graphics)->unit = UnitDisplay;
2263 (*graphics)->scale = 1.0;
2264 (*graphics)->xres = image->xres;
2265 (*graphics)->yres = image->yres;
2266 (*graphics)->busy = FALSE;
2267 (*graphics)->textcontrast = 4;
2268 list_init(&(*graphics)->containers);
2269 (*graphics)->contid = 0;
2271 TRACE("<-- %p\n", *graphics);
2273 return Ok;
2276 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
2278 GpStatus ret;
2279 HDC hdc;
2281 TRACE("(%p, %p)\n", hwnd, graphics);
2283 hdc = GetDC(hwnd);
2285 if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
2287 ReleaseDC(hwnd, hdc);
2288 return ret;
2291 (*graphics)->hwnd = hwnd;
2292 (*graphics)->owndc = TRUE;
2294 return Ok;
2297 /* FIXME: no icm handling */
2298 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
2300 TRACE("(%p, %p)\n", hwnd, graphics);
2302 return GdipCreateFromHWND(hwnd, graphics);
2305 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
2306 UINT access, IStream **stream)
2308 DWORD dwMode;
2309 HRESULT ret;
2311 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
2313 if(!stream || !filename)
2314 return InvalidParameter;
2316 if(access & GENERIC_WRITE)
2317 dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
2318 else if(access & GENERIC_READ)
2319 dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
2320 else
2321 return InvalidParameter;
2323 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
2325 return hresult_to_status(ret);
2328 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
2330 GraphicsContainerItem *cont, *next;
2331 GpStatus stat;
2332 TRACE("(%p)\n", graphics);
2334 if(!graphics) return InvalidParameter;
2335 if(graphics->busy) return ObjectBusy;
2337 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2339 stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
2340 if (stat != Ok)
2341 return stat;
2344 if(graphics->owndc)
2345 ReleaseDC(graphics->hwnd, graphics->hdc);
2347 LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
2348 list_remove(&cont->entry);
2349 delete_container(cont);
2352 GdipDeleteRegion(graphics->clip);
2354 /* Native returns ObjectBusy on the second free, instead of crashing as we'd
2355 * do otherwise, but we can't have that in the test suite because it means
2356 * accessing freed memory. */
2357 graphics->busy = TRUE;
2359 GdipFree(graphics);
2361 return Ok;
2364 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
2365 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2367 GpStatus status;
2368 GpPath *path;
2370 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2371 width, height, startAngle, sweepAngle);
2373 if(!graphics || !pen || width <= 0 || height <= 0)
2374 return InvalidParameter;
2376 if(graphics->busy)
2377 return ObjectBusy;
2379 status = GdipCreatePath(FillModeAlternate, &path);
2380 if (status != Ok) return status;
2382 status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
2383 if (status == Ok)
2384 status = GdipDrawPath(graphics, pen, path);
2386 GdipDeletePath(path);
2387 return status;
2390 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
2391 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2393 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2394 width, height, startAngle, sweepAngle);
2396 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2399 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
2400 REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
2402 GpPointF pt[4];
2404 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
2405 x2, y2, x3, y3, x4, y4);
2407 if(!graphics || !pen)
2408 return InvalidParameter;
2410 if(graphics->busy)
2411 return ObjectBusy;
2413 pt[0].X = x1;
2414 pt[0].Y = y1;
2415 pt[1].X = x2;
2416 pt[1].Y = y2;
2417 pt[2].X = x3;
2418 pt[2].Y = y3;
2419 pt[3].X = x4;
2420 pt[3].Y = y4;
2421 return GdipDrawBeziers(graphics, pen, pt, 4);
2424 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
2425 INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
2427 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
2428 x2, y2, x3, y3, x4, y4);
2430 return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
2433 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
2434 GDIPCONST GpPointF *points, INT count)
2436 GpStatus status;
2437 GpPath *path;
2439 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2441 if(!graphics || !pen || !points || (count <= 0))
2442 return InvalidParameter;
2444 if(graphics->busy)
2445 return ObjectBusy;
2447 status = GdipCreatePath(FillModeAlternate, &path);
2448 if (status != Ok) return status;
2450 status = GdipAddPathBeziers(path, points, count);
2451 if (status == Ok)
2452 status = GdipDrawPath(graphics, pen, path);
2454 GdipDeletePath(path);
2455 return status;
2458 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
2459 GDIPCONST GpPoint *points, INT count)
2461 GpPointF *pts;
2462 GpStatus ret;
2463 INT i;
2465 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2467 if(!graphics || !pen || !points || (count <= 0))
2468 return InvalidParameter;
2470 if(graphics->busy)
2471 return ObjectBusy;
2473 pts = GdipAlloc(sizeof(GpPointF) * count);
2474 if(!pts)
2475 return OutOfMemory;
2477 for(i = 0; i < count; i++){
2478 pts[i].X = (REAL)points[i].X;
2479 pts[i].Y = (REAL)points[i].Y;
2482 ret = GdipDrawBeziers(graphics,pen,pts,count);
2484 GdipFree(pts);
2486 return ret;
2489 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
2490 GDIPCONST GpPointF *points, INT count)
2492 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2494 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
2497 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
2498 GDIPCONST GpPoint *points, INT count)
2500 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2502 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
2505 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
2506 GDIPCONST GpPointF *points, INT count, REAL tension)
2508 GpPath *path;
2509 GpStatus status;
2511 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2513 if(!graphics || !pen || !points || count <= 0)
2514 return InvalidParameter;
2516 if(graphics->busy)
2517 return ObjectBusy;
2519 status = GdipCreatePath(FillModeAlternate, &path);
2520 if (status != Ok) return status;
2522 status = GdipAddPathClosedCurve2(path, points, count, tension);
2523 if (status == Ok)
2524 status = GdipDrawPath(graphics, pen, path);
2526 GdipDeletePath(path);
2528 return status;
2531 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
2532 GDIPCONST GpPoint *points, INT count, REAL tension)
2534 GpPointF *ptf;
2535 GpStatus stat;
2536 INT i;
2538 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2540 if(!points || count <= 0)
2541 return InvalidParameter;
2543 ptf = GdipAlloc(sizeof(GpPointF)*count);
2544 if(!ptf)
2545 return OutOfMemory;
2547 for(i = 0; i < count; i++){
2548 ptf[i].X = (REAL)points[i].X;
2549 ptf[i].Y = (REAL)points[i].Y;
2552 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
2554 GdipFree(ptf);
2556 return stat;
2559 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
2560 GDIPCONST GpPointF *points, INT count)
2562 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2564 return GdipDrawCurve2(graphics,pen,points,count,1.0);
2567 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
2568 GDIPCONST GpPoint *points, INT count)
2570 GpPointF *pointsF;
2571 GpStatus ret;
2572 INT i;
2574 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2576 if(!points)
2577 return InvalidParameter;
2579 pointsF = GdipAlloc(sizeof(GpPointF)*count);
2580 if(!pointsF)
2581 return OutOfMemory;
2583 for(i = 0; i < count; i++){
2584 pointsF[i].X = (REAL)points[i].X;
2585 pointsF[i].Y = (REAL)points[i].Y;
2588 ret = GdipDrawCurve(graphics,pen,pointsF,count);
2589 GdipFree(pointsF);
2591 return ret;
2594 /* Approximates cardinal spline with Bezier curves. */
2595 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
2596 GDIPCONST GpPointF *points, INT count, REAL tension)
2598 GpPath *path;
2599 GpStatus status;
2601 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2603 if(!graphics || !pen)
2604 return InvalidParameter;
2606 if(graphics->busy)
2607 return ObjectBusy;
2609 if(count < 2)
2610 return InvalidParameter;
2612 status = GdipCreatePath(FillModeAlternate, &path);
2613 if (status != Ok) return status;
2615 status = GdipAddPathCurve2(path, points, count, tension);
2616 if (status == Ok)
2617 status = GdipDrawPath(graphics, pen, path);
2619 GdipDeletePath(path);
2620 return status;
2623 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
2624 GDIPCONST GpPoint *points, INT count, REAL tension)
2626 GpPointF *pointsF;
2627 GpStatus ret;
2628 INT i;
2630 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2632 if(!points)
2633 return InvalidParameter;
2635 pointsF = GdipAlloc(sizeof(GpPointF)*count);
2636 if(!pointsF)
2637 return OutOfMemory;
2639 for(i = 0; i < count; i++){
2640 pointsF[i].X = (REAL)points[i].X;
2641 pointsF[i].Y = (REAL)points[i].Y;
2644 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
2645 GdipFree(pointsF);
2647 return ret;
2650 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
2651 GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
2652 REAL tension)
2654 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2656 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2657 return InvalidParameter;
2660 return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
2663 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
2664 GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
2665 REAL tension)
2667 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2669 if(count < 0){
2670 return OutOfMemory;
2673 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2674 return InvalidParameter;
2677 return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
2680 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
2681 REAL y, REAL width, REAL height)
2683 GpPath *path;
2684 GpStatus status;
2686 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2688 if(!graphics || !pen)
2689 return InvalidParameter;
2691 if(graphics->busy)
2692 return ObjectBusy;
2694 status = GdipCreatePath(FillModeAlternate, &path);
2695 if (status != Ok) return status;
2697 status = GdipAddPathEllipse(path, x, y, width, height);
2698 if (status == Ok)
2699 status = GdipDrawPath(graphics, pen, path);
2701 GdipDeletePath(path);
2702 return status;
2705 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
2706 INT y, INT width, INT height)
2708 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2710 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2714 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
2716 UINT width, height;
2718 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
2720 if(!graphics || !image)
2721 return InvalidParameter;
2723 GdipGetImageWidth(image, &width);
2724 GdipGetImageHeight(image, &height);
2726 return GdipDrawImagePointRect(graphics, image, x, y,
2727 0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
2730 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
2731 INT y)
2733 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
2735 return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
2738 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
2739 REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
2740 GpUnit srcUnit)
2742 GpPointF points[3];
2743 REAL scale_x, scale_y, width, height;
2745 TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2747 if (!graphics || !image) return InvalidParameter;
2749 scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
2750 scale_x *= graphics->xres / image->xres;
2751 scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
2752 scale_y *= graphics->yres / image->yres;
2753 width = srcwidth * scale_x;
2754 height = srcheight * scale_y;
2756 points[0].X = points[2].X = x;
2757 points[0].Y = points[1].Y = y;
2758 points[1].X = x + width;
2759 points[2].Y = y + height;
2761 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2762 srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
2765 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
2766 INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
2767 GpUnit srcUnit)
2769 return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2772 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
2773 GDIPCONST GpPointF *dstpoints, INT count)
2775 UINT width, height;
2777 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2779 if(!image)
2780 return InvalidParameter;
2782 GdipGetImageWidth(image, &width);
2783 GdipGetImageHeight(image, &height);
2785 return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
2786 width, height, UnitPixel, NULL, NULL, NULL);
2789 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
2790 GDIPCONST GpPoint *dstpoints, INT count)
2792 GpPointF ptf[3];
2794 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2796 if (count != 3 || !dstpoints)
2797 return InvalidParameter;
2799 ptf[0].X = (REAL)dstpoints[0].X;
2800 ptf[0].Y = (REAL)dstpoints[0].Y;
2801 ptf[1].X = (REAL)dstpoints[1].X;
2802 ptf[1].Y = (REAL)dstpoints[1].Y;
2803 ptf[2].X = (REAL)dstpoints[2].X;
2804 ptf[2].Y = (REAL)dstpoints[2].Y;
2806 return GdipDrawImagePoints(graphics, image, ptf, count);
2809 static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
2810 unsigned int dataSize, const unsigned char *pStr, void *userdata)
2812 GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
2813 return TRUE;
2816 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
2817 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2818 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2819 DrawImageAbort callback, VOID * callbackData)
2821 GpPointF ptf[4];
2822 POINT pti[4];
2823 GpStatus stat;
2825 TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
2826 count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2827 callbackData);
2829 if (count > 3)
2830 return NotImplemented;
2832 if(!graphics || !image || !points || count != 3)
2833 return InvalidParameter;
2835 TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
2836 debugstr_pointf(&points[2]));
2838 memcpy(ptf, points, 3 * sizeof(GpPointF));
2840 /* Ensure source width/height is positive */
2841 if (srcwidth < 0)
2843 GpPointF tmp = ptf[1];
2844 srcx = srcx + srcwidth;
2845 srcwidth = -srcwidth;
2846 ptf[2].X = ptf[2].X + ptf[1].X - ptf[0].X;
2847 ptf[2].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2848 ptf[1] = ptf[0];
2849 ptf[0] = tmp;
2852 if (srcheight < 0)
2854 GpPointF tmp = ptf[2];
2855 srcy = srcy + srcheight;
2856 srcheight = -srcheight;
2857 ptf[1].X = ptf[1].X + ptf[2].X - ptf[0].X;
2858 ptf[1].Y = ptf[1].Y + ptf[2].Y - ptf[0].Y;
2859 ptf[2] = ptf[0];
2860 ptf[0] = tmp;
2863 ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
2864 ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2865 if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
2866 return Ok;
2867 transform_and_round_points(graphics, pti, ptf, 4);
2869 TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
2870 wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
2872 srcx = units_to_pixels(srcx, srcUnit, image->xres);
2873 srcy = units_to_pixels(srcy, srcUnit, image->yres);
2874 srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
2875 srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
2876 TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
2878 if (image->picture)
2880 if (!graphics->hdc)
2882 FIXME("graphics object has no HDC\n");
2885 if(IPicture_Render(image->picture, graphics->hdc,
2886 pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
2887 srcx, srcy, srcwidth, srcheight, NULL) != S_OK)
2889 if(callback)
2890 callback(callbackData);
2891 return GenericError;
2894 else if (image->type == ImageTypeBitmap)
2896 GpBitmap* bitmap = (GpBitmap*)image;
2897 BOOL do_resampling = FALSE;
2898 BOOL use_software = FALSE;
2900 TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
2901 graphics->xres, graphics->yres,
2902 graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
2903 graphics->scale, image->xres, image->yres, bitmap->format,
2904 imageAttributes ? imageAttributes->outside_color : 0);
2906 if (ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
2907 ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
2908 srcx < 0 || srcy < 0 ||
2909 srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
2910 do_resampling = TRUE;
2912 if (imageAttributes || graphics->alpha_hdc || do_resampling ||
2913 (graphics->image && graphics->image->type == ImageTypeBitmap))
2914 use_software = TRUE;
2916 if (use_software)
2918 RECT dst_area;
2919 GpRectF graphics_bounds;
2920 GpRect src_area;
2921 int i, x, y, src_stride, dst_stride;
2922 GpMatrix dst_to_src;
2923 REAL m11, m12, m21, m22, mdx, mdy;
2924 LPBYTE src_data, dst_data, dst_dyn_data=NULL;
2925 BitmapData lockeddata;
2926 InterpolationMode interpolation = graphics->interpolation;
2927 PixelOffsetMode offset_mode = graphics->pixeloffset;
2928 GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
2929 REAL x_dx, x_dy, y_dx, y_dy;
2930 static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
2932 if (!imageAttributes)
2933 imageAttributes = &defaultImageAttributes;
2935 dst_area.left = dst_area.right = pti[0].x;
2936 dst_area.top = dst_area.bottom = pti[0].y;
2937 for (i=1; i<4; i++)
2939 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2940 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2941 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2942 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2945 stat = get_graphics_bounds(graphics, &graphics_bounds);
2946 if (stat != Ok) return stat;
2948 if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
2949 if (graphics_bounds.Y > dst_area.top) dst_area.top = floorf(graphics_bounds.Y);
2950 if (graphics_bounds.X + graphics_bounds.Width < dst_area.right) dst_area.right = ceilf(graphics_bounds.X + graphics_bounds.Width);
2951 if (graphics_bounds.Y + graphics_bounds.Height < dst_area.bottom) dst_area.bottom = ceilf(graphics_bounds.Y + graphics_bounds.Height);
2953 TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
2955 if (IsRectEmpty(&dst_area)) return Ok;
2957 m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2958 m21 = (ptf[2].X - ptf[0].X) / srcheight;
2959 mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2960 m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2961 m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2962 mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
2964 GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
2966 stat = GdipInvertMatrix(&dst_to_src);
2967 if (stat != Ok) return stat;
2969 if (do_resampling)
2971 get_bitmap_sample_size(interpolation, imageAttributes->wrap,
2972 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
2974 else
2976 /* Make sure src_area is equal in size to dst_area. */
2977 src_area.X = srcx + dst_area.left - pti[0].x;
2978 src_area.Y = srcy + dst_area.top - pti[0].y;
2979 src_area.Width = dst_area.right - dst_area.left;
2980 src_area.Height = dst_area.bottom - dst_area.top;
2983 TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
2985 src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
2986 if (!src_data)
2987 return OutOfMemory;
2988 src_stride = sizeof(ARGB) * src_area.Width;
2990 /* Read the bits we need from the source bitmap into an ARGB buffer. */
2991 lockeddata.Width = src_area.Width;
2992 lockeddata.Height = src_area.Height;
2993 lockeddata.Stride = src_stride;
2994 lockeddata.PixelFormat = PixelFormat32bppARGB;
2995 lockeddata.Scan0 = src_data;
2997 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
2998 PixelFormat32bppARGB, &lockeddata);
3000 if (stat == Ok)
3001 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
3003 if (stat != Ok)
3005 GdipFree(src_data);
3006 return stat;
3009 apply_image_attributes(imageAttributes, src_data,
3010 src_area.Width, src_area.Height,
3011 src_stride, ColorAdjustTypeBitmap);
3013 if (do_resampling)
3015 /* Transform the bits as needed to the destination. */
3016 dst_data = dst_dyn_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
3017 if (!dst_data)
3019 GdipFree(src_data);
3020 return OutOfMemory;
3023 dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
3025 GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
3027 x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
3028 x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
3029 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
3030 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
3032 for (x=dst_area.left; x<dst_area.right; x++)
3034 for (y=dst_area.top; y<dst_area.bottom; y++)
3036 GpPointF src_pointf;
3037 ARGB *dst_color;
3039 src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
3040 src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
3042 dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
3044 if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
3045 *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3046 imageAttributes, interpolation, offset_mode);
3047 else
3048 *dst_color = 0;
3052 else
3054 dst_data = src_data;
3055 dst_stride = src_stride;
3058 stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
3059 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
3061 GdipFree(src_data);
3063 GdipFree(dst_dyn_data);
3065 return stat;
3067 else
3069 HDC hdc;
3070 BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
3071 HBITMAP hbitmap, old_hbm=NULL;
3073 if (!(bitmap->format == PixelFormat16bppRGB555 ||
3074 bitmap->format == PixelFormat24bppRGB ||
3075 bitmap->format == PixelFormat32bppRGB ||
3076 bitmap->format == PixelFormat32bppPARGB))
3078 BITMAPINFOHEADER bih;
3079 BYTE *temp_bits;
3080 PixelFormat dst_format;
3082 /* we can't draw a bitmap of this format directly */
3083 hdc = CreateCompatibleDC(0);
3084 temp_hdc = TRUE;
3085 temp_bitmap = TRUE;
3087 bih.biSize = sizeof(BITMAPINFOHEADER);
3088 bih.biWidth = bitmap->width;
3089 bih.biHeight = -bitmap->height;
3090 bih.biPlanes = 1;
3091 bih.biBitCount = 32;
3092 bih.biCompression = BI_RGB;
3093 bih.biSizeImage = 0;
3094 bih.biXPelsPerMeter = 0;
3095 bih.biYPelsPerMeter = 0;
3096 bih.biClrUsed = 0;
3097 bih.biClrImportant = 0;
3099 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
3100 (void**)&temp_bits, NULL, 0);
3102 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3103 dst_format = PixelFormat32bppPARGB;
3104 else
3105 dst_format = PixelFormat32bppRGB;
3107 convert_pixels(bitmap->width, bitmap->height,
3108 bitmap->width*4, temp_bits, dst_format,
3109 bitmap->stride, bitmap->bits, bitmap->format,
3110 bitmap->image.palette);
3112 else
3114 if (bitmap->hbitmap)
3115 hbitmap = bitmap->hbitmap;
3116 else
3118 GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
3119 temp_bitmap = TRUE;
3122 hdc = bitmap->hdc;
3123 temp_hdc = (hdc == 0);
3126 if (temp_hdc)
3128 if (!hdc) hdc = CreateCompatibleDC(0);
3129 old_hbm = SelectObject(hdc, hbitmap);
3132 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3134 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
3135 hdc, srcx, srcy, srcwidth, srcheight);
3137 else
3139 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
3140 hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
3143 if (temp_hdc)
3145 SelectObject(hdc, old_hbm);
3146 DeleteDC(hdc);
3149 if (temp_bitmap)
3150 DeleteObject(hbitmap);
3153 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
3155 GpRectF rc;
3157 rc.X = srcx;
3158 rc.Y = srcy;
3159 rc.Width = srcwidth;
3160 rc.Height = srcheight;
3162 return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
3163 points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
3165 else
3167 WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
3168 return InvalidParameter;
3171 return Ok;
3174 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
3175 GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
3176 INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3177 DrawImageAbort callback, VOID * callbackData)
3179 GpPointF pointsF[3];
3180 INT i;
3182 TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
3183 srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
3184 callbackData);
3186 if(!points || count!=3)
3187 return InvalidParameter;
3189 for(i = 0; i < count; i++){
3190 pointsF[i].X = (REAL)points[i].X;
3191 pointsF[i].Y = (REAL)points[i].Y;
3194 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
3195 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
3196 callback, callbackData);
3199 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
3200 REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
3201 REAL srcwidth, REAL srcheight, GpUnit srcUnit,
3202 GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
3203 VOID * callbackData)
3205 GpPointF points[3];
3207 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
3208 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3209 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3211 points[0].X = dstx;
3212 points[0].Y = dsty;
3213 points[1].X = dstx + dstwidth;
3214 points[1].Y = dsty;
3215 points[2].X = dstx;
3216 points[2].Y = dsty + dstheight;
3218 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3219 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3222 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
3223 INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
3224 INT srcwidth, INT srcheight, GpUnit srcUnit,
3225 GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
3226 VOID * callbackData)
3228 GpPointF points[3];
3230 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
3231 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3232 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3234 points[0].X = dstx;
3235 points[0].Y = dsty;
3236 points[1].X = dstx + dstwidth;
3237 points[1].Y = dsty;
3238 points[2].X = dstx;
3239 points[2].Y = dsty + dstheight;
3241 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3242 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3245 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
3246 REAL x, REAL y, REAL width, REAL height)
3248 RectF bounds;
3249 GpUnit unit;
3250 GpStatus ret;
3252 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
3254 if(!graphics || !image)
3255 return InvalidParameter;
3257 ret = GdipGetImageBounds(image, &bounds, &unit);
3258 if(ret != Ok)
3259 return ret;
3261 return GdipDrawImageRectRect(graphics, image, x, y, width, height,
3262 bounds.X, bounds.Y, bounds.Width, bounds.Height,
3263 unit, NULL, NULL, NULL);
3266 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
3267 INT x, INT y, INT width, INT height)
3269 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
3271 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
3274 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
3275 REAL y1, REAL x2, REAL y2)
3277 GpPointF pt[2];
3279 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
3281 pt[0].X = x1;
3282 pt[0].Y = y1;
3283 pt[1].X = x2;
3284 pt[1].Y = y2;
3285 return GdipDrawLines(graphics, pen, pt, 2);
3288 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
3289 INT y1, INT x2, INT y2)
3291 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
3293 return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
3296 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
3297 GpPointF *points, INT count)
3299 GpStatus status;
3300 GpPath *path;
3302 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3304 if(!pen || !graphics || (count < 2))
3305 return InvalidParameter;
3307 if(graphics->busy)
3308 return ObjectBusy;
3310 status = GdipCreatePath(FillModeAlternate, &path);
3311 if (status != Ok) return status;
3313 status = GdipAddPathLine2(path, points, count);
3314 if (status == Ok)
3315 status = GdipDrawPath(graphics, pen, path);
3317 GdipDeletePath(path);
3318 return status;
3321 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
3322 GpPoint *points, INT count)
3324 GpStatus retval;
3325 GpPointF *ptf;
3326 int i;
3328 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3330 ptf = GdipAlloc(count * sizeof(GpPointF));
3331 if(!ptf) return OutOfMemory;
3333 for(i = 0; i < count; i ++){
3334 ptf[i].X = (REAL) points[i].X;
3335 ptf[i].Y = (REAL) points[i].Y;
3338 retval = GdipDrawLines(graphics, pen, ptf, count);
3340 GdipFree(ptf);
3341 return retval;
3344 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3346 INT save_state;
3347 GpStatus retval;
3348 HRGN hrgn=NULL;
3350 TRACE("(%p, %p, %p)\n", graphics, pen, path);
3352 if(!pen || !graphics)
3353 return InvalidParameter;
3355 if(graphics->busy)
3356 return ObjectBusy;
3358 if (!graphics->hdc)
3360 FIXME("graphics object has no HDC\n");
3361 return Ok;
3364 save_state = prepare_dc(graphics, pen);
3366 retval = get_clip_hrgn(graphics, &hrgn);
3368 if (retval != Ok)
3369 goto end;
3371 if (hrgn)
3372 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3374 retval = draw_poly(graphics, pen, path->pathdata.Points,
3375 path->pathdata.Types, path->pathdata.Count, TRUE);
3377 end:
3378 restore_dc(graphics, save_state);
3379 DeleteObject(hrgn);
3381 return retval;
3384 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
3385 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3387 GpStatus status;
3388 GpPath *path;
3390 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
3391 width, height, startAngle, sweepAngle);
3393 if(!graphics || !pen)
3394 return InvalidParameter;
3396 if(graphics->busy)
3397 return ObjectBusy;
3399 status = GdipCreatePath(FillModeAlternate, &path);
3400 if (status != Ok) return status;
3402 status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
3403 if (status == Ok)
3404 status = GdipDrawPath(graphics, pen, path);
3406 GdipDeletePath(path);
3407 return status;
3410 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
3411 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3413 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
3414 width, height, startAngle, sweepAngle);
3416 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3419 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
3420 REAL y, REAL width, REAL height)
3422 GpStatus status;
3423 GpPath *path;
3425 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
3427 if(!pen || !graphics)
3428 return InvalidParameter;
3430 if(graphics->busy)
3431 return ObjectBusy;
3433 status = GdipCreatePath(FillModeAlternate, &path);
3434 if (status != Ok) return status;
3436 status = GdipAddPathRectangle(path, x, y, width, height);
3437 if (status == Ok)
3438 status = GdipDrawPath(graphics, pen, path);
3440 GdipDeletePath(path);
3441 return status;
3444 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
3445 INT y, INT width, INT height)
3447 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
3449 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3452 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
3453 GDIPCONST GpRectF* rects, INT count)
3455 GpStatus status;
3456 GpPath *path;
3458 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
3460 if(!graphics || !pen || !rects || count < 1)
3461 return InvalidParameter;
3463 if(graphics->busy)
3464 return ObjectBusy;
3466 status = GdipCreatePath(FillModeAlternate, &path);
3467 if (status != Ok) return status;
3469 status = GdipAddPathRectangles(path, rects, count);
3470 if (status == Ok)
3471 status = GdipDrawPath(graphics, pen, path);
3473 GdipDeletePath(path);
3474 return status;
3477 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
3478 GDIPCONST GpRect* rects, INT count)
3480 GpRectF *rectsF;
3481 GpStatus ret;
3482 INT i;
3484 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
3486 if(!rects || count<=0)
3487 return InvalidParameter;
3489 rectsF = GdipAlloc(sizeof(GpRectF) * count);
3490 if(!rectsF)
3491 return OutOfMemory;
3493 for(i = 0;i < count;i++){
3494 rectsF[i].X = (REAL)rects[i].X;
3495 rectsF[i].Y = (REAL)rects[i].Y;
3496 rectsF[i].Width = (REAL)rects[i].Width;
3497 rectsF[i].Height = (REAL)rects[i].Height;
3500 ret = GdipDrawRectangles(graphics, pen, rectsF, count);
3501 GdipFree(rectsF);
3503 return ret;
3506 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
3507 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
3509 GpPath *path;
3510 GpStatus status;
3512 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
3513 count, tension, fill);
3515 if(!graphics || !brush || !points)
3516 return InvalidParameter;
3518 if(graphics->busy)
3519 return ObjectBusy;
3521 if(count == 1) /* Do nothing */
3522 return Ok;
3524 status = GdipCreatePath(fill, &path);
3525 if (status != Ok) return status;
3527 status = GdipAddPathClosedCurve2(path, points, count, tension);
3528 if (status == Ok)
3529 status = GdipFillPath(graphics, brush, path);
3531 GdipDeletePath(path);
3532 return status;
3535 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
3536 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
3538 GpPointF *ptf;
3539 GpStatus stat;
3540 INT i;
3542 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
3543 count, tension, fill);
3545 if(!points || count == 0)
3546 return InvalidParameter;
3548 if(count == 1) /* Do nothing */
3549 return Ok;
3551 ptf = GdipAlloc(sizeof(GpPointF)*count);
3552 if(!ptf)
3553 return OutOfMemory;
3555 for(i = 0;i < count;i++){
3556 ptf[i].X = (REAL)points[i].X;
3557 ptf[i].Y = (REAL)points[i].Y;
3560 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
3562 GdipFree(ptf);
3564 return stat;
3567 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
3568 GDIPCONST GpPointF *points, INT count)
3570 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3571 return GdipFillClosedCurve2(graphics, brush, points, count,
3572 0.5f, FillModeAlternate);
3575 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
3576 GDIPCONST GpPoint *points, INT count)
3578 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3579 return GdipFillClosedCurve2I(graphics, brush, points, count,
3580 0.5f, FillModeAlternate);
3583 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
3584 REAL y, REAL width, REAL height)
3586 GpStatus stat;
3587 GpPath *path;
3589 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3591 if(!graphics || !brush)
3592 return InvalidParameter;
3594 if(graphics->busy)
3595 return ObjectBusy;
3597 stat = GdipCreatePath(FillModeAlternate, &path);
3599 if (stat == Ok)
3601 stat = GdipAddPathEllipse(path, x, y, width, height);
3603 if (stat == Ok)
3604 stat = GdipFillPath(graphics, brush, path);
3606 GdipDeletePath(path);
3609 return stat;
3612 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
3613 INT y, INT width, INT height)
3615 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3617 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3620 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3622 INT save_state;
3623 GpStatus retval;
3624 HRGN hrgn=NULL;
3626 if(!graphics->hdc || !brush_can_fill_path(brush))
3627 return NotImplemented;
3629 save_state = SaveDC(graphics->hdc);
3630 EndPath(graphics->hdc);
3631 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
3632 : WINDING));
3634 retval = get_clip_hrgn(graphics, &hrgn);
3636 if (retval != Ok)
3637 goto end;
3639 if (hrgn)
3640 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3642 BeginPath(graphics->hdc);
3643 retval = draw_poly(graphics, NULL, path->pathdata.Points,
3644 path->pathdata.Types, path->pathdata.Count, FALSE);
3646 if(retval != Ok)
3647 goto end;
3649 EndPath(graphics->hdc);
3650 brush_fill_path(graphics, brush);
3652 retval = Ok;
3654 end:
3655 RestoreDC(graphics->hdc, save_state);
3656 DeleteObject(hrgn);
3658 return retval;
3661 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3663 GpStatus stat;
3664 GpRegion *rgn;
3666 if (!brush_can_fill_pixels(brush))
3667 return NotImplemented;
3669 /* FIXME: This could probably be done more efficiently without regions. */
3671 stat = GdipCreateRegionPath(path, &rgn);
3673 if (stat == Ok)
3675 stat = GdipFillRegion(graphics, brush, rgn);
3677 GdipDeleteRegion(rgn);
3680 return stat;
3683 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3685 GpStatus stat = NotImplemented;
3687 TRACE("(%p, %p, %p)\n", graphics, brush, path);
3689 if(!brush || !graphics || !path)
3690 return InvalidParameter;
3692 if(graphics->busy)
3693 return ObjectBusy;
3695 if (!graphics->image && !graphics->alpha_hdc)
3696 stat = GDI32_GdipFillPath(graphics, brush, path);
3698 if (stat == NotImplemented)
3699 stat = SOFTWARE_GdipFillPath(graphics, brush, path);
3701 if (stat == NotImplemented)
3703 FIXME("Not implemented for brushtype %i\n", brush->bt);
3704 stat = Ok;
3707 return stat;
3710 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
3711 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3713 GpStatus stat;
3714 GpPath *path;
3716 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
3717 graphics, brush, x, y, width, height, startAngle, sweepAngle);
3719 if(!graphics || !brush)
3720 return InvalidParameter;
3722 if(graphics->busy)
3723 return ObjectBusy;
3725 stat = GdipCreatePath(FillModeAlternate, &path);
3727 if (stat == Ok)
3729 stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
3731 if (stat == Ok)
3732 stat = GdipFillPath(graphics, brush, path);
3734 GdipDeletePath(path);
3737 return stat;
3740 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
3741 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3743 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
3744 graphics, brush, x, y, width, height, startAngle, sweepAngle);
3746 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3749 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
3750 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
3752 GpStatus stat;
3753 GpPath *path;
3755 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3757 if(!graphics || !brush || !points || !count)
3758 return InvalidParameter;
3760 if(graphics->busy)
3761 return ObjectBusy;
3763 stat = GdipCreatePath(fillMode, &path);
3765 if (stat == Ok)
3767 stat = GdipAddPathPolygon(path, points, count);
3769 if (stat == Ok)
3770 stat = GdipFillPath(graphics, brush, path);
3772 GdipDeletePath(path);
3775 return stat;
3778 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
3779 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
3781 GpStatus stat;
3782 GpPath *path;
3784 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3786 if(!graphics || !brush || !points || !count)
3787 return InvalidParameter;
3789 if(graphics->busy)
3790 return ObjectBusy;
3792 stat = GdipCreatePath(fillMode, &path);
3794 if (stat == Ok)
3796 stat = GdipAddPathPolygonI(path, points, count);
3798 if (stat == Ok)
3799 stat = GdipFillPath(graphics, brush, path);
3801 GdipDeletePath(path);
3804 return stat;
3807 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
3808 GDIPCONST GpPointF *points, INT count)
3810 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3812 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
3815 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
3816 GDIPCONST GpPoint *points, INT count)
3818 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3820 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
3823 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
3824 REAL x, REAL y, REAL width, REAL height)
3826 GpRectF rect;
3828 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3830 rect.X = x;
3831 rect.Y = y;
3832 rect.Width = width;
3833 rect.Height = height;
3835 return GdipFillRectangles(graphics, brush, &rect, 1);
3838 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
3839 INT x, INT y, INT width, INT height)
3841 GpRectF rect;
3843 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3845 rect.X = (REAL)x;
3846 rect.Y = (REAL)y;
3847 rect.Width = (REAL)width;
3848 rect.Height = (REAL)height;
3850 return GdipFillRectangles(graphics, brush, &rect, 1);
3853 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3854 INT count)
3856 GpStatus status;
3857 GpPath *path;
3859 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3861 if(!graphics || !brush || !rects || count <= 0)
3862 return InvalidParameter;
3864 if (graphics->image && graphics->image->type == ImageTypeMetafile)
3866 status = METAFILE_FillRectangles((GpMetafile*)graphics->image, brush, rects, count);
3867 /* FIXME: Add gdi32 drawing. */
3868 return status;
3871 status = GdipCreatePath(FillModeAlternate, &path);
3872 if (status != Ok) return status;
3874 status = GdipAddPathRectangles(path, rects, count);
3875 if (status == Ok)
3876 status = GdipFillPath(graphics, brush, path);
3878 GdipDeletePath(path);
3879 return status;
3882 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3883 INT count)
3885 GpRectF *rectsF;
3886 GpStatus ret;
3887 INT i;
3889 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3891 if(!rects || count <= 0)
3892 return InvalidParameter;
3894 rectsF = GdipAlloc(sizeof(GpRectF)*count);
3895 if(!rectsF)
3896 return OutOfMemory;
3898 for(i = 0; i < count; i++){
3899 rectsF[i].X = (REAL)rects[i].X;
3900 rectsF[i].Y = (REAL)rects[i].Y;
3901 rectsF[i].X = (REAL)rects[i].Width;
3902 rectsF[i].Height = (REAL)rects[i].Height;
3905 ret = GdipFillRectangles(graphics,brush,rectsF,count);
3906 GdipFree(rectsF);
3908 return ret;
3911 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3912 GpRegion* region)
3914 INT save_state;
3915 GpStatus status;
3916 HRGN hrgn;
3917 RECT rc;
3919 if(!graphics->hdc || !brush_can_fill_path(brush))
3920 return NotImplemented;
3922 status = GdipGetRegionHRgn(region, graphics, &hrgn);
3923 if(status != Ok)
3924 return status;
3926 save_state = SaveDC(graphics->hdc);
3927 EndPath(graphics->hdc);
3929 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3931 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3933 BeginPath(graphics->hdc);
3934 Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3935 EndPath(graphics->hdc);
3937 brush_fill_path(graphics, brush);
3940 RestoreDC(graphics->hdc, save_state);
3942 DeleteObject(hrgn);
3944 return Ok;
3947 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
3948 GpRegion* region)
3950 GpStatus stat;
3951 GpRegion *temp_region;
3952 GpMatrix world_to_device;
3953 GpRectF graphics_bounds;
3954 DWORD *pixel_data;
3955 HRGN hregion;
3956 RECT bound_rect;
3957 GpRect gp_bound_rect;
3959 if (!brush_can_fill_pixels(brush))
3960 return NotImplemented;
3962 stat = get_graphics_bounds(graphics, &graphics_bounds);
3964 if (stat == Ok)
3965 stat = GdipCloneRegion(region, &temp_region);
3967 if (stat == Ok)
3969 stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
3970 CoordinateSpaceWorld, &world_to_device);
3972 if (stat == Ok)
3973 stat = GdipTransformRegion(temp_region, &world_to_device);
3975 if (stat == Ok)
3976 stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
3978 if (stat == Ok)
3979 stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
3981 GdipDeleteRegion(temp_region);
3984 if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
3986 DeleteObject(hregion);
3987 return Ok;
3990 if (stat == Ok)
3992 gp_bound_rect.X = bound_rect.left;
3993 gp_bound_rect.Y = bound_rect.top;
3994 gp_bound_rect.Width = bound_rect.right - bound_rect.left;
3995 gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
3997 pixel_data = GdipAlloc(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
3998 if (!pixel_data)
3999 stat = OutOfMemory;
4001 if (stat == Ok)
4003 stat = brush_fill_pixels(graphics, brush, pixel_data,
4004 &gp_bound_rect, gp_bound_rect.Width);
4006 if (stat == Ok)
4007 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
4008 gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
4009 gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion);
4011 GdipFree(pixel_data);
4014 DeleteObject(hregion);
4017 return stat;
4020 /*****************************************************************************
4021 * GdipFillRegion [GDIPLUS.@]
4023 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
4024 GpRegion* region)
4026 GpStatus stat = NotImplemented;
4028 TRACE("(%p, %p, %p)\n", graphics, brush, region);
4030 if (!(graphics && brush && region))
4031 return InvalidParameter;
4033 if(graphics->busy)
4034 return ObjectBusy;
4036 if (!graphics->image && !graphics->alpha_hdc)
4037 stat = GDI32_GdipFillRegion(graphics, brush, region);
4039 if (stat == NotImplemented)
4040 stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
4042 if (stat == NotImplemented)
4044 FIXME("not implemented for brushtype %i\n", brush->bt);
4045 stat = Ok;
4048 return stat;
4051 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
4053 TRACE("(%p,%u)\n", graphics, intention);
4055 if(!graphics)
4056 return InvalidParameter;
4058 if(graphics->busy)
4059 return ObjectBusy;
4061 /* We have no internal operation queue, so there's no need to clear it. */
4063 if (graphics->hdc)
4064 GdiFlush();
4066 return Ok;
4069 /*****************************************************************************
4070 * GdipGetClipBounds [GDIPLUS.@]
4072 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
4074 GpStatus status;
4075 GpRegion *clip;
4077 TRACE("(%p, %p)\n", graphics, rect);
4079 if(!graphics)
4080 return InvalidParameter;
4082 if(graphics->busy)
4083 return ObjectBusy;
4085 status = GdipCreateRegion(&clip);
4086 if (status != Ok) return status;
4088 status = GdipGetClip(graphics, clip);
4089 if (status == Ok)
4090 status = GdipGetRegionBounds(clip, graphics, rect);
4092 GdipDeleteRegion(clip);
4093 return status;
4096 /*****************************************************************************
4097 * GdipGetClipBoundsI [GDIPLUS.@]
4099 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
4101 TRACE("(%p, %p)\n", graphics, rect);
4103 if(!graphics)
4104 return InvalidParameter;
4106 if(graphics->busy)
4107 return ObjectBusy;
4109 return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
4112 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
4113 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
4114 CompositingMode *mode)
4116 TRACE("(%p, %p)\n", graphics, mode);
4118 if(!graphics || !mode)
4119 return InvalidParameter;
4121 if(graphics->busy)
4122 return ObjectBusy;
4124 *mode = graphics->compmode;
4126 return Ok;
4129 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
4130 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
4131 CompositingQuality *quality)
4133 TRACE("(%p, %p)\n", graphics, quality);
4135 if(!graphics || !quality)
4136 return InvalidParameter;
4138 if(graphics->busy)
4139 return ObjectBusy;
4141 *quality = graphics->compqual;
4143 return Ok;
4146 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
4147 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
4148 InterpolationMode *mode)
4150 TRACE("(%p, %p)\n", graphics, mode);
4152 if(!graphics || !mode)
4153 return InvalidParameter;
4155 if(graphics->busy)
4156 return ObjectBusy;
4158 *mode = graphics->interpolation;
4160 return Ok;
4163 /* FIXME: Need to handle color depths less than 24bpp */
4164 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
4166 FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
4168 if(!graphics || !argb)
4169 return InvalidParameter;
4171 if(graphics->busy)
4172 return ObjectBusy;
4174 return Ok;
4177 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
4179 TRACE("(%p, %p)\n", graphics, scale);
4181 if(!graphics || !scale)
4182 return InvalidParameter;
4184 if(graphics->busy)
4185 return ObjectBusy;
4187 *scale = graphics->scale;
4189 return Ok;
4192 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
4194 TRACE("(%p, %p)\n", graphics, unit);
4196 if(!graphics || !unit)
4197 return InvalidParameter;
4199 if(graphics->busy)
4200 return ObjectBusy;
4202 *unit = graphics->unit;
4204 return Ok;
4207 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
4208 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4209 *mode)
4211 TRACE("(%p, %p)\n", graphics, mode);
4213 if(!graphics || !mode)
4214 return InvalidParameter;
4216 if(graphics->busy)
4217 return ObjectBusy;
4219 *mode = graphics->pixeloffset;
4221 return Ok;
4224 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
4225 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
4227 TRACE("(%p, %p)\n", graphics, mode);
4229 if(!graphics || !mode)
4230 return InvalidParameter;
4232 if(graphics->busy)
4233 return ObjectBusy;
4235 *mode = graphics->smoothing;
4237 return Ok;
4240 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
4242 TRACE("(%p, %p)\n", graphics, contrast);
4244 if(!graphics || !contrast)
4245 return InvalidParameter;
4247 *contrast = graphics->textcontrast;
4249 return Ok;
4252 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
4253 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
4254 TextRenderingHint *hint)
4256 TRACE("(%p, %p)\n", graphics, hint);
4258 if(!graphics || !hint)
4259 return InvalidParameter;
4261 if(graphics->busy)
4262 return ObjectBusy;
4264 *hint = graphics->texthint;
4266 return Ok;
4269 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
4271 GpRegion *clip_rgn;
4272 GpStatus stat;
4274 TRACE("(%p, %p)\n", graphics, rect);
4276 if(!graphics || !rect)
4277 return InvalidParameter;
4279 if(graphics->busy)
4280 return ObjectBusy;
4282 /* intersect window and graphics clipping regions */
4283 if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
4284 return stat;
4286 if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
4287 goto cleanup;
4289 /* get bounds of the region */
4290 stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
4292 cleanup:
4293 GdipDeleteRegion(clip_rgn);
4295 return stat;
4298 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
4300 GpRectF rectf;
4301 GpStatus stat;
4303 TRACE("(%p, %p)\n", graphics, rect);
4305 if(!graphics || !rect)
4306 return InvalidParameter;
4308 if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
4310 rect->X = gdip_round(rectf.X);
4311 rect->Y = gdip_round(rectf.Y);
4312 rect->Width = gdip_round(rectf.Width);
4313 rect->Height = gdip_round(rectf.Height);
4316 return stat;
4319 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4321 TRACE("(%p, %p)\n", graphics, matrix);
4323 if(!graphics || !matrix)
4324 return InvalidParameter;
4326 if(graphics->busy)
4327 return ObjectBusy;
4329 *matrix = graphics->worldtrans;
4330 return Ok;
4333 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
4335 GpSolidFill *brush;
4336 GpStatus stat;
4337 GpRectF wnd_rect;
4339 TRACE("(%p, %x)\n", graphics, color);
4341 if(!graphics)
4342 return InvalidParameter;
4344 if(graphics->busy)
4345 return ObjectBusy;
4347 if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
4348 return stat;
4350 if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
4351 GdipDeleteBrush((GpBrush*)brush);
4352 return stat;
4355 GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
4356 wnd_rect.Width, wnd_rect.Height);
4358 GdipDeleteBrush((GpBrush*)brush);
4360 return Ok;
4363 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
4365 TRACE("(%p, %p)\n", graphics, res);
4367 if(!graphics || !res)
4368 return InvalidParameter;
4370 return GdipIsEmptyRegion(graphics->clip, graphics, res);
4373 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
4375 GpStatus stat;
4376 GpRegion* rgn;
4377 GpPointF pt;
4379 TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
4381 if(!graphics || !result)
4382 return InvalidParameter;
4384 if(graphics->busy)
4385 return ObjectBusy;
4387 pt.X = x;
4388 pt.Y = y;
4389 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
4390 CoordinateSpaceWorld, &pt, 1)) != Ok)
4391 return stat;
4393 if((stat = GdipCreateRegion(&rgn)) != Ok)
4394 return stat;
4396 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4397 goto cleanup;
4399 stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
4401 cleanup:
4402 GdipDeleteRegion(rgn);
4403 return stat;
4406 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
4408 return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
4411 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
4413 GpStatus stat;
4414 GpRegion* rgn;
4415 GpPointF pts[2];
4417 TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
4419 if(!graphics || !result)
4420 return InvalidParameter;
4422 if(graphics->busy)
4423 return ObjectBusy;
4425 pts[0].X = x;
4426 pts[0].Y = y;
4427 pts[1].X = x + width;
4428 pts[1].Y = y + height;
4430 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
4431 CoordinateSpaceWorld, pts, 2)) != Ok)
4432 return stat;
4434 pts[1].X -= pts[0].X;
4435 pts[1].Y -= pts[0].Y;
4437 if((stat = GdipCreateRegion(&rgn)) != Ok)
4438 return stat;
4440 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4441 goto cleanup;
4443 stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
4445 cleanup:
4446 GdipDeleteRegion(rgn);
4447 return stat;
4450 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
4452 return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
4455 GpStatus gdip_format_string(HDC hdc,
4456 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4457 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
4458 gdip_format_string_callback callback, void *user_data)
4460 WCHAR* stringdup;
4461 int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
4462 nheight, lineend, lineno = 0;
4463 RectF bounds;
4464 StringAlignment halign;
4465 GpStatus stat = Ok;
4466 SIZE size;
4467 HotkeyPrefix hkprefix;
4468 INT *hotkeyprefix_offsets=NULL;
4469 INT hotkeyprefix_count=0;
4470 INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
4471 BOOL seen_prefix = FALSE;
4472 GpStringFormat *dyn_format=NULL;
4474 if(length == -1) length = lstrlenW(string);
4476 stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
4477 if(!stringdup) return OutOfMemory;
4479 if (!format)
4481 stat = GdipStringFormatGetGenericDefault(&dyn_format);
4482 if (stat != Ok)
4484 GdipFree(stringdup);
4485 return stat;
4487 format = dyn_format;
4490 nwidth = rect->Width;
4491 nheight = rect->Height;
4492 if (ignore_empty_clip)
4494 if (!nwidth) nwidth = INT_MAX;
4495 if (!nheight) nheight = INT_MAX;
4498 hkprefix = format->hkprefix;
4500 if (hkprefix == HotkeyPrefixShow)
4502 for (i=0; i<length; i++)
4504 if (string[i] == '&')
4505 hotkeyprefix_count++;
4509 if (hotkeyprefix_count)
4510 hotkeyprefix_offsets = GdipAlloc(sizeof(INT) * hotkeyprefix_count);
4512 hotkeyprefix_count = 0;
4514 for(i = 0, j = 0; i < length; i++){
4515 /* FIXME: This makes the indexes passed to callback inaccurate. */
4516 if(!isprintW(string[i]) && (string[i] != '\n'))
4517 continue;
4519 /* FIXME: tabs should be handled using tabstops from stringformat */
4520 if (string[i] == '\t')
4521 continue;
4523 if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
4524 hotkeyprefix_offsets[hotkeyprefix_count++] = j;
4525 else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
4527 seen_prefix = TRUE;
4528 continue;
4531 seen_prefix = FALSE;
4533 stringdup[j] = string[i];
4534 j++;
4537 length = j;
4539 halign = format->align;
4541 while(sum < length){
4542 GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
4543 nwidth, &fit, NULL, &size);
4544 fitcpy = fit;
4546 if(fit == 0)
4547 break;
4549 for(lret = 0; lret < fit; lret++)
4550 if(*(stringdup + sum + lret) == '\n')
4551 break;
4553 /* Line break code (may look strange, but it imitates windows). */
4554 if(lret < fit)
4555 lineend = fit = lret; /* this is not an off-by-one error */
4556 else if(fit < (length - sum)){
4557 if(*(stringdup + sum + fit) == ' ')
4558 while(*(stringdup + sum + fit) == ' ')
4559 fit++;
4560 else
4561 while(*(stringdup + sum + fit - 1) != ' '){
4562 fit--;
4564 if(*(stringdup + sum + fit) == '\t')
4565 break;
4567 if(fit == 0){
4568 fit = fitcpy;
4569 break;
4572 lineend = fit;
4573 while(*(stringdup + sum + lineend - 1) == ' ' ||
4574 *(stringdup + sum + lineend - 1) == '\t')
4575 lineend--;
4577 else
4578 lineend = fit;
4580 GetTextExtentExPointW(hdc, stringdup + sum, lineend,
4581 nwidth, &j, NULL, &size);
4583 bounds.Width = size.cx;
4585 if(height + size.cy > nheight)
4587 if (format->attr & StringFormatFlagsLineLimit)
4588 break;
4589 bounds.Height = nheight - (height + size.cy);
4591 else
4592 bounds.Height = size.cy;
4594 bounds.Y = rect->Y + height;
4596 switch (halign)
4598 case StringAlignmentNear:
4599 default:
4600 bounds.X = rect->X;
4601 break;
4602 case StringAlignmentCenter:
4603 bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
4604 break;
4605 case StringAlignmentFar:
4606 bounds.X = rect->X + rect->Width - bounds.Width;
4607 break;
4610 for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
4611 if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
4612 break;
4614 stat = callback(hdc, stringdup, sum, lineend,
4615 font, rect, format, lineno, &bounds,
4616 &hotkeyprefix_offsets[hotkeyprefix_pos],
4617 hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
4619 if (stat != Ok)
4620 break;
4622 sum += fit + (lret < fitcpy ? 1 : 0);
4623 height += size.cy;
4624 lineno++;
4626 hotkeyprefix_pos = hotkeyprefix_end_pos;
4628 if(height > nheight)
4629 break;
4631 /* Stop if this was a linewrap (but not if it was a linebreak). */
4632 if ((lret == fitcpy) && (format->attr & StringFormatFlagsNoWrap))
4633 break;
4636 GdipFree(stringdup);
4637 GdipFree(hotkeyprefix_offsets);
4638 GdipDeleteStringFormat(dyn_format);
4640 return stat;
4643 struct measure_ranges_args {
4644 GpRegion **regions;
4645 REAL rel_width, rel_height;
4648 static GpStatus measure_ranges_callback(HDC hdc,
4649 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4650 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4651 INT lineno, const RectF *bounds, INT *underlined_indexes,
4652 INT underlined_index_count, void *user_data)
4654 int i;
4655 GpStatus stat = Ok;
4656 struct measure_ranges_args *args = user_data;
4658 for (i=0; i<format->range_count; i++)
4660 INT range_start = max(index, format->character_ranges[i].First);
4661 INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
4662 if (range_start < range_end)
4664 GpRectF range_rect;
4665 SIZE range_size;
4667 range_rect.Y = bounds->Y / args->rel_height;
4668 range_rect.Height = bounds->Height / args->rel_height;
4670 GetTextExtentExPointW(hdc, string + index, range_start - index,
4671 INT_MAX, NULL, NULL, &range_size);
4672 range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
4674 GetTextExtentExPointW(hdc, string + index, range_end - index,
4675 INT_MAX, NULL, NULL, &range_size);
4676 range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
4678 stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
4679 if (stat != Ok)
4680 break;
4684 return stat;
4687 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
4688 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
4689 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
4690 INT regionCount, GpRegion** regions)
4692 GpStatus stat;
4693 int i;
4694 HFONT gdifont, oldfont;
4695 struct measure_ranges_args args;
4696 HDC hdc, temp_hdc=NULL;
4697 GpPointF pt[3];
4698 RectF scaled_rect;
4699 REAL margin_x;
4701 TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
4702 length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
4704 if (!(graphics && string && font && layoutRect && stringFormat && regions))
4705 return InvalidParameter;
4707 if (regionCount < stringFormat->range_count)
4708 return InvalidParameter;
4710 if(!graphics->hdc)
4712 hdc = temp_hdc = CreateCompatibleDC(0);
4713 if (!temp_hdc) return OutOfMemory;
4715 else
4716 hdc = graphics->hdc;
4718 if (stringFormat->attr)
4719 TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
4721 pt[0].X = 0.0;
4722 pt[0].Y = 0.0;
4723 pt[1].X = 1.0;
4724 pt[1].Y = 0.0;
4725 pt[2].X = 0.0;
4726 pt[2].Y = 1.0;
4727 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4728 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4729 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4730 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4731 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4733 margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
4734 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
4736 scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
4737 scaled_rect.Y = layoutRect->Y * args.rel_height;
4738 scaled_rect.Width = layoutRect->Width * args.rel_width;
4739 scaled_rect.Height = layoutRect->Height * args.rel_height;
4741 get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
4742 oldfont = SelectObject(hdc, gdifont);
4744 for (i=0; i<stringFormat->range_count; i++)
4746 stat = GdipSetEmpty(regions[i]);
4747 if (stat != Ok)
4748 return stat;
4751 args.regions = regions;
4753 stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
4754 (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
4756 SelectObject(hdc, oldfont);
4757 DeleteObject(gdifont);
4759 if (temp_hdc)
4760 DeleteDC(temp_hdc);
4762 return stat;
4765 struct measure_string_args {
4766 RectF *bounds;
4767 INT *codepointsfitted;
4768 INT *linesfilled;
4769 REAL rel_width, rel_height;
4772 static GpStatus measure_string_callback(HDC hdc,
4773 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4774 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4775 INT lineno, const RectF *bounds, INT *underlined_indexes,
4776 INT underlined_index_count, void *user_data)
4778 struct measure_string_args *args = user_data;
4779 REAL new_width, new_height;
4781 new_width = bounds->Width / args->rel_width;
4782 new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
4784 if (new_width > args->bounds->Width)
4785 args->bounds->Width = new_width;
4787 if (new_height > args->bounds->Height)
4788 args->bounds->Height = new_height;
4790 if (args->codepointsfitted)
4791 *args->codepointsfitted = index + length;
4793 if (args->linesfilled)
4794 (*args->linesfilled)++;
4796 return Ok;
4799 /* Find the smallest rectangle that bounds the text when it is printed in rect
4800 * according to the format options listed in format. If rect has 0 width and
4801 * height, then just find the smallest rectangle that bounds the text when it's
4802 * printed at location (rect->X, rect-Y). */
4803 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
4804 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4805 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
4806 INT *codepointsfitted, INT *linesfilled)
4808 HFONT oldfont, gdifont;
4809 struct measure_string_args args;
4810 HDC temp_hdc=NULL, hdc;
4811 GpPointF pt[3];
4812 RectF scaled_rect;
4813 REAL margin_x;
4814 INT lines, glyphs;
4816 TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
4817 debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
4818 bounds, codepointsfitted, linesfilled);
4820 if(!graphics || !string || !font || !rect || !bounds)
4821 return InvalidParameter;
4823 if(!graphics->hdc)
4825 hdc = temp_hdc = CreateCompatibleDC(0);
4826 if (!temp_hdc) return OutOfMemory;
4828 else
4829 hdc = graphics->hdc;
4831 if(linesfilled) *linesfilled = 0;
4832 if(codepointsfitted) *codepointsfitted = 0;
4834 if(format)
4835 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4837 pt[0].X = 0.0;
4838 pt[0].Y = 0.0;
4839 pt[1].X = 1.0;
4840 pt[1].Y = 0.0;
4841 pt[2].X = 0.0;
4842 pt[2].Y = 1.0;
4843 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4844 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4845 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4846 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4847 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4849 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
4850 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
4852 scaled_rect.X = (rect->X + margin_x) * args.rel_width;
4853 scaled_rect.Y = rect->Y * args.rel_height;
4854 scaled_rect.Width = rect->Width * args.rel_width;
4855 scaled_rect.Height = rect->Height * args.rel_height;
4856 if (scaled_rect.Width >= 0.5)
4858 scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
4859 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
4862 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
4863 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
4865 get_font_hfont(graphics, font, format, &gdifont, NULL);
4866 oldfont = SelectObject(hdc, gdifont);
4868 bounds->X = rect->X;
4869 bounds->Y = rect->Y;
4870 bounds->Width = 0.0;
4871 bounds->Height = 0.0;
4873 args.bounds = bounds;
4874 args.codepointsfitted = &glyphs;
4875 args.linesfilled = &lines;
4876 lines = glyphs = 0;
4878 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
4879 measure_string_callback, &args);
4881 if (linesfilled) *linesfilled = lines;
4882 if (codepointsfitted) *codepointsfitted = glyphs;
4884 if (lines)
4885 bounds->Width += margin_x * 2.0;
4887 SelectObject(hdc, oldfont);
4888 DeleteObject(gdifont);
4890 if (temp_hdc)
4891 DeleteDC(temp_hdc);
4893 return Ok;
4896 struct draw_string_args {
4897 GpGraphics *graphics;
4898 GDIPCONST GpBrush *brush;
4899 REAL x, y, rel_width, rel_height, ascent;
4902 static GpStatus draw_string_callback(HDC hdc,
4903 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4904 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4905 INT lineno, const RectF *bounds, INT *underlined_indexes,
4906 INT underlined_index_count, void *user_data)
4908 struct draw_string_args *args = user_data;
4909 PointF position;
4910 GpStatus stat;
4912 position.X = args->x + bounds->X / args->rel_width;
4913 position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
4915 stat = draw_driver_string(args->graphics, &string[index], length, font, format,
4916 args->brush, &position,
4917 DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
4919 if (stat == Ok && underlined_index_count)
4921 OUTLINETEXTMETRICW otm;
4922 REAL underline_y, underline_height;
4923 int i;
4925 GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
4927 underline_height = otm.otmsUnderscoreSize / args->rel_height;
4928 underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
4930 for (i=0; i<underlined_index_count; i++)
4932 REAL start_x, end_x;
4933 SIZE text_size;
4934 INT ofs = underlined_indexes[i] - index;
4936 GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
4937 start_x = text_size.cx / args->rel_width;
4939 GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
4940 end_x = text_size.cx / args->rel_width;
4942 GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
4946 return stat;
4949 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
4950 INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
4951 GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
4953 HRGN rgn = NULL;
4954 HFONT gdifont;
4955 GpPointF pt[3], rectcpy[4];
4956 POINT corners[4];
4957 REAL rel_width, rel_height, margin_x;
4958 INT save_state, format_flags = 0;
4959 REAL offsety = 0.0;
4960 struct draw_string_args args;
4961 RectF scaled_rect;
4962 HDC hdc, temp_hdc=NULL;
4963 TEXTMETRICW textmetric;
4965 TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
4966 length, font, debugstr_rectf(rect), format, brush);
4968 if(!graphics || !string || !font || !brush || !rect)
4969 return InvalidParameter;
4971 if(graphics->hdc)
4973 hdc = graphics->hdc;
4975 else
4977 hdc = temp_hdc = CreateCompatibleDC(0);
4980 if(format){
4981 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4983 format_flags = format->attr;
4985 /* Should be no need to explicitly test for StringAlignmentNear as
4986 * that is default behavior if no alignment is passed. */
4987 if(format->vertalign != StringAlignmentNear){
4988 RectF bounds, in_rect = *rect;
4989 in_rect.Height = 0.0; /* avoid height clipping */
4990 GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0);
4992 TRACE("bounds %s\n", debugstr_rectf(&bounds));
4994 if(format->vertalign == StringAlignmentCenter)
4995 offsety = (rect->Height - bounds.Height) / 2;
4996 else if(format->vertalign == StringAlignmentFar)
4997 offsety = (rect->Height - bounds.Height);
4999 TRACE("vertical align %d, offsety %f\n", format->vertalign, offsety);
5002 save_state = SaveDC(hdc);
5004 pt[0].X = 0.0;
5005 pt[0].Y = 0.0;
5006 pt[1].X = 1.0;
5007 pt[1].Y = 0.0;
5008 pt[2].X = 0.0;
5009 pt[2].Y = 1.0;
5010 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
5011 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5012 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5013 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5014 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5016 rectcpy[3].X = rectcpy[0].X = rect->X;
5017 rectcpy[1].Y = rectcpy[0].Y = rect->Y;
5018 rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
5019 rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
5020 transform_and_round_points(graphics, corners, rectcpy, 4);
5022 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
5023 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
5025 scaled_rect.X = margin_x * rel_width;
5026 scaled_rect.Y = 0.0;
5027 scaled_rect.Width = rel_width * rect->Width;
5028 scaled_rect.Height = rel_height * rect->Height;
5029 if (scaled_rect.Width >= 0.5)
5031 scaled_rect.Width -= margin_x * 2.0 * rel_width;
5032 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
5035 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
5036 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
5038 if (!(format_flags & StringFormatFlagsNoClip) &&
5039 scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 &&
5040 rect->Width > 0.0 && rect->Height > 0.0)
5042 /* FIXME: If only the width or only the height is 0, we should probably still clip */
5043 rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
5044 SelectClipRgn(hdc, rgn);
5047 get_font_hfont(graphics, font, format, &gdifont, NULL);
5048 SelectObject(hdc, gdifont);
5050 args.graphics = graphics;
5051 args.brush = brush;
5053 args.x = rect->X;
5054 args.y = rect->Y + offsety;
5056 args.rel_width = rel_width;
5057 args.rel_height = rel_height;
5059 GetTextMetricsW(hdc, &textmetric);
5060 args.ascent = textmetric.tmAscent / rel_height;
5062 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
5063 draw_string_callback, &args);
5065 DeleteObject(rgn);
5066 DeleteObject(gdifont);
5068 RestoreDC(hdc, save_state);
5070 DeleteDC(temp_hdc);
5072 return Ok;
5075 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
5077 TRACE("(%p)\n", graphics);
5079 if(!graphics)
5080 return InvalidParameter;
5082 if(graphics->busy)
5083 return ObjectBusy;
5085 return GdipSetInfinite(graphics->clip);
5088 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
5090 TRACE("(%p)\n", graphics);
5092 if(!graphics)
5093 return InvalidParameter;
5095 if(graphics->busy)
5096 return ObjectBusy;
5098 return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
5101 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
5103 return GdipEndContainer(graphics, state);
5106 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
5107 GpMatrixOrder order)
5109 TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
5111 if(!graphics)
5112 return InvalidParameter;
5114 if(graphics->busy)
5115 return ObjectBusy;
5117 return GdipRotateMatrix(&graphics->worldtrans, angle, order);
5120 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
5122 return GdipBeginContainer2(graphics, state);
5125 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
5126 GraphicsContainer *state)
5128 GraphicsContainerItem *container;
5129 GpStatus sts;
5131 TRACE("(%p, %p)\n", graphics, state);
5133 if(!graphics || !state)
5134 return InvalidParameter;
5136 sts = init_container(&container, graphics);
5137 if(sts != Ok)
5138 return sts;
5140 list_add_head(&graphics->containers, &container->entry);
5141 *state = graphics->contid = container->contid;
5143 return Ok;
5146 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
5148 FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
5149 return NotImplemented;
5152 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
5154 FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
5155 return NotImplemented;
5158 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
5160 FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
5161 return NotImplemented;
5164 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
5166 GpStatus sts;
5167 GraphicsContainerItem *container, *container2;
5169 TRACE("(%p, %x)\n", graphics, state);
5171 if(!graphics)
5172 return InvalidParameter;
5174 LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
5175 if(container->contid == state)
5176 break;
5179 /* did not find a matching container */
5180 if(&container->entry == &graphics->containers)
5181 return Ok;
5183 sts = restore_container(graphics, container);
5184 if(sts != Ok)
5185 return sts;
5187 /* remove all of the containers on top of the found container */
5188 LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
5189 if(container->contid == state)
5190 break;
5191 list_remove(&container->entry);
5192 delete_container(container);
5195 list_remove(&container->entry);
5196 delete_container(container);
5198 return Ok;
5201 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
5202 REAL sy, GpMatrixOrder order)
5204 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
5206 if(!graphics)
5207 return InvalidParameter;
5209 if(graphics->busy)
5210 return ObjectBusy;
5212 return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
5215 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
5216 CombineMode mode)
5218 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
5220 if(!graphics || !srcgraphics)
5221 return InvalidParameter;
5223 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
5226 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
5227 CompositingMode mode)
5229 TRACE("(%p, %d)\n", graphics, mode);
5231 if(!graphics)
5232 return InvalidParameter;
5234 if(graphics->busy)
5235 return ObjectBusy;
5237 graphics->compmode = mode;
5239 return Ok;
5242 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
5243 CompositingQuality quality)
5245 TRACE("(%p, %d)\n", graphics, quality);
5247 if(!graphics)
5248 return InvalidParameter;
5250 if(graphics->busy)
5251 return ObjectBusy;
5253 graphics->compqual = quality;
5255 return Ok;
5258 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
5259 InterpolationMode mode)
5261 TRACE("(%p, %d)\n", graphics, mode);
5263 if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
5264 return InvalidParameter;
5266 if(graphics->busy)
5267 return ObjectBusy;
5269 if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
5270 mode = InterpolationModeBilinear;
5272 if (mode == InterpolationModeHighQuality)
5273 mode = InterpolationModeHighQualityBicubic;
5275 graphics->interpolation = mode;
5277 return Ok;
5280 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
5282 GpStatus stat;
5284 TRACE("(%p, %.2f)\n", graphics, scale);
5286 if(!graphics || (scale <= 0.0))
5287 return InvalidParameter;
5289 if(graphics->busy)
5290 return ObjectBusy;
5292 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5294 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, graphics->unit, scale);
5295 if (stat != Ok)
5296 return stat;
5299 graphics->scale = scale;
5301 return Ok;
5304 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
5306 GpStatus stat;
5308 TRACE("(%p, %d)\n", graphics, unit);
5310 if(!graphics)
5311 return InvalidParameter;
5313 if(graphics->busy)
5314 return ObjectBusy;
5316 if(unit == UnitWorld)
5317 return InvalidParameter;
5319 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5321 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, unit, graphics->scale);
5322 if (stat != Ok)
5323 return stat;
5326 graphics->unit = unit;
5328 return Ok;
5331 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
5332 mode)
5334 TRACE("(%p, %d)\n", graphics, mode);
5336 if(!graphics)
5337 return InvalidParameter;
5339 if(graphics->busy)
5340 return ObjectBusy;
5342 graphics->pixeloffset = mode;
5344 return Ok;
5347 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
5349 static int calls;
5351 TRACE("(%p,%i,%i)\n", graphics, x, y);
5353 if (!(calls++))
5354 FIXME("value is unused in rendering\n");
5356 if (!graphics)
5357 return InvalidParameter;
5359 graphics->origin_x = x;
5360 graphics->origin_y = y;
5362 return Ok;
5365 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
5367 TRACE("(%p,%p,%p)\n", graphics, x, y);
5369 if (!graphics || !x || !y)
5370 return InvalidParameter;
5372 *x = graphics->origin_x;
5373 *y = graphics->origin_y;
5375 return Ok;
5378 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
5380 TRACE("(%p, %d)\n", graphics, mode);
5382 if(!graphics)
5383 return InvalidParameter;
5385 if(graphics->busy)
5386 return ObjectBusy;
5388 graphics->smoothing = mode;
5390 return Ok;
5393 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
5395 TRACE("(%p, %d)\n", graphics, contrast);
5397 if(!graphics)
5398 return InvalidParameter;
5400 graphics->textcontrast = contrast;
5402 return Ok;
5405 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
5406 TextRenderingHint hint)
5408 TRACE("(%p, %d)\n", graphics, hint);
5410 if(!graphics || hint > TextRenderingHintClearTypeGridFit)
5411 return InvalidParameter;
5413 if(graphics->busy)
5414 return ObjectBusy;
5416 graphics->texthint = hint;
5418 return Ok;
5421 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
5423 TRACE("(%p, %p)\n", graphics, matrix);
5425 if(!graphics || !matrix)
5426 return InvalidParameter;
5428 if(graphics->busy)
5429 return ObjectBusy;
5431 TRACE("%f,%f,%f,%f,%f,%f\n",
5432 matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
5433 matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
5435 graphics->worldtrans = *matrix;
5437 return Ok;
5440 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
5441 REAL dy, GpMatrixOrder order)
5443 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
5445 if(!graphics)
5446 return InvalidParameter;
5448 if(graphics->busy)
5449 return ObjectBusy;
5451 return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
5454 /*****************************************************************************
5455 * GdipSetClipHrgn [GDIPLUS.@]
5457 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
5459 GpRegion *region;
5460 GpStatus status;
5462 TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
5464 if(!graphics)
5465 return InvalidParameter;
5467 if(graphics->busy)
5468 return ObjectBusy;
5470 /* hrgn is already in device units */
5471 status = GdipCreateRegionHrgn(hrgn, &region);
5472 if(status != Ok)
5473 return status;
5475 status = GdipCombineRegionRegion(graphics->clip, region, mode);
5477 GdipDeleteRegion(region);
5478 return status;
5481 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
5483 GpStatus status;
5484 GpPath *clip_path;
5486 TRACE("(%p, %p, %d)\n", graphics, path, mode);
5488 if(!graphics)
5489 return InvalidParameter;
5491 if(graphics->busy)
5492 return ObjectBusy;
5494 status = GdipClonePath(path, &clip_path);
5495 if (status == Ok)
5497 GpMatrix world_to_device;
5499 get_graphics_transform(graphics, CoordinateSpaceDevice,
5500 CoordinateSpaceWorld, &world_to_device);
5501 status = GdipTransformPath(clip_path, &world_to_device);
5502 if (status == Ok)
5503 GdipCombineRegionPath(graphics->clip, clip_path, mode);
5505 GdipDeletePath(clip_path);
5507 return status;
5510 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
5511 REAL width, REAL height,
5512 CombineMode mode)
5514 GpStatus status;
5515 GpRectF rect;
5516 GpRegion *region;
5518 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
5520 if(!graphics)
5521 return InvalidParameter;
5523 if(graphics->busy)
5524 return ObjectBusy;
5526 rect.X = x;
5527 rect.Y = y;
5528 rect.Width = width;
5529 rect.Height = height;
5530 status = GdipCreateRegionRect(&rect, &region);
5531 if (status == Ok)
5533 GpMatrix world_to_device;
5535 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
5536 status = GdipTransformRegion(region, &world_to_device);
5537 if (status == Ok)
5538 status = GdipCombineRegionRegion(graphics->clip, region, mode);
5540 GdipDeleteRegion(region);
5542 return status;
5545 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
5546 INT width, INT height,
5547 CombineMode mode)
5549 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
5551 if(!graphics)
5552 return InvalidParameter;
5554 if(graphics->busy)
5555 return ObjectBusy;
5557 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
5560 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
5561 CombineMode mode)
5563 GpStatus status;
5564 GpRegion *clip;
5566 TRACE("(%p, %p, %d)\n", graphics, region, mode);
5568 if(!graphics || !region)
5569 return InvalidParameter;
5571 if(graphics->busy)
5572 return ObjectBusy;
5574 status = GdipCloneRegion(region, &clip);
5575 if (status == Ok)
5577 GpMatrix world_to_device;
5579 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
5580 status = GdipTransformRegion(clip, &world_to_device);
5581 if (status == Ok)
5582 status = GdipCombineRegionRegion(graphics->clip, clip, mode);
5584 GdipDeleteRegion(clip);
5586 return status;
5589 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
5590 INT count)
5592 INT save_state;
5593 POINT *pti;
5595 TRACE("(%p, %p, %d)\n", graphics, points, count);
5597 if(!graphics || !pen || count<=0)
5598 return InvalidParameter;
5600 if(graphics->busy)
5601 return ObjectBusy;
5603 if (!graphics->hdc)
5605 FIXME("graphics object has no HDC\n");
5606 return Ok;
5609 pti = GdipAlloc(sizeof(POINT) * count);
5611 save_state = prepare_dc(graphics, pen);
5612 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
5614 transform_and_round_points(graphics, pti, (GpPointF*)points, count);
5615 Polygon(graphics->hdc, pti, count);
5617 restore_dc(graphics, save_state);
5618 GdipFree(pti);
5620 return Ok;
5623 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
5624 INT count)
5626 GpStatus ret;
5627 GpPointF *ptf;
5628 INT i;
5630 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
5632 if(count<=0) return InvalidParameter;
5633 ptf = GdipAlloc(sizeof(GpPointF) * count);
5635 for(i = 0;i < count; i++){
5636 ptf[i].X = (REAL)points[i].X;
5637 ptf[i].Y = (REAL)points[i].Y;
5640 ret = GdipDrawPolygon(graphics,pen,ptf,count);
5641 GdipFree(ptf);
5643 return ret;
5646 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
5648 TRACE("(%p, %p)\n", graphics, dpi);
5650 if(!graphics || !dpi)
5651 return InvalidParameter;
5653 if(graphics->busy)
5654 return ObjectBusy;
5656 *dpi = graphics->xres;
5657 return Ok;
5660 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
5662 TRACE("(%p, %p)\n", graphics, dpi);
5664 if(!graphics || !dpi)
5665 return InvalidParameter;
5667 if(graphics->busy)
5668 return ObjectBusy;
5670 *dpi = graphics->yres;
5671 return Ok;
5674 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
5675 GpMatrixOrder order)
5677 GpMatrix m;
5678 GpStatus ret;
5680 TRACE("(%p, %p, %d)\n", graphics, matrix, order);
5682 if(!graphics || !matrix)
5683 return InvalidParameter;
5685 if(graphics->busy)
5686 return ObjectBusy;
5688 m = graphics->worldtrans;
5690 ret = GdipMultiplyMatrix(&m, matrix, order);
5691 if(ret == Ok)
5692 graphics->worldtrans = m;
5694 return ret;
5697 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
5698 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
5700 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
5702 GpStatus stat=Ok;
5704 TRACE("(%p, %p)\n", graphics, hdc);
5706 if(!graphics || !hdc)
5707 return InvalidParameter;
5709 if(graphics->busy)
5710 return ObjectBusy;
5712 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5714 stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
5716 else if (!graphics->hdc || graphics->alpha_hdc ||
5717 (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
5719 /* Create a fake HDC and fill it with a constant color. */
5720 HDC temp_hdc;
5721 HBITMAP hbitmap;
5722 GpRectF bounds;
5723 BITMAPINFOHEADER bmih;
5724 int i;
5726 stat = get_graphics_bounds(graphics, &bounds);
5727 if (stat != Ok)
5728 return stat;
5730 graphics->temp_hbitmap_width = bounds.Width;
5731 graphics->temp_hbitmap_height = bounds.Height;
5733 bmih.biSize = sizeof(bmih);
5734 bmih.biWidth = graphics->temp_hbitmap_width;
5735 bmih.biHeight = -graphics->temp_hbitmap_height;
5736 bmih.biPlanes = 1;
5737 bmih.biBitCount = 32;
5738 bmih.biCompression = BI_RGB;
5739 bmih.biSizeImage = 0;
5740 bmih.biXPelsPerMeter = 0;
5741 bmih.biYPelsPerMeter = 0;
5742 bmih.biClrUsed = 0;
5743 bmih.biClrImportant = 0;
5745 hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
5746 (void**)&graphics->temp_bits, NULL, 0);
5747 if (!hbitmap)
5748 return GenericError;
5750 temp_hdc = CreateCompatibleDC(0);
5751 if (!temp_hdc)
5753 DeleteObject(hbitmap);
5754 return GenericError;
5757 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5758 ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
5760 SelectObject(temp_hdc, hbitmap);
5762 graphics->temp_hbitmap = hbitmap;
5763 *hdc = graphics->temp_hdc = temp_hdc;
5765 else
5767 *hdc = graphics->hdc;
5770 if (stat == Ok)
5771 graphics->busy = TRUE;
5773 return stat;
5776 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
5778 GpStatus stat=Ok;
5780 TRACE("(%p, %p)\n", graphics, hdc);
5782 if(!graphics || !hdc || !graphics->busy)
5783 return InvalidParameter;
5785 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5787 stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
5789 else if (graphics->temp_hdc == hdc)
5791 DWORD* pos;
5792 int i;
5794 /* Find the pixels that have changed, and mark them as opaque. */
5795 pos = (DWORD*)graphics->temp_bits;
5796 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5798 if (*pos != DC_BACKGROUND_KEY)
5800 *pos |= 0xff000000;
5802 pos++;
5805 /* Write the changed pixels to the real target. */
5806 alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
5807 graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
5808 graphics->temp_hbitmap_width * 4);
5810 /* Clean up. */
5811 DeleteDC(graphics->temp_hdc);
5812 DeleteObject(graphics->temp_hbitmap);
5813 graphics->temp_hdc = NULL;
5814 graphics->temp_hbitmap = NULL;
5816 else if (hdc != graphics->hdc)
5818 stat = InvalidParameter;
5821 if (stat == Ok)
5822 graphics->busy = FALSE;
5824 return stat;
5827 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
5829 GpRegion *clip;
5830 GpStatus status;
5831 GpMatrix device_to_world;
5833 TRACE("(%p, %p)\n", graphics, region);
5835 if(!graphics || !region)
5836 return InvalidParameter;
5838 if(graphics->busy)
5839 return ObjectBusy;
5841 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
5842 return status;
5844 get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
5845 status = GdipTransformRegion(clip, &device_to_world);
5846 if (status != Ok)
5848 GdipDeleteRegion(clip);
5849 return status;
5852 /* free everything except root node and header */
5853 delete_element(&region->node);
5854 memcpy(region, clip, sizeof(GpRegion));
5855 GdipFree(clip);
5857 return Ok;
5860 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
5861 GpCoordinateSpace src_space, GpMatrix *matrix)
5863 GpStatus stat = Ok;
5864 REAL scale_x, scale_y;
5866 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
5868 if (dst_space != src_space)
5870 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
5871 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
5873 if(graphics->unit != UnitDisplay)
5875 scale_x *= graphics->scale;
5876 scale_y *= graphics->scale;
5879 /* transform from src_space to CoordinateSpacePage */
5880 switch (src_space)
5882 case CoordinateSpaceWorld:
5883 GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
5884 break;
5885 case CoordinateSpacePage:
5886 break;
5887 case CoordinateSpaceDevice:
5888 GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
5889 break;
5892 /* transform from CoordinateSpacePage to dst_space */
5893 switch (dst_space)
5895 case CoordinateSpaceWorld:
5897 GpMatrix inverted_transform = graphics->worldtrans;
5898 stat = GdipInvertMatrix(&inverted_transform);
5899 if (stat == Ok)
5900 GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
5901 break;
5903 case CoordinateSpacePage:
5904 break;
5905 case CoordinateSpaceDevice:
5906 GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
5907 break;
5910 return stat;
5913 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
5914 GpCoordinateSpace src_space, GpPointF *points, INT count)
5916 GpMatrix matrix;
5917 GpStatus stat;
5919 if(!graphics || !points || count <= 0)
5920 return InvalidParameter;
5922 if(graphics->busy)
5923 return ObjectBusy;
5925 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5927 if (src_space == dst_space) return Ok;
5929 stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
5930 if (stat != Ok) return stat;
5932 return GdipTransformMatrixPoints(&matrix, points, count);
5935 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
5936 GpCoordinateSpace src_space, GpPoint *points, INT count)
5938 GpPointF *pointsF;
5939 GpStatus ret;
5940 INT i;
5942 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5944 if(count <= 0)
5945 return InvalidParameter;
5947 pointsF = GdipAlloc(sizeof(GpPointF) * count);
5948 if(!pointsF)
5949 return OutOfMemory;
5951 for(i = 0; i < count; i++){
5952 pointsF[i].X = (REAL)points[i].X;
5953 pointsF[i].Y = (REAL)points[i].Y;
5956 ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
5958 if(ret == Ok)
5959 for(i = 0; i < count; i++){
5960 points[i].X = gdip_round(pointsF[i].X);
5961 points[i].Y = gdip_round(pointsF[i].Y);
5963 GdipFree(pointsF);
5965 return ret;
5968 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
5970 static int calls;
5972 TRACE("\n");
5974 if (!calls++)
5975 FIXME("stub\n");
5977 return NULL;
5980 /*****************************************************************************
5981 * GdipTranslateClip [GDIPLUS.@]
5983 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
5985 TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
5987 if(!graphics)
5988 return InvalidParameter;
5990 if(graphics->busy)
5991 return ObjectBusy;
5993 return GdipTranslateRegion(graphics->clip, dx, dy);
5996 /*****************************************************************************
5997 * GdipTranslateClipI [GDIPLUS.@]
5999 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
6001 TRACE("(%p, %d, %d)\n", graphics, dx, dy);
6003 if(!graphics)
6004 return InvalidParameter;
6006 if(graphics->busy)
6007 return ObjectBusy;
6009 return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
6013 /*****************************************************************************
6014 * GdipMeasureDriverString [GDIPLUS.@]
6016 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6017 GDIPCONST GpFont *font, GDIPCONST PointF *positions,
6018 INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
6020 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
6021 HFONT hfont;
6022 HDC hdc;
6023 REAL min_x, min_y, max_x, max_y, x, y;
6024 int i;
6025 TEXTMETRICW textmetric;
6026 const WORD *glyph_indices;
6027 WORD *dynamic_glyph_indices=NULL;
6028 REAL rel_width, rel_height, ascent, descent;
6029 GpPointF pt[3];
6031 TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
6033 if (!graphics || !text || !font || !positions || !boundingBox)
6034 return InvalidParameter;
6036 if (length == -1)
6037 length = strlenW(text);
6039 if (length == 0)
6041 boundingBox->X = 0.0;
6042 boundingBox->Y = 0.0;
6043 boundingBox->Width = 0.0;
6044 boundingBox->Height = 0.0;
6047 if (flags & unsupported_flags)
6048 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6050 get_font_hfont(graphics, font, NULL, &hfont, matrix);
6052 hdc = CreateCompatibleDC(0);
6053 SelectObject(hdc, hfont);
6055 GetTextMetricsW(hdc, &textmetric);
6057 pt[0].X = 0.0;
6058 pt[0].Y = 0.0;
6059 pt[1].X = 1.0;
6060 pt[1].Y = 0.0;
6061 pt[2].X = 0.0;
6062 pt[2].Y = 1.0;
6063 if (matrix)
6065 GpMatrix xform = *matrix;
6066 GdipTransformMatrixPoints(&xform, pt, 3);
6068 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
6069 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
6070 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
6071 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
6072 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
6074 if (flags & DriverStringOptionsCmapLookup)
6076 glyph_indices = dynamic_glyph_indices = GdipAlloc(sizeof(WORD) * length);
6077 if (!glyph_indices)
6079 DeleteDC(hdc);
6080 DeleteObject(hfont);
6081 return OutOfMemory;
6084 GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
6086 else
6087 glyph_indices = text;
6089 min_x = max_x = x = positions[0].X;
6090 min_y = max_y = y = positions[0].Y;
6092 ascent = textmetric.tmAscent / rel_height;
6093 descent = textmetric.tmDescent / rel_height;
6095 for (i=0; i<length; i++)
6097 int char_width;
6098 ABC abc;
6100 if (!(flags & DriverStringOptionsRealizedAdvance))
6102 x = positions[i].X;
6103 y = positions[i].Y;
6106 GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
6107 char_width = abc.abcA + abc.abcB + abc.abcC;
6109 if (min_y > y - ascent) min_y = y - ascent;
6110 if (max_y < y + descent) max_y = y + descent;
6111 if (min_x > x) min_x = x;
6113 x += char_width / rel_width;
6115 if (max_x < x) max_x = x;
6118 GdipFree(dynamic_glyph_indices);
6119 DeleteDC(hdc);
6120 DeleteObject(hfont);
6122 boundingBox->X = min_x;
6123 boundingBox->Y = min_y;
6124 boundingBox->Width = max_x - min_x;
6125 boundingBox->Height = max_y - min_y;
6127 return Ok;
6130 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6131 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
6132 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
6133 INT flags, GDIPCONST GpMatrix *matrix)
6135 static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
6136 INT save_state;
6137 GpPointF pt;
6138 HFONT hfont;
6139 UINT eto_flags=0;
6141 if (flags & unsupported_flags)
6142 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6144 if (!(flags & DriverStringOptionsCmapLookup))
6145 eto_flags |= ETO_GLYPH_INDEX;
6147 save_state = SaveDC(graphics->hdc);
6148 SetBkMode(graphics->hdc, TRANSPARENT);
6149 SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
6151 pt = positions[0];
6152 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
6154 get_font_hfont(graphics, font, format, &hfont, matrix);
6155 SelectObject(graphics->hdc, hfont);
6157 SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
6159 ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
6161 RestoreDC(graphics->hdc, save_state);
6163 DeleteObject(hfont);
6165 return Ok;
6168 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6169 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
6170 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
6171 INT flags, GDIPCONST GpMatrix *matrix)
6173 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
6174 GpStatus stat;
6175 PointF *real_positions, real_position;
6176 POINT *pti;
6177 HFONT hfont;
6178 HDC hdc;
6179 int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
6180 DWORD max_glyphsize=0;
6181 GLYPHMETRICS glyphmetrics;
6182 static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
6183 BYTE *glyph_mask;
6184 BYTE *text_mask;
6185 int text_mask_stride;
6186 BYTE *pixel_data;
6187 int pixel_data_stride;
6188 GpRect pixel_area;
6189 UINT ggo_flags = GGO_GRAY8_BITMAP;
6191 if (length <= 0)
6192 return Ok;
6194 if (!(flags & DriverStringOptionsCmapLookup))
6195 ggo_flags |= GGO_GLYPH_INDEX;
6197 if (flags & unsupported_flags)
6198 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6200 pti = GdipAlloc(sizeof(POINT) * length);
6201 if (!pti)
6202 return OutOfMemory;
6204 if (flags & DriverStringOptionsRealizedAdvance)
6206 real_position = positions[0];
6208 transform_and_round_points(graphics, pti, &real_position, 1);
6210 else
6212 real_positions = GdipAlloc(sizeof(PointF) * length);
6213 if (!real_positions)
6215 GdipFree(pti);
6216 return OutOfMemory;
6219 memcpy(real_positions, positions, sizeof(PointF) * length);
6221 transform_and_round_points(graphics, pti, real_positions, length);
6223 GdipFree(real_positions);
6226 get_font_hfont(graphics, font, format, &hfont, matrix);
6228 hdc = CreateCompatibleDC(0);
6229 SelectObject(hdc, hfont);
6231 /* Get the boundaries of the text to be drawn */
6232 for (i=0; i<length; i++)
6234 DWORD glyphsize;
6235 int left, top, right, bottom;
6237 glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
6238 &glyphmetrics, 0, NULL, &identity);
6240 if (glyphsize == GDI_ERROR)
6242 ERR("GetGlyphOutlineW failed\n");
6243 GdipFree(pti);
6244 DeleteDC(hdc);
6245 DeleteObject(hfont);
6246 return GenericError;
6249 if (glyphsize > max_glyphsize)
6250 max_glyphsize = glyphsize;
6252 if (glyphsize != 0)
6254 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
6255 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
6256 right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
6257 bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
6259 if (left < min_x) min_x = left;
6260 if (top < min_y) min_y = top;
6261 if (right > max_x) max_x = right;
6262 if (bottom > max_y) max_y = bottom;
6265 if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
6267 pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
6268 pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
6272 if (max_glyphsize == 0)
6273 /* Nothing to draw. */
6274 return Ok;
6276 glyph_mask = GdipAlloc(max_glyphsize);
6277 text_mask = GdipAlloc((max_x - min_x) * (max_y - min_y));
6278 text_mask_stride = max_x - min_x;
6280 if (!(glyph_mask && text_mask))
6282 GdipFree(glyph_mask);
6283 GdipFree(text_mask);
6284 GdipFree(pti);
6285 DeleteDC(hdc);
6286 DeleteObject(hfont);
6287 return OutOfMemory;
6290 /* Generate a mask for the text */
6291 for (i=0; i<length; i++)
6293 DWORD ret;
6294 int left, top, stride;
6296 ret = GetGlyphOutlineW(hdc, text[i], ggo_flags,
6297 &glyphmetrics, max_glyphsize, glyph_mask, &identity);
6299 if (ret == GDI_ERROR || ret == 0)
6300 continue; /* empty glyph */
6302 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
6303 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
6304 stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
6306 for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
6308 BYTE *glyph_val = glyph_mask + y * stride;
6309 BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
6310 for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
6312 *text_val = min(64, *text_val + *glyph_val);
6313 glyph_val++;
6314 text_val++;
6319 GdipFree(pti);
6320 DeleteDC(hdc);
6321 DeleteObject(hfont);
6322 GdipFree(glyph_mask);
6324 /* get the brush data */
6325 pixel_data = GdipAlloc(4 * (max_x - min_x) * (max_y - min_y));
6326 if (!pixel_data)
6328 GdipFree(text_mask);
6329 return OutOfMemory;
6332 pixel_area.X = min_x;
6333 pixel_area.Y = min_y;
6334 pixel_area.Width = max_x - min_x;
6335 pixel_area.Height = max_y - min_y;
6336 pixel_data_stride = pixel_area.Width * 4;
6338 stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
6339 if (stat != Ok)
6341 GdipFree(text_mask);
6342 GdipFree(pixel_data);
6343 return stat;
6346 /* multiply the brush data by the mask */
6347 for (y=0; y<pixel_area.Height; y++)
6349 BYTE *text_val = text_mask + text_mask_stride * y;
6350 BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
6351 for (x=0; x<pixel_area.Width; x++)
6353 *pixel_val = (*pixel_val) * (*text_val) / 64;
6354 text_val++;
6355 pixel_val+=4;
6359 GdipFree(text_mask);
6361 /* draw the result */
6362 stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
6363 pixel_area.Height, pixel_data_stride);
6365 GdipFree(pixel_data);
6367 return stat;
6370 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6371 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
6372 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
6373 INT flags, GDIPCONST GpMatrix *matrix)
6375 GpStatus stat = NotImplemented;
6377 if (length == -1)
6378 length = strlenW(text);
6380 if (graphics->hdc && !graphics->alpha_hdc &&
6381 ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
6382 brush->bt == BrushTypeSolidColor &&
6383 (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
6384 stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
6385 brush, positions, flags, matrix);
6386 if (stat == NotImplemented)
6387 stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
6388 brush, positions, flags, matrix);
6389 return stat;
6392 /*****************************************************************************
6393 * GdipDrawDriverString [GDIPLUS.@]
6395 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6396 GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
6397 GDIPCONST PointF *positions, INT flags,
6398 GDIPCONST GpMatrix *matrix )
6400 TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
6402 if (!graphics || !text || !font || !brush || !positions)
6403 return InvalidParameter;
6405 return draw_driver_string(graphics, text, length, font, NULL,
6406 brush, positions, flags, matrix);
6409 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
6410 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
6412 FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
6413 return NotImplemented;
6416 /*****************************************************************************
6417 * GdipIsVisibleClipEmpty [GDIPLUS.@]
6419 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
6421 GpStatus stat;
6422 GpRegion* rgn;
6424 TRACE("(%p, %p)\n", graphics, res);
6426 if((stat = GdipCreateRegion(&rgn)) != Ok)
6427 return stat;
6429 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
6430 goto cleanup;
6432 stat = GdipIsEmptyRegion(rgn, graphics, res);
6434 cleanup:
6435 GdipDeleteRegion(rgn);
6436 return stat;
6439 GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
6441 static int calls;
6443 TRACE("(%p) stub\n", graphics);
6445 if(!(calls++))
6446 FIXME("not implemented\n");
6448 return NotImplemented;