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