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