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