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 TRACE("(%p,%0.2f,%0.2f,%u)\n", pen, dx, dy, order); 464 465 if(!pen) 466 return InvalidParameter; 467 468 return GdipTranslateMatrix(&pen->transform, dx, dy, order); 469 } 470 471 GpStatus WINGDIPAPI GdipScalePenTransform(GpPen *pen, REAL sx, REAL sy, GpMatrixOrder order) 472 { 473 TRACE("(%p,%0.2f,%0.2f,%u)\n", pen, sx, sy, order); 474 475 if(!pen) 476 return InvalidParameter; 477 478 return GdipScaleMatrix(&pen->transform, sx, sy, order); 479 } 480 481 GpStatus WINGDIPAPI GdipRotatePenTransform(GpPen *pen, REAL angle, GpMatrixOrder order) 482 { 483 TRACE("(%p,%0.2f,%u)\n", pen, angle, order); 484 485 if(!pen) 486 return InvalidParameter; 487 488 return GdipRotateMatrix(&pen->transform, angle, order); 489 } 490 491 GpStatus WINGDIPAPI GdipMultiplyPenTransform(GpPen *pen, GDIPCONST GpMatrix *matrix, 492 GpMatrixOrder order) 493 { 494 TRACE("(%p,%p,%u)\n", pen, matrix, order); 495 496 if(!pen) 497 return InvalidParameter; 498 499 return GdipMultiplyMatrix(&pen->transform, matrix, order); 500 } 501 502 GpStatus WINGDIPAPI GdipSetPenBrushFill(GpPen *pen, GpBrush *brush) 503 { 504 TRACE("(%p, %p)\n", pen, brush); 505 506 if(!pen || !brush) 507 return InvalidParameter; 508 509 GdipDeleteBrush(pen->brush); 510 return GdipCloneBrush(brush, &pen->brush); 511 } 512 513 GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb) 514 { 515 TRACE("(%p, %x)\n", pen, argb); 516 517 if(!pen) 518 return InvalidParameter; 519 520 if(pen->brush->bt != BrushTypeSolidColor) 521 return NotImplemented; 522 523 return GdipSetSolidFillColor(((GpSolidFill*)pen->brush), argb); 524 } 525 526 GpStatus WINGDIPAPI GdipGetPenCompoundCount(GpPen *pen, INT *count) 527 { 528 FIXME("(%p, %p): stub\n", pen, count); 529 530 if (!pen || !count) 531 return InvalidParameter; 532 533 return NotImplemented; 534 } 535 536 GpStatus WINGDIPAPI GdipSetPenCompoundArray(GpPen *pen, GDIPCONST REAL *dash, 537 INT count) 538 { 539 FIXME("(%p, %p, %i): stub\n", pen, dash, count); 540 541 if (!pen || !dash || count < 2 || count%2 == 1) 542 return InvalidParameter; 543 544 return NotImplemented; 545 } 546 547 GpStatus WINGDIPAPI GdipSetPenCustomEndCap(GpPen *pen, GpCustomLineCap* customCap) 548 { 549 GpCustomLineCap * cap; 550 GpStatus ret; 551 552 TRACE("(%p, %p)\n", pen, customCap); 553 554 /* native crashes on pen == NULL, customCap != NULL */ 555 if(!customCap) return InvalidParameter; 556 557 if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){ 558 GdipDeleteCustomLineCap(pen->customend); 559 pen->endcap = LineCapCustom; 560 pen->customend = cap; 561 } 562 563 return ret; 564 } 565 566 GpStatus WINGDIPAPI GdipSetPenCustomStartCap(GpPen *pen, GpCustomLineCap* customCap) 567 { 568 GpCustomLineCap * cap; 569 GpStatus ret; 570 571 TRACE("(%p, %p)\n", pen, customCap); 572 573 /* native crashes on pen == NULL, customCap != NULL */ 574 if(!customCap) return InvalidParameter; 575 576 if((ret = GdipCloneCustomLineCap(customCap, &cap)) == Ok){ 577 GdipDeleteCustomLineCap(pen->customstart); 578 pen->startcap = LineCapCustom; 579 pen->customstart = cap; 580 } 581 582 return ret; 583 } 584 585 GpStatus WINGDIPAPI GdipSetPenDashArray(GpPen *pen, GDIPCONST REAL *dash, 586 INT count) 587 { 588 INT i; 589 REAL sum = 0; 590 591 TRACE("(%p, %p, %d)\n", pen, dash, count); 592 593 if(!pen || !dash) 594 return InvalidParameter; 595 596 if(count <= 0) 597 return OutOfMemory; 598 599 for(i = 0; i < count; i++){ 600 sum += dash[i]; 601 if(dash[i] < 0.0) 602 return InvalidParameter; 603 } 604 605 if(sum == 0.0 && count) 606 return InvalidParameter; 607 608 heap_free(pen->dashes); 609 pen->dashes = NULL; 610 611 if(count > 0) 612 pen->dashes = heap_alloc_zero(count * sizeof(REAL)); 613 if(!pen->dashes){ 614 pen->numdashes = 0; 615 return OutOfMemory; 616 } 617 618 GdipSetPenDashStyle(pen, DashStyleCustom); 619 memcpy(pen->dashes, dash, count * sizeof(REAL)); 620 pen->numdashes = count; 621 622 return Ok; 623 } 624 625 GpStatus WINGDIPAPI GdipSetPenDashCap197819(GpPen *pen, GpDashCap dashCap) 626 { 627 TRACE("(%p, %d)\n", pen, dashCap); 628 629 if(!pen) 630 return InvalidParameter; 631 632 pen->dashcap = dashCap; 633 634 return Ok; 635 } 636 637 /* FIXME: dash offset not used */ 638 GpStatus WINGDIPAPI GdipSetPenDashOffset(GpPen *pen, REAL offset) 639 { 640 TRACE("(%p, %.2f)\n", pen, offset); 641 642 if(!pen) 643 return InvalidParameter; 644 645 pen->offset = offset; 646 647 return Ok; 648 } 649 650 GpStatus WINGDIPAPI GdipSetPenDashStyle(GpPen *pen, GpDashStyle dash) 651 { 652 TRACE("(%p, %d)\n", pen, dash); 653 654 if(!pen) 655 return InvalidParameter; 656 657 if(dash != DashStyleCustom){ 658 heap_free(pen->dashes); 659 pen->dashes = NULL; 660 pen->numdashes = 0; 661 } 662 663 pen->dash = dash; 664 pen->style &= ~(PS_ALTERNATE | PS_SOLID | PS_DASH | PS_DOT | PS_DASHDOT | 665 PS_DASHDOTDOT | PS_NULL | PS_USERSTYLE | PS_INSIDEFRAME); 666 pen->style |= gdip_to_gdi_dash(dash); 667 668 return Ok; 669 } 670 671 GpStatus WINGDIPAPI GdipSetPenEndCap(GpPen *pen, GpLineCap cap) 672 { 673 TRACE("(%p, %d)\n", pen, cap); 674 675 if(!pen) return InvalidParameter; 676 677 /* The old custom cap gets deleted even if the new style is LineCapCustom. */ 678 GdipDeleteCustomLineCap(pen->customend); 679 pen->customend = NULL; 680 pen->endcap = cap; 681 682 return Ok; 683 } 684 685 /* FIXME: startcap, dashcap not used. */ 686 GpStatus WINGDIPAPI GdipSetPenLineCap197819(GpPen *pen, GpLineCap start, 687 GpLineCap end, GpDashCap dash) 688 { 689 TRACE("%p, %d, %d, %d)\n", pen, start, end, dash); 690 691 if(!pen) 692 return InvalidParameter; 693 694 GdipDeleteCustomLineCap(pen->customend); 695 GdipDeleteCustomLineCap(pen->customstart); 696 pen->customend = NULL; 697 pen->customstart = NULL; 698 699 pen->startcap = start; 700 pen->endcap = end; 701 pen->dashcap = dash; 702 703 return Ok; 704 } 705 706 /* FIXME: Miter line joins behave a bit differently than they do in windows. 707 * Both kinds of miter joins clip if the angle is less than 11 degrees. */ 708 GpStatus WINGDIPAPI GdipSetPenLineJoin(GpPen *pen, GpLineJoin join) 709 { 710 TRACE("(%p, %d)\n", pen, join); 711 712 if(!pen) return InvalidParameter; 713 714 pen->join = join; 715 pen->style &= ~(PS_JOIN_ROUND | PS_JOIN_BEVEL | PS_JOIN_MITER); 716 pen->style |= gdip_to_gdi_join(join); 717 718 return Ok; 719 } 720 721 GpStatus WINGDIPAPI GdipSetPenMiterLimit(GpPen *pen, REAL limit) 722 { 723 TRACE("(%p, %.2f)\n", pen, limit); 724 725 if(!pen) 726 return InvalidParameter; 727 728 pen->miterlimit = limit; 729 730 return Ok; 731 } 732 733 GpStatus WINGDIPAPI GdipSetPenStartCap(GpPen *pen, GpLineCap cap) 734 { 735 TRACE("(%p, %d)\n", pen, cap); 736 737 if(!pen) return InvalidParameter; 738 739 GdipDeleteCustomLineCap(pen->customstart); 740 pen->customstart = NULL; 741 pen->startcap = cap; 742 743 return Ok; 744 } 745 746 GpStatus WINGDIPAPI GdipSetPenWidth(GpPen *pen, REAL width) 747 { 748 TRACE("(%p, %.2f)\n", pen, width); 749 750 if(!pen) return InvalidParameter; 751 752 pen->width = width; 753 754 return Ok; 755 } 756 757 GpStatus WINGDIPAPI GdipSetPenMode(GpPen *pen, GpPenAlignment mode) 758 { 759 TRACE("(%p, %d)\n", pen, mode); 760 761 if(!pen) return InvalidParameter; 762 763 pen->align = mode; 764 765 return Ok; 766 } 767