Removal of non-Haiku target platform logic from build system (part 1.)
[haiku.git] / src / apps / icon-o-matic / import_export / svg / DocumentBuilder.cpp
blob39fd6d5aabd902457c30c1d4c9377609d6c67753
1 /*
2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
9 //----------------------------------------------------------------------------
10 // Anti-Grain Geometry - Version 2.2
11 // Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
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.
18 //----------------------------------------------------------------------------
19 // Contact: mcseem@antigrain.com
20 // mcseemagg@yahoo.com
21 // http://www.antigrain.com
22 //----------------------------------------------------------------------------
24 #include "DocumentBuilder.h"
26 #include <new>
27 #include <stdio.h>
29 #include <Bitmap.h>
31 #include <agg_bounding_rect.h>
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"
46 using std::nothrow;
48 namespace agg {
49 namespace svg {
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("")
63 // remove_all
64 void
65 DocumentBuilder::remove_all()
67 fPathStorage.remove_all();
68 fAttributesStorage.remove_all();
69 fAttributesStack.remove_all();
70 fTransform.reset();
73 // begin_path
74 void
75 DocumentBuilder::begin_path()
77 push_attr();
78 unsigned idx = fPathStorage.start_new_path();
79 fAttributesStorage.add(path_attributes(cur_attr(), idx));
82 // end_path
83 void
84 DocumentBuilder::end_path()
86 if (fAttributesStorage.size() == 0) {
87 throw exception("end_path: The path was not begun");
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();
96 // move_to
97 void
98 DocumentBuilder::move_to(double x, double y, bool rel) // M, m
100 if (rel)
101 fPathStorage.move_rel(x, y);
102 else
103 fPathStorage.move_to(x, y);
106 // line_to
107 void
108 DocumentBuilder::line_to(double x, double y, bool rel) // L, l
110 if (rel)
111 fPathStorage.line_rel(x, y);
112 else
113 fPathStorage.line_to(x, y);
116 // hline_to
117 void
118 DocumentBuilder::hline_to(double x, bool rel) // H, h
120 if (rel)
121 fPathStorage.hline_rel(x);
122 else
123 fPathStorage.hline_to(x);
126 // vline_to
127 void
128 DocumentBuilder::vline_to(double y, bool rel) // V, v
130 if (rel)
131 fPathStorage.vline_rel(y);
132 else
133 fPathStorage.vline_to(y);
136 // curve3
137 void
138 DocumentBuilder::curve3(double x1, double y1, // Q, q
139 double x, double y, bool rel)
141 if (rel)
142 fPathStorage.curve3_rel(x1, y1, x, y);
143 else
144 fPathStorage.curve3(x1, y1, x, y);
147 // curve3
148 void
149 DocumentBuilder::curve3(double x, double y, bool rel) // T, t
151 if (rel)
152 fPathStorage.curve3_rel(x, y);
153 else
154 fPathStorage.curve3(x, y);
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)
163 if (rel) {
164 fPathStorage.curve4_rel(x1, y1, x2, y2, x, y);
165 } else {
166 fPathStorage.curve4(x1, y1, x2, y2, x, y);
170 // curve4
171 void
172 DocumentBuilder::curve4(double x2, double y2, // S, s
173 double x, double y, bool rel)
175 if (rel) {
176 fPathStorage.curve4_rel(x2, y2, x, y);
177 } else {
178 fPathStorage.curve4(x2, y2, x, y);
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)
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);
196 // close_subpath
197 void
198 DocumentBuilder::close_subpath()
200 fPathStorage.end_poly(path_flags_close);
203 // SetTitle
204 void
205 DocumentBuilder::SetTitle(const char* title)
207 fTitle = title;
210 // SetDimensions
211 void
212 DocumentBuilder::SetDimensions(uint32 width, uint32 height, BRect viewBox)
214 fWidth = width;
215 fHeight = height;
216 fViewBox = viewBox;
219 // cur_attr
220 path_attributes&
221 DocumentBuilder::cur_attr()
223 if (fAttributesStack.size() == 0) {
224 throw exception("cur_attr: Attribute stack is empty");
226 return fAttributesStack[fAttributesStack.size() - 1];
229 // push_attr
230 void
231 DocumentBuilder::push_attr()
233 //printf("DocumentBuilder::push_attr() (size: %d)\n", fAttributesStack.size());
234 fAttributesStack.add(fAttributesStack.size() ? fAttributesStack[fAttributesStack.size() - 1]
235 : path_attributes());
238 // pop_attr
239 void
240 DocumentBuilder::pop_attr()
242 //printf("DocumentBuilder::pop_attr() (size: %d)\n", fAttributesStack.size());
243 if (fAttributesStack.size() == 0) {
244 throw exception("pop_attr: Attribute stack is empty");
246 fAttributesStack.remove_last();
249 // fill
250 void
251 DocumentBuilder::fill(const rgba8& f)
253 path_attributes& attr = cur_attr();
254 attr.fill_color = f;
255 attr.fill_flag = true;
258 // stroke
259 void
260 DocumentBuilder::stroke(const rgba8& s)
262 path_attributes& attr = cur_attr();
263 attr.stroke_color = s;
264 attr.stroke_flag = true;
267 // even_odd
268 void
269 DocumentBuilder::even_odd(bool flag)
271 cur_attr().even_odd_flag = flag;
274 // stroke_width
275 void
276 DocumentBuilder::stroke_width(double w)
278 path_attributes& attr = cur_attr();
279 attr.stroke_width = w;
280 attr.stroke_flag = true;
283 // fill_none
284 void
285 DocumentBuilder::fill_none()
287 cur_attr().fill_flag = false;
290 // fill_url
291 void
292 DocumentBuilder::fill_url(const char* url)
294 sprintf(cur_attr().fill_url, "%s", url);
297 // stroke_none
298 void
299 DocumentBuilder::stroke_none()
301 cur_attr().stroke_flag = false;
304 // stroke_url
305 void
306 DocumentBuilder::stroke_url(const char* url)
308 sprintf(cur_attr().stroke_url, "%s", url);
311 // opacity
312 void
313 DocumentBuilder::opacity(double op)
315 cur_attr().opacity *= op;
316 //printf("opacity: %.1f\n", cur_attr().opacity);
319 // fill_opacity
320 void
321 DocumentBuilder::fill_opacity(double op)
323 cur_attr().fill_color.opacity(op);
324 // cur_attr().opacity *= op;
327 // stroke_opacity
328 void
329 DocumentBuilder::stroke_opacity(double op)
331 cur_attr().stroke_color.opacity(op);
332 // cur_attr().opacity *= op;
335 // line_join
336 void
337 DocumentBuilder::line_join(line_join_e join)
339 cur_attr().line_join = join;
342 // line_cap
343 void
344 DocumentBuilder::line_cap(line_cap_e cap)
346 cur_attr().line_cap = cap;
349 // miter_limit
350 void
351 DocumentBuilder::miter_limit(double ml)
353 cur_attr().miter_limit = ml;
356 // transform
357 trans_affine&
358 DocumentBuilder::transform()
360 return cur_attr().transform;
363 // parse_path
364 void
365 DocumentBuilder::parse_path(PathTokenizer& tok)
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;
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;
388 case 'V': case 'v':
389 vline_to(tok.last_number(), cmd == 'v');
390 break;
392 case 'H': case 'h':
393 hline_to(tok.last_number(), cmd == 'h');
394 break;
396 case 'Q': case 'q':
397 arg[0] = tok.last_number();
398 for(i = 1; i < 4; i++) {
399 arg[i] = tok.next(cmd);
401 curve3(arg[0], arg[1], arg[2], arg[3], cmd == 'q');
402 break;
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;
410 case 'C': case 'c':
411 arg[0] = tok.last_number();
412 for(i = 1; i < 6; i++) {
413 arg[i] = tok.next(cmd);
415 curve4(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], cmd == 'c');
416 break;
418 case 'S': case 's':
419 arg[0] = tok.last_number();
420 for(i = 1; i < 4; i++) {
421 arg[i] = tok.next(cmd);
423 curve4(arg[0], arg[1], arg[2], arg[3], cmd == 's');
424 break;
426 case 'A': case 'a': {
427 arg[0] = tok.last_number();
428 for(i = 1; i < 3; i++) {
429 arg[i] = tok.next(cmd);
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);
436 elliptical_arc(arg[0], arg[1], arg[2],
437 large_arc_flag, sweep_flag,
438 arg[3], arg[4], cmd == 'a');
439 break;
442 case 'Z': case 'z':
443 close_subpath();
444 break;
446 default:
448 char buf[100];
449 sprintf(buf, "parse_path: Invalid path command '%c'", cmd);
450 throw exception(buf);
453 lastCmd = cmd;
457 // #pragma mark -
459 // GetIcon
460 status_t
461 DocumentBuilder::GetIcon(Icon* icon, SVGImporter* importer,
462 const char* fallbackName)
464 double xMin;
465 double yMin;
466 double xMax;
467 double yMax;
469 int32 pathCount = fAttributesStorage.size();
471 agg::conv_transform<agg::path_storage> transformedPaths(
472 fPathStorage, fTransform);
473 agg::bounding_rect(transformedPaths, *this, 0, pathCount,
474 &xMin, &yMin, &xMax, &yMax);
476 xMin = floor(xMin);
477 yMin = floor(yMin);
478 xMax = ceil(xMax);
479 yMax = ceil(yMax);
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();
492 BRect boundingBox(xMin, yMin, xMax, yMax);
494 if (!bounds.IsValid() || !boundingBox.Intersects(bounds)) {
495 bounds = boundingBox;
496 printf("using bounding box: ");
497 bounds.PrintToStream();
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);
504 Transformable transform;
505 transform.TranslateBy(BPoint(-bounds.left, -bounds.top));
506 transform.ScaleBy(B_ORIGIN, scale, scale);
508 // if (fTitle.CountChars() > 0)
509 // icon->SetName(fTitle.String());
510 // else
511 // icon->SetName(fallbackName);
513 for (int32 i = 0; i < pathCount; i++) {
515 path_attributes& attributes = fAttributesStorage[i];
517 if (attributes.fill_flag)
518 _AddShape(attributes, false, transform, icon);
520 if (attributes.stroke_flag)
521 _AddShape(attributes, true, transform, icon);
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;
543 return B_OK;
546 // StartGradient
547 void
548 DocumentBuilder::StartGradient(bool radial)
550 if (fCurrentGradient) {
551 fprintf(stderr, "DocumentBuilder::StartGradient() - ERROR: "
552 "previous gradient (%s) not finished!\n",
553 fCurrentGradient->ID());
556 if (radial)
557 fCurrentGradient = new SVGRadialGradient();
558 else
559 fCurrentGradient = new SVGLinearGradient();
561 _AddGradient(fCurrentGradient);
564 // EndGradient
565 void
566 DocumentBuilder::EndGradient()
568 if (fCurrentGradient) {
569 // fCurrentGradient->PrintToStream();
570 } else {
571 fprintf(stderr, "DocumentBuilder::EndGradient() - "
572 "ERROR: no gradient started!\n");
574 fCurrentGradient = NULL;
577 // #pragma mark -
579 // _AddGradient
580 void
581 DocumentBuilder::_AddGradient(SVGGradient* gradient)
583 if (gradient) {
584 fGradients.AddItem((void*)gradient);
588 // _GradientAt
589 SVGGradient*
590 DocumentBuilder::_GradientAt(int32 index) const
592 return (SVGGradient*)fGradients.ItemAt(index);
595 // _FindGradient
596 SVGGradient*
597 DocumentBuilder::_FindGradient(const char* name) const
599 for (int32 i = 0; SVGGradient* g = _GradientAt(i); i++) {
600 if (strcmp(g->ID(), name) == 0)
601 return g;
603 return NULL;
607 // AddVertexSource
608 template<class VertexSource>
609 status_t
610 AddPathsFromVertexSource(Icon* icon, Shape* shape,
611 VertexSource& source, int32 index)
613 //printf("AddPathsFromVertexSource(pathID = %ld)\n", index);
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;
622 if (!shape->Paths()->AddPath(path))
623 return B_NO_MEMORY;
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");
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();
654 path = new (nothrow) VectorPath();
655 if (!path || !icon->Paths()->AddPath(path)) {
656 delete path;
657 return B_NO_MEMORY;
659 if (!shape->Paths()->AddPath(path))
660 return B_NO_MEMORY;
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;
675 if (!shape->Paths()->AddPath(path))
676 return B_NO_MEMORY;
678 if (!path->AddPoint(BPoint(x1, y1)))
679 return B_NO_MEMORY;
680 path->SetInOutConnected(path->CountPoints() - 1, false);
681 break;
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;
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);
696 // convert to curve4 for easier editing
697 int32 start = path->CountPoints() - 1;
698 BPoint from;
699 path->GetPointAt(start, from);
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;
706 path->SetPointOut(start, BPoint(cx2, cy2));
708 if (!path->AddPoint(BPoint(x2, y2)))
709 return B_NO_MEMORY;
711 int32 end = path->CountPoints() - 1;
712 path->SetInOutConnected(end, false);
713 path->SetPointIn(end, BPoint(cx3, cy3));
714 break;
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);
723 if (!path->AddPoint(BPoint(x3, y3)))
724 return B_NO_MEMORY;
726 int32 start = path->CountPoints() - 2;
727 int32 end = path->CountPoints() - 1;
729 //printf("curve4 [%ld] (%.2f, %.2f) -> [%ld] (%.2f, %.2f) -> (%.2f, %.2f)\n", start, x1, y1, end, x2, y2, x3, y3);
731 path->SetInOutConnected(end, false);
732 path->SetPointOut(start, BPoint(x1, y1));
733 path->SetPointIn(end, BPoint(x2, y2));
734 break;
736 default:
737 //printf("unkown command\n");
738 break;
740 cmd = source.vertex(&x1, &y1);
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();
751 return B_OK;
755 // _AddShape
756 status_t
757 DocumentBuilder::_AddShape(path_attributes& attributes, bool outline,
758 const Transformable& transform, Icon* icon)
760 Shape* shape = new (nothrow) Shape(NULL);
761 if (!shape || !icon->Shapes()->AddShape(shape)) {
762 delete shape;
763 return B_NO_MEMORY;
766 if (AddPathsFromVertexSource(icon, shape, fPathStorage, attributes.index) < B_OK)
767 printf("failed to convert from vertex source\n");
769 shape->multiply(attributes.transform);
770 shape->Multiply(transform);
772 StrokeTransformer* stroke = NULL;
773 if (outline) {
774 stroke = new (nothrow) StrokeTransformer(shape->VertexSource());
776 if (stroke) {
777 stroke->width(attributes.stroke_width);
778 stroke->line_cap(attributes.line_cap);
779 stroke->line_join(attributes.line_join);
782 if (!shape->AddTransformer(stroke)) {
783 delete stroke;
784 stroke = NULL;
786 } else {
787 // if (attributes.even_odd_flag)
788 // shape->SetFillingRule(FILL_MODE_EVEN_ODD);
789 // else
790 // shape->SetFillingRule(FILL_MODE_NON_ZERO);
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());
803 ObjectDeleter<Gradient> gradientDeleter(gradient);
805 rgb_color color;
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);
826 Style* style = new (nothrow) Style(color);
827 if (!style || !icon->Styles()->AddStyle(style)) {
828 delete style;
829 return B_NO_MEMORY;
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);
840 if (gradient)
841 gradient->Multiply(*shape);
843 if (stroke)
844 stroke->width(stroke->width() * shape->scale());
846 shape->Reset();
847 // }
849 if (gradient) {
850 style->SetGradient(gradient);
851 style->SetName(g->ID());
854 shape->SetStyle(style);
856 return B_OK;
859 } // namespace svg
860 } // namespace agg