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