ReactOS  0.4.13-dev-563-g0561610
graphics.c
Go to the documentation of this file.
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 
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
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 
72  GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
74 
75 /* Converts from gdiplus path point type to gdi path point type. */
77 {
78  BYTE ret;
79 
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 
97 
98  return ret;
99 }
100 
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  }
120  {
121  const GpLineGradient *line = (const GpLineGradient *)brush;
122  argb = line->startcolor;
123  break;
124  }
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 
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 
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 
221 {
222  switch (lb->lbStyle)
223  {
224  case BS_PATTERN:
226  break;
227  }
228  return Ok;
229 }
230 
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 
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;
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 
310 static void restore_dc(GpGraphics *graphics, INT state)
311 {
313  RestoreDC(graphics->hdc, state);
314 }
315 
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 
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;
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 
351 static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
352 {
353  GpRegion *rgn;
355  GpStatus stat;
356  BOOL identity;
357 
359 
360  if (stat == Ok)
362 
363  if (stat == Ok)
364  stat = GdipCloneRegion(graphics->clip, &rgn);
365 
366  if (stat == Ok)
367  {
368  if (!identity)
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 */
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 
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;
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 
442  (void**)&temp_bits, NULL, 0);
443 
444  if ((GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
445  GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE) ||
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 
453  gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
454  hdc, 0, 0, src_width, src_height);
455  DeleteDC(hdc);
457 
458  return Ok;
459 }
460 
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  {
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  {
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 
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 
549 
550  return stat;
551  }
552 }
553 
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 */
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 
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 
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 
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 
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 
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 */
712 {
713  UINT x, y;
714  INT i;
715 
716  if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
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
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 
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] ||
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. */
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  {
880  /* FIXME: Include a greater range for the prefilter? */
883  left = (INT)(floorf(srcx));
884  top = (INT)(floorf(srcy));
885  right = (INT)(ceilf(srcx+srcwidth));
886  bottom = (INT)(ceilf(srcy+srcheight));
887  break;
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. */
918  {
919  left = 0;
920  right = bitmap->width-1;
921  }
922 
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 
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 
982 static inline int positive_ceilf(float f)
983 {
984  return f - (int)f > 0.0f ? f + 1.0f : f;
985 }
986 
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 */
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  }
1034  {
1035  FLOAT pixel_offset;
1036  switch (offset_mode)
1037  {
1038  default:
1039  case PixelOffsetModeNone:
1041  pixel_offset = 0.5;
1042  break;
1043 
1044  case PixelOffsetModeHalf:
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 
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 */
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  }
1103  {
1104  FLOAT pixel_offset;
1105  switch (offset_mode)
1106  {
1107  default:
1108  case PixelOffsetModeNone:
1110  pixel_offset = 0.5;
1111  break;
1112 
1113  case PixelOffsetModeHalf:
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 
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 */
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  }
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 
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  {
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 
1225 {
1226  switch (brush->bt)
1227  {
1228  case BrushTypeSolidColor:
1229  case BrushTypeHatchFill:
1231  case BrushTypeTextureFill:
1232  case BrushTypePathGradient:
1233  return TRUE;
1234  default:
1235  return FALSE;
1236  }
1237 }
1238 
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  }
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. */
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. */
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 
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,
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  {
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 
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 
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 
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. */
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;
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 
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 
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 
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 
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 
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);
1837  GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
1841 
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. */
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. */
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. */
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. */
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;
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++){
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 
2068 
2069  round_points(pti, ptcopy, count);
2070 
2071  for(i = 0; i < count; 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 
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 
2102 
2103 typedef struct _GraphicsContainerItem {
2104  struct list entry;
2107 
2121 
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 
2157 {
2160 }
2161 
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 
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 
2240 {
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 
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 */
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 
2299 
2300 end:
2301  GdipDeleteRegion(tmp);
2302  return stat;
2303 }
2304 
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;
2332  lf->lfQuality = DEFAULT_QUALITY;
2333  lf->lfPitchAndFamily = 0;
2334  strcpyW(lf->lfFaceName, font->family->FamilyName);
2335 }
2336 
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 
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 
2396 {
2397  TRACE("(%p, %p)\n", hdc, graphics);
2398 
2399  return GdipCreateFromHDC2(hdc, NULL, graphics);
2400 }
2401 
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 
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 
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 
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 
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 */
2552 {
2553  TRACE("(%p, %p)\n", hwnd, graphics);
2554 
2555  return GdipCreateFromHWND(hwnd, graphics);
2556 }
2557 
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)
2571  else if(access & GENERIC_READ)
2573  else
2574  return InvalidParameter;
2575 
2577 
2578  return hresult_to_status(ret);
2579 }
2580 
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  {
2593  if (stat != Ok)
2594  return stat;
2595  }
2596 
2597  if(graphics->owndc)
2598  ReleaseDC(graphics->hwnd, graphics->hdc);
2599 
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 
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 
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 
2642  return status;
2643 }
2644 
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 
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 
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 
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 
2703  if (status != Ok) return status;
2704 
2706  if (status == Ok)
2707  status = GdipDrawPath(graphics, pen, path);
2708 
2710  return status;
2711 }
2712 
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 
2746 {
2747  TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2748 
2749  return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
2750 }
2751 
2754 {
2755  TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2756 
2757  return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
2758 }
2759 
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 
2775  if (status != Ok) return status;
2776 
2778  if (status == Ok)
2779  status = GdipDrawPath(graphics, pen, path);
2780 
2782 
2783  return status;
2784 }
2785 
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 
2816 {
2817  TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2818 
2819  return GdipDrawCurve2(graphics,pen,points,count,1.0);
2820 }
2821 
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. */
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 
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 
2875  return status;
2876 }
2877 
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 
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 
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 
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 
2950  if (status != Ok) return status;
2951 
2953  if (status == Ok)
2954  status = GdipDrawPath(graphics, pen, path);
2955 
2957  return status;
2958 }
2959 
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 
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 
2980 
2981  return GdipDrawImagePointRect(graphics, image, x, y,
2982  0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
2983 }
2984 
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 
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 
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 
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 
3039 
3040  return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
3042 }
3043 
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 
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 
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;
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  {
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;
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 
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 
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  REAL delta_xx, delta_xy, delta_yx, delta_yy;
3268 
3269  /* Transform the bits as needed to the destination. */
3270  dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
3271  if (!dst_data)
3272  {
3273  heap_free(src_data);
3274  return OutOfMemory;
3275  }
3276 
3277  dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
3278 
3279  GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
3280 
3281  x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
3282  x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
3283  y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
3284  y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
3285 
3286  delta_yy = dst_area.top * y_dy;
3287  delta_yx = dst_area.top * y_dx;
3288 
3289  for (y=dst_area.top; y<dst_area.bottom; y++)
3290  {
3291  delta_xx = dst_area.left * x_dx;
3292  delta_xy = dst_area.left * x_dy;
3293 
3294  for (x=dst_area.left; x<dst_area.right; x++)
3295  {
3296  GpPointF src_pointf;
3297  ARGB *dst_color;
3298 
3299  src_pointf.X = dst_to_src_points[0].X + delta_xx + delta_yx;
3300  src_pointf.Y = dst_to_src_points[0].Y + delta_xy + delta_yy;
3301 
3302  dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
3303 
3304  if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
3305  {
3306  if (lockeddata.PixelFormat != PixelFormat32bppPARGB)
3307  *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3308  imageAttributes, interpolation, offset_mode);
3309  else
3310  *dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
3311  imageAttributes, interpolation, offset_mode);
3312  }
3313  else
3314  *dst_color = 0;
3315 
3316  delta_xx += x_dx;
3317  delta_yx += y_dx;
3318  }
3319 
3320  delta_xy += x_dy;
3321  delta_yy += y_dy;
3322  }
3323  }
3324  else
3325  {
3326  dst_data = src_data;
3327  dst_stride = src_stride;
3328  }
3329 
3330  gdi_transform_acquire(graphics);
3331 
3332  stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
3333  dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
3334  lockeddata.PixelFormat);
3335 
3336  gdi_transform_release(graphics);
3337 
3338  heap_free(src_data);
3339 
3340  heap_free(dst_dyn_data);
3341 
3342  return stat;
3343  }
3344  else
3345  {
3346  HDC hdc;
3347  BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
3348  HBITMAP hbitmap, old_hbm=NULL;
3349  HRGN hrgn;
3350  INT save_state;
3351 
3352  if (!(bitmap->format == PixelFormat16bppRGB555 ||
3353  bitmap->format == PixelFormat24bppRGB ||
3354  bitmap->format == PixelFormat32bppRGB ||
3355  bitmap->format == PixelFormat32bppPARGB))
3356  {
3357  BITMAPINFOHEADER bih;
3358  BYTE *temp_bits;
3360 
3361  /* we can't draw a bitmap of this format directly */
3362  hdc = CreateCompatibleDC(0);
3363  temp_hdc = TRUE;
3364  temp_bitmap = TRUE;
3365 
3366  bih.biSize = sizeof(BITMAPINFOHEADER);
3367  bih.biWidth = bitmap->width;
3368  bih.biHeight = -bitmap->height;
3369  bih.biPlanes = 1;
3370  bih.biBitCount = 32;
3371  bih.biCompression = BI_RGB;
3372  bih.biSizeImage = 0;
3373  bih.biXPelsPerMeter = 0;
3374  bih.biYPelsPerMeter = 0;
3375  bih.biClrUsed = 0;
3376  bih.biClrImportant = 0;
3377 
3379  (void**)&temp_bits, NULL, 0);
3380 
3381  if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3383  else
3385 
3387  bitmap->width*4, temp_bits, dst_format,
3388  bitmap->stride, bitmap->bits, bitmap->format,
3389  bitmap->image.palette);
3390  }
3391  else
3392  {
3393  if (bitmap->hbitmap)
3394  hbitmap = bitmap->hbitmap;
3395  else
3396  {
3398  temp_bitmap = TRUE;
3399  }
3400 
3401  hdc = bitmap->hdc;
3402  temp_hdc = (hdc == 0);
3403  }
3404 
3405  if (temp_hdc)
3406  {
3407  if (!hdc) hdc = CreateCompatibleDC(0);
3408  old_hbm = SelectObject(hdc, hbitmap);
3409  }
3410 
3411  save_state = SaveDC(graphics->hdc);
3412 
3413  stat = get_clip_hrgn(graphics, &hrgn);
3414 
3415  if (stat == Ok)
3416  {
3417  ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
3418  DeleteObject(hrgn);
3419  }
3420 
3421  gdi_transform_acquire(graphics);
3422 
3423  if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
3424  {
3425  gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
3426  hdc, srcx, srcy, srcwidth, srcheight);
3427  }
3428  else
3429  {
3430  StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
3431  hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
3432  }
3433 
3434  gdi_transform_release(graphics);
3435 
3436  RestoreDC(graphics->hdc, save_state);
3437 
3438  if (temp_hdc)
3439  {
3440  SelectObject(hdc, old_hbm);
3441  DeleteDC(hdc);
3442  }
3443 
3444  if (temp_bitmap)
3446  }
3447  }
3448  else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
3449  {
3450  GpRectF rc;
3451 
3452  rc.X = srcx;
3453  rc.Y = srcy;
3454  rc.Width = srcwidth;
3455  rc.Height = srcheight;
3456 
3458  points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
3459  }
3460  else
3461  {
3462  WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
3463  return InvalidParameter;
3464  }
3465 
3466  return Ok;
3467 }
3468 
3470  GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
3471  INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3472  DrawImageAbort callback, VOID * callbackData)
3473 {
3474  GpPointF pointsF[3];
3475  INT i;
3476 
3477  TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
3478  srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
3479  callbackData);
3480 
3481  if(!points || count!=3)
3482  return InvalidParameter;
3483 
3484  for(i = 0; i < count; i++){
3485  pointsF[i].X = (REAL)points[i].X;
3486  pointsF[i].Y = (REAL)points[i].Y;
3487  }
3488 
3489  return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
3490  (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
3491  callback, callbackData);
3492 }
3493 
3495  REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
3496  REAL srcwidth, REAL srcheight, GpUnit srcUnit,
3498  VOID * callbackData)
3499 {
3500  GpPointF points[3];
3501 
3502  TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
3503  graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3504  srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3505 
3506  points[0].X = dstx;
3507  points[0].Y = dsty;
3508  points[1].X = dstx + dstwidth;
3509  points[1].Y = dsty;
3510  points[2].X = dstx;
3511  points[2].Y = dsty + dstheight;
3512 
3513  return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3514  srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
3515 }
3516 
3518  INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
3519  INT srcwidth, INT srcheight, GpUnit srcUnit,
3521  VOID * callbackData)
3522 {
3523  GpPointF points[3];
3524 
3525  TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
3526  graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
3527  srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3528 
3529  points[0].X = dstx;
3530  points[0].Y = dsty;
3531  points[1].X = dstx + dstwidth;
3532  points[1].Y = dsty;
3533  points[2].X = dstx;
3534  points[2].Y = dsty + dstheight;
3535 
3536  return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
3537  srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
3538 }
3539 
3542 {
3543  RectF bounds;
3544  GpUnit unit;
3545  GpStatus ret;
3546 
3547  TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
3548 
3549  if(!graphics || !image)
3550  return InvalidParameter;
3551 
3552  ret = GdipGetImageBounds(image, &bounds, &unit);
3553  if(ret != Ok)
3554  return ret;
3555 
3556  return GdipDrawImageRectRect(graphics, image, x, y, width, height,
3557  bounds.X, bounds.Y, bounds.Width, bounds.Height,
3558  unit, NULL, NULL, NULL);
3559 }
3560 
3562  INT x, INT y, INT width, INT height)
3563 {
3564  TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
3565 
3566  return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
3567 }
3568 
3570  REAL y1, REAL x2, REAL y2)
3571 {
3572  GpPointF pt[2];
3573 
3574  TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
3575 
3576  if (!pen)
3577  return InvalidParameter;
3578 
3579  if (pen->unit == UnitPixel && pen->width <= 0.0)
3580  return Ok;
3581 
3582  pt[0].X = x1;
3583  pt[0].Y = y1;
3584  pt[1].X = x2;
3585  pt[1].Y = y2;
3586  return GdipDrawLines(graphics, pen, pt, 2);
3587 }
3588 
3590  INT y1, INT x2, INT y2)
3591 {
3592  TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
3593 
3594  return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
3595 }
3596 
3599 {
3600  GpStatus status;
3601  GpPath *path;
3602 
3603  TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3604 
3605  if(!pen || !graphics || (count < 2))
3606  return InvalidParameter;
3607 
3608  if(graphics->busy)
3609  return ObjectBusy;
3610 
3612  if (status != Ok) return status;
3613 
3615  if (status == Ok)
3616  status = GdipDrawPath(graphics, pen, path);
3617 
3619  return status;
3620 }
3621 
3623  GpPoint *points, INT count)
3624 {
3625  GpStatus retval;
3626  GpPointF *ptf;
3627  int i;
3628 
3629  TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3630 
3631  ptf = heap_alloc_zero(count * sizeof(GpPointF));
3632  if(!ptf) return OutOfMemory;
3633 
3634  for(i = 0; i < count; i ++){
3635  ptf[i].X = (REAL) points[i].X;
3636  ptf[i].Y = (REAL) points[i].Y;
3637  }
3638 
3639  retval = GdipDrawLines(graphics, pen, ptf, count);
3640 
3641  heap_free(ptf);
3642  return retval;
3643 }
3644 
3646 {
3647  INT save_state;
3648  GpStatus retval;
3649  HRGN hrgn=NULL;
3650 
3651  save_state = prepare_dc(graphics, pen);
3652 
3653  retval = get_clip_hrgn(graphics, &hrgn);
3654 
3655  if (retval != Ok)
3656  goto end;
3657 
3658  ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
3659 
3660  gdi_transform_acquire(graphics);
3661 
3662  retval = draw_poly(graphics, pen, path->pathdata.Points,
3663  path->pathdata.Types, path->pathdata.Count, TRUE);
3664 
3665  gdi_transform_release(graphics);
3666 
3667 end:
3668  restore_dc(graphics, save_state);
3669  DeleteObject(hrgn);
3670 
3671  return retval;
3672 }
3673 
3675 {
3676  GpStatus stat;
3677  GpPath* flat_path;
3679  GpRectF gp_bound_rect;
3680  GpRect gp_output_area;
3681  RECT output_area;
3682  INT output_height, output_width;
3683  DWORD *output_bits, *brush_bits=NULL;
3684  int i;
3685  static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0};
3686  const BYTE *dash_pattern;
3687  INT dash_pattern_size;
3688  BYTE *dyn_dash_pattern = NULL;
3689 
3690  stat = GdipClonePath(path, &flat_path);
3691 
3692  if (stat != Ok)
3693  return stat;
3694 
3696 
3697  if (stat == Ok)
3698  {
3701 
3702  if (stat == Ok)
3703  stat = GdipFlattenPath(flat_path, transform, 1.0);
3704 
3706  }
3707 
3708  /* estimate the output size in pixels, can be larger than necessary */
3709  if (stat == Ok)
3710  {
3711  output_area.left = floorf(flat_path->pathdata.Points[0].X);
3712  output_area.right = ceilf(flat_path->pathdata.Points[0].X);
3713  output_area.top = floorf(flat_path->pathdata.Points[0].Y);
3714  output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y);
3715 
3716  for (i=1; i<flat_path->pathdata.Count; i++)
3717  {
3718  REAL x, y;
3719  x = flat_path->pathdata.Points[i].X;
3720  y = flat_path->pathdata.Points[i].Y;
3721 
3722  if (floorf(x) < output_area.left) output_area.left = floorf(x);
3723  if (floorf(y) < output_area.top) output_area.top = floorf(y);
3724  if (ceilf(x) > output_area.right) output_area.right = ceilf(x);
3725  if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
3726  }
3727 
3728  stat = get_graphics_device_bounds(graphics, &gp_bound_rect);
3729  }
3730 
3731  if (stat == Ok)
3732  {
3733  output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
3734  output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
3735  output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
3736  output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
3737 
3738  output_width = output_area.right - output_area.left + 1;
3739  output_height = output_area.bottom - output_area.top + 1;
3740 
3741  if (output_width <= 0 || output_height <= 0)
3742  {
3743  GdipDeletePath(flat_path);
3744  return Ok;
3745  }
3746 
3747  gp_output_area.X = output_area.left;
3748  gp_output_area.Y = output_area.top;
3749  gp_output_area.Width = output_width;
3750  gp_output_area.Height = output_height;
3751 
3752  output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
3753  if (!output_bits)
3754  stat = OutOfMemory;
3755  }
3756 
3757  if (stat == Ok)
3758  {
3759  if (pen->brush->bt != BrushTypeSolidColor)
3760  {
3761  /* allocate and draw brush output */
3762  brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
3763 
3764  if (brush_bits)
3765  {
3766  stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
3767  &gp_output_area, output_width);
3768  }
3769  else
3770  stat = OutOfMemory;
3771  }
3772 
3773  if (stat == Ok)
3774  {
3775  /* convert dash pattern to bool array */
3776  switch (pen->dash)
3777  {
3778  case DashStyleCustom:
3779  {
3780  dash_pattern_size = 0;
3781 
3782  for (i=0; i < pen->numdashes; i++)
3783  dash_pattern_size += gdip_round(pen->dashes[i]);
3784 
3785  if (dash_pattern_size != 0)
3786  {
3787  dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
3788 
3789  if (dyn_dash_pattern)
3790  {
3791  int j=0;
3792  for (i=0; i < pen->numdashes; i++)
3793  {
3794  int k;
3795  for (k=0; k < gdip_round(pen->dashes[i]); k++)
3796  dyn_dash_pattern[j++] = (i&1)^1;
3797  }
3798  }
3799  else
3800  stat = OutOfMemory;
3801 
3802  break;
3803  }
3804  /* else fall through */
3805  }
3806  case DashStyleSolid:
3807  default:
3808  dash_pattern = static_dash_pattern;
3809  dash_pattern_size = 1;
3810  break;
3811  case DashStyleDash:
3812  dash_pattern = static_dash_pattern;
3813  dash_pattern_size = 4;
3814  break;
3815  case DashStyleDot:
3816  dash_pattern = &static_dash_pattern[4];
3817  dash_pattern_size = 2;
3818  break;
3819  case DashStyleDashDot:
3820  dash_pattern = static_dash_pattern;
3821  dash_pattern_size = 6;
3822  break;
3823  case DashStyleDashDotDot:
3824  dash_pattern = static_dash_pattern;
3825  dash_pattern_size = 8;
3826  break;
3827  }
3828  }
3829 
3830  if (stat == Ok)
3831  {
3832  /* trace path */
3833  GpPointF subpath_start = flat_path->pathdata.Points[0];
3834  INT prev_x = INT_MAX, prev_y = INT_MAX;
3835  int dash_pos = dash_pattern_size - 1;
3836 
3837  for (i=0; i < flat_path->pathdata.Count; i++)
3838  {
3839  BYTE type, type2;
3840  GpPointF start_point, end_point;
3841  GpPoint start_pointi, end_pointi;
3842 
3843  type = flat_path->pathdata.Types[i];
3844  if (i+1 < flat_path->pathdata.Count)
3845  type2 = flat_path->pathdata.Types[i+1];
3846  else
3847  type2 = PathPointTypeStart;
3848 
3849  start_point = flat_path->pathdata.Points[i];
3850 
3852  subpath_start = start_point;
3853 
3855  end_point = subpath_start;
3856  else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
3857  continue;
3858  else
3859  end_point = flat_path->pathdata.Points[i+1];
3860 
3861  start_pointi.X = floorf(start_point.X);
3862  start_pointi.Y = floorf(start_point.Y);
3863  end_pointi.X = floorf(end_point.X);
3864  end_pointi.Y = floorf(end_point.Y);
3865 
3866  if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
3867  continue;
3868 
3869  /* draw line segment */
3870  if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
3871  {
3872  INT x, y, start_y, end_y, step;
3873 
3874  if (start_pointi.Y < end_pointi.Y)
3875  {
3876  step = 1;
3877  start_y = ceilf(start_point.Y) - output_area.top;
3878  end_y = end_pointi.Y - output_area.top;
3879  }
3880  else
3881  {
3882  step = -1;
3883  start_y = start_point.Y - output_area.top;
3884  end_y = ceilf(end_point.Y) - output_area.top;
3885  }
3886 
3887  for (y=start_y; y != (end_y+step); y+=step)
3888  {
3889  x = gdip_round( start_point.X +
3890  (end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
3891  - output_area.left;
3892 
3893  if (x == prev_x && y == prev_y)
3894  continue;
3895 
3896  prev_x = x;
3897  prev_y = y;
3898  dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
3899 
3900  if (!dash_pattern[dash_pos])
3901  continue;
3902 
3903  if (x < 0 || x >= output_width || y < 0 || y >= output_height)
3904  continue;
3905 
3906  if (brush_bits)
3907  output_bits[x + y*output_width] = brush_bits[x + y*output_width];
3908  else
3909  output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
3910  }
3911  }
3912  else
3913  {
3914  INT x, y, start_x, end_x, step;
3915 
3916  if (start_pointi.X < end_pointi.X)
3917  {
3918  step = 1;
3919  start_x = ceilf(start_point.X) - output_area.left;
3920  end_x = end_pointi.X - output_area.left;
3921  }
3922  else
3923  {
3924  step = -1;
3925  start_x = start_point.X - output_area.left;
3926  end_x = ceilf(end_point.X) - output_area.left;
3927  }
3928 
3929  for (x=start_x; x != (end_x+step); x+=step)
3930  {
3931  y = gdip_round( start_point.Y +
3932  (end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
3933  - output_area.top;
3934 
3935  if (x == prev_x && y == prev_y)
3936  continue;
3937 
3938  prev_x = x;
3939  prev_y = y;
3940  dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
3941 
3942  if (!dash_pattern[dash_pos])
3943  continue;
3944 
3945  if (x < 0 || x >= output_width || y < 0 || y >= output_height)
3946  continue;
3947 
3948  if (brush_bits)
3949  output_bits[x + y*output_width] = brush_bits[x + y*output_width];
3950  else
3951  output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
3952  }
3953  }
3954  }
3955  }
3956