xref: /reactos/dll/win32/gdiplus/graphics.c (revision e128cbc6)
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, &center_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, &center_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, &center_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, &region);
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, &region);
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(&region->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