diff options
Diffstat (limited to 'ksvg/plugin/backends/libart/LibartCanvas.cpp')
-rw-r--r-- | ksvg/plugin/backends/libart/LibartCanvas.cpp | 425 |
1 files changed, 425 insertions, 0 deletions
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 |