xref: /haiku/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
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->Release();
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, y1;
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->Release();
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, y2;
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, y2;
719 				double x3, y3;
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->Release();
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 	const char* url = outline ? attributes.stroke_url : attributes.fill_url;
796 	if (url[0] != 0) {
797 		if (SVGGradient* g = _FindGradient(url)) {
798 			gradient = g->GetGradient(shape->Bounds());
799 		}
800 	}
801 
802 	ObjectDeleter<Gradient> gradientDeleter(gradient);
803 
804 	rgb_color color;
805 
806 	BGradient::ColorStop* step;
807 	if (gradient && (step = gradient->ColorAt(0))) {
808 		color.red		= step->color.red;
809 		color.green		= step->color.green;
810 		color.blue		= step->color.blue;
811 	} else {
812 		if (outline) {
813 			color.red	= attributes.stroke_color.r;
814 			color.green	= attributes.stroke_color.g;
815 			color.blue	= attributes.stroke_color.b;
816 			color.alpha = (uint8)(attributes.stroke_color.a * attributes.opacity);
817 		} else {
818 			color.red	= attributes.fill_color.r;
819 			color.green	= attributes.fill_color.g;
820 			color.blue	= attributes.fill_color.b;
821 			color.alpha = (uint8)(attributes.fill_color.a * attributes.opacity);
822 		}
823 	}
824 
825 	Style* style = new (nothrow) Style(color);
826 	if (!style || !icon->Styles()->AddStyle(style)) {
827 		delete style;
828 		return B_NO_MEMORY;
829 	}
830 
831 	// NOTE: quick hack to freeze all transformations (only works because
832 	// paths and styles are not used by multiple shapes!!)
833 //	if (modifiers() & B_COMMAND_KEY) {
834 		int32 pathCount = shape->Paths()->CountPaths();
835 		for (int32 i = 0; i < pathCount; i++) {
836 			VectorPath* path = shape->Paths()->PathAtFast(i);
837 			path->ApplyTransform(*shape);
838 		}
839 		if (gradient)
840 			gradient->Multiply(*shape);
841 
842 		if (stroke)
843 			stroke->width(stroke->width() * shape->scale());
844 
845 		shape->Reset();
846 //	}
847 
848 	if (gradient)
849 		style->SetGradient(gradient);
850 
851 	shape->SetStyle(style);
852 
853 	return B_OK;
854 }
855 
856 } // namespace svg
857 } // namespace agg
858 
859