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 #ifdef __REACTOS__ // CORE-19456 3268 DOUBLE delta_xx, delta_xy, delta_yx, delta_yy; 3269 #else 3270 REAL delta_xx, delta_xy, delta_yx, delta_yy; 3271 #endif 3272 3273 /* Transform the bits as needed to the destination. */ 3274 dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top)); 3275 if (!dst_data) 3276 { 3277 heap_free(src_data); 3278 return OutOfMemory; 3279 } 3280 3281 dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left); 3282 3283 GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3); 3284 3285 x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X; 3286 x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y; 3287 y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X; 3288 y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y; 3289 3290 delta_yy = dst_area.top * y_dy; 3291 delta_yx = dst_area.top * y_dx; 3292 3293 for (y=dst_area.top; y<dst_area.bottom; y++) 3294 { 3295 delta_xx = dst_area.left * x_dx; 3296 delta_xy = dst_area.left * x_dy; 3297 3298 for (x=dst_area.left; x<dst_area.right; x++) 3299 { 3300 GpPointF src_pointf; 3301 ARGB *dst_color; 3302 3303 src_pointf.X = dst_to_src_points[0].X + delta_xx + delta_yx; 3304 src_pointf.Y = dst_to_src_points[0].Y + delta_xy + delta_yy; 3305 3306 dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left)); 3307 3308 if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight) 3309 { 3310 if (lockeddata.PixelFormat != PixelFormat32bppPARGB) 3311 *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, 3312 imageAttributes, interpolation, offset_mode); 3313 else 3314 *dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, 3315 imageAttributes, interpolation, offset_mode); 3316 } 3317 else 3318 *dst_color = 0; 3319 3320 delta_xx += x_dx; 3321 delta_yx += y_dx; 3322 } 3323 3324 delta_xy += x_dy; 3325 delta_yy += y_dy; 3326 } 3327 } 3328 else 3329 { 3330 dst_data = src_data; 3331 dst_stride = src_stride; 3332 } 3333 3334 gdi_transform_acquire(graphics); 3335 3336 stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top, 3337 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride, 3338 lockeddata.PixelFormat); 3339 3340 gdi_transform_release(graphics); 3341 3342 heap_free(src_data); 3343 3344 heap_free(dst_dyn_data); 3345 3346 return stat; 3347 } 3348 else 3349 { 3350 HDC hdc; 3351 BOOL temp_hdc = FALSE, temp_bitmap = FALSE; 3352 HBITMAP hbitmap, old_hbm=NULL; 3353 HRGN hrgn; 3354 INT save_state; 3355 3356 if (!(bitmap->format == PixelFormat16bppRGB555 || 3357 bitmap->format == PixelFormat24bppRGB || 3358 bitmap->format == PixelFormat32bppRGB || 3359 bitmap->format == PixelFormat32bppPARGB)) 3360 { 3361 BITMAPINFOHEADER bih; 3362 BYTE *temp_bits; 3363 PixelFormat dst_format; 3364 3365 /* we can't draw a bitmap of this format directly */ 3366 hdc = CreateCompatibleDC(0); 3367 temp_hdc = TRUE; 3368 temp_bitmap = TRUE; 3369 3370 bih.biSize = sizeof(BITMAPINFOHEADER); 3371 bih.biWidth = bitmap->width; 3372 bih.biHeight = -bitmap->height; 3373 bih.biPlanes = 1; 3374 bih.biBitCount = 32; 3375 bih.biCompression = BI_RGB; 3376 bih.biSizeImage = 0; 3377 bih.biXPelsPerMeter = 0; 3378 bih.biYPelsPerMeter = 0; 3379 bih.biClrUsed = 0; 3380 bih.biClrImportant = 0; 3381 3382 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, 3383 (void**)&temp_bits, NULL, 0); 3384 3385 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha)) 3386 dst_format = PixelFormat32bppPARGB; 3387 else 3388 dst_format = PixelFormat32bppRGB; 3389 3390 convert_pixels(bitmap->width, bitmap->height, 3391 bitmap->width*4, temp_bits, dst_format, 3392 bitmap->stride, bitmap->bits, bitmap->format, 3393 bitmap->image.palette); 3394 } 3395 else 3396 { 3397 if (bitmap->hbitmap) 3398 hbitmap = bitmap->hbitmap; 3399 else 3400 { 3401 GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0); 3402 temp_bitmap = TRUE; 3403 } 3404 3405 hdc = bitmap->hdc; 3406 temp_hdc = (hdc == 0); 3407 } 3408 3409 if (temp_hdc) 3410 { 3411 if (!hdc) hdc = CreateCompatibleDC(0); 3412 old_hbm = SelectObject(hdc, hbitmap); 3413 } 3414 3415 save_state = SaveDC(graphics->hdc); 3416 3417 stat = get_clip_hrgn(graphics, &hrgn); 3418 3419 if (stat == Ok) 3420 { 3421 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); 3422 DeleteObject(hrgn); 3423 } 3424 3425 gdi_transform_acquire(graphics); 3426 3427 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha)) 3428 { 3429 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y, 3430 hdc, srcx, srcy, srcwidth, srcheight); 3431 } 3432 else 3433 { 3434 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y, 3435 hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY); 3436 } 3437 3438 gdi_transform_release(graphics); 3439 3440 RestoreDC(graphics->hdc, save_state); 3441 3442 if (temp_hdc) 3443 { 3444 SelectObject(hdc, old_hbm); 3445 DeleteDC(hdc); 3446 } 3447 3448 if (temp_bitmap) 3449 DeleteObject(hbitmap); 3450 } 3451 } 3452 else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf) 3453 { 3454 GpRectF rc; 3455 3456 rc.X = srcx; 3457 rc.Y = srcy; 3458 rc.Width = srcwidth; 3459 rc.Height = srcheight; 3460 3461 return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image, 3462 points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes); 3463 } 3464 else 3465 { 3466 WARN("GpImage with nothing we can draw (metafile in wrong state?)\n"); 3467 return InvalidParameter; 3468 } 3469 3470 return Ok; 3471 } 3472 3473 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image, 3474 GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth, 3475 INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes, 3476 DrawImageAbort callback, VOID * callbackData) 3477 { 3478 GpPointF pointsF[3]; 3479 INT i; 3480 3481 TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count, 3482 srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback, 3483 callbackData); 3484 3485 if(!points || count!=3) 3486 return InvalidParameter; 3487 3488 for(i = 0; i < count; i++){ 3489 pointsF[i].X = (REAL)points[i].X; 3490 pointsF[i].Y = (REAL)points[i].Y; 3491 } 3492 3493 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy, 3494 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes, 3495 callback, callbackData); 3496 } 3497 3498 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image, 3499 REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy, 3500 REAL srcwidth, REAL srcheight, GpUnit srcUnit, 3501 GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback, 3502 VOID * callbackData) 3503 { 3504 GpPointF points[3]; 3505 3506 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n", 3507 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy, 3508 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData); 3509 3510 points[0].X = dstx; 3511 points[0].Y = dsty; 3512 points[1].X = dstx + dstwidth; 3513 points[1].Y = dsty; 3514 points[2].X = dstx; 3515 points[2].Y = dsty + dstheight; 3516 3517 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy, 3518 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData); 3519 } 3520 3521 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image, 3522 INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy, 3523 INT srcwidth, INT srcheight, GpUnit srcUnit, 3524 GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback, 3525 VOID * callbackData) 3526 { 3527 GpPointF points[3]; 3528 3529 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", 3530 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy, 3531 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData); 3532 3533 points[0].X = dstx; 3534 points[0].Y = dsty; 3535 points[1].X = dstx + dstwidth; 3536 points[1].Y = dsty; 3537 points[2].X = dstx; 3538 points[2].Y = dsty + dstheight; 3539 3540 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy, 3541 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData); 3542 } 3543 3544 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image, 3545 REAL x, REAL y, REAL width, REAL height) 3546 { 3547 RectF bounds; 3548 GpUnit unit; 3549 GpStatus ret; 3550 3551 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height); 3552 3553 if(!graphics || !image) 3554 return InvalidParameter; 3555 3556 ret = GdipGetImageBounds(image, &bounds, &unit); 3557 if(ret != Ok) 3558 return ret; 3559 3560 return GdipDrawImageRectRect(graphics, image, x, y, width, height, 3561 bounds.X, bounds.Y, bounds.Width, bounds.Height, 3562 unit, NULL, NULL, NULL); 3563 } 3564 3565 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image, 3566 INT x, INT y, INT width, INT height) 3567 { 3568 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height); 3569 3570 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height); 3571 } 3572 3573 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1, 3574 REAL y1, REAL x2, REAL y2) 3575 { 3576 GpPointF pt[2]; 3577 3578 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2); 3579 3580 if (!pen) 3581 return InvalidParameter; 3582 3583 if (pen->unit == UnitPixel && pen->width <= 0.0) 3584 return Ok; 3585 3586 pt[0].X = x1; 3587 pt[0].Y = y1; 3588 pt[1].X = x2; 3589 pt[1].Y = y2; 3590 return GdipDrawLines(graphics, pen, pt, 2); 3591 } 3592 3593 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1, 3594 INT y1, INT x2, INT y2) 3595 { 3596 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2); 3597 3598 return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2); 3599 } 3600 3601 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST 3602 GpPointF *points, INT count) 3603 { 3604 GpStatus status; 3605 GpPath *path; 3606 3607 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count); 3608 3609 if(!pen || !graphics || (count < 2)) 3610 return InvalidParameter; 3611 3612 if(graphics->busy) 3613 return ObjectBusy; 3614 3615 status = GdipCreatePath(FillModeAlternate, &path); 3616 if (status != Ok) return status; 3617 3618 status = GdipAddPathLine2(path, points, count); 3619 if (status == Ok) 3620 status = GdipDrawPath(graphics, pen, path); 3621 3622 GdipDeletePath(path); 3623 return status; 3624 } 3625 3626 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST 3627 GpPoint *points, INT count) 3628 { 3629 GpStatus retval; 3630 GpPointF *ptf; 3631 int i; 3632 3633 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count); 3634 3635 ptf = heap_alloc_zero(count * sizeof(GpPointF)); 3636 if(!ptf) return OutOfMemory; 3637 3638 for(i = 0; i < count; i ++){ 3639 ptf[i].X = (REAL) points[i].X; 3640 ptf[i].Y = (REAL) points[i].Y; 3641 } 3642 3643 retval = GdipDrawLines(graphics, pen, ptf, count); 3644 3645 heap_free(ptf); 3646 return retval; 3647 } 3648 3649 static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path) 3650 { 3651 INT save_state; 3652 GpStatus retval; 3653 HRGN hrgn=NULL; 3654 3655 save_state = prepare_dc(graphics, pen); 3656 3657 retval = get_clip_hrgn(graphics, &hrgn); 3658 3659 if (retval != Ok) 3660 goto end; 3661 3662 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); 3663 3664 gdi_transform_acquire(graphics); 3665 3666 retval = draw_poly(graphics, pen, path->pathdata.Points, 3667 path->pathdata.Types, path->pathdata.Count, TRUE); 3668 3669 gdi_transform_release(graphics); 3670 3671 end: 3672 restore_dc(graphics, save_state); 3673 DeleteObject(hrgn); 3674 3675 return retval; 3676 } 3677 3678 static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPath *path) 3679 { 3680 GpStatus stat; 3681 GpPath* flat_path; 3682 GpMatrix* transform; 3683 GpRectF gp_bound_rect; 3684 GpRect gp_output_area; 3685 RECT output_area; 3686 INT output_height, output_width; 3687 DWORD *output_bits, *brush_bits=NULL; 3688 int i; 3689 static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0}; 3690 const BYTE *dash_pattern; 3691 INT dash_pattern_size; 3692 BYTE *dyn_dash_pattern = NULL; 3693 3694 stat = GdipClonePath(path, &flat_path); 3695 3696 if (stat != Ok) 3697 return stat; 3698 3699 stat = GdipCreateMatrix(&transform); 3700 3701 if (stat == Ok) 3702 { 3703 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, 3704 CoordinateSpaceWorld, transform); 3705 3706 if (stat == Ok) 3707 stat = GdipFlattenPath(flat_path, transform, 1.0); 3708 3709 GdipDeleteMatrix(transform); 3710 } 3711 3712 /* estimate the output size in pixels, can be larger than necessary */ 3713 if (stat == Ok) 3714 { 3715 output_area.left = floorf(flat_path->pathdata.Points[0].X); 3716 output_area.right = ceilf(flat_path->pathdata.Points[0].X); 3717 output_area.top = floorf(flat_path->pathdata.Points[0].Y); 3718 output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y); 3719 3720 for (i=1; i<flat_path->pathdata.Count; i++) 3721 { 3722 REAL x, y; 3723 x = flat_path->pathdata.Points[i].X; 3724 y = flat_path->pathdata.Points[i].Y; 3725 3726 if (floorf(x) < output_area.left) output_area.left = floorf(x); 3727 if (floorf(y) < output_area.top) output_area.top = floorf(y); 3728 if (ceilf(x) > output_area.right) output_area.right = ceilf(x); 3729 if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y); 3730 } 3731 3732 stat = get_graphics_device_bounds(graphics, &gp_bound_rect); 3733 } 3734 3735 if (stat == Ok) 3736 { 3737 output_area.left = max(output_area.left, floorf(gp_bound_rect.X)); 3738 output_area.top = max(output_area.top, floorf(gp_bound_rect.Y)); 3739 output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width)); 3740 output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height)); 3741 3742 output_width = output_area.right - output_area.left + 1; 3743 output_height = output_area.bottom - output_area.top + 1; 3744 3745 if (output_width <= 0 || output_height <= 0) 3746 { 3747 GdipDeletePath(flat_path); 3748 return Ok; 3749 } 3750 3751 gp_output_area.X = output_area.left; 3752 gp_output_area.Y = output_area.top; 3753 gp_output_area.Width = output_width; 3754 gp_output_area.Height = output_height; 3755 3756 output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD)); 3757 if (!output_bits) 3758 stat = OutOfMemory; 3759 } 3760 3761 if (stat == Ok) 3762 { 3763 if (pen->brush->bt != BrushTypeSolidColor) 3764 { 3765 /* allocate and draw brush output */ 3766 brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD)); 3767 3768 if (brush_bits) 3769 { 3770 stat = brush_fill_pixels(graphics, pen->brush, brush_bits, 3771 &gp_output_area, output_width); 3772 } 3773 else 3774 stat = OutOfMemory; 3775 } 3776 3777 if (stat == Ok) 3778 { 3779 /* convert dash pattern to bool array */ 3780 switch (pen->dash) 3781 { 3782 case DashStyleCustom: 3783 { 3784 dash_pattern_size = 0; 3785 3786 for (i=0; i < pen->numdashes; i++) 3787 dash_pattern_size += gdip_round(pen->dashes[i]); 3788 3789 if (dash_pattern_size != 0) 3790 { 3791 dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size); 3792 3793 if (dyn_dash_pattern) 3794 { 3795 int j=0; 3796 for (i=0; i < pen->numdashes; i++) 3797 { 3798 int k; 3799 for (k=0; k < gdip_round(pen->dashes[i]); k++) 3800 dyn_dash_pattern[j++] = (i&1)^1; 3801 } 3802 } 3803 else 3804 stat = OutOfMemory; 3805 3806 break; 3807 } 3808 /* else fall through */ 3809 } 3810 case DashStyleSolid: 3811 default: 3812 dash_pattern = static_dash_pattern; 3813 dash_pattern_size = 1; 3814 break; 3815 case DashStyleDash: 3816 dash_pattern = static_dash_pattern; 3817 dash_pattern_size = 4; 3818 break; 3819 case DashStyleDot: 3820 dash_pattern = &static_dash_pattern[4]; 3821 dash_pattern_size = 2; 3822 break; 3823 case DashStyleDashDot: 3824 dash_pattern = static_dash_pattern; 3825 dash_pattern_size = 6; 3826 break; 3827 case DashStyleDashDotDot: 3828 dash_pattern = static_dash_pattern; 3829 dash_pattern_size = 8; 3830 break; 3831 } 3832 } 3833 3834 if (stat == Ok) 3835 { 3836 /* trace path */ 3837 GpPointF subpath_start = flat_path->pathdata.Points[0]; 3838 INT prev_x = INT_MAX, prev_y = INT_MAX; 3839 int dash_pos = dash_pattern_size - 1; 3840 3841 for (i=0; i < flat_path->pathdata.Count; i++) 3842 { 3843 BYTE type, type2; 3844 GpPointF start_point, end_point; 3845 GpPoint start_pointi, end_pointi; 3846 3847 type = flat_path->pathdata.Types[i]; 3848 if (i+1 < flat_path->pathdata.Count) 3849 type2 = flat_path->pathdata.Types[i+1]; 3850 else 3851 type2 = PathPointTypeStart; 3852 3853 start_point = flat_path->pathdata.Points[i]; 3854 3855 if ((type & PathPointTypePathTypeMask) == PathPointTypeStart) 3856 subpath_start = start_point; 3857 3858 if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath) 3859 end_point = subpath_start; 3860 else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart) 3861 continue; 3862 else 3863 end_point = flat_path->pathdata.Points[i+1]; 3864 3865 start_pointi.X = floorf(start_point.X); 3866 start_pointi.Y = floorf(start_point.Y); 3867 end_pointi.X = floorf(end_point.X); 3868 end_pointi.Y = floorf(end_point.Y); 3869 3870 if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y) 3871 continue; 3872 3873 /* draw line segment */ 3874 if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X)) 3875 { 3876 INT x, y, start_y, end_y, step; 3877 3878 if (start_pointi.Y < end_pointi.Y) 3879 { 3880 step = 1; 3881 start_y = ceilf(start_point.Y) - output_area.top; 3882 end_y = end_pointi.Y - output_area.top; 3883 } 3884 else 3885 { 3886 step = -1; 3887 start_y = start_point.Y - output_area.top; 3888 end_y = ceilf(end_point.Y) - output_area.top; 3889 } 3890 3891 for (y=start_y; y != (end_y+step); y+=step) 3892 { 3893 x = gdip_round( start_point.X + 3894 (end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) ) 3895 - output_area.left; 3896 3897 if (x == prev_x && y == prev_y) 3898 continue; 3899 3900 prev_x = x; 3901 prev_y = y; 3902 dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1; 3903 3904 if (!dash_pattern[dash_pos]) 3905 continue; 3906 3907 if (x < 0 || x >= output_width || y < 0 || y >= output_height) 3908 continue; 3909 3910 if (brush_bits) 3911 output_bits[x + y*output_width] = brush_bits[x + y*output_width]; 3912 else 3913 output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color; 3914 } 3915 } 3916 else 3917 { 3918 INT x, y, start_x, end_x, step; 3919 3920 if (start_pointi.X < end_pointi.X) 3921 { 3922 step = 1; 3923 start_x = ceilf(start_point.X) - output_area.left; 3924 end_x = end_pointi.X - output_area.left; 3925 } 3926 else 3927 { 3928 step = -1; 3929 start_x = start_point.X - output_area.left; 3930 end_x = ceilf(end_point.X) - output_area.left; 3931 } 3932 3933 for (x=start_x; x != (end_x+step); x+=step) 3934 { 3935 y = gdip_round( start_point.Y + 3936 (end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) ) 3937 - output_area.top; 3938 3939 if (x == prev_x && y == prev_y) 3940 continue; 3941 3942 prev_x = x; 3943 prev_y = y; 3944 dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1; 3945 3946 if (!dash_pattern[dash_pos]) 3947 continue; 3948 3949 if (x < 0 || x >= output_width || y < 0 || y >= output_height) 3950 continue; 3951 3952 if (brush_bits) 3953 output_bits[x + y*output_width] = brush_bits[x + y*output_width]; 3954 else 3955 output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color; 3956 } 3957 } 3958 } 3959 } 3960 3961 /* draw output image */ 3962 if (stat == Ok) 3963 { 3964 gdi_transform_acquire(graphics); 3965 3966 stat = alpha_blend_pixels(graphics, output_area.left, output_area.top, 3967 (BYTE*)output_bits, output_width, output_height, output_width * 4, 3968 PixelFormat32bppARGB); 3969 3970 gdi_transform_release(graphics); 3971 } 3972 3973 heap_free(brush_bits); 3974 heap_free(dyn_dash_pattern); 3975 heap_free(output_bits); 3976 } 3977 3978 GdipDeletePath(flat_path); 3979 3980 return stat; 3981 } 3982 3983 static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path) 3984 { 3985 GpStatus stat; 3986 GpPath *wide_path; 3987 GpMatrix *transform=NULL; 3988 REAL flatness=1.0; 3989 3990 /* Check if the final pen thickness in pixels is too thin. */ 3991 if (pen->unit == UnitPixel) 3992 { 3993 if (pen->width < 1.415) 3994 return SOFTWARE_GdipDrawThinPath(graphics, pen, path); 3995 } 3996 else 3997 { 3998 GpPointF points[3] = {{0,0}, {1,0}, {0,1}}; 3999 4000 points[1].X = pen->width; 4001 points[2].Y = pen->width; 4002 4003 stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, 4004 CoordinateSpaceWorld, points, 3); 4005 4006 if (stat != Ok) 4007 return stat; 4008 4009 if (((points[1].X-points[0].X)*(points[1].X-points[0].X) + 4010 (points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) && 4011 ((points[2].X-points[0].X)*(points[2].X-points[0].X) + 4012 (points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001)) 4013 return SOFTWARE_GdipDrawThinPath(graphics, pen, path); 4014 } 4015 4016 stat = GdipClonePath(path, &wide_path); 4017 4018 if (stat != Ok) 4019 return stat; 4020 4021 if (pen->unit == UnitPixel) 4022 { 4023 /* We have to transform this to device coordinates to get the widths right. */ 4024 stat = GdipCreateMatrix(&transform); 4025 4026 if (stat == Ok) 4027 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, 4028 CoordinateSpaceWorld, transform); 4029 } 4030 else 4031 { 4032 /* Set flatness based on the final coordinate space */ 4033 GpMatrix t; 4034 4035 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, 4036 CoordinateSpaceWorld, &t); 4037 4038 if (stat != Ok) 4039 return stat; 4040 4041 flatness = 1.0/sqrt(fmax( 4042 t.matrix[0] * t.matrix[0] + t.matrix[1] * t.matrix[1], 4043 t.matrix[2] * t.matrix[2] + t.matrix[3] * t.matrix[3])); 4044 } 4045 4046 if (stat == Ok) 4047 stat = GdipWidenPath(wide_path, pen, transform, flatness); 4048 4049 if (pen->unit == UnitPixel) 4050 { 4051 /* Transform the path back to world coordinates */ 4052 if (stat == Ok) 4053 stat = GdipInvertMatrix(transform); 4054 4055 if (stat == Ok) 4056 stat = GdipTransformPath(wide_path, transform); 4057 } 4058 4059 /* Actually draw the path */ 4060 if (stat == Ok) 4061 stat = GdipFillPath(graphics, pen->brush, wide_path); 4062 4063 GdipDeleteMatrix(transform); 4064 4065 GdipDeletePath(wide_path); 4066 4067 return stat; 4068 } 4069 4070 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path) 4071 { 4072 GpStatus retval; 4073 4074 TRACE("(%p, %p, %p)\n", graphics, pen, path); 4075 4076 if(!pen || !graphics) 4077 return InvalidParameter; 4078 4079 if(graphics->busy) 4080 return ObjectBusy; 4081 4082 if (path->pathdata.Count == 0) 4083 return Ok; 4084 4085 if (graphics->image && graphics->image->type == ImageTypeMetafile) 4086 retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path); 4087 else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE)) 4088 retval = SOFTWARE_GdipDrawPath(graphics, pen, path); 4089 else 4090 retval = GDI32_GdipDrawPath(graphics, pen, path); 4091 4092 return retval; 4093 } 4094 4095 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x, 4096 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) 4097 { 4098 GpStatus status; 4099 GpPath *path; 4100 4101 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, 4102 width, height, startAngle, sweepAngle); 4103 4104 if(!graphics || !pen) 4105 return InvalidParameter; 4106 4107 if(graphics->busy) 4108 return ObjectBusy; 4109 4110 status = GdipCreatePath(FillModeAlternate, &path); 4111 if (status != Ok) return status; 4112 4113 status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle); 4114 if (status == Ok) 4115 status = GdipDrawPath(graphics, pen, path); 4116 4117 GdipDeletePath(path); 4118 return status; 4119 } 4120 4121 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x, 4122 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle) 4123 { 4124 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y, 4125 width, height, startAngle, sweepAngle); 4126 4127 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle); 4128 } 4129 4130 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x, 4131 REAL y, REAL width, REAL height) 4132 { 4133 GpStatus status; 4134 GpPath *path; 4135 4136 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height); 4137 4138 if(!pen || !graphics) 4139 return InvalidParameter; 4140 4141 if(graphics->busy) 4142 return ObjectBusy; 4143 4144 status = GdipCreatePath(FillModeAlternate, &path); 4145 if (status != Ok) return status; 4146 4147 status = GdipAddPathRectangle(path, x, y, width, height); 4148 if (status == Ok) 4149 status = GdipDrawPath(graphics, pen, path); 4150 4151 GdipDeletePath(path); 4152 return status; 4153 } 4154 4155 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x, 4156 INT y, INT width, INT height) 4157 { 4158 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height); 4159 4160 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height); 4161 } 4162 4163 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen, 4164 GDIPCONST GpRectF* rects, INT count) 4165 { 4166 GpStatus status; 4167 GpPath *path; 4168 4169 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count); 4170 4171 if(!graphics || !pen || !rects || count < 1) 4172 return InvalidParameter; 4173 4174 if(graphics->busy) 4175 return ObjectBusy; 4176 4177 status = GdipCreatePath(FillModeAlternate, &path); 4178 if (status != Ok) return status; 4179 4180 status = GdipAddPathRectangles(path, rects, count); 4181 if (status == Ok) 4182 status = GdipDrawPath(graphics, pen, path); 4183 4184 GdipDeletePath(path); 4185 return status; 4186 } 4187 4188 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen, 4189 GDIPCONST GpRect* rects, INT count) 4190 { 4191 GpRectF *rectsF; 4192 GpStatus ret; 4193 INT i; 4194 4195 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count); 4196 4197 if(!rects || count<=0) 4198 return InvalidParameter; 4199 4200 rectsF = heap_alloc_zero(sizeof(GpRectF) * count); 4201 if(!rectsF) 4202 return OutOfMemory; 4203 4204 for(i = 0;i < count;i++){ 4205 rectsF[i].X = (REAL)rects[i].X; 4206 rectsF[i].Y = (REAL)rects[i].Y; 4207 rectsF[i].Width = (REAL)rects[i].Width; 4208 rectsF[i].Height = (REAL)rects[i].Height; 4209 } 4210 4211 ret = GdipDrawRectangles(graphics, pen, rectsF, count); 4212 heap_free(rectsF); 4213 4214 return ret; 4215 } 4216 4217 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush, 4218 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill) 4219 { 4220 GpPath *path; 4221 GpStatus status; 4222 4223 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points, 4224 count, tension, fill); 4225 4226 if(!graphics || !brush || !points) 4227 return InvalidParameter; 4228 4229 if(graphics->busy) 4230 return ObjectBusy; 4231 4232 if(count == 1) /* Do nothing */ 4233 return Ok; 4234 4235 status = GdipCreatePath(fill, &path); 4236 if (status != Ok) return status; 4237 4238 status = GdipAddPathClosedCurve2(path, points, count, tension); 4239 if (status == Ok) 4240 status = GdipFillPath(graphics, brush, path); 4241 4242 GdipDeletePath(path); 4243 return status; 4244 } 4245 4246 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush, 4247 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill) 4248 { 4249 GpPointF *ptf; 4250 GpStatus stat; 4251 INT i; 4252 4253 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points, 4254 count, tension, fill); 4255 4256 if(!points || count == 0) 4257 return InvalidParameter; 4258 4259 if(count == 1) /* Do nothing */ 4260 return Ok; 4261 4262 ptf = heap_alloc_zero(sizeof(GpPointF)*count); 4263 if(!ptf) 4264 return OutOfMemory; 4265 4266 for(i = 0;i < count;i++){ 4267 ptf[i].X = (REAL)points[i].X; 4268 ptf[i].Y = (REAL)points[i].Y; 4269 } 4270 4271 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill); 4272 4273 heap_free(ptf); 4274 4275 return stat; 4276 } 4277 4278 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush, 4279 GDIPCONST GpPointF *points, INT count) 4280 { 4281 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count); 4282 return GdipFillClosedCurve2(graphics, brush, points, count, 4283 0.5f, FillModeAlternate); 4284 } 4285 4286 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush, 4287 GDIPCONST GpPoint *points, INT count) 4288 { 4289 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count); 4290 return GdipFillClosedCurve2I(graphics, brush, points, count, 4291 0.5f, FillModeAlternate); 4292 } 4293 4294 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x, 4295 REAL y, REAL width, REAL height) 4296 { 4297 GpStatus stat; 4298 GpPath *path; 4299 4300 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height); 4301 4302 if(!graphics || !brush) 4303 return InvalidParameter; 4304 4305 if(graphics->busy) 4306 return ObjectBusy; 4307 4308 stat = GdipCreatePath(FillModeAlternate, &path); 4309 4310 if (stat == Ok) 4311 { 4312 stat = GdipAddPathEllipse(path, x, y, width, height); 4313 4314 if (stat == Ok) 4315 stat = GdipFillPath(graphics, brush, path); 4316 4317 GdipDeletePath(path); 4318 } 4319 4320 return stat; 4321 } 4322 4323 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x, 4324 INT y, INT width, INT height) 4325 { 4326 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height); 4327 4328 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height); 4329 } 4330 4331 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path) 4332 { 4333 INT save_state; 4334 GpStatus retval; 4335 HRGN hrgn=NULL; 4336 4337 if(!graphics->hdc || !brush_can_fill_path(brush, TRUE)) 4338 return NotImplemented; 4339 4340 save_state = SaveDC(graphics->hdc); 4341 EndPath(graphics->hdc); 4342 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE 4343 : WINDING)); 4344 4345 retval = get_clip_hrgn(graphics, &hrgn); 4346 4347 if (retval != Ok) 4348 goto end; 4349 4350 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); 4351 4352 gdi_transform_acquire(graphics); 4353 4354 BeginPath(graphics->hdc); 4355 retval = draw_poly(graphics, NULL, path->pathdata.Points, 4356 path->pathdata.Types, path->pathdata.Count, FALSE); 4357 4358 if(retval == Ok) 4359 { 4360 EndPath(graphics->hdc); 4361 retval = brush_fill_path(graphics, brush); 4362 } 4363 4364 gdi_transform_release(graphics); 4365 4366 end: 4367 RestoreDC(graphics->hdc, save_state); 4368 DeleteObject(hrgn); 4369 4370 return retval; 4371 } 4372 4373 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path) 4374 { 4375 GpStatus stat; 4376 GpRegion *rgn; 4377 4378 if (!brush_can_fill_pixels(brush)) 4379 return NotImplemented; 4380 4381 /* FIXME: This could probably be done more efficiently without regions. */ 4382 4383 stat = GdipCreateRegionPath(path, &rgn); 4384 4385 if (stat == Ok) 4386 { 4387 stat = GdipFillRegion(graphics, brush, rgn); 4388 4389 GdipDeleteRegion(rgn); 4390 } 4391 4392 return stat; 4393 } 4394 4395 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path) 4396 { 4397 GpStatus stat = NotImplemented; 4398 4399 TRACE("(%p, %p, %p)\n", graphics, brush, path); 4400 4401 if(!brush || !graphics || !path) 4402 return InvalidParameter; 4403 4404 if(graphics->busy) 4405 return ObjectBusy; 4406 4407 if (!path->pathdata.Count) 4408 return Ok; 4409 4410 if (graphics->image && graphics->image->type == ImageTypeMetafile) 4411 return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path); 4412 4413 if (!graphics->image && !graphics->alpha_hdc) 4414 stat = GDI32_GdipFillPath(graphics, brush, path); 4415 4416 if (stat == NotImplemented) 4417 stat = SOFTWARE_GdipFillPath(graphics, brush, path); 4418 4419 if (stat == NotImplemented) 4420 { 4421 FIXME("Not implemented for brushtype %i\n", brush->bt); 4422 stat = Ok; 4423 } 4424 4425 return stat; 4426 } 4427 4428 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x, 4429 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle) 4430 { 4431 GpStatus stat; 4432 GpPath *path; 4433 4434 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", 4435 graphics, brush, x, y, width, height, startAngle, sweepAngle); 4436 4437 if(!graphics || !brush) 4438 return InvalidParameter; 4439 4440 if(graphics->busy) 4441 return ObjectBusy; 4442 4443 stat = GdipCreatePath(FillModeAlternate, &path); 4444 4445 if (stat == Ok) 4446 { 4447 stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle); 4448 4449 if (stat == Ok) 4450 stat = GdipFillPath(graphics, brush, path); 4451 4452 GdipDeletePath(path); 4453 } 4454 4455 return stat; 4456 } 4457 4458 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x, 4459 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle) 4460 { 4461 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", 4462 graphics, brush, x, y, width, height, startAngle, sweepAngle); 4463 4464 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle); 4465 } 4466 4467 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush, 4468 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode) 4469 { 4470 GpStatus stat; 4471 GpPath *path; 4472 4473 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode); 4474 4475 if(!graphics || !brush || !points || !count) 4476 return InvalidParameter; 4477 4478 if(graphics->busy) 4479 return ObjectBusy; 4480 4481 stat = GdipCreatePath(fillMode, &path); 4482 4483 if (stat == Ok) 4484 { 4485 stat = GdipAddPathPolygon(path, points, count); 4486 4487 if (stat == Ok) 4488 stat = GdipFillPath(graphics, brush, path); 4489 4490 GdipDeletePath(path); 4491 } 4492 4493 return stat; 4494 } 4495 4496 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush, 4497 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode) 4498 { 4499 GpStatus stat; 4500 GpPath *path; 4501 4502 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode); 4503 4504 if(!graphics || !brush || !points || !count) 4505 return InvalidParameter; 4506 4507 if(graphics->busy) 4508 return ObjectBusy; 4509 4510 stat = GdipCreatePath(fillMode, &path); 4511 4512 if (stat == Ok) 4513 { 4514 stat = GdipAddPathPolygonI(path, points, count); 4515 4516 if (stat == Ok) 4517 stat = GdipFillPath(graphics, brush, path); 4518 4519 GdipDeletePath(path); 4520 } 4521 4522 return stat; 4523 } 4524 4525 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush, 4526 GDIPCONST GpPointF *points, INT count) 4527 { 4528 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count); 4529 4530 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate); 4531 } 4532 4533 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush, 4534 GDIPCONST GpPoint *points, INT count) 4535 { 4536 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count); 4537 4538 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate); 4539 } 4540 4541 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush, 4542 REAL x, REAL y, REAL width, REAL height) 4543 { 4544 GpRectF rect; 4545 4546 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height); 4547 4548 rect.X = x; 4549 rect.Y = y; 4550 rect.Width = width; 4551 rect.Height = height; 4552 4553 return GdipFillRectangles(graphics, brush, &rect, 1); 4554 } 4555 4556 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush, 4557 INT x, INT y, INT width, INT height) 4558 { 4559 GpRectF rect; 4560 4561 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height); 4562 4563 rect.X = (REAL)x; 4564 rect.Y = (REAL)y; 4565 rect.Width = (REAL)width; 4566 rect.Height = (REAL)height; 4567 4568 return GdipFillRectangles(graphics, brush, &rect, 1); 4569 } 4570 4571 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects, 4572 INT count) 4573 { 4574 GpStatus status; 4575 GpPath *path; 4576 4577 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count); 4578 4579 if(!graphics || !brush || !rects || count <= 0) 4580 return InvalidParameter; 4581 4582 if (graphics->image && graphics->image->type == ImageTypeMetafile) 4583 { 4584 status = METAFILE_FillRectangles((GpMetafile*)graphics->image, brush, rects, count); 4585 /* FIXME: Add gdi32 drawing. */ 4586 return status; 4587 } 4588 4589 status = GdipCreatePath(FillModeAlternate, &path); 4590 if (status != Ok) return status; 4591 4592 status = GdipAddPathRectangles(path, rects, count); 4593 if (status == Ok) 4594 status = GdipFillPath(graphics, brush, path); 4595 4596 GdipDeletePath(path); 4597 return status; 4598 } 4599 4600 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects, 4601 INT count) 4602 { 4603 GpRectF *rectsF; 4604 GpStatus ret; 4605 INT i; 4606 4607 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count); 4608 4609 if(!rects || count <= 0) 4610 return InvalidParameter; 4611 4612 rectsF = heap_alloc_zero(sizeof(GpRectF)*count); 4613 if(!rectsF) 4614 return OutOfMemory; 4615 4616 for(i = 0; i < count; i++){ 4617 rectsF[i].X = (REAL)rects[i].X; 4618 rectsF[i].Y = (REAL)rects[i].Y; 4619 rectsF[i].Width = (REAL)rects[i].Width; 4620 rectsF[i].Height = (REAL)rects[i].Height; 4621 } 4622 4623 ret = GdipFillRectangles(graphics,brush,rectsF,count); 4624 heap_free(rectsF); 4625 4626 return ret; 4627 } 4628 4629 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush, 4630 GpRegion* region) 4631 { 4632 INT save_state; 4633 GpStatus status; 4634 HRGN hrgn; 4635 RECT rc; 4636 4637 if(!graphics->hdc || !brush_can_fill_path(brush, TRUE)) 4638 return NotImplemented; 4639 4640 save_state = SaveDC(graphics->hdc); 4641 EndPath(graphics->hdc); 4642 4643 hrgn = NULL; 4644 status = get_clip_hrgn(graphics, &hrgn); 4645 if (status != Ok) 4646 { 4647 RestoreDC(graphics->hdc, save_state); 4648 return status; 4649 } 4650 4651 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); 4652 DeleteObject(hrgn); 4653 4654 status = GdipGetRegionHRgn(region, graphics, &hrgn); 4655 if (status != Ok) 4656 { 4657 RestoreDC(graphics->hdc, save_state); 4658 return status; 4659 } 4660 4661 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND); 4662 DeleteObject(hrgn); 4663 4664 if (GetClipBox(graphics->hdc, &rc) != NULLREGION) 4665 { 4666 BeginPath(graphics->hdc); 4667 Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom); 4668 EndPath(graphics->hdc); 4669 4670 status = brush_fill_path(graphics, brush); 4671 } 4672 4673 RestoreDC(graphics->hdc, save_state); 4674 4675 4676 return status; 4677 } 4678 4679 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush, 4680 GpRegion* region) 4681 { 4682 GpStatus stat; 4683 GpRegion *temp_region; 4684 GpMatrix world_to_device; 4685 GpRectF graphics_bounds; 4686 DWORD *pixel_data; 4687 HRGN hregion; 4688 RECT bound_rect; 4689 GpRect gp_bound_rect; 4690 4691 if (!brush_can_fill_pixels(brush)) 4692 return NotImplemented; 4693 4694 stat = gdi_transform_acquire(graphics); 4695 4696 if (stat == Ok) 4697 stat = get_graphics_device_bounds(graphics, &graphics_bounds); 4698 4699 if (stat == Ok) 4700 stat = GdipCloneRegion(region, &temp_region); 4701 4702 if (stat == Ok) 4703 { 4704 stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, 4705 CoordinateSpaceWorld, &world_to_device); 4706 4707 if (stat == Ok) 4708 stat = GdipTransformRegion(temp_region, &world_to_device); 4709 4710 if (stat == Ok) 4711 stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect); 4712 4713 if (stat == Ok) 4714 stat = GdipGetRegionHRgn(temp_region, NULL, &hregion); 4715 4716 GdipDeleteRegion(temp_region); 4717 } 4718 4719 if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION) 4720 { 4721 DeleteObject(hregion); 4722 gdi_transform_release(graphics); 4723 return Ok; 4724 } 4725 4726 if (stat == Ok) 4727 { 4728 gp_bound_rect.X = bound_rect.left; 4729 gp_bound_rect.Y = bound_rect.top; 4730 gp_bound_rect.Width = bound_rect.right - bound_rect.left; 4731 gp_bound_rect.Height = bound_rect.bottom - bound_rect.top; 4732 4733 pixel_data = heap_alloc_zero(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height); 4734 if (!pixel_data) 4735 stat = OutOfMemory; 4736 4737 if (stat == Ok) 4738 { 4739 stat = brush_fill_pixels(graphics, brush, pixel_data, 4740 &gp_bound_rect, gp_bound_rect.Width); 4741 4742 if (stat == Ok) 4743 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X, 4744 gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width, 4745 gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion, 4746 PixelFormat32bppARGB); 4747 4748 heap_free(pixel_data); 4749 } 4750 4751 DeleteObject(hregion); 4752 } 4753 4754 gdi_transform_release(graphics); 4755 4756 return stat; 4757 } 4758 4759 /***************************************************************************** 4760 * GdipFillRegion [GDIPLUS.@] 4761 */ 4762 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush, 4763 GpRegion* region) 4764 { 4765 GpStatus stat = NotImplemented; 4766 4767 TRACE("(%p, %p, %p)\n", graphics, brush, region); 4768 4769 if (!(graphics && brush && region)) 4770 return InvalidParameter; 4771 4772 if(graphics->busy) 4773 return ObjectBusy; 4774 4775 if (!graphics->image && !graphics->alpha_hdc) 4776 stat = GDI32_GdipFillRegion(graphics, brush, region); 4777 4778 if (stat == NotImplemented) 4779 stat = SOFTWARE_GdipFillRegion(graphics, brush, region); 4780 4781 if (stat == NotImplemented) 4782 { 4783 FIXME("not implemented for brushtype %i\n", brush->bt); 4784 stat = Ok; 4785 } 4786 4787 return stat; 4788 } 4789 4790 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention) 4791 { 4792 TRACE("(%p,%u)\n", graphics, intention); 4793 4794 if(!graphics) 4795 return InvalidParameter; 4796 4797 if(graphics->busy) 4798 return ObjectBusy; 4799 4800 /* We have no internal operation queue, so there's no need to clear it. */ 4801 4802 if (graphics->hdc) 4803 GdiFlush(); 4804 4805 return Ok; 4806 } 4807 4808 /***************************************************************************** 4809 * GdipGetClipBounds [GDIPLUS.@] 4810 */ 4811 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect) 4812 { 4813 GpStatus status; 4814 GpRegion *clip; 4815 4816 TRACE("(%p, %p)\n", graphics, rect); 4817 4818 if(!graphics) 4819 return InvalidParameter; 4820 4821 if(graphics->busy) 4822 return ObjectBusy; 4823 4824 status = GdipCreateRegion(&clip); 4825 if (status != Ok) return status; 4826 4827 status = GdipGetClip(graphics, clip); 4828 if (status == Ok) 4829 status = GdipGetRegionBounds(clip, graphics, rect); 4830 4831 GdipDeleteRegion(clip); 4832 return status; 4833 } 4834 4835 /***************************************************************************** 4836 * GdipGetClipBoundsI [GDIPLUS.@] 4837 */ 4838 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect) 4839 { 4840 TRACE("(%p, %p)\n", graphics, rect); 4841 4842 if(!graphics) 4843 return InvalidParameter; 4844 4845 if(graphics->busy) 4846 return ObjectBusy; 4847 4848 return GdipGetRegionBoundsI(graphics->clip, graphics, rect); 4849 } 4850 4851 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */ 4852 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics, 4853 CompositingMode *mode) 4854 { 4855 TRACE("(%p, %p)\n", graphics, mode); 4856 4857 if(!graphics || !mode) 4858 return InvalidParameter; 4859 4860 if(graphics->busy) 4861 return ObjectBusy; 4862 4863 *mode = graphics->compmode; 4864 4865 return Ok; 4866 } 4867 4868 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */ 4869 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics, 4870 CompositingQuality *quality) 4871 { 4872 TRACE("(%p, %p)\n", graphics, quality); 4873 4874 if(!graphics || !quality) 4875 return InvalidParameter; 4876 4877 if(graphics->busy) 4878 return ObjectBusy; 4879 4880 *quality = graphics->compqual; 4881 4882 return Ok; 4883 } 4884 4885 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */ 4886 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics, 4887 InterpolationMode *mode) 4888 { 4889 TRACE("(%p, %p)\n", graphics, mode); 4890 4891 if(!graphics || !mode) 4892 return InvalidParameter; 4893 4894 if(graphics->busy) 4895 return ObjectBusy; 4896 4897 *mode = graphics->interpolation; 4898 4899 return Ok; 4900 } 4901 4902 /* FIXME: Need to handle color depths less than 24bpp */ 4903 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb) 4904 { 4905 FIXME("(%p, %p): Passing color unmodified\n", graphics, argb); 4906 4907 if(!graphics || !argb) 4908 return InvalidParameter; 4909 4910 if(graphics->busy) 4911 return ObjectBusy; 4912 4913 return Ok; 4914 } 4915 4916 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale) 4917 { 4918 TRACE("(%p, %p)\n", graphics, scale); 4919 4920 if(!graphics || !scale) 4921 return InvalidParameter; 4922 4923 if(graphics->busy) 4924 return ObjectBusy; 4925 4926 *scale = graphics->scale; 4927 4928 return Ok; 4929 } 4930 4931 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit) 4932 { 4933 TRACE("(%p, %p)\n", graphics, unit); 4934 4935 if(!graphics || !unit) 4936 return InvalidParameter; 4937 4938 if(graphics->busy) 4939 return ObjectBusy; 4940 4941 *unit = graphics->unit; 4942 4943 return Ok; 4944 } 4945 4946 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */ 4947 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode 4948 *mode) 4949 { 4950 TRACE("(%p, %p)\n", graphics, mode); 4951 4952 if(!graphics || !mode) 4953 return InvalidParameter; 4954 4955 if(graphics->busy) 4956 return ObjectBusy; 4957 4958 *mode = graphics->pixeloffset; 4959 4960 return Ok; 4961 } 4962 4963 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */ 4964 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode) 4965 { 4966 TRACE("(%p, %p)\n", graphics, mode); 4967 4968 if(!graphics || !mode) 4969 return InvalidParameter; 4970 4971 if(graphics->busy) 4972 return ObjectBusy; 4973 4974 *mode = graphics->smoothing; 4975 4976 return Ok; 4977 } 4978 4979 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast) 4980 { 4981 TRACE("(%p, %p)\n", graphics, contrast); 4982 4983 if(!graphics || !contrast) 4984 return InvalidParameter; 4985 4986 *contrast = graphics->textcontrast; 4987 4988 return Ok; 4989 } 4990 4991 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */ 4992 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics, 4993 TextRenderingHint *hint) 4994 { 4995 TRACE("(%p, %p)\n", graphics, hint); 4996 4997 if(!graphics || !hint) 4998 return InvalidParameter; 4999 5000 if(graphics->busy) 5001 return ObjectBusy; 5002 5003 *hint = graphics->texthint; 5004 5005 return Ok; 5006 } 5007 5008 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect) 5009 { 5010 GpRegion *clip_rgn; 5011 GpStatus stat; 5012 GpMatrix device_to_world; 5013 5014 TRACE("(%p, %p)\n", graphics, rect); 5015 5016 if(!graphics || !rect) 5017 return InvalidParameter; 5018 5019 if(graphics->busy) 5020 return ObjectBusy; 5021 5022 /* intersect window and graphics clipping regions */ 5023 if((stat = GdipCreateRegion(&clip_rgn)) != Ok) 5024 return stat; 5025 5026 if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok) 5027 goto cleanup; 5028 5029 /* transform to world coordinates */ 5030 if((stat = get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world)) != Ok) 5031 goto cleanup; 5032 5033 if((stat = GdipTransformRegion(clip_rgn, &device_to_world)) != Ok) 5034 goto cleanup; 5035 5036 /* get bounds of the region */ 5037 stat = GdipGetRegionBounds(clip_rgn, graphics, rect); 5038 5039 cleanup: 5040 GdipDeleteRegion(clip_rgn); 5041 5042 return stat; 5043 } 5044 5045 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect) 5046 { 5047 GpRectF rectf; 5048 GpStatus stat; 5049 5050 TRACE("(%p, %p)\n", graphics, rect); 5051 5052 if(!graphics || !rect) 5053 return InvalidParameter; 5054 5055 if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok) 5056 { 5057 rect->X = gdip_round(rectf.X); 5058 rect->Y = gdip_round(rectf.Y); 5059 rect->Width = gdip_round(rectf.Width); 5060 rect->Height = gdip_round(rectf.Height); 5061 } 5062 5063 return stat; 5064 } 5065 5066 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix) 5067 { 5068 TRACE("(%p, %p)\n", graphics, matrix); 5069 5070 if(!graphics || !matrix) 5071 return InvalidParameter; 5072 5073 if(graphics->busy) 5074 return ObjectBusy; 5075 5076 *matrix = graphics->worldtrans; 5077 return Ok; 5078 } 5079 5080 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color) 5081 { 5082 GpSolidFill *brush; 5083 GpStatus stat; 5084 GpRectF wnd_rect; 5085 5086 TRACE("(%p, %x)\n", graphics, color); 5087 5088 if(!graphics) 5089 return InvalidParameter; 5090 5091 if(graphics->busy) 5092 return ObjectBusy; 5093 5094 if (graphics->image && graphics->image->type == ImageTypeMetafile) 5095 return METAFILE_GraphicsClear((GpMetafile*)graphics->image, color); 5096 5097 if((stat = GdipCreateSolidFill(color, &brush)) != Ok) 5098 return stat; 5099 5100 if((stat = GdipGetVisibleClipBounds(graphics, &wnd_rect)) != Ok){ 5101 GdipDeleteBrush((GpBrush*)brush); 5102 return stat; 5103 } 5104 5105 GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y, 5106 wnd_rect.Width, wnd_rect.Height); 5107 5108 GdipDeleteBrush((GpBrush*)brush); 5109 5110 return Ok; 5111 } 5112 5113 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res) 5114 { 5115 TRACE("(%p, %p)\n", graphics, res); 5116 5117 if(!graphics || !res) 5118 return InvalidParameter; 5119 5120 return GdipIsEmptyRegion(graphics->clip, graphics, res); 5121 } 5122 5123 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result) 5124 { 5125 GpStatus stat; 5126 GpRegion* rgn; 5127 GpPointF pt; 5128 5129 TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result); 5130 5131 if(!graphics || !result) 5132 return InvalidParameter; 5133 5134 if(graphics->busy) 5135 return ObjectBusy; 5136 5137 pt.X = x; 5138 pt.Y = y; 5139 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, 5140 CoordinateSpaceWorld, &pt, 1)) != Ok) 5141 return stat; 5142 5143 if((stat = GdipCreateRegion(&rgn)) != Ok) 5144 return stat; 5145 5146 if((stat = get_visible_clip_region(graphics, rgn)) != Ok) 5147 goto cleanup; 5148 5149 stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result); 5150 5151 cleanup: 5152 GdipDeleteRegion(rgn); 5153 return stat; 5154 } 5155 5156 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result) 5157 { 5158 return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result); 5159 } 5160 5161 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result) 5162 { 5163 GpStatus stat; 5164 GpRegion* rgn; 5165 GpPointF pts[2]; 5166 5167 TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result); 5168 5169 if(!graphics || !result) 5170 return InvalidParameter; 5171 5172 if(graphics->busy) 5173 return ObjectBusy; 5174 5175 pts[0].X = x; 5176 pts[0].Y = y; 5177 pts[1].X = x + width; 5178 pts[1].Y = y + height; 5179 5180 if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, 5181 CoordinateSpaceWorld, pts, 2)) != Ok) 5182 return stat; 5183 5184 pts[1].X -= pts[0].X; 5185 pts[1].Y -= pts[0].Y; 5186 5187 if((stat = GdipCreateRegion(&rgn)) != Ok) 5188 return stat; 5189 5190 if((stat = get_visible_clip_region(graphics, rgn)) != Ok) 5191 goto cleanup; 5192 5193 stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result); 5194 5195 cleanup: 5196 GdipDeleteRegion(rgn); 5197 return stat; 5198 } 5199 5200 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result) 5201 { 5202 return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result); 5203 } 5204 5205 GpStatus gdip_format_string(HDC hdc, 5206 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, 5207 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, 5208 gdip_format_string_callback callback, void *user_data) 5209 { 5210 WCHAR* stringdup; 5211 int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth, 5212 nheight, lineend, lineno = 0; 5213 RectF bounds; 5214 StringAlignment halign; 5215 GpStatus stat = Ok; 5216 SIZE size; 5217 HotkeyPrefix hkprefix; 5218 INT *hotkeyprefix_offsets=NULL; 5219 INT hotkeyprefix_count=0; 5220 INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0; 5221 BOOL seen_prefix = FALSE; 5222 5223 if(length == -1) length = lstrlenW(string); 5224 5225 stringdup = heap_alloc_zero((length + 1) * sizeof(WCHAR)); 5226 if(!stringdup) return OutOfMemory; 5227 5228 if (!format) 5229 format = &default_drawstring_format; 5230 5231 nwidth = rect->Width; 5232 nheight = rect->Height; 5233 if (ignore_empty_clip) 5234 { 5235 if (!nwidth) nwidth = INT_MAX; 5236 if (!nheight) nheight = INT_MAX; 5237 } 5238 5239 hkprefix = format->hkprefix; 5240 5241 if (hkprefix == HotkeyPrefixShow) 5242 { 5243 for (i=0; i<length; i++) 5244 { 5245 if (string[i] == '&') 5246 hotkeyprefix_count++; 5247 } 5248 } 5249 5250 if (hotkeyprefix_count) 5251 hotkeyprefix_offsets = heap_alloc_zero(sizeof(INT) * hotkeyprefix_count); 5252 5253 hotkeyprefix_count = 0; 5254 5255 for(i = 0, j = 0; i < length; i++){ 5256 /* FIXME: This makes the indexes passed to callback inaccurate. */ 5257 if(!isprintW(string[i]) && (string[i] != '\n')) 5258 continue; 5259 5260 /* FIXME: tabs should be handled using tabstops from stringformat */ 5261 if (string[i] == '\t') 5262 continue; 5263 5264 if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&') 5265 hotkeyprefix_offsets[hotkeyprefix_count++] = j; 5266 else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&') 5267 { 5268 seen_prefix = TRUE; 5269 continue; 5270 } 5271 5272 seen_prefix = FALSE; 5273 5274 stringdup[j] = string[i]; 5275 j++; 5276 } 5277 5278 length = j; 5279 5280 halign = format->align; 5281 5282 while(sum < length){ 5283 GetTextExtentExPointW(hdc, stringdup + sum, length - sum, 5284 nwidth, &fit, NULL, &size); 5285 fitcpy = fit; 5286 5287 if(fit == 0) 5288 break; 5289 5290 for(lret = 0; lret < fit; lret++) 5291 if(*(stringdup + sum + lret) == '\n') 5292 break; 5293 5294 /* Line break code (may look strange, but it imitates windows). */ 5295 if(lret < fit) 5296 lineend = fit = lret; /* this is not an off-by-one error */ 5297 else if(fit < (length - sum)){ 5298 if(*(stringdup + sum + fit) == ' ') 5299 while(*(stringdup + sum + fit) == ' ') 5300 fit++; 5301 else 5302 while(*(stringdup + sum + fit - 1) != ' '){ 5303 fit--; 5304 5305 if(*(stringdup + sum + fit) == '\t') 5306 break; 5307 5308 if(fit == 0){ 5309 fit = fitcpy; 5310 break; 5311 } 5312 } 5313 lineend = fit; 5314 while(*(stringdup + sum + lineend - 1) == ' ' || 5315 *(stringdup + sum + lineend - 1) == '\t') 5316 lineend--; 5317 } 5318 else 5319 lineend = fit; 5320 5321 GetTextExtentExPointW(hdc, stringdup + sum, lineend, 5322 nwidth, &j, NULL, &size); 5323 5324 bounds.Width = size.cx; 5325 5326 if(height + size.cy > nheight) 5327 { 5328 if (format->attr & StringFormatFlagsLineLimit) 5329 break; 5330 bounds.Height = nheight - (height + size.cy); 5331 } 5332 else 5333 bounds.Height = size.cy; 5334 5335 bounds.Y = rect->Y + height; 5336 5337 switch (halign) 5338 { 5339 case StringAlignmentNear: 5340 default: 5341 bounds.X = rect->X; 5342 break; 5343 case StringAlignmentCenter: 5344 bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2); 5345 break; 5346 case StringAlignmentFar: 5347 bounds.X = rect->X + rect->Width - bounds.Width; 5348 break; 5349 } 5350 5351 for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++) 5352 if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend) 5353 break; 5354 5355 stat = callback(hdc, stringdup, sum, lineend, 5356 font, rect, format, lineno, &bounds, 5357 &hotkeyprefix_offsets[hotkeyprefix_pos], 5358 hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); 5359 5360 if (stat != Ok) 5361 break; 5362 5363 sum += fit + (lret < fitcpy ? 1 : 0); 5364 height += size.cy; 5365 lineno++; 5366 5367 hotkeyprefix_pos = hotkeyprefix_end_pos; 5368 5369 if(height > nheight) 5370 break; 5371 5372 /* Stop if this was a linewrap (but not if it was a linebreak). */ 5373 if ((lret == fitcpy) && (format->attr & StringFormatFlagsNoWrap)) 5374 break; 5375 } 5376 5377 heap_free(stringdup); 5378 heap_free(hotkeyprefix_offsets); 5379 5380 return stat; 5381 } 5382 5383 struct measure_ranges_args { 5384 GpRegion **regions; 5385 REAL rel_width, rel_height; 5386 }; 5387 5388 static GpStatus measure_ranges_callback(HDC hdc, 5389 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, 5390 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, 5391 INT lineno, const RectF *bounds, INT *underlined_indexes, 5392 INT underlined_index_count, void *user_data) 5393 { 5394 int i; 5395 GpStatus stat = Ok; 5396 struct measure_ranges_args *args = user_data; 5397 5398 for (i=0; i<format->range_count; i++) 5399 { 5400 INT range_start = max(index, format->character_ranges[i].First); 5401 INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length); 5402 if (range_start < range_end) 5403 { 5404 GpRectF range_rect; 5405 SIZE range_size; 5406 5407 range_rect.Y = bounds->Y / args->rel_height; 5408 range_rect.Height = bounds->Height / args->rel_height; 5409 5410 GetTextExtentExPointW(hdc, string + index, range_start - index, 5411 INT_MAX, NULL, NULL, &range_size); 5412 range_rect.X = (bounds->X + range_size.cx) / args->rel_width; 5413 5414 GetTextExtentExPointW(hdc, string + index, range_end - index, 5415 INT_MAX, NULL, NULL, &range_size); 5416 range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; 5417 5418 stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); 5419 if (stat != Ok) 5420 break; 5421 } 5422 } 5423 5424 return stat; 5425 } 5426 5427 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, 5428 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font, 5429 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat, 5430 INT regionCount, GpRegion** regions) 5431 { 5432 GpStatus stat; 5433 int i; 5434 HFONT gdifont, oldfont; 5435 struct measure_ranges_args args; 5436 HDC hdc, temp_hdc=NULL; 5437 GpPointF pt[3]; 5438 RectF scaled_rect; 5439 REAL margin_x; 5440 5441 TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_wn(string, length), 5442 length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions); 5443 5444 if (!(graphics && string && font && layoutRect && stringFormat && regions)) 5445 return InvalidParameter; 5446 5447 if (regionCount < stringFormat->range_count) 5448 return InvalidParameter; 5449 5450 if(!graphics->hdc) 5451 { 5452 hdc = temp_hdc = CreateCompatibleDC(0); 5453 if (!temp_hdc) return OutOfMemory; 5454 } 5455 else 5456 hdc = graphics->hdc; 5457 5458 if (stringFormat->attr) 5459 TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr); 5460 5461 pt[0].X = 0.0; 5462 pt[0].Y = 0.0; 5463 pt[1].X = 1.0; 5464 pt[1].Y = 0.0; 5465 pt[2].X = 0.0; 5466 pt[2].Y = 1.0; 5467 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3); 5468 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+ 5469 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X)); 5470 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+ 5471 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X)); 5472 5473 margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0; 5474 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres); 5475 5476 scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width; 5477 scaled_rect.Y = layoutRect->Y * args.rel_height; 5478 scaled_rect.Width = layoutRect->Width * args.rel_width; 5479 scaled_rect.Height = layoutRect->Height * args.rel_height; 5480 5481 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23; 5482 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23; 5483 5484 get_font_hfont(graphics, font, stringFormat, &gdifont, NULL); 5485 oldfont = SelectObject(hdc, gdifont); 5486 5487 for (i=0; i<stringFormat->range_count; i++) 5488 { 5489 stat = GdipSetEmpty(regions[i]); 5490 if (stat != Ok) 5491 return stat; 5492 } 5493 5494 args.regions = regions; 5495 5496 gdi_transform_acquire(graphics); 5497 5498 stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat, 5499 (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args); 5500 5501 gdi_transform_release(graphics); 5502 5503 SelectObject(hdc, oldfont); 5504 DeleteObject(gdifont); 5505 5506 if (temp_hdc) 5507 DeleteDC(temp_hdc); 5508 5509 return stat; 5510 } 5511 5512 struct measure_string_args { 5513 RectF *bounds; 5514 INT *codepointsfitted; 5515 INT *linesfilled; 5516 REAL rel_width, rel_height; 5517 }; 5518 5519 static GpStatus measure_string_callback(HDC hdc, 5520 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, 5521 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, 5522 INT lineno, const RectF *bounds, INT *underlined_indexes, 5523 INT underlined_index_count, void *user_data) 5524 { 5525 struct measure_string_args *args = user_data; 5526 REAL new_width, new_height; 5527 5528 new_width = bounds->Width / args->rel_width; 5529 new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y; 5530 5531 if (new_width > args->bounds->Width) 5532 args->bounds->Width = new_width; 5533 5534 if (new_height > args->bounds->Height) 5535 args->bounds->Height = new_height; 5536 5537 if (args->codepointsfitted) 5538 *args->codepointsfitted = index + length; 5539 5540 if (args->linesfilled) 5541 (*args->linesfilled)++; 5542 5543 return Ok; 5544 } 5545 5546 /* Find the smallest rectangle that bounds the text when it is printed in rect 5547 * according to the format options listed in format. If rect has 0 width and 5548 * height, then just find the smallest rectangle that bounds the text when it's 5549 * printed at location (rect->X, rect-Y). */ 5550 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, 5551 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, 5552 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds, 5553 INT *codepointsfitted, INT *linesfilled) 5554 { 5555 HFONT oldfont, gdifont; 5556 struct measure_string_args args; 5557 HDC temp_hdc=NULL, hdc; 5558 GpPointF pt[3]; 5559 RectF scaled_rect; 5560 REAL margin_x; 5561 INT lines, glyphs; 5562 5563 TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics, 5564 debugstr_wn(string, length), length, font, debugstr_rectf(rect), format, 5565 bounds, codepointsfitted, linesfilled); 5566 5567 if(!graphics || !string || !font || !rect || !bounds) 5568 return InvalidParameter; 5569 5570 if(!graphics->hdc) 5571 { 5572 hdc = temp_hdc = CreateCompatibleDC(0); 5573 if (!temp_hdc) return OutOfMemory; 5574 } 5575 else 5576 hdc = graphics->hdc; 5577 5578 if(linesfilled) *linesfilled = 0; 5579 if(codepointsfitted) *codepointsfitted = 0; 5580 5581 if(format) 5582 TRACE("may be ignoring some format flags: attr %x\n", format->attr); 5583 5584 pt[0].X = 0.0; 5585 pt[0].Y = 0.0; 5586 pt[1].X = 1.0; 5587 pt[1].Y = 0.0; 5588 pt[2].X = 0.0; 5589 pt[2].Y = 1.0; 5590 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3); 5591 args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+ 5592 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X)); 5593 args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+ 5594 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X)); 5595 5596 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0; 5597 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres); 5598 5599 scaled_rect.X = (rect->X + margin_x) * args.rel_width; 5600 scaled_rect.Y = rect->Y * args.rel_height; 5601 scaled_rect.Width = rect->Width * args.rel_width; 5602 scaled_rect.Height = rect->Height * args.rel_height; 5603 if (scaled_rect.Width >= 0.5) 5604 { 5605 scaled_rect.Width -= margin_x * 2.0 * args.rel_width; 5606 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */ 5607 } 5608 5609 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23; 5610 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23; 5611 5612 get_font_hfont(graphics, font, format, &gdifont, NULL); 5613 oldfont = SelectObject(hdc, gdifont); 5614 5615 bounds->X = rect->X; 5616 bounds->Y = rect->Y; 5617 bounds->Width = 0.0; 5618 bounds->Height = 0.0; 5619 5620 args.bounds = bounds; 5621 args.codepointsfitted = &glyphs; 5622 args.linesfilled = &lines; 5623 lines = glyphs = 0; 5624 5625 gdi_transform_acquire(graphics); 5626 5627 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE, 5628 measure_string_callback, &args); 5629 5630 gdi_transform_release(graphics); 5631 5632 if (linesfilled) *linesfilled = lines; 5633 if (codepointsfitted) *codepointsfitted = glyphs; 5634 5635 if (lines) 5636 bounds->Width += margin_x * 2.0; 5637 5638 SelectObject(hdc, oldfont); 5639 DeleteObject(gdifont); 5640 5641 if (temp_hdc) 5642 DeleteDC(temp_hdc); 5643 5644 return Ok; 5645 } 5646 5647 struct draw_string_args { 5648 GpGraphics *graphics; 5649 GDIPCONST GpBrush *brush; 5650 REAL x, y, rel_width, rel_height, ascent; 5651 }; 5652 5653 static GpStatus draw_string_callback(HDC hdc, 5654 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, 5655 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, 5656 INT lineno, const RectF *bounds, INT *underlined_indexes, 5657 INT underlined_index_count, void *user_data) 5658 { 5659 struct draw_string_args *args = user_data; 5660 PointF position; 5661 GpStatus stat; 5662 5663 position.X = args->x + bounds->X / args->rel_width; 5664 position.Y = args->y + bounds->Y / args->rel_height + args->ascent; 5665 5666 stat = draw_driver_string(args->graphics, &string[index], length, font, format, 5667 args->brush, &position, 5668 DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); 5669 5670 if (stat == Ok && underlined_index_count) 5671 { 5672 OUTLINETEXTMETRICW otm; 5673 REAL underline_y, underline_height; 5674 int i; 5675 5676 GetOutlineTextMetricsW(hdc, sizeof(otm), &otm); 5677 5678 underline_height = otm.otmsUnderscoreSize / args->rel_height; 5679 underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2; 5680 5681 for (i=0; i<underlined_index_count; i++) 5682 { 5683 REAL start_x, end_x; 5684 SIZE text_size; 5685 INT ofs = underlined_indexes[i] - index; 5686 5687 GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); 5688 start_x = text_size.cx / args->rel_width; 5689 5690 GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); 5691 end_x = text_size.cx / args->rel_width; 5692 5693 GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); 5694 } 5695 } 5696 5697 return stat; 5698 } 5699 5700 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string, 5701 INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, 5702 GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush) 5703 { 5704 HRGN rgn = NULL; 5705 HFONT gdifont; 5706 GpPointF pt[3], rectcpy[4]; 5707 POINT corners[4]; 5708 REAL rel_width, rel_height, margin_x; 5709 INT save_state, format_flags = 0; 5710 REAL offsety = 0.0; 5711 struct draw_string_args args; 5712 RectF scaled_rect; 5713 HDC hdc, temp_hdc=NULL; 5714 TEXTMETRICW textmetric; 5715 5716 TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length), 5717 length, font, debugstr_rectf(rect), format, brush); 5718 5719 if(!graphics || !string || !font || !brush || !rect) 5720 return InvalidParameter; 5721 5722 if(graphics->hdc) 5723 { 5724 hdc = graphics->hdc; 5725 } 5726 else 5727 { 5728 hdc = temp_hdc = CreateCompatibleDC(0); 5729 } 5730 5731 if(format){ 5732 TRACE("may be ignoring some format flags: attr %x\n", format->attr); 5733 5734 format_flags = format->attr; 5735 5736 /* Should be no need to explicitly test for StringAlignmentNear as 5737 * that is default behavior if no alignment is passed. */ 5738 if(format->line_align != StringAlignmentNear){ 5739 RectF bounds, in_rect = *rect; 5740 in_rect.Height = 0.0; /* avoid height clipping */ 5741 GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0); 5742 5743 TRACE("bounds %s\n", debugstr_rectf(&bounds)); 5744 5745 if(format->line_align == StringAlignmentCenter) 5746 offsety = (rect->Height - bounds.Height) / 2; 5747 else if(format->line_align == StringAlignmentFar) 5748 offsety = (rect->Height - bounds.Height); 5749 } 5750 TRACE("line align %d, offsety %f\n", format->line_align, offsety); 5751 } 5752 5753 save_state = SaveDC(hdc); 5754 5755 pt[0].X = 0.0; 5756 pt[0].Y = 0.0; 5757 pt[1].X = 1.0; 5758 pt[1].Y = 0.0; 5759 pt[2].X = 0.0; 5760 pt[2].Y = 1.0; 5761 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3); 5762 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+ 5763 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X)); 5764 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+ 5765 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X)); 5766 5767 rectcpy[3].X = rectcpy[0].X = rect->X; 5768 rectcpy[1].Y = rectcpy[0].Y = rect->Y; 5769 rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width; 5770 rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height; 5771 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4); 5772 round_points(corners, rectcpy, 4); 5773 5774 margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0; 5775 margin_x *= units_scale(font->unit, graphics->unit, graphics->xres); 5776 5777 scaled_rect.X = margin_x * rel_width; 5778 scaled_rect.Y = 0.0; 5779 scaled_rect.Width = rel_width * rect->Width; 5780 scaled_rect.Height = rel_height * rect->Height; 5781 if (scaled_rect.Width >= 0.5) 5782 { 5783 scaled_rect.Width -= margin_x * 2.0 * rel_width; 5784 if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */ 5785 } 5786 5787 if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23; 5788 if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23; 5789 5790 if (!(format_flags & StringFormatFlagsNoClip) && 5791 scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 && 5792 rect->Width > 0.0 && rect->Height > 0.0) 5793 { 5794 /* FIXME: If only the width or only the height is 0, we should probably still clip */ 5795 rgn = CreatePolygonRgn(corners, 4, ALTERNATE); 5796 SelectClipRgn(hdc, rgn); 5797 } 5798 5799 get_font_hfont(graphics, font, format, &gdifont, NULL); 5800 SelectObject(hdc, gdifont); 5801 5802 args.graphics = graphics; 5803 args.brush = brush; 5804 5805 args.x = rect->X; 5806 args.y = rect->Y + offsety; 5807 5808 args.rel_width = rel_width; 5809 args.rel_height = rel_height; 5810 5811 gdi_transform_acquire(graphics); 5812 5813 GetTextMetricsW(hdc, &textmetric); 5814 args.ascent = textmetric.tmAscent / rel_height; 5815 5816 gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE, 5817 draw_string_callback, &args); 5818 5819 gdi_transform_release(graphics); 5820 5821 DeleteObject(rgn); 5822 DeleteObject(gdifont); 5823 5824 RestoreDC(hdc, save_state); 5825 5826 DeleteDC(temp_hdc); 5827 5828 return Ok; 5829 } 5830 5831 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics) 5832 { 5833 TRACE("(%p)\n", graphics); 5834 5835 if(!graphics) 5836 return InvalidParameter; 5837 5838 if(graphics->busy) 5839 return ObjectBusy; 5840 5841 return GdipSetInfinite(graphics->clip); 5842 } 5843 5844 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics) 5845 { 5846 GpStatus stat; 5847 5848 TRACE("(%p)\n", graphics); 5849 5850 if(!graphics) 5851 return InvalidParameter; 5852 5853 if(graphics->busy) 5854 return ObjectBusy; 5855 5856 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 5857 stat = METAFILE_ResetWorldTransform((GpMetafile*)graphics->image); 5858 5859 if (stat != Ok) 5860 return stat; 5861 } 5862 5863 return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); 5864 } 5865 5866 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle, 5867 GpMatrixOrder order) 5868 { 5869 GpStatus stat; 5870 5871 TRACE("(%p, %.2f, %d)\n", graphics, angle, order); 5872 5873 if(!graphics) 5874 return InvalidParameter; 5875 5876 if(graphics->busy) 5877 return ObjectBusy; 5878 5879 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 5880 stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order); 5881 5882 if (stat != Ok) 5883 return stat; 5884 } 5885 5886 return GdipRotateMatrix(&graphics->worldtrans, angle, order); 5887 } 5888 5889 static GpStatus begin_container(GpGraphics *graphics, 5890 GraphicsContainerType type, GraphicsContainer *state) 5891 { 5892 GraphicsContainerItem *container; 5893 GpStatus sts; 5894 5895 if(!graphics || !state) 5896 return InvalidParameter; 5897 5898 sts = init_container(&container, graphics, type); 5899 if(sts != Ok) 5900 return sts; 5901 5902 list_add_head(&graphics->containers, &container->entry); 5903 *state = graphics->contid = container->contid; 5904 5905 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 5906 if (type == BEGIN_CONTAINER) 5907 METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid); 5908 else 5909 METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid); 5910 } 5911 5912 return Ok; 5913 } 5914 5915 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state) 5916 { 5917 TRACE("(%p, %p)\n", graphics, state); 5918 return begin_container(graphics, SAVE_GRAPHICS, state); 5919 } 5920 5921 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, 5922 GraphicsContainer *state) 5923 { 5924 TRACE("(%p, %p)\n", graphics, state); 5925 return begin_container(graphics, BEGIN_CONTAINER, state); 5926 } 5927 5928 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state) 5929 { 5930 GraphicsContainerItem *container; 5931 GpMatrix transform; 5932 GpStatus stat; 5933 GpRectF scaled_srcrect; 5934 REAL scale_x, scale_y; 5935 5936 TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state); 5937 5938 if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state) 5939 return InvalidParameter; 5940 5941 stat = init_container(&container, graphics, BEGIN_CONTAINER); 5942 if(stat != Ok) 5943 return stat; 5944 5945 list_add_head(&graphics->containers, &container->entry); 5946 *state = graphics->contid = container->contid; 5947 5948 scale_x = units_to_pixels(1.0, unit, graphics->xres); 5949 scale_y = units_to_pixels(1.0, unit, graphics->yres); 5950 5951 scaled_srcrect.X = scale_x * srcrect->X; 5952 scaled_srcrect.Y = scale_y * srcrect->Y; 5953 scaled_srcrect.Width = scale_x * srcrect->Width; 5954 scaled_srcrect.Height = scale_y * srcrect->Height; 5955 5956 transform.matrix[0] = dstrect->Width / scaled_srcrect.Width; 5957 transform.matrix[1] = 0.0; 5958 transform.matrix[2] = 0.0; 5959 transform.matrix[3] = dstrect->Height / scaled_srcrect.Height; 5960 transform.matrix[4] = dstrect->X - scaled_srcrect.X; 5961 transform.matrix[5] = dstrect->Y - scaled_srcrect.Y; 5962 5963 GdipMultiplyMatrix(&graphics->worldtrans, &transform, MatrixOrderPrepend); 5964 5965 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 5966 METAFILE_BeginContainer((GpMetafile*)graphics->image, dstrect, srcrect, unit, container->contid); 5967 } 5968 5969 return Ok; 5970 } 5971 5972 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state) 5973 { 5974 GpRectF dstrectf, srcrectf; 5975 5976 TRACE("(%p, %p, %p, %d, %p)\n", graphics, dstrect, srcrect, unit, state); 5977 5978 if (!dstrect || !srcrect) 5979 return InvalidParameter; 5980 5981 dstrectf.X = dstrect->X; 5982 dstrectf.Y = dstrect->Y; 5983 dstrectf.Width = dstrect->Width; 5984 dstrectf.Height = dstrect->Height; 5985 5986 srcrectf.X = srcrect->X; 5987 srcrectf.Y = srcrect->Y; 5988 srcrectf.Width = srcrect->Width; 5989 srcrectf.Height = srcrect->Height; 5990 5991 return GdipBeginContainer(graphics, &dstrectf, &srcrectf, unit, state); 5992 } 5993 5994 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data) 5995 { 5996 FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data); 5997 return NotImplemented; 5998 } 5999 6000 static GpStatus end_container(GpGraphics *graphics, GraphicsContainerType type, 6001 GraphicsContainer state) 6002 { 6003 GpStatus sts; 6004 GraphicsContainerItem *container, *container2; 6005 6006 if(!graphics) 6007 return InvalidParameter; 6008 6009 LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){ 6010 if(container->contid == state && container->type == type) 6011 break; 6012 } 6013 6014 /* did not find a matching container */ 6015 if(&container->entry == &graphics->containers) 6016 return Ok; 6017 6018 sts = restore_container(graphics, container); 6019 if(sts != Ok) 6020 return sts; 6021 6022 /* remove all of the containers on top of the found container */ 6023 LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){ 6024 if(container->contid == state) 6025 break; 6026 list_remove(&container->entry); 6027 delete_container(container); 6028 } 6029 6030 list_remove(&container->entry); 6031 delete_container(container); 6032 6033 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 6034 if (type == BEGIN_CONTAINER) 6035 METAFILE_EndContainer((GpMetafile*)graphics->image, state); 6036 else 6037 METAFILE_RestoreGraphics((GpMetafile*)graphics->image, state); 6038 } 6039 6040 return Ok; 6041 } 6042 6043 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state) 6044 { 6045 TRACE("(%p, %x)\n", graphics, state); 6046 return end_container(graphics, BEGIN_CONTAINER, state); 6047 } 6048 6049 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state) 6050 { 6051 TRACE("(%p, %x)\n", graphics, state); 6052 return end_container(graphics, SAVE_GRAPHICS, state); 6053 } 6054 6055 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx, 6056 REAL sy, GpMatrixOrder order) 6057 { 6058 GpStatus stat; 6059 6060 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order); 6061 6062 if(!graphics) 6063 return InvalidParameter; 6064 6065 if(graphics->busy) 6066 return ObjectBusy; 6067 6068 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 6069 stat = METAFILE_ScaleWorldTransform((GpMetafile*)graphics->image, sx, sy, order); 6070 6071 if (stat != Ok) 6072 return stat; 6073 } 6074 6075 return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order); 6076 } 6077 6078 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics, 6079 CombineMode mode) 6080 { 6081 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode); 6082 6083 if(!graphics || !srcgraphics) 6084 return InvalidParameter; 6085 6086 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode); 6087 } 6088 6089 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics, 6090 CompositingMode mode) 6091 { 6092 TRACE("(%p, %d)\n", graphics, mode); 6093 6094 if(!graphics) 6095 return InvalidParameter; 6096 6097 if(graphics->busy) 6098 return ObjectBusy; 6099 6100 if(graphics->compmode == mode) 6101 return Ok; 6102 6103 if(graphics->image && graphics->image->type == ImageTypeMetafile) 6104 { 6105 GpStatus stat; 6106 6107 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image, 6108 EmfPlusRecordTypeSetCompositingMode, mode); 6109 if(stat != Ok) 6110 return stat; 6111 } 6112 6113 graphics->compmode = mode; 6114 6115 return Ok; 6116 } 6117 6118 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics, 6119 CompositingQuality quality) 6120 { 6121 TRACE("(%p, %d)\n", graphics, quality); 6122 6123 if(!graphics) 6124 return InvalidParameter; 6125 6126 if(graphics->busy) 6127 return ObjectBusy; 6128 6129 if(graphics->compqual == quality) 6130 return Ok; 6131 6132 if(graphics->image && graphics->image->type == ImageTypeMetafile) 6133 { 6134 GpStatus stat; 6135 6136 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image, 6137 EmfPlusRecordTypeSetCompositingQuality, quality); 6138 if(stat != Ok) 6139 return stat; 6140 } 6141 6142 graphics->compqual = quality; 6143 6144 return Ok; 6145 } 6146 6147 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics, 6148 InterpolationMode mode) 6149 { 6150 TRACE("(%p, %d)\n", graphics, mode); 6151 6152 if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic) 6153 return InvalidParameter; 6154 6155 if(graphics->busy) 6156 return ObjectBusy; 6157 6158 if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality) 6159 mode = InterpolationModeBilinear; 6160 6161 if (mode == InterpolationModeHighQuality) 6162 mode = InterpolationModeHighQualityBicubic; 6163 6164 if (mode == graphics->interpolation) 6165 return Ok; 6166 6167 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6168 { 6169 GpStatus stat; 6170 6171 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image, 6172 EmfPlusRecordTypeSetInterpolationMode, mode); 6173 if (stat != Ok) 6174 return stat; 6175 } 6176 6177 graphics->interpolation = mode; 6178 6179 return Ok; 6180 } 6181 6182 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale) 6183 { 6184 GpStatus stat; 6185 6186 TRACE("(%p, %.2f)\n", graphics, scale); 6187 6188 if(!graphics || (scale <= 0.0)) 6189 return InvalidParameter; 6190 6191 if(graphics->busy) 6192 return ObjectBusy; 6193 6194 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6195 { 6196 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, graphics->unit, scale); 6197 if (stat != Ok) 6198 return stat; 6199 } 6200 6201 graphics->scale = scale; 6202 6203 return Ok; 6204 } 6205 6206 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit) 6207 { 6208 GpStatus stat; 6209 6210 TRACE("(%p, %d)\n", graphics, unit); 6211 6212 if(!graphics) 6213 return InvalidParameter; 6214 6215 if(graphics->busy) 6216 return ObjectBusy; 6217 6218 if(unit == UnitWorld) 6219 return InvalidParameter; 6220 6221 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6222 { 6223 stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, unit, graphics->scale); 6224 if (stat != Ok) 6225 return stat; 6226 } 6227 6228 graphics->unit = unit; 6229 6230 return Ok; 6231 } 6232 6233 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode 6234 mode) 6235 { 6236 TRACE("(%p, %d)\n", graphics, mode); 6237 6238 if(!graphics) 6239 return InvalidParameter; 6240 6241 if(graphics->busy) 6242 return ObjectBusy; 6243 6244 if(graphics->pixeloffset == mode) 6245 return Ok; 6246 6247 if(graphics->image && graphics->image->type == ImageTypeMetafile) 6248 { 6249 GpStatus stat; 6250 6251 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image, 6252 EmfPlusRecordTypeSetPixelOffsetMode, mode); 6253 if(stat != Ok) 6254 return stat; 6255 } 6256 6257 graphics->pixeloffset = mode; 6258 6259 return Ok; 6260 } 6261 6262 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y) 6263 { 6264 static int calls; 6265 6266 TRACE("(%p,%i,%i)\n", graphics, x, y); 6267 6268 if (!(calls++)) 6269 FIXME("value is unused in rendering\n"); 6270 6271 if (!graphics) 6272 return InvalidParameter; 6273 6274 graphics->origin_x = x; 6275 graphics->origin_y = y; 6276 6277 return Ok; 6278 } 6279 6280 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y) 6281 { 6282 TRACE("(%p,%p,%p)\n", graphics, x, y); 6283 6284 if (!graphics || !x || !y) 6285 return InvalidParameter; 6286 6287 *x = graphics->origin_x; 6288 *y = graphics->origin_y; 6289 6290 return Ok; 6291 } 6292 6293 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode) 6294 { 6295 TRACE("(%p, %d)\n", graphics, mode); 6296 6297 if(!graphics) 6298 return InvalidParameter; 6299 6300 if(graphics->busy) 6301 return ObjectBusy; 6302 6303 if(graphics->smoothing == mode) 6304 return Ok; 6305 6306 if(graphics->image && graphics->image->type == ImageTypeMetafile) { 6307 GpStatus stat; 6308 BOOL antialias = (mode != SmoothingModeDefault && 6309 mode != SmoothingModeNone && mode != SmoothingModeHighSpeed); 6310 6311 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image, 6312 EmfPlusRecordTypeSetAntiAliasMode, (mode << 1) + antialias); 6313 if(stat != Ok) 6314 return stat; 6315 } 6316 6317 graphics->smoothing = mode; 6318 6319 return Ok; 6320 } 6321 6322 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast) 6323 { 6324 TRACE("(%p, %d)\n", graphics, contrast); 6325 6326 if(!graphics) 6327 return InvalidParameter; 6328 6329 graphics->textcontrast = contrast; 6330 6331 return Ok; 6332 } 6333 6334 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics, 6335 TextRenderingHint hint) 6336 { 6337 TRACE("(%p, %d)\n", graphics, hint); 6338 6339 if(!graphics || hint > TextRenderingHintClearTypeGridFit) 6340 return InvalidParameter; 6341 6342 if(graphics->busy) 6343 return ObjectBusy; 6344 6345 if(graphics->texthint == hint) 6346 return Ok; 6347 6348 if(graphics->image && graphics->image->type == ImageTypeMetafile) { 6349 GpStatus stat; 6350 6351 stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image, 6352 EmfPlusRecordTypeSetTextRenderingHint, hint); 6353 if(stat != Ok) 6354 return stat; 6355 } 6356 6357 graphics->texthint = hint; 6358 6359 return Ok; 6360 } 6361 6362 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix) 6363 { 6364 GpStatus stat; 6365 6366 TRACE("(%p, %p)\n", graphics, matrix); 6367 6368 if(!graphics || !matrix) 6369 return InvalidParameter; 6370 6371 if(graphics->busy) 6372 return ObjectBusy; 6373 6374 TRACE("%f,%f,%f,%f,%f,%f\n", 6375 matrix->matrix[0], matrix->matrix[1], matrix->matrix[2], 6376 matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]); 6377 6378 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 6379 stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix); 6380 6381 if (stat != Ok) 6382 return stat; 6383 } 6384 6385 graphics->worldtrans = *matrix; 6386 6387 return Ok; 6388 } 6389 6390 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx, 6391 REAL dy, GpMatrixOrder order) 6392 { 6393 GpStatus stat; 6394 6395 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order); 6396 6397 if(!graphics) 6398 return InvalidParameter; 6399 6400 if(graphics->busy) 6401 return ObjectBusy; 6402 6403 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 6404 stat = METAFILE_TranslateWorldTransform((GpMetafile*)graphics->image, dx, dy, order); 6405 6406 if (stat != Ok) 6407 return stat; 6408 } 6409 6410 return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order); 6411 } 6412 6413 /***************************************************************************** 6414 * GdipSetClipHrgn [GDIPLUS.@] 6415 */ 6416 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode) 6417 { 6418 GpRegion *region; 6419 GpStatus status; 6420 GpMatrix transform; 6421 6422 TRACE("(%p, %p, %d)\n", graphics, hrgn, mode); 6423 6424 if(!graphics) 6425 return InvalidParameter; 6426 6427 if(graphics->busy) 6428 return ObjectBusy; 6429 6430 /* hrgn is in gdi32 device units */ 6431 status = GdipCreateRegionHrgn(hrgn, ®ion); 6432 6433 if (status == Ok) 6434 { 6435 status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform); 6436 6437 if (status == Ok) 6438 status = GdipTransformRegion(region, &transform); 6439 6440 if (status == Ok) 6441 status = GdipCombineRegionRegion(graphics->clip, region, mode); 6442 6443 GdipDeleteRegion(region); 6444 } 6445 return status; 6446 } 6447 6448 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode) 6449 { 6450 GpStatus status; 6451 GpPath *clip_path; 6452 6453 TRACE("(%p, %p, %d)\n", graphics, path, mode); 6454 6455 if(!graphics) 6456 return InvalidParameter; 6457 6458 if(graphics->busy) 6459 return ObjectBusy; 6460 6461 status = GdipClonePath(path, &clip_path); 6462 if (status == Ok) 6463 { 6464 GpMatrix world_to_device; 6465 6466 get_graphics_transform(graphics, CoordinateSpaceDevice, 6467 CoordinateSpaceWorld, &world_to_device); 6468 status = GdipTransformPath(clip_path, &world_to_device); 6469 if (status == Ok) 6470 GdipCombineRegionPath(graphics->clip, clip_path, mode); 6471 6472 GdipDeletePath(clip_path); 6473 } 6474 return status; 6475 } 6476 6477 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y, 6478 REAL width, REAL height, 6479 CombineMode mode) 6480 { 6481 GpStatus status; 6482 GpRectF rect; 6483 GpRegion *region; 6484 6485 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode); 6486 6487 if(!graphics) 6488 return InvalidParameter; 6489 6490 if(graphics->busy) 6491 return ObjectBusy; 6492 6493 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6494 { 6495 status = METAFILE_SetClipRect((GpMetafile*)graphics->image, x, y, width, height, mode); 6496 if (status != Ok) 6497 return status; 6498 } 6499 6500 rect.X = x; 6501 rect.Y = y; 6502 rect.Width = width; 6503 rect.Height = height; 6504 status = GdipCreateRegionRect(&rect, ®ion); 6505 if (status == Ok) 6506 { 6507 GpMatrix world_to_device; 6508 BOOL identity; 6509 6510 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device); 6511 status = GdipIsMatrixIdentity(&world_to_device, &identity); 6512 if (status == Ok && !identity) 6513 status = GdipTransformRegion(region, &world_to_device); 6514 if (status == Ok) 6515 status = GdipCombineRegionRegion(graphics->clip, region, mode); 6516 6517 GdipDeleteRegion(region); 6518 } 6519 return status; 6520 } 6521 6522 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y, 6523 INT width, INT height, 6524 CombineMode mode) 6525 { 6526 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode); 6527 6528 if(!graphics) 6529 return InvalidParameter; 6530 6531 if(graphics->busy) 6532 return ObjectBusy; 6533 6534 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode); 6535 } 6536 6537 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region, 6538 CombineMode mode) 6539 { 6540 GpStatus status; 6541 GpRegion *clip; 6542 6543 TRACE("(%p, %p, %d)\n", graphics, region, mode); 6544 6545 if(!graphics || !region) 6546 return InvalidParameter; 6547 6548 if(graphics->busy) 6549 return ObjectBusy; 6550 6551 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6552 { 6553 status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode); 6554 if (status != Ok) 6555 return status; 6556 } 6557 6558 status = GdipCloneRegion(region, &clip); 6559 if (status == Ok) 6560 { 6561 GpMatrix world_to_device; 6562 BOOL identity; 6563 6564 get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device); 6565 status = GdipIsMatrixIdentity(&world_to_device, &identity); 6566 if (status == Ok && !identity) 6567 status = GdipTransformRegion(clip, &world_to_device); 6568 if (status == Ok) 6569 status = GdipCombineRegionRegion(graphics->clip, clip, mode); 6570 6571 GdipDeleteRegion(clip); 6572 } 6573 return status; 6574 } 6575 6576 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points, 6577 INT count) 6578 { 6579 GpStatus status; 6580 GpPath* path; 6581 6582 TRACE("(%p, %p, %d)\n", graphics, points, count); 6583 6584 if(!graphics || !pen || count<=0) 6585 return InvalidParameter; 6586 6587 if(graphics->busy) 6588 return ObjectBusy; 6589 6590 status = GdipCreatePath(FillModeAlternate, &path); 6591 if (status != Ok) return status; 6592 6593 status = GdipAddPathPolygon(path, points, count); 6594 if (status == Ok) 6595 status = GdipDrawPath(graphics, pen, path); 6596 6597 GdipDeletePath(path); 6598 6599 return status; 6600 } 6601 6602 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points, 6603 INT count) 6604 { 6605 GpStatus ret; 6606 GpPointF *ptf; 6607 INT i; 6608 6609 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count); 6610 6611 if(count<=0) return InvalidParameter; 6612 ptf = heap_alloc_zero(sizeof(GpPointF) * count); 6613 6614 for(i = 0;i < count; i++){ 6615 ptf[i].X = (REAL)points[i].X; 6616 ptf[i].Y = (REAL)points[i].Y; 6617 } 6618 6619 ret = GdipDrawPolygon(graphics,pen,ptf,count); 6620 heap_free(ptf); 6621 6622 return ret; 6623 } 6624 6625 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi) 6626 { 6627 TRACE("(%p, %p)\n", graphics, dpi); 6628 6629 if(!graphics || !dpi) 6630 return InvalidParameter; 6631 6632 if(graphics->busy) 6633 return ObjectBusy; 6634 6635 *dpi = graphics->xres; 6636 return Ok; 6637 } 6638 6639 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi) 6640 { 6641 TRACE("(%p, %p)\n", graphics, dpi); 6642 6643 if(!graphics || !dpi) 6644 return InvalidParameter; 6645 6646 if(graphics->busy) 6647 return ObjectBusy; 6648 6649 *dpi = graphics->yres; 6650 return Ok; 6651 } 6652 6653 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix, 6654 GpMatrixOrder order) 6655 { 6656 GpMatrix m; 6657 GpStatus ret; 6658 6659 TRACE("(%p, %p, %d)\n", graphics, matrix, order); 6660 6661 if(!graphics || !matrix) 6662 return InvalidParameter; 6663 6664 if(graphics->busy) 6665 return ObjectBusy; 6666 6667 if (graphics->image && graphics->image->type == ImageTypeMetafile) { 6668 ret = METAFILE_MultiplyWorldTransform((GpMetafile*)graphics->image, matrix, order); 6669 6670 if (ret != Ok) 6671 return ret; 6672 } 6673 6674 m = graphics->worldtrans; 6675 6676 ret = GdipMultiplyMatrix(&m, matrix, order); 6677 if(ret == Ok) 6678 graphics->worldtrans = m; 6679 6680 return ret; 6681 } 6682 6683 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */ 6684 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d; 6685 6686 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc) 6687 { 6688 GpStatus stat=Ok; 6689 6690 TRACE("(%p, %p)\n", graphics, hdc); 6691 6692 if(!graphics || !hdc) 6693 return InvalidParameter; 6694 6695 if(graphics->busy) 6696 return ObjectBusy; 6697 6698 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6699 { 6700 stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc); 6701 } 6702 else if (!graphics->hdc || 6703 (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha)) 6704 { 6705 /* Create a fake HDC and fill it with a constant color. */ 6706 HDC temp_hdc; 6707 HBITMAP hbitmap; 6708 GpRectF bounds; 6709 BITMAPINFOHEADER bmih; 6710 int i; 6711 6712 stat = get_graphics_bounds(graphics, &bounds); 6713 if (stat != Ok) 6714 return stat; 6715 6716 graphics->temp_hbitmap_width = bounds.Width; 6717 graphics->temp_hbitmap_height = bounds.Height; 6718 6719 bmih.biSize = sizeof(bmih); 6720 bmih.biWidth = graphics->temp_hbitmap_width; 6721 bmih.biHeight = -graphics->temp_hbitmap_height; 6722 bmih.biPlanes = 1; 6723 bmih.biBitCount = 32; 6724 bmih.biCompression = BI_RGB; 6725 bmih.biSizeImage = 0; 6726 bmih.biXPelsPerMeter = 0; 6727 bmih.biYPelsPerMeter = 0; 6728 bmih.biClrUsed = 0; 6729 bmih.biClrImportant = 0; 6730 6731 hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, 6732 (void**)&graphics->temp_bits, NULL, 0); 6733 if (!hbitmap) 6734 return GenericError; 6735 6736 temp_hdc = CreateCompatibleDC(0); 6737 if (!temp_hdc) 6738 { 6739 DeleteObject(hbitmap); 6740 return GenericError; 6741 } 6742 6743 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++) 6744 ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY; 6745 6746 SelectObject(temp_hdc, hbitmap); 6747 6748 graphics->temp_hbitmap = hbitmap; 6749 *hdc = graphics->temp_hdc = temp_hdc; 6750 } 6751 else 6752 { 6753 *hdc = graphics->hdc; 6754 } 6755 6756 if (stat == Ok) 6757 graphics->busy = TRUE; 6758 6759 return stat; 6760 } 6761 6762 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc) 6763 { 6764 GpStatus stat=Ok; 6765 6766 TRACE("(%p, %p)\n", graphics, hdc); 6767 6768 if(!graphics || !hdc || !graphics->busy) 6769 return InvalidParameter; 6770 6771 if (graphics->image && graphics->image->type == ImageTypeMetafile) 6772 { 6773 stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc); 6774 } 6775 else if (graphics->temp_hdc == hdc) 6776 { 6777 DWORD* pos; 6778 int i; 6779 6780 /* Find the pixels that have changed, and mark them as opaque. */ 6781 pos = (DWORD*)graphics->temp_bits; 6782 for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++) 6783 { 6784 if (*pos != DC_BACKGROUND_KEY) 6785 { 6786 *pos |= 0xff000000; 6787 } 6788 pos++; 6789 } 6790 6791 /* Write the changed pixels to the real target. */ 6792 alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits, 6793 graphics->temp_hbitmap_width, graphics->temp_hbitmap_height, 6794 graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB); 6795 6796 /* Clean up. */ 6797 DeleteDC(graphics->temp_hdc); 6798 DeleteObject(graphics->temp_hbitmap); 6799 graphics->temp_hdc = NULL; 6800 graphics->temp_hbitmap = NULL; 6801 } 6802 else if (hdc != graphics->hdc) 6803 { 6804 stat = InvalidParameter; 6805 } 6806 6807 if (stat == Ok) 6808 graphics->busy = FALSE; 6809 6810 return stat; 6811 } 6812 6813 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region) 6814 { 6815 GpRegion *clip; 6816 GpStatus status; 6817 GpMatrix device_to_world; 6818 6819 TRACE("(%p, %p)\n", graphics, region); 6820 6821 if(!graphics || !region) 6822 return InvalidParameter; 6823 6824 if(graphics->busy) 6825 return ObjectBusy; 6826 6827 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok) 6828 return status; 6829 6830 get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world); 6831 status = GdipTransformRegion(clip, &device_to_world); 6832 if (status != Ok) 6833 { 6834 GdipDeleteRegion(clip); 6835 return status; 6836 } 6837 6838 /* free everything except root node and header */ 6839 delete_element(®ion->node); 6840 memcpy(region, clip, sizeof(GpRegion)); 6841 heap_free(clip); 6842 6843 return Ok; 6844 } 6845 6846 GpStatus gdi_transform_acquire(GpGraphics *graphics) 6847 { 6848 if (graphics->gdi_transform_acquire_count == 0 && graphics->hdc) 6849 { 6850 graphics->gdi_transform_save = SaveDC(graphics->hdc); 6851 SetGraphicsMode(graphics->hdc, GM_COMPATIBLE); 6852 SetMapMode(graphics->hdc, MM_TEXT); 6853 SetWindowOrgEx(graphics->hdc, 0, 0, NULL); 6854 SetViewportOrgEx(graphics->hdc, 0, 0, NULL); 6855 } 6856 graphics->gdi_transform_acquire_count++; 6857 return Ok; 6858 } 6859 6860 GpStatus gdi_transform_release(GpGraphics *graphics) 6861 { 6862 if (graphics->gdi_transform_acquire_count <= 0) 6863 { 6864 ERR("called without matching gdi_transform_acquire\n"); 6865 return GenericError; 6866 } 6867 if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc) 6868 { 6869 RestoreDC(graphics->hdc, graphics->gdi_transform_save); 6870 } 6871 graphics->gdi_transform_acquire_count--; 6872 return Ok; 6873 } 6874 6875 GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space, 6876 GpCoordinateSpace src_space, GpMatrix *matrix) 6877 { 6878 GpStatus stat = Ok; 6879 REAL scale_x, scale_y; 6880 6881 GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); 6882 6883 if (dst_space != src_space) 6884 { 6885 scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres); 6886 scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres); 6887 6888 if(graphics->unit != UnitDisplay) 6889 { 6890 scale_x *= graphics->scale; 6891 scale_y *= graphics->scale; 6892 } 6893 6894 if (dst_space < src_space) 6895 { 6896 /* transform towards world space */ 6897 switch ((int)src_space) 6898 { 6899 case WineCoordinateSpaceGdiDevice: 6900 { 6901 GpMatrix gdixform; 6902 gdixform = graphics->gdi_transform; 6903 stat = GdipInvertMatrix(&gdixform); 6904 if (stat != Ok) 6905 break; 6906 GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend); 6907 if (dst_space == CoordinateSpaceDevice) 6908 break; 6909 /* else fall-through */ 6910 } 6911 case CoordinateSpaceDevice: 6912 GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend); 6913 if (dst_space == CoordinateSpacePage) 6914 break; 6915 /* else fall-through */ 6916 case CoordinateSpacePage: 6917 { 6918 GpMatrix inverted_transform = graphics->worldtrans; 6919 stat = GdipInvertMatrix(&inverted_transform); 6920 if (stat == Ok) 6921 GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend); 6922 break; 6923 } 6924 } 6925 } 6926 else 6927 { 6928 /* transform towards device space */ 6929 switch ((int)src_space) 6930 { 6931 case CoordinateSpaceWorld: 6932 GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend); 6933 if (dst_space == CoordinateSpacePage) 6934 break; 6935 /* else fall-through */ 6936 case CoordinateSpacePage: 6937 GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend); 6938 if (dst_space == CoordinateSpaceDevice) 6939 break; 6940 /* else fall-through */ 6941 case CoordinateSpaceDevice: 6942 { 6943 GdipMultiplyMatrix(matrix, &graphics->gdi_transform, MatrixOrderAppend); 6944 break; 6945 } 6946 } 6947 } 6948 } 6949 return stat; 6950 } 6951 6952 GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space, 6953 GpCoordinateSpace src_space, GpPointF *points, INT count) 6954 { 6955 GpMatrix matrix; 6956 GpStatus stat; 6957 6958 stat = get_graphics_transform(graphics, dst_space, src_space, &matrix); 6959 if (stat != Ok) return stat; 6960 6961 return GdipTransformMatrixPoints(&matrix, points, count); 6962 } 6963 6964 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space, 6965 GpCoordinateSpace src_space, GpPointF *points, INT count) 6966 { 6967 if(!graphics || !points || count <= 0 || 6968 dst_space < 0 || dst_space > CoordinateSpaceDevice || 6969 src_space < 0 || src_space > CoordinateSpaceDevice) 6970 return InvalidParameter; 6971 6972 if(graphics->busy) 6973 return ObjectBusy; 6974 6975 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count); 6976 6977 if (src_space == dst_space) return Ok; 6978 6979 return gdip_transform_points(graphics, dst_space, src_space, points, count); 6980 } 6981 6982 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space, 6983 GpCoordinateSpace src_space, GpPoint *points, INT count) 6984 { 6985 GpPointF *pointsF; 6986 GpStatus ret; 6987 INT i; 6988 6989 TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count); 6990 6991 if(count <= 0) 6992 return InvalidParameter; 6993 6994 pointsF = heap_alloc_zero(sizeof(GpPointF) * count); 6995 if(!pointsF) 6996 return OutOfMemory; 6997 6998 for(i = 0; i < count; i++){ 6999 pointsF[i].X = (REAL)points[i].X; 7000 pointsF[i].Y = (REAL)points[i].Y; 7001 } 7002 7003 ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count); 7004 7005 if(ret == Ok) 7006 for(i = 0; i < count; i++){ 7007 points[i].X = gdip_round(pointsF[i].X); 7008 points[i].Y = gdip_round(pointsF[i].Y); 7009 } 7010 heap_free(pointsF); 7011 7012 return ret; 7013 } 7014 7015 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void) 7016 { 7017 static int calls; 7018 7019 TRACE("\n"); 7020 7021 if (!calls++) 7022 FIXME("stub\n"); 7023 7024 return NULL; 7025 } 7026 7027 /***************************************************************************** 7028 * GdipTranslateClip [GDIPLUS.@] 7029 */ 7030 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy) 7031 { 7032 TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy); 7033 7034 if(!graphics) 7035 return InvalidParameter; 7036 7037 if(graphics->busy) 7038 return ObjectBusy; 7039 7040 return GdipTranslateRegion(graphics->clip, dx, dy); 7041 } 7042 7043 /***************************************************************************** 7044 * GdipTranslateClipI [GDIPLUS.@] 7045 */ 7046 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy) 7047 { 7048 TRACE("(%p, %d, %d)\n", graphics, dx, dy); 7049 7050 if(!graphics) 7051 return InvalidParameter; 7052 7053 if(graphics->busy) 7054 return ObjectBusy; 7055 7056 return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy); 7057 } 7058 7059 7060 /***************************************************************************** 7061 * GdipMeasureDriverString [GDIPLUS.@] 7062 */ 7063 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, 7064 GDIPCONST GpFont *font, GDIPCONST PointF *positions, 7065 INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox) 7066 { 7067 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance); 7068 HFONT hfont; 7069 HDC hdc; 7070 REAL min_x, min_y, max_x, max_y, x, y; 7071 int i; 7072 TEXTMETRICW textmetric; 7073 const WORD *glyph_indices; 7074 WORD *dynamic_glyph_indices=NULL; 7075 REAL rel_width, rel_height, ascent, descent; 7076 GpPointF pt[3]; 7077 7078 TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox); 7079 7080 if (!graphics || !text || !font || !positions || !boundingBox) 7081 return InvalidParameter; 7082 7083 if (length == -1) 7084 length = strlenW(text); 7085 7086 if (length == 0) 7087 { 7088 boundingBox->X = 0.0; 7089 boundingBox->Y = 0.0; 7090 boundingBox->Width = 0.0; 7091 boundingBox->Height = 0.0; 7092 } 7093 7094 if (flags & unsupported_flags) 7095 FIXME("Ignoring flags %x\n", flags & unsupported_flags); 7096 7097 get_font_hfont(graphics, font, NULL, &hfont, matrix); 7098 7099 hdc = CreateCompatibleDC(0); 7100 SelectObject(hdc, hfont); 7101 7102 GetTextMetricsW(hdc, &textmetric); 7103 7104 pt[0].X = 0.0; 7105 pt[0].Y = 0.0; 7106 pt[1].X = 1.0; 7107 pt[1].Y = 0.0; 7108 pt[2].X = 0.0; 7109 pt[2].Y = 1.0; 7110 if (matrix) 7111 { 7112 GpMatrix xform = *matrix; 7113 GdipTransformMatrixPoints(&xform, pt, 3); 7114 } 7115 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3); 7116 rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+ 7117 (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X)); 7118 rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+ 7119 (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X)); 7120 7121 if (flags & DriverStringOptionsCmapLookup) 7122 { 7123 glyph_indices = dynamic_glyph_indices = heap_alloc_zero(sizeof(WORD) * length); 7124 if (!glyph_indices) 7125 { 7126 DeleteDC(hdc); 7127 DeleteObject(hfont); 7128 return OutOfMemory; 7129 } 7130 7131 GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0); 7132 } 7133 else 7134 glyph_indices = text; 7135 7136 min_x = max_x = x = positions[0].X; 7137 min_y = max_y = y = positions[0].Y; 7138 7139 ascent = textmetric.tmAscent / rel_height; 7140 descent = textmetric.tmDescent / rel_height; 7141 7142 for (i=0; i<length; i++) 7143 { 7144 int char_width; 7145 ABC abc; 7146 7147 if (!(flags & DriverStringOptionsRealizedAdvance)) 7148 { 7149 x = positions[i].X; 7150 y = positions[i].Y; 7151 } 7152 7153 GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc); 7154 char_width = abc.abcA + abc.abcB + abc.abcC; 7155 7156 if (min_y > y - ascent) min_y = y - ascent; 7157 if (max_y < y + descent) max_y = y + descent; 7158 if (min_x > x) min_x = x; 7159 7160 x += char_width / rel_width; 7161 7162 if (max_x < x) max_x = x; 7163 } 7164 7165 heap_free(dynamic_glyph_indices); 7166 DeleteDC(hdc); 7167 DeleteObject(hfont); 7168 7169 boundingBox->X = min_x; 7170 boundingBox->Y = min_y; 7171 boundingBox->Width = max_x - min_x; 7172 boundingBox->Height = max_y - min_y; 7173 7174 return Ok; 7175 } 7176 7177 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, 7178 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, 7179 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, 7180 INT flags, GDIPCONST GpMatrix *matrix) 7181 { 7182 static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup); 7183 INT save_state; 7184 GpPointF pt; 7185 HFONT hfont; 7186 UINT eto_flags=0; 7187 GpStatus status; 7188 HRGN hrgn; 7189 7190 if (flags & unsupported_flags) 7191 FIXME("Ignoring flags %x\n", flags & unsupported_flags); 7192 7193 if (!(flags & DriverStringOptionsCmapLookup)) 7194 eto_flags |= ETO_GLYPH_INDEX; 7195 7196 save_state = SaveDC(graphics->hdc); 7197 SetBkMode(graphics->hdc, TRANSPARENT); 7198 SetTextColor(graphics->hdc, get_gdi_brush_color(brush)); 7199 7200 status = get_clip_hrgn(graphics, &hrgn); 7201 7202 if (status == Ok) 7203 { 7204 ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); 7205 DeleteObject(hrgn); 7206 } 7207 7208 pt = positions[0]; 7209 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &pt, 1); 7210 7211 get_font_hfont(graphics, font, format, &hfont, matrix); 7212 SelectObject(graphics->hdc, hfont); 7213 7214 SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT); 7215 7216 gdi_transform_acquire(graphics); 7217 7218 ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL); 7219 7220 gdi_transform_release(graphics); 7221 7222 RestoreDC(graphics->hdc, save_state); 7223 7224 DeleteObject(hfont); 7225 7226 return Ok; 7227 } 7228 7229 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, 7230 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, 7231 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, 7232 INT flags, GDIPCONST GpMatrix *matrix) 7233 { 7234 static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance); 7235 GpStatus stat; 7236 PointF *real_positions, real_position; 7237 POINT *pti; 7238 HFONT hfont; 7239 HDC hdc; 7240 int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y; 7241 DWORD max_glyphsize=0; 7242 GLYPHMETRICS glyphmetrics; 7243 static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}}; 7244 BYTE *glyph_mask; 7245 BYTE *text_mask; 7246 int text_mask_stride; 7247 BYTE *pixel_data; 7248 int pixel_data_stride; 7249 GpRect pixel_area; 7250 UINT ggo_flags = GGO_GRAY8_BITMAP; 7251 7252 if (length <= 0) 7253 return Ok; 7254 7255 if (!(flags & DriverStringOptionsCmapLookup)) 7256 ggo_flags |= GGO_GLYPH_INDEX; 7257 7258 if (flags & unsupported_flags) 7259 FIXME("Ignoring flags %x\n", flags & unsupported_flags); 7260 7261 pti = heap_alloc_zero(sizeof(POINT) * length); 7262 if (!pti) 7263 return OutOfMemory; 7264 7265 if (flags & DriverStringOptionsRealizedAdvance) 7266 { 7267 real_position = positions[0]; 7268 7269 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1); 7270 round_points(pti, &real_position, 1); 7271 } 7272 else 7273 { 7274 real_positions = heap_alloc_zero(sizeof(PointF) * length); 7275 if (!real_positions) 7276 { 7277 heap_free(pti); 7278 return OutOfMemory; 7279 } 7280 7281 memcpy(real_positions, positions, sizeof(PointF) * length); 7282 7283 gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length); 7284 round_points(pti, real_positions, length); 7285 7286 heap_free(real_positions); 7287 } 7288 7289 get_font_hfont(graphics, font, format, &hfont, matrix); 7290 7291 hdc = CreateCompatibleDC(0); 7292 SelectObject(hdc, hfont); 7293 7294 /* Get the boundaries of the text to be drawn */ 7295 for (i=0; i<length; i++) 7296 { 7297 DWORD glyphsize; 7298 int left, top, right, bottom; 7299 7300 glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags, 7301 &glyphmetrics, 0, NULL, &identity); 7302 7303 if (glyphsize == GDI_ERROR) 7304 { 7305 ERR("GetGlyphOutlineW failed\n"); 7306 heap_free(pti); 7307 DeleteDC(hdc); 7308 DeleteObject(hfont); 7309 return GenericError; 7310 } 7311 7312 if (glyphsize > max_glyphsize) 7313 max_glyphsize = glyphsize; 7314 7315 if (glyphsize != 0) 7316 { 7317 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x; 7318 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y; 7319 right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX; 7320 bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY; 7321 7322 if (left < min_x) min_x = left; 7323 if (top < min_y) min_y = top; 7324 if (right > max_x) max_x = right; 7325 if (bottom > max_y) max_y = bottom; 7326 } 7327 7328 if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance) 7329 { 7330 pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX; 7331 pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY; 7332 } 7333 } 7334 7335 if (max_glyphsize == 0) 7336 /* Nothing to draw. */ 7337 return Ok; 7338 7339 glyph_mask = heap_alloc_zero(max_glyphsize); 7340 text_mask = heap_alloc_zero((max_x - min_x) * (max_y - min_y)); 7341 text_mask_stride = max_x - min_x; 7342 7343 if (!(glyph_mask && text_mask)) 7344 { 7345 heap_free(glyph_mask); 7346 heap_free(text_mask); 7347 heap_free(pti); 7348 DeleteDC(hdc); 7349 DeleteObject(hfont); 7350 return OutOfMemory; 7351 } 7352 7353 /* Generate a mask for the text */ 7354 for (i=0; i<length; i++) 7355 { 7356 DWORD ret; 7357 int left, top, stride; 7358 7359 ret = GetGlyphOutlineW(hdc, text[i], ggo_flags, 7360 &glyphmetrics, max_glyphsize, glyph_mask, &identity); 7361 7362 if (ret == GDI_ERROR || ret == 0) 7363 continue; /* empty glyph */ 7364 7365 left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x; 7366 top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y; 7367 stride = (glyphmetrics.gmBlackBoxX + 3) & (~3); 7368 7369 for (y=0; y<glyphmetrics.gmBlackBoxY; y++) 7370 { 7371 BYTE *glyph_val = glyph_mask + y * stride; 7372 BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride; 7373 for (x=0; x<glyphmetrics.gmBlackBoxX; x++) 7374 { 7375 *text_val = min(64, *text_val + *glyph_val); 7376 glyph_val++; 7377 text_val++; 7378 } 7379 } 7380 } 7381 7382 heap_free(pti); 7383 DeleteDC(hdc); 7384 DeleteObject(hfont); 7385 heap_free(glyph_mask); 7386 7387 /* get the brush data */ 7388 pixel_data = heap_alloc_zero(4 * (max_x - min_x) * (max_y - min_y)); 7389 if (!pixel_data) 7390 { 7391 heap_free(text_mask); 7392 return OutOfMemory; 7393 } 7394 7395 pixel_area.X = min_x; 7396 pixel_area.Y = min_y; 7397 pixel_area.Width = max_x - min_x; 7398 pixel_area.Height = max_y - min_y; 7399 pixel_data_stride = pixel_area.Width * 4; 7400 7401 stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width); 7402 if (stat != Ok) 7403 { 7404 heap_free(text_mask); 7405 heap_free(pixel_data); 7406 return stat; 7407 } 7408 7409 /* multiply the brush data by the mask */ 7410 for (y=0; y<pixel_area.Height; y++) 7411 { 7412 BYTE *text_val = text_mask + text_mask_stride * y; 7413 BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3; 7414 for (x=0; x<pixel_area.Width; x++) 7415 { 7416 *pixel_val = (*pixel_val) * (*text_val) / 64; 7417 text_val++; 7418 pixel_val+=4; 7419 } 7420 } 7421 7422 heap_free(text_mask); 7423 7424 gdi_transform_acquire(graphics); 7425 7426 /* draw the result */ 7427 stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width, 7428 pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB); 7429 7430 gdi_transform_release(graphics); 7431 7432 heap_free(pixel_data); 7433 7434 return stat; 7435 } 7436 7437 static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, 7438 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, 7439 GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, 7440 INT flags, GDIPCONST GpMatrix *matrix) 7441 { 7442 GpStatus stat = NotImplemented; 7443 7444 if (length == -1) 7445 length = strlenW(text); 7446 7447 if (graphics->hdc && !graphics->alpha_hdc && 7448 ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) && 7449 brush->bt == BrushTypeSolidColor && 7450 (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000) 7451 stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format, 7452 brush, positions, flags, matrix); 7453 if (stat == NotImplemented) 7454 stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format, 7455 brush, positions, flags, matrix); 7456 return stat; 7457 } 7458 7459 /***************************************************************************** 7460 * GdipDrawDriverString [GDIPLUS.@] 7461 */ 7462 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, 7463 GDIPCONST GpFont *font, GDIPCONST GpBrush *brush, 7464 GDIPCONST PointF *positions, INT flags, 7465 GDIPCONST GpMatrix *matrix ) 7466 { 7467 TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix); 7468 7469 if (!graphics || !text || !font || !brush || !positions) 7470 return InvalidParameter; 7471 7472 return draw_driver_string(graphics, text, length, font, NULL, 7473 brush, positions, flags, matrix); 7474 } 7475 7476 /***************************************************************************** 7477 * GdipIsVisibleClipEmpty [GDIPLUS.@] 7478 */ 7479 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res) 7480 { 7481 GpStatus stat; 7482 GpRegion* rgn; 7483 7484 TRACE("(%p, %p)\n", graphics, res); 7485 7486 if((stat = GdipCreateRegion(&rgn)) != Ok) 7487 return stat; 7488 7489 if((stat = get_visible_clip_region(graphics, rgn)) != Ok) 7490 goto cleanup; 7491 7492 stat = GdipIsEmptyRegion(rgn, graphics, res); 7493 7494 cleanup: 7495 GdipDeleteRegion(rgn); 7496 return stat; 7497 } 7498 7499 GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics) 7500 { 7501 static int calls; 7502 7503 TRACE("(%p) stub\n", graphics); 7504 7505 if(!(calls++)) 7506 FIXME("not implemented\n"); 7507 7508 return NotImplemented; 7509 } 7510 7511 GpStatus WINGDIPAPI GdipGraphicsSetAbort(GpGraphics *graphics, GdiplusAbort *pabort) 7512 { 7513 TRACE("(%p, %p)\n", graphics, pabort); 7514 7515 if (!graphics) 7516 return InvalidParameter; 7517 7518 if (pabort) 7519 FIXME("Abort callback is not supported.\n"); 7520 7521 return Ok; 7522 } 7523