Icon-O-Matic: replace SVG parser with nanosvg

The parser was based on an old example from agg. NanoSVG is originally
based on the same code, but has lots of bugfixes. So it makes sense
to use it.

Nanosvg revision 25241c5a8f8451d41ab1b02ab2d865b01600d949

Fixes #5955, #8586, #13021.

Change-Id: I38ff9aa4e1d403c41979ebe42f7b45d4500a870c
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2661
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Adrien Destugues
2020-05-22 14:08:17 +00:00
committed by waddlesplash
parent c419919252
commit 2a3b385efb
14 changed files with 3168 additions and 2692 deletions
-3
View File
@@ -233,11 +233,8 @@ Application Icon-O-Matic :
# import_export/svg
DocumentBuilder.cpp
PathTokenizer.cpp
SVGExporter.cpp
SVGGradients.cpp
SVGImporter.cpp
SVGParser.cpp
# shape
PathManipulator.cpp
@@ -6,21 +6,8 @@
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#define NANOSVG_IMPLEMENTATION
#include "DocumentBuilder.h"
#include <new>
@@ -28,8 +15,6 @@
#include <Bitmap.h>
#include <agg_bounding_rect.h>
#include "AutoDeleter.h"
#include "GradientTransformable.h"
#include "Icon.h"
@@ -39,167 +24,25 @@
#include "StrokeTransformer.h"
#include "Style.h"
#include "StyleContainer.h"
#include "SVGGradients.h"
#include "SVGImporter.h"
#include "VectorPath.h"
#include <agg_math_stroke.h>
#include <agg_trans_affine.h>
using std::nothrow;
namespace agg {
namespace svg {
// constructor
DocumentBuilder::DocumentBuilder()
: fGradients(20),
fCurrentGradient(NULL),
fWidth(0),
DocumentBuilder::DocumentBuilder(NSVGimage* source)
: fWidth(0),
fHeight(0),
fViewBox(0.0, 0.0, -1.0, -1.0),
fTitle("")
fTitle(""),
fSource(source)
{
}
// remove_all
void
DocumentBuilder::remove_all()
{
fPathStorage.remove_all();
fAttributesStorage.remove_all();
fAttributesStack.remove_all();
fTransform.reset();
}
// begin_path
void
DocumentBuilder::begin_path()
{
push_attr();
unsigned idx = fPathStorage.start_new_path();
fAttributesStorage.add(path_attributes(cur_attr(), idx));
}
// end_path
void
DocumentBuilder::end_path()
{
if (fAttributesStorage.size() == 0) {
throw exception("end_path: The path was not begun");
}
path_attributes attr = cur_attr();
unsigned idx = fAttributesStorage[fAttributesStorage.size() - 1].index;
attr.index = idx;
fAttributesStorage[fAttributesStorage.size() - 1] = attr;
pop_attr();
}
// move_to
void
DocumentBuilder::move_to(double x, double y, bool rel) // M, m
{
if (rel)
fPathStorage.move_rel(x, y);
else
fPathStorage.move_to(x, y);
}
// line_to
void
DocumentBuilder::line_to(double x, double y, bool rel) // L, l
{
if (rel)
fPathStorage.line_rel(x, y);
else
fPathStorage.line_to(x, y);
}
// hline_to
void
DocumentBuilder::hline_to(double x, bool rel) // H, h
{
if (rel)
fPathStorage.hline_rel(x);
else
fPathStorage.hline_to(x);
}
// vline_to
void
DocumentBuilder::vline_to(double y, bool rel) // V, v
{
if (rel)
fPathStorage.vline_rel(y);
else
fPathStorage.vline_to(y);
}
// curve3
void
DocumentBuilder::curve3(double x1, double y1, // Q, q
double x, double y, bool rel)
{
if (rel)
fPathStorage.curve3_rel(x1, y1, x, y);
else
fPathStorage.curve3(x1, y1, x, y);
}
// curve3
void
DocumentBuilder::curve3(double x, double y, bool rel) // T, t
{
if (rel)
fPathStorage.curve3_rel(x, y);
else
fPathStorage.curve3(x, y);
}
// curve4
void
DocumentBuilder::curve4(double x1, double y1, // C, c
double x2, double y2,
double x, double y, bool rel)
{
if (rel) {
fPathStorage.curve4_rel(x1, y1, x2, y2, x, y);
} else {
fPathStorage.curve4(x1, y1, x2, y2, x, y);
}
}
// curve4
void
DocumentBuilder::curve4(double x2, double y2, // S, s
double x, double y, bool rel)
{
if (rel) {
fPathStorage.curve4_rel(x2, y2, x, y);
} else {
fPathStorage.curve4(x2, y2, x, y);
}
}
// elliptical_arc
void
DocumentBuilder::elliptical_arc(double rx, double ry, double angle,
bool large_arc_flag, bool sweep_flag,
double x, double y, bool rel)
{
angle = angle / 180.0 * pi;
if (rel) {
fPathStorage.arc_rel(rx, ry, angle, large_arc_flag, sweep_flag, x, y);
} else {
fPathStorage.arc_to(rx, ry, angle, large_arc_flag, sweep_flag, x, y);
}
}
// close_subpath
void
DocumentBuilder::close_subpath()
{
fPathStorage.end_poly(path_flags_close);
}
// SetTitle
void
DocumentBuilder::SetTitle(const char* title)
@@ -216,244 +59,6 @@ DocumentBuilder::SetDimensions(uint32 width, uint32 height, BRect viewBox)
fViewBox = viewBox;
}
// cur_attr
path_attributes&
DocumentBuilder::cur_attr()
{
if (fAttributesStack.size() == 0) {
throw exception("cur_attr: Attribute stack is empty");
}
return fAttributesStack[fAttributesStack.size() - 1];
}
// push_attr
void
DocumentBuilder::push_attr()
{
//printf("DocumentBuilder::push_attr() (size: %d)\n", fAttributesStack.size());
fAttributesStack.add(fAttributesStack.size() ? fAttributesStack[fAttributesStack.size() - 1]
: path_attributes());
}
// pop_attr
void
DocumentBuilder::pop_attr()
{
//printf("DocumentBuilder::pop_attr() (size: %d)\n", fAttributesStack.size());
if (fAttributesStack.size() == 0) {
throw exception("pop_attr: Attribute stack is empty");
}
fAttributesStack.remove_last();
}
// fill
void
DocumentBuilder::fill(const rgba8& f)
{
path_attributes& attr = cur_attr();
attr.fill_color = f;
attr.fill_flag = true;
}
// stroke
void
DocumentBuilder::stroke(const rgba8& s)
{
path_attributes& attr = cur_attr();
attr.stroke_color = s;
attr.stroke_flag = true;
}
// even_odd
void
DocumentBuilder::even_odd(bool flag)
{
cur_attr().even_odd_flag = flag;
}
// stroke_width
void
DocumentBuilder::stroke_width(double w)
{
path_attributes& attr = cur_attr();
attr.stroke_width = w;
attr.stroke_flag = true;
}
// fill_none
void
DocumentBuilder::fill_none()
{
cur_attr().fill_flag = false;
}
// fill_url
void
DocumentBuilder::fill_url(const char* url)
{
sprintf(cur_attr().fill_url, "%s", url);
}
// stroke_none
void
DocumentBuilder::stroke_none()
{
cur_attr().stroke_flag = false;
}
// stroke_url
void
DocumentBuilder::stroke_url(const char* url)
{
sprintf(cur_attr().stroke_url, "%s", url);
}
// opacity
void
DocumentBuilder::opacity(double op)
{
cur_attr().opacity *= op;
//printf("opacity: %.1f\n", cur_attr().opacity);
}
// fill_opacity
void
DocumentBuilder::fill_opacity(double op)
{
cur_attr().fill_color.opacity(op);
// cur_attr().opacity *= op;
}
// stroke_opacity
void
DocumentBuilder::stroke_opacity(double op)
{
cur_attr().stroke_color.opacity(op);
// cur_attr().opacity *= op;
}
// line_join
void
DocumentBuilder::line_join(line_join_e join)
{
cur_attr().line_join = join;
}
// line_cap
void
DocumentBuilder::line_cap(line_cap_e cap)
{
cur_attr().line_cap = cap;
}
// miter_limit
void
DocumentBuilder::miter_limit(double ml)
{
cur_attr().miter_limit = ml;
}
// transform
trans_affine&
DocumentBuilder::transform()
{
return cur_attr().transform;
}
// parse_path
void
DocumentBuilder::parse_path(PathTokenizer& tok)
{
char lastCmd = 0;
while(tok.next()) {
double arg[10];
char cmd = tok.last_command();
unsigned i;
switch(cmd) {
case 'M': case 'm':
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
if (lastCmd != cmd)
move_to(arg[0], arg[1], cmd == 'm');
else
line_to(arg[0], arg[1], lastCmd == 'm');
break;
case 'L': case 'l':
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
line_to(arg[0], arg[1], cmd == 'l');
break;
case 'V': case 'v':
vline_to(tok.last_number(), cmd == 'v');
break;
case 'H': case 'h':
hline_to(tok.last_number(), cmd == 'h');
break;
case 'Q': case 'q':
arg[0] = tok.last_number();
for(i = 1; i < 4; i++) {
arg[i] = tok.next(cmd);
}
curve3(arg[0], arg[1], arg[2], arg[3], cmd == 'q');
break;
case 'T': case 't':
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
curve3(arg[0], arg[1], cmd == 't');
break;
case 'C': case 'c':
arg[0] = tok.last_number();
for(i = 1; i < 6; i++) {
arg[i] = tok.next(cmd);
}
curve4(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], cmd == 'c');
break;
case 'S': case 's':
arg[0] = tok.last_number();
for(i = 1; i < 4; i++) {
arg[i] = tok.next(cmd);
}
curve4(arg[0], arg[1], arg[2], arg[3], cmd == 's');
break;
case 'A': case 'a': {
arg[0] = tok.last_number();
for(i = 1; i < 3; i++) {
arg[i] = tok.next(cmd);
}
bool large_arc_flag = (bool)tok.next(cmd);
bool sweep_flag = (bool)tok.next(cmd);
for(i = 3; i < 5; i++) {
arg[i] = tok.next(cmd);
}
elliptical_arc(arg[0], arg[1], arg[2],
large_arc_flag, sweep_flag,
arg[3], arg[4], cmd == 'a');
break;
}
case 'Z': case 'z':
close_subpath();
break;
default:
{
char buf[100];
sprintf(buf, "parse_path: Invalid path command '%c'", cmd);
throw exception(buf);
}
}
lastCmd = cmd;
}
}
// #pragma mark -
// GetIcon
@@ -461,22 +66,10 @@ status_t
DocumentBuilder::GetIcon(Icon* icon, SVGImporter* importer,
const char* fallbackName)
{
double xMin;
double yMin;
double xMax;
double yMax;
int32 pathCount = fAttributesStorage.size();
agg::conv_transform<agg::path_storage> transformedPaths(
fPathStorage, fTransform);
agg::bounding_rect(transformedPaths, *this, 0, pathCount,
&xMin, &yMin, &xMax, &yMax);
xMin = floor(xMin);
yMin = floor(yMin);
xMax = ceil(xMax);
yMax = ceil(yMax);
double xMin = 0;
double yMin = 0;
double xMax = ceil(fSource->width);
double yMax = ceil(fSource->height);
BRect bounds;
if (fViewBox.IsValid()) {
@@ -497,7 +90,7 @@ printf("using bounding box: ");
bounds.PrintToStream();
}
float size = min_c(bounds.Width() + 1.0, bounds.Height() + 1.0);
float size = max_c(bounds.Width(), bounds.Height());
double scale = 64.0 / size;
printf("scale: %f\n", scale);
@@ -510,15 +103,11 @@ printf("scale: %f\n", scale);
// else
// icon->SetName(fallbackName);
for (int32 i = 0; i < pathCount; i++) {
path_attributes& attributes = fAttributesStorage[i];
if (attributes.fill_flag)
_AddShape(attributes, false, transform, icon);
if (attributes.stroke_flag)
_AddShape(attributes, true, transform, icon);
for (NSVGshape* shape = fSource->shapes; shape != NULL; shape = shape->next) {
if (shape->fill.type != NSVG_PAINT_NONE)
_AddShape(shape, false, transform, icon);
if (shape->stroke.type != NSVG_PAINT_NONE)
_AddShape(shape, true, transform, icon);
}
// clean up styles and paths (remove duplicates)
@@ -543,218 +132,67 @@ printf("scale: %f\n", scale);
return B_OK;
}
// StartGradient
void
DocumentBuilder::StartGradient(bool radial)
{
if (fCurrentGradient) {
fprintf(stderr, "DocumentBuilder::StartGradient() - ERROR: "
"previous gradient (%s) not finished!\n",
fCurrentGradient->ID());
}
if (radial)
fCurrentGradient = new SVGRadialGradient();
else
fCurrentGradient = new SVGLinearGradient();
_AddGradient(fCurrentGradient);
}
// EndGradient
void
DocumentBuilder::EndGradient()
{
if (fCurrentGradient) {
// fCurrentGradient->PrintToStream();
} else {
fprintf(stderr, "DocumentBuilder::EndGradient() - "
"ERROR: no gradient started!\n");
}
fCurrentGradient = NULL;
}
// #pragma mark -
// _AddGradient
void
DocumentBuilder::_AddGradient(SVGGradient* gradient)
{
if (gradient) {
fGradients.AddItem((void*)gradient);
}
}
// _GradientAt
SVGGradient*
DocumentBuilder::_GradientAt(int32 index) const
{
return (SVGGradient*)fGradients.ItemAt(index);
}
// _FindGradient
SVGGradient*
DocumentBuilder::_FindGradient(const char* name) const
{
for (int32 i = 0; SVGGradient* g = _GradientAt(i); i++) {
if (strcmp(g->ID(), name) == 0)
return g;
}
return NULL;
}
// AddVertexSource
template<class VertexSource>
status_t
AddPathsFromVertexSource(Icon* icon, Shape* shape,
VertexSource& source, int32 index)
AddPathsFromVertexSource(Icon* icon, Shape* shape, NSVGshape* svgShape)
{
//printf("AddPathsFromVertexSource(pathID = %ld)\n", index);
// start with the first path
VectorPath* path = new (nothrow) VectorPath();
if (!path || !icon->Paths()->AddPath(path)) {
delete path;
return B_NO_MEMORY;
}
if (!shape->Paths()->AddPath(path))
return B_NO_MEMORY;
source.rewind(index);
double x1 = 0, y1 = 0;
unsigned cmd = source.vertex(&x1, &y1);
bool keepGoing = true;
int32 subPath = 0;
while (keepGoing) {
if (agg::is_next_poly(cmd)) {
//printf("next polygon\n");
if (agg::is_end_poly(cmd)) {
//printf(" end polygon\n");
path->SetClosed(true);
subPath++;
} else {
//printf(" not end polygon\n");
}
if (agg::is_stop(cmd)) {
//printf(" stop = true\n");
keepGoing = false;
} else {
if (subPath > 0) {
//printf(" new subpath\n");
path->CleanUp();
if (path->CountPoints() == 0) {
//printf(" path with no points!\n");
icon->Paths()->RemovePath(path);
shape->Paths()->RemovePath(path);
path->ReleaseReference();
}
path = new (nothrow) VectorPath();
if (!path || !icon->Paths()->AddPath(path)) {
delete path;
return B_NO_MEMORY;
}
if (!shape->Paths()->AddPath(path))
return B_NO_MEMORY;
}
}
for (NSVGpath* svgPath = svgShape->paths; svgPath != NULL;
svgPath = svgPath->next) {
VectorPath* path = new (nothrow) VectorPath();
if (!path || !icon->Paths()->AddPath(path)) {
delete path;
return B_NO_MEMORY;
}
switch (cmd) {
case agg::path_cmd_move_to:
//printf("move to (%.2f, %.2f) (subPath: %ld)\n", x1, y1, subPath);
if (path->CountPoints() > 0) {
// cannot MoveTo on a path that has already points!
path->CleanUp();
path = new (nothrow) VectorPath();
if (!path || !icon->Paths()->AddPath(path)) {
delete path;
return B_NO_MEMORY;
}
if (!shape->Paths()->AddPath(path))
return B_NO_MEMORY;
}
if (!path->AddPoint(BPoint(x1, y1)))
return B_NO_MEMORY;
path->SetInOutConnected(path->CountPoints() - 1, false);
break;
case agg::path_cmd_line_to:
//printf("line to (%.2f, %.2f) (subPath: %ld)\n", x1, y1, subPath);
if (!path->AddPoint(BPoint(x1, y1)))
return B_NO_MEMORY;
path->SetInOutConnected(path->CountPoints() - 1, false);
break;
if (!shape->Paths()->AddPath(path))
return B_NO_MEMORY;
case agg::path_cmd_curve3: {
double x2 = 0, y2 = 0;
cmd = source.vertex(&x2, &y2);
//printf("curve3 (%.2f, %.2f)\n", x1, y1);
//printf(" (%.2f, %.2f)\n", x2, y2);
path->SetClosed(svgPath->closed);
// convert to curve4 for easier editing
int32 start = path->CountPoints() - 1;
BPoint from;
path->GetPointAt(start, from);
int pointCount = svgPath->npts;
float* points = svgPath->pts;
double cx2 = (1.0/3.0) * from.x + (2.0/3.0) * x1;
double cy2 = (1.0/3.0) * from.y + (2.0/3.0) * y1;
double cx3 = (2.0/3.0) * x1 + (1.0/3.0) * x2;
double cy3 = (2.0/3.0) * y1 + (1.0/3.0) * y2;
// First entry in the points list is always a "move" to the path
// starting point
if (!path->AddPoint(BPoint(points[0], points[1])))
return B_NO_MEMORY;
path->SetInOutConnected(path->CountPoints() - 1, false);
path->SetPointOut(start, BPoint(cx2, cy2));
pointCount--;
points += 2;
if (!path->AddPoint(BPoint(x2, y2)))
return B_NO_MEMORY;
while (pointCount > 0) {
BPoint vector1(points[0], points[1]);
BPoint vector2(points[2], points[3]);
BPoint endPoint(points[4], points[5]);
int32 end = path->CountPoints() - 1;
path->SetInOutConnected(end, false);
path->SetPointIn(end, BPoint(cx3, cy3));
break;
}
if (!path->AddPoint(endPoint))
return B_NO_MEMORY;
case agg::path_cmd_curve4: {
double x2 = 0, y2 = 0;
double x3 = 0, y3 = 0;
cmd = source.vertex(&x2, &y2);
cmd = source.vertex(&x3, &y3);
int32 start = path->CountPoints() - 2;
int32 end = path->CountPoints() - 1;
if (!path->AddPoint(BPoint(x3, y3)))
return B_NO_MEMORY;
path->SetInOutConnected(end, false);
path->SetPointOut(start, vector1);
path->SetPointIn(end, vector2);
int32 start = path->CountPoints() - 2;
int32 end = path->CountPoints() - 1;
//printf("curve4 [%ld] (%.2f, %.2f) -> [%ld] (%.2f, %.2f) -> (%.2f, %.2f)\n", start, x1, y1, end, x2, y2, x3, y3);
path->SetInOutConnected(end, false);
path->SetPointOut(start, BPoint(x1, y1));
path->SetPointIn(end, BPoint(x2, y2));
break;
}
default:
//printf("unkown command\n");
break;
pointCount -= 3;
points += 6;
}
cmd = source.vertex(&x1, &y1);
}
//path->PrintToStream();
path->CleanUp();
if (path->CountPoints() == 0) {
//printf("path with no points!\n");
icon->Paths()->RemovePath(path);
shape->Paths()->RemovePath(path);
path->ReleaseReference();
}
// FIXME handle closed vs open paths
return B_OK;
}
// _AddShape
status_t
DocumentBuilder::_AddShape(path_attributes& attributes, bool outline,
DocumentBuilder::_AddShape(NSVGshape* svgShape, bool outline,
const Transformable& transform, Icon* icon)
{
Shape* shape = new (nothrow) Shape(NULL);
@@ -763,20 +201,43 @@ DocumentBuilder::_AddShape(path_attributes& attributes, bool outline,
return B_NO_MEMORY;
}
if (AddPathsFromVertexSource(icon, shape, fPathStorage, attributes.index) < B_OK)
if (AddPathsFromVertexSource(icon, shape, svgShape) < B_OK)
printf("failed to convert from vertex source\n");
shape->multiply(attributes.transform);
shape->SetName(svgShape->id);
shape->Multiply(transform);
StrokeTransformer* stroke = NULL;
NSVGpaint* paint = NULL;
if (outline) {
stroke = new (nothrow) StrokeTransformer(shape->VertexSource());
paint = &svgShape->stroke;
if (stroke) {
stroke->width(attributes.stroke_width);
stroke->line_cap(attributes.line_cap);
stroke->line_join(attributes.line_join);
stroke->width(svgShape->strokeWidth);
switch(svgShape->strokeLineCap) {
case NSVG_CAP_BUTT:
stroke->line_cap(agg::butt_cap);
break;
case NSVG_CAP_ROUND:
stroke->line_cap(agg::round_cap);
break;
case NSVG_CAP_SQUARE:
stroke->line_cap(agg::square_cap);
break;
}
switch(svgShape->strokeLineJoin) {
case NSVG_JOIN_MITER:
stroke->line_join(agg::miter_join);
break;
case NSVG_JOIN_ROUND:
stroke->line_join(agg::round_join);
break;
case NSVG_JOIN_BEVEL:
stroke->line_join(agg::bevel_join);
break;
}
}
if (!shape->AddTransformer(stroke)) {
@@ -784,45 +245,83 @@ DocumentBuilder::_AddShape(path_attributes& attributes, bool outline,
stroke = NULL;
}
} else {
// if (attributes.even_odd_flag)
// shape->SetFillingRule(FILL_MODE_EVEN_ODD);
// else
// shape->SetFillingRule(FILL_MODE_NON_ZERO);
paint = &svgShape->fill;
#if 0 // FIXME filling rule are missing from Shape class
if (svgShape->fillRule == NSVG_FILLRULE_EVENODD)
shape->SetFillingRule(FILL_MODE_EVEN_ODD);
else
shape->SetFillingRule(FILL_MODE_NON_ZERO);
#endif
}
Gradient* gradient = NULL;
SVGGradient* g = NULL;
const char* url = outline ? attributes.stroke_url : attributes.fill_url;
if (url[0] != 0) {
g = _FindGradient(url);
if (g != NULL)
gradient = g->GetGradient(shape->Bounds());
}
ObjectDeleter<Gradient> gradientDeleter(gradient);
Gradient gradient(true);
rgb_color color;
BGradient::ColorStop* step;
if (gradient && (step = gradient->ColorAt(0))) {
color.red = step->color.red;
color.green = step->color.green;
color.blue = step->color.blue;
} else {
if (outline) {
color.red = attributes.stroke_color.r;
color.green = attributes.stroke_color.g;
color.blue = attributes.stroke_color.b;
color.alpha = (uint8)(attributes.stroke_color.a * attributes.opacity);
} else {
color.red = attributes.fill_color.r;
color.green = attributes.fill_color.g;
color.blue = attributes.fill_color.b;
color.alpha = (uint8)(attributes.fill_color.a * attributes.opacity);
switch(paint->type) {
case NSVG_PAINT_COLOR:
color.red = paint->color & 0xFF;
color.green = (paint->color >> 8) & 0xFF;
color.blue = (paint->color >> 16) & 0xFF;
color.alpha = (paint->color >> 24) & 0xFF;
break;
case NSVG_PAINT_LINEAR_GRADIENT:
{
gradient.SetType(GRADIENT_LINEAR);
// The base gradient axis in Icon-O-Matic is a horizontal line from
// (-64, 0) to (64, 0). The base gradient axis used by nanosvg is
// a vertical line from (0, 0) to (0, 1). This initial transform
// converts from one space to the other.
agg::trans_affine baseTransform(0, 1.0/128.0, -1.0/128.0, 0,
-0.5, 0.5);
gradient.multiply(baseTransform);
break;
}
case NSVG_PAINT_RADIAL_GRADIENT:
{
gradient.SetType(GRADIENT_CIRCULAR);
agg::trans_affine baseTransform(0, 1.0/64.0, -1.0/64.0, 0,
0, 0);
gradient.multiply(baseTransform);
break;
}
}
if (paint->type != NSVG_PAINT_COLOR) {
gradient.SetInterpolation(INTERPOLATION_LINEAR);
agg::trans_affine gradientTransform(
paint->gradient->xform[0], paint->gradient->xform[1],
paint->gradient->xform[2], paint->gradient->xform[3],
paint->gradient->xform[4], paint->gradient->xform[5]
);
// The transform from nanosvg converts a screen space coordinate into
// a gradient offset. It is the inverse of what we need at this stage,
// so we just invert it and multiply our base transform with it.
gradient.multiply_inv(gradientTransform);
// Finally, scale the gradient according to the global scaling to fit
// the 64x64 box we work in.
gradient.Multiply(*shape);
for (int i = 0; i < paint->gradient->nstops; i++) {
rgb_color stopColor;
stopColor.red = paint->gradient->stops[i].color & 0xFF;
stopColor.green = (paint->gradient->stops[i].color >> 8) & 0xFF;
stopColor.blue = (paint->gradient->stops[i].color >> 16) & 0xFF;
stopColor.alpha = (paint->gradient->stops[i].color >> 24) & 0xFF;
gradient.AddColor(stopColor, paint->gradient->stops[i].offset);
}
BGradient::ColorStop* step = gradient.ColorAt(0);
if (step) {
color.red = step->color.red;
color.green = step->color.green;
color.blue = step->color.blue;
color.alpha = step->color.alpha;
}
}
color.alpha = (uint8)(color.alpha * svgShape->opacity);
Style* style = new (nothrow) Style(color);
if (!style || !icon->Styles()->AddStyle(style)) {
delete style;
@@ -831,31 +330,22 @@ DocumentBuilder::_AddShape(path_attributes& attributes, bool outline,
// NOTE: quick hack to freeze all transformations (only works because
// paths and styles are not used by multiple shapes!!)
// if (modifiers() & B_COMMAND_KEY) {
int32 pathCount = shape->Paths()->CountPaths();
for (int32 i = 0; i < pathCount; i++) {
VectorPath* path = shape->Paths()->PathAtFast(i);
path->ApplyTransform(*shape);
}
if (gradient)
gradient->Multiply(*shape);
if (stroke)
stroke->width(stroke->width() * shape->scale());
shape->Reset();
// }
if (gradient) {
style->SetGradient(gradient);
style->SetName(g->ID());
int32 pathCount = shape->Paths()->CountPaths();
for (int32 i = 0; i < pathCount; i++) {
VectorPath* path = shape->Paths()->PathAtFast(i);
path->ApplyTransform(*shape);
}
if (stroke)
stroke->width(stroke->width() * shape->scale());
if (paint->type != NSVG_PAINT_COLOR)
style->SetGradient(&gradient);
shape->Reset();
shape->SetStyle(style);
return B_OK;
}
} // namespace svg
} // namespace agg
@@ -6,41 +6,17 @@
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef DOCUMENT_BUILD_H
#define DOCUMENT_BUILD_H
#include <stdio.h>
#include <List.h>
#include <Rect.h>
#include <String.h>
#include <agg_array.h>
#include <agg_color_rgba.h>
#include <agg_conv_transform.h>
#include <agg_conv_stroke.h>
#include <agg_conv_contour.h>
#include <agg_conv_curve.h>
#include <agg_path_storage.h>
#include <agg_rasterizer_scanline_aa.h>
#include "IconBuild.h"
#include "PathTokenizer.h"
#include "nanosvg.h"
class SVGImporter;
@@ -52,207 +28,33 @@ _END_ICON_NAMESPACE
_USING_ICON_NAMESPACE
namespace agg {
namespace svg {
class SVGGradient;
// Basic path attributes
struct path_attributes {
unsigned index;
rgba8 fill_color;
rgba8 stroke_color;
double opacity;
bool fill_flag;
bool stroke_flag;
bool even_odd_flag;
line_join_e line_join;
line_cap_e line_cap;
double miter_limit;
double stroke_width;
trans_affine transform;
char stroke_url[64];
char fill_url[64];
// Empty constructor
path_attributes() :
index (0),
fill_color (rgba(0,0,0)),
stroke_color (rgba(0,0,0)),
opacity (1.0),
fill_flag (true),
stroke_flag (false),
even_odd_flag (false),
line_join (miter_join),
line_cap (butt_cap),
miter_limit (4.0),
stroke_width (1.0),
transform ()
{
stroke_url[0] = 0;
fill_url[0] = 0;
}
// Copy constructor
path_attributes(const path_attributes& attr) :
index (attr.index),
fill_color (attr.fill_color),
stroke_color (attr.stroke_color),
opacity (attr.opacity),
fill_flag (attr.fill_flag),
stroke_flag (attr.stroke_flag),
even_odd_flag (attr.even_odd_flag),
line_join (attr.line_join),
line_cap (attr.line_cap),
miter_limit (attr.miter_limit),
stroke_width (attr.stroke_width),
transform (attr.transform)
{
sprintf(stroke_url, "%s", attr.stroke_url);
sprintf(fill_url, "%s", attr.fill_url);
}
// Copy constructor with new index value
path_attributes(const path_attributes& attr, unsigned idx) :
index (idx),
fill_color (attr.fill_color),
stroke_color (attr.stroke_color),
fill_flag (attr.fill_flag),
stroke_flag (attr.stroke_flag),
even_odd_flag (attr.even_odd_flag),
line_join (attr.line_join),
line_cap (attr.line_cap),
miter_limit (attr.miter_limit),
stroke_width (attr.stroke_width),
transform (attr.transform)
{
sprintf(stroke_url, "%s", attr.stroke_url);
sprintf(fill_url, "%s", attr.fill_url);
}
};
class DocumentBuilder {
public:
typedef pod_bvector<path_attributes> attr_storage;
DocumentBuilder();
void remove_all();
// Use these functions as follows:
// begin_path() when the XML tag <path> comes ("start_element" handler)
// parse_path() on "d=" tag attribute
// end_path() when parsing of the entire tag is done.
void begin_path();
void parse_path(PathTokenizer& tok);
void end_path();
// The following functions are essentially a "reflection" of
// the respective SVG path commands.
void move_to(double x, double y, bool rel = false); // M, m
void line_to(double x, double y, bool rel = false); // L, l
void hline_to(double x, bool rel = false); // H, h
void vline_to(double y, bool rel = false); // V, v
void curve3(double x1, double y1, // Q, q
double x, double y, bool rel = false);
void curve3(double x, double y, bool rel = false); // T, t
void curve4(double x1, double y1, // C, c
double x2, double y2,
double x, double y, bool rel = false);
void curve4(double x2, double y2, // S, s
double x, double y, bool rel = false);
void elliptical_arc(double rx, double ry,
double angle,
bool large_arc_flag,
bool sweep_flag,
double x, double y,
bool rel = false); // A, a
void close_subpath(); // Z, z
/* template<class VertexSource>
void add_path(VertexSource& vs,
unsigned path_id = 0,
bool solid_path = true)
{
fPathStorage.add_path(vs, path_id, solid_path);
}*/
DocumentBuilder(NSVGimage* image);
void SetTitle(const char* title);
void SetDimensions(uint32 width, uint32 height, BRect viewBox);
// Call these functions on <g> tag (start_element, end_element respectively)
void push_attr();
void pop_attr();
// Attribute setting functions.
void fill(const rgba8& f);
void stroke(const rgba8& s);
void even_odd(bool flag);
void stroke_width(double w);
void fill_none();
void fill_url(const char* url);
void stroke_none();
void stroke_url(const char* url);
void opacity(double op);
void fill_opacity(double op);
void stroke_opacity(double op);
void line_join(line_join_e join);
void line_cap(line_cap_e cap);
void miter_limit(double ml);
trans_affine& transform();
/* // Make all polygons CCW-oriented
void arrange_orientations()
{
fPathStorage.arrange_orientations_all_paths(path_flags_ccw);
}*/
unsigned operator [](unsigned idx)
{
fTransform = fAttributesStorage[idx].transform;
return fAttributesStorage[idx].index;
}
status_t GetIcon(Icon* icon,
SVGImporter* importer,
const char* fallbackName);
void StartGradient(bool radial = false);
void EndGradient();
SVGGradient* CurrentGradient() const
{ return fCurrentGradient; }
private:
void _AddGradient(SVGGradient* gradient);
SVGGradient* _GradientAt(int32 index) const;
SVGGradient* _FindGradient(const char* name) const;
status_t _AddShape(path_attributes& attributes,
status_t _AddShape(NSVGshape* svgShape,
bool outline,
const Transformable& transform,
Icon* icon);
path_attributes& cur_attr();
path_storage fPathStorage;
attr_storage fAttributesStorage;
attr_storage fAttributesStack;
trans_affine fTransform;
BList fGradients;
SVGGradient* fCurrentGradient;
uint32 fWidth;
uint32 fHeight;
BRect fViewBox;
BString fTitle;
NSVGimage* fSource;
};
} // namespace svg
} // namespace agg
#endif // DOCUMENT_BUILD_H
@@ -1,157 +0,0 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: [email protected]
// [email protected]
// http://www.antigrain.com
#include "PathTokenizer.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
namespace agg {
namespace svg {
// globals
const char PathTokenizer::sCommands[] = "+-MmZzLlHhVvCcSsQqTtAaFfPp";
const char PathTokenizer::sNumeric[] = ".Ee0123456789";
const char PathTokenizer::sSeparators[] = " ,\t\n\r";
// constructor
PathTokenizer::PathTokenizer()
: fPath(0),
fLastNumber(0.0),
fLastCommand(0)
{
init_char_mask(fCommandsMask, sCommands);
init_char_mask(fNumericMask, sNumeric);
init_char_mask(fSeparatorsMask, sSeparators);
}
// set_path_str
void
PathTokenizer::set_path_str(const char* str)
{
fPath = str;
fLastCommand = 0;
fLastNumber = 0.0;
}
// next
bool
PathTokenizer::next()
{
if(fPath == 0) return false;
// Skip all white spaces and other garbage
while (*fPath && !is_command(*fPath) && !isNumeric(*fPath)) {
if (!is_separator(*fPath)) {
char buf[100];
sprintf(buf, "PathTokenizer::next : Invalid Character %c", *fPath);
throw exception(buf);
}
fPath++;
}
if (*fPath == 0) return false;
if (is_command(*fPath)) {
// Check if the command is a numeric sign character
if(*fPath == '-' || *fPath == '+')
{
return parse_number();
}
fLastCommand = *fPath++;
while(*fPath && is_separator(*fPath)) fPath++;
if(*fPath == 0) return true;
}
return parse_number();
}
// next
double
PathTokenizer::next(char cmd)
{
if (!next()) throw exception("parse_path: Unexpected end of path");
if (last_command() != cmd) {
char buf[100];
sprintf(buf, "parse_path: Command %c: bad or missing parameters", cmd);
throw exception(buf);
}
return last_number();
}
// init_char_mask
void
PathTokenizer::init_char_mask(char* mask, const char* char_set)
{
memset(mask, 0, 256/8);
while (*char_set) {
unsigned c = unsigned(*char_set++) & 0xFF;
mask[c >> 3] |= 1 << (c & 7);
}
}
// contains
inline bool
PathTokenizer::contains(const char* mask, unsigned c) const
{
return (mask[(c >> 3) & (256 / 8 - 1)] & (1 << (c & 7))) != 0;
}
// is_command
inline bool
PathTokenizer::is_command(unsigned c) const
{
return contains(fCommandsMask, c);
}
// isNumeric
inline bool
PathTokenizer::isNumeric(unsigned c) const
{
return contains(fNumericMask, c);
}
// is_separator
inline bool
PathTokenizer::is_separator(unsigned c) const
{
return contains(fSeparatorsMask, c);
}
// parse_number
bool
PathTokenizer::parse_number()
{
char* end;
fLastNumber = strtod(fPath, &end);
fPath = end;
return true;
}
} //namespace svg
} //namespace agg
@@ -1,106 +0,0 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef PATH_TOKENIZER_H
#define PATH_TOKENIZER_H
#include "SVGException.h"
namespace agg {
namespace svg {
// SVG path tokenizer.
// Example:
//
// agg::svg::PathTokenizer tok;
//
// tok.set_str("M-122.304 84.285L-122.304 84.285 122.203 86.179 ");
// while(tok.next())
// {
// printf("command='%c' number=%f\n",
// tok.last_command(),
// tok.last_number());
// }
//
// The tokenizer does all the routine job of parsing the SVG paths.
// It doesn't recognize any graphical primitives, it even doesn't know
// anything about pairs of coordinates (X,Y). The purpose of this class
// is to tokenize the numeric values and commands. SVG paths can
// have single numeric values for Horizontal or Vertical line_to commands
// as well as more than two coordinates (4 or 6) for Bezier curves
// depending on the semantics of the command.
// The behaviour is as follows:
//
// Each call to next() returns true if there's new command or new numeric
// value or false when the path ends. How to interpret the result
// depends on the sematics of the command. For example, command "C"
// (cubic Bezier curve) implies 6 floating point numbers preceded by this
// command. If the command assumes no arguments (like z or Z) the
// the last_number() values won't change, that is, last_number() always
// returns the last recognized numeric value, so does last_command().
class PathTokenizer {
public:
PathTokenizer();
void set_path_str(const char* str);
bool next();
double next(char cmd);
char last_command() const
{ return fLastCommand; }
double last_number() const
{ return fLastNumber; }
private:
static void init_char_mask(char* mask,
const char* char_set);
inline bool contains(const char* mask, unsigned c) const;
inline bool is_command(unsigned c) const;
inline bool isNumeric(unsigned c) const;
inline bool is_separator(unsigned c) const;
bool parse_number();
char fSeparatorsMask[256 / 8];
char fCommandsMask[256 / 8];
char fNumericMask[256 / 8];
const char* fPath;
double fLastNumber;
char fLastCommand;
static const char sCommands[];
static const char sNumeric[];
static const char sSeparators[];
};
} //namespace svg
} //namespace agg
#endif // PATH_TOKENIZER_H
@@ -1,199 +0,0 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include <stdio.h>
#include <stdlib.h>
#include "GradientTransformable.h"
#include "SVGGradients.h"
namespace agg {
namespace svg {
// constructor
SVGGradient::SVGGradient()
: BMessage(),
fGradient(NULL),
fID(""),
fGradientUnits(UNSPECIFIED)
{
}
// destructor
SVGGradient::~SVGGradient()
{
}
// SetID
void
SVGGradient::SetID(const char* id)
{
fID = id;
}
// ID
const char*
SVGGradient::ID() const
{
return fID.String();
}
// AddStop
void
SVGGradient::AddStop(float offset, rgba8 color)
{
if (!fGradient)
fGradient = MakeGradient();
rgb_color c = { color.r, color.g, color.b, color.a };
fGradient->AddColor(c, offset);
}
// SetTransformation
void
SVGGradient::SetTransformation(const trans_affine& transform)
{
//printf("SVGGradient::SetTransformation()\n");
if (!fGradient)
fGradient = MakeGradient();
fGradient->multiply(transform);
}
// GetGradient
Gradient*
SVGGradient::GetGradient(BRect objectBounds)
{
if (fGradient) {
Gradient* gradient = new Gradient(*fGradient);
IdentifyGradientUnits();
if (fGradientUnits == OBJECT_BOUNDING_BOX) {
gradient->FitToBounds(objectBounds);
}
return gradient;
}
return NULL;
}
// IdentifyGradientUnits
void
SVGGradient::IdentifyGradientUnits()
{
if (fGradientUnits == UNSPECIFIED) {
const char* units;
if (FindString("gradientUnits", &units) >= B_OK) {
if (strcmp(units, "objectBoundingBox") == 0)
fGradientUnits = OBJECT_BOUNDING_BOX;
}
}
}
// constructor
SVGLinearGradient::SVGLinearGradient()
: SVGGradient()
{
}
// destructor
SVGLinearGradient::~SVGLinearGradient()
{
}
// MakeGradient
Gradient*
SVGLinearGradient::MakeGradient() const
{
//printf("SVGLinearGradient::MakeGradient()\n");
//PrintToStream();
Gradient* gradient = new Gradient(true);
gradient->SetType(GRADIENT_LINEAR);
gradient->SetInterpolation(INTERPOLATION_LINEAR);
// setup the gradient transform
BPoint start(-64.0, -64.0);
BPoint end(64.0, -64.0);
BString coordinate;
if (FindString("x1", &coordinate) >= B_OK)
start.x = atof(coordinate.String());
if (FindString("y1", &coordinate) >= B_OK)
start.y = atof(coordinate.String());
if (FindString("x2", &coordinate) >= B_OK)
end.x = atof(coordinate.String());
if (FindString("y2", &coordinate) >= B_OK)
end.y = atof(coordinate.String());
// the transformed parallelogram
double parl[6];
parl[0] = start.x;
parl[1] = start.y;
parl[2] = end.x;
parl[3] = end.y;
parl[4] = end.x - (end.y - start.y);
parl[5] = end.y + (end.x - start.x);
trans_affine transform(-64.0, -64.0, 64.0, 64.0, parl);
gradient->multiply(transform);
return gradient;
}
// constructor
SVGRadialGradient::SVGRadialGradient()
: SVGGradient()
{
}
// destructor
SVGRadialGradient::~SVGRadialGradient()
{
}
// MakeGradient
Gradient*
SVGRadialGradient::MakeGradient() const
{
//printf("SVGRadialGradient::MakeGradient()\n");
// TODO: handle userSpaceOnUse/objectBoundingBox
Gradient* gradient = new Gradient(true);
gradient->SetType(GRADIENT_CIRCULAR);
gradient->SetInterpolation(INTERPOLATION_LINEAR);
double cx = 0.0;
double cy = 0.0;
double r = 100.0;
BString value;
if (FindString("cx", &value) >= B_OK)
cx = atof(value.String());
if (FindString("cy", &value) >= B_OK)
cy = atof(value.String());
if (FindString("r", &value) >= B_OK)
r = atof(value.String());
// the transformed parallelogram
double parl[6];
parl[0] = cx - r;
parl[1] = cy - r;
parl[2] = cx + r;
parl[3] = cy - r;
parl[4] = cx + r;
parl[5] = cy + r;
trans_affine transform(-64.0, -64.0, 64.0, 64.0, parl);
gradient->multiply(transform);
return gradient;
}
} // namespace svg
} // namespace agg
@@ -1,81 +0,0 @@
/*
* Copyright 2006-2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SVG_GRADIENTS_H
#define SVG_GRADIENTS_H
#include <agg_color_rgba.h>
#include <agg_trans_affine.h>
#include <Message.h>
#include <String.h>
#include "IconBuild.h"
_BEGIN_ICON_NAMESPACE
class Gradient;
_END_ICON_NAMESPACE
namespace agg {
namespace svg {
class SVGGradient : public BMessage {
public:
SVGGradient();
virtual ~SVGGradient();
void SetID(const char* id);
const char* ID() const;
virtual void AddStop(float offset, rgba8 color);
void SetTransformation(const trans_affine& transform);
Gradient* GetGradient(BRect objectBounds);
protected:
virtual Gradient* MakeGradient() const = 0;
void IdentifyGradientUnits();
private:
enum {
UNSPECIFIED = 0,
USER_SPACE_ON_USE,
OBJECT_BOUNDING_BOX,
};
Gradient* fGradient;
BString fID;
uint32 fGradientUnits;
};
class SVGLinearGradient : public SVGGradient {
public:
SVGLinearGradient();
virtual ~SVGLinearGradient();
protected:
virtual Gradient* MakeGradient() const;
};
class SVGRadialGradient : public SVGGradient {
public:
SVGRadialGradient();
virtual ~SVGRadialGradient();
protected:
virtual Gradient* MakeGradient() const;
};
} // namespace svg
} // namespace agg
#endif // SVG_GRADIENTS_H
@@ -18,7 +18,7 @@
#include <Path.h>
#include "DocumentBuilder.h"
#include "SVGParser.h"
#include "nanosvg.h"
#undef B_TRANSLATION_CONTEXT
@@ -69,23 +69,22 @@ SVGImporter::Import(Icon* icon, const entry_ref* ref)
return B_ERROR;
}
try {
agg::svg::DocumentBuilder builder;
agg::svg::Parser parser(builder);
parser.parse(path.Path());
ret = builder.GetIcon(icon, this, ref->name);
} catch(agg::svg::exception& e) {
NSVGimage* svg = nsvgParseFromFile(path.Path(), "px", 96);
if (svg == NULL) {
char error[1024];
sprintf(error, B_TRANSLATE("Failed to open the file '%s' as "
"an SVG document.\n\n"
"Error: %s"), ref->name, e.msg());
"an SVG document.\n\n"), ref->name);
BAlert* alert = new BAlert(B_TRANSLATE("load error"),
error, B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
error, B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(NULL);
ret = B_ERROR;
return B_ERROR;
}
DocumentBuilder builder(svg);
ret = builder.GetIcon(icon, this, ref->name);
nsvgDelete(svg);
return ret;
}
File diff suppressed because it is too large Load Diff
@@ -1,105 +0,0 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef SVG_PARSER_H
#define SVG_PARSER_H
#include "PathTokenizer.h"
#include "DocumentBuilder.h"
namespace agg {
namespace svg {
class Parser {
enum { buf_size = BUFSIZ };
public:
Parser(DocumentBuilder& builder);
virtual ~Parser();
void parse(const char* pathToFile);
const char* title() const
{ return fTitle; }
bool TagsIgnored() const
{ return fTagsIgnored; }
private:
// XML event handlers
static void start_element(void* data, const char* el,
const char** attr);
static void end_element(void* data, const char* el);
static void content(void* data, const char* s, int len);
void parse_svg(const char** attr);
void parse_attr(const char** attr);
void parse_path(const char** attr);
void parse_poly(const char** attr, bool close_flag);
void parse_circle(const char** attr);
void parse_ellipse(const char** attr);
void parse_rect(const char** attr);
void parse_line(const char** attr);
void parse_style(const char* str);
trans_affine parse_transform(const char* str);
void parse_gradient(const char** attr, bool radial);
void parse_gradient_stop(const char** attr);
unsigned parse_matrix(const char* str, trans_affine& transform);
unsigned parse_translate(const char* str, trans_affine& transform);
unsigned parse_rotate(const char* str, trans_affine& transform);
unsigned parse_scale(const char* str, trans_affine& transform);
unsigned parse_skew_x(const char* str, trans_affine& transform);
unsigned parse_skew_y(const char* str, trans_affine& transform);
bool parse_attr(const char* name,
const char* value);
bool parse_name_value(const char* nv_start,
const char* nv_end);
void copy_name(const char* start, const char* end);
void copy_value(const char* start, const char* end);
private:
DocumentBuilder& fBuilder;
PathTokenizer fPathTokenizer;
char* fBuffer;
char* fTitle;
unsigned fTitleLength;
bool fTitleFlag;
bool fPathFlag;
char* fAttrName;
char* fAttrValue;
unsigned fAttrNameLength;
unsigned fAttrValueLength;
rgba8 fGradientStopColor;
bool fTagsIgnored;
};
} // namespace svg
} // namespace agg
#endif // SVG_PARSER_H
File diff suppressed because it is too large Load Diff
@@ -601,6 +601,8 @@ Gradient::PrintToStream() const
step->color.blue,
step->color.alpha);
}
Transformable::PrintToStream();
}
// _MakeEmpty
@@ -319,3 +319,12 @@ Transformable::TransformationChanged()
// default implementation doesn't care
}
void
Transformable::PrintToStream() const
{
double matrix[6];
store_to(matrix);
printf("Transform:\n%f\t%f\t%f\n%f\t%f\t%f\n",
matrix[0], matrix[2], matrix[4], matrix[1], matrix[3], matrix[5]);
}
@@ -70,6 +70,8 @@ class Transformable : public agg::trans_affine {
virtual void TransformationChanged();
// hook function that is called when the transformation
// is changed for some reason
virtual void PrintToStream() const;
};