summaryrefslogtreecommitdiffstats
path: root/ksvg/plugin/backends/agg/AggCanvasItems.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksvg/plugin/backends/agg/AggCanvasItems.cpp')
-rw-r--r--ksvg/plugin/backends/agg/AggCanvasItems.cpp1747
1 files changed, 1747 insertions, 0 deletions
diff --git a/ksvg/plugin/backends/agg/AggCanvasItems.cpp b/ksvg/plugin/backends/agg/AggCanvasItems.cpp
new file mode 100644
index 00000000..4ceb6109
--- /dev/null
+++ b/ksvg/plugin/backends/agg/AggCanvasItems.cpp
@@ -0,0 +1,1747 @@
+/*
+ Copyright (C) 2001-2003 KSVG Team
+ This file is part of the KDE project
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qimage.h>
+
+#include "SVGPaint.h"
+#include "SVGRectImpl.h"
+#include "SVGAngleImpl.h"
+#include "SVGPaintImpl.h"
+#include "SVGMatrixImpl.h"
+#include "SVGUnitTypes.h"
+#include "SVGHelperImpl.h"
+#include "SVGDocumentImpl.h"
+#include "SVGPointListImpl.h"
+#include "SVGMarkerElement.h"
+#include "SVGMarkerElementImpl.h"
+#include "SVGSVGElementImpl.h"
+#include "SVGPathSegListImpl.h"
+#include "SVGAnimatedRectImpl.h"
+#include "SVGAnimatedAngleImpl.h"
+#include "SVGAnimatedLengthImpl.h"
+#include "SVGPolygonElementImpl.h"
+#include "SVGClipPathElementImpl.h"
+#include "SVGPolylineElementImpl.h"
+#include "SVGStopElementImpl.h"
+#include "SVGGradientElement.h"
+#include "SVGGradientElementImpl.h"
+#include "SVGLinearGradientElementImpl.h"
+#include "SVGRadialGradientElementImpl.h"
+#include "SVGPatternElementImpl.h"
+#include "SVGAnimatedNumberImpl.h"
+#include "SVGAnimatedLengthListImpl.h"
+#include "SVGAnimatedEnumerationImpl.h"
+#include "SVGAnimatedStringImpl.h"
+#include "SVGPreserveAspectRatioImpl.h"
+#include "SVGAnimatedPreserveAspectRatioImpl.h"
+#include "SVGAnimatedTransformListImpl.h"
+#include "SVGTransformListImpl.h"
+#include "SVGUnitConverter.h"
+
+#include "Glyph.h"
+#include "Converter.h"
+#include "KSVGTextChunk.h"
+
+#include "agg_rasterizer_scanline_aa.h"
+#include "agg_scanline_u.h"
+#include "agg_scanline_p.h"
+#include "agg_bounding_rect.h"
+#include "agg_ellipse.h"
+#include "agg_span_image_filter_rgba32.h"
+#include "agg_color_rgba8.h"
+#include "agg_gray8.h"
+#include "agg_span_gradient.h"
+#include "agg_span_interpolator_linear.h"
+#include "agg_span_pattern_rgba32.h"
+#include "agg_renderer_scanline.h"
+
+#include "AggCanvas.h"
+#include "AggCanvasItems.h"
+
+extern "C"
+{
+/* These are in KSVGHelper.cpp */
+int linearRGBFromsRGB(int sRGB8bit);
+int sRGBFromLinearRGB(int linearRGB8bit);
+}
+
+struct color_function_profile
+{
+ color_function_profile() {}
+ color_function_profile(const agg::rgba8 *colors) :
+ m_colors(colors) {}
+
+ const agg::rgba8& operator [] (unsigned v) const
+ {
+ return m_colors[v];
+ }
+
+ const agg::rgba8 *m_colors;
+};
+
+using namespace KSVG;
+
+// agg2 helpers
+
+agg::vcgen_stroke::line_cap_e toAggLineCap(PathStrokeCapType cap)
+{
+ if(cap == PATH_STROKE_CAP_BUTT)
+ return agg::vcgen_stroke::butt_cap;
+ else if(cap == PATH_STROKE_CAP_ROUND)
+ return agg::vcgen_stroke::round_cap;
+ else
+ return agg::vcgen_stroke::square_cap;
+}
+
+template<class Source>
+stroke<Source>::stroke(Source& src, KSVG::SVGStylableImpl *style) : m_s(src)
+{
+ m_s.width(style->getStrokeWidth()->baseVal()->value());
+ m_s.line_join((agg::vcgen_stroke::line_join_e)style->getJoinStyle());
+ m_s.miter_limit(style->getStrokeMiterlimit());
+ m_s.line_cap(toAggLineCap(style->getCapStyle()));
+}
+
+template<class Source>
+dash_stroke<Source>::dash_stroke(Source& src, KSVG::SVGStylableImpl *style) : m_d(src), m_ds(m_d)
+{
+ unsigned int dashLength = style->getDashArray() ? style->getDashArray()->baseVal()->numberOfItems() : 0;
+ // there are dashes to be rendered
+ unsigned int count = (dashLength % 2) == 0 ? dashLength : dashLength * 2;
+ for(unsigned int i = 0; i < count; i += 2)
+ m_d.add_dash(style->getDashArray()->baseVal()->getItem(i % dashLength)->value(),
+ style->getDashArray()->baseVal()->getItem((i + 1) % dashLength)->value());
+ m_d.dash_start(style->getDashOffset()->baseVal()->value());
+ m_ds.width(style->getStrokeWidth()->baseVal()->value());
+ m_ds.line_join((agg::vcgen_stroke::line_join_e)style->getJoinStyle());
+ m_ds.miter_limit(style->getStrokeMiterlimit());
+ m_ds.line_cap(toAggLineCap(style->getCapStyle()));
+}
+
+template<class Source>
+dash_stroke_simple<Source>::dash_stroke_simple(Source& src, KSVG::SVGStylableImpl *style)
+{
+ //if(style->isStroked() && style->getStrokeWidth()->baseVal()->value() > 0)
+ //{
+ unsigned int dashLength = style->getDashArray() ? style->getDashArray()->baseVal()->numberOfItems() : 0;
+ if(dashLength > 0)
+ impl = new dash_stroke<Source>(src, style);
+ else
+ impl = new stroke<Source>(src, style);
+ //}
+ //else
+ //impl = 0;
+}
+
+void renderPathSolid(AggCanvas *canvas, const agg::rgba8 &color)
+{
+ agg::scanline_p8 sl;
+ if(canvas->nrChannels() == 3)
+ {
+ typedef agg::pixfmt_rgb24 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_p_solid<renderer_base> renderer_solid;
+
+ pixfmt pixf(canvas->buf());
+ renderer_base rb(pixf);
+ renderer_solid ren(rb);
+
+ ren.color(color);
+ canvas->m_ras.render(sl, ren);
+ }
+ else
+ {
+ typedef agg::pixfmt_rgba32 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_p_solid<renderer_base> renderer_solid;
+
+ pixfmt pixf(canvas->buf());
+ renderer_base rb(pixf);
+ renderer_solid ren(rb);
+
+ ren.color(color);
+ canvas->m_ras.render(sl, ren);
+ }
+}
+
+// #####
+
+BezierPathAggStroked::BezierPathAggStroked(SVGStylableImpl *style)
+: T2P::BezierPathAgg(), m_curved_trans_clipped(m_curved_trans), m_curved_stroked(m_curved, style), m_curved_stroked_trans(m_curved_stroked, m_transform), m_curved_stroked_trans_clipped(m_curved_stroked_trans), m_style(style)
+{
+}
+
+BezierPathAggStroked::BezierPathAggStroked(const T2P::BezierPathAgg &other, SVGStylableImpl *style)
+: T2P::BezierPathAgg(other), m_curved_trans_clipped(m_curved_trans), m_curved_stroked(m_curved, style), m_curved_stroked_trans(m_curved_stroked, m_transform), m_curved_stroked_trans_clipped(m_curved_stroked_trans), m_style(style)
+{
+}
+
+AggShape::AggShape(AggCanvas *c, SVGStylableImpl *style) : CanvasItem(), BezierPathAggStroked(style), m_canvas(c)
+{
+ m_context = NORMAL;
+ m_fillPainter = 0;
+ m_strokePainter = 0;
+}
+
+AggShape::~AggShape()
+{
+ freeSVPs();
+ delete m_fillPainter;
+ delete m_strokePainter;
+}
+
+QRect AggShape::bbox() const
+{
+ return m_bbox;
+}
+
+bool AggShape::fillContains(const QPoint &p)
+{
+ agg::rasterizer_scanline_aa<> ras;
+ ras.filling_rule(m_style->getFillRule() == RULE_EVENODD ? agg::fill_even_odd : agg::fill_non_zero);
+ ras.add_path(m_curved_trans);
+ bool b = ras.hit_test(p.x(), p.y());
+ return b;
+}
+
+bool AggShape::strokeContains(const QPoint &p)
+{
+ agg::rasterizer_scanline_aa<> ras;
+ ras.add_path(m_curved_stroked_trans);
+ bool b = ras.hit_test(p.x(), p.y());
+ return b;
+}
+
+void AggShape::update(CanvasItemUpdate reason, int param1, int param2)
+{
+ if(reason == UPDATE_STYLE)
+ {
+ if(!m_fillPainter || !m_strokePainter)
+ AggShape::init();
+ if(m_fillPainter)
+ m_fillPainter->update(m_style);
+ if(m_strokePainter)
+ m_strokePainter->update(m_style);
+ m_canvas->invalidate(this, false);
+ }
+ else if(reason == UPDATE_TRANSFORM)
+ {
+ freeSVPs();
+ init();
+ m_canvas->invalidate(this, true);
+ }
+ else if(reason == UPDATE_ZOOM)
+ init();
+ else if(reason == UPDATE_PAN)
+ {
+ agg::trans_affine mtx(1, 0, 0, 1, param1, param2);
+ m_transform *= mtx;
+ }
+ else if(reason == UPDATE_LINEWIDTH)
+ {
+ init();
+ m_canvas->invalidate(this, true);
+ }
+}
+
+void AggShape::draw(SVGShapeImpl *shape)
+{
+ if(!m_referenced && (!m_style->getVisible() || !m_style->getDisplay() || !shape->directRender()))
+ return;
+
+ //if(!m_strokeSVP && (!m_fillSVP || !m_style->isFilled()))
+ // init();
+ agg::rect cb;
+ if(m_canvas->nrChannels() == 3)
+ {
+ agg::pixfmt_rgb24 pixf(m_canvas->buf());
+ agg::renderer_base<agg::pixfmt_rgb24> rb(pixf);
+ cb = rb.clip_box();
+ }
+ else
+ {
+ agg::pixfmt_rgba32 pixf(m_canvas->buf());
+ agg::renderer_base<agg::pixfmt_rgba32> rb(pixf);
+ cb = rb.clip_box();
+ }
+
+ m_curved_trans_clipped.clip_box(cb.x1, cb.y1, cb.x2 + 1, cb.y2 + 1);
+ m_curved_stroked_trans_clipped.clip_box(cb.x1, cb.y1, cb.x2 + 1, cb.y2 + 1);
+
+ double x1, y1, x2, y2;
+ agg::bounding_rect(m_curved_trans, *this, 0, 1, &x1, &y1, &x2, &y2);
+ m_bbox = QRect(int(x1), int(y1), int(x2 - x1), int(y2 - y1));
+
+ m_curved.approximation_scale(pow(m_transform.scale(), 0.75));
+
+ if(m_fillPainter)
+ m_fillPainter->draw(m_canvas, m_curved_trans, m_style, shape);
+ if(m_strokePainter)
+ m_strokePainter->draw(m_canvas, m_curved_stroked_trans, m_style, shape);
+}
+
+bool AggShape::isVisible(SVGShapeImpl *shape)
+{
+ return m_referenced || (m_style->getVisible() && m_style->getDisplay() && shape->directRender());
+}
+
+void AggShape::calcSVPs(const SVGMatrixImpl *matrix)
+{
+ // transform
+ m_transform = agg::trans_affine(matrix->a(), matrix->b(), matrix->c(), matrix->d(), matrix->e(), matrix->f());
+
+ double x1, y1, x2, y2;
+ agg::bounding_rect(m_curved_trans, *this, 0, 1, &x1, &y1, &x2, &y2);
+ m_bbox = QRect(int(x1), int(y1), int(x2 - x1), int(y2 - y1));
+}
+
+void AggShape::init(const SVGMatrixImpl *)
+{
+}
+
+void AggShape::init()
+{
+ if(m_style->isFilled())
+ {
+ if(m_fillPainter == 0)
+ m_fillPainter = new AggFillPaintServer(m_style);
+ }
+ else
+ {
+ delete m_fillPainter;
+ m_fillPainter = 0;
+ }
+
+ // Spec: A zero value causes no stroke to be painted.
+ if(m_style->isStroked() && m_style->getStrokeWidth()->baseVal()->value() > 0)
+ {
+ if(m_strokePainter == 0)
+ m_strokePainter = new AggStrokePaintServer(m_style);
+ }
+ else
+ {
+ delete m_strokePainter;
+ m_strokePainter = 0;
+ }
+}
+
+void AggShape::freeSVPs()
+{
+ m_storage.remove_all();
+}
+
+// #####
+
+AggStrokePaintServer::AggStrokePaintServer(SVGStylableImpl *style)
+{
+ update(style);
+}
+
+void AggStrokePaintServer::update(SVGStylableImpl *style)
+{
+ if(style->getStrokeColor()->paintType() != SVG_PAINTTYPE_URI)
+ {
+ QColor qcolor;
+ if(style->getStrokeColor()->paintType() == SVG_PAINTTYPE_CURRENTCOLOR)
+ qcolor = style->getColor()->rgbColor().color();
+ else
+ qcolor = style->getStrokeColor()->rgbColor().color();
+
+ short opacity = static_cast<short>(style->getStrokeOpacity() * style->getOpacity() * 255);
+
+ // Spec: clamping
+ opacity = opacity < 0 ? 0 : opacity;
+ opacity = opacity > 255 ? 255 : opacity;
+
+ m_color = agg::rgba8(qcolor.red(), qcolor.green(), qcolor.blue());
+ m_color.opacity(style->getStrokeOpacity() * style->getOpacity());
+ }
+}
+
+template<class VertexSource>
+void AggStrokePaintServer::draw(AggCanvas *canvas, VertexSource &vs, SVGStylableImpl *style, SVGShapeImpl *shape)
+{
+ canvas->m_ras.reset();
+ if(style->getStrokeColor()->paintType() == SVG_PAINTTYPE_URI)
+ {
+ AggPaintServer *pserver = static_cast<AggPaintServer *>(SVGPaintServerImpl::paintServer(shape->ownerDoc(), style->getStrokeColor()->uri().string()));
+ if(!pserver) return;
+ pserver->setBBoxTarget(shape);
+
+ // TODO : Clipping
+ if(!pserver->finalized())
+ pserver->finalizePaintServer();
+ canvas->m_ras.add_path(vs);
+ pserver->render(canvas);
+ }
+ else
+ {
+ canvas->m_ras.add_path(vs);
+ renderPathSolid(canvas, m_color);
+ }
+}
+
+AggFillPaintServer::AggFillPaintServer(SVGStylableImpl *style)
+{
+ update(style);
+}
+
+void AggFillPaintServer::update(SVGStylableImpl *style)
+{
+ if(style->getFillColor()->paintType() != SVG_PAINTTYPE_URI)
+ {
+ QColor qcolor;
+ if(style->getFillColor()->paintType() == SVG_PAINTTYPE_CURRENTCOLOR)
+ qcolor = style->getColor()->rgbColor().color();
+ else
+ qcolor = style->getFillColor()->rgbColor().color();
+
+ short opacity = static_cast<short>(style->getFillOpacity() * style->getOpacity() * 255);
+
+ // Spec: clamping
+ opacity = opacity < 0 ? 0 : opacity;
+ opacity = opacity > 255 ? 255 : opacity;
+
+ m_color = agg::rgba8(qcolor.red(), qcolor.green(), qcolor.blue());
+ m_color.opacity(style->getFillOpacity() * style->getOpacity());
+ }
+}
+
+template<class VertexSource>
+void AggFillPaintServer::draw(AggCanvas *canvas, VertexSource &vs, SVGStylableImpl *style, SVGShapeImpl *shape)
+{
+ canvas->m_ras.reset();
+ if(style->getFillColor()->paintType() == SVG_PAINTTYPE_URI)
+ {
+ AggPaintServer *pserver = static_cast<AggPaintServer *>(SVGPaintServerImpl::paintServer(shape->ownerDoc(), style->getFillColor()->uri().string()));
+ if(!pserver) return;
+ pserver->setBBoxTarget(shape);
+
+ // TODO : Clipping
+ if(!pserver->finalized())
+ pserver->finalizePaintServer();
+ canvas->m_ras.add_path(vs);
+ pserver->render(canvas);
+ }
+ else
+ {
+ canvas->m_ras.filling_rule(style->getFillRule() == RULE_EVENODD ? agg::fill_even_odd : agg::fill_non_zero);
+ canvas->m_ras.add_path(vs);
+ renderPathSolid(canvas, m_color);
+ }
+}
+
+// #####
+
+AggRectangle::AggRectangle(AggCanvas *c, SVGRectElementImpl *rect)
+: AggShape(c, rect), m_rect(rect)
+{
+ init();
+}
+
+void AggRectangle::draw()
+{
+ if(isVisible())
+ AggShape::draw(m_rect);
+}
+
+bool AggRectangle::isVisible()
+{
+ // Spec: a value of zero disables rendering
+ return AggShape::isVisible(m_rect) && m_rect->width()->baseVal()->value() > 0 && m_rect->height()->baseVal()->value() > 0;
+}
+
+void AggRectangle::init()
+{
+ init(m_rect->screenCTM());
+}
+
+void AggRectangle::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ double x = m_rect->x()->baseVal()->value();
+ double y = m_rect->y()->baseVal()->value();
+ double width = m_rect->width()->baseVal()->value();
+ double height = m_rect->height()->baseVal()->value();
+ double rx = m_rect->rx()->baseVal()->value();
+ double ry = m_rect->ry()->baseVal()->value();
+
+ // Spec: If there is no rx or ry specified, draw a normal rect
+ if(rx == -1 && ry == -1)
+ {
+ m_storage.move_to(x, y);
+ m_storage.line_to(x + width, y);
+ m_storage.line_to(x + width, y + height);
+ m_storage.line_to(x, y + height);
+ }
+ else
+ {
+ int i = 0;
+
+ // Spec: If rx isn't specified, but ry, set rx to ry
+ if(rx == -1)
+ rx = ry;
+
+ // Spec: If ry isn't specified, but rx, set ry to rx
+ if(ry == -1)
+ ry = rx;
+
+ // Spec: If rx is greater than half of the width of the rectangle
+ // then set rx to half of the width
+ if(rx > width / 2)
+ rx = width / 2;
+
+ // Spec: If ry is greater than half of the height of the rectangle
+ // then set ry to half of the height
+ if(ry > height / 2)
+ ry = height / 2;
+
+ m_storage.move_to(x + rx, y);
+
+ i++;
+ m_storage.curve4(x + rx * (1 - 0.552), y, x, y + ry * (1 - 0.552), x, y + ry);
+ i++;
+ if(ry < height / 2)
+ {
+ m_storage.line_to(x, y + height - ry);
+ i++;
+ }
+ m_storage.curve4(x, y + height - ry * (1 - 0.552), x + rx * (1 - 0.552), y + height, x + rx, y + height);
+ i++;
+ if(rx < width / 2)
+ {
+ m_storage.line_to(x + width - rx, y + height);
+ i++;
+ }
+ m_storage.curve4(x + width - rx * (1 - 0.552), y + height, x + width, y + height - ry * (1 - 0.552), x + width, y + height - ry);
+ i++;
+ if(ry < height / 2)
+ {
+ m_storage.line_to(x + width, y + ry);
+ i++;
+ }
+ m_storage.curve4(x + width, y + ry * (1 - 0.552), x + width - rx * (1 - 0.552), y, x + width - rx, y);
+ i++;
+ if(rx < width / 2)
+ {
+ m_storage.line_to(x + rx, y);
+ i++;
+ }
+ }
+ m_storage.close_polygon();
+ }
+ //if(m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(vec, m_rect, screenCTM, &m_fillSVP);
+}
+
+// #####
+
+AggEllipse::AggEllipse(AggCanvas *c, SVGEllipseElementImpl *ellipse)
+: AggShape(c, ellipse), m_ellipse(ellipse)
+{
+ init();
+}
+
+void AggEllipse::draw()
+{
+ if(isVisible())
+ AggShape::draw(m_ellipse);
+}
+
+bool AggEllipse::isVisible()
+{
+ // Spec: dont render when rx and/or ry is zero
+ return AggShape::isVisible(m_ellipse) && m_ellipse->rx()->baseVal()->value() > 0 && m_ellipse->ry()->baseVal()->value() > 0;
+}
+
+void AggEllipse::init()
+{
+ init(m_ellipse->screenCTM());
+}
+
+void AggEllipse::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ double rx = m_ellipse->rx()->baseVal()->value();
+ double ry = m_ellipse->ry()->baseVal()->value();
+ double cx = m_ellipse->cx()->baseVal()->value();
+ double cy = m_ellipse->cy()->baseVal()->value();
+
+ agg::ellipse ell(cx, cy, rx, ry, 100);
+ ell.rewind(0);
+ double x, y;
+ unsigned int cmd;
+ while((cmd = ell.vertex(&x, &y)) != agg::path_cmd_stop)
+ m_storage.add_vertex(x, y, cmd);
+ m_storage.close_polygon();
+ }
+ //if(m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(vec2, m_ellipse, screenCTM, &m_fillSVP);
+}
+
+// #####
+
+AggCircle::AggCircle(AggCanvas *c, SVGCircleElementImpl *circle)
+: AggShape(c, circle), m_circle(circle)
+{
+ init();
+}
+
+void AggCircle::draw()
+{
+ if(isVisible())
+ AggShape::draw(m_circle);
+}
+
+bool AggCircle::isVisible()
+{
+ // Spec: a value of zero disables rendering
+ return AggShape::isVisible(m_circle) && m_circle->r()->baseVal()->value() > 0;
+}
+
+void AggCircle::init()
+{
+ init(m_circle->screenCTM());
+}
+
+void AggCircle::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ double r = m_circle->r()->baseVal()->value();
+ double cx = m_circle->cx()->baseVal()->value();
+ double cy = m_circle->cy()->baseVal()->value();
+
+ agg::ellipse ell(cx, cy, r, r, 100);
+ ell.rewind(0);
+ double x, y;
+ unsigned int cmd;
+ while((cmd = ell.vertex(&x, &y)) != agg::path_cmd_stop)
+ m_storage.add_vertex(x, y, cmd);
+ m_storage.close_polygon();
+ }
+ //if(m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(vec2, m_ellipse, screenCTM, &m_fillSVP);
+}
+
+// #####
+
+AggLine::AggLine(AggCanvas *c, SVGLineElementImpl *line)
+: AggShape(c, line), MarkerHelper(), m_line(line)
+{
+ init();
+}
+
+AggLine::~AggLine()
+{
+}
+
+void AggLine::draw()
+{
+ if(isVisible())
+ {
+ // transform ( zoom?)
+ //agg::trans_affine transform = m_transform;
+ //agg::trans_affine mtx(m_canvas->zoom(), 0, 0, m_canvas->zoom(), m_canvas->pan().x(), m_canvas->pan().y());
+ //m_transform *= mtx;
+ //m_curved.approximation_scale(pow(m_transform.scale(), 0.75));
+
+ if(m_style->isStroked())
+ {
+ m_canvas->m_ras.reset();
+ m_canvas->m_ras.add_path(m_curved_stroked_trans);
+ QColor qcolor;
+ if(m_style->getStrokeColor()->paintType() == SVG_PAINTTYPE_CURRENTCOLOR)
+ qcolor = m_style->getColor()->rgbColor().color();
+ else
+ qcolor = m_style->getStrokeColor()->rgbColor().color();
+ agg::rgba8 color(qcolor.red(), qcolor.green(), qcolor.blue());
+ color.opacity(m_style->getStrokeOpacity() * m_style->getOpacity());
+ renderPathSolid(m_canvas, color);
+ }
+ //m_transform = transform;
+
+ if(m_line->hasMarkers())
+ {
+ double x1 = m_line->x1()->baseVal()->value();
+ double y1 = m_line->y1()->baseVal()->value();
+ double x2 = m_line->x2()->baseVal()->value();
+ double y2 = m_line->y2()->baseVal()->value();
+ double slope = SVGAngleImpl::todeg(atan2(y2 - y1, x2 - x1));
+
+ if(m_line->hasStartMarker())
+ doStartMarker(m_line, m_line, x1, y1, slope);
+ if(m_line->hasEndMarker())
+ doEndMarker(m_line, m_line, x2, y2, slope);
+ }
+ }
+}
+
+bool AggLine::isVisible()
+{
+ return AggShape::isVisible(m_line);
+}
+
+void AggLine::init()
+{
+ init(m_line->screenCTM());
+}
+
+void AggLine::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ m_storage.move_to(m_line->x1()->baseVal()->value(), m_line->y1()->baseVal()->value());
+ // A subpath consisting of a moveto and lineto to the same exact location or a subpath consisting of a moveto
+ // and a closepath will be stroked only if the 'stroke-linecap' property is set to "round", producing a circle
+ // centered at the given point.
+ double x2 = m_line->x2()->baseVal()->value();
+ double y2 = m_line->y2()->baseVal()->value();
+ if(x2 == m_line->x1()->baseVal()->value() && y2 == m_line->y1()->baseVal()->value() && m_line->getCapStyle() == PATH_STROKE_CAP_ROUND)
+ m_storage.line_to(x2 + .5, y2);
+ else
+ m_storage.line_to(x2, y2);
+ }
+ //if(m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(polyline, m_poly, screenCTM, &m_fillSVP);
+}
+
+// #####
+AggPoly::AggPoly(AggCanvas *c, SVGPolyElementImpl *poly)
+: AggShape(c, poly), MarkerHelper(), m_poly(poly)
+{
+}
+
+AggPoly::~AggPoly()
+{
+}
+
+void AggPoly::init()
+{
+ init(m_poly->screenCTM());
+}
+
+void AggPoly::draw()
+{
+ if(isVisible())
+ {
+ AggShape::draw(m_poly);
+
+ if(m_poly->hasMarkers())
+ m_poly->drawMarkers();
+ }
+}
+
+bool AggPoly::isVisible()
+{
+ return AggShape::isVisible(m_poly);
+}
+
+// #####
+AggPolyline::AggPolyline(AggCanvas *c, SVGPolylineElementImpl *poly)
+: AggPoly(c, poly)
+{
+ AggPoly::init();
+}
+
+AggPolyline::~AggPolyline()
+{
+}
+
+void AggPolyline::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ unsigned int numberOfPoints = m_poly->points()->numberOfItems();
+
+ if(numberOfPoints < 1)
+ return;
+
+ m_storage.move_to(m_poly->points()->getItem(0)->x(), m_poly->points()->getItem(0)->y());
+
+ // A subpath consisting of a moveto and lineto to the same exact location or a subpath consisting of a moveto
+ // and a closepath will be stroked only if the 'stroke-linecap' property is set to "round", producing a circle
+ // centered at the given point.
+ if(numberOfPoints == 2)
+ {
+ double x1 = m_poly->points()->getItem(1)->x();
+ double y1 = m_poly->points()->getItem(1)->y();
+ if(x1 == m_poly->points()->getItem(0)->x() && y1 == m_poly->points()->getItem(0)->y() && m_poly->getCapStyle() == PATH_STROKE_CAP_ROUND)
+ m_storage.line_to(m_poly->points()->getItem(1)->x() + .5, m_poly->points()->getItem(1)->y());
+ else
+ m_storage.line_to(m_poly->points()->getItem(1)->x(), m_poly->points()->getItem(1)->y());
+ }
+ else
+ {
+ unsigned int index;
+ for(index = 1; index < numberOfPoints; index++)
+ m_storage.line_to(m_poly->points()->getItem(index)->x(), m_poly->points()->getItem(index)->y());
+ }
+ }
+ //if(m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(polyline, m_poly, screenCTM, &m_fillSVP);
+}
+
+// #####
+
+AggPolygon::AggPolygon(AggCanvas *c, SVGPolygonElementImpl *poly)
+: AggPoly(c, poly)
+{
+ AggPoly::init();
+}
+
+AggPolygon::~AggPolygon()
+{
+}
+
+void AggPolygon::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ int numberOfPoints = m_poly->points()->numberOfItems();
+
+ if(numberOfPoints < 1)
+ return;
+
+ m_storage.move_to(m_poly->points()->getItem(0)->x(), m_poly->points()->getItem(0)->y());
+
+ int index;
+ for(index = 1; index < numberOfPoints; index++)
+ m_storage.line_to(m_poly->points()->getItem(index)->x(), m_poly->points()->getItem(index)->y());
+
+ m_storage.line_to(m_poly->points()->getItem(0)->x(), m_poly->points()->getItem(0)->y());
+ m_storage.close_polygon();
+ }
+ //if(m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(polygon, m_poly, screenCTM, &m_fillSVP);
+}
+
+// #####
+
+AggPath::AggPath(AggCanvas *c, SVGPathElementImpl *path)
+: AggShape(c, path), SVGPathParser(), MarkerHelper(), m_path(path)
+{
+ init();
+}
+
+AggPath::~AggPath()
+{
+}
+
+void AggPath::draw()
+{
+ AggShape::draw(m_path);
+
+ if(m_path->hasMarkers())
+ {
+ SVGPathElementImpl::MarkerData markers = m_path->markerData();
+ int numMarkers = markers.numMarkers();
+
+ if(m_path->hasStartMarker())
+ doStartMarker(m_path, m_path, markers.marker(0).x, markers.marker(0).y, markers.marker(0).angle);
+
+ for(int i = 1; i < numMarkers - 1; i++)
+ {
+ if(m_path->hasMidMarker())
+ doMidMarker(m_path, m_path, markers.marker(i).x, markers.marker(i).y, markers.marker(i).angle);
+ }
+
+ if(m_path->hasEndMarker())
+ doEndMarker(m_path, m_path, markers.marker(numMarkers - 1).x, markers.marker(numMarkers - 1).y, markers.marker(numMarkers - 1).angle);
+ }
+}
+
+bool AggPath::isVisible()
+{
+ return AggShape::isVisible(m_path);
+}
+
+void AggPath::init()
+{
+ init(m_path->screenCTM());
+}
+
+void AggPath::init(const SVGMatrixImpl *screenCTM)
+{
+ AggShape::init();
+ if(m_storage.total_vertices() == 0)
+ {
+ if(!m_path->getAttribute("d").string().isEmpty())
+ {
+ m_storage.start_new_path();
+ parseSVG(m_path->getAttribute("d").string(), true);
+
+ // A subpath consisting of a moveto and lineto to the same exact location or a subpath consisting of a moveto
+ // and a closepath will be stroked only if the 'stroke-linecap' property is set to "round", producing a circle
+ // centered at the given point.
+ if(m_storage.total_vertices() == 2 && agg::is_line_to(m_storage.command(1)))
+ {
+ double x1, y1;
+ double x2, y2;
+ m_storage.vertex(0, &x1, &y1);
+ m_storage.vertex(1, &x2, &y2);
+ if(x1 == x2 && y1 == y2 && m_path->getCapStyle() == PATH_STROKE_CAP_ROUND)
+ m_storage.modify_vertex(1, x2 + .5, y2);
+ }
+ // TODO : handle filled paths that are not closed explicitly
+ }
+ }
+
+ // There are pure-moveto paths which reference paint servers *bah*
+ // Do NOT render them
+ bool dontrender = m_storage.total_vertices() == 1 && agg::is_move_to((*m_storage.begin()).cmd);
+ if(!dontrender && m_context == NORMAL)
+ calcSVPs(screenCTM);
+ //else
+ // calcClipSVP(ksvg_art_bez_path_to_vec(m_array.data(), 0.25), m_path, screenCTM, &m_fillSVP);
+}
+
+void AggPath::svgMoveTo(double x1, double y1, bool, bool)
+{
+ m_storage.move_to(x1, y1);
+}
+
+void AggPath::svgLineTo(double x1, double y1, bool)
+{
+ m_storage.line_to(x1, y1);
+}
+
+void AggPath::svgCurveToCubic(double x1, double y1, double x2, double y2, double x3, double y3, bool)
+{
+ m_storage.curve4(x1, y1, x2, y2, x3, y3);
+}
+
+void AggPath::svgClosePath()
+{
+ m_storage.close_polygon();
+}
+
+// #####
+
+AggMarker::AggMarker(AggCanvas *c, SVGMarkerElementImpl *marker)
+: CanvasMarker(marker), m_canvas(c)//, m_clippingRectangle(0)
+{
+}
+
+AggMarker::~AggMarker()
+{
+ //if(m_clippingRectangle)
+ // art_svp_free(m_clippingRectangle);
+}
+
+void AggMarker::init()
+{
+}
+
+void AggMarker::draw()
+{
+}
+
+void AggMarker::draw(SVGShapeImpl * /*obj*/, int /*x*/, int /*y*/, float /*lwidth*/, double /*angle*/)
+{
+}
+
+// #####
+
+AggImage::AggImage(AggCanvas *c, SVGImageElementImpl *image)
+: m_canvas(c), m_image(image)
+{
+}
+
+AggImage::~AggImage()
+{
+}
+
+void AggImage::draw()
+{
+ if(isVisible())
+ {
+ //KSVGPolygon clippingPolygon = m_image->clippingShape();
+
+ QImage *img = m_image->image();
+ if(!img) return;
+ QImage image = m_image->scaledImage();
+ agg::rendering_buffer source_buffer;
+ source_buffer.attach(image.bits(), image.width(), image.height(), image.width() * 4);
+
+ typedef agg::pixfmt_rgb24 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+
+ pixfmt pixf(m_canvas->buf());
+ renderer_base rb(pixf);
+
+ typedef agg::span_interpolator_linear<> interpolator_type;
+ typedef agg::span_image_filter_rgba32_bilinear<agg::order_bgra32, interpolator_type> span_gen_type;
+ typedef agg::renderer_scanline_u<renderer_base, span_gen_type> renderer_type;
+ SVGMatrixImpl *ctm = m_image->scaledImageMatrix();
+ agg::trans_affine img_mtx(ctm->a(), ctm->b(), ctm->c(), ctm->d(), ctm->e(), ctm->f());
+ kdDebug() << "ctm->e() : " << ctm->e() << endl;
+ kdDebug() << "ctm->f() : " << ctm->f() << endl;
+ double x1 = 0;
+ double y1 = 0;
+ double x2 = image.width();
+ double y2 = image.height();
+ img_mtx.transform(&x1, &y1);
+ img_mtx.transform(&x2, &y2);
+ img_mtx.invert();
+
+ interpolator_type interpolator(img_mtx);
+ agg::span_allocator<agg::rgba8> sa;
+ span_gen_type sg(sa, source_buffer, agg::rgba(1, 1, 1, 0), interpolator);
+ renderer_type ri(rb, sg);
+
+ agg::scanline_u8 sl;
+
+ //rb.reset_clipping(true);
+ // Clip image against buffer
+ agg::path_storage viewp;
+ viewp.move_to(x1, y1);
+ viewp.line_to(x1, y2);
+ viewp.line_to(x2, y2);
+ viewp.line_to(x2, y1);
+ viewp.close_polygon();
+ m_canvas->m_ras.add_path(viewp);
+ m_canvas->m_ras.render(sl, ri);
+
+ ctm->deref();
+ }
+}
+
+bool AggImage::isVisible()
+{
+ return (m_referenced || (m_image->getVisible() && m_image->getDisplay() && m_image->directRender())) && m_image->image();
+}
+
+void AggImage::init()
+{
+}
+
+QRect AggImage::bbox() const
+{
+ QRect bbox(static_cast<int>(m_image->x()->baseVal()->value()),
+ static_cast<int>(m_image->y()->baseVal()->value()),
+ static_cast<int>(m_image->width()->baseVal()->value()),
+ static_cast<int>(m_image->height()->baseVal()->value()));
+
+
+ return SVGHelperImpl::fromUserspace(m_image, bbox);
+}
+
+// #####
+
+AggText::AggText(AggCanvas *c, SVGTextElementImpl *text)
+: CanvasText(text), m_canvas(c)
+{
+ init();
+ m_drawItems.setAutoDelete(true);
+}
+
+AggText::~AggText()
+{
+}
+
+bool AggText::fillContains(const QPoint &p)
+{
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *fill = it.current();
+ while(fill)
+ {
+ if(fill->svp)
+ {
+ agg::rasterizer_scanline_aa<> ras;
+ ras.filling_rule(fill->element->getFillRule() == RULE_EVENODD ? agg::fill_even_odd : agg::fill_non_zero);
+ ras.add_path(fill->svp->m_curved_trans);
+ if(ras.hit_test(p.x(), p.y()))
+ return true;
+ }
+
+ fill = ++it;
+ }
+
+ return false;
+}
+
+bool AggText::strokeContains(const QPoint &p)
+{
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *stroke = it.current();
+ while(stroke)
+ {
+ if(stroke->svp)
+ {
+ agg::rasterizer_scanline_aa<> ras;
+ ras.filling_rule(stroke->element->getFillRule() == RULE_EVENODD ? agg::fill_even_odd : agg::fill_non_zero);
+ ras.add_path(stroke->svp->m_curved_stroked_trans);
+ if(ras.hit_test(p.x(), p.y()))
+ return true;
+ }
+
+ stroke = ++it;
+ }
+
+ return false;
+}
+
+QRect AggText::bbox() const
+{
+ QRect result, rect;
+
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *elem = it.current();
+ while(elem)
+ {
+ double x1, y1, x2, y2;
+ if(elem && elem->svp)
+ {
+ if(agg::bounding_rect(elem->svp->m_curved_trans, *this, 0, 1, &x1, &y1, &x2, &y2))
+ {
+ rect.setX(int(x1));
+ rect.setY(int(y1));
+ rect.setWidth(int(x2 - x1));
+ rect.setHeight(int(y2 - y1));
+
+ result = result.unite(rect);
+ }
+ }
+ elem = ++it;
+ }
+ return result;
+}
+
+void AggText::update(CanvasItemUpdate reason, int param1, int param2)
+{
+ if(reason == UPDATE_STYLE)
+ {
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *svpelement = it.current();
+ SVGTextContentElementImpl *text;
+ while(svpelement)
+ {
+ text = svpelement->element;
+ if(svpelement->fillPainter)
+ svpelement->fillPainter->update(text);
+ if(svpelement->strokePainter)
+ svpelement->strokePainter->update(text);
+
+ svpelement = ++it;
+ }
+ m_canvas->invalidate(this, false);
+ }
+ else if(reason == UPDATE_TRANSFORM)
+ {
+ clearCurved();
+ init();
+ m_canvas->invalidate(this, true);
+ }
+ else if(reason == UPDATE_ZOOM)
+ {
+ clearCurved();
+ init();
+ }
+ else if(reason == UPDATE_PAN)
+ {
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *svpelement = it.current();
+ T2P::BezierPathAgg *bpath;
+ while(svpelement)
+ {
+ bpath = svpelement->svp;
+ agg::trans_affine mtx(1, 0, 0, 1, param1, param2);
+ bpath->m_transform *= mtx;
+
+ svpelement = ++it;
+ }
+ }
+ /*
+ else if(reason == UPDATE_LINEWIDTH)
+ {
+ }*/
+}
+
+void AggText::draw()
+{
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *svpelement = it.current();
+ BezierPathAggStroked *bpath;
+ SVGTextContentElementImpl *text;
+
+ while(svpelement)
+ {
+ bpath = svpelement->svp;
+ text = svpelement->element;
+ if(!text->getVisible() || !text->getDisplay() || !text->directRender())
+ return;
+
+ bpath->m_curved.approximation_scale(pow(bpath->m_transform.scale(), 1.25));
+
+ if(svpelement->fillPainter)
+ svpelement->fillPainter->draw(m_canvas, bpath->m_curved_trans, text, text);
+ if(svpelement->strokePainter)
+ svpelement->strokePainter->draw(m_canvas, bpath->m_curved_stroked_trans, text, text);
+
+ svpelement = ++it;
+ }
+}
+
+bool AggText::isVisible()
+{
+ bool foundVisible = false;
+ QPtrListIterator<SVPElement> it(m_drawItems);
+
+ SVPElement *svpelement = it.current();
+ SVGTextContentElementImpl *text;
+
+ while(svpelement)
+ {
+ text = svpelement->element;
+ if(text->getVisible() && text->getDisplay() && text->directRender())
+ {
+ foundVisible = true;
+ break;
+ }
+
+ svpelement = ++it;
+ }
+
+ return foundVisible;
+}
+
+void AggText::init()
+{
+ init(m_text->screenCTM());
+}
+
+void AggText::renderCallback(SVGTextContentElementImpl *element, const SVGMatrixImpl *screenCTM, T2P::GlyphSet *glyph, T2P::GlyphLayoutParams *params, double anchor) const
+{
+ for(unsigned int i = 0; i < glyph->glyphCount(); i++)
+ {
+ T2P::GlyphAffinePair *glyphAffine = glyph->set().at(i);
+ T2P::BezierPathAgg *bpath = const_cast<T2P::BezierPathAgg *>(static_cast<const T2P::BezierPathAgg *>(glyphAffine->transformatedPath()));
+
+ // text-anchor/baseline-shift support
+ if(!params->tb())
+ bpath->m_transform = agg::trans_affine(screenCTM->a(), screenCTM->b(), screenCTM->c(), screenCTM->d(), screenCTM->e() - anchor * screenCTM->a(), screenCTM->f());
+ else
+ bpath->m_transform = agg::trans_affine(screenCTM->a(), screenCTM->b(), screenCTM->c(), screenCTM->d(), screenCTM->e(), screenCTM->f() - anchor * screenCTM->d());
+
+ SVPElement *svpelement = new SVPElement();
+ svpelement->svp = new BezierPathAggStroked(*bpath, element);
+ svpelement->element = element;
+
+ if(element->isFilled())
+ svpelement->fillPainter = new AggFillPaintServer(element);
+
+ // Spec: A zero value causes no stroke to be painted.
+ if(element->isStroked() && element->getStrokeWidth()->baseVal()->value() > 0)
+ svpelement->strokePainter = new AggStrokePaintServer(element);
+ m_drawItems.append(svpelement);
+ }
+}
+
+void AggText::init(const SVGMatrixImpl *screenCTM)
+{
+ int curx = 0, cury = 0, endx = 0, endy = 0;
+ KSVGTextChunk *textChunk = CanvasText::createTextChunk(m_canvas, screenCTM, curx, cury, endx, endy);
+
+ if(textChunk->count() > 0)
+ CanvasText::createGlyphs(textChunk, m_canvas, screenCTM, curx, cury, endx, endy);
+
+ delete textChunk;
+}
+
+void AggText::clearCurved()
+{
+ m_drawItems.clear();
+ // TODO: Huh - nobody does anything with the *trans* objects?:
+}
+
+void AggText::addTextDecoration(SVGTextContentElementImpl *element, double x, double y, double width, double height) const
+{
+ if(element->isFilled() || element->isStroked())
+ {
+ // compute rect
+ BezierPathAggStroked *bpath = new BezierPathAggStroked(element);
+ bpath->m_storage.move_to(x, y);
+ bpath->m_storage.line_to(x + width, y);
+ bpath->m_storage.line_to(x + width, y + height);
+ bpath->m_storage.line_to(x, y + height);
+
+ const SVGMatrixImpl *mat = m_text->screenCTM();
+ bpath->m_transform = agg::trans_affine(mat->a(), mat->b(), mat->c(), mat->d(), mat->e(), mat->f());
+
+ SVPElement *svpelement = new SVPElement();
+ svpelement->svp = bpath;
+ svpelement->element = element;
+
+ if(element->isFilled())
+ svpelement->fillPainter = new AggFillPaintServer(element);
+
+ // Spec: A zero value causes no stroke to be painted.
+ if(element->isStroked() && element->getStrokeWidth()->baseVal()->value() > 0)
+ svpelement->strokePainter = new AggStrokePaintServer(element);
+
+ m_drawItems.append(svpelement);
+ }
+}
+
+AggText::SVPElement::~SVPElement()
+{
+ delete svp;
+ delete fillPainter;
+ delete strokePainter;
+}
+
+// ###
+
+AggGradient::AggGradient(SVGGradientElementImpl *gradient) : m_gradient(gradient)
+{
+}
+
+void AggGradient::parseGradientStops(SVGGradientElementImpl *gradient)
+{
+ bool srgb = m_gradient->getColorInterpolation() == CI_SRGB;
+ int r = 0, g = 0, b = 0, a = 255, r1 = 0, g1 = 0, b1 = 0, a1 = 255;
+ unsigned int end = 255;
+ float oldOffset = -1, newOffset = -1;
+ for(DOM::Node node = gradient->firstChild(); !node.isNull(); node = node.nextSibling())
+ {
+ SVGStopElementImpl *elem = dynamic_cast<SVGStopElementImpl *>(gradient->ownerDoc()->getElementFromHandle(node.handle()));
+ if(node.nodeName() == "stop" && elem)
+ {
+ oldOffset = newOffset;
+ newOffset = elem->offset()->baseVal();
+
+ // Spec: skip double offset specifications
+ if(oldOffset == newOffset)
+ continue;
+
+ //offsets++;
+
+ // Get color
+ QColor qStopColor;
+
+ if(elem->getStopColor()->colorType() == SVG_COLORTYPE_CURRENTCOLOR)
+ qStopColor = elem->getColor()->rgbColor().color();
+ else
+ qStopColor = elem->getStopColor()->rgbColor().color();
+
+ // Convert in an agg suitable form
+ QString tempName = qStopColor.name();
+ const char *str = tempName.latin1();
+
+ // We need to take into account fill/stroke opacity, if available (Rob)
+ float opacity = 1.0;
+ SVGStylableImpl *style = dynamic_cast<SVGStylableImpl *>(getBBoxTarget());
+ if(style)
+ opacity = style->getFillOpacity() * style->getOpacity();
+ int stopColor = 0;
+
+ for(int i = 1; str[i]; i++)
+ {
+ int hexval;
+ if(str[i] >= '0' && str[i] <= '9')
+ hexval = str[i] - '0';
+ else if (str[i] >= 'A' && str[i] <= 'F')
+ hexval = str[i] - 'A' + 10;
+ else if (str[i] >= 'a' && str[i] <= 'f')
+ hexval = str[i] - 'a' + 10;
+ else
+ break;
+
+ stopColor = (stopColor << 4) + hexval;
+ }
+
+ // Apply stop-opacity
+ opacity *= elem->stopOpacity();
+
+ // Get rgba color including stop-opacity
+ Q_UINT32 rgba = (stopColor << 8) | int(floor(int(opacity * 255.0) + 0.5));
+
+ // Convert from separated to premultiplied alpha
+ a = rgba & 0xff;
+ r = !srgb ? linearRGBFromsRGB((rgba >> 24)) : (rgba >> 24);
+ g = !srgb ? linearRGBFromsRGB(((rgba >> 16) & 0xff)) : (rgba >> 16) & 0xff;
+ b = !srgb ? linearRGBFromsRGB(((rgba >> 8) & 0xff)) : (rgba >> 8) & 0xff;
+
+ end = int(newOffset * 255);
+ // interpolate
+ unsigned int start = (oldOffset == -1) ? 0 : int(oldOffset * 255);
+ if(oldOffset == -1)
+ {
+ r1 = r;
+ g1 = g;
+ b1 = b;
+ a1 = a;
+ }
+ int diffr = r - r1;
+ int diffg = g - g1;
+ int diffb = b - b1;
+ int diffa = a - a1;
+ unsigned int nsteps = end - start;
+ for(unsigned int i = 0;i <= nsteps;i++)
+ {
+ double diff = double(i) / double(nsteps);
+ m_colorprofile[start + i].r = !srgb ? sRGBFromLinearRGB(int(r1 + diff * diffr)) : int(r1 + diff * diffr);
+ m_colorprofile[start + i].g = !srgb ? sRGBFromLinearRGB(int(g1 + diff * diffg)) : int(g1 + diff * diffg);
+ m_colorprofile[start + i].b = !srgb ? sRGBFromLinearRGB(int(b1 + diff * diffb)) : int(b1 + diff * diffb);
+ m_colorprofile[start + i].a = !srgb ? sRGBFromLinearRGB(int(a1 + diff * diffa)) : int(a1 + diff * diffa);
+ }
+ r1 = r;
+ g1 = g;
+ b1 = b;
+ a1 = a;
+ }
+ }
+ // last section
+ for(unsigned int i = end;i <= 255;i++)
+ {
+ m_colorprofile[i].r = r;
+ m_colorprofile[i].g = g;
+ m_colorprofile[i].b = b;
+ m_colorprofile[i].a = a;
+ }
+}
+
+void AggGradient::finalizePaintServer()
+{
+ parseGradientStops(m_gradient->stopsSource());
+
+ QString _href = SVGURIReferenceImpl::getTarget(m_gradient->href()->baseVal().string());
+ if(!_href.isEmpty())
+ reference(_href);
+
+ setFinalized();
+}
+
+void AggGradient::reference(const QString &/*href*/)
+{
+}
+
+void AggLinearGradient::render(AggCanvas *c)
+{
+ SVGLinearGradientElementImpl *linear = dynamic_cast<SVGLinearGradientElementImpl *>(m_gradient);
+
+ linear->converter()->finalize(getBBoxTarget(), linear->ownerSVGElement(), linear->gradientUnits()->baseVal());
+
+ double _x1 = linear->x1()->baseVal()->value();
+ double _y1 = linear->y1()->baseVal()->value();
+ double _x2 = linear->x2()->baseVal()->value();
+ double _y2 = linear->y2()->baseVal()->value();
+
+ // Adjust to gradient transform
+ SVGMatrixImpl *gradTrans = linear->gradientTransform()->baseVal()->concatenate();
+ if(gradTrans)
+ {
+ QWMatrix m = gradTrans->qmatrix();
+ m.map(_x1, _y1, &_x1, &_y1);
+ m.map(_x2, _y2, &_x2, &_y2);
+ gradTrans->deref();
+ }
+
+ // Get the basic bbox that will be the rendering area
+ SVGRectImpl *userBBox = getBBoxTarget()->getBBox();
+
+ // Compute x1, y1, x2 and y2
+ bool objectbbox = (linear->gradientUnits()->baseVal() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
+
+ // Respect current transformation matrix (so gradients zoom with...)
+ SVGTransformableImpl *transformable = dynamic_cast<SVGTransformableImpl *>(getBBoxTarget());
+ const SVGMatrixImpl *matrix = 0;
+ if(transformable)
+ matrix = transformable->screenCTM();
+
+ if(objectbbox)
+ {
+ _x1 += userBBox->x();
+ _y1 += userBBox->y();
+ _x2 += userBBox->x();
+ _y2 += userBBox->y();
+ }
+
+ userBBox->deref();
+
+ gradient_polymorphic_wrapper_base* gr_ptr = &m_linPad;
+ if(linear->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REPEAT)
+ gr_ptr = &m_linRepeat;
+ else if(linear->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REFLECT)
+ gr_ptr = &m_linReflect;
+
+ agg::trans_affine mtx_g1;
+ double dx = _x2 - _x1;
+ double dy = _y2 - _y1;
+ double angle = (atan2(dy, dx));
+ mtx_g1 *= agg::trans_affine_rotation(angle);
+ mtx_g1 *= agg::trans_affine_translation(_x1, _y1);
+ mtx_g1 *= agg::trans_affine(matrix->a(), matrix->b(), matrix->c(), matrix->d(), matrix->e(), matrix->f());
+ mtx_g1.invert();
+
+ double len = sqrt(dx * dx + dy * dy);
+ if(len > 0)
+ {
+ typedef agg::span_interpolator_linear<> interpolator_type;
+ typedef agg::span_gradient<agg::rgba8,
+ interpolator_type,
+ gradient_polymorphic_wrapper_base,
+ color_function_profile> gradient_span_gen;
+ typedef agg::span_allocator<gradient_span_gen::color_type> gradient_span_alloc;
+ color_function_profile colors(m_colorprofile);
+ gradient_span_alloc span_alloc;
+ interpolator_type inter(mtx_g1);
+ gradient_span_gen span_gen(span_alloc, inter, *gr_ptr, colors, 0, len);
+
+ agg::scanline_u8 sl;
+
+ if(c->nrChannels() == 3)
+ {
+ typedef agg::pixfmt_rgb24 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_u<renderer_base, gradient_span_gen> renderer_gradient;
+
+ pixfmt pixf(c->buf());
+ renderer_base rb(pixf);
+
+ renderer_gradient r1(rb, span_gen);
+ c->m_ras.render(sl, r1);
+ }
+ else
+ {
+ typedef agg::pixfmt_rgba32 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_u<renderer_base, gradient_span_gen> renderer_gradient;
+
+ pixfmt pixf(c->buf());
+ renderer_base rb(pixf);
+
+ renderer_gradient r1(rb, span_gen);
+ c->m_ras.render(sl, r1);
+ }
+ }
+}
+
+void AggRadialGradient::render(AggCanvas *c)
+{
+ SVGRadialGradientElementImpl *radial = dynamic_cast<SVGRadialGradientElementImpl *>(m_gradient);
+
+ radial->converter()->finalize(getBBoxTarget(), radial->ownerSVGElement(), radial->gradientUnits()->baseVal());
+
+ double _cx = radial->cx()->baseVal()->value();
+ double _cy = radial->cy()->baseVal()->value();
+ double _fx = radial->fx()->baseVal()->value();
+ double _fy = radial->fy()->baseVal()->value();
+ double _r = radial->r()->baseVal()->value();
+
+ // Get the basic bbox that will be the rendering area
+ SVGRectImpl *screenBBox = getBBoxTarget()->getBBoxInternal();
+
+ if(screenBBox->width() == 0 || screenBBox->height() == 0)
+ {
+ screenBBox->deref();
+ return;
+ }
+
+ screenBBox->deref();
+
+ // Compute x1, y1, x2 and y2
+ bool objectbbox = (radial->gradientUnits()->baseVal() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
+
+ SVGRectImpl *userBBox = getBBoxTarget()->getBBox();
+ float width = userBBox->width();
+ float height = userBBox->height();
+
+ if(objectbbox)
+ {
+ _cx += userBBox->x();
+ _cy += userBBox->y();
+ _fx += userBBox->x();
+ _fy += userBBox->y();
+ }
+
+ gradient_polymorphic_wrapper_base* gr_ptr = &m_radialPad;
+ if(radial->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REPEAT)
+ gr_ptr = &m_radialRepeat;
+ else if(radial->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REFLECT)
+ gr_ptr = &m_radialReflect;
+
+ agg::trans_affine mtx_g1;
+
+
+ // Adjust to gradient transform
+ SVGMatrixImpl *gradTrans = radial->gradientTransform()->baseVal()->concatenate();
+ if(gradTrans)
+ {
+ agg::trans_affine mtx;
+ QWMatrix m = gradTrans->qmatrix();
+ mtx = agg::trans_affine(m.m11(), m.m12(), m.m21(), m.m22(), m.dx(), m.dy());
+ gradTrans->deref();
+ mtx_g1 *= mtx;
+ }
+
+ userBBox->deref();
+
+ int diff = int(width - height); // allow slight tolerance
+ if(objectbbox && !(diff > -2 && diff < 2))
+ {
+ // make elliptical or circular depending on bbox aspect ratio
+ float ratioX = (width / height) * sqrt(2);
+ float ratioY = (height / width) * sqrt(2);
+ mtx_g1 *= agg::trans_affine_scaling((width > height) ? sqrt(2) : ratioX, (width > height) ? ratioY :sqrt(2));
+ }
+
+ mtx_g1 *= agg::trans_affine_translation(_cx, _cy);
+
+ // Respect current transformation matrix (so gradients zoom with...)
+ SVGTransformableImpl *transformable = dynamic_cast<SVGTransformableImpl *>(getBBoxTarget());
+ const SVGMatrixImpl *matrix = 0;
+ if(transformable)
+ matrix = transformable->screenCTM();
+
+ if(!matrix)
+ return;
+
+ mtx_g1 *= agg::trans_affine(matrix->a(), matrix->b(), matrix->c(), matrix->d(), matrix->e(), matrix->f());
+
+ mtx_g1.invert();
+
+ typedef agg::span_interpolator_linear<> interpolator_type;
+ typedef agg::span_gradient<agg::rgba8,
+ interpolator_type,
+ gradient_polymorphic_wrapper_base,
+ color_function_profile> gradient_span_gen;
+ typedef agg::span_allocator<gradient_span_gen::color_type> gradient_span_alloc;
+ color_function_profile colors(m_colorprofile);
+ gradient_span_alloc span_alloc;
+ interpolator_type inter(mtx_g1);
+ gradient_span_gen span_gen(span_alloc, inter, *gr_ptr, colors, 0, _r);
+
+ agg::scanline_u8 sl;
+ if(c->nrChannels() == 3)
+ {
+ typedef agg::pixfmt_rgb24 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_u<renderer_base, gradient_span_gen> renderer_gradient;
+
+ pixfmt pixf(c->buf());
+ renderer_base rb(pixf);
+
+ renderer_gradient r1(rb, span_gen);
+ c->m_ras.render(sl, r1);
+ }
+ else
+ {
+ typedef agg::pixfmt_rgba32 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_u<renderer_base, gradient_span_gen> renderer_gradient;
+
+ pixfmt pixf(c->buf());
+ renderer_base rb(pixf);
+
+ renderer_gradient r1(rb, span_gen);
+ c->m_ras.render(sl, r1);
+ }
+}
+
+AggPattern::AggPattern(SVGPatternElementImpl *pattern) : AggPaintServer(), m_pattern(pattern)
+{
+}
+
+void AggPattern::finalizePaintServer()
+{
+ m_pattern->finalizePaintServer();
+ setFinalized();
+}
+
+void AggPattern::reference(const QString &href)
+{
+ m_pattern->reference(href);
+}
+
+void AggPattern::render(AggCanvas *c)
+{
+ SVGPatternElementImpl::Tile tile = m_pattern->createTile(getBBoxTarget());
+
+ if(!tile.image().isNull())
+ {
+ QWMatrix m = tile.screenToTile();
+ double affine[6];
+
+ affine[0] = m.m11();
+ affine[1] = m.m12();
+ affine[2] = m.m21();
+ affine[3] = m.m22();
+ affine[4] = m.dx();
+ affine[5] = m.dy();
+
+ typedef agg::pixfmt_rgb24 pixfmt;
+ typedef agg::renderer_base<pixfmt> renderer_base;
+ typedef agg::renderer_scanline_p_solid<renderer_base> renderer_solid;
+
+ pixfmt pixf(c->buf());
+ renderer_base rb(pixf);
+ renderer_solid rs(rb);
+ //double width = c->buf().width();
+ //double height = c->buf().height();
+
+ typedef agg::span_pattern_rgba32<agg::order_rgba32> span_gen_type;
+ typedef agg::renderer_scanline_u<renderer_base, span_gen_type> renderer_type;
+
+ SVGRectImpl *screenBBox = getBBoxTarget()->getBBoxInternal();
+ double offset_x = affine[4];
+ double offset_y = affine[5];
+ screenBBox->deref();
+ agg::rendering_buffer m_pattern_rbuf;
+ m_pattern_rbuf.attach(tile.image().bits(), tile.image().width(), tile.image().height(), tile.image().width() * 4);
+ agg::span_allocator<agg::rgba8> sa;
+ span_gen_type sg(sa, m_pattern_rbuf, unsigned(offset_x), unsigned(offset_y));
+
+ renderer_type rp(rb, sg);
+
+ agg::scanline_u8 sl;
+ rs.color(agg::rgba(0,0,0));
+ c->m_ras.render(sl, rp);
+ }
+}
+
+// vim:ts=4:noet