You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/ksvg/plugin/backends/agg/AggCanvasItems.cpp

1748 lines
46 KiB

/*
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 <tqimage.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;
}
TQRect AggShape::bbox() const
{
return m_bbox;
}
bool AggShape::fillContains(const TQPoint &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 TQPoint &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 = TQRect(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 = TQRect(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)
{
TQColor 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)
{
TQColor 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);
TQColor 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();
TQImage *img = m_image->image();
if(!img) return;
TQImage 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()
{
}
TQRect AggImage::bbox() const
{
TQRect 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 TQPoint &p)
{
TQPtrListIterator<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 TQPoint &p)
{
TQPtrListIterator<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;
}
TQRect AggText::bbox() const
{
TQRect result, rect;
TQPtrListIterator<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)
{
TQPtrListIterator<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)
{
TQPtrListIterator<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()
{
TQPtrListIterator<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;
TQPtrListIterator<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
TQColor 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
TQString 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
TQ_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());
TQString _href = SVGURIReferenceImpl::getTarget(m_gradient->href()->baseVal().string());
if(!_href.isEmpty())
reference(_href);
setFinalized();
}
void AggGradient::reference(const TQString &/*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)
{
TQWMatrix 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;
TQWMatrix 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 TQString &href)
{
m_pattern->reference(href);
}
void AggPattern::render(AggCanvas *c)
{
SVGPatternElementImpl::Tile tile = m_pattern->createTile(getBBoxTarget());
if(!tile.image().isNull())
{
TQWMatrix 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