1 /* 2 * Copyright 2006, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 //---------------------------------------------------------------------------- 10 // Anti-Grain Geometry - Version 2.2 11 // Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com) 12 // 13 // Permission to copy, use, modify, sell and distribute this software 14 // is granted provided this copyright notice appears in all copies. 15 // This software is provided "as is" without express or implied 16 // warranty, and with no claim as to its suitability for any purpose. 17 // 18 //---------------------------------------------------------------------------- 19 // Contact: mcseem@antigrain.com 20 // mcseemagg@yahoo.com 21 // http://www.antigrain.com 22 //---------------------------------------------------------------------------- 23 24 #include "DocumentBuilder.h" 25 26 #include <new> 27 #include <stdio.h> 28 29 #include <Bitmap.h> 30 31 #include <agg_bounding_rect.h> 32 33 #include "AutoDeleter.h" 34 #include "GradientTransformable.h" 35 #include "Icon.h" 36 #include "PathContainer.h" 37 #include "Shape.h" 38 #include "ShapeContainer.h" 39 #include "StrokeTransformer.h" 40 #include "Style.h" 41 #include "StyleContainer.h" 42 #include "SVGGradients.h" 43 #include "SVGImporter.h" 44 #include "VectorPath.h" 45 46 using std::nothrow; 47 48 namespace agg { 49 namespace svg { 50 51 // constructor 52 DocumentBuilder::DocumentBuilder() 53 : fGradients(20), 54 fCurrentGradient(NULL), 55 fWidth(0), 56 fHeight(0), 57 fViewBox(0.0, 0.0, -1.0, -1.0), 58 fTitle("") 59 { 60 } 61 62 63 // remove_all 64 void 65 DocumentBuilder::remove_all() 66 { 67 fPathStorage.remove_all(); 68 fAttributesStorage.remove_all(); 69 fAttributesStack.remove_all(); 70 fTransform.reset(); 71 } 72 73 // begin_path 74 void 75 DocumentBuilder::begin_path() 76 { 77 push_attr(); 78 unsigned idx = fPathStorage.start_new_path(); 79 fAttributesStorage.add(path_attributes(cur_attr(), idx)); 80 } 81 82 // end_path 83 void 84 DocumentBuilder::end_path() 85 { 86 if (fAttributesStorage.size() == 0) { 87 throw exception("end_path: The path was not begun"); 88 } 89 path_attributes attr = cur_attr(); 90 unsigned idx = fAttributesStorage[fAttributesStorage.size() - 1].index; 91 attr.index = idx; 92 fAttributesStorage[fAttributesStorage.size() - 1] = attr; 93 pop_attr(); 94 } 95 96 // move_to 97 void 98 DocumentBuilder::move_to(double x, double y, bool rel) // M, m 99 { 100 if (rel) 101 fPathStorage.move_rel(x, y); 102 else 103 fPathStorage.move_to(x, y); 104 } 105 106 // line_to 107 void 108 DocumentBuilder::line_to(double x, double y, bool rel) // L, l 109 { 110 if (rel) 111 fPathStorage.line_rel(x, y); 112 else 113 fPathStorage.line_to(x, y); 114 } 115 116 // hline_to 117 void 118 DocumentBuilder::hline_to(double x, bool rel) // H, h 119 { 120 if (rel) 121 fPathStorage.hline_rel(x); 122 else 123 fPathStorage.hline_to(x); 124 } 125 126 // vline_to 127 void 128 DocumentBuilder::vline_to(double y, bool rel) // V, v 129 { 130 if (rel) 131 fPathStorage.vline_rel(y); 132 else 133 fPathStorage.vline_to(y); 134 } 135 136 // curve3 137 void 138 DocumentBuilder::curve3(double x1, double y1, // Q, q 139 double x, double y, bool rel) 140 { 141 if (rel) 142 fPathStorage.curve3_rel(x1, y1, x, y); 143 else 144 fPathStorage.curve3(x1, y1, x, y); 145 } 146 147 // curve3 148 void 149 DocumentBuilder::curve3(double x, double y, bool rel) // T, t 150 { 151 if (rel) 152 fPathStorage.curve3_rel(x, y); 153 else 154 fPathStorage.curve3(x, y); 155 } 156 157 // curve4 158 void 159 DocumentBuilder::curve4(double x1, double y1, // C, c 160 double x2, double y2, 161 double x, double y, bool rel) 162 { 163 if (rel) { 164 fPathStorage.curve4_rel(x1, y1, x2, y2, x, y); 165 } else { 166 fPathStorage.curve4(x1, y1, x2, y2, x, y); 167 } 168 } 169 170 // curve4 171 void 172 DocumentBuilder::curve4(double x2, double y2, // S, s 173 double x, double y, bool rel) 174 { 175 if (rel) { 176 fPathStorage.curve4_rel(x2, y2, x, y); 177 } else { 178 fPathStorage.curve4(x2, y2, x, y); 179 } 180 } 181 182 // elliptical_arc 183 void 184 DocumentBuilder::elliptical_arc(double rx, double ry, double angle, 185 bool large_arc_flag, bool sweep_flag, 186 double x, double y, bool rel) 187 { 188 angle = angle / 180.0 * pi; 189 if (rel) { 190 fPathStorage.arc_rel(rx, ry, angle, large_arc_flag, sweep_flag, x, y); 191 } else { 192 fPathStorage.arc_to(rx, ry, angle, large_arc_flag, sweep_flag, x, y); 193 } 194 } 195 196 // close_subpath 197 void 198 DocumentBuilder::close_subpath() 199 { 200 fPathStorage.end_poly(path_flags_close); 201 } 202 203 // SetTitle 204 void 205 DocumentBuilder::SetTitle(const char* title) 206 { 207 fTitle = title; 208 } 209 210 // SetDimensions 211 void 212 DocumentBuilder::SetDimensions(uint32 width, uint32 height, BRect viewBox) 213 { 214 fWidth = width; 215 fHeight = height; 216 fViewBox = viewBox; 217 } 218 219 // cur_attr 220 path_attributes& 221 DocumentBuilder::cur_attr() 222 { 223 if (fAttributesStack.size() == 0) { 224 throw exception("cur_attr: Attribute stack is empty"); 225 } 226 return fAttributesStack[fAttributesStack.size() - 1]; 227 } 228 229 // push_attr 230 void 231 DocumentBuilder::push_attr() 232 { 233 //printf("DocumentBuilder::push_attr() (size: %d)\n", fAttributesStack.size()); 234 fAttributesStack.add(fAttributesStack.size() ? fAttributesStack[fAttributesStack.size() - 1] 235 : path_attributes()); 236 } 237 238 // pop_attr 239 void 240 DocumentBuilder::pop_attr() 241 { 242 //printf("DocumentBuilder::pop_attr() (size: %d)\n", fAttributesStack.size()); 243 if (fAttributesStack.size() == 0) { 244 throw exception("pop_attr: Attribute stack is empty"); 245 } 246 fAttributesStack.remove_last(); 247 } 248 249 // fill 250 void 251 DocumentBuilder::fill(const rgba8& f) 252 { 253 path_attributes& attr = cur_attr(); 254 attr.fill_color = f; 255 attr.fill_flag = true; 256 } 257 258 // stroke 259 void 260 DocumentBuilder::stroke(const rgba8& s) 261 { 262 path_attributes& attr = cur_attr(); 263 attr.stroke_color = s; 264 attr.stroke_flag = true; 265 } 266 267 // even_odd 268 void 269 DocumentBuilder::even_odd(bool flag) 270 { 271 cur_attr().even_odd_flag = flag; 272 } 273 274 // stroke_width 275 void 276 DocumentBuilder::stroke_width(double w) 277 { 278 path_attributes& attr = cur_attr(); 279 attr.stroke_width = w; 280 attr.stroke_flag = true; 281 } 282 283 // fill_none 284 void 285 DocumentBuilder::fill_none() 286 { 287 cur_attr().fill_flag = false; 288 } 289 290 // fill_url 291 void 292 DocumentBuilder::fill_url(const char* url) 293 { 294 sprintf(cur_attr().fill_url, "%s", url); 295 } 296 297 // stroke_none 298 void 299 DocumentBuilder::stroke_none() 300 { 301 cur_attr().stroke_flag = false; 302 } 303 304 // stroke_url 305 void 306 DocumentBuilder::stroke_url(const char* url) 307 { 308 sprintf(cur_attr().stroke_url, "%s", url); 309 } 310 311 // opacity 312 void 313 DocumentBuilder::opacity(double op) 314 { 315 cur_attr().opacity *= op; 316 //printf("opacity: %.1f\n", cur_attr().opacity); 317 } 318 319 // fill_opacity 320 void 321 DocumentBuilder::fill_opacity(double op) 322 { 323 cur_attr().fill_color.opacity(op); 324 // cur_attr().opacity *= op; 325 } 326 327 // stroke_opacity 328 void 329 DocumentBuilder::stroke_opacity(double op) 330 { 331 cur_attr().stroke_color.opacity(op); 332 // cur_attr().opacity *= op; 333 } 334 335 // line_join 336 void 337 DocumentBuilder::line_join(line_join_e join) 338 { 339 cur_attr().line_join = join; 340 } 341 342 // line_cap 343 void 344 DocumentBuilder::line_cap(line_cap_e cap) 345 { 346 cur_attr().line_cap = cap; 347 } 348 349 // miter_limit 350 void 351 DocumentBuilder::miter_limit(double ml) 352 { 353 cur_attr().miter_limit = ml; 354 } 355 356 // transform 357 trans_affine& 358 DocumentBuilder::transform() 359 { 360 return cur_attr().transform; 361 } 362 363 // parse_path 364 void 365 DocumentBuilder::parse_path(PathTokenizer& tok) 366 { 367 char lastCmd = 0; 368 while(tok.next()) { 369 double arg[10]; 370 char cmd = tok.last_command(); 371 unsigned i; 372 switch(cmd) { 373 case 'M': case 'm': 374 arg[0] = tok.last_number(); 375 arg[1] = tok.next(cmd); 376 if (lastCmd != cmd) 377 move_to(arg[0], arg[1], cmd == 'm'); 378 else 379 line_to(arg[0], arg[1], lastCmd == 'm'); 380 break; 381 382 case 'L': case 'l': 383 arg[0] = tok.last_number(); 384 arg[1] = tok.next(cmd); 385 line_to(arg[0], arg[1], cmd == 'l'); 386 break; 387 388 case 'V': case 'v': 389 vline_to(tok.last_number(), cmd == 'v'); 390 break; 391 392 case 'H': case 'h': 393 hline_to(tok.last_number(), cmd == 'h'); 394 break; 395 396 case 'Q': case 'q': 397 arg[0] = tok.last_number(); 398 for(i = 1; i < 4; i++) { 399 arg[i] = tok.next(cmd); 400 } 401 curve3(arg[0], arg[1], arg[2], arg[3], cmd == 'q'); 402 break; 403 404 case 'T': case 't': 405 arg[0] = tok.last_number(); 406 arg[1] = tok.next(cmd); 407 curve3(arg[0], arg[1], cmd == 't'); 408 break; 409 410 case 'C': case 'c': 411 arg[0] = tok.last_number(); 412 for(i = 1; i < 6; i++) { 413 arg[i] = tok.next(cmd); 414 } 415 curve4(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], cmd == 'c'); 416 break; 417 418 case 'S': case 's': 419 arg[0] = tok.last_number(); 420 for(i = 1; i < 4; i++) { 421 arg[i] = tok.next(cmd); 422 } 423 curve4(arg[0], arg[1], arg[2], arg[3], cmd == 's'); 424 break; 425 426 case 'A': case 'a': { 427 arg[0] = tok.last_number(); 428 for(i = 1; i < 3; i++) { 429 arg[i] = tok.next(cmd); 430 } 431 bool large_arc_flag = (bool)tok.next(cmd); 432 bool sweep_flag = (bool)tok.next(cmd); 433 for(i = 3; i < 5; i++) { 434 arg[i] = tok.next(cmd); 435 } 436 elliptical_arc(arg[0], arg[1], arg[2], 437 large_arc_flag, sweep_flag, 438 arg[3], arg[4], cmd == 'a'); 439 break; 440 } 441 442 case 'Z': case 'z': 443 close_subpath(); 444 break; 445 446 default: 447 { 448 char buf[100]; 449 sprintf(buf, "parse_path: Invalid path command '%c'", cmd); 450 throw exception(buf); 451 } 452 } 453 lastCmd = cmd; 454 } 455 } 456 457 // #pragma mark - 458 459 // GetIcon 460 status_t 461 DocumentBuilder::GetIcon(Icon* icon, SVGImporter* importer, 462 const char* fallbackName) 463 { 464 double xMin; 465 double yMin; 466 double xMax; 467 double yMax; 468 469 int32 pathCount = fAttributesStorage.size(); 470 471 agg::conv_transform<agg::path_storage> transformedPaths( 472 fPathStorage, fTransform); 473 agg::bounding_rect(transformedPaths, *this, 0, pathCount, 474 &xMin, &yMin, &xMax, &yMax); 475 476 xMin = floor(xMin); 477 yMin = floor(yMin); 478 xMax = ceil(xMax); 479 yMax = ceil(yMax); 480 481 BRect bounds; 482 if (fViewBox.IsValid()) { 483 bounds = fViewBox; 484 printf("view box: "); 485 bounds.PrintToStream(); 486 } else { 487 bounds.Set(0.0, 0.0, (int32)fWidth - 1, (int32)fHeight - 1); 488 printf("width/height: "); 489 bounds.PrintToStream(); 490 } 491 492 BRect boundingBox(xMin, yMin, xMax, yMax); 493 494 if (!bounds.IsValid() || !boundingBox.Intersects(bounds)) { 495 bounds = boundingBox; 496 printf("using bounding box: "); 497 bounds.PrintToStream(); 498 } 499 500 float size = min_c(bounds.Width() + 1.0, bounds.Height() + 1.0); 501 double scale = 64.0 / size; 502 printf("scale: %f\n", scale); 503 504 Transformable transform; 505 transform.TranslateBy(BPoint(-bounds.left, -bounds.top)); 506 transform.ScaleBy(B_ORIGIN, scale, scale); 507 508 // if (fTitle.CountChars() > 0) 509 // icon->SetName(fTitle.String()); 510 // else 511 // icon->SetName(fallbackName); 512 513 for (int32 i = 0; i < pathCount; i++) { 514 515 path_attributes& attributes = fAttributesStorage[i]; 516 517 if (attributes.fill_flag) 518 _AddShape(attributes, false, transform, icon); 519 520 if (attributes.stroke_flag) 521 _AddShape(attributes, true, transform, icon); 522 } 523 524 // clean up styles and paths (remove duplicates) 525 int32 count = icon->Shapes()->CountShapes(); 526 for (int32 i = 1; i < count; i++) { 527 Shape* shape = icon->Shapes()->ShapeAtFast(i); 528 Style* style = shape->Style(); 529 if (!style) 530 continue; 531 int32 styleIndex = icon->Styles()->IndexOf(style); 532 for (int32 j = 0; j < styleIndex; j++) { 533 Style* earlierStyle = icon->Styles()->StyleAtFast(j); 534 if (*style == *earlierStyle) { 535 shape->SetStyle(earlierStyle); 536 icon->Styles()->RemoveStyle(style); 537 style->ReleaseReference(); 538 break; 539 } 540 } 541 } 542 543 return B_OK; 544 } 545 546 // StartGradient 547 void 548 DocumentBuilder::StartGradient(bool radial) 549 { 550 if (fCurrentGradient) { 551 fprintf(stderr, "DocumentBuilder::StartGradient() - ERROR: " 552 "previous gradient (%s) not finished!\n", 553 fCurrentGradient->ID()); 554 } 555 556 if (radial) 557 fCurrentGradient = new SVGRadialGradient(); 558 else 559 fCurrentGradient = new SVGLinearGradient(); 560 561 _AddGradient(fCurrentGradient); 562 } 563 564 // EndGradient 565 void 566 DocumentBuilder::EndGradient() 567 { 568 if (fCurrentGradient) { 569 // fCurrentGradient->PrintToStream(); 570 } else { 571 fprintf(stderr, "DocumentBuilder::EndGradient() - " 572 "ERROR: no gradient started!\n"); 573 } 574 fCurrentGradient = NULL; 575 } 576 577 // #pragma mark - 578 579 // _AddGradient 580 void 581 DocumentBuilder::_AddGradient(SVGGradient* gradient) 582 { 583 if (gradient) { 584 fGradients.AddItem((void*)gradient); 585 } 586 } 587 588 // _GradientAt 589 SVGGradient* 590 DocumentBuilder::_GradientAt(int32 index) const 591 { 592 return (SVGGradient*)fGradients.ItemAt(index); 593 } 594 595 // _FindGradient 596 SVGGradient* 597 DocumentBuilder::_FindGradient(const char* name) const 598 { 599 for (int32 i = 0; SVGGradient* g = _GradientAt(i); i++) { 600 if (strcmp(g->ID(), name) == 0) 601 return g; 602 } 603 return NULL; 604 } 605 606 607 // AddVertexSource 608 template<class VertexSource> 609 status_t 610 AddPathsFromVertexSource(Icon* icon, Shape* shape, 611 VertexSource& source, int32 index) 612 { 613 //printf("AddPathsFromVertexSource(pathID = %ld)\n", index); 614 615 // start with the first path 616 VectorPath* path = new (nothrow) VectorPath(); 617 if (!path || !icon->Paths()->AddPath(path)) { 618 delete path; 619 return B_NO_MEMORY; 620 } 621 622 if (!shape->Paths()->AddPath(path)) 623 return B_NO_MEMORY; 624 625 source.rewind(index); 626 double x1 = 0, y1 = 0; 627 unsigned cmd = source.vertex(&x1, &y1); 628 bool keepGoing = true; 629 int32 subPath = 0; 630 while (keepGoing) { 631 if (agg::is_next_poly(cmd)) { 632 //printf("next polygon\n"); 633 if (agg::is_end_poly(cmd)) { 634 //printf(" end polygon\n"); 635 path->SetClosed(true); 636 subPath++; 637 } else { 638 //printf(" not end polygon\n"); 639 } 640 641 if (agg::is_stop(cmd)) { 642 //printf(" stop = true\n"); 643 keepGoing = false; 644 } else { 645 if (subPath > 0) { 646 //printf(" new subpath\n"); 647 path->CleanUp(); 648 if (path->CountPoints() == 0) { 649 //printf(" path with no points!\n"); 650 icon->Paths()->RemovePath(path); 651 shape->Paths()->RemovePath(path); 652 path->ReleaseReference(); 653 } 654 path = new (nothrow) VectorPath(); 655 if (!path || !icon->Paths()->AddPath(path)) { 656 delete path; 657 return B_NO_MEMORY; 658 } 659 if (!shape->Paths()->AddPath(path)) 660 return B_NO_MEMORY; 661 } 662 } 663 } 664 switch (cmd) { 665 case agg::path_cmd_move_to: 666 //printf("move to (%.2f, %.2f) (subPath: %ld)\n", x1, y1, subPath); 667 if (path->CountPoints() > 0) { 668 // cannot MoveTo on a path that has already points! 669 path->CleanUp(); 670 path = new (nothrow) VectorPath(); 671 if (!path || !icon->Paths()->AddPath(path)) { 672 delete path; 673 return B_NO_MEMORY; 674 } 675 if (!shape->Paths()->AddPath(path)) 676 return B_NO_MEMORY; 677 } 678 if (!path->AddPoint(BPoint(x1, y1))) 679 return B_NO_MEMORY; 680 path->SetInOutConnected(path->CountPoints() - 1, false); 681 break; 682 683 case agg::path_cmd_line_to: 684 //printf("line to (%.2f, %.2f) (subPath: %ld)\n", x1, y1, subPath); 685 if (!path->AddPoint(BPoint(x1, y1))) 686 return B_NO_MEMORY; 687 path->SetInOutConnected(path->CountPoints() - 1, false); 688 break; 689 690 case agg::path_cmd_curve3: { 691 double x2 = 0, y2 = 0; 692 cmd = source.vertex(&x2, &y2); 693 //printf("curve3 (%.2f, %.2f)\n", x1, y1); 694 //printf(" (%.2f, %.2f)\n", x2, y2); 695 696 // convert to curve4 for easier editing 697 int32 start = path->CountPoints() - 1; 698 BPoint from; 699 path->GetPointAt(start, from); 700 701 double cx2 = (1.0/3.0) * from.x + (2.0/3.0) * x1; 702 double cy2 = (1.0/3.0) * from.y + (2.0/3.0) * y1; 703 double cx3 = (2.0/3.0) * x1 + (1.0/3.0) * x2; 704 double cy3 = (2.0/3.0) * y1 + (1.0/3.0) * y2; 705 706 path->SetPointOut(start, BPoint(cx2, cy2)); 707 708 if (!path->AddPoint(BPoint(x2, y2))) 709 return B_NO_MEMORY; 710 711 int32 end = path->CountPoints() - 1; 712 path->SetInOutConnected(end, false); 713 path->SetPointIn(end, BPoint(cx3, cy3)); 714 break; 715 } 716 717 case agg::path_cmd_curve4: { 718 double x2 = 0, y2 = 0; 719 double x3 = 0, y3 = 0; 720 cmd = source.vertex(&x2, &y2); 721 cmd = source.vertex(&x3, &y3); 722 723 if (!path->AddPoint(BPoint(x3, y3))) 724 return B_NO_MEMORY; 725 726 int32 start = path->CountPoints() - 2; 727 int32 end = path->CountPoints() - 1; 728 729 //printf("curve4 [%ld] (%.2f, %.2f) -> [%ld] (%.2f, %.2f) -> (%.2f, %.2f)\n", start, x1, y1, end, x2, y2, x3, y3); 730 731 path->SetInOutConnected(end, false); 732 path->SetPointOut(start, BPoint(x1, y1)); 733 path->SetPointIn(end, BPoint(x2, y2)); 734 break; 735 } 736 default: 737 //printf("unkown command\n"); 738 break; 739 } 740 cmd = source.vertex(&x1, &y1); 741 } 742 //path->PrintToStream(); 743 path->CleanUp(); 744 if (path->CountPoints() == 0) { 745 //printf("path with no points!\n"); 746 icon->Paths()->RemovePath(path); 747 shape->Paths()->RemovePath(path); 748 path->ReleaseReference(); 749 } 750 751 return B_OK; 752 } 753 754 755 // _AddShape 756 status_t 757 DocumentBuilder::_AddShape(path_attributes& attributes, bool outline, 758 const Transformable& transform, Icon* icon) 759 { 760 Shape* shape = new (nothrow) Shape(NULL); 761 if (!shape || !icon->Shapes()->AddShape(shape)) { 762 delete shape; 763 return B_NO_MEMORY; 764 } 765 766 if (AddPathsFromVertexSource(icon, shape, fPathStorage, attributes.index) < B_OK) 767 printf("failed to convert from vertex source\n"); 768 769 shape->multiply(attributes.transform); 770 shape->Multiply(transform); 771 772 StrokeTransformer* stroke = NULL; 773 if (outline) { 774 stroke = new (nothrow) StrokeTransformer(shape->VertexSource()); 775 776 if (stroke) { 777 stroke->width(attributes.stroke_width); 778 stroke->line_cap(attributes.line_cap); 779 stroke->line_join(attributes.line_join); 780 } 781 782 if (!shape->AddTransformer(stroke)) { 783 delete stroke; 784 stroke = NULL; 785 } 786 } else { 787 // if (attributes.even_odd_flag) 788 // shape->SetFillingRule(FILL_MODE_EVEN_ODD); 789 // else 790 // shape->SetFillingRule(FILL_MODE_NON_ZERO); 791 } 792 793 794 Gradient* gradient = NULL; 795 SVGGradient* g = NULL; 796 const char* url = outline ? attributes.stroke_url : attributes.fill_url; 797 if (url[0] != 0) { 798 g = _FindGradient(url); 799 if (g != NULL) 800 gradient = g->GetGradient(shape->Bounds()); 801 } 802 803 ObjectDeleter<Gradient> gradientDeleter(gradient); 804 805 rgb_color color; 806 807 BGradient::ColorStop* step; 808 if (gradient && (step = gradient->ColorAt(0))) { 809 color.red = step->color.red; 810 color.green = step->color.green; 811 color.blue = step->color.blue; 812 } else { 813 if (outline) { 814 color.red = attributes.stroke_color.r; 815 color.green = attributes.stroke_color.g; 816 color.blue = attributes.stroke_color.b; 817 color.alpha = (uint8)(attributes.stroke_color.a * attributes.opacity); 818 } else { 819 color.red = attributes.fill_color.r; 820 color.green = attributes.fill_color.g; 821 color.blue = attributes.fill_color.b; 822 color.alpha = (uint8)(attributes.fill_color.a * attributes.opacity); 823 } 824 } 825 826 Style* style = new (nothrow) Style(color); 827 if (!style || !icon->Styles()->AddStyle(style)) { 828 delete style; 829 return B_NO_MEMORY; 830 } 831 832 // NOTE: quick hack to freeze all transformations (only works because 833 // paths and styles are not used by multiple shapes!!) 834 // if (modifiers() & B_COMMAND_KEY) { 835 int32 pathCount = shape->Paths()->CountPaths(); 836 for (int32 i = 0; i < pathCount; i++) { 837 VectorPath* path = shape->Paths()->PathAtFast(i); 838 path->ApplyTransform(*shape); 839 } 840 if (gradient) 841 gradient->Multiply(*shape); 842 843 if (stroke) 844 stroke->width(stroke->width() * shape->scale()); 845 846 shape->Reset(); 847 // } 848 849 if (gradient) { 850 style->SetGradient(gradient); 851 style->SetName(g->ID()); 852 } 853 854 shape->SetStyle(style); 855 856 return B_OK; 857 } 858 859 } // namespace svg 860 } // namespace agg 861 862