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