diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /ksvg/plugin/backends | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'ksvg/plugin/backends')
25 files changed, 6805 insertions, 0 deletions
diff --git a/ksvg/plugin/backends/Makefile.am b/ksvg/plugin/backends/Makefile.am new file mode 100644 index 00000000..eb3c3089 --- /dev/null +++ b/ksvg/plugin/backends/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = libart #agg diff --git a/ksvg/plugin/backends/agg/AggCanvas.cpp b/ksvg/plugin/backends/agg/AggCanvas.cpp new file mode 100644 index 00000000..245d36e1 --- /dev/null +++ b/ksvg/plugin/backends/agg/AggCanvas.cpp @@ -0,0 +1,130 @@ +/* + 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 "AggCanvas.h" +#include "SVGShapeImpl.h" +#include "SVGPaintImpl.h" +#include "SVGPaint.h" +#include "SVGGradientElementImpl.h" +#include "SVGLinearGradientElementImpl.h" +#include "SVGRadialGradientElementImpl.h" +#include "SVGPatternElementImpl.h" +#include "SVGAnimatedLengthImpl.h" +#include <kdebug.h> + +#include "AggCanvasItems.h" +#include "GlyphTracerAgg.h" + +#include "agg_vertex_iterator.h" +#include "agg_bounding_rect.h" + +using namespace KSVG; + +AggCanvas::AggCanvas(unsigned int width, unsigned int height) : KSVGCanvas(width, height) +{ + m_fontContext = new T2P::Converter(new T2P::GlyphTracerAgg()); +} + +void AggCanvas::setRenderBufferSize(int w, int h) +{ + KSVGCanvas::setRenderBufferSize(w, h); + m_buf.attach(m_buffer, m_width, m_height, m_width * m_nrChannels); +} + +void AggCanvas::setBuffer(unsigned char *buffer) +{ + KSVGCanvas::setBuffer(buffer); + m_buf.attach(m_buffer, m_width, m_height, m_width * m_nrChannels); +} + +T2P::BezierPath *AggCanvas::toBezierPath(CanvasItem *item) const +{ + // Only handle AggPath items + return dynamic_cast<AggPath *>(item); +} + +// drawing primitives +CanvasItem *AggCanvas::createRectangle(SVGRectElementImpl *rect) +{ + return new AggRectangle(this, rect); +} + +CanvasItem *AggCanvas::createEllipse(SVGEllipseElementImpl *ellipse) +{ + return new AggEllipse(this, ellipse); +} + +CanvasItem *AggCanvas::createCircle(SVGCircleElementImpl *circle) +{ + return new AggCircle(this, circle); +} + +CanvasItem *AggCanvas::createLine(SVGLineElementImpl *line) +{ + return new AggLine(this, line); +} + +CanvasItem *AggCanvas::createPolyline(SVGPolylineElementImpl *poly) +{ + return new AggPolyline(this, poly); +} + +CanvasItem *AggCanvas::createPolygon(SVGPolygonElementImpl *poly) +{ + return new AggPolygon(this, poly); +} + +CanvasItem *AggCanvas::createPath(SVGPathElementImpl *path) +{ + return new AggPath(this, path); +} + +CanvasItem *AggCanvas::createClipPath(SVGClipPathElementImpl *) +{ + return 0; +} + +CanvasItem *AggCanvas::createImage(SVGImageElementImpl *image) +{ + return new AggImage(this, image); +} + +CanvasItem *AggCanvas::createCanvasMarker(SVGMarkerElementImpl *marker) +{ + return new AggMarker(this, marker); +} + +CanvasItem *AggCanvas::createText(SVGTextElementImpl *text) +{ + return new AggText(this, text); +} + +CanvasPaintServer *AggCanvas::createPaintServer(SVGElementImpl *pserver) +{ + AggPaintServer *result = 0; + if(dynamic_cast<SVGLinearGradientElementImpl *>(pserver)) + result = new AggLinearGradient(dynamic_cast<SVGLinearGradientElementImpl *>(pserver)); + else if(dynamic_cast<SVGRadialGradientElementImpl *>(pserver)) + result = new AggRadialGradient(dynamic_cast<SVGRadialGradientElementImpl *>(pserver)); + else if(dynamic_cast<SVGPatternElementImpl *>(pserver)) + result = new AggPattern(dynamic_cast<SVGPatternElementImpl *>(pserver)); + return result; +} + diff --git a/ksvg/plugin/backends/agg/AggCanvas.h b/ksvg/plugin/backends/agg/AggCanvas.h new file mode 100644 index 00000000..9df7a826 --- /dev/null +++ b/ksvg/plugin/backends/agg/AggCanvas.h @@ -0,0 +1,77 @@ +/* + 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. +*/ + +#ifndef AGGCANVAS_H +#define AGGCANVAS_H + +#include "KSVGCanvas.h" + +#include "agg_basics.h" +#include "agg_rendering_buffer.h" +#include "agg_path_storage.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_scanline_u.h" +#include "agg_renderer_scanline.h" +#include "agg_pixfmt_rgb24.h" + +namespace KSVG +{ + +class AggPaintServer; +class SVGElementImpl; +class AggCanvas : public KSVGCanvas +{ +public: + AggCanvas(unsigned int width, unsigned int height); + + virtual T2P::BezierPath *toBezierPath(CanvasItem *item) const; + + // creating canvas items + virtual CanvasItem *createRectangle(SVGRectElementImpl *rect); + virtual CanvasItem *createEllipse(SVGEllipseElementImpl *ellipse); + virtual CanvasItem *createCircle(SVGCircleElementImpl *circle); + virtual CanvasItem *createLine(SVGLineElementImpl *line); + virtual CanvasItem *createPolyline(SVGPolylineElementImpl *poly); + virtual CanvasItem *createPolygon(SVGPolygonElementImpl *poly); + virtual CanvasItem *createPath(SVGPathElementImpl *path); + virtual CanvasItem *createClipPath(SVGClipPathElementImpl *clippath); + virtual CanvasItem *createImage(SVGImageElementImpl *image); + virtual CanvasItem *createCanvasMarker(SVGMarkerElementImpl *marker); + virtual CanvasItem *createText(SVGTextElementImpl *text); + virtual CanvasPaintServer *createPaintServer(SVGElementImpl *pserver); + + virtual void setRenderBufferSize(int w, int h); + + agg::rendering_buffer &buf() { return m_buf; } + + float zoom() const { return m_zoom; } + + agg::rasterizer_scanline_aa<> m_ras; + +protected: + virtual void setBuffer(unsigned char *buffer); + +protected: + agg::rendering_buffer m_buf; +}; + +}; + +#endif diff --git a/ksvg/plugin/backends/agg/AggCanvasFactory.cpp b/ksvg/plugin/backends/agg/AggCanvasFactory.cpp new file mode 100644 index 00000000..439623ad --- /dev/null +++ b/ksvg/plugin/backends/agg/AggCanvasFactory.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 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 <kdebug.h> + +#include "AggCanvas.h" +#include "AggCanvasFactory.h" + +using namespace KSVG; + +K_EXPORT_COMPONENT_FACTORY(libksvgrendereragg, AggCanvasFactory) + +AggCanvasFactory::AggCanvasFactory() +{ +} + +AggCanvasFactory::~AggCanvasFactory() +{ +} + +QObject *AggCanvasFactory::createObject(QObject *, const char *, const char *, const QStringList &args) +{ + unsigned int width = (*args.at(1)).toUInt(); + unsigned int height = (*args.at(0)).toUInt(); + return new AggCanvas(width, height); +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/agg/AggCanvasFactory.h b/ksvg/plugin/backends/agg/AggCanvasFactory.h new file mode 100644 index 00000000..9fb3ffbf --- /dev/null +++ b/ksvg/plugin/backends/agg/AggCanvasFactory.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 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. +*/ + +#ifndef AGGCANVASFACTORY_H +#define AGGCANVASFACTORY_H + +#include <klibloader.h> + +#include "CanvasItem.h" +#include "KSVGCanvas.h" + +namespace KSVG +{ + +class AggCanvasFactory : public KLibFactory +{ +public: + AggCanvasFactory(); + virtual ~AggCanvasFactory(); + + virtual QObject *createObject(QObject *parent = 0, const char *pname = 0, const char *name = "QObject", const QStringList &args = QStringList()); +}; + +}; + +#endif + +/// vim:ts=4:noet 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 diff --git a/ksvg/plugin/backends/agg/AggCanvasItems.h b/ksvg/plugin/backends/agg/AggCanvasItems.h new file mode 100644 index 00000000..a8c242ec --- /dev/null +++ b/ksvg/plugin/backends/agg/AggCanvasItems.h @@ -0,0 +1,500 @@ +/* + 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. +*/ + +#ifndef AGGCANVASITEMS_H +#define AGGCANVASITEMS_H + +#include "CanvasItem.h" +#include "CanvasItems.h" +#include "BezierPathAgg.h" + +#include "SVGPathElementImpl.h" +#include "SVGPolyElementImpl.h" +#include "SVGLineElementImpl.h" +#include "SVGRectElementImpl.h" +#include "SVGTextElementImpl.h" +#include "SVGImageElementImpl.h" +#include "SVGCircleElementImpl.h" +#include "SVGEllipseElementImpl.h" + +#include "agg_basics.h" +#include "agg_conv_transform.h" +#include "agg_conv_curve.h" +#include "agg_conv_dash.h" +#include "agg_conv_stroke.h" +#include "agg_conv_clip_polygon.h" +#include "agg_span_gradient.h" + +#include "SVGBBoxTarget.h" + +// gradient helpers + +//------------------------------------------------------------------------ +class gradient_polymorphic_wrapper_base +{ +public: + virtual int calculate(int x, int y, int) const = 0; +}; + +template<class GradientF> +class gradient_polymorphic_wrapper : public gradient_polymorphic_wrapper_base +{ +public: + virtual int calculate(int x, int y, int d) const + { + return m_gradient.calculate(x, y, d); + } +private: + GradientF m_gradient; +}; + +class gradient_radial_repeat +{ +public: + int calculate(int x, int y, int d) const + { + return int(sqrt(pow(x, 2) + pow(y, 2))) % d; + } +}; + +class gradient_radial_reflect +{ +public: + int calculate(int x, int y, int d) const + { + int dist = int(sqrt(pow(x, 2) + pow(y, 2))); + if((dist / d) % 2 == 0) + return dist % d; + else + return d - (dist % d); + } +}; + +class gradient_linear_repeat +{ +public: + int calculate(int x, int y, int d) const + { + return (x < 0) ? (d - (-x % d)) : (x % d); + } +}; + +class gradient_linear_reflect +{ +public: + int calculate(int x, int y, int d) const + { + if((abs(x) / d) % 2 == 0) + return (x < 0) ? (-x % d) : (x % d); + else + return (x > 0) ? (d - (x % d)) : (d - (-x % d)); + } +}; + + +#define AGG_CLASS(Class, Type, Member) \ +class Agg##Class : public AggShape \ +{ \ +public: \ + Agg##Class(AggCanvas *c, Type *Member); \ + virtual ~Agg##Class() { } \ + virtual void draw(); \ + virtual bool isVisible(); \ + virtual void init(); \ + virtual void init(const SVGMatrixImpl *screenCTM); \ + virtual SVGElementImpl *element() const { return m_##Member; } \ +protected: \ + Type *m_##Member; \ +}; + +class stroke_dash_base +{ +public: + virtual ~stroke_dash_base() {} + + virtual void rewind(unsigned) = 0; + virtual unsigned vertex(double*, double*) = 0; +}; + +template<class Source> +class stroke : public stroke_dash_base +{ +public: + typedef agg::conv_stroke<Source> stroke_type; + + stroke(Source& src, KSVG::SVGStylableImpl *style); + + void rewind(unsigned id) { m_s.rewind(id); } + unsigned vertex(double* x, double* y) { return m_s.vertex(x, y); } + +private: + stroke_type m_s; +}; + +template<class Source> +class dash_stroke : public stroke_dash_base +{ +public: + typedef agg::conv_dash<Source> dash_type; + typedef agg::conv_stroke<dash_type> dash_stroke_type; + + dash_stroke(Source& src, KSVG::SVGStylableImpl *style); + + void rewind(unsigned id) { m_ds.rewind(id); } + unsigned vertex(double* x, double* y) { return m_ds.vertex(x, y); } + +private: + dash_type m_d; + dash_stroke_type m_ds; +}; + +template<class Source> +class dash_stroke_simple +{ +public: + dash_stroke_simple(Source& src, KSVG::SVGStylableImpl *style); + ~dash_stroke_simple() { delete impl; } + + void rewind(unsigned id) { impl->rewind(id); } + unsigned vertex(double* x, double* y) { return impl->vertex(x, y); } + +private: + stroke_dash_base *impl; +}; + +typedef dash_stroke_simple<curved> curved_stroked; +typedef agg::conv_transform<curved_stroked> curved_stroked_trans; +typedef agg::conv_clip_polygon<curved_stroked_trans> curved_stroked_trans_clipped; +typedef agg::conv_clip_polygon<curved_trans> curved_trans_clipped; +//typedef agg::conv_contour<curved_trans> curved_trans_contour; +//typedef agg::conv_clip_polygon<curved_trans_contour> curved_trans_contour_clipped; + +namespace KSVG +{ + class SVGGradientElementImpl; + class SVGRadialGradientElementImpl; + class SVGLinearGradientElementImpl; + class SVGPatternElementImpl; + + class AggPaintServer : public CanvasPaintServer + { + public: + AggPaintServer() : CanvasPaintServer() {} + virtual ~AggPaintServer() {} + + virtual void finalizePaintServer() = 0; + virtual void reference(const QString &href) = 0; + + virtual void render(AggCanvas *c) = 0; + }; + + class AggGradient : public AggPaintServer + { + public: + AggGradient(SVGGradientElementImpl *gradient); + virtual ~AggGradient() {} + + void parseGradientStops(SVGGradientElementImpl *gradient); + + virtual void finalizePaintServer(); + virtual void reference(const QString &href); + + protected: + SVGGradientElementImpl *m_gradient; + agg::rgba8 m_colorprofile[256]; + }; + + class AggLinearGradient : public AggGradient + { + public: + AggLinearGradient(SVGLinearGradientElementImpl *linear) : AggGradient(linear) {} + + virtual void render(AggCanvas *c); + + protected: + gradient_polymorphic_wrapper<agg::gradient_x> m_linPad; + gradient_polymorphic_wrapper<gradient_linear_repeat> m_linRepeat; + gradient_polymorphic_wrapper<gradient_linear_reflect> m_linReflect; + }; + + class AggRadialGradient : public AggGradient + { + public: + AggRadialGradient(SVGRadialGradientElementImpl *radial) : AggGradient(radial) {} + + virtual void render(AggCanvas *c); + + protected: + gradient_polymorphic_wrapper<agg::gradient_circle> m_radialPad; + gradient_polymorphic_wrapper<gradient_radial_repeat> m_radialRepeat; + gradient_polymorphic_wrapper<gradient_radial_reflect> m_radialReflect; + }; + + class AggPattern : public AggPaintServer + { + public: + AggPattern(SVGPatternElementImpl *pattern); + virtual ~AggPattern() {} + + virtual void finalizePaintServer(); + virtual void reference(const QString &); + + virtual void render(AggCanvas *c); + + protected: + SVGPatternElementImpl *m_pattern; + }; + + class AggCanvas; + + class AggFillPaintServer + { + public: + AggFillPaintServer(SVGStylableImpl *style); + void update(SVGStylableImpl *style); + template<class VertexSource> + void draw(AggCanvas *canvas, VertexSource &vs, SVGStylableImpl *style, SVGShapeImpl *shape); + + private: + agg::rgba8 m_color; + }; + + class AggStrokePaintServer + { + public: + AggStrokePaintServer(SVGStylableImpl *style); + void update(SVGStylableImpl *style); + template<class VertexSource> + void draw(AggCanvas *canvas, VertexSource &vs, SVGStylableImpl *style, SVGShapeImpl *shape); + + private: + agg::rgba8 m_color; + }; + + class BezierPathAggStroked : public T2P::BezierPathAgg + { + public: + BezierPathAggStroked(SVGStylableImpl *style); + BezierPathAggStroked(const T2P::BezierPathAgg &other, SVGStylableImpl *style); + + curved_trans_clipped m_curved_trans_clipped; + curved_stroked m_curved_stroked; + curved_stroked_trans m_curved_stroked_trans; + curved_stroked_trans_clipped m_curved_stroked_trans_clipped; + SVGStylableImpl *m_style; + }; + + class AggShape : public CanvasItem, public BezierPathAggStroked + { + public: + AggShape(AggCanvas *c, SVGStylableImpl *style); + virtual ~AggShape(); + + virtual QRect bbox() const; + virtual bool fillContains(const QPoint &p); + virtual bool strokeContains(const QPoint &p); + virtual void update(CanvasItemUpdate reason, int param1 = 0, int param2 = 0); + void draw(SVGShapeImpl *shape); + void calcSVPs(const SVGMatrixImpl *matrix); + virtual void init(); + virtual void init(const SVGMatrixImpl *); + bool isVisible(SVGShapeImpl *shape); + + void setRenderContext(RenderContext context) { m_context = context; } + + agg::path_storage &storage() { return m_storage; } + agg::trans_affine &transform() { return m_transform; } + + protected: + void freeSVPs(); + + RenderContext m_context; + AggCanvas *m_canvas; + QRect m_bbox; + AggFillPaintServer *m_fillPainter; + AggStrokePaintServer *m_strokePainter; + }; + + AGG_CLASS(Rectangle, SVGRectElementImpl, rect) + AGG_CLASS(Ellipse, SVGEllipseElementImpl, ellipse) + AGG_CLASS(Circle, SVGCircleElementImpl, circle) + + class AggLine : public AggShape, public MarkerHelper + { + public: + AggLine(AggCanvas *c, SVGLineElementImpl *line); + virtual ~AggLine(); + + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM); + + virtual SVGElementImpl *element() const { return m_line; } + + protected: + SVGLineElementImpl *m_line; + }; + + class AggPoly : public AggShape, public MarkerHelper + { + public: + AggPoly(AggCanvas *c, SVGPolyElementImpl *poly); + virtual ~AggPoly(); + + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM) = 0; + + virtual SVGElementImpl *element() const { return m_poly; } + + protected: + SVGPolyElementImpl *m_poly; + + }; + class AggPolyline : public AggPoly + { + public: + AggPolyline(AggCanvas *c, SVGPolylineElementImpl *poly); + virtual ~AggPolyline(); + + virtual void init(const SVGMatrixImpl *screenCTM); + }; + + class AggPolygon : public AggPoly + { + public: + AggPolygon(AggCanvas *c, SVGPolygonElementImpl *poly); + virtual ~AggPolygon(); + + virtual void init(const SVGMatrixImpl *screenCTM); + }; + + class AggPath : public AggShape, public ::SVGPathParser, public MarkerHelper + { + public: + AggPath(AggCanvas *c, SVGPathElementImpl *path); + virtual ~AggPath(); + + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM); + + virtual SVGElementImpl *element() const { return m_path; } + + protected: + SVGPathElementImpl *m_path; + + virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true); + virtual void svgLineTo(double x1, double y1, bool abs = true); + virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true); + virtual void svgClosePath(); + }; + + class AggMarker : public CanvasMarker + { + public: + AggMarker(AggCanvas *c, SVGMarkerElementImpl *marker); + virtual ~AggMarker(); + + virtual QRect bbox() const { return QRect(); } + virtual bool fillContains(const QPoint &) { return true; } + virtual bool strokeContains(const QPoint &) { return true; } + virtual void update(CanvasItemUpdate, int = 0, int = 0) { } + virtual void init(); + virtual void draw(); + virtual bool isVisible() { return false; } + + void draw(SVGShapeImpl *obj, int x, int y, float lwidth = 1.0, double angle = 0.0); + //const ArtSVP *clippingRectangle() const { return m_clippingRectangle; } + + protected: + AggCanvas *m_canvas; + //ArtSVP *m_clippingRectangle; + }; + + class AggImage : public CanvasItem + { + public: + AggImage(AggCanvas *c, SVGImageElementImpl *image); + virtual ~AggImage(); + + virtual QRect bbox() const; + virtual bool fillContains(const QPoint &) { return true; } + virtual bool strokeContains(const QPoint &) { return true; } + virtual void update(CanvasItemUpdate, int = 0, int = 0) { } + virtual void init(); + virtual void draw(); + virtual bool isVisible(); + + virtual SVGElementImpl *element() const { return m_image; } + + protected: + AggCanvas *m_canvas; + SVGImageElementImpl *m_image; + }; + + class AggText : public CanvasText + { + public: + AggText(AggCanvas *c, SVGTextElementImpl *text); + virtual ~AggText(); + + virtual QRect bbox() const; + virtual bool fillContains(const QPoint &p); + virtual bool strokeContains(const QPoint &p); + virtual void update(CanvasItemUpdate, int param1 = 0, int param2 = 0); + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM); + + virtual void renderCallback(SVGTextContentElementImpl *element, const SVGMatrixImpl *screenCTM, T2P::GlyphSet *glyph, T2P::GlyphLayoutParams *params, double anchor) const; + + virtual void addTextDecoration(SVGTextContentElementImpl *element, double x, double y, double w, double h) const; + + unsigned operator [](unsigned) const { return 0; } + + protected: + AggCanvas *m_canvas; + + private: + void clearCurved(); + class SVPElement + { + public: + SVPElement() { svp = 0; element = 0; fillPainter = 0; strokePainter = 0; } + ~SVPElement(); + + BezierPathAggStroked *svp; + SVGTextContentElementImpl *element; + AggFillPaintServer *fillPainter; + AggStrokePaintServer *strokePainter; + }; + + mutable QPtrList<SVPElement> m_drawItems; + }; +}; + +#endif + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/agg/BezierPathAgg.cpp b/ksvg/plugin/backends/agg/BezierPathAgg.cpp new file mode 100644 index 00000000..10a75c1c --- /dev/null +++ b/ksvg/plugin/backends/agg/BezierPathAgg.cpp @@ -0,0 +1,126 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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 <math.h> +#include "Point.h" +#include "BezierPathAgg.h" + +#include <kdebug.h> + +#include <agg_basics.h> +#include <agg_bounding_rect.h> + +using namespace T2P; + +double BezierPathAgg::length(double t) +{ + if(m_length < 0.0) + { + double total = 0.0; + double x = 0.0, y = 0.0; + double x2, y2; + unsigned cmd; + unsigned int id = 0; + m_curved_trans.rewind(id); + while(!agg::is_stop(cmd = m_curved_trans.vertex(&x2, &y2))) + { + if(agg::is_move_to(cmd)) + { + x = x2; + y = y2; + } + else if(agg::is_line_to(cmd)) + { + double dx = x, dy = y; + dx = x2 - dx; + dy = y2 - dy; + total += sqrt(pow(dx, 2) + pow(dy, 2)); + x = x2; + y = y2; + } + } + return total * t; + } + else + return m_length * t; +} + +void BezierPathAgg::pointTangentNormalAt(double t, Point *p, Point *tn, Point *n) +{ + double totallen = length(t); + double total = 0.0; + double x = 0.0, y = 0.0; + double x2, y2; + unsigned cmd; + unsigned int id = 0; + m_curved_trans.rewind(id); + while(!agg::is_stop(cmd = m_curved_trans.vertex(&x2, &y2))) + { + if(agg::is_move_to(cmd)) + { + x = x2; + y = y2; + } + else if(agg::is_line_to(cmd)) + { + double dx = x, dy = y; + x = x2; + y = y2; + dx = x - dx; + dy = y - dy; + double seg_len = sqrt(pow(dx, 2) + pow(dy, 2)); + total += seg_len; + if(total >= totallen) + { + double fract = 1 - (totallen - (total - seg_len)) / seg_len; + if(p) + { + p->setX(x - dx * fract); + p->setY(y - dy * fract); + } + if(tn) + { + //kdDebug() << k_funcinfo << "dx : " << dx << endl; + //kdDebug() << k_funcinfo << "dy : " << dy << endl; + tn->setX(dx); + tn->setY(dy); + } + if(n) + { + // Calculate vector product of "binormal" x tangent + // (0,0,1) x (dx,dy,0), which is simply (dy,-dx,0). + n->setX(dy); + n->setY(-dx); + } + return; + } + } + } +} + +void BezierPathAgg::boundingBox(Point *topLeft, Point *bottomRight) +{ + double x1, y1, x2, y2; + agg::bounding_rect(m_curved, *this, 0, 1, &x1, &y1, &x2, &y2); + *topLeft = Point(x1, y1); + *bottomRight = Point(x2, y2); +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/agg/BezierPathAgg.h b/ksvg/plugin/backends/agg/BezierPathAgg.h new file mode 100644 index 00000000..21451aa9 --- /dev/null +++ b/ksvg/plugin/backends/agg/BezierPathAgg.h @@ -0,0 +1,83 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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 + along 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. +*/ + +#ifndef T2P_BEZIERPATH_AGG_H +#define T2P_BEZIERPATH_AGG_H + +#include <agg_curves.h> +#include <agg_conv_curve.h> +#include <agg_conv_stroke.h> +#include <agg_path_storage.h> +#include <agg_conv_transform.h> + +#include "Affine.h" +#include "BezierPath.h" + +typedef agg::conv_curve<agg::path_storage> curved; +typedef agg::conv_transform<curved> curved_trans; + +namespace T2P +{ + class BezierPathAgg : public BezierPath + { + public: + BezierPathAgg() : BezierPath(), + m_curved(m_storage), m_curved_trans(m_curved, m_transform) + { + m_length = -1; + } + + ~BezierPathAgg() {} + + BezierPathAgg(const BezierPathAgg &other) : BezierPath(), m_storage(other.m_storage), + m_curved(m_storage), m_curved_trans(m_curved, m_transform) + { + m_transform = other.m_transform; + m_length = other.m_length; + } + + void copy_from(const agg::path_storage &ps, Affine &affine) + { + for(unsigned i = 0; i < ps.total_vertices(); i++) + { + double x, y; + unsigned cmd = ps.vertex(i, &x, &y); + m_storage.add_vertex(affine.dx() + x * affine.m11() + y * affine.m21(), + affine.dy() + x * affine.m12() + y * affine.m22(), cmd); + } + } + + unsigned operator [](unsigned) { return 0; } + + virtual double length(double t = 1.0); + virtual void pointTangentNormalAt(double t, Point *p = 0, Point *tn = 0, Point *n = 0); + virtual void boundingBox(Point *topLeft, Point *bottomRight); + + agg::path_storage m_storage; + curved m_curved; + curved_trans m_curved_trans; + agg::trans_affine m_transform; + double m_length; + }; +}; + +#endif + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/agg/GlyphTracerAgg.cpp b/ksvg/plugin/backends/agg/GlyphTracerAgg.cpp new file mode 100644 index 00000000..39b00146 --- /dev/null +++ b/ksvg/plugin/backends/agg/GlyphTracerAgg.cpp @@ -0,0 +1,113 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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 <iostream> + +#include "Glyph.h" +#include "Point.h" +#include "BezierPathAgg.h" +#include "GlyphTracerAgg.h" + +using namespace T2P; + +int traceMoveto(FT_Vector *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathAgg *bpath = static_cast<BezierPathAgg *>(glyph->modifiableBezierPath()); + Point p = affine.mapPoint(Point(to->x, to->y)); + bpath->m_storage.move_to(p.x(), p.y()); + return 0; +} + +int traceLineto(FT_Vector *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathAgg *bpath = static_cast<BezierPathAgg *>(glyph->modifiableBezierPath()); + Point p = affine.mapPoint(Point(to->x, to->y)); + bpath->m_storage.line_to(p.x(), p.y()); + return 0; +} + +int traceConicBezier(FT_Vector *control, FT_Vector *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathAgg *bpath = static_cast<BezierPathAgg *>(glyph->modifiableBezierPath()); + Point c = affine.mapPoint(Point(control->x, control->y)); + Point p = affine.mapPoint(Point(to->x, to->y)); + + double sx, sy; + bpath->m_storage.vertex(bpath->m_storage.total_vertices() - 1, &sx, &sy); + bpath->m_storage.curve4(c.x() - (c.x() - sx) / 3, c.y() - (c.y() - sy) / 3, + c.x() + (p.x() - c.x()) / 3, c.y() + (p.y() - c.y()) / 3, p.x(), p.y()); + + return 0; +} + +int traceCubicBezier(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathAgg *bpath = static_cast<BezierPathAgg *>(glyph->modifiableBezierPath()); + Point p = affine.mapPoint(Point(to->x, to->y)); + Point c1 = affine.mapPoint(Point(control1->x, control1->y)); + Point c2 = affine.mapPoint(Point(control2->x, control2->y)); + bpath->m_storage.curve4(c1.x(), c1.y(), c2.x(), c2.y(), p.x(), p.y()); + return 0; +} + +GlyphTracerAgg::GlyphTracerAgg() : GlyphTracer() +{ + setMoveto(*traceMoveto); + setLineto(*traceLineto); + setConicBezier(*traceConicBezier); + setCubicBezier(*traceCubicBezier); +} + +GlyphTracerAgg::~GlyphTracerAgg() +{ +} + +void GlyphTracerAgg::correctGlyph(GlyphAffinePair *glyphAffine) +{ + + // Take bezier path belonging to glyph (Note: that one is _UNMODIFIABLE_, once calculated) + const BezierPathAgg *path = static_cast<const BezierPathAgg *>(glyphAffine->glyph()->bezierPath()); + + BezierPathAgg *transformatedPath = static_cast<BezierPathAgg *>(allocBezierPath(0)); + Affine &affine = glyphAffine->affine(); + transformatedPath->copy_from(path->m_storage, affine); + glyphAffine->setTransformatedPath(transformatedPath); +} + +BezierPath *GlyphTracerAgg::allocBezierPath(int) +{ + return new BezierPathAgg(); +} + +void GlyphTracerAgg::closePath(Glyph *glyph) +{ + BezierPathAgg *bpath = static_cast<BezierPathAgg *>(glyph->modifiableBezierPath()); + bpath->m_storage.close_polygon(); +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/agg/GlyphTracerAgg.h b/ksvg/plugin/backends/agg/GlyphTracerAgg.h new file mode 100644 index 00000000..5fb24d4b --- /dev/null +++ b/ksvg/plugin/backends/agg/GlyphTracerAgg.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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. +*/ + +#ifndef T2P_GLYPHTRACER_AGG_H +#define T2P_GLYPHTRACER_AGG_H + +#include "GlyphTracer.h" + +// FreeType 2 includes +#include <ft2build.h> +#include FT_FREETYPE_H + +namespace T2P +{ + class Glyph; + class BezierPath; + + class GlyphTracerAgg : public GlyphTracer + { + public: + GlyphTracerAgg(); + ~GlyphTracerAgg(); + + virtual void correctGlyph(GlyphAffinePair *glyphAffine); + virtual BezierPath *allocBezierPath(int size); + virtual void closePath(Glyph *glyph); + }; +}; + +#endif + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/agg/Makefile.am b/ksvg/plugin/backends/agg/Makefile.am new file mode 100644 index 00000000..4e883809 --- /dev/null +++ b/ksvg/plugin/backends/agg/Makefile.am @@ -0,0 +1,15 @@ +kde_module_LTLIBRARIES = libksvgrendereragg.la + +# Damn agg2 has no configuration querying possibility! +AGG_CFLAGS = -I/cvs/agg2/include/ +AGG_LIBS = /cvs/agg2/src/libagg.a + +libksvgrendereragg_la_SOURCES = BezierPathAgg.cpp GlyphTracerAgg.cpp AggCanvas.cpp AggCanvasItems.cpp AggCanvasFactory.cpp +libksvgrendereragg_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +libksvgrendereragg_la_LIBADD = ../../../libksvg.la $(AGG_LIBS) +libksvgrendereragg_la_METASOURCES = AUTO + +kde_services_DATA = ksvgaggcanvas.desktop + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +INCLUDES = $(KDE_INCLUDES) $(AGG_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) -I$(top_srcdir)/ksvg/dom -I$(top_srcdir)/ksvg/impl -I$(top_srcdir)/ksvg/ecma -I$(top_srcdir)/ksvg/core -I$(top_srcdir)/ksvg/impl/libs/art_support -I$(top_srcdir)/ksvg/impl/libs/libtext2path/src $(all_includes) diff --git a/ksvg/plugin/backends/agg/ksvgaggcanvas.desktop b/ksvg/plugin/backends/agg/ksvgaggcanvas.desktop new file mode 100644 index 00000000..ac51836b --- /dev/null +++ b/ksvg/plugin/backends/agg/ksvgaggcanvas.desktop @@ -0,0 +1,101 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KSVG/Renderer +X-KDE-Library=libksvgrendereragg +X-KSVG-InternalName=agg +Name=KSVG Rendering Backend - Anti Grain Geometry +Name[ar]=خلفية رسم ل KSVG - هندسة Anti Grain +Name[bs]=KSVG renderiranje - Anti Grain Geometry +Name[ca]=Representació en segon pla de KSVG - Geometria anti gra +Name[cs]=Vykreslovací nástroj KSVG - Anti Grain Geometry +Name[cy]=Ôl-wyneb Llunio KSVG - Geometreg Wrth-Raen +Name[da]=Underliggende program for KSVG-visning - antikorn geometri +Name[de]=KSVG-Darstellungsmodul - Antikörnungs-Geometrie +Name[el]= Σύστημα υποστήριξης αποτύπωσης του KSVG - Anti Grain Geometry +Name[es]=Motor de procesado de KSVG - Geometría suavizada +Name[et]=KSVG renderdamise taustarakendus - teralisusevastane geomeetria (AGG) +Name[eu]=KSVG errendatze programa - Anti Grain geometriarekin +Name[fa]=پایانۀ پشتیبانی پرداخت KSVG - هندسۀ ضد ذره +Name[fi]=KSVG-piirtäjän taustaohjelma - sileä geometria +Name[fr]=Moteur de rendu KSVG - Anti Grain Geometry +Name[gl]=Mecanismo de Interpretación KSVG - Xeometría Anti-gran +Name[he]=מנוע רינדור KSVG +Name[hi]=के-एसवीजी रेंडरिंग बैकएण्ड- एन्टी ग्रेन ज्यामिती +Name[hu]=KSVG megjelenítő motor - AGG (Anti Grain Geometry) +Name[is]=KSVG teiknari - Anti Grain Geometry +Name[it]=Backend KSVG per il rendering - Geometrie senza sgranature +Name[ja]=KSVG レンダリングバックエンド - Anti Grain Geometry +Name[kk]=KSVG кескіндеу бағдарламасы - қиыршықтыққа қарсы геометриясы +Name[km]=កម្មវិធីសម្រាប់បង្ហាញ KSVG - រាងមិនគ្រើម +Name[lt]=KSVG atkūrimo programinė sąsaja - Anti Grain geometrija +Name[ms]=Tepi Belakang KSVG - Geometri Anti Bijian +Name[nb]=Modul for KSVG-tegning – antikorngeometri +Name[nds]=KSVG-Dorstellhölper - Antigrissel-Geometrie +Name[ne]=KSVG रेन्डरिङ ब्याकइन्ड - एन्टि ग्रेन ज्योमेट्रि +Name[nl]=KSVG weergavecomponent - Anti Grain Geometry +Name[nn]=Modul for KSVG-teikning – antikorngeometri +Name[pl]=Narzędzie do renderowania KSVG - nieziarnista geometria +Name[pt]=Infra-Estrutura de Desenho do KSVG - Geometria Anti-Grão +Name[pt_BR]=Estrutura de Renderização do KSVG - Geometria Anti-Grain +Name[ro]=Motor de randare KSVG - Anti Grain Geometry +Name[ru]=Движок отрисовки KSVG - противозернистая геометрия +Name[sk]=Nástroj pre zobrazovanie KSVG - antigranularitná geometria +Name[sl]=Izrisovalnik KSVG - Protizrnska geometrija +Name[sr]=KSVG-ов позадински систем за рендеровање — Противзрнаста геометрија +Name[sr@Latn]=KSVG-ov pozadinski sistem za renderovanje — Protivzrnasta geometrija +Name[sv]=KSVG-uppritningsmodul - geometri mot korninghet +Name[ta]=KSVG வழங்கும் பின் அமைப்பு - ஆன்டி க்ரேன் ஜியோமெட்ரி +Name[tg]=Лағжандаи тасовироти KSVG - геометрияи мутақобили гандумӣ +Name[tr]=KSVG Derleme Aracı - Taneciksiz Geometri +Name[uk]=Інтерфейс відтворення KSVG - AGG +Name[zh_CN]=KSVG 渲染后端 - 反增长几何形状 +Name[zh_HK]=KSVG 合成後端 - Anti Grain Geometry +Name[zh_TW]=KSVG 上色後端介面 - Anti Grain Geometry +Comment=New, unstable ksvg rendering backend +Comment[bs]=Novi, nestabilni ksvg renderiranje backend +Comment[ca]=Nou, inestable representador en segon pla de ksvg +Comment[cs]=Nový, nestabilní vykreslovací nástroj KSVG +Comment[cy]=Ôl-wyneb llunio ksvg newydd, ansad +Comment[da]=Nyt, ustabilt underliggende program til ksvg-visning +Comment[de]=Neues, unausgereiftes KSVG-Darstellungsmodul +Comment[el]=Νέο υπό ανάπτυξη σύστημα υποστήριξης αποτύπωσης +Comment[es]=Nuevo motor de procesado de ksvg, inestable +Comment[et]=Uus ebastabiilne ksvg renderdamise taustarakendus +Comment[eu]=Berria, ksvg errendatze programa ezegonkorra +Comment[fa]=جدید، پایانۀ پشتیبانی ناپایدار پرداخت ksvg +Comment[fi]=Uusi epävakaa ksvg-piirtäjän taustaohjelma +Comment[fr]=Nouveau moteur de rendu KSVG instable +Comment[gl]=Novo e inestábel mecanismo de interpretación ksvg +Comment[he]=חדש, מנוע רינדור לא יציב עבור KSVG +Comment[hi]=नया, अस्थिर के-एसवीजी रेंडरिंग बैकएण्ड +Comment[hu]=Új, még nem teljesen kiforrott megjelenítőmotor a KSVG-hez +Comment[is]=Nýr og óstöðugur ksvg teiknari +Comment[it]=Nuovo, instabile backend di KSVG per il rendering +Comment[ja]=新しく、まだ開発途上の ksvg レンダリングバックエンド +Comment[kk]=Жаңа, әлі тұрақсыз KSVG кескіндеу бағдарламасы +Comment[km]=កម្មវិធីសម្រាប់បង្ហាញ ksvg ថ្មីតែមិនសូវឋិតថេរ +Comment[lt]=Nauja, nestabili ksvg atkūrimo programinė sąsaja +Comment[ms]=Tepi Belakang Menrealisasi ksvg yang baru dan tidak stabil +Comment[nb]=Ny og ustabil modul for ksvg-tegning +Comment[nds]=Nieg, nich stevig KSVG-Dorstellhölper +Comment[ne]=नयाँ, अस्थिर ksvg रेन्डरिङ ब्याकइन्ड +Comment[nl]=Nieuwe, niet stabiele KSVG weergavecomponent +Comment[nn]=Ny og ustabil modul for ksvg-teikning +Comment[pl]=Nowe, niestabilne, narzędzie do renderowania KSVG +Comment[pt]=Uma infra-estrutura de desenho do ksvg, nova e instável +Comment[pt_BR]=Nova e instável estrutura de renderização do ksvg +Comment[ro]=Motor de randare KSVG nou, netestat suficient +Comment[ru]=Новый, нестабильный движок прорисовки ksvg +Comment[sk]=Nová, nestabilná verzia nástroja pre zobrazovanie ksvg +Comment[sl]=Nov, nestabilen izrisovalnik KSVG +Comment[sr]=Нов, нестабилан KSVG-ов позадински систем за рендеровање +Comment[sr@Latn]=Nov, nestabilan KSVG-ov pozadinski sistem za renderovanje +Comment[sv]=Ny, instabil KSVG-uppritningsmodul +Comment[ta]=புதிய, நிலையில்லாத ksvg வழங்கும் பின் அமைப்பு +Comment[tg]=Лағжандаи ғайриустувори тасовироти ksvg-и нав +Comment[tr]=Yeni, stabil olmayan ksvg derleme aracı +Comment[uk]=Новий, нестабільний інтерфейс відтворення KSVG +Comment[zh_CN]=新的不稳定的 ksvg 渲染后端 +Comment[zh_HK]=新但不穩定的 ksvg 合成後端 +Comment[zh_TW]=新的,不穩定的 ksvg 上色後端介面 +author=Rob Buis <buis@kde.org> diff --git a/ksvg/plugin/backends/libart/BezierPathLibart.cpp b/ksvg/plugin/backends/libart/BezierPathLibart.cpp new file mode 100644 index 00000000..fb163952 --- /dev/null +++ b/ksvg/plugin/backends/libart/BezierPathLibart.cpp @@ -0,0 +1,160 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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 <math.h> +#include "Point.h" +#include "BezierPathLibart.h" + +#include <kdebug.h> + +#include <libart_lgpl/art_bpath.h> +#include <libart_lgpl/art_vpath.h> +#include <libart_lgpl/art_vpath_bpath.h> + +using namespace T2P; + +BezierPathLibart::BezierPathLibart() : BezierPath() +{ + m_array.resize(0); + m_length = -1; +} + +BezierPathLibart::BezierPathLibart(ArtBpath *other) : BezierPath() +{ + int i = 0; + for(;other[i].code != ART_END; i++) + { + ensureSpace(m_array, i) + m_array[i] = other[i]; + } + ensureSpace(m_array, i) + m_array[i].code = ART_END; +} + +BezierPathLibart::~BezierPathLibart() +{ +} + +double BezierPathLibart::length(double t) +{ + if(m_length < 0.0) + { + double total = 0.0; + // We cheat a bit... + ArtVpath *vpath = art_bez_path_to_vec(m_array.data(), 0.25); + double x = 0.0, y = 0.0; + for(int i = 0; vpath[i].code != ART_END; i++) + { + if(vpath[i].code == ART_MOVETO) + { + x = vpath[i].x; + y = vpath[i].y; + } + else if(vpath[i].code == ART_LINETO) + { + double dx = x, dy = y; + x = vpath[i].x; + y = vpath[i].y; + dx = x - dx; + dy = y - dy; + total += sqrt(pow(dx, 2) + pow(dy, 2)); + } + } + art_free(vpath); + return total * t; + } + else + return m_length * t; +} + +void BezierPathLibart::pointTangentNormalAt(double t, Point *p, Point *tn, Point *n) +{ + double totallen = length(t); + // We cheat a bit... + ArtVpath *vpath = art_bez_path_to_vec(m_array.data(), 0.25); + double total = 0.0; + double x = 0.0, y = 0.0; + for(int i = 0; vpath[i].code != ART_END; i++) + { + if(vpath[i].code == ART_MOVETO) + { + x = vpath[i].x; + y = vpath[i].y; + } + else if(vpath[i].code == ART_LINETO) + { + double dx = x, dy = y; + x = vpath[i].x; + y = vpath[i].y; + dx = x - dx; + dy = y - dy; + double seg_len = sqrt(pow(dx, 2) + pow(dy, 2)); + total += seg_len; + if(total >= totallen) + { + double fract = 1 - (totallen - (total - seg_len)) / seg_len; + if(p) + { + p->setX(x - dx * fract); + p->setY(y - dy * fract); + } + // Calculate tangent + if(tn) + { + tn->setX(dx); + tn->setY(dy); + } + // Calculate normal vector. + if(n) + { + // Calculate vector product of "binormal" x tangent + // (0,0,1) x (dx,dy,0), which is simply (dy,-dx,0). + n->setX(dy); + n->setY(-dx); + } + return; + } + } + } + art_free(vpath); +} + +void BezierPathLibart::boundingBox(Point *topLeft, Point *bottomRight) +{ + if(m_array.count() > 0) + { + // We cheat a bit... + ArtVpath *vpath = art_bez_path_to_vec(m_array.data(), 0.25); + + ArtDRect rect; + art_vpath_bbox_drect(vpath, &rect); + art_free(vpath); + + *topLeft = Point(rect.x0, rect.y0); + *bottomRight = Point(rect.x1, rect.y1); + } + else + { + *topLeft = Point(0, 0); + *bottomRight = Point(0, 0); + } +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/BezierPathLibart.h b/ksvg/plugin/backends/libart/BezierPathLibart.h new file mode 100644 index 00000000..a6dfd2b9 --- /dev/null +++ b/ksvg/plugin/backends/libart/BezierPathLibart.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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. +*/ + +#ifndef T2P_BEZIERPATH_LIBART_H +#define T2P_BEZIERPATH_LIBART_H + +#include "BezierPath.h" +#include <qmemarray.h> + +class _ArtBpath; + +#define ensureSpace(vec, index) if((int)vec.size() == index) vec.resize(index + 1); + +namespace T2P +{ + class BezierPathLibart : public BezierPath + { + public: + BezierPathLibart(); + BezierPathLibart(_ArtBpath *other); + ~BezierPathLibart(); + + virtual double length(double t = 1.0); + virtual void pointTangentNormalAt(double t, Point *p = 0, Point *tn = 0, Point *n = 0); + virtual void boundingBox(Point *topLeft, Point *bottomRight); + + // Don't make those private members, these are all internal anyway... + QMemArray<_ArtBpath> m_array; + double m_length; + }; +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/GlyphTracerLibart.cpp b/ksvg/plugin/backends/libart/GlyphTracerLibart.cpp new file mode 100644 index 00000000..b034c6e6 --- /dev/null +++ b/ksvg/plugin/backends/libart/GlyphTracerLibart.cpp @@ -0,0 +1,177 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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 <iostream> + +#include <kdebug.h> + +#include "Glyph.h" +#include "Point.h" +#include "BezierPathLibart.h" +#include "GlyphTracerLibart.h" + +#include <libart_lgpl/art_bpath.h> + +#include <config.h> + +#ifdef HAVE_FREETYPE_2_2_x + #define FT_VECTOR_PARAMETER const FT_Vector +#else + #define FT_VECTOR_PARAMETER FT_Vector +#endif + +using namespace T2P; + +int traceMoveto(FT_VECTOR_PARAMETER *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathLibart *path = static_cast<BezierPathLibart *>(glyph->modifiableBezierPath()); + Point p = affine.mapPoint(Point(to->x, to->y)); + + int index = path->m_array.count(); + if(index == 0 || + p.x() != path->m_array[index - 1].x3 || + p.y() != path->m_array[index - 1].y3) + { + path->m_array.resize(index + 1); + path->m_array[index].code = ART_MOVETO; + path->m_array[index].x3 = p.x(); + path->m_array[index].y3 = p.y(); + } + + return 0; +} + +int traceLineto(FT_VECTOR_PARAMETER *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathLibart *path = static_cast<BezierPathLibart *>(glyph->modifiableBezierPath()); + Point p = affine.mapPoint(Point(to->x, to->y)); + + int index = path->m_array.count(); + ArtBpath *last = &path->m_array[index - 1]; + + if((p.x() != last->x3) || (p.y() != last->y3)) + { + path->m_array.resize(index + 1); + path->m_array[index].code = ART_LINETO; + path->m_array[index].x3 = p.x(); + path->m_array[index].y3 = p.y(); + } + + return 0; +} + +int traceConicBezier(FT_VECTOR_PARAMETER *control, FT_VECTOR_PARAMETER *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathLibart *path = static_cast<BezierPathLibart *>(glyph->modifiableBezierPath()); + + int index = path->m_array.count(); + if(!(index > 0)) + return -1; + + path->m_array.resize(index + 1); + ArtBpath *s = &path->m_array[index - 1]; + ArtBpath *e = &path->m_array[index]; + + e->code = ART_CURVETO; + + Point c = affine.mapPoint(Point(control->x, control->y)); + Point p = affine.mapPoint(Point(to->x, to->y)); + e->x3 = p.x(); + e->y3 = p.y(); + + path->m_array[index].x1 = c.x() - (c.x() - s->x3) / 3; + path->m_array[index].y1 = c.y() - (c.y() - s->y3) / 3; + path->m_array[index].x2 = c.x() + (e->x3 - c.x()) / 3; + path->m_array[index].y2 = c.y() + (e->y3 - c.y()) / 3; + + return 0; +} + +int traceCubicBezier(FT_VECTOR_PARAMETER *control1, FT_VECTOR_PARAMETER *control2, FT_VECTOR_PARAMETER *to, void *obj) +{ + Glyph *glyph = reinterpret_cast<Glyph *>(obj); + Affine &affine = glyph->affine(); + BezierPathLibart *path = static_cast<BezierPathLibart *>(glyph->modifiableBezierPath()); + + Point p = affine.mapPoint(Point(to->x, to->y)); + Point c1 = affine.mapPoint(Point(control1->x, control1->y)); + Point c2 = affine.mapPoint(Point(control2->x, control2->y)); + + int index = path->m_array.count(); + path->m_array.resize(index + 1); + path->m_array[index].code = ART_CURVETO; + path->m_array[index].x1 = c1.x(); + path->m_array[index].y1 = c1.y(); + path->m_array[index].x2 = c2.x(); + path->m_array[index].y2 = c2.y(); + path->m_array[index].x3 = p.x(); + path->m_array[index].y3 = p.y(); + + return 0; +} + +GlyphTracerLibart::GlyphTracerLibart() : GlyphTracer() +{ + setMoveto(*traceMoveto); + setLineto(*traceLineto); + setConicBezier(*traceConicBezier); + setCubicBezier(*traceCubicBezier); +} + +GlyphTracerLibart::~GlyphTracerLibart() +{ +} + +void GlyphTracerLibart::correctGlyph(GlyphAffinePair *glyphAffine) +{ + // Take bezier path belonging to glyph (Note: that one is _UNMODIFIABLE_, once calculated) + const BezierPathLibart *path = static_cast<const BezierPathLibart *>(glyphAffine->glyph()->bezierPath()); + + // Create a new empty path with the same size + ArtBpath *transformed = art_bpath_affine_transform(path->m_array.data(), glyphAffine->affine().data()); + BezierPathLibart *transformatedPath = new BezierPathLibart(transformed); + art_free(transformed); + glyphAffine->setTransformatedPath(transformatedPath); +} + +BezierPath *GlyphTracerLibart::allocBezierPath(int) +{ + BezierPathLibart *bpath = new BezierPathLibart(); + //if(size != 0) + // bpath->m_array.resize(size); + + return bpath; +} + +void GlyphTracerLibart::closePath(Glyph *glyph) +{ + BezierPathLibart *path = static_cast<BezierPathLibart *>(glyph->modifiableBezierPath()); + int index = path->m_array.count(); + path->m_array.resize(index + 1); + path->m_array[index].code = ART_END; +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/GlyphTracerLibart.h b/ksvg/plugin/backends/libart/GlyphTracerLibart.h new file mode 100644 index 00000000..39b87490 --- /dev/null +++ b/ksvg/plugin/backends/libart/GlyphTracerLibart.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org> + 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. +*/ + +#ifndef T2P_GLYPHTRACER_LIBART_H +#define T2P_GLYPHTRACER_LIBART_H + +#include "GlyphTracer.h" + +// FreeType 2 includes +#include <ft2build.h> +#include FT_FREETYPE_H + +namespace T2P +{ + class Glyph; + class BezierPath; + class GlyphAffinePair; + + class GlyphTracerLibart : public GlyphTracer + { + public: + GlyphTracerLibart(); + virtual ~GlyphTracerLibart(); + + virtual void correctGlyph(GlyphAffinePair *glyphAffine); + virtual BezierPath *allocBezierPath(int size); + virtual void closePath(Glyph *glyph); + }; +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/LibartCanvas.cpp b/ksvg/plugin/backends/libart/LibartCanvas.cpp new file mode 100644 index 00000000..5697b623 --- /dev/null +++ b/ksvg/plugin/backends/libart/LibartCanvas.cpp @@ -0,0 +1,425 @@ +/* + 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 "LibartCanvas.h" +#include "SVGMatrixImpl.h" +#include "SVGRectImpl.h" +#include "SVGPaintImpl.h" +#include "SVGShapeImpl.h" +#include "SVGDocumentImpl.h" +#include "SVGSVGElementImpl.h" +#include "SVGStringListImpl.h" +#include "SVGPatternElementImpl.h" +#include "SVGGradientElementImpl.h" +#include "SVGLinearGradientElementImpl.h" +#include "SVGRadialGradientElementImpl.h" +#include "SVGClipPathElementImpl.h" +#include "SVGTextPositioningElementImpl.h" +#include "SVGAnimatedLengthImpl.h" +#include "SVGAnimatedLengthListImpl.h" +#include "SVGAnimatedEnumerationImpl.h" +#include "SVGMarkerElementImpl.h" +#include "SVGMaskElementImpl.h" + +#include <kdebug.h> +#include <kglobal.h> +#include <kgenericfactory.h> + +#include "SVGPaint.h" + +#include <qdatetime.h> +#include <qstring.h> +#include <qimage.h> + +#include "KSVGHelper.h" +#include "KSVGTextChunk.h" +#include "LibartCanvasItems.h" + +#include <libart_lgpl/art_rgb.h> +#include <libart_lgpl/art_affine.h> +#include <libart_lgpl/art_alphagamma.h> +#include <libart_lgpl/art_rgb_svp.h> +#include <libart_lgpl/art_svp.h> +#include <libart_lgpl/art_svp_ops.h> +#include <libart_lgpl/art_svp_intersect.h> +#include <libart_lgpl/art_rect_svp.h> +#include <libart_lgpl/art_svp_vpath.h> + +#include <libs/art_support/art_misc.h> +#include <libs/art_support/art_rgba_svp.h> + +#include <Font.h> +#include "BezierPathLibart.h" +#include "GlyphTracerLibart.h" + +#include <fontconfig/fontconfig.h> + +ArtSVP *art_svp_from_rect(int x0, int y0, int x1, int y1) +{ + ArtVpath vpath[] = + { + { ART_MOVETO, x0, y0 }, + { ART_LINETO, x0, y1 }, + { ART_LINETO, x1, y1 }, + { ART_LINETO, x1, y0 }, + { ART_LINETO, x0, y0 }, + { ART_END, 0, 0} + }; + + return art_svp_from_vpath(vpath); +} + +ArtSVP *art_svp_from_irect(ArtIRect *bbox) +{ + return art_svp_from_rect(bbox->x0, bbox->y0, bbox->x1, bbox->y1); +} + +ArtSVP *art_svp_from_qrect(const QRect& rect) +{ + return art_svp_from_rect(rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1); +} + +using namespace KSVG; + +LibartCanvas::LibartCanvas(unsigned int width, unsigned int height) : KSVGCanvas(width, height) +{ + m_fontContext = new T2P::Converter(new T2P::GlyphTracerLibart()); +} + +T2P::BezierPath *LibartCanvas::toBezierPath(CanvasItem *item) const +{ + LibartPath *path = dynamic_cast<LibartPath *>(item); + if(!path) + return 0; + + // Only handle LibartPath items + //T2P::BezierPathLibart *result = new T2P::BezierPathLibart(path->m_array.data()); + return path; +} + +// drawing primitives +CanvasItem *LibartCanvas::createRectangle(SVGRectElementImpl *rect) +{ + return new LibartRectangle(this, rect); +} + +CanvasItem *LibartCanvas::createEllipse(SVGEllipseElementImpl *ellipse) +{ + return new LibartEllipse(this, ellipse); +} + +CanvasItem *LibartCanvas::createCircle(SVGCircleElementImpl *circle) +{ + return new LibartCircle(this, circle); +} + +CanvasItem *LibartCanvas::createLine(SVGLineElementImpl *line) +{ + return new LibartLine(this, line); +} + +CanvasItem *LibartCanvas::createPolyline(SVGPolylineElementImpl *poly) +{ + return new LibartPolyline(this, poly); +} + +CanvasItem *LibartCanvas::createPolygon(SVGPolygonElementImpl *poly) +{ + return new LibartPolygon(this, poly); +} + +CanvasItem *LibartCanvas::createPath(SVGPathElementImpl *path) +{ + return new LibartPath(this, path); +} + +CanvasItem *LibartCanvas::createClipPath(SVGClipPathElementImpl *clippath) +{ + CanvasClipPath *result = new LibartClipPath(this, clippath); + QString index = clippath->id().string(); + m_clipPaths.insert(index, result); + return result; +} + +CanvasItem *LibartCanvas::createImage(SVGImageElementImpl *image) +{ + return new LibartImage(this, image); +} + +CanvasItem *LibartCanvas::createCanvasMarker(SVGMarkerElementImpl *marker) +{ + return new LibartMarker(this, marker); +} + +CanvasItem *LibartCanvas::createText(SVGTextElementImpl *text) +{ + return new LibartText(this, text); +} + +CanvasPaintServer *LibartCanvas::createPaintServer(SVGElementImpl *pserver) +{ + LibartPaintServer *result; + if(dynamic_cast<SVGLinearGradientElementImpl *>(pserver)) + result = new LibartLinearGradient(dynamic_cast<SVGLinearGradientElementImpl *>(pserver)); + else if(dynamic_cast<SVGRadialGradientElementImpl *>(pserver)) + result = new LibartRadialGradient(dynamic_cast<SVGRadialGradientElementImpl *>(pserver)); + else if(dynamic_cast<SVGPatternElementImpl *>(pserver)) + result = new LibartPattern(dynamic_cast<SVGPatternElementImpl *>(pserver)); + return result; +} + +void LibartCanvas::drawImage(QImage image, SVGStylableImpl *style, const SVGMatrixImpl *matrix, const KSVGPolygon& clippingPolygon) +{ + SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(style); + + if(shape) + { + if(image.depth() != 32) + image = image.convertDepth(32); + + ArtSVP *imageBorder = svpFromPolygon(clippingPolygon); + ArtSVP *clipSvp = clipSingleSVP(imageBorder, shape); + + ArtDRect bbox; + art_drect_svp(&bbox, clipSvp); + + // clamp to viewport + int x0 = int(bbox.x0); + int y0 = int(bbox.y0); + + // Use inclusive coords for x1/y1 for clipToBuffer + int x1 = int(ceil(bbox.x1)) - 1; + int y1 = int(ceil(bbox.y1)) - 1; + + if(x0 < int(m_width) && y0 < int(m_height) && x1 >= 0 && y1 >= 0) + { + clipToBuffer(x0, y0, x1, y1); + + QRect screenBBox(x0, y0, x1 - x0 + 1, y1 - y0 + 1); + + QByteArray mask = SVGMaskElementImpl::maskRectangle(shape, screenBBox); + + double affine[6]; + KSVGHelper::matrixToAffine(matrix, affine); + + ksvg_art_rgb_affine_clip(clipSvp, m_buffer + x0 * nrChannels() + y0 * rowStride(), x0, y0, x1 + 1, y1 + 1, rowStride(), nrChannels(), image.bits(), image.width(), image.height(), image.width() * 4, affine, int(style->getOpacity() * 255), (const art_u8 *)mask.data()); + } + + art_svp_free(imageBorder); + art_svp_free(clipSvp); + } +} + +ArtSVP *LibartCanvas::clippingRect(const QRect &rect, const SVGMatrixImpl *ctm) +{ + ArtVpath *vec = allocVPath(6); + // Order of points in clipping rectangle must be counter-clockwise + bool flip = ((ctm->a() * ctm->d()) < (ctm->b() * ctm->c())); + + vec[0].code = ART_MOVETO; + vec[0].x = rect.x(); + vec[0].y = rect.y(); + + vec[1].code = ART_LINETO; + vec[1].x = rect.x() + (flip ? rect.width() : 0); + vec[1].y = rect.y() + (flip ? 0 : rect.height()); + + vec[2].code = ART_LINETO; + vec[2].x = rect.x() + rect.width(); + vec[2].y = rect.y() + rect.height(); + + vec[3].code = ART_LINETO; + vec[3].x = rect.x() + (flip ? 0 : rect.width()); + vec[3].y = rect.y() + (flip ? rect.height() : 0); + + vec[4].code = ART_LINETO; + vec[4].x = rect.x(); + vec[4].y = rect.y(); + + vec[5].code = ART_END; + + double affine[6]; + KSVGHelper::matrixToAffine(ctm, affine); + + ArtVpath *temp = art_vpath_affine_transform(vec, affine); + art_free(vec); + + ArtSVP *result = art_svp_from_vpath(temp); + art_free(temp); + return result; +} + +ArtSVP *LibartCanvas::clipSingleSVP(ArtSVP *svp, SVGShapeImpl *shape) +{ + ArtSVP *clippedSvp = copy_svp(svp); + SVGStylableImpl *style = dynamic_cast<SVGStylableImpl *>(shape); + + if(style) + { + QString clipPathRef = style->getClipPath(); + + if(!clipPathRef.isEmpty()) + { + CanvasClipPath *clipPath = m_clipPaths[clipPathRef]; + + if(clipPath) + { + LibartClipPath *lclip = dynamic_cast<LibartClipPath *>(clipPath); + reinterpret_cast<SVGClipPathElementImpl *>(clipPath->element())->setBBoxTarget(shape); + + lclip->init(); + + if(lclip->clipSVP()) + { + ArtSVP *s = art_svp_intersect(lclip->clipSVP(), clippedSvp); + art_svp_free(clippedSvp); + clippedSvp = s; + } + } + } + } + + SVGSVGElementImpl *svg = dynamic_cast<SVGSVGElementImpl *>(shape); + + // Clip outer svg, unless width and height not set + if(svg && (!svg->isRootElement() || !svg->getAttribute("width").isEmpty() || !svg->getAttribute("height").isEmpty()) && !svg->getOverflow()) + { + ArtSVP *svgClip = clippingRect(svg->clip(), svg->screenCTM()); + ArtSVP *s = art_svp_intersect(svgClip, clippedSvp); + art_svp_free(clippedSvp); + art_svp_free(svgClip); + clippedSvp = s; + } + + if(dynamic_cast<SVGPatternElementImpl *>(shape) != 0) + { + // TODO: inherit clipping paths into tile space + } + else if(dynamic_cast<SVGMarkerElementImpl *>(shape) != 0) + { + SVGMarkerElementImpl *marker = static_cast<SVGMarkerElementImpl *>(shape); + + if(!marker->clipShape().isEmpty()) + { + ArtSVP *clipShape = svpFromPolygon(marker->clipShape()); + ArtSVP *s = art_svp_intersect(clipShape, clippedSvp); + art_svp_free(clipShape); + art_svp_free(clippedSvp); + clippedSvp = s; + } + + // TODO: inherit clipping paths into marker space + } + else + { + SVGElementImpl *element = dynamic_cast<SVGElementImpl *>(shape); + DOM::Node parentNode = element->parentNode(); + + if(!parentNode.isNull()) + { + SVGElementImpl *parent = element->ownerDoc()->getElementFromHandle(parentNode.handle()); + + if(parent) + { + SVGShapeImpl *parentShape = dynamic_cast<SVGShapeImpl *>(parent); + + if(parentShape) + { + // Clip against ancestor clipping paths + ArtSVP *parentClippedSvp = clipSingleSVP(clippedSvp, parentShape); + art_svp_free(clippedSvp); + clippedSvp = parentClippedSvp; + } + } + } + } + + return clippedSvp; +} + +void LibartCanvas::drawSVP(ArtSVP *svp, art_u32 color, QByteArray mask, QRect screenBBox) +{ + int x0 = screenBBox.left(); + int y0 = screenBBox.top(); + int x1 = screenBBox.right(); + int y1 = screenBBox.bottom(); + + if(m_nrChannels == 3) + { + if(mask.data()) + art_ksvg_rgb_svp_alpha_mask(svp, x0, y0, x1 + 1, y1 + 1, color, m_buffer + x0 * 3 + y0 * 3 * m_width, m_width * 3, 0, (art_u8 *)mask.data()); + else + art_rgb_svp_alpha(svp, x0, y0, x1 + 1, y1 + 1, color, m_buffer + x0 * 3 + y0 * 3 * m_width, m_width * 3, 0); + } + else + art_ksvg_rgba_svp_alpha(svp, x0, y0, x1 + 1, y1 + 1, color, m_buffer + x0 * 4 + y0 * 4 * m_width, m_width * 4, 0, (art_u8 *)mask.data()); +} + +ArtSVP *LibartCanvas::copy_svp(const ArtSVP *svp) +{ + // No API to copy an SVP so do so without accessing the data structure directly. + ArtVpath *vec = allocVPath(1); + + vec[0].code = ART_END; + + ArtSVP *empty = art_svp_from_vpath(vec); + art_free(vec); + + ArtSVP *result = art_svp_union(empty, svp); + art_svp_free(empty); + + return result; +} + +ArtSVP *LibartCanvas::svpFromPolygon(const KSVGPolygon& polygon) +{ + if(polygon.numPoints() > 2) + { + ArtVpath *points = new ArtVpath[polygon.numPoints() + 2]; + + points[0].code = ART_MOVETO; + points[0].x = polygon.point(0).x(); + points[0].y = polygon.point(0).y(); + + unsigned int i; + + for(i = 1; i < polygon.numPoints(); i++) + { + points[i].code = ART_LINETO; + points[i].x = polygon.point(i).x(); + points[i].y = polygon.point(i).y(); + } + + points[i].code = ART_LINETO; + points[i].x = polygon.point(0).x(); + points[i].y = polygon.point(0).y(); + + points[i + 1].code = ART_END; + + ArtSVP *svp = art_svp_from_vpath(points); + delete [] points; + + return svp; + } + else + return 0; +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/LibartCanvas.h b/ksvg/plugin/backends/libart/LibartCanvas.h new file mode 100644 index 00000000..e0da1682 --- /dev/null +++ b/ksvg/plugin/backends/libart/LibartCanvas.h @@ -0,0 +1,84 @@ +/* + 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. +*/ + +#ifndef LIBARTCANVAS_H +#define LIBARTCANVAS_H + +#include "CanvasItem.h" +#include "KSVGCanvas.h" + +#include <libart_lgpl/art_svp.h> +#include <libart_lgpl/art_vpath.h> +#include <libart_lgpl/art_config.h> + +#include <Glyph.h> +#include <Converter.h> +#include "GlyphTracerLibart.h" + +class QString; +class QImage; + +struct _ArtSVP; + +namespace KSVG +{ + +class LibartPaintServer; +class SVGElementImpl; +class SVGStylableImpl; +class SVGSVGElementImpl; +class KSVGPolygon; +class LibartCanvas : public KSVGCanvas +{ +public: + LibartCanvas(unsigned int width, unsigned int height); + + void drawSVP(_ArtSVP *svp, art_u32 color, QByteArray mask, QRect screenBBox); + void drawImage(QImage image, SVGStylableImpl *style, const SVGMatrixImpl *matrix, const KSVGPolygon& clippingPolygon); + + virtual T2P::BezierPath *toBezierPath(CanvasItem *item) const; + + // creating canvas items + virtual CanvasItem *createRectangle(SVGRectElementImpl *rect); + virtual CanvasItem *createEllipse(SVGEllipseElementImpl *ellipse); + virtual CanvasItem *createCircle(SVGCircleElementImpl *circle); + virtual CanvasItem *createLine(SVGLineElementImpl *line); + virtual CanvasItem *createPolyline(SVGPolylineElementImpl *poly); + virtual CanvasItem *createPolygon(SVGPolygonElementImpl *poly); + virtual CanvasItem *createPath(SVGPathElementImpl *path); + virtual CanvasItem *createClipPath(SVGClipPathElementImpl *clippath); + virtual CanvasItem *createImage(SVGImageElementImpl *image); + virtual CanvasItem *createCanvasMarker(SVGMarkerElementImpl *marker); + virtual CanvasItem *createText(SVGTextElementImpl *text); + virtual CanvasPaintServer *createPaintServer(SVGElementImpl *pserver); + + _ArtSVP *clippingRect(const QRect &rect, const SVGMatrixImpl *ctm); + _ArtSVP *svpFromPolygon(const KSVGPolygon& polygon); + + static ArtSVP *copy_svp(const ArtSVP *svp); + + _ArtSVP *clipSingleSVP(_ArtSVP *svp, SVGShapeImpl *clipShape); +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/LibartCanvasFactory.cpp b/ksvg/plugin/backends/libart/LibartCanvasFactory.cpp new file mode 100644 index 00000000..48d37f05 --- /dev/null +++ b/ksvg/plugin/backends/libart/LibartCanvasFactory.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 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 <kdebug.h> + +#include "LibartCanvas.h" +#include "LibartCanvasFactory.h" + +using namespace KSVG; + +K_EXPORT_COMPONENT_FACTORY(libksvgrendererlibart, LibartCanvasFactory) + +LibartCanvasFactory::LibartCanvasFactory() +{ +} + +LibartCanvasFactory::~LibartCanvasFactory() +{ +} + +QObject *LibartCanvasFactory::createObject(QObject *, const char *, const char *, const QStringList &args) +{ + int width = (*args.at(1)).toInt(); + int height = (*args.at(0)).toInt(); + return new LibartCanvas(width, height); +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/LibartCanvasFactory.h b/ksvg/plugin/backends/libart/LibartCanvasFactory.h new file mode 100644 index 00000000..5b3ed0b3 --- /dev/null +++ b/ksvg/plugin/backends/libart/LibartCanvasFactory.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 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. +*/ + +#ifndef LIBARTCANVASFACTORY_H +#define LIBARTCANVASFACTORY_H + +#include <klibloader.h> + +#include "CanvasItem.h" +#include "KSVGCanvas.h" + +namespace KSVG +{ + +class LibartCanvasFactory : public KLibFactory +{ +public: + LibartCanvasFactory(); + virtual ~LibartCanvasFactory(); + + virtual QObject *createObject(QObject *parent = 0, const char *pname = 0, const char *name = "QObject", const QStringList &args = QStringList()); +}; + +} + +#endif + +/// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/LibartCanvasItems.cpp b/ksvg/plugin/backends/libart/LibartCanvasItems.cpp new file mode 100644 index 00000000..5dd97be1 --- /dev/null +++ b/ksvg/plugin/backends/libart/LibartCanvasItems.cpp @@ -0,0 +1,2209 @@ +/* + 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 <cfloat> + +#include <qimage.h> +#include <qwmatrix.h> + +#include "SVGPaint.h" +#include "SVGRectImpl.h" +#include "SVGAngleImpl.h" +#include "SVGPaintImpl.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 "SVGAnimatedStringImpl.h" +#include "SVGImageElementImpl.h" +#include "SVGAnimatedAngleImpl.h" +#include "SVGAnimatedLengthImpl.h" +#include "SVGPolygonElementImpl.h" +#include "SVGClipPathElementImpl.h" +#include "SVGPolylineElementImpl.h" +#include "SVGAnimatedLengthListImpl.h" +#include "SVGAnimatedNumberImpl.h" +#include "SVGAnimatedEnumerationImpl.h" +#include "SVGPreserveAspectRatioImpl.h" +#include "SVGAnimatedPreserveAspectRatioImpl.h" +#include "SVGGradientElementImpl.h" +#include "SVGGradientElement.h" +#include "SVGLinearGradientElementImpl.h" +#include "SVGRadialGradientElementImpl.h" +#include "SVGPatternElementImpl.h" +#include "SVGPatternElement.h" +#include "SVGStopElementImpl.h" +#include "SVGStylableImpl.h" +#include "SVGAnimatedTransformListImpl.h" +#include "SVGTransformListImpl.h" +#include "SVGUnitConverter.h" +#include "SVGTextPathElementImpl.h" +#include "SVGMaskElementImpl.h" + +#include "KSVGHelper.h" +#include "LibartCanvasItems.h" +#include "KSVGTextChunk.h" + +#include "art_misc.h" +#include "art_render_misc.h" +#include "BezierPathLibart.h" +#include "Point.h" + +#include <dom/dom_node.h> + + +#include <libart_lgpl/art_vpath.h> +#include <libart_lgpl/art_bpath.h> +#include <libart_lgpl/art_affine.h> +#include <libart_lgpl/art_svp_ops.h> +#include <libart_lgpl/art_svp_point.h> +#include <libart_lgpl/art_vpath_svp.h> +#include <libart_lgpl/art_svp_intersect.h> +#include <libart_lgpl/art_svp_vpath.h> +#include <libart_lgpl/art_svp_vpath_stroke.h> +#include <libart_lgpl/art_rect_svp.h> +#include <libart_lgpl/art_vpath_dash.h> +#include <libart_lgpl/art_render.h> +#include <libart_lgpl/art_rect_svp.h> +#include <libart_lgpl/art_render_gradient.h> +#include <libart_lgpl/art_render_svp.h> +#include <libart_lgpl/art_render_mask.h> + +using namespace KSVG; + +LibartShape::LibartShape(LibartCanvas *c, SVGStylableImpl *style) : m_canvas(c), m_style(style) +{ + m_fillSVP = 0; + m_strokeSVP = 0; + m_fillPainter = 0; + m_strokePainter = 0; +} + +LibartShape::~LibartShape() +{ + freeSVPs(); + delete m_fillPainter; + delete m_strokePainter; +} + +QRect LibartShape::bbox() const +{ + QRect rect; + if(m_strokeSVP || m_fillSVP) + { + ArtIRect *irect = new ArtIRect(); + ArtVpath *vpath = art_vpath_from_svp(m_strokeSVP ? m_strokeSVP : m_fillSVP); + art_vpath_bbox_irect(vpath, irect); + art_free(vpath); + + rect.setX(irect->x0); + rect.setY(irect->y0); + rect.setWidth(irect->x1 - irect->x0); + rect.setHeight(irect->y1 - irect->y0); + + delete irect; + } + + return rect; +} + +bool LibartShape::isVisible(SVGShapeImpl *shape) +{ + return m_referenced || (m_style->getVisible() && m_style->getDisplay() && shape->directRender()); +} + +bool LibartShape::fillContains(const QPoint &p) +{ + if(m_fillSVP) + return art_svp_point_wind(m_fillSVP, p.x(), p.y()) != 0; + else + return false; +} + +bool LibartShape::strokeContains(const QPoint &p) +{ + if(m_strokeSVP) + return art_svp_point_wind(m_strokeSVP, p.x(), p.y()) != 0; + else + return false; +} + +void LibartShape::update(CanvasItemUpdate reason, int param1, int param2) +{ + if(reason == UPDATE_STYLE) + { + if(!m_fillPainter || !m_strokePainter) + LibartShape::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) + { + reset(); + m_canvas->invalidate(this, true); + } + else if(reason == UPDATE_ZOOM) + reset(); + else if(reason == UPDATE_PAN) + { + if(m_fillSVP) + ksvg_art_svp_move(m_fillSVP, param1, param2); + if(m_strokeSVP) + ksvg_art_svp_move(m_strokeSVP, param1, param2); + } + else if(reason == UPDATE_LINEWIDTH) + { + if(m_strokeSVP) + { + art_svp_free(m_strokeSVP); + m_strokeSVP = 0; + } + init(); + m_canvas->invalidate(this, true); + } +} + +void LibartShape::draw(SVGShapeImpl *shape) +{ + if(!m_referenced && (!m_style->getVisible() || !m_style->getDisplay() || !shape->directRender())) + return; + + bool fillOk = m_fillSVP && m_style->isFilled(); + bool strokeOk = m_strokeSVP && m_style->isStroked() && m_style->getStrokeWidth()->baseVal()->value() > 0; // Spec: A zero value causes no stroke to be painted. + + if(fillOk || strokeOk) + { + if(m_fillPainter && m_fillSVP) + m_fillPainter->draw(m_canvas, m_fillSVP, m_style, shape); + + if(m_strokePainter && m_strokeSVP) + m_strokePainter->draw(m_canvas, m_strokeSVP, m_style, shape); + } +} + +void LibartShape::init(const SVGMatrixImpl *) +{ +} + +void LibartPainter::update(SVGStylableImpl *style) +{ + if(paintType(style) != SVG_PAINTTYPE_URI) + { + QColor qcolor; + if(paintType(style) == SVG_PAINTTYPE_CURRENTCOLOR) + qcolor = style->getColor()->rgbColor().color(); + else + qcolor = color(style); + + short _opacity = static_cast<short>(opacity(style) * 255 + 0.5); + + // Spec: clamping + _opacity = _opacity < 0 ? 0 : _opacity; + _opacity = _opacity > 255 ? 255 : _opacity; + + m_color = KSVGHelper::toArtColor(qcolor, _opacity); + } +} + +void LibartPainter::draw(LibartCanvas *canvas, _ArtSVP *svp, SVGStylableImpl *style, SVGShapeImpl *shape) +{ + ArtSVP *clippedSvp = canvas->clipSingleSVP(svp, shape); + + // Clipping + ArtDRect bbox; + art_drect_svp(&bbox, clippedSvp); + + // clamp to viewport + int x0 = int(bbox.x0); + int y0 = int(bbox.y0); + + // Use inclusive coords for x1/y1 for clipToBuffer + int x1 = int(ceil(bbox.x1)) - 1; + int y1 = int(ceil(bbox.y1)) - 1; + + if(x0 < int(canvas->width()) && y0 < int(canvas->height()) && x1 > -1 && y1 > -1) + { + canvas->clipToBuffer(x0, y0, x1, y1); + + QRect screenBBox(x0, y0, x1 - x0 + 1, y1 - y0 + 1); + + QByteArray mask = SVGMaskElementImpl::maskRectangle(shape, screenBBox); + + if(paintType(style) == SVG_PAINTTYPE_URI) + { + LibartPaintServer *pserver = static_cast<LibartPaintServer *>(SVGPaintServerImpl::paintServer(shape->ownerDoc(), paintUri(style))); + + if(pserver) + { + pserver->setBBoxTarget(shape); + if(!pserver->finalized()) + pserver->finalizePaintServer(); + pserver->render(canvas, clippedSvp, opacity(style), mask, screenBBox); + } + } + else + canvas->drawSVP(clippedSvp, m_color, mask, screenBBox); + } + + art_svp_free(clippedSvp); +} + +LibartStrokePainter::LibartStrokePainter(SVGStylableImpl *style) +{ + update(style); +} + +LibartFillPainter::LibartFillPainter(SVGStylableImpl *style) +{ + update(style); +} + +void LibartShape::init() +{ + if(m_style->isFilled()) + { + if(m_fillPainter == 0) + m_fillPainter = new LibartFillPainter(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 LibartStrokePainter(m_style); + } + else + { + delete m_strokePainter; + m_strokePainter = 0; + } +} + +void LibartShape::initClipItem() +{ + init(); +} + +ArtSVP *LibartShape::clipSVP() +{ + return m_fillSVP; +} + +void LibartShape::freeSVPs() +{ + if(m_fillSVP) + art_svp_free(m_fillSVP); + if(m_strokeSVP) + art_svp_free(m_strokeSVP); + + m_fillSVP = 0; + m_strokeSVP = 0; +} + +void LibartShape::calcClipSVP(ArtVpath *vec, SVGStylableImpl *style, const SVGMatrixImpl *matrix, _ArtSVP **clipSVP) +{ + double affine[6]; + KSVGHelper::matrixToAffine(matrix, affine); + + if(!style) + { + art_free(vec); + return; + } + + ArtVpath *vtemp = art_vpath_affine_transform(vec, affine); + art_free(vec); + vec = vtemp; + + ArtSVP *temp; + ArtSvpWriter *swr; + temp = art_svp_from_vpath(vec); + + if(style->getClipRule() == RULE_EVENODD) + swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN); + else + swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO); + + art_svp_intersector(temp, swr); + *clipSVP = art_svp_writer_rewind_reap(swr); + + art_svp_free(temp); + art_free(vec); +} + +void LibartShape::calcSVPs(ArtVpath *vec, SVGStylableImpl *style, const SVGMatrixImpl *matrix, ArtSVP **strokeSVP, ArtSVP **fillSVP) +{ + if(style) + { + double affine[6]; + KSVGHelper::matrixToAffine(matrix, affine); + + ArtVpath *temp = art_vpath_affine_transform(vec, affine); + art_free(vec); + vec = temp; + calcSVPInternal(vec, style, affine, strokeSVP, fillSVP); + } + else + art_free(vec); +} + +void LibartShape::calcSVPs(ArtBpath *bpath, SVGStylableImpl *style, const SVGMatrixImpl *matrix, ArtSVP **strokeSVP, ArtSVP **fillSVP) +{ + if(style) + { + double affine[6]; + KSVGHelper::matrixToAffine(matrix, affine); + + ArtBpath *temp = art_bpath_affine_transform(bpath, affine); + ArtVpath *vec = ksvg_art_bez_path_to_vec(temp, 0.25); + art_free(temp); + calcSVPInternal(vec, style, affine, strokeSVP, fillSVP); + } +} + +void LibartShape::calcSVPInternal(ArtVpath *vec, SVGStylableImpl *style, double *affine, ArtSVP **strokeSVP, ArtSVP **fillSVP) +{ + ArtSVP *svp; + + // Filling + { + ArtSvpWriter *swr; + ArtSVP *temp; + temp = art_svp_from_vpath(vec); + + if(style->getFillRule() == RULE_EVENODD) + swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN); + else + swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO); + + art_svp_intersector(temp, swr); + svp = art_svp_writer_rewind_reap(swr); + + *fillSVP = svp; + art_svp_free(temp); + } + + // Stroking + if(style->isStroked() || style->getStrokeColor()->paintType() == SVG_PAINTTYPE_URI) + { + double ratio = art_affine_expansion(affine); + + unsigned int dashLength; + if(style->getDashArray() && (dashLength = style->getDashArray()->baseVal()->numberOfItems()) > 0) + { + // HACK: libart will hang in art_vpath_dash() if passed an array with only zeroes. + bool allZeroes = true; + + // there are dashes to be rendered + ArtVpathDash dash; + dash.offset = int(style->getDashOffset()->baseVal()->value()) * ratio; + dash.n_dash = dashLength; + double *dashes = new double[dashLength]; + for(unsigned int i = 0; i < dashLength; i++) + { + dashes[i] = style->getDashArray()->baseVal()->getItem(i)->value() * ratio; + if(dashes[i] != 0.0) + allZeroes = false; + } + dash.dash = dashes; + + if(!allZeroes) + { + // get the dashed VPath and use that for the stroke render operation + ArtVpath *vec2 = art_vpath_dash(vec, &dash); + art_free(vec); + vec = vec2; + } + + // reset the dashes + delete [] dashes; + } + + double penWidth = style->getStrokeWidth()->baseVal()->value() * ratio; + svp = art_svp_vpath_stroke(vec, (ArtPathStrokeJoinType)style->getJoinStyle(), (ArtPathStrokeCapType)style->getCapStyle(), penWidth, style->getStrokeMiterlimit(), 0.25); + + *strokeSVP = svp; + } + art_free(vec); +} + +// ##### + +LibartRectangle::LibartRectangle(LibartCanvas *c, SVGRectElementImpl *rect) +: LibartShape(c, rect), m_rect(rect) +{ + init(); +} + +void LibartRectangle::draw() +{ + if(isVisible()) + LibartShape::draw(m_rect); +} + +bool LibartRectangle::isVisible() +{ + // Spec: a value of zero disables rendering + return LibartShape::isVisible(m_rect) && m_rect->width()->baseVal()->value() > 0 && m_rect->height()->baseVal()->value() > 0; +} + +void LibartRectangle::init() +{ + init(m_rect->screenCTM()); +} + +void LibartRectangle::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + 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) + { + ArtVpath *vec = allocVPath(6); + + vec[0].code = ART_MOVETO; + vec[0].x = x; + vec[0].y = y; + + vec[1].code = ART_LINETO; + vec[1].x = x; + vec[1].y = y + height; + + vec[2].code = ART_LINETO; + vec[2].x = x + width; + vec[2].y = y + height; + + vec[3].code = ART_LINETO; + vec[3].x = x + width; + vec[3].y = y; + + vec[4].code = ART_LINETO; + vec[4].x = x; + vec[4].y = y; + + vec[5].code = ART_END; + + if(m_context == NORMAL) + calcSVPs(vec, m_rect, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(vec, m_rect, screenCTM, &m_fillSVP); + } + else + { + ArtVpath *res; + ArtBpath *vec = allocBPath(10); + + 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; + + vec[i].code = ART_MOVETO_OPEN; + vec[i].x3 = x + rx; + vec[i].y3 = y; + + i++; + + vec[i].code = ART_CURVETO; + vec[i].x1 = x + rx * (1 - 0.552); + vec[i].y1 = y; + vec[i].x2 = x; + vec[i].y2 = y + ry * (1 - 0.552); + vec[i].x3 = x; + vec[i].y3 = y + ry; + + i++; + + if(ry < height / 2) + { + vec[i].code = ART_LINETO; + vec[i].x3 = x; + vec[i].y3 = y + height - ry; + + i++; + } + vec[i].code = ART_CURVETO; + vec[i].x1 = x; + vec[i].y1 = y + height - ry * (1 - 0.552); + vec[i].x2 = x + rx * (1 - 0.552); + vec[i].y2 = y + height; + vec[i].x3 = x + rx; + vec[i].y3 = y + height; + + i++; + + if(rx < width / 2) + { + vec[i].code = ART_LINETO; + vec[i].x3 = x + width - rx; + vec[i].y3 = y + height; + + i++; + } + + vec[i].code = ART_CURVETO; + vec[i].x1 = x + width - rx * (1 - 0.552); + vec[i].y1 = y + height; + vec[i].x2 = x + width; + vec[i].y2 = y + height - ry * (1 - 0.552); + vec[i].x3 = x + width; + + vec[i].y3 = y + height - ry; + + i++; + + if(ry < height / 2) + { + vec[i].code = ART_LINETO; + vec[i].x3 = x + width; + vec[i].y3 = y + ry; + + i++; + } + vec[i].code = ART_CURVETO; + vec[i].x1 = x + width; + vec[i].y1 = y + ry * (1 - 0.552); + vec[i].x2 = x + width - rx * (1 - 0.552); + vec[i].y2 = y; + vec[i].x3 = x + width - rx; + vec[i].y3 = y; + + i++; + + if(rx < width / 2) + { + vec[i].code = ART_LINETO; + vec[i].x3 = x + rx; + vec[i].y3 = y; + + i++; + } + + vec[i].code = ART_END; + + res = ksvg_art_bez_path_to_vec(vec, 0.25); + if(m_context == NORMAL) + calcSVPs(res, m_rect, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(res, m_rect, screenCTM, &m_fillSVP); + art_free(vec); + } +} + +// ##### + +LibartEllipse::LibartEllipse(LibartCanvas *c, SVGEllipseElementImpl *ellipse) +: LibartShape(c, ellipse), m_ellipse(ellipse) +{ + init(); +} + +void LibartEllipse::draw() +{ + if(isVisible()) + LibartShape::draw(m_ellipse); +} + +bool LibartEllipse::isVisible() +{ + // Spec: dont render when rx and/or ry is zero + return LibartShape::isVisible(m_ellipse) && m_ellipse->rx()->baseVal()->value() > 0 && m_ellipse->ry()->baseVal()->value() > 0; +} + +void LibartEllipse::init() +{ + init(m_ellipse->screenCTM()); +} + +void LibartEllipse::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + ArtBpath *temp = allocBPath(6); + + double x1, y1, x2, y2, x3, y3; + double len = 0.55228474983079356; + 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(); + double cos4[] = {1.0, 0.0, -1.0, 0.0, 1.0}; + double sin4[] = {0.0, 1.0, 0.0, -1.0, 0.0}; + int i = 0; + + temp[i].code = ART_MOVETO; + temp[i].x3 = cx + rx; + temp[i].y3 = cy; + + i++; + + while(i < 5) + { + x1 = cos4[i-1] + len * cos4[i]; + y1 = sin4[i-1] + len * sin4[i]; + x2 = cos4[i] + len * cos4[i-1]; + y2 = sin4[i] + len * sin4[i-1]; + x3 = cos4[i]; + y3 = sin4[i]; + + temp[i].code = ART_CURVETO; + temp[i].x1 = cx + x1 * rx; + temp[i].y1 = cy + y1 * ry; + temp[i].x2 = cx + x2 * rx; + temp[i].y2 = cy + y2 * ry; + temp[i].x3 = cx + x3 * rx; + temp[i].y3 = cy + y3 * ry; + + i++; + } + + temp[i].code = ART_END; + + if(m_context == NORMAL) + calcSVPs(temp, m_ellipse, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(ksvg_art_bez_path_to_vec(temp, 0.25), m_ellipse, screenCTM, &m_fillSVP); + art_free(temp); +} + +// ##### + +LibartCircle::LibartCircle(LibartCanvas *c, SVGCircleElementImpl *circle) +: LibartShape(c, circle), m_circle(circle) +{ + init(); +} + +void LibartCircle::draw() +{ + // Spec: a value of zero disables rendering + if(isVisible()) + LibartShape::draw(m_circle); +} + +bool LibartCircle::isVisible() +{ + // Spec: dont render when rx and/or ry is zero + return LibartShape::isVisible(m_circle) && m_circle->r()->baseVal()->value() > 0; +} + +void LibartCircle::init() +{ + init(m_circle->screenCTM()); +} + +void LibartCircle::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + ArtBpath *temp = allocBPath(6); + + double x1, y1, x2, y2, x3, y3; + double len = 0.55228474983079356; + double r = m_circle->r()->baseVal()->value(); + double cx = m_circle->cx()->baseVal()->value(); + double cy = m_circle->cy()->baseVal()->value(); + double cos4[] = {1.0, 0.0, -1.0, 0.0, 1.0}; + double sin4[] = {0.0, 1.0, 0.0, -1.0, 0.0}; + int i = 0; + + temp[i].code = ART_MOVETO; + temp[i].x3 = cx + r; + temp[i].y3 = cy; + + i++; + + while(i < 5) + { + x1 = cos4[i-1] + len * cos4[i]; + y1 = sin4[i-1] + len * sin4[i]; + x2 = cos4[i] + len * cos4[i-1]; + y2 = sin4[i] + len * sin4[i-1]; + x3 = cos4[i]; + y3 = sin4[i]; + + temp[i].code = ART_CURVETO; + temp[i].x1 = cx + x1 * r; + temp[i].y1 = cy + y1 * r; + temp[i].x2 = cx + x2 * r; + temp[i].y2 = cy + y2 * r; + temp[i].x3 = cx + x3 * r; + temp[i].y3 = cy + y3 * r; + + i++; + } + + temp[i].code = ART_END; + + if(m_context == NORMAL) + calcSVPs(temp, m_circle, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(ksvg_art_bez_path_to_vec(temp, 0.25), m_circle, screenCTM, &m_fillSVP); + art_free(temp); +} + +// ##### + +LibartLine::LibartLine(LibartCanvas *c, SVGLineElementImpl *line) +: LibartShape(c, line), MarkerHelper(), m_line(line) +{ + init(); +} + +LibartLine::~LibartLine() +{ +} + +void LibartLine::draw() +{ + LibartShape::draw(m_line); + + 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 LibartLine::isVisible() +{ + return LibartShape::isVisible(m_line); +} + +void LibartLine::init() +{ + init(m_line->screenCTM()); +} + +void LibartLine::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + ArtVpath *vec; + + vec = allocVPath(3); + + vec[0].code = ART_MOVETO_OPEN; + vec[0].x = m_line->x1()->baseVal()->value(); + vec[0].y = m_line->y1()->baseVal()->value(); + + vec[1].code = ART_LINETO; + vec[1].x = m_line->x2()->baseVal()->value(); + vec[1].y = m_line->y2()->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. + if(vec[1].x == vec[0].x && vec[1].y == vec[0].y && m_line->getCapStyle() == PATH_STROKE_CAP_ROUND) + vec[1].x += .5; + + vec[2].code = ART_END; + + if(m_context == NORMAL) + { + calcSVPs(vec, m_line, screenCTM, &m_strokeSVP, &m_fillSVP); + art_svp_free(m_fillSVP); + m_fillSVP = 0; + } + else + calcClipSVP(vec, m_line, screenCTM, &m_fillSVP); +} + +// ##### +LibartPoly::LibartPoly(LibartCanvas *c, SVGPolyElementImpl *poly) +: LibartShape(c, poly), MarkerHelper(), m_poly(poly) +{ +} + +LibartPoly::~LibartPoly() +{ +} + +void LibartPoly::init() +{ + init(m_poly->screenCTM()); +} + +void LibartPoly::draw() +{ + LibartShape::draw(m_poly); + + if(m_poly->hasMarkers()) + m_poly->drawMarkers(); +} + +bool LibartPoly::isVisible() +{ + return LibartShape::isVisible(m_poly); +} + +// ##### +LibartPolyline::LibartPolyline(LibartCanvas *c, SVGPolylineElementImpl *poly) +: LibartPoly(c, poly) +{ + LibartPoly::init(); +} + +LibartPolyline::~LibartPolyline() +{ +} + +void LibartPolyline::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + unsigned int numberOfPoints = m_poly->points()->numberOfItems(); + + if(numberOfPoints < 1) + return; + + ArtVpath *polyline = allocVPath(2 + numberOfPoints); + + polyline[0].code = ART_MOVETO_OPEN; + polyline[0].x = m_poly->points()->getItem(0)->x(); + polyline[0].y = m_poly->points()->getItem(0)->y(); + + unsigned int index; + for(index = 1; index < numberOfPoints; index++) + { + polyline[index].code = ART_LINETO; + polyline[index].x = m_poly->points()->getItem(index)->x(); + polyline[index].y = m_poly->points()->getItem(index)->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 && polyline[1].x == polyline[0].x && polyline[1].y == polyline[0].y && m_poly->getCapStyle() == PATH_STROKE_CAP_ROUND) + polyline[1].x += .5; + + + if(m_poly->isFilled()) // if the polyline must be filled, inform libart that it should not be closed. + { + polyline[index].code = (ArtPathcode) ART_END2; + polyline[index].x = m_poly->points()->getItem(0)->x(); + polyline[index++].y = m_poly->points()->getItem(0)->y(); + } + + polyline[index].code = ART_END; + if(m_context == NORMAL) + calcSVPs(polyline, m_poly, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(polyline, m_poly, screenCTM, &m_fillSVP); +} + +// ##### + +LibartPolygon::LibartPolygon(LibartCanvas *c, SVGPolygonElementImpl *poly) +: LibartPoly(c, poly) +{ + LibartPoly::init(); +} + +LibartPolygon::~LibartPolygon() +{ +} + +void LibartPolygon::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + unsigned int numberOfPoints = m_poly->points()->numberOfItems(); + + if(numberOfPoints < 1) + return; + + ArtVpath *polygon = allocVPath(2 + numberOfPoints); + + polygon[0].code = ART_MOVETO; + polygon[0].x = m_poly->points()->getItem(0)->x(); + polygon[0].y = m_poly->points()->getItem(0)->y(); + + unsigned int index; + for(index = 1; index < numberOfPoints; index++) + { + polygon[index].code = ART_LINETO; + polygon[index].x = m_poly->points()->getItem(index)->x(); + polygon[index].y = m_poly->points()->getItem(index)->y(); + } + + polygon[index].code = ART_LINETO; + polygon[index].x = m_poly->points()->getItem(0)->x(); + polygon[index].y = m_poly->points()->getItem(0)->y(); + + index++; + polygon[index].code = ART_END; + + if(m_context == NORMAL) + calcSVPs(polygon, m_poly, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(polygon, m_poly, screenCTM, &m_fillSVP); +} + +// ##### + +LibartPath::LibartPath(LibartCanvas *c, SVGPathElementImpl *path) +: LibartShape(c, path), MarkerHelper(), T2P::BezierPathLibart(), ::SVGPathParser(), m_path(path) +{ + reset(); +} + +LibartPath::~LibartPath() +{ +} + +void LibartPath::reset() +{ + m_array.resize(0); + LibartShape::reset(); +} + +void LibartPath::draw() +{ + LibartShape::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 LibartPath::isVisible() +{ + return LibartShape::isVisible(m_path); +} + +void LibartPath::init() +{ + init(m_path->screenCTM()); +} + +void LibartPath::init(const SVGMatrixImpl *screenCTM) +{ + LibartShape::init(); + if(m_array.count() > 0) + { + if(m_context == NORMAL) + calcSVPs(m_array.data(), m_path, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(ksvg_art_bez_path_to_vec(m_array.data(), 0.25), m_path, screenCTM, &m_fillSVP); + } + else if(!m_path->getAttribute("d").string().isEmpty()) + { + parseSVG(m_path->getAttribute("d").string(), true); + + int index = m_array.count(); + double curx = m_array[index - 1].x3; + double cury = m_array[index - 1].y3; + + // Find last subpath + int find = -1; + for(int i = index - 1; i >= 0; i--) + { + if(m_array[i].code == ART_MOVETO_OPEN || m_array[i].code == ART_MOVETO) + { + find = i; + break; + } + } + + // Fix a problem where the .svg file used floats as values... (sofico.svg) + if(curx != m_array[find].x3 && cury != m_array[find].y3) + { + if((int) curx == (int) m_array[find].x3 && (int) cury == (int) m_array[find].y3) + { + ensureSpace(m_array, index) + + m_array[index].code = ART_LINETO; + m_array[index].x3 = m_array[find].x3; + m_array[index].y3 = m_array[find].y3; + + curx = m_array[find].x3; + cury = m_array[find].y3; + + index++; + } + } + + // handle filled paths that are not closed explicitly + if(m_path->getFillColor()->paintType() != SVG_PAINTTYPE_NONE) + { + if((int) curx != (int) m_array[find].x3 || (int) cury != (int) m_array[find].y3) + { + ensureSpace(m_array, index) + + m_array[index].code = (ArtPathcode)ART_END2; + m_array[index].x3 = m_array[find].x3; + m_array[index].y3 = m_array[find].y3; + + curx = m_array[find].x3; + cury = m_array[find].y3; + + index++; + } + } + + // close + ensureSpace(m_array, index) + + m_array[index].code = ART_END; + + // 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(index == 2 && m_array[1].code == ART_LINETO && m_array[1].x3 == m_array[0].x3 && m_array[1].y3 == m_array[0].y3 && m_path->getCapStyle() == PATH_STROKE_CAP_ROUND) + m_array[1].x3 += .5; + + // There are pure-moveto paths which reference paint servers *bah* + // Do NOT render them + bool render = false; + for(int i = index; i >= 0; i--) + { + if(m_array[i].code != ART_MOVETO_OPEN && m_array[i].code != ART_MOVETO && !(m_array[i].code >= ART_END)) + { + render = true; + break; + } + } + + if(render && m_context == NORMAL) + calcSVPs(m_array.data(), m_path, screenCTM, &m_strokeSVP, &m_fillSVP); + else + calcClipSVP(ksvg_art_bez_path_to_vec(m_array.data(), 0.25), m_path, screenCTM, &m_fillSVP); + } +} + +void LibartPath::svgMoveTo(double x1, double y1, bool closed, bool) +{ + int index = m_array.count(); + + if(index > 0 && !closed) + { + // Find last subpath + int find = -1; + for(int i = index - 1; i >= 0; i--) + { + if(m_array[i].code == ART_MOVETO_OPEN || m_array[i].code == ART_MOVETO) + { + find = i; + break; + } + } + + ensureSpace(m_array, index) + + m_array[index].code = (ArtPathcode) ART_END2; + m_array[index].x3 = m_array[find].x3; + m_array[index].y3 = m_array[find].y3; + + index++; + } + + ensureSpace(m_array, index) + + m_array[index].code = (index == 0) ? ART_MOVETO : ART_MOVETO_OPEN; + m_array[index].x3 = x1; + m_array[index].y3 = y1; +} + +void LibartPath::svgLineTo(double x1, double y1, bool) +{ + int index = m_array.count(); + + ensureSpace(m_array, index) + + m_array[index].code = ART_LINETO; + m_array[index].x3 = x1; + m_array[index].y3 = y1; +} + +void LibartPath::svgCurveToCubic(double x1, double y1, double x2, double y2, double x3, double y3, bool) +{ + int index = m_array.count(); + + ensureSpace(m_array, index) + + m_array[index].code = ART_CURVETO; + m_array[index].x1 = x1; + m_array[index].y1 = y1; + m_array[index].x2 = x2; + m_array[index].y2 = y2; + m_array[index].x3 = x3; + m_array[index].y3 = y3; +} + +void LibartPath::svgClosePath() +{ + int index = m_array.count(); + double curx = m_array[index - 1].x3; + double cury = m_array[index - 1].y3; + + int find = -1; + for(int i = index - 1; i >= 0; i--) + { + if(m_array[i].code == ART_MOVETO_OPEN || m_array[i].code == ART_MOVETO) + { + find = i; + break; + } + } + + if(find != -1) + { + if(m_array[find].x3 != curx || m_array[find].y3 != cury) + { + ensureSpace(m_array, index) + + m_array[index].code = ART_LINETO; + m_array[index].x3 = m_array[find].x3; + m_array[index].y3 = m_array[find].y3; + } + } +} + +// ##### + +LibartClipPath::LibartClipPath(LibartCanvas *c, SVGClipPathElementImpl *clipPath) +: CanvasClipPath(clipPath), m_canvas(c) +{ + m_clipSVP = 0; + m_clipItems.setAutoDelete(true); +} + +LibartClipPath::~LibartClipPath() +{ + if(m_clipSVP) + art_svp_free(m_clipSVP); +} + +void LibartClipPath::update(CanvasItemUpdate, int, int) +{ + if(m_clipSVP) + art_svp_free(m_clipSVP); + m_clipSVP = 0; +} + +void LibartClipPath::init() +{ + SVGMatrixImpl *clipMatrix = 0; + + // Start with referencing element's coordinate system + SVGLocatableImpl *locatableReferrer = dynamic_cast<SVGLocatableImpl *>(m_clipPath->getBBoxTarget()); + if(locatableReferrer) + clipMatrix = locatableReferrer->getScreenCTM(); + else + clipMatrix = SVGSVGElementImpl::createSVGMatrix(); + + if(m_clipPath->clipPathUnits()->baseVal() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && m_clipPath->getBBoxTarget()) + { + SVGRectImpl *rect = m_clipPath->getBBoxTarget()->getBBox(); + + clipMatrix->translate(rect->qrect().x(), rect->qrect().y()); + clipMatrix->scaleNonUniform(rect->qrect().width(), rect->qrect().height()); + + rect->deref(); + } + + // Add transformations on the clipPath element itself + if(m_clipPath->localMatrix()) + clipMatrix->multiply(m_clipPath->localMatrix()); + + if(m_clipSVP) + { + art_svp_free(m_clipSVP); + m_clipSVP = 0; + } + + DOM::Node node = m_clipPath->firstChild(); + for(; !node.isNull(); node = node.nextSibling()) + { + SVGElementImpl *element = m_clipPath->ownerDoc()->getElementFromHandle(node.handle()); + SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(element); + SVGTestsImpl *tests = dynamic_cast<SVGTestsImpl *>(element); + + bool ok = tests ? tests->ok() : true; + + if(element && shape && ok && !shape->isContainer()) + { + LibartClipItem *clipElement = dynamic_cast<LibartClipItem *>(shape->item()); + + if(dynamic_cast<LibartText *>(shape->item())) + { + // The cast to a clipElement above is failing when it is valid. But only + // in the plugin - svgdisplay works fine. What's going on? (Adrian) + clipElement = dynamic_cast<LibartText *>(shape->item()); + } + + if(clipElement) + { + clipElement->setRenderContext(CLIPPING); + + // Push coordinate system down to children. + SVGLocatableImpl *locatable = dynamic_cast<SVGLocatableImpl *>(shape); + if(locatable) + locatable->updateCachedScreenCTM(clipMatrix); + + clipElement->initClipItem(); + + ArtSVP *one = clipElement->clipSVP(); + if(!one) + break; + + if(m_clipSVP == 0) + m_clipSVP = LibartCanvas::copy_svp(one); + else + { + ArtSVP *svp_union = art_svp_union(m_clipSVP, one); + art_svp_free(m_clipSVP); + m_clipSVP = svp_union; + } + } + } + } + + clipMatrix->deref(); +} + +void LibartClipPath::draw() +{ +} + +ArtSVP *LibartClipPath::clipSVP() +{ + return m_clipSVP; +} + +// ##### + +LibartImage::LibartImage(LibartCanvas *c, SVGImageElementImpl *image) +: m_canvas(c), m_image(image) +{ +} + +LibartImage::~LibartImage() +{ +} + +void LibartImage::draw() +{ + if(isVisible()) + { + SVGMatrixImpl *ctm = m_image->scaledImageMatrix(); + QImage image = m_image->scaledImage(); + KSVGPolygon clippingPolygon = m_image->clippingShape(); + + m_canvas->drawImage(image, m_image, ctm, clippingPolygon); + + ctm->deref(); + } +} + +bool LibartImage::isVisible() +{ + return (m_referenced || (m_image->getVisible() && m_image->getDisplay() && m_image->directRender())) && m_image->image(); +} + +void LibartImage::init() +{ +} + +QRect LibartImage::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); +} + +// ##### + +LibartMarker::LibartMarker(LibartCanvas *c, SVGMarkerElementImpl *marker) +: CanvasMarker(marker), m_canvas(c) +{ +} + +LibartMarker::~LibartMarker() +{ +} + +void LibartMarker::init() +{ +} + +void LibartMarker::draw() +{ +} + +// ##### + +LibartText::LibartText(LibartCanvas *c, SVGTextElementImpl *text) +: CanvasText(text), m_canvas(c) +{ + m_drawFillItems.setAutoDelete(true); + m_drawStrokeItems.setAutoDelete(true); + m_fillPainters.setAutoDelete(true); + m_strokePainters.setAutoDelete(true); + + init(); +} + +LibartText::~LibartText() +{ + clearSVPs(); +} + +LibartText::SVPElement::~SVPElement() +{ + if(svp) + art_svp_free(svp); +} + +QRect LibartText::bbox() const +{ + QRect result, rect; + + QPtrListIterator<SVPElement> it1(m_drawFillItems); + QPtrListIterator<SVPElement> it2(m_drawStrokeItems); + + SVPElement *fill = it1.current(), *stroke = it2.current(); + while(fill != 0 || stroke != 0) + { + ArtIRect *irect = new ArtIRect(); + ArtVpath *vpath = art_vpath_from_svp((stroke && stroke->svp) ? stroke->svp : fill->svp); + art_vpath_bbox_irect(vpath, irect); + art_free(vpath); + + rect.setX(irect->x0); + rect.setY(irect->y0); + rect.setWidth(irect->x1 - irect->x0); + rect.setHeight(irect->y1 - irect->y0); + + delete irect; + + result = result.unite(rect); + + fill = ++it1; + stroke = ++it2; + } + return result; +} + +bool LibartText::fillContains(const QPoint &p) +{ + QPtrListIterator<SVPElement> it(m_drawFillItems); + + SVPElement *fill = it.current(); + while(fill && fill->svp) + { + if(fill->svp && art_svp_point_wind(fill->svp, p.x(), p.y()) != 0) + return true; + + fill = ++it; + } + + return false; +} + +bool LibartText::strokeContains(const QPoint &p) +{ + QPtrListIterator<SVPElement> it(m_drawStrokeItems); + + SVPElement *stroke = it.current(); + while(stroke && stroke->svp) + { + if(stroke->svp && art_svp_point_wind(stroke->svp, p.x(), p.y()) != 0) + return true; + + stroke = ++it; + } + + return false; +} + +void LibartText::update(CanvasItemUpdate reason, int param1, int param2) +{ + if(reason == UPDATE_STYLE) + { + QPtrListIterator<SVPElement> it1(m_drawFillItems); + QPtrListIterator<SVPElement> it2(m_drawStrokeItems); + SVPElement *fill = it1.current(), *stroke = it2.current(); + while(fill != 0 || stroke != 0) + { + SVGTextContentElementImpl *text = fill ? fill->element : stroke->element; + + bool fillOk = fill && fill->svp && text->isFilled(); + bool strokeOk = stroke && stroke->svp && text->isStroked() && text->getStrokeWidth()->baseVal()->value() > 0; // Spec: A zero value causes no stroke to be painted. + if(fillOk || strokeOk) + { + if(m_fillPainters.find(text)) + m_fillPainters[text]->update(text); + + if(m_strokePainters.find(text)) + m_strokePainters[text]->update(text); + } + fill = ++it1; + stroke = ++it2; + } + m_canvas->invalidate(this, false); + } + else if(reason == UPDATE_TRANSFORM) + { + clearSVPs(); + init(); + m_canvas->invalidate(this, true); + } + else if(reason == UPDATE_ZOOM) + { + clearSVPs(); + init(); + } + else if(reason == UPDATE_PAN) + { + QPtrListIterator<SVPElement> it1(m_drawFillItems); + QPtrListIterator<SVPElement> it2(m_drawStrokeItems); + + double affine[6]; + KSVGHelper::matrixToAffine(m_text->screenCTM(), affine); + + SVPElement *fill = it1.current(), *stroke = it2.current(); + while(fill != 0 || stroke != 0) + { + SVGTextContentElementImpl *text = fill ? fill->element : stroke->element; + + bool fillOk = fill && fill->svp && text->isFilled(); + bool strokeOk = stroke && stroke->svp && text->isStroked() && text->getStrokeWidth()->baseVal()->value() > 0; // Spec: A zero value causes no stroke to be painted. + + if(fillOk) + ksvg_art_svp_move(fill->svp, param1, param2); + if(strokeOk) + ksvg_art_svp_move(stroke->svp, param1, param2); + fill = ++it1; + stroke = ++it2; + } + } + /* + else if(reason == UPDATE_LINEWIDTH) + { + }*/ +} + +void LibartText::draw() +{ + QPtrListIterator<SVPElement> it1(m_drawFillItems); + QPtrListIterator<SVPElement> it2(m_drawStrokeItems); + + SVPElement *fill = it1.current(), *stroke = it2.current(); + while(fill != 0 || stroke != 0) + { + SVGTextContentElementImpl *text = fill ? fill->element : stroke->element; + if(!text || !text->getVisible() || !text->getDisplay() || !text->directRender()) + return; + + bool fillOk = fill && fill->svp && text->isFilled(); + bool strokeOk = stroke && stroke->svp && text->isStroked() && text->getStrokeWidth()->baseVal()->value() > 0; // Spec: A zero value causes no stroke to be painted. + + if(fillOk || strokeOk) + { + if(fillOk && m_fillPainters.find(text)) + m_fillPainters[text]->draw(m_canvas, fill->svp, text, text); + + if(strokeOk && m_strokePainters.find(text)) + m_strokePainters[text]->draw(m_canvas, stroke->svp, text, text); + } + fill = ++it1; + stroke = ++it2; + } +} + +bool LibartText::isVisible() +{ + bool foundVisible = false; + QPtrListIterator<SVPElement> it1(m_drawFillItems); + QPtrListIterator<SVPElement> it2(m_drawStrokeItems); + + SVPElement *fill = it1.current(), *stroke = it2.current(); + while(fill != 0 || stroke != 0) + { + SVGTextContentElementImpl *text = fill ? fill->element : stroke->element; + if(text && text->getVisible() && text->getDisplay() && text->directRender()) + { + foundVisible = true; + break; + } + + fill = ++it1; + stroke = ++it2; + } + + return foundVisible; +} + +void LibartText::init() +{ + init(m_text->screenCTM()); +} + +void LibartText::renderCallback(SVGTextContentElementImpl *element, const SVGMatrixImpl *screenCTM, T2P::GlyphSet *glyph, T2P::GlyphLayoutParams *params, double anchor) const +{ + unsigned int glyphCount = glyph->glyphCount(); // Don't call it n times in the for loop + for(unsigned int i = 0; i < glyphCount; i++) + { + T2P::GlyphAffinePair *glyphAffine = glyph->set()[i]; + ArtBpath *bezier = static_cast<const T2P::BezierPathLibart *>(glyphAffine->transformatedPath())->m_array.data(); + ArtBpath *result = bezier; + + // text-anchor support + if(anchor != 0) + { + double correct[6]; + + if(!params->tb()) + art_affine_translate(correct, -anchor, 0); + else + art_affine_translate(correct, 0, -anchor); + + ArtBpath *temp = art_bpath_affine_transform(result, correct); + //art_free(result); + result = temp; + } + + ArtSVP *fillSVP = 0, *strokeSVP = 0; + + if(m_context == NORMAL) + LibartShape::calcSVPs(result, m_text, screenCTM, &strokeSVP, &fillSVP); + else + LibartShape::calcClipSVP(ksvg_art_bez_path_to_vec(result, 0.25), m_text, screenCTM, &fillSVP); + + SVPElement *fillElement = new SVPElement(); + fillElement->svp = fillSVP; + fillElement->element = element; + + SVPElement *strokeElement = new SVPElement(); + strokeElement->svp = strokeSVP; + strokeElement->element = element; + + m_drawFillItems.append(fillElement); + m_drawStrokeItems.append(strokeElement); + + if(!m_fillPainters.find(element) && element->isFilled()) + m_fillPainters.insert(element, new LibartFillPainter(element)); + + // Spec: A zero value causes no stroke to be painted. + if(!m_strokePainters.find(element) && element->isStroked() && element->getStrokeWidth()->baseVal()->value() > 0) + m_strokePainters.insert(element, new LibartStrokePainter(element)); + } +} + +void LibartText::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 LibartText::clearSVPs() +{ + m_drawFillItems.clear(); + m_drawStrokeItems.clear(); + m_fillPainters.clear(); + m_strokePainters.clear(); +} + +void LibartText::addTextDecoration(SVGTextContentElementImpl *element, double x, double y, double width, double height) const +{ + if(m_text->isFilled() || m_text->isStroked()) + { + // compute rect svp + ArtVpath *vec = allocVPath(6); + + vec[0].code = ART_MOVETO; + vec[0].x = x; + vec[0].y = y; + + vec[1].code = ART_LINETO; + vec[1].x = x; + vec[1].y = y + height; + + vec[2].code = ART_LINETO; + vec[2].x = x + width; + vec[2].y = y + height; + + vec[3].code = ART_LINETO; + vec[3].x = x + width; + vec[3].y = y; + + vec[4].code = ART_LINETO; + vec[4].x = x; + vec[4].y = y; + + vec[5].code = ART_END; + double affine[6]; + KSVGHelper::matrixToAffine(m_text->screenCTM(), affine); + + ArtVpath *temp = art_vpath_affine_transform(vec, affine); + art_free(vec); + vec = temp; + + if(m_text->isFilled()) + { + ArtSvpWriter *swr; + ArtSVP *temp = art_svp_from_vpath(vec); + + swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN); + + art_svp_intersector(temp, swr); + ArtSVP *fillSVP = art_svp_writer_rewind_reap(swr); + + SVPElement *fillElement = new SVPElement(); + fillElement->svp = fillSVP; + fillElement->element = element; + + m_drawFillItems.append(fillElement); + + if(!m_fillPainters.find(element) && element->isFilled()) + m_fillPainters.insert(element, new LibartFillPainter(element)); + + art_svp_free(temp); + } + // Stroking + if(m_text->isStroked() || m_text->getStrokeColor()->paintType() == SVG_PAINTTYPE_URI) + { + double ratio = art_affine_expansion(affine); + ArtSVP *strokeSVP = art_svp_vpath_stroke(vec, (ArtPathStrokeJoinType)m_text->getJoinStyle(), (ArtPathStrokeCapType)m_text->getCapStyle(), m_text->getStrokeWidth()->baseVal()->value() * ratio, m_text->getStrokeMiterlimit(), 0.25); + + SVPElement *strokeElement = new SVPElement(); + strokeElement->svp = strokeSVP; + strokeElement->element = element; + + m_drawStrokeItems.append(strokeElement); + + // Spec: A zero value causes no stroke to be painted. + if(!m_strokePainters.find(element) && element->isStroked() && element->getStrokeWidth()->baseVal()->value() > 0) + m_strokePainters.insert(element, new LibartStrokePainter(element)); + } + art_free(vec); + } +} + +void LibartText::initClipItem() +{ + init(); +} + +ArtSVP *LibartText::clipSVP() +{ + ArtSVP *svp = 0; + QPtrListIterator<SVPElement> it(m_drawFillItems); + + SVPElement *fill = it.current(); + while(fill && fill->svp) + { + if(svp == 0) + svp = LibartCanvas::copy_svp(fill->svp); + else + { + ArtSVP *svp_union = art_svp_union(svp, fill->svp); + art_svp_free(svp); + svp = svp_union; + } + + fill = ++it; + } + + return svp; +} + +ArtRender *LibartPaintServer::createRenderer(QRect bbox, KSVGCanvas *c) +{ + int x0 = bbox.x(); + int y0 = bbox.y(); + int x1 = bbox.right(); + int y1 = bbox.bottom(); + + c->clipToBuffer(x0, y0, x1, y1); + + // Note: We always pass 3 for the number of channels since the ART_ALPHA parameter + // adds the alpha channel when present. + ArtRender *render = 0; + render = art_render_new(QMIN(x0, x1), + QMIN(y0, y1), + QMAX(x0, x1) + 1, + QMAX(y0, y1) + 1, + c->renderingBuffer() + x0 * c->nrChannels() + y0 * c->rowStride(), + c->rowStride(), 3, 8, + c->nrChannels() == 3 ? ART_ALPHA_NONE : ART_ALPHA_PREMUL, 0); + + return render; +} + +void LibartGradient::parseGradientStops(SVGGradientElementImpl *gradient) +{ + const double epsilon = DBL_EPSILON; + + for(DOM::Node node = gradient->firstChild(); !node.isNull(); node = node.nextSibling()) + { + SVGStopElementImpl *elem = dynamic_cast<SVGStopElementImpl *>(m_gradient->ownerDoc()->getElementFromHandle(node.handle())); + if(elem) + { + m_stops.resize(m_stops.size() + 1); + + ArtGradientStop *stop = &(m_stops[m_stops.size() - 1]); + + stop->offset = elem->offset()->baseVal(); + + // Spec: clamp range to 0 to 1 + if(stop->offset < epsilon) + stop->offset = 0; + else if(stop->offset > 1 - epsilon) + stop->offset = 1; + + // Spec: if offset is less than previous offset, set it to the previous offset + if(m_stops.size() > 1 && stop->offset < (stop - 1)->offset + epsilon) + stop->offset = (stop - 1)->offset; + + // Get color + QColor qStopColor; + + if(elem->getStopColor()->colorType() == SVG_COLORTYPE_CURRENTCOLOR) + qStopColor = elem->getColor()->rgbColor().color(); + else + qStopColor = elem->getStopColor()->rgbColor().color(); + + // Convert in a libart suitable form + QString tempName = qStopColor.name(); + const char *str = tempName.latin1(); + + 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 + float opacity = elem->stopOpacity(); + + // Get rgba color including stop-opacity + Q_UINT32 rgba = (stopColor << 8) | int(opacity * 255.0 + 0.5); + Q_UINT32 r, g, b, a; + + a = rgba & 0xff; + r = (rgba >> 24) & 0xff; + g = (rgba >> 16) & 0xff; + b = (rgba >> 8) & 0xff; + + stop->color[0] = ART_PIX_MAX_FROM_8(r); + stop->color[1] = ART_PIX_MAX_FROM_8(g); + stop->color[2] = ART_PIX_MAX_FROM_8(b); + stop->color[3] = ART_PIX_MAX_FROM_8(a); + } + } +} + +void LibartGradient::finalizePaintServer() +{ + parseGradientStops(m_gradient->stopsSource()); + + QString _href = SVGURIReferenceImpl::getTarget(m_gradient->href()->baseVal().string()); + if(!_href.isEmpty()) + reference(_href); + + setFinalized(); +} + +void LibartGradient::reference(const QString &) +{ +} + +void LibartLinearGradient::render(KSVGCanvas *c, ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox) +{ + if(!m_stops.isEmpty()) + { + m_linear->converter()->finalize(getBBoxTarget(), m_linear->ownerSVGElement(), m_linear->gradientUnits()->baseVal()); + + ArtKSVGGradientLinear *linear = art_new(ArtKSVGGradientLinear, 1); + + if(m_linear->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REPEAT) + linear->spread = ART_GRADIENT_REPEAT; + else if(m_linear->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REFLECT) + linear->spread = ART_GRADIENT_REFLECT; + else + linear->spread = ART_GRADIENT_PAD; + + linear->interpolation = m_linear->getColorInterpolation() == CI_SRGB ? ART_KSVG_SRGB_INTERPOLATION : ART_KSVG_LINEARRGB_INTERPOLATION; + + ArtRender *render = createRenderer(screenBBox, c); + + double _x1 = m_linear->x1()->baseVal()->value(); + double _y1 = m_linear->y1()->baseVal()->value(); + double _x2 = m_linear->x2()->baseVal()->value(); + double _y2 = m_linear->y2()->baseVal()->value(); + + // Respect current transformation matrix (so gradients zoom with...) + SVGTransformableImpl *transformable = dynamic_cast<SVGTransformableImpl *>(getBBoxTarget()); + SVGMatrixImpl *matrix = 0; + if(transformable) + matrix = transformable->getScreenCTM(); + else + matrix = SVGSVGElementImpl::createSVGMatrix(); + + const double epsilon = DBL_EPSILON; + + if(m_linear->gradientUnits()->baseVal() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) + { + // Here we're undoing the unit-converter's work because putting the + // bounding box transform into the matrix here lets the gradient transform + // sit at the right point in the chain to work with bounding box coordinates. It + // also removes the need for code to generate the 'not perpendicular to gradient vector' effect. + SVGRectImpl *userBbox = getBBoxTarget()->getBBox(); + + double width = userBbox->width(); + double height = userBbox->height(); + + // Catch case of width or height of zero, which can be the case for lines. + if(width < epsilon) + width = 1; + if(height < epsilon) + height = 1; + + _x1 /= width; + _y1 /= height; + _x2 /= width; + _y2 /= height; + + matrix->translate(userBbox->x(), userBbox->y()); + matrix->scaleNonUniform(width, height); + + userBbox->deref(); + } + + // Adjust to gradient transform + SVGMatrixImpl *gradTrans = m_linear->gradientTransform()->baseVal()->concatenate(); + if(gradTrans) + { + matrix->multiply(gradTrans); + gradTrans->deref(); + } + + double dx = _x2 - _x1; + double dy = _y2 - _y1; + + if(fabs(dx) < epsilon && fabs(dy) < epsilon) + { + // Lines can generate (0, 0) with bbox coords. + dx = 1; + dy = 0; + } + + double angle = atan2(dy, dx); + double length = sqrt(dx * dx + dy * dy); + + const double pi = 3.14159265358979323846; + + matrix->translate(_x1, _y1); + matrix->scale(length); + matrix->rotate(angle * 180.0 / pi); + + double affine[6]; + + KSVGHelper::matrixToAffine(matrix, affine); + art_affine_invert(linear->affine, affine); + + matrix->deref(); + + QMemArray<ArtGradientStop> stops = m_stops; + stops.detach(); + + for(unsigned int i = 0; i < stops.size(); i++) + stops[i].color[3] = ArtPixMaxDepth(stops[i].color[3] * opacity + 0.5); + + if(m_linear->x1()->baseVal()->valueInSpecifiedUnits() == m_linear->x2()->baseVal()->valueInSpecifiedUnits() + && m_linear->y1()->baseVal()->valueInSpecifiedUnits() == m_linear->y2()->baseVal()->valueInSpecifiedUnits()) + { + // Spec: If x1 == x2 and y1 == y2, paint the area in a single colour, using the colour + // of the last stop. + // + // Using valueInSpecifiedUnits() so that we are comparing the values before possible + // conversion to bounding box units by the converter. + if(stops.size() > 1) + { + stops[0] = stops[stops.size() - 1]; + stops.resize(1); + } + } + + linear->stops = &(stops[0]); + linear->n_stops = stops.size(); + + art_render_svp(render, svp); + art_ksvg_render_gradient_linear(render, linear, ART_FILTER_HYPER); + + if(mask.data()) + art_render_mask(render, screenBBox.left(), screenBBox.top(), screenBBox.right() + 1, screenBBox.bottom() + 1, + (const art_u8 *)mask.data(), screenBBox.width()); + + art_render_invoke(render); + + art_free(linear); + } +} + +void LibartRadialGradient::render(KSVGCanvas *c, ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox) +{ + if(!m_stops.isEmpty()) + { + m_radial->converter()->finalize(getBBoxTarget(), m_radial->ownerSVGElement(), m_radial->gradientUnits()->baseVal()); + + ArtKSVGGradientRadial *radial = art_new(ArtKSVGGradientRadial, 1); + + if(m_radial->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REPEAT) + radial->spread = ART_GRADIENT_REPEAT; + else if(m_radial->spreadMethod()->baseVal() == SVG_SPREADMETHOD_REFLECT) + radial->spread = ART_GRADIENT_REFLECT; + else + radial->spread = ART_GRADIENT_PAD; + + radial->interpolation = m_radial->getColorInterpolation() == CI_SRGB ? ART_KSVG_SRGB_INTERPOLATION : ART_KSVG_LINEARRGB_INTERPOLATION; + + ArtRender *render = createRenderer(screenBBox, c); + + // Respect current transformation matrix (so gradients zoom with...) + SVGTransformableImpl *transformable = dynamic_cast<SVGTransformableImpl *>(getBBoxTarget()); + SVGMatrixImpl *matrix = 0; + if(transformable) + matrix = transformable->getScreenCTM(); + else + matrix = SVGSVGElementImpl::createSVGMatrix(); + + double _cx = m_radial->cx()->baseVal()->value(); + double _cy = m_radial->cy()->baseVal()->value(); + double _r = m_radial->r()->baseVal()->value(); + + double _fx; + double _fy; + + // Spec: If attribute fx is not specified, fx will coincide with cx. + if(m_radial->getAttribute("fx").isEmpty()) + _fx = _cx; + else + _fx = m_radial->fx()->baseVal()->value(); + + // Spec: If attribute fy is not specified, fy will coincide with cy. + if(m_radial->getAttribute("fy").isEmpty()) + _fy = _cy; + else + _fy = m_radial->fy()->baseVal()->value(); + + const double epsilon = DBL_EPSILON; + + if(m_radial->gradientUnits()->baseVal() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) + { + // Here we're undoing the unit-converter's work because putting the + // bounding box transform into the matrix here lets the gradient transform + // sit at the right point in the chain to work with bounding box coordinates. + // It also produces the elliptical shape due to the non-uniform scaling. + SVGRectImpl *userBBox = getBBoxTarget()->getBBox(); + + double width = userBBox->width(); + double height = userBBox->height(); + + // Catch case of width or height of zero, which can be the case for lines. + if(width < epsilon) + width = 1; + if(height < epsilon) + height = 1; + + _cx /= width; + _cy /= height; + _fx /= width; + _fy /= height; + _r /= (sqrt(width * width + height * height) / 1.4142135623731); + + matrix->translate(userBBox->x(), userBBox->y()); + matrix->scaleNonUniform(width, height); + + userBBox->deref(); + } + + // Adjust to gradient transforms + SVGMatrixImpl *transform = m_radial->gradientTransform()->baseVal()->concatenate(); + + if(transform) + { + matrix->multiply(transform); + transform->deref(); + } + + double fx = (_fx - _cx) / _r; + double fy = (_fy - _cy) / _r; + + if(fx * fx + fy * fy > 0.99) + { + // Spec: If (fx, fy) lies outside the circle defined by (cx, cy) and r, set (fx, fy) + // to the point of intersection of the line through (fx, fy) and the circle. + // + // Note: We need to keep (fx, fy) inside the unit circle in order for + // libart to render the gradient correctly. + double angle = atan2(fy, fx); + fx = cos(angle) * 0.99; + fy = sin(angle) * 0.99; + } + + radial->fx = fx; + radial->fy = fy; + + matrix->translate(_cx, _cy); + matrix->scale(_r); + + double affine[6]; + + KSVGHelper::matrixToAffine(matrix, affine); + art_affine_invert(radial->affine, affine); + + matrix->deref(); + + QMemArray<ArtGradientStop> stops = m_stops; + stops.detach(); + + for(unsigned int i = 0; i < stops.size(); i++) + stops[i].color[3] = ArtPixMaxDepth(stops[i].color[3] * opacity + 0.5); + + radial->stops = &(stops[0]); + radial->n_stops = stops.size(); + + art_render_svp(render, svp); + art_ksvg_render_gradient_radial(render, radial, ART_FILTER_HYPER); + + if(mask.data()) + art_render_mask(render, screenBBox.left(), screenBBox.top(), screenBBox.right() + 1, screenBBox.bottom() + 1, + (const art_u8 *)mask.data(), screenBBox.width()); + + art_render_invoke(render); + + art_free(radial); + } +} + +LibartPattern::LibartPattern(SVGPatternElementImpl *pattern) + : m_pattern(pattern) +{ +} + +void LibartPattern::finalizePaintServer() +{ + m_pattern->finalizePaintServer(); + setFinalized(); +} + +void LibartPattern::reference(const QString &href) +{ + m_pattern->reference(href); +} + +void LibartPattern::render(KSVGCanvas *c, ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox) +{ + 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(); + + int alpha = int(opacity * 255 + 0.5); + + ksvg_art_rgb_texture(svp, c->renderingBuffer() + screenBBox.x() * c->nrChannels() + screenBBox.y() * c->rowStride(), screenBBox.left(), screenBBox.top(), screenBBox.right() + 1, screenBBox.bottom() + 1, c->rowStride(), c->nrChannels(), tile.image().bits(), tile.image().width(), tile.image().height(), tile.image().width() * 4, affine, ART_FILTER_NEAREST, 0L, alpha, (art_u8 *)mask.data()); + } +} + +// vim:ts=4:noet diff --git a/ksvg/plugin/backends/libart/LibartCanvasItems.h b/ksvg/plugin/backends/libart/LibartCanvasItems.h new file mode 100644 index 00000000..c62224e5 --- /dev/null +++ b/ksvg/plugin/backends/libart/LibartCanvasItems.h @@ -0,0 +1,414 @@ +/* + 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. +*/ + +#ifndef LIBARTCANVASITEMS_H +#define LIBARTCANVASITEMS_H + +#include <qptrlist.h> + +#include "CanvasItems.h" +#include "LibartCanvas.h" +#include "BezierPathLibart.h" + +#include "SVGPathElementImpl.h" +#include "SVGPolyElementImpl.h" +#include "SVGLineElementImpl.h" +#include "SVGRectElementImpl.h" +#include "SVGTextElementImpl.h" +#include "SVGCircleElementImpl.h" +#include "SVGEllipseElementImpl.h" + +// Helpers +#define allocVPath(n) art_new(ArtVpath, n) +#define allocBPath(n) art_new(ArtBpath, n) + +#define LIBART_CLASS(Class, Type, Member) \ +class Libart##Class : public LibartShape \ +{ \ +public: \ + Libart##Class(LibartCanvas *c, Type *Member); \ + virtual ~Libart##Class() { } \ + virtual void draw(); \ + virtual bool isVisible(); \ + virtual void init(); \ + virtual void init(const SVGMatrixImpl *screenCTM); \ + virtual SVGElementImpl *element() const { return m_##Member; } \ +protected: \ + Type *m_##Member; \ +}; + +struct _ArtSVP; +struct _ArtBpath; +struct _ArtRender; +struct _ArtGradientStop; + +namespace KSVG +{ + class SVGImageElementImpl; + class SVGGradientElementImpl; + class SVGRadialGradientElementImpl; + class SVGLinearGradientElementImpl; + class SVGPatternElementImpl; + + class LibartPaintServer : public CanvasPaintServer + { + public: + LibartPaintServer() : CanvasPaintServer() {} + virtual ~LibartPaintServer() {} + + virtual void render(KSVGCanvas *c, _ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox) = 0; + + _ArtRender *createRenderer(QRect rect, KSVGCanvas *c); + }; + + class LibartGradient : public LibartPaintServer + { + public: + LibartGradient(SVGGradientElementImpl *gradient) : m_gradient(gradient) {} + virtual ~LibartGradient() {} + + void parseGradientStops(SVGGradientElementImpl *gradient); + + virtual void finalizePaintServer(); + virtual void reference(const QString &href); + + protected: + SVGGradientElementImpl *m_gradient; + QMemArray<_ArtGradientStop> m_stops; + }; + + class LibartLinearGradient : public LibartGradient + { + public: + LibartLinearGradient(SVGLinearGradientElementImpl *linear) : LibartGradient(linear), m_linear(linear) {} + + virtual void render(KSVGCanvas *c, _ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox); + + protected: + SVGLinearGradientElementImpl *m_linear; + }; + + class LibartRadialGradient : public LibartGradient + { + public: + LibartRadialGradient(SVGRadialGradientElementImpl *radial) : LibartGradient(radial), m_radial(radial) {} + + virtual void render(KSVGCanvas *c, _ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox); + + protected: + SVGRadialGradientElementImpl *m_radial; + }; + + class LibartPattern : public LibartPaintServer + { + public: + LibartPattern(SVGPatternElementImpl *pattern); + virtual ~LibartPattern() {} + + virtual void finalizePaintServer(); + virtual void reference(const QString &href); + + virtual void render(KSVGCanvas *c, _ArtSVP *svp, float opacity, QByteArray mask, QRect screenBBox); + + protected: + SVGPatternElementImpl *m_pattern; + }; + + class LibartPainter + { + public: + LibartPainter() { m_color = 0; } + virtual ~LibartPainter() {} + + void update(SVGStylableImpl *style); + void draw(LibartCanvas *canvas, _ArtSVP *svp, SVGStylableImpl *style, SVGShapeImpl *shape); + + virtual float opacity(SVGStylableImpl *style) const = 0; + virtual unsigned short paintType(SVGStylableImpl *style) const = 0; + virtual QString paintUri(SVGStylableImpl *style) const = 0; + virtual QRgb color(SVGStylableImpl *style) const = 0; + + protected: + art_u32 m_color; + }; + + class LibartFillPainter : public LibartPainter + { + public: + LibartFillPainter(SVGStylableImpl *style); + + float opacity(SVGStylableImpl *style) const { return style->getFillOpacity() * style->getOpacity(); } + unsigned short paintType(SVGStylableImpl *style) const { return style->getFillColor()->paintType(); } + QString paintUri(SVGStylableImpl *style) const { return style->getFillColor()->uri().string(); } + QRgb color(SVGStylableImpl *style) const { return style->getFillColor()->rgbColor().color(); } + }; + + class LibartStrokePainter : public LibartPainter + { + public: + LibartStrokePainter(SVGStylableImpl *style); + + float opacity(SVGStylableImpl *style) const { return style->getStrokeOpacity() * style->getOpacity(); } + unsigned short paintType(SVGStylableImpl *style) const { return style->getStrokeColor()->paintType(); } + QString paintUri(SVGStylableImpl *style) const { return style->getStrokeColor()->uri().string(); } + QRgb color(SVGStylableImpl *style) const { return style->getStrokeColor()->rgbColor().color(); } + }; + + class LibartClipItem + { + public: + LibartClipItem() { m_context = NORMAL; } + virtual ~LibartClipItem() {} + + virtual void initClipItem() = 0; + void setRenderContext(RenderContext context) { m_context = context; } + virtual ArtSVP *clipSVP() = 0; + + protected: + RenderContext m_context; + }; + + class LibartShape : public CanvasItem, public LibartClipItem + { + public: + LibartShape(LibartCanvas *c, SVGStylableImpl *style); + virtual ~LibartShape(); + + virtual QRect bbox() const; + virtual bool fillContains(const QPoint &p); + virtual bool strokeContains(const QPoint &p); + virtual void update(CanvasItemUpdate reason, int param1 = 0, int param2 = 0); + void draw(SVGShapeImpl *shape); + bool isVisible(SVGShapeImpl *shape); + + virtual void init(); + virtual void init(const SVGMatrixImpl *); + virtual void reset() { freeSVPs(); init(); } + + void initClipItem(); + ArtSVP *clipSVP(); + + static void calcClipSVP(ArtVpath *vec, SVGStylableImpl *style, const SVGMatrixImpl *matrix, _ArtSVP **clipSVP); + static void calcSVPs(ArtVpath *vec, SVGStylableImpl *style, const SVGMatrixImpl *matrix, _ArtSVP **strokeSVP, _ArtSVP **fillSVP); + static void calcSVPs(_ArtBpath *bpath, SVGStylableImpl *style, const SVGMatrixImpl *matrix, _ArtSVP **strokeSVP, _ArtSVP **fillSVP); + + protected: + void freeSVPs(); + static void calcSVPInternal(ArtVpath *vec, SVGStylableImpl *style, double *affine, ArtSVP **strokeSVP, ArtSVP **fillSVP); + + _ArtSVP *m_fillSVP; + _ArtSVP *m_strokeSVP; + LibartFillPainter *m_fillPainter; + LibartStrokePainter *m_strokePainter; + LibartCanvas *m_canvas; + SVGStylableImpl *m_style; + }; + + LIBART_CLASS(Rectangle, SVGRectElementImpl, rect) + LIBART_CLASS(Ellipse, SVGEllipseElementImpl, ellipse) + LIBART_CLASS(Circle, SVGCircleElementImpl, circle) + + class LibartLine : public LibartShape, public MarkerHelper + { + public: + LibartLine(LibartCanvas *c, SVGLineElementImpl *line); + virtual ~LibartLine(); + + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM); + + virtual SVGElementImpl *element() const { return m_line; } + + protected: + SVGLineElementImpl *m_line; + }; + + class LibartPoly : public LibartShape, public MarkerHelper + { + public: + LibartPoly(LibartCanvas *c, SVGPolyElementImpl *poly); + virtual ~LibartPoly(); + + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM) = 0; + + virtual SVGElementImpl *element() const { return m_poly; } + + protected: + SVGPolyElementImpl *m_poly; + + }; + class LibartPolyline : public LibartPoly + { + public: + LibartPolyline(LibartCanvas *c, SVGPolylineElementImpl *poly); + virtual ~LibartPolyline(); + + virtual void init(const SVGMatrixImpl *screenCTM); + }; + + class LibartPolygon : public LibartPoly + { + public: + LibartPolygon(LibartCanvas *c, SVGPolygonElementImpl *poly); + virtual ~LibartPolygon(); + + virtual void init(const SVGMatrixImpl *screenCTM); + }; + + class LibartPath : public LibartShape, public MarkerHelper, public T2P::BezierPathLibart, public ::SVGPathParser + { + public: + LibartPath(LibartCanvas *c, SVGPathElementImpl *path); + virtual ~LibartPath(); + + virtual void draw(); + virtual bool isVisible(); + virtual void reset(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM); + + virtual SVGElementImpl *element() const { return m_path; } + + protected: + friend class LibartCanvas; + SVGPathElementImpl *m_path; + + virtual void svgMoveTo(double x1, double y1, bool closed, bool abs = true); + virtual void svgLineTo(double x1, double y1, bool abs = true); + virtual void svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs = true); + virtual void svgClosePath(); + }; + + class LibartClipPath : public CanvasClipPath + { + public: + LibartClipPath(LibartCanvas *c, SVGClipPathElementImpl *clipPath); + virtual ~LibartClipPath(); + + virtual QRect bbox() const { return QRect(); } + virtual bool fillContains(const QPoint &) { return true; } + virtual bool strokeContains(const QPoint &) { return true; } + virtual void update(CanvasItemUpdate, int param1 = 0, int param2 = 0); + virtual void init(); + virtual void draw(); + virtual bool isVisible() { return false; } + + _ArtSVP *clipSVP(); + + protected: + LibartCanvas *m_canvas; + _ArtSVP *m_clipSVP; + + QPtrList<CanvasItem> m_clipItems; + }; + + class LibartImage : public CanvasItem + { + public: + LibartImage(LibartCanvas *c, SVGImageElementImpl *image); + virtual ~LibartImage(); + + virtual QRect bbox() const; + virtual bool fillContains(const QPoint &) { return true; } + virtual bool strokeContains(const QPoint &) { return true; } + virtual void update(CanvasItemUpdate, int = 0, int = 0) { } + virtual void init(); + virtual void draw(); + virtual bool isVisible(); + + // We can't include SVGImageElementImpl.h here + // because of compiliation errors (X11 headers!) + virtual SVGElementImpl *element() const { return reinterpret_cast<SVGElementImpl *>(m_image); } + + protected: + LibartCanvas *m_canvas; + SVGImageElementImpl *m_image; + }; + + class LibartMarker : public CanvasMarker + { + public: + LibartMarker(LibartCanvas *c, SVGMarkerElementImpl *marker); + virtual ~LibartMarker(); + + virtual QRect bbox() const { return QRect(); } + virtual bool fillContains(const QPoint &) { return true; } + virtual bool strokeContains(const QPoint &) { return true; } + virtual void update(CanvasItemUpdate, int = 0, int = 0) { } + virtual void init(); + virtual void draw(); + virtual bool isVisible() { return false; } + + protected: + LibartCanvas *m_canvas; + }; + + class LibartText : public CanvasText, public LibartClipItem + { + public: + LibartText(LibartCanvas *c, SVGTextElementImpl *text); + virtual ~LibartText(); + + virtual QRect bbox() const; + virtual bool fillContains(const QPoint &p); + virtual bool strokeContains(const QPoint &p); + virtual void update(CanvasItemUpdate reason, int param1 = 0, int param2 = 0); + virtual void draw(); + virtual bool isVisible(); + virtual void init(); + virtual void init(const SVGMatrixImpl *screenCTM); + + virtual void renderCallback(SVGTextContentElementImpl *element, const SVGMatrixImpl *screenCTM, T2P::GlyphSet *glyph, T2P::GlyphLayoutParams *params, double anchor) const; + virtual void addTextDecoration(SVGTextContentElementImpl *element, double x, double y, double w, double h) const; + + void initClipItem(); + ArtSVP *clipSVP(); + + protected: + LibartCanvas *m_canvas; + + private: + void clearSVPs(); + + class SVPElement + { + public: + SVPElement() { svp = 0; element = 0; } + ~SVPElement(); + + _ArtSVP *svp; + SVGTextContentElementImpl *element; + }; + + // renderCallback() is const. + mutable QPtrList<SVPElement> m_drawFillItems; + mutable QPtrList<SVPElement> m_drawStrokeItems; + mutable QPtrDict<LibartFillPainter> m_fillPainters; + mutable QPtrDict<LibartStrokePainter> m_strokePainters; + }; +} + +#endif + diff --git a/ksvg/plugin/backends/libart/Makefile.am b/ksvg/plugin/backends/libart/Makefile.am new file mode 100644 index 00000000..b3f4ea5b --- /dev/null +++ b/ksvg/plugin/backends/libart/Makefile.am @@ -0,0 +1,11 @@ +kde_module_LTLIBRARIES = libksvgrendererlibart.la + +libksvgrendererlibart_la_SOURCES = BezierPathLibart.cpp GlyphTracerLibart.cpp LibartCanvas.cpp LibartCanvasItems.cpp LibartCanvasFactory.cpp +libksvgrendererlibart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +libksvgrendererlibart_la_LIBADD = ../../../libksvg.la +libksvgrendererlibart_la_METASOURCES = AUTO + +kde_services_DATA = ksvglibartcanvas.desktop + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +INCLUDES = $(LIBART_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) -I$(top_srcdir)/ksvg/dom -I$(top_srcdir)/ksvg/impl -I$(top_srcdir)/ksvg/ecma -I$(top_srcdir)/ksvg/core -I$(top_srcdir)/ksvg/impl/libs/art_support -I$(top_srcdir)/ksvg/impl/libs/libtext2path/src $(KDE_INCLUDES) $(all_includes) diff --git a/ksvg/plugin/backends/libart/ksvglibartcanvas.desktop b/ksvg/plugin/backends/libart/ksvglibartcanvas.desktop new file mode 100644 index 00000000..af6f3a6c --- /dev/null +++ b/ksvg/plugin/backends/libart/ksvglibartcanvas.desktop @@ -0,0 +1,101 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KSVG/Renderer +X-KDE-Library=libksvgrendererlibart +X-KSVG-InternalName=libart +Name=KSVG Rendering Backend - Libart +Name[ar]=خلفية الرسم ل KSVG - Libart +Name[bs]=KSVG renderiranje - Libart +Name[ca]=Representació en segon pla de KSVG - Biblioteca d'imatges +Name[cs]=Vykreslovací nástroj KSVG - Libart +Name[cy]=Ôl-wyneb Llunio KSVG - Libart +Name[da]=Underliggende program til KSVG-visning - Libart +Name[de]=KSVG-Darstellungsmodul - Libart +Name[el]=Σύστημα υποστήριξης αποτύπωσης του KSVG - Libart +Name[es]=Motor de procesado de KSVG - Libart +Name[et]=KSVG renderdamise taustarakendus - Libart +Name[eu]=KSVG errendatze programa - Libart +Name[fa]=پایانۀ پشتیبانی پرداخت KSVG - Libart +Name[fi]=KSVG-piirtäjän taustaohjelma - Libart +Name[fr]=Moteur de rendu KSVG - Libart +Name[ga]=Inneall Rindreála KSVG - Libart +Name[gl]=Mecanismo de Interpretación KSVG - Libart +Name[hi]=के-एसवीजी रेंडरिंग बैकएण्ड- लिबआर्ट +Name[hu]=KSVG megjelenítőmotor - Libart +Name[is]=KSVG teiknari - Libart +Name[it]=Backend di KSVG per il rendering - Libart +Name[ja]=KSVG レンダリングバックエンド - Libart +Name[kk]=KSVG кескіндеу бағдарламасы - Libart +Name[km]=កម្មវិធីសម្រាប់បង្ហាញ KSVG - Libart +Name[lt]=KSVG atkūrimo programinė sąsaja - Libart +Name[ms]=Tepi Belakang Menrealisasi KSVG - Libart +Name[nb]=Modul for KSVG-tegning – Libart +Name[nds]=KSVG-Dorstellhölper - Libart +Name[ne]=KSVG रेन्डरिङ ब्याकइन्ड - लिबर्ट +Name[nl]=KSVG weergavecomponent - Libart +Name[nn]=Modul for KSVG-teikning – Libart +Name[pl]=Narzędzie do renderowania KSVG - Libart +Name[pt]=Infra-Estrutura de Desenho do KSVG - Libart +Name[pt_BR]=Estrutura de KSVG Rendering Backend - Libart +Name[ro]=Motor de randare KSVG - Libart +Name[ru]=Движок отрисовки KSVG - Libart +Name[sk]=Nástroj pre zobrazovanie KSVG - libart +Name[sl]=Izrisovalnik KSVG - Libart +Name[sr]=KSVG-ов позадински систем за рендеровање — Libart +Name[sr@Latn]=KSVG-ov pozadinski sistem za renderovanje — Libart +Name[sv]=KSVG-uppritningsmodul - konstbibliotek +Name[ta]=KSVG வழங்கும் பின் அமைப்பு - லிபார்ட் +Name[tg]=Лағжандаи тасовироти KSVG - Libart +Name[tr]=KSVG Derleme Aracı - Libart +Name[uk]=Інтерфейс відтворення KSVG - Libart +Name[zh_CN]=KSVG 渲染后端 - Libart +Name[zh_HK]=KSVG 合成後端 - Libart +Name[zh_TW]=KSVG 上色後端介面 - Libart +Comment=Mature ksvg rendering backend +Comment[ar]=خلفية الرسم لksvg البالغة +Comment[bs]=Zreli ksvg rendering backend +Comment[ca]=Representador madur en segon pla de ksvg +Comment[cs]=Vyspělý vykreslovací nástroj KSVG +Comment[cy]=Ôl-wyneb llunio ksvg aeddfed +Comment[da]=Modent underliggende program til ksvg-visning +Comment[de]=Ausgereiftes KSVG-Darstellungsmodul +Comment[el]=Ώριμο σύστημα υποστήριξης αποτύπωσης του KSVG +Comment[es]=Motor de procesado de ksvg tradicional +Comment[et]=Kasutamisküps ksvg renderdamise taustarakendus +Comment[eu]=ksvg errendatze programa egonkorra +Comment[fa]=پایانۀ پشتیبانی کامل پرداخت ksvg +Comment[fi]=Kypsä ksvg-piirtäjän taustaohjelma +Comment[fr]=Moteur de rendu KSVG mature +Comment[gl]=Mecanismo de interpretación maduro ksvg +Comment[hi]=परिपक्व के-एसवीजी रेंडरिंग बैकएण्ड +Comment[hu]=Egy kiforrott megjelenítőmotor a KSVG-hez +Comment[is]=Reyndur ksvg teiknari +Comment[it]=Maturo backend di KSVG per il rendering +Comment[ja]=成熟した ksvg レンダリングバックエンド +Comment[kk]=Баяғы, жетілірдірген, KSVG кескіндеу бағдарламасы +Comment[km]=កម្មវិធីសម្រាប់បង្ហាញ ksvg ចាស់ៗ +Comment[lt]=Išvystyta ksvg atkūrimo programinė sąsaja +Comment[ms]=Tepi belakang menrealisasi ksvg matang +Comment[nb]=Velutviklet modul for ksvg-tegning +Comment[nds]=Reep KSVG-Dorstellhölper +Comment[ne]=पूर्ण विकसित ksvg रेन्डरिङ ब्याकइन्ड +Comment[nl]=Volgroeide KSVG weergavecomponent +Comment[nn]=Velutvikla modul for ksvg-teikning +Comment[pl]=Dopracowane narzędzie do renderowania KSVG +Comment[pt]=Uma infra-estrutura de desenho do ksvg mais madura +Comment[pt_BR]=Estrutura de renderização madura do ksvg +Comment[ro]=Motor de randare KSVG matur +Comment[ru]=Старый движок отрисовки ksvg +Comment[sk]=Stabilný nástroj pre zobrazovanie ksvg +Comment[sl]=Zrel izrisovalnik KSVG +Comment[sr]=Стари KSVG-ов позадински систем за рендеровање +Comment[sr@Latn]=Stari KSVG-ov pozadinski sistem za renderovanje +Comment[sv]=Mogen KSVG-uppritningsmodul +Comment[ta]=முழுமையான ksvg வழங்கும் பின் அமைப்பு +Comment[tg]=Лағжандаи тасовироти кӯҳнаи ksvg +Comment[tr]=Tamamlanmış ksvg derleme aracı +Comment[uk]=Стабільний інтерфейс відтворення KSVG +Comment[zh_CN]=稳定的 ksvg 渲染后端 +Comment[zh_HK]=成熟的 ksvg 合成後端 +Comment[zh_TW]=成熟的 ksvg 上色後端介面 +author=Nikolas Zimmermann <wildfox@kde.org> |