[rshell]
[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 int 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 INT brush_can_fill_path(GpBrush *brush)
944 {
945 switch (brush->bt)
946 {
947 case BrushTypeSolidColor:
948 return 1;
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 0;
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 INT 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 1;
1021 default:
1022 return 0;
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 int 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 = 1;
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 int seen_start=0, seen_end=0, seen_center=0;
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 = 1;
1374 start_center_line ^= 1;
1375 }
1376 if (!seen_end && yf >= end_point.Y)
1377 {
1378 seen_end = 1;
1379 end_center_line ^= 1;
1380 }
1381 if (!seen_center && yf >= center_point.Y)
1382 {
1383 seen_center = 1;
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 GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
2247 GpMetafile **metafile)
2248 {
2249 ENHMETAHEADER header;
2250 MetafileType metafile_type;
2251
2252 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
2253
2254 if(!hemf || !metafile)
2255 return InvalidParameter;
2256
2257 if (GetEnhMetaFileHeader(hemf, sizeof(header), &header) == 0)
2258 return GenericError;
2259
2260 metafile_type = METAFILE_GetEmfType(hemf);
2261
2262 if (metafile_type == MetafileTypeInvalid)
2263 return GenericError;
2264
2265 *metafile = GdipAlloc(sizeof(GpMetafile));
2266 if (!*metafile)
2267 return OutOfMemory;
2268
2269 (*metafile)->image.type = ImageTypeMetafile;
2270 (*metafile)->image.format = ImageFormatEMF;
2271 (*metafile)->image.frame_count = 1;
2272 (*metafile)->image.xres = (REAL)header.szlDevice.cx;
2273 (*metafile)->image.yres = (REAL)header.szlDevice.cy;
2274 (*metafile)->bounds.X = (REAL)header.rclBounds.left;
2275 (*metafile)->bounds.Y = (REAL)header.rclBounds.top;
2276 (*metafile)->bounds.Width = (REAL)(header.rclBounds.right - header.rclBounds.left);
2277 (*metafile)->bounds.Height = (REAL)(header.rclBounds.bottom - header.rclBounds.top);
2278 (*metafile)->unit = UnitPixel;
2279 (*metafile)->metafile_type = metafile_type;
2280 (*metafile)->hemf = hemf;
2281 (*metafile)->preserve_hemf = !delete;
2282
2283 TRACE("<-- %p\n", *metafile);
2284
2285 return Ok;
2286 }
2287
2288 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
2289 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2290 {
2291 UINT read;
2292 BYTE *copy;
2293 HENHMETAFILE hemf;
2294 GpStatus retval = Ok;
2295
2296 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
2297
2298 if(!hwmf || !metafile || !placeable)
2299 return InvalidParameter;
2300
2301 *metafile = NULL;
2302 read = GetMetaFileBitsEx(hwmf, 0, NULL);
2303 if(!read)
2304 return GenericError;
2305 copy = GdipAlloc(read);
2306 GetMetaFileBitsEx(hwmf, read, copy);
2307
2308 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
2309 GdipFree(copy);
2310
2311 /* FIXME: We should store and use hwmf instead of converting to hemf */
2312 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
2313
2314 if (retval == Ok)
2315 {
2316 (*metafile)->image.xres = (REAL)placeable->Inch;
2317 (*metafile)->image.yres = (REAL)placeable->Inch;
2318 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
2319 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
2320 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
2321 placeable->BoundingBox.Left);
2322 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
2323 placeable->BoundingBox.Top);
2324 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
2325 (*metafile)->image.format = ImageFormatWMF;
2326
2327 if (delete) DeleteMetaFile(hwmf);
2328 }
2329 else
2330 DeleteEnhMetaFile(hemf);
2331 return retval;
2332 }
2333
2334 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
2335 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2336 {
2337 HMETAFILE hmf = GetMetaFileW(file);
2338
2339 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
2340
2341 if(!hmf) return InvalidParameter;
2342
2343 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
2344 }
2345
2346 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
2347 GpMetafile **metafile)
2348 {
2349 FIXME("(%p, %p): stub\n", file, metafile);
2350 return NotImplemented;
2351 }
2352
2353 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
2354 GpMetafile **metafile)
2355 {
2356 FIXME("(%p, %p): stub\n", stream, metafile);
2357 return NotImplemented;
2358 }
2359
2360 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
2361 UINT access, IStream **stream)
2362 {
2363 DWORD dwMode;
2364 HRESULT ret;
2365
2366 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
2367
2368 if(!stream || !filename)
2369 return InvalidParameter;
2370
2371 if(access & GENERIC_WRITE)
2372 dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
2373 else if(access & GENERIC_READ)
2374 dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
2375 else
2376 return InvalidParameter;
2377
2378 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
2379
2380 return hresult_to_status(ret);
2381 }
2382
2383 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
2384 {
2385 GraphicsContainerItem *cont, *next;
2386 GpStatus stat;
2387 TRACE("(%p)\n", graphics);
2388
2389 if(!graphics) return InvalidParameter;
2390 if(graphics->busy) return ObjectBusy;
2391
2392 if (graphics->image && graphics->image_type == ImageTypeMetafile)
2393 {
2394 stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
2395 if (stat != Ok)
2396 return stat;
2397 }
2398
2399 if(graphics->owndc)
2400 ReleaseDC(graphics->hwnd, graphics->hdc);
2401
2402 LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
2403 list_remove(&cont->entry);
2404 delete_container(cont);
2405 }
2406
2407 GdipDeleteRegion(graphics->clip);
2408 GdipFree(graphics);
2409
2410 return Ok;
2411 }
2412
2413 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
2414 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2415 {
2416 GpStatus status;
2417 GpPath *path;
2418
2419 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2420 width, height, startAngle, sweepAngle);
2421
2422 if(!graphics || !pen || width <= 0 || height <= 0)
2423 return InvalidParameter;
2424
2425 if(graphics->busy)
2426 return ObjectBusy;
2427
2428 status = GdipCreatePath(FillModeAlternate, &path);
2429 if (status != Ok) return status;
2430
2431 status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
2432 if (status == Ok)
2433 status = GdipDrawPath(graphics, pen, path);
2434
2435 GdipDeletePath(path);
2436 return status;
2437 }
2438
2439 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
2440 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2441 {
2442 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2443 width, height, startAngle, sweepAngle);
2444
2445 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2446 }
2447
2448 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
2449 REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
2450 {
2451 GpPointF pt[4];
2452
2453 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
2454 x2, y2, x3, y3, x4, y4);
2455
2456 if(!graphics || !pen)
2457 return InvalidParameter;
2458
2459 if(graphics->busy)
2460 return ObjectBusy;
2461
2462 pt[0].X = x1;
2463 pt[0].Y = y1;
2464 pt[1].X = x2;
2465 pt[1].Y = y2;
2466 pt[2].X = x3;
2467 pt[2].Y = y3;
2468 pt[3].X = x4;
2469 pt[3].Y = y4;
2470 return GdipDrawBeziers(graphics, pen, pt, 4);
2471 }
2472
2473 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
2474 INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
2475 {
2476 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
2477 x2, y2, x3, y3, x4, y4);
2478
2479 return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
2480 }
2481
2482 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
2483 GDIPCONST GpPointF *points, INT count)
2484 {
2485 GpStatus status;
2486 GpPath *path;
2487
2488 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2489
2490 if(!graphics || !pen || !points || (count <= 0))
2491 return InvalidParameter;
2492
2493 if(graphics->busy)
2494 return ObjectBusy;
2495
2496 status = GdipCreatePath(FillModeAlternate, &path);
2497 if (status != Ok) return status;
2498
2499 status = GdipAddPathBeziers(path, points, count);
2500 if (status == Ok)
2501 status = GdipDrawPath(graphics, pen, path);
2502
2503 GdipDeletePath(path);
2504 return status;
2505 }
2506
2507 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
2508 GDIPCONST GpPoint *points, INT count)
2509 {
2510 GpPointF *pts;
2511 GpStatus ret;
2512 INT i;
2513
2514 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2515
2516 if(!graphics || !pen || !points || (count <= 0))
2517 return InvalidParameter;
2518
2519 if(graphics->busy)
2520 return ObjectBusy;
2521
2522 pts = GdipAlloc(sizeof(GpPointF) * count);
2523 if(!pts)
2524 return OutOfMemory;
2525
2526 for(i = 0; i < count; i++){
2527 pts[i].X = (REAL)points[i].X;
2528 pts[i].Y = (REAL)points[i].Y;
2529 }
2530
2531 ret = GdipDrawBeziers(graphics,pen,pts,count);
2532
2533 GdipFree(pts);
2534
2535 return ret;
2536 }
2537
2538 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
2539 GDIPCONST GpPointF *points, INT count)
2540 {
2541 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2542
2543 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
2544 }
2545
2546 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
2547 GDIPCONST GpPoint *points, INT count)
2548 {
2549 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2550
2551 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
2552 }
2553
2554 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
2555 GDIPCONST GpPointF *points, INT count, REAL tension)
2556 {
2557 GpPath *path;
2558 GpStatus status;
2559
2560 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2561
2562 if(!graphics || !pen || !points || count <= 0)
2563 return InvalidParameter;
2564
2565 if(graphics->busy)
2566 return ObjectBusy;
2567
2568 status = GdipCreatePath(FillModeAlternate, &path);
2569 if (status != Ok) return status;
2570
2571 status = GdipAddPathClosedCurve2(path, points, count, tension);
2572 if (status == Ok)
2573 status = GdipDrawPath(graphics, pen, path);
2574
2575 GdipDeletePath(path);
2576
2577 return status;
2578 }
2579
2580 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
2581 GDIPCONST GpPoint *points, INT count, REAL tension)
2582 {
2583 GpPointF *ptf;
2584 GpStatus stat;
2585 INT i;
2586
2587 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2588
2589 if(!points || count <= 0)
2590 return InvalidParameter;
2591
2592 ptf = GdipAlloc(sizeof(GpPointF)*count);
2593 if(!ptf)
2594 return OutOfMemory;
2595
2596 for(i = 0; i < count; i++){
2597 ptf[i].X = (REAL)points[i].X;
2598 ptf[i].Y = (REAL)points[i].Y;
2599 }
2600
2601 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
2602
2603 GdipFree(ptf);
2604
2605 return stat;
2606 }
2607
2608 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
2609 GDIPCONST GpPointF *points, INT count)
2610 {
2611 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2612
2613 return GdipDrawCurve2(graphics,pen,points,count,1.0);
2614 }
2615
2616 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
2617 GDIPCONST GpPoint *points, INT count)
2618 {
2619 GpPointF *pointsF;
2620 GpStatus ret;
2621 INT i;
2622
2623 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2624
2625 if(!points)
2626 return InvalidParameter;
2627
2628 pointsF = GdipAlloc(sizeof(GpPointF)*count);
2629 if(!pointsF)
2630 return OutOfMemory;
2631
2632 for(i = 0; i < count; i++){
2633 pointsF[i].X = (REAL)points[i].X;
2634 pointsF[i].Y = (REAL)points[i].Y;
2635 }
2636
2637 ret = GdipDrawCurve(graphics,pen,pointsF,count);
2638 GdipFree(pointsF);
2639
2640 return ret;
2641 }
2642
2643 /* Approximates cardinal spline with Bezier curves. */
2644 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
2645 GDIPCONST GpPointF *points, INT count, REAL tension)
2646 {
2647 GpPath *path;
2648 GpStatus status;
2649
2650 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2651
2652 if(!graphics || !pen)
2653 return InvalidParameter;
2654
2655 if(graphics->busy)
2656 return ObjectBusy;
2657
2658 if(count < 2)
2659 return InvalidParameter;
2660
2661 status = GdipCreatePath(FillModeAlternate, &path);
2662 if (status != Ok) return status;
2663
2664 status = GdipAddPathCurve2(path, points, count, tension);
2665 if (status == Ok)
2666 status = GdipDrawPath(graphics, pen, path);
2667
2668 GdipDeletePath(path);
2669 return status;
2670 }
2671
2672 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
2673 GDIPCONST GpPoint *points, INT count, REAL tension)
2674 {
2675 GpPointF *pointsF;
2676 GpStatus ret;
2677 INT i;
2678
2679 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
2680
2681 if(!points)
2682 return InvalidParameter;
2683
2684 pointsF = GdipAlloc(sizeof(GpPointF)*count);
2685 if(!pointsF)
2686 return OutOfMemory;
2687
2688 for(i = 0; i < count; i++){
2689 pointsF[i].X = (REAL)points[i].X;
2690 pointsF[i].Y = (REAL)points[i].Y;
2691 }
2692
2693 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
2694 GdipFree(pointsF);
2695
2696 return ret;
2697 }
2698
2699 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
2700 GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
2701 REAL tension)
2702 {
2703 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2704
2705 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2706 return InvalidParameter;
2707 }
2708
2709 return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
2710 }
2711
2712 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
2713 GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
2714 REAL tension)
2715 {
2716 TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
2717
2718 if(count < 0){
2719 return OutOfMemory;
2720 }
2721
2722 if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
2723 return InvalidParameter;
2724 }
2725
2726 return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
2727 }
2728
2729 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
2730 REAL y, REAL width, REAL height)
2731 {
2732 GpPath *path;
2733 GpStatus status;
2734
2735 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2736
2737 if(!graphics || !pen)
2738 return InvalidParameter;
2739
2740 if(graphics->busy)
2741 return ObjectBusy;
2742
2743 status = GdipCreatePath(FillModeAlternate, &path);
2744 if (status != Ok) return status;
2745
2746 status = GdipAddPathEllipse(path, x, y, width, height);
2747 if (status == Ok)
2748 status = GdipDrawPath(graphics, pen, path);
2749
2750 GdipDeletePath(path);
2751 return status;
2752 }
2753
2754 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
2755 INT y, INT width, INT height)
2756 {
2757 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2758
2759 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2760 }
2761
2762
2763 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
2764 {
2765 UINT width, height;
2766
2767 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
2768
2769 if(!graphics || !image)
2770 return InvalidParameter;
2771
2772 GdipGetImageWidth(image, &width);
2773 GdipGetImageHeight(image, &height);
2774
2775 return GdipDrawImagePointRect(graphics, image, x, y,
2776 0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
2777 }
2778
2779 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
2780 INT y)
2781 {
2782 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
2783
2784 return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
2785 }
2786
2787 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
2788 REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
2789 GpUnit srcUnit)
2790 {
2791 GpPointF points[3];
2792 REAL scale_x, scale_y, width, height;
2793
2794 TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2795
2796 if (!graphics || !image) return InvalidParameter;
2797
2798 scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
2799 scale_x *= graphics->xres / image->xres;
2800 scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
2801 scale_y *= graphics->yres / image->yres;
2802 width = srcwidth * scale_x;
2803 height = srcheight * scale_y;
2804
2805 points[0].X = points[2].X = x;
2806 points[0].Y = points[1].Y = y;
2807 points[1].X = x + width;
2808 points[2].Y = y + height;
2809
2810 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2811 srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
2812 }
2813
2814 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
2815 INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
2816 GpUnit srcUnit)
2817 {
2818 return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
2819 }
2820
2821 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
2822 GDIPCONST GpPointF *dstpoints, INT count)
2823 {
2824 UINT width, height;
2825
2826 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2827
2828 if(!image)
2829 return InvalidParameter;
2830
2831 GdipGetImageWidth(image, &width);
2832 GdipGetImageHeight(image, &height);
2833
2834 return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
2835 width, height, UnitPixel, NULL, NULL, NULL);
2836 }
2837
2838 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
2839 GDIPCONST GpPoint *dstpoints, INT count)
2840 {
2841 GpPointF ptf[3];
2842
2843 TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
2844
2845 if (count != 3 || !dstpoints)
2846 return InvalidParameter;
2847
2848 ptf[0].X = (REAL)dstpoints[0].X;
2849 ptf[0].Y = (REAL)dstpoints[0].Y;
2850 ptf[1].X = (REAL)dstpoints[1].X;
2851 ptf[1].Y = (REAL)dstpoints[1].Y;
2852 ptf[2].X = (REAL)dstpoints[2].X;
2853 ptf[2].Y = (REAL)dstpoints[2].Y;
2854
2855 return GdipDrawImagePoints(graphics, image, ptf, count);
2856 }
2857
2858 static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
2859 unsigned int dataSize, const unsigned char *pStr, void *userdata)
2860 {
2861 GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
2862 return TRUE;
2863 }
2864
2865 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
2866 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2867 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2868 DrawImageAbort callback, VOID * callbackData)
2869 {
2870 GpPointF ptf[4];
2871 POINT pti[4];
2872 GpStatus stat;
2873
2874 TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
2875 count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2876 callbackData);
2877
2878 if (count > 3)
2879 return NotImplemented;
2880
2881 if(!graphics || !image || !points || count != 3)
2882 return InvalidParameter;
2883
2884 TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
2885 debugstr_pointf(&points[2]));
2886
2887 memcpy(ptf, points, 3 * sizeof(GpPointF));
2888 ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
2889 ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2890 if (!srcwidth || !srcheight || ptf[3].X == ptf[0].X || ptf[3].Y == ptf[0].Y)
2891 return Ok;
2892 transform_and_round_points(graphics, pti, ptf, 4);
2893
2894 TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
2895 wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
2896
2897 srcx = units_to_pixels(srcx, srcUnit, image->xres);
2898 srcy = units_to_pixels(srcy, srcUnit, image->yres);
2899 srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
2900 srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
2901 TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
2902
2903 if (image->picture)
2904 {
2905 if (!graphics->hdc)
2906 {
2907 FIXME("graphics object has no HDC\n");
2908 }
2909
2910 if(IPicture_Render(image->picture, graphics->hdc,
2911 pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
2912 srcx, srcy, srcwidth, srcheight, NULL) != S_OK)
2913 {
2914 if(callback)
2915 callback(callbackData);
2916 return GenericError;
2917 }
2918 }
2919 else if (image->type == ImageTypeBitmap)
2920 {
2921 GpBitmap* bitmap = (GpBitmap*)image;
2922 int use_software=0;
2923
2924 TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
2925 graphics->xres, graphics->yres,
2926 graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
2927 graphics->scale, image->xres, image->yres, bitmap->format,
2928 imageAttributes ? imageAttributes->outside_color : 0);
2929
2930 if (imageAttributes || graphics->alpha_hdc ||
2931 (graphics->image && graphics->image->type == ImageTypeBitmap) ||
2932 ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
2933 ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
2934 srcx < 0 || srcy < 0 ||
2935 srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
2936 use_software = 1;
2937
2938 if (use_software)
2939 {
2940 RECT dst_area;
2941 GpRect src_area;
2942 int i, x, y, src_stride, dst_stride;
2943 GpMatrix dst_to_src;
2944 REAL m11, m12, m21, m22, mdx, mdy;
2945 LPBYTE src_data, dst_data;
2946 BitmapData lockeddata;
2947 InterpolationMode interpolation = graphics->interpolation;
2948 PixelOffsetMode offset_mode = graphics->pixeloffset;
2949 GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
2950 REAL x_dx, x_dy, y_dx, y_dy;
2951 static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
2952
2953 if (!imageAttributes)
2954 imageAttributes = &defaultImageAttributes;
2955
2956 dst_area.left = dst_area.right = pti[0].x;
2957 dst_area.top = dst_area.bottom = pti[0].y;
2958 for (i=1; i<4; i++)
2959 {
2960 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2961 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2962 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2963 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2964 }
2965
2966 TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
2967
2968 m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2969 m21 = (ptf[2].X - ptf[0].X) / srcheight;
2970 mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2971 m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2972 m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2973 mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
2974
2975 GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
2976
2977 stat = GdipInvertMatrix(&dst_to_src);
2978 if (stat != Ok) return stat;
2979
2980 dst_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
2981 if (!dst_data) return OutOfMemory;
2982
2983 dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
2984
2985 get_bitmap_sample_size(interpolation, imageAttributes->wrap,
2986 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
2987
2988 TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
2989
2990 src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
2991 if (!src_data)
2992 {
2993 GdipFree(dst_data);
2994 return OutOfMemory;
2995 }
2996 src_stride = sizeof(ARGB) * src_area.Width;
2997
2998 /* Read the bits we need from the source bitmap into an ARGB buffer. */
2999 lockeddata.Width = src_area.Width;
3000 lockeddata.Height = src_area.Height;
3001 lockeddata.Stride = src_stride;
3002 lockeddata.PixelFormat = PixelFormat32bppARGB;
3003 lockeddata.Scan0 = src_data;
3004
3005 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
3006 PixelFormat32bppARGB, &lockeddata);
3007
3008 if (stat == Ok)
3009 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
3010
3011 if (stat != Ok)
3012 {
3013 if (src_data != dst_data)
3014 GdipFree(src_data);
3015 GdipFree(dst_data);
3016 return stat;
3017 }
3018
3019 apply_image_attributes(imageAttributes, src_data,
3020 src_area.Width, src_area.Height,
3021 src_stride, ColorAdjustTypeBitmap);
3022
3023 /* Transform the bits as needed to the destination. */
3024 GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
3025
3026 x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
3027 x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
3028 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
3029 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
3030
3031 for (x=dst_area.left; x<dst_area.right; x++)
3032 {
3033 for (y=dst_area.top; y<dst_area.bottom; y++)
3034 {
3035 GpPointF src_pointf;
3036 ARGB *dst_color;
3037
3038 src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
3039 src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
3040
3041 dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
3042
3043 if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
3044 *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3045 imageAttributes, interpolation, offset_mode);
3046 else
3047 *dst_color = 0;
3048 }
3049 }
3050
3051 GdipFree(src_data);
3052
3053 stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
3054 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
3055
3056 GdipFree(dst_data);
3057
3058 return stat;
3059 }
3060 else
3061 {
3062 HDC hdc;
3063 int temp_hdc=0, temp_bitmap=0;
3064 HBITMAP hbitmap, old_hbm=NULL;
3065
3066 if (!(bitmap->format == PixelFormat16bppRGB555 ||
3067 bitmap->format == PixelFormat24bppRGB ||
3068 bitmap->format == PixelFormat32bppRGB ||
3069 bitmap->format == PixelFormat32bppPARGB))
3070 {
3071 BITMAPINFOHEADER bih;
3072 BYTE *temp_bits;
3073 PixelFormat dst_format;
3074
3075 /* we can't draw a bitmap of this format directly */
3076 hdc = CreateCompatibleDC(0);
3077 temp_hdc = 1;
3078 temp_bitmap = 1;
3079
3080 bih.biSize = sizeof(BITMAPINFOHEADER);
3081 bih.biWidth = bitmap->width;
3082 bih.biHeight = -bitmap->height;
3083 bih.biPlanes = 1;
3084 bih.biBitCount = 32;
3085 bih.biCompression = BI_RGB;
3086 bih.biSizeImage = 0;
3087 bih.biXPelsPerMeter = 0;
3088 bih.biYPelsPerMeter = 0;
3089 bih.biClrUsed = 0;
3090 bih.biClrImportant = 0;
3091
3092 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
3093 (void**)&temp_bits, NULL, 0);
3094
3095 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3096 dst_format = PixelFormat32bppPARGB;
3097 else
3098 dst_format = PixelFormat32bppRGB;
3099
3100 convert_pixels(bitmap->width, bitmap->height,
3101 bitmap->width*4, temp_bits, dst_format,
3102 bitmap->stride, bitmap->bits, bitmap->format,
3103 bitmap->image.palette);
3104 }
3105 else
3106 {
3107 if (bitmap->hbitmap)
3108 hbitmap = bitmap->hbitmap;
3109 else
3110 {
3111 GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
3112 temp_bitmap = 1;
3113 }
3114
3115 hdc = bitmap->hdc;
3116 temp_hdc = (hdc == 0);
3117 }
3118
3119 if (temp_hdc)
3120 {
3121 if (!hdc) hdc = CreateCompatibleDC(0);
3122 old_hbm = SelectObject(hdc, hbitmap);
3123 }
3124
3125 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3126 {
3127 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
3128 hdc, srcx, srcy, srcwidth, srcheight);
3129 }
3130 else
3131 {
3132 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
3133 hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
3134 }
3135
3136 if (temp_hdc)
3137 {
3138 SelectObject(hdc, old_hbm);
3139 DeleteDC(hdc);
3140 }
3141
3142 if (temp_bitmap)
3143 DeleteObject(hbitmap);
3144 }
3145 }
3146 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
3147 {
3148 GpRectF rc;
3149
3150 rc.X = srcx;
3151 rc.Y = srcy;
3152 rc.Width = srcwidth;
3153 rc.Height = srcheight;
3154
3155 return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
3156 points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
3157 }
3158 else
3159 {
3160 WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
3161 return InvalidParameter;
3162 }
3163
3164 return Ok;
3165 }
3166
3167 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
3168 GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
3169 INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3170 DrawImageAbort callback, VOID * callbackData)
3171 {
3172 GpPointF pointsF[3];
3173 INT i;
3174
3175 TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
3176 srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
3177 callbackData);
3178
3179 if(!points || count!=3)
3180 return InvalidParameter;
3181
3182 for(i = 0; i < count; i++){
3183 pointsF[i].X = (REAL)points[i].X;
3184 pointsF[i].Y = (REAL)points[i].Y;
3185 }
3186
3187 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
3188 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
3189 callback, callbackData);
3190 }
3191
3192 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
3193 REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
3194 REAL srcwidth, REAL srcheight, GpUnit srcUnit,
3195 GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
3196 VOID * callbackData)
3197 {
3198 GpPointF points[3];
3199
3200 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
3201 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3202 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3203
3204 points[0].X = dstx;
3205 points[0].Y = dsty;
3206 points[1].X = dstx + dstwidth;
3207 points[1].Y = dsty;
3208 points[2].X = dstx;
3209 points[2].Y = dsty + dstheight;
3210
3211 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3212 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3213 }
3214
3215 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
3216 INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
3217 INT srcwidth, INT srcheight, GpUnit srcUnit,
3218 GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
3219 VOID * callbackData)
3220 {
3221 GpPointF points[3];
3222
3223 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
3224 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3225 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3226
3227 points[0].X = dstx;
3228 points[0].Y = dsty;
3229 points[1].X = dstx + dstwidth;
3230 points[1].Y = dsty;
3231 points[2].X = dstx;
3232 points[2].Y = dsty + dstheight;
3233
3234 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3235 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3236 }
3237
3238 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
3239 REAL x, REAL y, REAL width, REAL height)
3240 {
3241 RectF bounds;
3242 GpUnit unit;
3243 GpStatus ret;
3244
3245 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
3246
3247 if(!graphics || !image)
3248 return InvalidParameter;
3249
3250 ret = GdipGetImageBounds(image, &bounds, &unit);
3251 if(ret != Ok)
3252 return ret;
3253
3254 return GdipDrawImageRectRect(graphics, image, x, y, width, height,
3255 bounds.X, bounds.Y, bounds.Width, bounds.Height,
3256 unit, NULL, NULL, NULL);
3257 }
3258
3259 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
3260 INT x, INT y, INT width, INT height)
3261 {
3262 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
3263
3264 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
3265 }
3266
3267 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
3268 REAL y1, REAL x2, REAL y2)
3269 {
3270 GpPointF pt[2];
3271
3272 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
3273
3274 pt[0].X = x1;
3275 pt[0].Y = y1;
3276 pt[1].X = x2;
3277 pt[1].Y = y2;
3278 return GdipDrawLines(graphics, pen, pt, 2);
3279 }
3280
3281 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
3282 INT y1, INT x2, INT y2)
3283 {
3284 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
3285
3286 return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
3287 }
3288
3289 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
3290 GpPointF *points, INT count)
3291 {
3292 GpStatus status;
3293 GpPath *path;
3294
3295 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3296
3297 if(!pen || !graphics || (count < 2))
3298 return InvalidParameter;
3299
3300 if(graphics->busy)
3301 return ObjectBusy;
3302
3303 status = GdipCreatePath(FillModeAlternate, &path);
3304 if (status != Ok) return status;
3305
3306 status = GdipAddPathLine2(path, points, count);
3307 if (status == Ok)
3308 status = GdipDrawPath(graphics, pen, path);
3309
3310 GdipDeletePath(path);
3311 return status;
3312 }
3313
3314 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
3315 GpPoint *points, INT count)
3316 {
3317 GpStatus retval;
3318 GpPointF *ptf;
3319 int i;
3320
3321 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3322
3323 ptf = GdipAlloc(count * sizeof(GpPointF));
3324 if(!ptf) return OutOfMemory;
3325
3326 for(i = 0; i < count; i ++){
3327 ptf[i].X = (REAL) points[i].X;
3328 ptf[i].Y = (REAL) points[i].Y;
3329 }
3330
3331 retval = GdipDrawLines(graphics, pen, ptf, count);
3332
3333 GdipFree(ptf);
3334 return retval;
3335 }
3336
3337 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3338 {
3339 INT save_state;
3340 GpStatus retval;
3341
3342 TRACE("(%p, %p, %p)\n", graphics, pen, path);
3343
3344 if(!pen || !graphics)
3345 return InvalidParameter;
3346
3347 if(graphics->busy)
3348 return ObjectBusy;
3349
3350 if (!graphics->hdc)
3351 {
3352 FIXME("graphics object has no HDC\n");
3353 return Ok;
3354 }
3355
3356 save_state = prepare_dc(graphics, pen);
3357
3358 retval = draw_poly(graphics, pen, path->pathdata.Points,
3359 path->pathdata.Types, path->pathdata.Count, TRUE);
3360
3361 restore_dc(graphics, save_state);
3362
3363 return retval;
3364 }
3365
3366 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
3367 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3368 {
3369 GpStatus status;
3370 GpPath *path;
3371
3372 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
3373 width, height, startAngle, sweepAngle);
3374
3375 if(!graphics || !pen)
3376 return InvalidParameter;
3377
3378 if(graphics->busy)
3379 return ObjectBusy;
3380
3381 status = GdipCreatePath(FillModeAlternate, &path);
3382 if (status != Ok) return status;
3383
3384 status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
3385 if (status == Ok)
3386 status = GdipDrawPath(graphics, pen, path);
3387
3388 GdipDeletePath(path);
3389 return status;
3390 }
3391
3392 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
3393 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3394 {
3395 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
3396 width, height, startAngle, sweepAngle);
3397
3398 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3399 }
3400
3401 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
3402 REAL y, REAL width, REAL height)
3403 {
3404 GpStatus status;
3405 GpPath *path;
3406
3407 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
3408
3409 if(!pen || !graphics)
3410 return InvalidParameter;
3411
3412 if(graphics->busy)
3413 return ObjectBusy;
3414
3415 status = GdipCreatePath(FillModeAlternate, &path);
3416 if (status != Ok) return status;
3417
3418 status = GdipAddPathRectangle(path, x, y, width, height);
3419 if (status == Ok)
3420 status = GdipDrawPath(graphics, pen, path);
3421
3422 GdipDeletePath(path);
3423 return status;
3424 }
3425
3426 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
3427 INT y, INT width, INT height)
3428 {
3429 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
3430
3431 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3432 }
3433
3434 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
3435 GDIPCONST GpRectF* rects, INT count)
3436 {
3437 GpStatus status;
3438 GpPath *path;
3439
3440 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
3441
3442 if(!graphics || !pen || !rects || count < 1)
3443 return InvalidParameter;
3444
3445 if(graphics->busy)
3446 return ObjectBusy;
3447
3448 status = GdipCreatePath(FillModeAlternate, &path);
3449 if (status != Ok) return status;
3450
3451 status = GdipAddPathRectangles(path, rects, count);
3452 if (status == Ok)
3453 status = GdipDrawPath(graphics, pen, path);
3454
3455 GdipDeletePath(path);
3456 return status;
3457 }
3458
3459 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
3460 GDIPCONST GpRect* rects, INT count)
3461 {
3462 GpRectF *rectsF;
3463 GpStatus ret;
3464 INT i;
3465
3466 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
3467
3468 if(!rects || count<=0)
3469 return InvalidParameter;
3470
3471 rectsF = GdipAlloc(sizeof(GpRectF) * count);
3472 if(!rectsF)
3473 return OutOfMemory;
3474
3475 for(i = 0;i < count;i++){
3476 rectsF[i].X = (REAL)rects[i].X;
3477 rectsF[i].Y = (REAL)rects[i].Y;
3478 rectsF[i].Width = (REAL)rects[i].Width;
3479 rectsF[i].Height = (REAL)rects[i].Height;
3480 }
3481
3482 ret = GdipDrawRectangles(graphics, pen, rectsF, count);
3483 GdipFree(rectsF);
3484
3485 return ret;
3486 }
3487
3488 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
3489 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
3490 {
3491 GpPath *path;
3492 GpStatus status;
3493
3494 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
3495 count, tension, fill);
3496
3497 if(!graphics || !brush || !points)
3498 return InvalidParameter;
3499
3500 if(graphics->busy)
3501 return ObjectBusy;
3502
3503 if(count == 1) /* Do nothing */
3504 return Ok;
3505
3506 status = GdipCreatePath(fill, &path);
3507 if (status != Ok) return status;
3508
3509 status = GdipAddPathClosedCurve2(path, points, count, tension);
3510 if (status == Ok)
3511 status = GdipFillPath(graphics, brush, path);
3512
3513 GdipDeletePath(path);
3514 return status;
3515 }
3516
3517 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
3518 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
3519 {
3520 GpPointF *ptf;
3521 GpStatus stat;
3522 INT i;
3523
3524 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
3525 count, tension, fill);
3526
3527 if(!points || count == 0)
3528 return InvalidParameter;
3529
3530 if(count == 1) /* Do nothing */
3531 return Ok;
3532
3533 ptf = GdipAlloc(sizeof(GpPointF)*count);
3534 if(!ptf)
3535 return OutOfMemory;
3536
3537 for(i = 0;i < count;i++){
3538 ptf[i].X = (REAL)points[i].X;
3539 ptf[i].Y = (REAL)points[i].Y;
3540 }
3541
3542 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
3543
3544 GdipFree(ptf);
3545
3546 return stat;
3547 }
3548
3549 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
3550 GDIPCONST GpPointF *points, INT count)
3551 {
3552 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3553 return GdipFillClosedCurve2(graphics, brush, points, count,
3554 0.5f, FillModeAlternate);
3555 }
3556
3557 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
3558 GDIPCONST GpPoint *points, INT count)
3559 {
3560 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3561 return GdipFillClosedCurve2I(graphics, brush, points, count,
3562 0.5f, FillModeAlternate);
3563 }
3564
3565 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
3566 REAL y, REAL width, REAL height)
3567 {
3568 GpStatus stat;
3569 GpPath *path;
3570
3571 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3572
3573 if(!graphics || !brush)
3574 return InvalidParameter;
3575
3576 if(graphics->busy)
3577 return ObjectBusy;
3578
3579 stat = GdipCreatePath(FillModeAlternate, &path);
3580
3581 if (stat == Ok)
3582 {
3583 stat = GdipAddPathEllipse(path, x, y, width, height);
3584
3585 if (stat == Ok)
3586 stat = GdipFillPath(graphics, brush, path);
3587
3588 GdipDeletePath(path);
3589 }
3590
3591 return stat;
3592 }
3593
3594 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
3595 INT y, INT width, INT height)
3596 {
3597 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3598
3599 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3600 }
3601
3602 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3603 {
3604 INT save_state;
3605 GpStatus retval;
3606
3607 if(!graphics->hdc || !brush_can_fill_path(brush))
3608 return NotImplemented;
3609
3610 save_state = SaveDC(graphics->hdc);
3611 EndPath(graphics->hdc);
3612 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
3613 : WINDING));
3614
3615 BeginPath(graphics->hdc);
3616 retval = draw_poly(graphics, NULL, path->pathdata.Points,
3617 path->pathdata.Types, path->pathdata.Count, FALSE);
3618
3619 if(retval != Ok)
3620 goto end;
3621
3622 EndPath(graphics->hdc);
3623 brush_fill_path(graphics, brush);
3624
3625 retval = Ok;
3626
3627 end:
3628 RestoreDC(graphics->hdc, save_state);
3629
3630 return retval;
3631 }
3632
3633 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3634 {
3635 GpStatus stat;
3636 GpRegion *rgn;
3637
3638 if (!brush_can_fill_pixels(brush))
3639 return NotImplemented;
3640
3641 /* FIXME: This could probably be done more efficiently without regions. */
3642
3643 stat = GdipCreateRegionPath(path, &rgn);
3644
3645 if (stat == Ok)
3646 {
3647 stat = GdipFillRegion(graphics, brush, rgn);
3648
3649 GdipDeleteRegion(rgn);
3650 }
3651
3652 return stat;
3653 }
3654
3655 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3656 {
3657 GpStatus stat = NotImplemented;
3658
3659 TRACE("(%p, %p, %p)\n", graphics, brush, path);
3660
3661 if(!brush || !graphics || !path)
3662 return InvalidParameter;
3663
3664 if(graphics->busy)
3665 return ObjectBusy;
3666
3667 if (!graphics->image && !graphics->alpha_hdc)
3668 stat = GDI32_GdipFillPath(graphics, brush, path);
3669
3670 if (stat == NotImplemented)
3671 stat = SOFTWARE_GdipFillPath(graphics, brush, path);
3672
3673 if (stat == NotImplemented)
3674 {
3675 FIXME("Not implemented for brushtype %i\n", brush->bt);
3676 stat = Ok;
3677 }
3678
3679 return stat;
3680 }
3681
3682 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
3683 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3684 {
3685 GpStatus stat;
3686 GpPath *path;
3687
3688 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
3689 graphics, brush, x, y, width, height, startAngle, sweepAngle);
3690
3691 if(!graphics || !brush)
3692 return InvalidParameter;
3693
3694 if(graphics->busy)
3695 return ObjectBusy;
3696
3697 stat = GdipCreatePath(FillModeAlternate, &path);
3698
3699 if (stat == Ok)
3700 {
3701 stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
3702
3703 if (stat == Ok)
3704 stat = GdipFillPath(graphics, brush, path);
3705
3706 GdipDeletePath(path);
3707 }
3708
3709 return stat;
3710 }
3711
3712 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
3713 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3714 {
3715 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
3716 graphics, brush, x, y, width, height, startAngle, sweepAngle);
3717
3718 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3719 }
3720
3721 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
3722 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
3723 {
3724 GpStatus stat;
3725 GpPath *path;
3726
3727 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3728
3729 if(!graphics || !brush || !points || !count)
3730 return InvalidParameter;
3731
3732 if(graphics->busy)
3733 return ObjectBusy;
3734
3735 stat = GdipCreatePath(fillMode, &path);
3736
3737 if (stat == Ok)
3738 {
3739 stat = GdipAddPathPolygon(path, points, count);
3740
3741 if (stat == Ok)
3742 stat = GdipFillPath(graphics, brush, path);
3743
3744 GdipDeletePath(path);
3745 }
3746
3747 return stat;
3748 }
3749
3750 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
3751 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
3752 {
3753 GpStatus stat;
3754 GpPath *path;
3755
3756 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3757
3758 if(!graphics || !brush || !points || !count)
3759 return InvalidParameter;
3760
3761 if(graphics->busy)
3762 return ObjectBusy;
3763
3764 stat = GdipCreatePath(fillMode, &path);
3765
3766 if (stat == Ok)
3767 {
3768 stat = GdipAddPathPolygonI(path, points, count);
3769
3770 if (stat == Ok)
3771 stat = GdipFillPath(graphics, brush, path);
3772
3773 GdipDeletePath(path);
3774 }
3775
3776 return stat;
3777 }
3778
3779 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
3780 GDIPCONST GpPointF *points, INT count)
3781 {
3782 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3783
3784 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
3785 }
3786
3787 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
3788 GDIPCONST GpPoint *points, INT count)
3789 {
3790 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3791
3792 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
3793 }
3794
3795 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
3796 REAL x, REAL y, REAL width, REAL height)
3797 {
3798 GpStatus stat;
3799 GpPath *path;
3800
3801 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3802
3803 if(!graphics || !brush)
3804 return InvalidParameter;
3805
3806 if(graphics->busy)
3807 return ObjectBusy;
3808
3809 stat = GdipCreatePath(FillModeAlternate, &path);
3810
3811 if (stat == Ok)
3812 {
3813 stat = GdipAddPathRectangle(path, x, y, width, height);
3814
3815 if (stat == Ok)
3816 stat = GdipFillPath(graphics, brush, path);
3817
3818 GdipDeletePath(path);
3819 }
3820
3821 return stat;
3822 }
3823
3824 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
3825 INT x, INT y, INT width, INT height)
3826 {
3827 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3828
3829 return GdipFillRectangle(graphics, brush, x, y, width, height);
3830 }
3831
3832 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3833 INT count)
3834 {
3835 GpStatus status;
3836 GpPath *path;
3837
3838 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3839
3840 if(!rects)
3841 return InvalidParameter;
3842
3843 status = GdipCreatePath(FillModeAlternate, &path);
3844 if (status != Ok) return status;
3845
3846 status = GdipAddPathRectangles(path, rects, count);
3847 if (status == Ok)
3848 status = GdipFillPath(graphics, brush, path);
3849
3850 GdipDeletePath(path);
3851 return status;
3852 }
3853
3854 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3855 INT count)
3856 {
3857 GpRectF *rectsF;
3858 GpStatus ret;
3859 INT i;
3860
3861 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3862
3863 if(!rects || count <= 0)
3864 return InvalidParameter;
3865
3866 rectsF = GdipAlloc(sizeof(GpRectF)*count);
3867 if(!rectsF)
3868 return OutOfMemory;
3869
3870 for(i = 0; i < count; i++){
3871 rectsF[i].X = (REAL)rects[i].X;
3872 rectsF[i].Y = (REAL)rects[i].Y;
3873 rectsF[i].X = (REAL)rects[i].Width;
3874 rectsF[i].Height = (REAL)rects[i].Height;
3875 }
3876
3877 ret = GdipFillRectangles(graphics,brush,rectsF,count);
3878 GdipFree(rectsF);
3879
3880 return ret;
3881 }
3882
3883 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3884 GpRegion* region)
3885 {
3886 INT save_state;
3887 GpStatus status;
3888 HRGN hrgn;
3889 RECT rc;
3890
3891 if(!graphics->hdc || !brush_can_fill_path(brush))
3892 return NotImplemented;
3893
3894 status = GdipGetRegionHRgn(region, graphics, &hrgn);
3895 if(status != Ok)
3896 return status;
3897
3898 save_state = SaveDC(graphics->hdc);
3899 EndPath(graphics->hdc);
3900
3901 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3902
3903 if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3904 {
3905 BeginPath(graphics->hdc);
3906 Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3907 EndPath(graphics->hdc);
3908
3909 brush_fill_path(graphics, brush);
3910 }
3911
3912 RestoreDC(graphics->hdc, save_state);
3913
3914 DeleteObject(hrgn);
3915
3916 return Ok;
3917 }
3918
3919 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
3920 GpRegion* region)
3921 {
3922 GpStatus stat;
3923 GpRegion *temp_region;
3924 GpMatrix world_to_device;
3925 GpRectF graphics_bounds;
3926 DWORD *pixel_data;
3927 HRGN hregion;
3928 RECT bound_rect;
3929 GpRect gp_bound_rect;
3930
3931 if (!brush_can_fill_pixels(brush))
3932 return NotImplemented;
3933
3934 stat = get_graphics_bounds(graphics, &graphics_bounds);
3935
3936 if (stat == Ok)
3937 stat = GdipCloneRegion(region, &temp_region);
3938
3939 if (stat == Ok)
3940 {
3941 stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
3942 CoordinateSpaceWorld, &world_to_device);
3943
3944 if (stat == Ok)
3945 stat = GdipTransformRegion(temp_region, &world_to_device);
3946
3947 if (stat == Ok)
3948 stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
3949
3950 if (stat == Ok)
3951 stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
3952
3953 GdipDeleteRegion(temp_region);
3954 }
3955
3956 if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
3957 {
3958 DeleteObject(hregion);
3959 return Ok;
3960 }
3961
3962 if (stat == Ok)
3963 {
3964 gp_bound_rect.X = bound_rect.left;
3965 gp_bound_rect.Y = bound_rect.top;
3966 gp_bound_rect.Width = bound_rect.right - bound_rect.left;
3967 gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
3968
3969 pixel_data = GdipAlloc(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
3970 if (!pixel_data)
3971 stat = OutOfMemory;
3972
3973 if (stat == Ok)
3974 {
3975 stat = brush_fill_pixels(graphics, brush, pixel_data,
3976 &gp_bound_rect, gp_bound_rect.Width);
3977
3978 if (stat == Ok)
3979 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
3980 gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
3981 gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion);
3982
3983 GdipFree(pixel_data);
3984 }
3985
3986 DeleteObject(hregion);
3987 }
3988
3989 return stat;
3990 }
3991
3992 /*****************************************************************************
3993 * GdipFillRegion [GDIPLUS.@]
3994 */
3995 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3996 GpRegion* region)
3997 {
3998 GpStatus stat = NotImplemented;
3999
4000 TRACE("(%p, %p, %p)\n", graphics, brush, region);
4001
4002 if (!(graphics && brush && region))
4003 return InvalidParameter;
4004
4005 if(graphics->busy)
4006 return ObjectBusy;
4007
4008 if (!graphics->image && !graphics->alpha_hdc)
4009 stat = GDI32_GdipFillRegion(graphics, brush, region);
4010
4011 if (stat == NotImplemented)
4012 stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
4013
4014 if (stat == NotImplemented)
4015 {
4016 FIXME("not implemented for brushtype %i\n", brush->bt);
4017 stat = Ok;
4018 }
4019
4020 return stat;
4021 }
4022
4023 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
4024 {
4025 TRACE("(%p,%u)\n", graphics, intention);
4026
4027 if(!graphics)
4028 return InvalidParameter;
4029
4030 if(graphics->busy)
4031 return ObjectBusy;
4032
4033 /* We have no internal operation queue, so there's no need to clear it. */
4034
4035 if (graphics->hdc)
4036 GdiFlush();
4037
4038 return Ok;
4039 }
4040
4041 /*****************************************************************************
4042 * GdipGetClipBounds [GDIPLUS.@]
4043 */
4044 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
4045 {
4046 GpStatus status;
4047 GpRegion *clip;
4048
4049 TRACE("(%p, %p)\n", graphics, rect);
4050
4051 if(!graphics)
4052 return InvalidParameter;
4053
4054 if(graphics->busy)
4055 return ObjectBusy;
4056
4057 status = GdipCreateRegion(&clip);
4058 if (status != Ok) return status;
4059
4060 status = GdipGetClip(graphics, clip);
4061 if (status == Ok)
4062 status = GdipGetRegionBounds(clip, graphics, rect);
4063
4064 GdipDeleteRegion(clip);
4065 return status;
4066 }
4067
4068 /*****************************************************************************
4069 * GdipGetClipBoundsI [GDIPLUS.@]
4070 */
4071 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
4072 {
4073 TRACE("(%p, %p)\n", graphics, rect);
4074
4075 if(!graphics)
4076 return InvalidParameter;
4077
4078 if(graphics->busy)
4079 return ObjectBusy;
4080
4081 return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
4082 }
4083
4084 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
4085 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
4086 CompositingMode *mode)
4087 {
4088 TRACE("(%p, %p)\n", graphics, mode);
4089
4090 if(!graphics || !mode)
4091 return InvalidParameter;
4092
4093 if(graphics->busy)
4094 return ObjectBusy;
4095
4096 *mode = graphics->compmode;
4097
4098 return Ok;
4099 }
4100
4101 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
4102 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
4103 CompositingQuality *quality)
4104 {
4105 TRACE("(%p, %p)\n", graphics, quality);
4106
4107 if(!graphics || !quality)
4108 return InvalidParameter;
4109
4110 if(graphics->busy)
4111 return ObjectBusy;
4112
4113 *quality = graphics->compqual;
4114
4115 return Ok;
4116 }
4117
4118 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
4119 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
4120 InterpolationMode *mode)
4121 {
4122 TRACE("(%p, %p)\n", graphics, mode);
4123
4124 if(!graphics || !mode)
4125 return InvalidParameter;
4126
4127 if(graphics->busy)
4128 return ObjectBusy;
4129
4130 *mode = graphics->interpolation;
4131
4132 return Ok;
4133 }
4134
4135 /* FIXME: Need to handle color depths less than 24bpp */
4136 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
4137 {
4138 FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
4139
4140 if(!graphics || !argb)
4141 return InvalidParameter;
4142
4143 if(graphics->busy)
4144 return ObjectBusy;
4145
4146 return Ok;
4147 }
4148
4149 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
4150 {
4151 TRACE("(%p, %p)\n", graphics, scale);
4152
4153 if(!graphics || !scale)
4154 return InvalidParameter;
4155
4156 if(graphics->busy)
4157 return ObjectBusy;
4158
4159 *scale = graphics->scale;
4160
4161 return Ok;
4162 }
4163
4164 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
4165 {
4166 TRACE("(%p, %p)\n", graphics, unit);
4167
4168 if(!graphics || !unit)
4169 return InvalidParameter;
4170
4171 if(graphics->busy)
4172 return ObjectBusy;
4173
4174 *unit = graphics->unit;
4175
4176 return Ok;
4177 }
4178
4179 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
4180 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4181 *mode)
4182 {
4183 TRACE("(%p, %p)\n", graphics, mode);
4184
4185 if(!graphics || !mode)
4186 return InvalidParameter;
4187
4188 if(graphics->busy)
4189 return ObjectBusy;
4190
4191 *mode = graphics->pixeloffset;
4192
4193 return Ok;
4194 }
4195
4196 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
4197 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
4198 {
4199 TRACE("(%p, %p)\n", graphics, mode);
4200
4201 if(!graphics || !mode)
4202 return InvalidParameter;
4203
4204 if(graphics->busy)
4205 return ObjectBusy;
4206
4207 *mode = graphics->smoothing;
4208
4209 return Ok;
4210 }
4211
4212 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
4213 {
4214 TRACE("(%p, %p)\n", graphics, contrast);
4215
4216 if(!graphics || !contrast)
4217 return InvalidParameter;
4218
4219 *contrast = graphics->textcontrast;
4220
4221 return Ok;
4222 }
4223
4224 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
4225 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
4226 TextRenderingHint *hint)
4227 {
4228 TRACE("(%p, %p)\n", graphics, hint);
4229
4230 if(!graphics || !hint)
4231 return InvalidParameter;
4232
4233 if(graphics->busy)
4234 return ObjectBusy;
4235
4236 *hint = graphics->texthint;
4237
4238 return Ok;
4239 }
4240
4241 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
4242 {
4243 GpRegion *clip_rgn;
4244 GpStatus stat;
4245
4246 TRACE("(%p, %p)\n", graphics, rect);
4247
4248 if(!graphics || !rect)
4249 return InvalidParameter;
4250
4251 if(graphics->busy)
4252 return ObjectBusy;
4253
4254 /* intersect window and graphics clipping regions */
4255 if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
4256 return stat;
4257
4258 if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
4259 goto cleanup;
4260
4261 /* get bounds of the region */
4262 stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
4263
4264 cleanup:
4265 GdipDeleteRegion(clip_rgn);
4266
4267 return stat;
4268 }
4269
4270 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
4271 {
4272 GpRectF rectf;
4273 GpStatus stat;
4274
4275 TRACE("(%p, %p)\n", graphics, rect);
4276
4277 if(!graphics || !rect)
4278 return InvalidParameter;
4279
4280 if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
4281 {
4282 rect->X = gdip_round(rectf.X);
4283 rect->Y = gdip_round(rectf.Y);
4284 rect->Width = gdip_round(rectf.Width);
4285 rect->Height = gdip_round(rectf.Height);
4286 }
4287
4288 return stat;
4289 }
4290
4291 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4292 {
4293 TRACE("(%p, %p)\n", graphics, matrix);
4294
4295 if(!graphics || !matrix)
4296 return InvalidParameter;
4297
4298 if(graphics->busy)
4299 return ObjectBusy;
4300
4301 *matrix = graphics->worldtrans;
4302 return Ok;
4303 }
4304
4305 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
4306 {
4307 GpSolidFill *brush;
4308 GpStatus stat;
4309 GpRectF wnd_rect;
4310
4311 TRACE("(%p, %x)\n", graphics, color);
4312
4313 if(!graphics)
4314 return InvalidParameter;
4315
4316 if(graphics->busy)
4317 return ObjectBusy;
4318
4319 if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
4320 return stat;
4321
4322 if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
4323 GdipDeleteBrush((GpBrush*)brush);
4324 return stat;
4325 }
4326
4327 GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
4328 wnd_rect.Width, wnd_rect.Height);
4329
4330 GdipDeleteBrush((GpBrush*)brush);
4331
4332 return Ok;
4333 }
4334
4335 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
4336 {
4337 TRACE("(%p, %p)\n", graphics, res);
4338
4339 if(!graphics || !res)
4340 return InvalidParameter;
4341
4342 return GdipIsEmptyRegion(graphics->clip, graphics, res);
4343 }
4344
4345 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
4346 {
4347 GpStatus stat;
4348 GpRegion* rgn;
4349 GpPointF pt;
4350
4351 TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
4352
4353 if(!graphics || !result)
4354 return InvalidParameter;
4355
4356 if(graphics->busy)
4357 return ObjectBusy;
4358
4359 pt.X = x;
4360 pt.Y = y;
4361 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
4362 CoordinateSpaceWorld, &pt, 1)) != Ok)
4363 return stat;
4364
4365 if((stat = GdipCreateRegion(&rgn)) != Ok)
4366 return stat;
4367
4368 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4369 goto cleanup;
4370
4371 stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
4372
4373 cleanup:
4374 GdipDeleteRegion(rgn);
4375 return stat;
4376 }
4377
4378 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
4379 {
4380 return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
4381 }
4382
4383 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
4384 {
4385 GpStatus stat;
4386 GpRegion* rgn;
4387 GpPointF pts[2];
4388
4389 TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
4390
4391 if(!graphics || !result)
4392 return InvalidParameter;
4393
4394 if(graphics->busy)
4395 return ObjectBusy;
4396
4397 pts[0].X = x;
4398 pts[0].Y = y;
4399 pts[1].X = x + width;
4400 pts[1].Y = y + height;
4401
4402 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
4403 CoordinateSpaceWorld, pts, 2)) != Ok)
4404 return stat;
4405
4406 pts[1].X -= pts[0].X;
4407 pts[1].Y -= pts[0].Y;
4408
4409 if((stat = GdipCreateRegion(&rgn)) != Ok)
4410 return stat;
4411
4412 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4413 goto cleanup;
4414
4415 stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
4416
4417 cleanup:
4418 GdipDeleteRegion(rgn);
4419 return stat;
4420 }
4421
4422 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
4423 {
4424 return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
4425 }
4426
4427 GpStatus gdip_format_string(HDC hdc,
4428 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4429 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
4430 gdip_format_string_callback callback, void *user_data)
4431 {
4432 WCHAR* stringdup;
4433 int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
4434 nheight, lineend, lineno = 0;
4435 RectF bounds;
4436 StringAlignment halign;
4437 GpStatus stat = Ok;
4438 SIZE size;
4439 HotkeyPrefix hkprefix;
4440 INT *hotkeyprefix_offsets=NULL;
4441 INT hotkeyprefix_count=0;
4442 INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
4443 int seen_prefix=0;
4444
4445 if(length == -1) length = lstrlenW(string);
4446
4447 stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
4448 if(!stringdup) return OutOfMemory;
4449
4450 nwidth = rect->Width;
4451 nheight = rect->Height;
4452 if (ignore_empty_clip)
4453 {
4454 if (!nwidth) nwidth = INT_MAX;
4455 if (!nheight) nheight = INT_MAX;
4456 }
4457
4458 if (format)
4459 hkprefix = format->hkprefix;
4460 else
4461 hkprefix = HotkeyPrefixNone;
4462
4463 if (hkprefix == HotkeyPrefixShow)
4464 {
4465 for (i=0; i<length; i++)
4466 {
4467 if (string[i] == '&')
4468 hotkeyprefix_count++;
4469 }
4470 }
4471
4472 if (hotkeyprefix_count)
4473 hotkeyprefix_offsets = GdipAlloc(sizeof(INT) * hotkeyprefix_count);
4474
4475 hotkeyprefix_count = 0;
4476
4477 for(i = 0, j = 0; i < length; i++){
4478 /* FIXME: This makes the indexes passed to callback inaccurate. */
4479 if(!isprintW(string[i]) && (string[i] != '\n'))
4480 continue;
4481
4482 /* FIXME: tabs should be handled using tabstops from stringformat */
4483 if (string[i] == '\t')
4484 continue;
4485
4486 if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
4487 hotkeyprefix_offsets[hotkeyprefix_count++] = j;
4488 else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
4489 {
4490 seen_prefix = 1;
4491 continue;
4492 }
4493
4494 seen_prefix = 0;
4495
4496 stringdup[j] = string[i];
4497 j++;
4498 }
4499
4500 length = j;
4501
4502 if (format) halign = format->align;
4503 else halign = StringAlignmentNear;
4504
4505 while(sum < length){
4506 GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
4507 nwidth, &fit, NULL, &size);
4508 fitcpy = fit;
4509
4510 if(fit == 0)
4511 break;
4512
4513 for(lret = 0; lret < fit; lret++)
4514 if(*(stringdup + sum + lret) == '\n')
4515 break;
4516
4517 /* Line break code (may look strange, but it imitates windows). */
4518 if(lret < fit)
4519 lineend = fit = lret; /* this is not an off-by-one error */
4520 else if(fit < (length - sum)){
4521 if(*(stringdup + sum + fit) == ' ')
4522 while(*(stringdup + sum + fit) == ' ')
4523 fit++;
4524 else
4525 while(*(stringdup + sum + fit - 1) != ' '){
4526 fit--;
4527
4528 if(*(stringdup + sum + fit) == '\t')
4529 break;
4530
4531 if(fit == 0){
4532 fit = fitcpy;
4533 break;
4534 }
4535 }
4536 lineend = fit;
4537 while(*(stringdup + sum + lineend - 1) == ' ' ||
4538 *(stringdup + sum + lineend - 1) == '\t')
4539 lineend--;
4540 }
4541 else
4542 lineend = fit;
4543
4544 GetTextExtentExPointW(hdc, stringdup + sum, lineend,
4545 nwidth, &j, NULL, &size);
4546
4547 bounds.Width = size.cx;
4548
4549 if(height + size.cy > nheight)
4550 bounds.Height = nheight - (height + size.cy);
4551 else
4552 bounds.Height = size.cy;
4553
4554 bounds.Y = rect->Y + height;
4555
4556 switch (halign)
4557 {
4558 case StringAlignmentNear:
4559 default:
4560 bounds.X = rect->X;
4561 break;
4562 case StringAlignmentCenter:
4563 bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
4564 break;
4565 case StringAlignmentFar:
4566 bounds.X = rect->X + rect->Width - bounds.Width;
4567 break;
4568 }
4569
4570 for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
4571 if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
4572 break;
4573
4574 stat = callback(hdc, stringdup, sum, lineend,
4575 font, rect, format, lineno, &bounds,
4576 &hotkeyprefix_offsets[hotkeyprefix_pos],
4577 hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
4578
4579 if (stat != Ok)
4580 break;
4581
4582 sum += fit + (lret < fitcpy ? 1 : 0);
4583 height += size.cy;
4584 lineno++;
4585
4586 hotkeyprefix_pos = hotkeyprefix_end_pos;
4587
4588 if(height > nheight)
4589 break;
4590
4591 /* Stop if this was a linewrap (but not if it was a linebreak). */
4592 if ((lret == fitcpy) && format &&
4593 (format->attr & (StringFormatFlagsNoWrap | StringFormatFlagsLineLimit)))
4594 break;
4595 }
4596
4597 GdipFree(stringdup);
4598 GdipFree(hotkeyprefix_offsets);
4599
4600 return stat;
4601 }
4602
4603 struct measure_ranges_args {
4604 GpRegion **regions;
4605 REAL rel_width, rel_height;
4606 };
4607
4608 static GpStatus measure_ranges_callback(HDC hdc,
4609 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4610 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4611 INT lineno, const RectF *bounds, INT *underlined_indexes,
4612 INT underlined_index_count, void *user_data)
4613 {
4614 int i;
4615 GpStatus stat = Ok;
4616 struct measure_ranges_args *args = user_data;
4617
4618 for (i=0; i<format->range_count; i++)
4619 {
4620 INT range_start = max(index, format->character_ranges[i].First);
4621 INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
4622 if (range_start < range_end)
4623 {
4624 GpRectF range_rect;
4625 SIZE range_size;
4626
4627 range_rect.Y = bounds->Y / args->rel_height;
4628 range_rect.Height = bounds->Height / args->rel_height;
4629
4630 GetTextExtentExPointW(hdc, string + index, range_start - index,
4631 INT_MAX, NULL, NULL, &range_size);
4632 range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
4633
4634 GetTextExtentExPointW(hdc, string + index, range_end - index,
4635 INT_MAX, NULL, NULL, &range_size);
4636 range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
4637
4638 stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
4639 if (stat != Ok)
4640 break;
4641 }
4642 }
4643
4644 return stat;
4645 }
4646
4647 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
4648 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
4649 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
4650 INT regionCount, GpRegion** regions)
4651 {
4652 GpStatus stat;
4653 int i;
4654 HFONT gdifont, oldfont;
4655 struct measure_ranges_args args;
4656 HDC hdc, temp_hdc=NULL;
4657 GpPointF pt[3];
4658 RectF scaled_rect;
4659 REAL margin_x;
4660
4661 TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
4662 length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
4663
4664 if (!(graphics && string && font && layoutRect && stringFormat && regions))
4665 return InvalidParameter;
4666
4667 if (regionCount < stringFormat->range_count)
4668 return InvalidParameter;
4669
4670 if(!graphics->hdc)
4671 {
4672 hdc = temp_hdc = CreateCompatibleDC(0);
4673 if (!temp_hdc) return OutOfMemory;
4674 }
4675 else
4676 hdc = graphics->hdc;
4677
4678 if (stringFormat->attr)
4679 TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
4680
4681 pt[0].X = 0.0;
4682 pt[0].Y = 0.0;
4683 pt[1].X = 1.0;
4684 pt[1].Y = 0.0;
4685 pt[2].X = 0.0;
4686 pt[2].Y = 1.0;
4687 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4688 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4689 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4690 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4691 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4692
4693 margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
4694 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
4695
4696 scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
4697 scaled_rect.Y = layoutRect->Y * args.rel_height;
4698 scaled_rect.Width = layoutRect->Width * args.rel_width;
4699 scaled_rect.Height = layoutRect->Height * args.rel_height;
4700
4701 get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
4702 oldfont = SelectObject(hdc, gdifont);
4703
4704 for (i=0; i<stringFormat->range_count; i++)
4705 {
4706 stat = GdipSetEmpty(regions[i]);
4707 if (stat != Ok)
4708 return stat;
4709 }
4710
4711 args.regions = regions;
4712
4713 stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
4714 (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
4715
4716 SelectObject(hdc, oldfont);
4717 DeleteObject(gdifont);
4718
4719 if (temp_hdc)
4720 DeleteDC(temp_hdc);
4721
4722 return stat;
4723 }
4724
4725 struct measure_string_args {
4726 RectF *bounds;
4727 INT *codepointsfitted;
4728 INT *linesfilled;
4729 REAL rel_width, rel_height;
4730 };
4731
4732 static GpStatus measure_string_callback(HDC hdc,
4733 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4734 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4735 INT lineno, const RectF *bounds, INT *underlined_indexes,
4736 INT underlined_index_count, void *user_data)
4737 {
4738 struct measure_string_args *args = user_data;
4739 REAL new_width, new_height;
4740
4741 new_width = bounds->Width / args->rel_width;
4742 new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
4743
4744 if (new_width > args->bounds->Width)
4745 args->bounds->Width = new_width;
4746
4747 if (new_height > args->bounds->Height)
4748 args->bounds->Height = new_height;
4749
4750 if (args->codepointsfitted)
4751 *args->codepointsfitted = index + length;
4752
4753 if (args->linesfilled)
4754 (*args->linesfilled)++;
4755
4756 return Ok;
4757 }
4758
4759 /* Find the smallest rectangle that bounds the text when it is printed in rect
4760 * according to the format options listed in format. If rect has 0 width and
4761 * height, then just find the smallest rectangle that bounds the text when it's
4762 * printed at location (rect->X, rect-Y). */
4763 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
4764 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4765 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
4766 INT *codepointsfitted, INT *linesfilled)
4767 {
4768 HFONT oldfont, gdifont;
4769 struct measure_string_args args;
4770 HDC temp_hdc=NULL, hdc;
4771 GpPointF pt[3];
4772 RectF scaled_rect;
4773 REAL margin_x;
4774 INT lines, glyphs;
4775
4776 TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
4777 debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
4778 bounds, codepointsfitted, linesfilled);
4779
4780 if(!graphics || !string || !font || !rect || !bounds)
4781 return InvalidParameter;
4782
4783 if(!graphics->hdc)
4784 {
4785 hdc = temp_hdc = CreateCompatibleDC(0);
4786 if (!temp_hdc) return OutOfMemory;
4787 }
4788 else
4789 hdc = graphics->hdc;
4790
4791 if(linesfilled) *linesfilled = 0;
4792 if(codepointsfitted) *codepointsfitted = 0;
4793
4794 if(format)
4795 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4796
4797 pt[0].X = 0.0;
4798 pt[0].Y = 0.0;
4799 pt[1].X = 1.0;
4800 pt[1].Y = 0.0;
4801 pt[2].X = 0.0;
4802 pt[2].Y = 1.0;
4803 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4804 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4805 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4806 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4807 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4808
4809 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
4810 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
4811
4812 scaled_rect.X = (rect->X + margin_x) * args.rel_width;
4813 scaled_rect.Y = rect->Y * args.rel_height;
4814 scaled_rect.Width = rect->Width * args.rel_width;
4815 scaled_rect.Height = rect->Height * args.rel_height;
4816 if (scaled_rect.Width >= 0.5)
4817 {
4818 scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
4819 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
4820 }
4821
4822 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
4823 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
4824
4825 get_font_hfont(graphics, font, format, &gdifont, NULL);
4826 oldfont = SelectObject(hdc, gdifont);
4827
4828 bounds->X = rect->X;
4829 bounds->Y = rect->Y;
4830 bounds->Width = 0.0;
4831 bounds->Height = 0.0;
4832
4833 args.bounds = bounds;
4834 args.codepointsfitted = &glyphs;
4835 args.linesfilled = &lines;
4836 lines = glyphs = 0;
4837
4838 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
4839 measure_string_callback, &args);
4840
4841 if (linesfilled) *linesfilled = lines;
4842 if (codepointsfitted) *codepointsfitted = glyphs;
4843
4844 if (lines)
4845 bounds->Width += margin_x * 2.0;
4846
4847 SelectObject(hdc, oldfont);
4848 DeleteObject(gdifont);
4849
4850 if (temp_hdc)
4851 DeleteDC(temp_hdc);
4852
4853 return Ok;
4854 }
4855
4856 struct draw_string_args {
4857 GpGraphics *graphics;
4858 GDIPCONST GpBrush *brush;
4859 REAL x, y, rel_width, rel_height, ascent;
4860 };
4861
4862 static GpStatus draw_string_callback(HDC hdc,
4863 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4864 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4865 INT lineno, const RectF *bounds, INT *underlined_indexes,
4866 INT underlined_index_count, void *user_data)
4867 {
4868 struct draw_string_args *args = user_data;
4869 PointF position;
4870 GpStatus stat;
4871
4872 position.X = args->x + bounds->X / args->rel_width;
4873 position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
4874
4875 stat = draw_driver_string(args->graphics, &string[index], length, font, format,
4876 args->brush, &position,
4877 DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
4878
4879 if (stat == Ok && underlined_index_count)
4880 {
4881 OUTLINETEXTMETRICW otm;
4882 REAL underline_y, underline_height;
4883 int i;
4884
4885 GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
4886
4887 underline_height = otm.otmsUnderscoreSize / args->rel_height;
4888 underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
4889
4890 for (i=0; i<underlined_index_count; i++)
4891 {
4892 REAL start_x, end_x;
4893 SIZE text_size;
4894 INT ofs = underlined_indexes[i] - index;
4895
4896 GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
4897 start_x = text_size.cx / args->rel_width;
4898
4899 GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
4900 end_x = text_size.cx / args->rel_width;
4901
4902 GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
4903 }
4904 }
4905
4906 return stat;
4907 }
4908
4909 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
4910 INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
4911 GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
4912 {
4913 HRGN rgn = NULL;
4914 HFONT gdifont;
4915 GpPointF pt[3], rectcpy[4];
4916 POINT corners[4];
4917 REAL rel_width, rel_height, margin_x;
4918 INT save_state, format_flags = 0;
4919 REAL offsety = 0.0;
4920 struct draw_string_args args;
4921 RectF scaled_rect;
4922 HDC hdc, temp_hdc=NULL;
4923 TEXTMETRICW textmetric;
4924
4925 TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
4926 length, font, debugstr_rectf(rect), format, brush);
4927
4928 if(!graphics || !string || !font || !brush || !rect)
4929 return InvalidParameter;
4930
4931 if(graphics->hdc)
4932 {
4933 hdc = graphics->hdc;
4934 }
4935 else
4936 {
4937 hdc = temp_hdc = CreateCompatibleDC(0);
4938 }
4939
4940 if(format){
4941 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4942
4943 format_flags = format->attr;
4944
4945 /* Should be no need to explicitly test for StringAlignmentNear as
4946 * that is default behavior if no alignment is passed. */
4947 if(format->vertalign != StringAlignmentNear){
4948 RectF bounds, in_rect = *rect;
4949 in_rect.Height = 0.0; /* avoid height clipping */
4950 GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0);
4951
4952 TRACE("bounds %s\n", debugstr_rectf(&bounds));
4953
4954 if(format->vertalign == StringAlignmentCenter)
4955 offsety = (rect->Height - bounds.Height) / 2;
4956 else if(format->vertalign == StringAlignmentFar)
4957 offsety = (rect->Height - bounds.Height);
4958 }
4959 TRACE("vertical align %d, offsety %f\n", format->vertalign, offsety);
4960 }
4961
4962 save_state = SaveDC(hdc);
4963
4964 pt[0].X = 0.0;
4965 pt[0].Y = 0.0;
4966 pt[1].X = 1.0;
4967 pt[1].Y = 0.0;
4968 pt[2].X = 0.0;
4969 pt[2].Y = 1.0;
4970 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4971 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4972 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4973 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4974 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4975
4976 rectcpy[3].X = rectcpy[0].X = rect->X;
4977 rectcpy[1].Y = rectcpy[0].Y = rect->Y;
4978 rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
4979 rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
4980 transform_and_round_points(graphics, corners, rectcpy, 4);
4981
4982 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
4983 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
4984
4985 scaled_rect.X = margin_x * rel_width;
4986 scaled_rect.Y = 0.0;
4987 scaled_rect.Width = rel_width * rect->Width;
4988 scaled_rect.Height = rel_height * rect->Height;
4989 if (scaled_rect.Width >= 0.5)
4990 {
4991 scaled_rect.Width -= margin_x * 2.0 * rel_width;
4992 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
4993 }
4994
4995 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
4996 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
4997
4998 if (!(format_flags & StringFormatFlagsNoClip) &&
4999 scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 &&
5000 rect->Width > 0.0 && rect->Height > 0.0)
5001 {
5002 /* FIXME: If only the width or only the height is 0, we should probably still clip */
5003 rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
5004 SelectClipRgn(hdc, rgn);
5005 }
5006
5007 get_font_hfont(graphics, font, format, &gdifont, NULL);
5008 SelectObject(hdc, gdifont);
5009
5010 args.graphics = graphics;
5011 args.brush = brush;
5012
5013 args.x = rect->X;
5014 args.y = rect->Y + offsety;
5015
5016 args.rel_width = rel_width;
5017 args.rel_height = rel_height;
5018
5019 GetTextMetricsW(hdc, &textmetric);
5020 args.ascent = textmetric.tmAscent / rel_height;
5021
5022 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
5023 draw_string_callback, &args);
5024
5025 DeleteObject(rgn);
5026 DeleteObject(gdifont);
5027
5028 RestoreDC(hdc, save_state);
5029
5030 DeleteDC(temp_hdc);
5031
5032 return Ok;
5033 }
5034
5035 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
5036 {
5037 TRACE("(%p)\n", graphics);
5038
5039 if(!graphics)
5040 return InvalidParameter;
5041
5042 if(graphics->busy)
5043 return ObjectBusy;
5044
5045 return GdipSetInfinite(graphics->clip);
5046 }
5047
5048 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
5049 {
5050 TRACE("(%p)\n", graphics);
5051
5052 if(!graphics)
5053 return InvalidParameter;
5054
5055 if(graphics->busy)
5056 return ObjectBusy;
5057
5058 return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
5059 }
5060
5061 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
5062 {
5063 return GdipEndContainer(graphics, state);
5064 }
5065
5066 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
5067 GpMatrixOrder order)
5068 {
5069 TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
5070
5071 if(!graphics)
5072 return InvalidParameter;
5073
5074 if(graphics->busy)
5075 return ObjectBusy;
5076
5077 return GdipRotateMatrix(&graphics->worldtrans, angle, order);
5078 }
5079
5080 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
5081 {
5082 return GdipBeginContainer2(graphics, state);
5083 }
5084
5085 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
5086 GraphicsContainer *state)
5087 {
5088 GraphicsContainerItem *container;
5089 GpStatus sts;
5090
5091 TRACE("(%p, %p)\n", graphics, state);
5092
5093 if(!graphics || !state)
5094 return InvalidParameter;
5095
5096 sts = init_container(&container, graphics);
5097 if(sts != Ok)
5098 return sts;
5099
5100 list_add_head(&graphics->containers, &container->entry);
5101 *state = graphics->contid = container->contid;
5102
5103 return Ok;
5104 }
5105
5106 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
5107 {
5108 FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
5109 return NotImplemented;
5110 }
5111
5112 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
5113 {
5114 FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
5115 return NotImplemented;
5116 }
5117
5118 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
5119 {
5120 FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
5121 return NotImplemented;
5122 }
5123
5124 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
5125 {
5126 GpStatus sts;
5127 GraphicsContainerItem *container, *container2;
5128
5129 TRACE("(%p, %x)\n", graphics, state);
5130
5131 if(!graphics)
5132 return InvalidParameter;
5133
5134 LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
5135 if(container->contid == state)
5136 break;
5137 }
5138
5139 /* did not find a matching container */
5140 if(&container->entry == &graphics->containers)
5141 return Ok;
5142
5143 sts = restore_container(graphics, container);
5144 if(sts != Ok)
5145 return sts;
5146
5147 /* remove all of the containers on top of the found container */
5148 LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
5149 if(container->contid == state)
5150 break;
5151 list_remove(&container->entry);
5152 delete_container(container);
5153 }
5154
5155 list_remove(&container->entry);
5156 delete_container(container);
5157
5158 return Ok;
5159 }
5160
5161 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
5162 REAL sy, GpMatrixOrder order)
5163 {
5164 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
5165
5166 if(!graphics)
5167 return InvalidParameter;
5168
5169 if(graphics->busy)
5170 return ObjectBusy;
5171
5172 return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
5173 }
5174
5175 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
5176 CombineMode mode)
5177 {
5178 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
5179
5180 if(!graphics || !srcgraphics)
5181 return InvalidParameter;
5182
5183 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
5184 }
5185
5186 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
5187 CompositingMode mode)
5188 {
5189 TRACE("(%p, %d)\n", graphics, mode);
5190
5191 if(!graphics)
5192 return InvalidParameter;
5193
5194 if(graphics->busy)
5195 return ObjectBusy;
5196
5197 graphics->compmode = mode;
5198
5199 return Ok;
5200 }
5201
5202 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
5203 CompositingQuality quality)
5204 {
5205 TRACE("(%p, %d)\n", graphics, quality);
5206
5207 if(!graphics)
5208 return InvalidParameter;
5209
5210 if(graphics->busy)
5211 return ObjectBusy;
5212
5213 graphics->compqual = quality;
5214
5215 return Ok;
5216 }
5217
5218 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
5219 InterpolationMode mode)
5220 {
5221 TRACE("(%p, %d)\n", graphics, mode);
5222
5223 if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
5224 return InvalidParameter;
5225
5226 if(graphics->busy)
5227 return ObjectBusy;
5228
5229 if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
5230 mode = InterpolationModeBilinear;
5231
5232 if (mode == InterpolationModeHighQuality)
5233 mode = InterpolationModeHighQualityBicubic;
5234
5235 graphics->interpolation = mode;
5236
5237 return Ok;
5238 }
5239
5240 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
5241 {
5242 TRACE("(%p, %.2f)\n", graphics, scale);
5243
5244 if(!graphics || (scale <= 0.0))
5245 return InvalidParameter;
5246
5247 if(graphics->busy)
5248 return ObjectBusy;
5249
5250 graphics->scale = scale;
5251
5252 return Ok;
5253 }
5254
5255 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
5256 {
5257 TRACE("(%p, %d)\n", graphics, unit);
5258
5259 if(!graphics)
5260 return InvalidParameter;
5261
5262 if(graphics->busy)
5263 return ObjectBusy;
5264
5265 if(unit == UnitWorld)
5266 return InvalidParameter;
5267
5268 graphics->unit = unit;
5269
5270 return Ok;
5271 }
5272
5273 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
5274 mode)
5275 {
5276 TRACE("(%p, %d)\n", graphics, mode);
5277
5278 if(!graphics)
5279 return InvalidParameter;
5280
5281 if(graphics->busy)
5282 return ObjectBusy;
5283
5284 graphics->pixeloffset = mode;
5285
5286 return Ok;
5287 }
5288
5289 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
5290 {
5291 static int calls;
5292
5293 TRACE("(%p,%i,%i)\n", graphics, x, y);
5294
5295 if (!(calls++))
5296 FIXME("value is unused in rendering\n");
5297
5298 if (!graphics)
5299 return InvalidParameter;
5300
5301 graphics->origin_x = x;
5302 graphics->origin_y = y;
5303
5304 return Ok;
5305 }
5306
5307 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
5308 {
5309 TRACE("(%p,%p,%p)\n", graphics, x, y);
5310
5311 if (!graphics || !x || !y)
5312 return InvalidParameter;
5313
5314 *x = graphics->origin_x;
5315 *y = graphics->origin_y;
5316
5317 return Ok;
5318 }
5319
5320 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
5321 {
5322 TRACE("(%p, %d)\n", graphics, mode);
5323
5324 if(!graphics)
5325 return InvalidParameter;
5326
5327 if(graphics->busy)
5328 return ObjectBusy;
5329
5330 graphics->smoothing = mode;
5331
5332 return Ok;
5333 }
5334
5335 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
5336 {
5337 TRACE("(%p, %d)\n", graphics, contrast);
5338
5339 if(!graphics)
5340 return InvalidParameter;
5341
5342 graphics->textcontrast = contrast;
5343
5344 return Ok;
5345 }
5346
5347 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
5348 TextRenderingHint hint)
5349 {
5350 TRACE("(%p, %d)\n", graphics, hint);
5351
5352 if(!graphics || hint > TextRenderingHintClearTypeGridFit)
5353 return InvalidParameter;
5354
5355 if(graphics->busy)
5356 return ObjectBusy;
5357
5358 graphics->texthint = hint;
5359
5360 return Ok;
5361 }
5362
5363 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
5364 {
5365 TRACE("(%p, %p)\n", graphics, matrix);
5366
5367 if(!graphics || !matrix)
5368 return InvalidParameter;
5369
5370 if(graphics->busy)
5371 return ObjectBusy;
5372
5373 TRACE("%f,%f,%f,%f,%f,%f\n",
5374 matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
5375 matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
5376
5377 graphics->worldtrans = *matrix;
5378
5379 return Ok;
5380 }
5381
5382 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
5383 REAL dy, GpMatrixOrder order)
5384 {
5385 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
5386
5387 if(!graphics)
5388 return InvalidParameter;
5389
5390 if(graphics->busy)
5391 return ObjectBusy;
5392
5393 return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
5394 }
5395
5396 /*****************************************************************************
5397 * GdipSetClipHrgn [GDIPLUS.@]
5398 */
5399 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
5400 {
5401 GpRegion *region;
5402 GpStatus status;
5403
5404 TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
5405
5406 if(!graphics)
5407 return InvalidParameter;
5408
5409 if(graphics->busy)
5410 return ObjectBusy;
5411
5412 /* hrgn is already in device units */
5413 status = GdipCreateRegionHrgn(hrgn, &region);
5414 if(status != Ok)
5415 return status;
5416
5417 status = GdipCombineRegionRegion(graphics->clip, region, mode);
5418
5419 GdipDeleteRegion(region);
5420 return status;
5421 }
5422
5423 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
5424 {
5425 GpStatus status;
5426 GpPath *clip_path;
5427
5428 TRACE("(%p, %p, %d)\n", graphics, path, mode);
5429
5430 if(!graphics)
5431 return InvalidParameter;
5432
5433 if(graphics->busy)
5434 return ObjectBusy;
5435
5436 status = GdipClonePath(path, &clip_path);
5437 if (status == Ok)
5438 {
5439 GpMatrix world_to_device;
5440
5441 get_graphics_transform(graphics, CoordinateSpaceDevice,
5442 CoordinateSpaceWorld, &world_to_device);
5443 status = GdipTransformPath(clip_path, &world_to_device);
5444 if (status == Ok)
5445 GdipCombineRegionPath(graphics->clip, clip_path, mode);
5446
5447 GdipDeletePath(clip_path);
5448 }
5449 return status;
5450 }
5451
5452 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
5453 REAL width, REAL height,
5454 CombineMode mode)
5455 {
5456 GpStatus status;
5457 GpRectF rect;
5458 GpRegion *region;
5459
5460 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
5461
5462 if(!graphics)
5463 return InvalidParameter;
5464
5465 if(graphics->busy)
5466 return ObjectBusy;
5467
5468 rect.X = x;
5469 rect.Y = y;
5470 rect.Width = width;
5471 rect.Height = height;
5472 status = GdipCreateRegionRect(&rect, &region);
5473 if (status == Ok)
5474 {
5475 GpMatrix world_to_device;
5476
5477 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
5478 status = GdipTransformRegion(region, &world_to_device);
5479 if (status == Ok)
5480 status = GdipCombineRegionRegion(graphics->clip, region, mode);
5481
5482 GdipDeleteRegion(region);
5483 }
5484 return status;
5485 }
5486
5487 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
5488 INT width, INT height,
5489 CombineMode mode)
5490 {
5491 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
5492
5493 if(!graphics)
5494 return InvalidParameter;
5495
5496 if(graphics->busy)
5497 return ObjectBusy;
5498
5499 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
5500 }
5501
5502 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
5503 CombineMode mode)
5504 {
5505 GpStatus status;
5506 GpRegion *clip;
5507
5508 TRACE("(%p, %p, %d)\n", graphics, region, mode);
5509
5510 if(!graphics || !region)
5511 return InvalidParameter;
5512
5513 if(graphics->busy)
5514 return ObjectBusy;
5515
5516 status = GdipCloneRegion(region, &clip);
5517 if (status == Ok)
5518 {
5519 GpMatrix world_to_device;
5520
5521 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
5522 status = GdipTransformRegion(clip, &world_to_device);
5523 if (status == Ok)
5524 status = GdipCombineRegionRegion(graphics->clip, clip, mode);
5525
5526 GdipDeleteRegion(clip);
5527 }
5528 return status;
5529 }
5530
5531 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
5532 UINT limitDpi)
5533 {
5534 static int calls;
5535
5536 TRACE("(%p,%u)\n", metafile, limitDpi);
5537
5538 if(!(calls++))
5539 FIXME("not implemented\n");
5540
5541 return NotImplemented;
5542 }
5543
5544 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
5545 INT count)
5546 {
5547 INT save_state;
5548 POINT *pti;
5549
5550 TRACE("(%p, %p, %d)\n", graphics, points, count);
5551
5552 if(!graphics || !pen || count<=0)
5553 return InvalidParameter;
5554
5555 if(graphics->busy)
5556 return ObjectBusy;
5557
5558 if (!graphics->hdc)
5559 {
5560 FIXME("graphics object has no HDC\n");
5561 return Ok;
5562 }
5563
5564 pti = GdipAlloc(sizeof(POINT) * count);
5565
5566 save_state = prepare_dc(graphics, pen);
5567 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
5568
5569 transform_and_round_points(graphics, pti, (GpPointF*)points, count);
5570 Polygon(graphics->hdc, pti, count);
5571
5572 restore_dc(graphics, save_state);
5573 GdipFree(pti);
5574
5575 return Ok;
5576 }
5577
5578 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
5579 INT count)
5580 {
5581 GpStatus ret;
5582 GpPointF *ptf;
5583 INT i;
5584
5585 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
5586
5587 if(count<=0) return InvalidParameter;
5588 ptf = GdipAlloc(sizeof(GpPointF) * count);
5589
5590 for(i = 0;i < count; i++){
5591 ptf[i].X = (REAL)points[i].X;
5592 ptf[i].Y = (REAL)points[i].Y;
5593 }
5594
5595 ret = GdipDrawPolygon(graphics,pen,ptf,count);
5596 GdipFree(ptf);
5597
5598 return ret;
5599 }
5600
5601 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
5602 {
5603 TRACE("(%p, %p)\n", graphics, dpi);
5604
5605 if(!graphics || !dpi)
5606 return InvalidParameter;
5607
5608 if(graphics->busy)
5609 return ObjectBusy;
5610
5611 *dpi = graphics->xres;
5612 return Ok;
5613 }
5614
5615 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
5616 {
5617 TRACE("(%p, %p)\n", graphics, dpi);
5618
5619 if(!graphics || !dpi)
5620 return InvalidParameter;
5621
5622 if(graphics->busy)
5623 return ObjectBusy;
5624
5625 *dpi = graphics->yres;
5626 return Ok;
5627 }
5628
5629 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
5630 GpMatrixOrder order)
5631 {
5632 GpMatrix m;
5633 GpStatus ret;
5634
5635 TRACE("(%p, %p, %d)\n", graphics, matrix, order);
5636
5637 if(!graphics || !matrix)
5638 return InvalidParameter;
5639
5640 if(graphics->busy)
5641 return ObjectBusy;
5642
5643 m = graphics->worldtrans;
5644
5645 ret = GdipMultiplyMatrix(&m, matrix, order);
5646 if(ret == Ok)
5647 graphics->worldtrans = m;
5648
5649 return ret;
5650 }
5651
5652 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
5653 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
5654
5655 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
5656 {
5657 GpStatus stat=Ok;
5658
5659 TRACE("(%p, %p)\n", graphics, hdc);
5660
5661 if(!graphics || !hdc)
5662 return InvalidParameter;
5663
5664 if(graphics->busy)
5665 return ObjectBusy;
5666
5667 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5668 {
5669 stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
5670 }
5671 else if (!graphics->hdc || graphics->alpha_hdc ||
5672 (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
5673 {
5674 /* Create a fake HDC and fill it with a constant color. */
5675 HDC temp_hdc;
5676 HBITMAP hbitmap;
5677 GpRectF bounds;
5678 BITMAPINFOHEADER bmih;
5679 int i;
5680
5681 stat = get_graphics_bounds(graphics, &bounds);
5682 if (stat != Ok)
5683 return stat;
5684
5685 graphics->temp_hbitmap_width = bounds.Width;
5686 graphics->temp_hbitmap_height = bounds.Height;
5687
5688 bmih.biSize = sizeof(bmih);
5689 bmih.biWidth = graphics->temp_hbitmap_width;
5690 bmih.biHeight = -graphics->temp_hbitmap_height;
5691 bmih.biPlanes = 1;
5692 bmih.biBitCount = 32;
5693 bmih.biCompression = BI_RGB;
5694 bmih.biSizeImage = 0;
5695 bmih.biXPelsPerMeter = 0;
5696 bmih.biYPelsPerMeter = 0;
5697 bmih.biClrUsed = 0;
5698 bmih.biClrImportant = 0;
5699
5700 hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
5701 (void**)&graphics->temp_bits, NULL, 0);
5702 if (!hbitmap)
5703 return GenericError;
5704
5705 temp_hdc = CreateCompatibleDC(0);
5706 if (!temp_hdc)
5707 {
5708 DeleteObject(hbitmap);
5709 return GenericError;
5710 }
5711
5712 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5713 ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
5714
5715 SelectObject(temp_hdc, hbitmap);
5716
5717 graphics->temp_hbitmap = hbitmap;
5718 *hdc = graphics->temp_hdc = temp_hdc;
5719 }
5720 else
5721 {
5722 *hdc = graphics->hdc;
5723 }
5724
5725 if (stat == Ok)
5726 graphics->busy = TRUE;
5727
5728 return stat;
5729 }
5730
5731 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
5732 {
5733 GpStatus stat=Ok;
5734
5735 TRACE("(%p, %p)\n", graphics, hdc);
5736
5737 if(!graphics || !hdc || !graphics->busy)
5738 return InvalidParameter;
5739
5740 if (graphics->image && graphics->image->type == ImageTypeMetafile)
5741 {
5742 stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
5743 }
5744 else if (graphics->temp_hdc == hdc)
5745 {
5746 DWORD* pos;
5747 int i;
5748
5749 /* Find the pixels that have changed, and mark them as opaque. */
5750 pos = (DWORD*)graphics->temp_bits;
5751 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5752 {
5753 if (*pos != DC_BACKGROUND_KEY)
5754 {
5755 *pos |= 0xff000000;
5756 }
5757 pos++;
5758 }
5759
5760 /* Write the changed pixels to the real target. */
5761 alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
5762 graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
5763 graphics->temp_hbitmap_width * 4);
5764
5765 /* Clean up. */
5766 DeleteDC(graphics->temp_hdc);
5767 DeleteObject(graphics->temp_hbitmap);
5768 graphics->temp_hdc = NULL;
5769 graphics->temp_hbitmap = NULL;
5770 }
5771 else if (hdc != graphics->hdc)
5772 {
5773 stat = InvalidParameter;
5774 }
5775
5776 if (stat == Ok)
5777 graphics->busy = FALSE;
5778
5779 return stat;
5780 }
5781
5782 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
5783 {
5784 GpRegion *clip;
5785 GpStatus status;
5786 GpMatrix device_to_world;
5787
5788 TRACE("(%p, %p)\n", graphics, region);
5789
5790 if(!graphics || !region)
5791 return InvalidParameter;
5792
5793 if(graphics->busy)
5794 return ObjectBusy;
5795
5796 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
5797 return status;
5798
5799 get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
5800 status = GdipTransformRegion(clip, &device_to_world);
5801 if (status != Ok)
5802 {
5803 GdipDeleteRegion(clip);
5804 return status;
5805 }
5806
5807 /* free everything except root node and header */
5808 delete_element(&region->node);
5809 memcpy(region, clip, sizeof(GpRegion));
5810 GdipFree(clip);
5811
5812 return Ok;
5813 }
5814
5815 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
5816 GpCoordinateSpace src_space, GpMatrix *matrix)
5817 {
5818 GpStatus stat = Ok;
5819 REAL scale_x, scale_y;
5820
5821 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
5822
5823 if (dst_space != src_space)
5824 {
5825 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
5826 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
5827
5828 if(graphics->unit != UnitDisplay)
5829 {
5830 scale_x *= graphics->scale;
5831 scale_y *= graphics->scale;
5832 }
5833
5834 /* transform from src_space to CoordinateSpacePage */
5835 switch (src_space)
5836 {
5837 case CoordinateSpaceWorld:
5838 GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
5839 break;
5840 case CoordinateSpacePage:
5841 break;
5842 case CoordinateSpaceDevice:
5843 GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
5844 break;
5845 }
5846
5847 /* transform from CoordinateSpacePage to dst_space */
5848 switch (dst_space)
5849 {
5850 case CoordinateSpaceWorld:
5851 {
5852 GpMatrix inverted_transform = graphics->worldtrans;
5853 stat = GdipInvertMatrix(&inverted_transform);
5854 if (stat == Ok)
5855 GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
5856 break;
5857 }
5858 case CoordinateSpacePage:
5859 break;
5860 case CoordinateSpaceDevice:
5861 GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
5862 break;
5863 }
5864 }
5865 return stat;
5866 }
5867
5868 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
5869 GpCoordinateSpace src_space, GpPointF *points, INT count)
5870 {
5871 GpMatrix matrix;
5872 GpStatus stat;
5873
5874 if(!graphics || !points || count <= 0)
5875 return InvalidParameter;
5876
5877 if(graphics->busy)
5878 return ObjectBusy;
5879
5880 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5881
5882 if (src_space == dst_space) return Ok;
5883
5884 stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
5885 if (stat != Ok) return stat;
5886
5887 return GdipTransformMatrixPoints(&matrix, points, count);
5888 }
5889
5890 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
5891 GpCoordinateSpace src_space, GpPoint *points, INT count)
5892 {
5893 GpPointF *pointsF;
5894 GpStatus ret;
5895 INT i;
5896
5897 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5898
5899 if(count <= 0)
5900 return InvalidParameter;
5901
5902 pointsF = GdipAlloc(sizeof(GpPointF) * count);
5903 if(!pointsF)
5904 return OutOfMemory;
5905
5906 for(i = 0; i < count; i++){
5907 pointsF[i].X = (REAL)points[i].X;
5908 pointsF[i].Y = (REAL)points[i].Y;
5909 }
5910
5911 ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
5912
5913 if(ret == Ok)
5914 for(i = 0; i < count; i++){
5915 points[i].X = gdip_round(pointsF[i].X);
5916 points[i].Y = gdip_round(pointsF[i].Y);
5917 }
5918 GdipFree(pointsF);
5919
5920 return ret;
5921 }
5922
5923 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
5924 {
5925 static int calls;
5926
5927 TRACE("\n");
5928
5929 if (!calls++)
5930 FIXME("stub\n");
5931
5932 return NULL;
5933 }
5934
5935 /*****************************************************************************
5936 * GdipTranslateClip [GDIPLUS.@]
5937 */
5938 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
5939 {
5940 TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
5941
5942 if(!graphics)
5943 return InvalidParameter;
5944
5945 if(graphics->busy)
5946 return ObjectBusy;
5947
5948 return GdipTranslateRegion(graphics->clip, dx, dy);
5949 }
5950
5951 /*****************************************************************************
5952 * GdipTranslateClipI [GDIPLUS.@]
5953 */
5954 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
5955 {
5956 TRACE("(%p, %d, %d)\n", graphics, dx, dy);
5957
5958 if(!graphics)
5959 return InvalidParameter;
5960
5961 if(graphics->busy)
5962 return ObjectBusy;
5963
5964 return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
5965 }
5966
5967
5968 /*****************************************************************************
5969 * GdipMeasureDriverString [GDIPLUS.@]
5970 */
5971 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5972 GDIPCONST GpFont *font, GDIPCONST PointF *positions,
5973 INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
5974 {
5975 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
5976 HFONT hfont;
5977 HDC hdc;
5978 REAL min_x, min_y, max_x, max_y, x, y;
5979 int i;
5980 TEXTMETRICW textmetric;
5981 const WORD *glyph_indices;
5982 WORD *dynamic_glyph_indices=NULL;
5983 REAL rel_width, rel_height, ascent, descent;
5984 GpPointF pt[3];
5985
5986 TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
5987
5988 if (!graphics || !text || !font || !positions || !boundingBox)
5989 return InvalidParameter;
5990
5991 if (length == -1)
5992 length = strlenW(text);
5993
5994 if (length == 0)
5995 {
5996 boundingBox->X = 0.0;
5997 boundingBox->Y = 0.0;
5998 boundingBox->Width = 0.0;
5999 boundingBox->Height = 0.0;
6000 }
6001
6002 if (flags & unsupported_flags)
6003 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6004
6005 get_font_hfont(graphics, font, NULL, &hfont, matrix);
6006
6007 hdc = CreateCompatibleDC(0);
6008 SelectObject(hdc, hfont);
6009
6010 GetTextMetricsW(hdc, &textmetric);
6011
6012 pt[0].X = 0.0;
6013 pt[0].Y = 0.0;
6014 pt[1].X = 1.0;
6015 pt[1].Y = 0.0;
6016 pt[2].X = 0.0;
6017 pt[2].Y = 1.0;
6018 if (matrix)
6019 {
6020 GpMatrix xform = *matrix;
6021 GdipTransformMatrixPoints(&xform, pt, 3);
6022 }
6023 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
6024 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
6025 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
6026 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
6027 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
6028
6029 if (flags & DriverStringOptionsCmapLookup)
6030 {
6031 glyph_indices = dynamic_glyph_indices = GdipAlloc(sizeof(WORD) * length);
6032 if (!glyph_indices)
6033 {
6034 DeleteDC(hdc);
6035 DeleteObject(hfont);
6036 return OutOfMemory;
6037 }
6038
6039 GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
6040 }
6041 else
6042 glyph_indices = text;
6043
6044 min_x = max_x = x = positions[0].X;
6045 min_y = max_y = y = positions[0].Y;
6046
6047 ascent = textmetric.tmAscent / rel_height;
6048 descent = textmetric.tmDescent / rel_height;
6049
6050 for (i=0; i<length; i++)
6051 {
6052 int char_width;
6053 ABC abc;
6054
6055 if (!(flags & DriverStringOptionsRealizedAdvance))
6056 {
6057 x = positions[i].X;
6058 y = positions[i].Y;
6059 }
6060
6061 GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
6062 char_width = abc.abcA + abc.abcB + abc.abcC;
6063
6064 if (min_y > y - ascent) min_y = y - ascent;
6065 if (max_y < y + descent) max_y = y + descent;
6066 if (min_x > x) min_x = x;
6067
6068 x += char_width / rel_width;
6069
6070 if (max_x < x) max_x = x;
6071 }
6072
6073 GdipFree(dynamic_glyph_indices);
6074 DeleteDC(hdc);
6075 DeleteObject(hfont);
6076
6077 boundingBox->X = min_x;
6078 boundingBox->Y = min_y;
6079 boundingBox->Width = max_x - min_x;
6080 boundingBox->Height = max_y - min_y;
6081
6082 return Ok;
6083 }
6084
6085 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6086 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
6087 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
6088 INT flags, GDIPCONST GpMatrix *matrix)
6089 {
6090 static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
6091 INT save_state;
6092 GpPointF pt;
6093 HFONT hfont;
6094 UINT eto_flags=0;
6095
6096 if (flags & unsupported_flags)
6097 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6098
6099 if (!(flags & DriverStringOptionsCmapLookup))
6100 eto_flags |= ETO_GLYPH_INDEX;
6101
6102 save_state = SaveDC(graphics->hdc);
6103 SetBkMode(graphics->hdc, TRANSPARENT);
6104 SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
6105
6106 pt = positions[0];
6107 GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
6108
6109 get_font_hfont(graphics, font, format, &hfont, matrix);
6110 SelectObject(graphics->hdc, hfont);
6111
6112 SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
6113
6114 ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
6115
6116 RestoreDC(graphics->hdc, save_state);
6117
6118 DeleteObject(hfont);
6119
6120 return Ok;
6121 }
6122
6123 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6124 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
6125 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
6126 INT flags, GDIPCONST GpMatrix *matrix)
6127 {
6128 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
6129 GpStatus stat;
6130 PointF *real_positions, real_position;
6131 POINT *pti;
6132 HFONT hfont;
6133 HDC hdc;
6134 int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
6135 DWORD max_glyphsize=0;
6136 GLYPHMETRICS glyphmetrics;
6137 static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
6138 BYTE *glyph_mask;
6139 BYTE *text_mask;
6140 int text_mask_stride;
6141 BYTE *pixel_data;
6142 int pixel_data_stride;
6143 GpRect pixel_area;
6144 UINT ggo_flags = GGO_GRAY8_BITMAP;
6145
6146 if (length <= 0)
6147 return Ok;
6148
6149 if (!(flags & DriverStringOptionsCmapLookup))
6150 ggo_flags |= GGO_GLYPH_INDEX;
6151
6152 if (flags & unsupported_flags)
6153 FIXME("Ignoring flags %x\n", flags & unsupported_flags);
6154
6155 pti = GdipAlloc(sizeof(POINT) * length);
6156 if (!pti)
6157 return OutOfMemory;
6158
6159 if (flags & DriverStringOptionsRealizedAdvance)
6160 {
6161 real_position = positions[0];
6162
6163 transform_and_round_points(graphics, pti, &real_position, 1);
6164 }
6165 else
6166 {
6167 real_positions = GdipAlloc(sizeof(PointF) * length);
6168 if (!real_positions)
6169 {
6170 GdipFree(pti);
6171 return OutOfMemory;
6172 }
6173
6174 memcpy(real_positions, positions, sizeof(PointF) * length);
6175
6176 transform_and_round_points(graphics, pti, real_positions, length);
6177
6178 GdipFree(real_positions);
6179 }
6180
6181 get_font_hfont(graphics, font, format, &hfont, matrix);
6182
6183 hdc = CreateCompatibleDC(0);
6184 SelectObject(hdc, hfont);
6185
6186 /* Get the boundaries of the text to be drawn */
6187 for (i=0; i<length; i++)
6188 {
6189 DWORD glyphsize;
6190 int left, top, right, bottom;
6191
6192 glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
6193 &glyphmetrics, 0, NULL, &identity);
6194
6195 if (glyphsize == GDI_ERROR)
6196 {
6197 ERR("GetGlyphOutlineW failed\n");
6198 GdipFree(pti);
6199 DeleteDC(hdc);
6200 DeleteObject(hfont);
6201 return GenericError;
6202 }
6203
6204 if (glyphsize > max_glyphsize)
6205 max_glyphsize = glyphsize;
6206
6207 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
6208 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
6209 right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
6210 bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
6211
6212 if (left < min_x) min_x = left;
6213 if (top < min_y) min_y = top;
6214 if (right > max_x) max_x = right;
6215 if (bottom > max_y) max_y = bottom;
6216
6217 if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
6218 {
6219 pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
6220 pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
6221 }
6222 }
6223
6224 glyph_mask = GdipAlloc(max_glyphsize);
6225 text_mask = GdipAlloc((max_x - min_x) * (max_y - min_y));
6226 text_mask_stride = max_x - min_x;
6227
6228 if (!(glyph_mask && text_mask))
6229 {
6230 GdipFree(glyph_mask);
6231 GdipFree(text_mask);
6232 GdipFree(pti);
6233 DeleteDC(hdc);
6234 DeleteObject(hfont);
6235 return OutOfMemory;
6236 }
6237
6238 /* Generate a mask for the text */
6239 for (i=0; i<length; i++)
6240 {
6241 int left, top, stride;
6242
6243 GetGlyphOutlineW(hdc, text[i], ggo_flags,
6244 &glyphmetrics, max_glyphsize, glyph_mask, &identity);
6245
6246 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
6247 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
6248 stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
6249
6250 for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
6251 {
6252 BYTE *glyph_val = glyph_mask + y * stride;
6253 BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
6254 for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
6255 {
6256 *text_val = min(64, *text_val + *glyph_val);
6257 glyph_val++;
6258 text_val++;
6259 }
6260 }
6261 }
6262
6263 GdipFree(pti);
6264 DeleteDC(hdc);
6265 DeleteObject(hfont);
6266 GdipFree(glyph_mask);
6267
6268 /* get the brush data */
6269 pixel_data = GdipAlloc(4 * (max_x - min_x) * (max_y - min_y));
6270 if (!pixel_data)
6271 {
6272 GdipFree(text_mask);
6273 return OutOfMemory;
6274 }
6275
6276 pixel_area.X = min_x;
6277 pixel_area.Y = min_y;
6278 pixel_area.Width = max_x - min_x;
6279 pixel_area.Height = max_y - min_y;
6280 pixel_data_stride = pixel_area.Width * 4;
6281
6282 stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
6283 if (stat != Ok)
6284 {
6285 GdipFree(text_mask);
6286 GdipFree(pixel_data);
6287 return stat;
6288 }
6289
6290 /* multiply the brush data by the mask */
6291 for (y=0; y<pixel_area.Height; y++)
6292 {
6293 BYTE *text_val = text_mask + text_mask_stride * y;
6294 BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
6295 for (x=0; x<pixel_area.Width; x++)
6296 {
6297 *pixel_val = (*pixel_val) * (*text_val) / 64;
6298 text_val++;
6299 pixel_val+=4;
6300 }
6301 }
6302
6303 GdipFree(text_mask);
6304
6305 /* draw the result */
6306 stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
6307 pixel_area.Height, pixel_data_stride);
6308
6309 GdipFree(pixel_data);
6310
6311 return stat;
6312 }
6313
6314 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6315 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
6316 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
6317 INT flags, GDIPCONST GpMatrix *matrix)
6318 {
6319 GpStatus stat = NotImplemented;
6320
6321 if (length == -1)
6322 length = strlenW(text);
6323
6324 if (graphics->hdc && !graphics->alpha_hdc &&
6325 ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
6326 brush->bt == BrushTypeSolidColor &&
6327 (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
6328 stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
6329 brush, positions, flags, matrix);
6330 if (stat == NotImplemented)
6331 stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
6332 brush, positions, flags, matrix);
6333 return stat;
6334 }
6335
6336 /*****************************************************************************
6337 * GdipDrawDriverString [GDIPLUS.@]
6338 */
6339 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
6340 GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
6341 GDIPCONST PointF *positions, INT flags,
6342 GDIPCONST GpMatrix *matrix )
6343 {
6344 TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
6345
6346 if (!graphics || !text || !font || !brush || !positions)
6347 return InvalidParameter;
6348
6349 return draw_driver_string(graphics, text, length, font, NULL,
6350 brush, positions, flags, matrix);
6351 }
6352
6353 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
6354 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
6355 {
6356 FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
6357 return NotImplemented;
6358 }
6359
6360 /*****************************************************************************
6361 * GdipIsVisibleClipEmpty [GDIPLUS.@]
6362 */
6363 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
6364 {
6365 GpStatus stat;
6366 GpRegion* rgn;
6367
6368 TRACE("(%p, %p)\n", graphics, res);
6369
6370 if((stat = GdipCreateRegion(&rgn)) != Ok)
6371 return stat;
6372
6373 if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
6374 goto cleanup;
6375
6376 stat = GdipIsEmptyRegion(rgn, graphics, res);
6377
6378 cleanup:
6379 GdipDeleteRegion(rgn);
6380 return stat;
6381 }
6382
6383 GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
6384 {
6385 static int calls;
6386
6387 TRACE("(%p) stub\n", graphics);
6388
6389 if(!(calls++))
6390 FIXME("not implemented\n");
6391
6392 return NotImplemented;
6393 }