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