1 /* 2 * Copyright (C) 2007 Google (Evan Stade) 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <stdarg.h> 20 21 #include "windef.h" 22 #include "winbase.h" 23 #include "wingdi.h" 24 25 #include "objbase.h" 26 27 #include "gdiplus.h" 28 #include "gdiplus_private.h" 29 #include "wine/debug.h" 30 31 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); 32 33 static DWORD gdip_to_gdi_dash(GpDashStyle dash) 34 { 35 switch(dash){ 36 case DashStyleSolid: 37 return PS_SOLID; 38 case DashStyleDash: 39 return PS_DASH; 40 case DashStyleDot: 41 return PS_DOT; 42 case DashStyleDashDot: 43 return PS_DASHDOT; 44 case DashStyleDashDotDot: 45 return PS_DASHDOTDOT; 46 case DashStyleCustom: 47 return PS_USERSTYLE; 48 default: 49 ERR("Not a member of GpDashStyle enumeration\n"); 50 return 0; 51 } 52 } 53 54 static DWORD gdip_to_gdi_join(GpLineJoin join) 55 { 56 switch(join){ 57 case LineJoinRound: 58 return PS_JOIN_ROUND; 59 case LineJoinBevel: 60 return PS_JOIN_BEVEL; 61 case LineJoinMiter: 62 case LineJoinMiterClipped: 63 return PS_JOIN_MITER; 64 default: 65 ERR("Not a member of GpLineJoin enumeration\n"); 66 return 0; 67 } 68 } 69 70 static GpPenType bt_to_pt(GpBrushType bt) 71 { 72 switch(bt){ 73 case BrushTypeSolidColor: 74 return PenTypeSolidColor; 75 case BrushTypeHatchFill: 76 return PenTypeHatchFill; 77 case BrushTypeTextureFill: 78 return PenTypeTextureFill; 79 case BrushTypePathGradient: 80 return PenTypePathGradient; 81 case BrushTypeLinearGradient: 82 return PenTypeLinearGradient; 83 default: 84 return PenTypeUnknown; 85 } 86 } 87 88 GpStatus WINGDIPAPI GdipClonePen(GpPen *pen, GpPen **clonepen) 89 { 90 GpStatus stat; 91 92 TRACE("(%p, %p)\n", pen, clonepen); 93 94 if(!pen || !clonepen) 95 return InvalidParameter; 96 97 *clonepen = heap_alloc_zero(sizeof(GpPen)); 98 if(!*clonepen) return OutOfMemory; 99 100 **clonepen = *pen; 101 102 (*clonepen)->customstart = NULL; 103 (*clonepen)->customend = NULL; 104 (*clonepen)->brush = NULL; 105 (*clonepen)->dashes = NULL; 106 107 stat = GdipCloneBrush(pen->brush, &(*clonepen)->brush); 108 109 if (stat == Ok && pen->customstart) 110 stat = GdipCloneCustomLineCap(pen->customstart, &(*clonepen)->customstart); 111 112 if (stat == Ok && pen->customend) 113 stat = GdipCloneCustomLineCap(pen->customend, &(*clonepen)->customend); 114 115 if (stat == Ok && pen->dashes) 116 { 117 (*clonepen)->dashes = heap_alloc_zero(pen->numdashes * sizeof(REAL)); 118 if ((*clonepen)->dashes) 119 memcpy((*clonepen)->dashes, pen->dashes, pen->numdashes * sizeof(REAL)); 120 else 121 stat = OutOfMemory; 122 } 123 124 if (stat != Ok) 125 { 126 GdipDeletePen(*clonepen); 127 *clonepen = NULL; 128 return stat; 129 } 130 131 TRACE("<-- %p\n", *clonepen); 132 133 return Ok; 134 } 135 136 GpStatus WINGDIPAPI GdipCreatePen1(ARGB color, REAL width, GpUnit unit, 137 GpPen **pen) 138 { 139 GpBrush *brush; 140 GpStatus status; 141 142 TRACE("(%x, %.2f, %d, %p)\n", color, width, unit, pen); 143 144 GdipCreateSolidFill(color, (GpSolidFill **)(&brush)); 145 status = GdipCreatePen2(brush, width, unit, pen); 146 GdipDeleteBrush(brush); 147 return status; 148 } 149 150 GpStatus WINGDIPAPI GdipCreatePen2(GpBrush *brush, REAL width, GpUnit unit, 151 GpPen **pen) 152 { 153 GpPen *gp_pen; 154 GpBrush *clone_brush; 155 156 TRACE("(%p, %.2f, %d, %p)\n", brush, width, unit, pen); 157 158 if(!pen || !brush) 159 return InvalidParameter; 160 161 gp_pen = heap_alloc_zero(sizeof(GpPen)); 162 if(!gp_pen) return OutOfMemory; 163 164 gp_pen->style = GP_DEFAULT_PENSTYLE; 165 gp_pen->width = width; 166 gp_pen->unit = unit; 167 gp_pen->endcap = LineCapFlat; 168 gp_pen->join = LineJoinMiter; 169 gp_pen->miterlimit = 10.0; 170 gp_pen->dash = DashStyleSolid; 171 gp_pen->offset = 0.0; 172 gp_pen->customstart = NULL; 173 gp_pen->customend = NULL; 174 GdipSetMatrixElements(&gp_pen->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); 175 176 if(!((gp_pen->unit == UnitWorld) || (gp_pen->unit == UnitPixel))) { 177 FIXME("UnitWorld, UnitPixel only supported units\n"); 178 heap_free(gp_pen); 179 return NotImplemented; 180 } 181 182 GdipCloneBrush(brush, &clone_brush); 183 gp_pen->brush = clone_brush; 184 185 *pen = gp_pen; 186 187 TRACE("<-- %p\n", *pen); 188 189 return Ok; 190 } 191 192 GpStatus WINGDIPAPI GdipDeletePen(GpPen *pen) 193 { 194 TRACE("(%p)\n", pen); 195 196 if(!pen) return InvalidParameter; 197 198 GdipDeleteBrush(pen->brush); 199 GdipDeleteCustomLineCap(pen->customstart); 200 GdipDeleteCustomLineCap(pen->customend); 201 heap_free(pen->dashes); 202 heap_free(pen); 203 204 return Ok; 205 } 206 207 GpStatus WINGDIPAPI GdipGetPenBrushFill(GpPen *pen, GpBrush **brush) 208 { 209 TRACE("(%p, %p)\n", pen, brush); 210 211 if(!pen || !brush) 212 return InvalidParameter; 213 214 return GdipCloneBrush(pen->brush, brush); 215 } 216 217 GpStatus WINGDIPAPI GdipGetPenColor(GpPen *pen, ARGB *argb) 218 { 219 TRACE("(%p, %p)\n", pen, argb); 220 221 if(!pen || !argb) 222 return InvalidParameter; 223 224 if(pen->brush->bt != BrushTypeSolidColor) 225 return NotImplemented; 226 227 return GdipGetSolidFillColor(((GpSolidFill*)pen->brush), argb); 228 } 229 230 GpStatus WINGDIPAPI GdipGetPenCustomEndCap(GpPen *pen, GpCustomLineCap** customCap) 231 { 232 TRACE("(%p, %p)\n", pen, customCap); 233 234 if(!pen || !customCap) 235 return InvalidParameter; 236 237 if(!pen->customend){ 238 *customCap = NULL; 239 return Ok; 240 } 241 242 return GdipCloneCustomLineCap(pen->customend, customCap); 243 } 244 245 GpStatus WINGDIPAPI GdipGetPenCustomStartCap(GpPen *pen, GpCustomLineCap** customCap) 246 { 247 TRACE("(%p, %p)\n", pen, customCap); 248 249 if(!pen || !customCap) 250 return InvalidParameter; 251 252 if(!pen->customstart){ 253 *customCap = NULL; 254 return Ok; 255 } 256 257 return GdipCloneCustomLineCap(pen->customstart, customCap); 258 } 259 260 GpStatus WINGDIPAPI GdipGetPenDashArray(GpPen *pen, REAL *dash, INT count) 261 { 262 TRACE("(%p, %p, %d)\n", pen, dash, count); 263 264 if(!pen || !dash || count > pen->numdashes) 265 return InvalidParameter; 266 267 /* note: if you pass a negative value for count, it crashes native gdiplus. */ 268 if(count < 0) 269 return GenericError; 270 271 memcpy(dash, pen->dashes, count * sizeof(REAL)); 272 273 return Ok; 274 } 275 276 GpStatus WINGDIPAPI GdipGetPenDashCap197819(GpPen *pen, GpDashCap *dashCap) 277 { 278 TRACE("(%p, %p)\n", pen, dashCap); 279 280 if(!pen || !dashCap) 281 return InvalidParameter; 282 283 *dashCap = pen->dashcap; 284 285 return Ok; 286 } 287 288 GpStatus WINGDIPAPI GdipGetPenDashCount(GpPen *pen, INT *count) 289 { 290 TRACE("(%p, %p)\n", pen, count); 291 292 if(!pen || !count) 293 return InvalidParameter; 294 295 *count = pen->numdashes; 296 297 return Ok; 298 } 299 300 GpStatus WINGDIPAPI GdipGetPenDashOffset(GpPen *pen, REAL *offset) 301 { 302 TRACE("(%p, %p)\n", pen, offset); 303 304 if(!pen || !offset) 305 return InvalidParameter; 306 307 *offset = pen->offset; 308 309 return Ok; 310 } 311 312 GpStatus WINGDIPAPI GdipGetPenDashStyle(GpPen *pen, GpDashStyle *dash) 313 { 314 TRACE("(%p, %p)\n", pen, dash); 315 316 if(!pen || !dash) 317 return InvalidParameter; 318 319 *dash = pen->dash; 320 321 return Ok; 322 } 323 324 GpStatus WINGDIPAPI GdipGetPenEndCap(GpPen *pen, GpLineCap *endCap) 325 { 326 TRACE("(%p, %p)\n", pen, endCap); 327 328 if(!pen || !endCap) 329 return InvalidParameter; 330 331 *endCap = pen->endcap; 332 333 return Ok; 334 } 335 336 GpStatus WINGDIPAPI GdipGetPenFillType(GpPen *pen, GpPenType* type) 337 { 338 TRACE("(%p, %p)\n", pen, type); 339 340 if(!pen || !type) 341 return InvalidParameter; 342 343 *type = bt_to_pt(pen->brush->bt); 344 345 return Ok; 346 } 347 348 GpStatus WINGDIPAPI GdipGetPenLineJoin(GpPen *pen, GpLineJoin *lineJoin) 349 { 350 TRACE("(%p, %p)\n", pen, lineJoin); 351 352 if(!pen || !lineJoin) 353 return InvalidParameter; 354 355 *lineJoin = pen->join; 356 357 return Ok; 358 } 359 360 GpStatus WINGDIPAPI GdipGetPenMode(GpPen *pen, GpPenAlignment *mode) 361 { 362 TRACE("(%p, %p)\n", pen, mode); 363 364 if(!pen || !mode) 365 return InvalidParameter; 366 367 *mode = pen->align; 368 369 return Ok; 370 } 371 372 GpStatus WINGDIPAPI GdipGetPenMiterLimit(GpPen *pen, REAL *miterLimit) 373 { 374 TRACE("(%p, %p)\n", pen, miterLimit); 375 376 if(!pen || !miterLimit) 377 return InvalidParameter; 378 379 *miterLimit = pen->miterlimit; 380 381 return Ok; 382 } 383 384 GpStatus WINGDIPAPI GdipGetPenStartCap(GpPen *pen, GpLineCap *startCap) 385 { 386 TRACE("(%p, %p)\n", pen, startCap); 387 388 if(!pen || !startCap) 389 return InvalidParameter; 390 391 *startCap = pen->startcap; 392 393 return Ok; 394 } 395 396 GpStatus WINGDIPAPI GdipGetPenUnit(GpPen *pen, GpUnit *unit) 397 { 398 TRACE("(%p, %p)\n", pen, unit); 399 400 if(!pen || !unit) 401 return InvalidParameter; 402 403 *unit = pen->unit; 404 405 return Ok; 406 } 407 408 GpStatus WINGDIPAPI GdipGetPenWidth(GpPen *pen, REAL *width) 409 { 410 TRACE("(%p, %p)\n", pen, width); 411 412 if(!pen || !width) 413 return InvalidParameter; 414 415 *width = pen->width; 416 417 return Ok; 418 } 419 420 GpStatus WINGDIPAPI GdipResetPenTransform(GpPen *pen) 421 { 422 TRACE("(%p)\n", pen); 423 424 if(!pen) 425 return InvalidParameter; 426 427 GdipSetMatrixElements(&pen->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); 428 429 return Ok; 430 } 431 432 GpStatus WINGDIPAPI GdipSetPenTransform(GpPen *pen, GpMatrix *matrix) 433 { 434 static int calls; 435 436 TRACE("(%p,%p)\n", pen, matrix); 437 438 if(!pen || !matrix) 439 return InvalidParameter; 440 441 if(!(calls++)) 442 FIXME("(%p,%p) Semi-stub\n", pen, matrix); 443 444 pen->transform = *matrix; 445 446 return Ok; 447 } 448 449 GpStatus WINGDIPAPI GdipGetPenTransform(GpPen *pen, GpMatrix *matrix) 450 { 451 TRACE("(%p,%p)\n", pen, matrix); 452 453 if(!pen || !matrix) 454 return InvalidParameter; 455 456 *matrix = pen->transform; 457 458 return Ok; 459 } 460 461 GpStatus WINGDIPAPI GdipTranslatePenTransform(GpPen *pen, REAL dx, REAL dy, GpMatrixOrder order) 462 { 463 static int calls; 464 465 TRACE("(%p,%0.2f,%0.2f,%u)\n", pen, dx, dy, order); 466 467 if(!pen) 468 return InvalidParameter; 469 470 if(!(calls++)) 471 FIXME("not implemented\n"); 472 473 return NotImplemented; 474 } 475 476 GpStatus WINGDIPAPI GdipScalePenTransform(GpPen *pen, REAL sx, REAL sy, GpMatrixOrder order) 477 { 478 static int calls; 479 480 TRACE("(%p,%0.2f,%0.2f,%u)\n", pen, sx, sy, order); 481 482 if(!pen) 483 return InvalidParameter; 484 485 if(!(calls++)) 486 FIXME("(%p, %.2f, %.2f, %d) stub\n", pen, sx, sy, order); 487 488 return NotImplemented; 489 } 490 491 GpStatus WINGDIPAPI GdipRotatePenTransform(GpPen *pen, REAL angle, GpMatrixOrder order) 492 { 493 static int calls; 494 495 TRACE("(%p,%0.2f,%u)\n", pen, angle, order); 496 497 if(!pen) 498 return InvalidParameter; 499 500 if(!(calls++)) 501 FIXME("not implemented\n"); 502 503 return NotImplemented; 504 } 505 506 GpStatus WINGDIPAPI GdipMultiplyPenTransform(GpPen *pen, GDIPCONST GpMatrix *matrix, 507 GpMatrixOrder order) 508 { 509 static int calls; 510 511 TRACE("(%p,%p,%u)\n", pen, matrix, order); 512 513 if(!pen) 514 return InvalidParameter; 515 516 if(!(calls++)) 517 FIXME("not implemented\n"); 518 519 return NotImplemented; 520 } 521 522 GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush) 523 { 524 TRACE("(%p, %p)\n", pen, brush); 525 526 if(!pen || !brush) 527 return InvalidParameter; 528 529 GdipDeleteBrush(pen->brush); 530 return GdipCloneBrush(brush, &pen->brush); 531 } 532 533 GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb) 534 { 535 TRACE("(%p, %x)\n", pen, argb); 536 537 if(!pen) 538 return InvalidParameter; 539 540 if(pen->brush->bt != BrushTypeSolidColor) 541 return NotImplemented; 542 543 return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb); 544 } 545 546 GpStatus WINGDIPAPI GdipGetPenCompoundCount(GpPen *pen, INT *count) 547 { 548 FIXME("(%p, %p): stub\n", pen, count); 549 550 if (!pen || !count) 551 return InvalidParameter; 552 553 return NotImplemented; 554 } 555 556 GpStatus WINGDIPAPI GdipSetPenCompoundArray(GpPen *pen, GDIPCONST REAL *dash, 557 INT count) 558 { 559 FIXME("(%p, %p, %i): stub\n", pen, dash, count); 560 561 if (!pen || !dash || count < 2 || count%2 == 1) 562 return InvalidParameter; 563 564 return NotImplemented; 565 } 566 567 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap) 568 { 569 GpCustomLineCap * cap; 570 GpStatus ret; 571 572 TRACE("(%p, %p)\n", pen, customCap); 573 574 /* native crashes on pen == NULL, customCap != NULL */ 575 if(!customCap) return InvalidParameter; 576 577 if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){ 578 GdipDeleteCustomLineCap(pen->customend); 579 pen->endcap = LineCapCustom; 580 pen->customend = cap; 581 } 582 583 return ret; 584 } 585 586 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap) 587 { 588 GpCustomLineCap * cap; 589 GpStatus ret; 590 591 TRACE("(%p, %p)\n", pen, customCap); 592 593 /* native crashes on pen == NULL, customCap != NULL */ 594 if(!customCap) return InvalidParameter; 595 596 if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){ 597 GdipDeleteCustomLineCap(pen->customstart); 598 pen->startcap = LineCapCustom; 599 pen->customstart = cap; 600 } 601 602 return ret; 603 } 604 605 GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash, 606 INT count) 607 { 608 INT i; 609 REAL sum = 0; 610 611 TRACE("(%p, %p, %d)\n", pen, dash, count); 612 613 if(!pen || !dash) 614 return InvalidParameter; 615 616 if(count <= 0) 617 return OutOfMemory; 618 619 for(i = 0; i < count; i++){ 620 sum += dash[i]; 621 if(dash[i] < 0.0) 622 return InvalidParameter; 623 } 624 625 if(sum == 0.0 && count) 626 return InvalidParameter; 627 628 heap_free(pen->dashes); 629 pen->dashes = NULL; 630 631 if(count > 0) 632 pen->dashes = heap_alloc_zero(count * sizeof(REAL)); 633 if(!pen->dashes){ 634 pen->numdashes = 0; 635 return OutOfMemory; 636 } 637 638 GdipSetPenDashStyle(pen, DashStyleCustom); 639 memcpy(pen->dashes, dash, count * sizeof(REAL)); 640 pen->numdashes = count; 641 642 return Ok; 643 } 644 645 GpStatus WINGDIPAPI GdipSetPenDashCap197819(GpPen *pen, GpDashCap dashCap) 646 { 647 TRACE("(%p, %d)\n", pen, dashCap); 648 649 if(!pen) 650 return InvalidParameter; 651 652 pen->dashcap = dashCap; 653 654 return Ok; 655 } 656 657 /* FIXME: dash offset not used */ 658 GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset) 659 { 660 TRACE("(%p, %.2f)\n", pen, offset); 661 662 if(!pen) 663 return InvalidParameter; 664 665 pen->offset = offset; 666 667 return Ok; 668 } 669 670 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash) 671 { 672 TRACE("(%p, %d)\n", pen, dash); 673 674 if(!pen) 675 return InvalidParameter; 676 677 if(dash != DashStyleCustom){ 678 heap_free(pen->dashes); 679 pen->dashes = NULL; 680 pen->numdashes = 0; 681 } 682 683 pen->dash = dash; 684 pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT | 685 PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME); 686 pen->style |= gdip_to_gdi_dash(dash); 687 688 return Ok; 689 } 690 691 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap) 692 { 693 TRACE("(%p, %d)\n", pen, cap); 694 695 if(!pen) return InvalidParameter; 696 697 /* The old custom cap gets deleted even if the new style is LineCapCustom. */ 698 GdipDeleteCustomLineCap(pen->customend); 699 pen->customend = NULL; 700 pen->endcap = cap; 701 702 return Ok; 703 } 704 705 /* FIXME: startcap, dashcap not used. */ 706 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start, 707 GpLineCap end, GpDashCap dash) 708 { 709 TRACE("%p, %d, %d, %d)\n", pen, start, end, dash); 710 711 if(!pen) 712 return InvalidParameter; 713 714 GdipDeleteCustomLineCap(pen->customend); 715 GdipDeleteCustomLineCap(pen->customstart); 716 pen->customend = NULL; 717 pen->customstart = NULL; 718 719 pen->startcap = start; 720 pen->endcap = end; 721 pen->dashcap = dash; 722 723 return Ok; 724 } 725 726 /* FIXME: Miter line joins behave a bit differently than they do in windows. 727 * Both kinds of miter joins clip if the angle is less than 11 degrees. */ 728 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join) 729 { 730 TRACE("(%p, %d)\n", pen, join); 731 732 if(!pen) return InvalidParameter; 733 734 pen->join = join; 735 pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER); 736 pen->style |= gdip_to_gdi_join(join); 737 738 return Ok; 739 } 740 741 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit) 742 { 743 TRACE("(%p, %.2f)\n", pen, limit); 744 745 if(!pen) 746 return InvalidParameter; 747 748 pen->miterlimit = limit; 749 750 return Ok; 751 } 752 753 GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap) 754 { 755 TRACE("(%p, %d)\n", pen, cap); 756 757 if(!pen) return InvalidParameter; 758 759 GdipDeleteCustomLineCap(pen->customstart); 760 pen->customstart = NULL; 761 pen->startcap = cap; 762 763 return Ok; 764 } 765 766 GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width) 767 { 768 TRACE("(%p, %.2f)\n", pen, width); 769 770 if(!pen) return InvalidParameter; 771 772 pen->width = width; 773 774 return Ok; 775 } 776 777 GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment mode) 778 { 779 TRACE("(%p, %d)\n", pen, mode); 780 781 if(!pen) return InvalidParameter; 782 783 pen->align = mode; 784 785 return Ok; 786 } 787