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