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

426 lines
11 KiB

/*
Copyright (C) 2001-2003 KSVG Team
This file is part of the KDE project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
aint with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "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 <tdeglobal.h>
#include <kgenericfactory.h>
#include "SVGPaint.h"
#include <tqdatetime.h>
#include <tqstring.h>
#include <tqimage.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, (double)x0, (double)y0 },
{ ART_LINETO, (double)x0, (double)y1 },
{ ART_LINETO, (double)x1, (double)y1 },
{ ART_LINETO, (double)x1, (double)y0 },
{ ART_LINETO, (double)x0, (double)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 TQRect& 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);
TQString 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(TQImage 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);
TQRect screenBBox(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
TQByteArray 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 TQRect &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)
{
TQString 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, TQByteArray mask, TQRect 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