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