diff options
Diffstat (limited to 'ksvg/plugin/backends/agg/AggCanvasItems.cpp')
-rw-r--r-- | ksvg/plugin/backends/agg/AggCanvasItems.cpp | 1747 |
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 |