2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Stephan Aßmus <superstippi@gmx.de>
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"
31 #include <agg_bounding_rect.h>
33 #include "AutoDeleter.h"
34 #include "GradientTransformable.h"
36 #include "PathContainer.h"
38 #include "ShapeContainer.h"
39 #include "StrokeTransformer.h"
41 #include "StyleContainer.h"
42 #include "SVGGradients.h"
43 #include "SVGImporter.h"
44 #include "VectorPath.h"
52 DocumentBuilder::DocumentBuilder()
54 fCurrentGradient(NULL
),
57 fViewBox(0.0, 0.0, -1.0, -1.0),
65 DocumentBuilder::remove_all()
67 fPathStorage
.remove_all();
68 fAttributesStorage
.remove_all();
69 fAttributesStack
.remove_all();
75 DocumentBuilder::begin_path()
78 unsigned idx
= fPathStorage
.start_new_path();
79 fAttributesStorage
.add(path_attributes(cur_attr(), idx
));
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
;
92 fAttributesStorage
[fAttributesStorage
.size() - 1] = attr
;
98 DocumentBuilder::move_to(double x
, double y
, bool rel
) // M, m
101 fPathStorage
.move_rel(x
, y
);
103 fPathStorage
.move_to(x
, y
);
108 DocumentBuilder::line_to(double x
, double y
, bool rel
) // L, l
111 fPathStorage
.line_rel(x
, y
);
113 fPathStorage
.line_to(x
, y
);
118 DocumentBuilder::hline_to(double x
, bool rel
) // H, h
121 fPathStorage
.hline_rel(x
);
123 fPathStorage
.hline_to(x
);
128 DocumentBuilder::vline_to(double y
, bool rel
) // V, v
131 fPathStorage
.vline_rel(y
);
133 fPathStorage
.vline_to(y
);
138 DocumentBuilder::curve3(double x1
, double y1
, // Q, q
139 double x
, double y
, bool rel
)
142 fPathStorage
.curve3_rel(x1
, y1
, x
, y
);
144 fPathStorage
.curve3(x1
, y1
, x
, y
);
149 DocumentBuilder::curve3(double x
, double y
, bool rel
) // T, t
152 fPathStorage
.curve3_rel(x
, y
);
154 fPathStorage
.curve3(x
, y
);
159 DocumentBuilder::curve4(double x1
, double y1
, // C, c
160 double x2
, double y2
,
161 double x
, double y
, bool rel
)
164 fPathStorage
.curve4_rel(x1
, y1
, x2
, y2
, x
, y
);
166 fPathStorage
.curve4(x1
, y1
, x2
, y2
, x
, y
);
172 DocumentBuilder::curve4(double x2
, double y2
, // S, s
173 double x
, double y
, bool rel
)
176 fPathStorage
.curve4_rel(x2
, y2
, x
, y
);
178 fPathStorage
.curve4(x2
, y2
, x
, y
);
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
;
190 fPathStorage
.arc_rel(rx
, ry
, angle
, large_arc_flag
, sweep_flag
, x
, y
);
192 fPathStorage
.arc_to(rx
, ry
, angle
, large_arc_flag
, sweep_flag
, x
, y
);
198 DocumentBuilder::close_subpath()
200 fPathStorage
.end_poly(path_flags_close
);
205 DocumentBuilder::SetTitle(const char* title
)
212 DocumentBuilder::SetDimensions(uint32 width
, uint32 height
, BRect viewBox
)
221 DocumentBuilder::cur_attr()
223 if (fAttributesStack
.size() == 0) {
224 throw exception("cur_attr: Attribute stack is empty");
226 return fAttributesStack
[fAttributesStack
.size() - 1];
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());
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();
251 DocumentBuilder::fill(const rgba8
& f
)
253 path_attributes
& attr
= cur_attr();
255 attr
.fill_flag
= true;
260 DocumentBuilder::stroke(const rgba8
& s
)
262 path_attributes
& attr
= cur_attr();
263 attr
.stroke_color
= s
;
264 attr
.stroke_flag
= true;
269 DocumentBuilder::even_odd(bool flag
)
271 cur_attr().even_odd_flag
= flag
;
276 DocumentBuilder::stroke_width(double w
)
278 path_attributes
& attr
= cur_attr();
279 attr
.stroke_width
= w
;
280 attr
.stroke_flag
= true;
285 DocumentBuilder::fill_none()
287 cur_attr().fill_flag
= false;
292 DocumentBuilder::fill_url(const char* url
)
294 sprintf(cur_attr().fill_url
, "%s", url
);
299 DocumentBuilder::stroke_none()
301 cur_attr().stroke_flag
= false;
306 DocumentBuilder::stroke_url(const char* url
)
308 sprintf(cur_attr().stroke_url
, "%s", url
);
313 DocumentBuilder::opacity(double op
)
315 cur_attr().opacity
*= op
;
316 //printf("opacity: %.1f\n", cur_attr().opacity);
321 DocumentBuilder::fill_opacity(double op
)
323 cur_attr().fill_color
.opacity(op
);
324 // cur_attr().opacity *= op;
329 DocumentBuilder::stroke_opacity(double op
)
331 cur_attr().stroke_color
.opacity(op
);
332 // cur_attr().opacity *= op;
337 DocumentBuilder::line_join(line_join_e join
)
339 cur_attr().line_join
= join
;
344 DocumentBuilder::line_cap(line_cap_e cap
)
346 cur_attr().line_cap
= cap
;
351 DocumentBuilder::miter_limit(double ml
)
353 cur_attr().miter_limit
= ml
;
358 DocumentBuilder::transform()
360 return cur_attr().transform
;
365 DocumentBuilder::parse_path(PathTokenizer
& tok
)
370 char cmd
= tok
.last_command();
374 arg
[0] = tok
.last_number();
375 arg
[1] = tok
.next(cmd
);
377 move_to(arg
[0], arg
[1], cmd
== 'm');
379 line_to(arg
[0], arg
[1], lastCmd
== 'm');
383 arg
[0] = tok
.last_number();
384 arg
[1] = tok
.next(cmd
);
385 line_to(arg
[0], arg
[1], cmd
== 'l');
389 vline_to(tok
.last_number(), cmd
== 'v');
393 hline_to(tok
.last_number(), cmd
== 'h');
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');
405 arg
[0] = tok
.last_number();
406 arg
[1] = tok
.next(cmd
);
407 curve3(arg
[0], arg
[1], cmd
== 't');
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');
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');
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');
449 sprintf(buf
, "parse_path: Invalid path command '%c'", cmd
);
450 throw exception(buf
);
461 DocumentBuilder::GetIcon(Icon
* icon
, SVGImporter
* importer
,
462 const char* fallbackName
)
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
);
482 if (fViewBox
.IsValid()) {
484 printf("view box: ");
485 bounds
.PrintToStream();
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());
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();
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();
548 DocumentBuilder::StartGradient(bool radial
)
550 if (fCurrentGradient
) {
551 fprintf(stderr
, "DocumentBuilder::StartGradient() - ERROR: "
552 "previous gradient (%s) not finished!\n",
553 fCurrentGradient
->ID());
557 fCurrentGradient
= new SVGRadialGradient();
559 fCurrentGradient
= new SVGLinearGradient();
561 _AddGradient(fCurrentGradient
);
566 DocumentBuilder::EndGradient()
568 if (fCurrentGradient
) {
569 // fCurrentGradient->PrintToStream();
571 fprintf(stderr
, "DocumentBuilder::EndGradient() - "
572 "ERROR: no gradient started!\n");
574 fCurrentGradient
= NULL
;
581 DocumentBuilder::_AddGradient(SVGGradient
* gradient
)
584 fGradients
.AddItem((void*)gradient
);
590 DocumentBuilder::_GradientAt(int32 index
) const
592 return (SVGGradient
*)fGradients
.ItemAt(index
);
597 DocumentBuilder::_FindGradient(const char* name
) const
599 for (int32 i
= 0; SVGGradient
* g
= _GradientAt(i
); i
++) {
600 if (strcmp(g
->ID(), name
) == 0)
608 template<class VertexSource
>
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
)) {
622 if (!shape
->Paths()->AddPath(path
))
625 source
.rewind(index
);
626 double x1
= 0, y1
= 0;
627 unsigned cmd
= source
.vertex(&x1
, &y1
);
628 bool keepGoing
= true;
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);
638 //printf(" not end polygon\n");
641 if (agg::is_stop(cmd
)) {
642 //printf(" stop = true\n");
646 //printf(" new subpath\n");
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
)) {
659 if (!shape
->Paths()->AddPath(path
))
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!
670 path
= new (nothrow
) VectorPath();
671 if (!path
|| !icon
->Paths()->AddPath(path
)) {
675 if (!shape
->Paths()->AddPath(path
))
678 if (!path
->AddPoint(BPoint(x1
, y1
)))
680 path
->SetInOutConnected(path
->CountPoints() - 1, false);
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
)))
687 path
->SetInOutConnected(path
->CountPoints() - 1, false);
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;
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
)))
711 int32 end
= path
->CountPoints() - 1;
712 path
->SetInOutConnected(end
, false);
713 path
->SetPointIn(end
, BPoint(cx3
, cy3
));
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
)))
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
));
737 //printf("unkown command\n");
740 cmd
= source
.vertex(&x1
, &y1
);
742 //path->PrintToStream();
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();
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
)) {
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
;
774 stroke
= new (nothrow
) StrokeTransformer(shape
->VertexSource());
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
)) {
787 // if (attributes.even_odd_flag)
788 // shape->SetFillingRule(FILL_MODE_EVEN_ODD);
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
;
798 g
= _FindGradient(url
);
800 gradient
= g
->GetGradient(shape
->Bounds());
803 ObjectDeleter
<Gradient
> gradientDeleter(gradient
);
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
;
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
);
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
)) {
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
);
841 gradient
->Multiply(*shape
);
844 stroke
->width(stroke
->width() * shape
->scale());
850 style
->SetGradient(gradient
);
851 style
->SetName(g
->ID());
854 shape
->SetStyle(style
);