/** ****************************************************************************** * * @file svgimageprovider.cpp * @author Dmytro Poplavskiy Copyright (C) 2012. * @addtogroup GCSPlugins GCS Plugins * @{ * @addtogroup OPMapPlugin QML Viewer Plugin * @{ * @brief Svg declarative image provider *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "svgimageprovider.h" #include #include #include SvgImageProvider::SvgImageProvider(const QString &basePath): QObject(), QDeclarativeImageProvider(QDeclarativeImageProvider::Image), m_basePath(basePath) { } SvgImageProvider::~SvgImageProvider() { qDeleteAll(m_renderers); } QSvgRenderer *SvgImageProvider::loadRenderer(const QString &svgFile) { QSvgRenderer *renderer = m_renderers.value(svgFile); if (!renderer) { renderer = new QSvgRenderer(svgFile); QString fn = QUrl::fromLocalFile(m_basePath).resolved(svgFile).toLocalFile(); //convert path to be relative to base if (!renderer->isValid()) renderer->load(fn); if (!renderer->isValid()) { qWarning() << "Failed to load svg file:" << svgFile << fn; delete renderer; return 0; } m_renderers.insert(svgFile, renderer); } return renderer; } /** Supported id format: fileName[!elementName[?parameters]] where parameters may be: vslice=1:2;hslice=2:4 - use the 3rd horizontal slice of total 4 slices, slice numbering starts from 0 requestedSize is related to the whole element size, even if slice is requested. usage: Image { source: "image://svg/pfd.svg!world" } */ QImage SvgImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { QString svgFile = id; QString element; QString parameters; int separatorPos = id.indexOf('!'); if (separatorPos != -1) { svgFile = id.left(separatorPos); element = id.mid(separatorPos+1); } int parametersPos = element.indexOf('?'); if (parametersPos != -1) { parameters = element.mid(parametersPos+1); element = element.left(parametersPos); } int hSlicesCount = 0; int hSlice = 0; int vSlicesCount = 0; int vSlice = 0; if (!parameters.isEmpty()) { QRegExp hSliceRx("hslice=(\\d+):(\\d+)"); if (hSliceRx.indexIn(parameters) != -1) { hSlice = hSliceRx.cap(1).toInt(); hSlicesCount = hSliceRx.cap(2).toInt(); } QRegExp vSliceRx("vslice=(\\d+):(\\d+)"); if (vSliceRx.indexIn(parameters) != -1) { vSlice = vSliceRx.cap(1).toInt(); vSlicesCount = vSliceRx.cap(2).toInt(); } } if (size) *size = QSize(); QSvgRenderer *renderer = loadRenderer(svgFile); if (!renderer) return QImage(); qreal xScale = 1.0; qreal yScale = 1.0; QSize docSize = renderer->defaultSize(); if (!requestedSize.isEmpty()) { if (!element.isEmpty()) { QRectF elementBounds = renderer->boundsOnElement(element); xScale = qreal(requestedSize.width())/elementBounds.width(); yScale = qreal(requestedSize.height())/elementBounds.height(); } else if (!docSize.isEmpty()) { xScale = qreal(requestedSize.width())/docSize.width(); yScale = qreal(requestedSize.height())/docSize.height(); } } //keep the aspect ratio //TODO: how to configure it? as a part of image path? xScale = yScale = qMin(xScale, yScale); if (!element.isEmpty()) { if (!renderer->elementExists(element)) { qWarning() << "invalid element:" << element << "of" << svgFile; return QImage(); } QRectF elementBounds = renderer->boundsOnElement(element); int elementWidth = qRound(elementBounds.width() * xScale); int elementHeigh = qRound(elementBounds.height() * yScale); int w = elementWidth; int h = elementHeigh; int x = 0; int y = 0; if (hSlicesCount > 1) { x = (w*hSlice)/hSlicesCount; w = (w*(hSlice+1))/hSlicesCount - x; } if (vSlicesCount > 1) { y = (h*(vSlice))/vSlicesCount; h = (h*(vSlice+1))/vSlicesCount - y; } QImage img(w, h, QImage::Format_ARGB32_Premultiplied); img.fill(0); QPainter p(&img); p.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::SmoothPixmapTransform); p.translate(-x,-y); renderer->render(&p, element, QRectF(0, 0, elementWidth, elementHeigh)); if (size) *size = QSize(w, h); //img.save(element+parameters+".png"); return img; } else { //render the whole svg file int w = qRound(docSize.width() * xScale); int h = qRound(docSize.height() * yScale); QImage img(w, h, QImage::Format_ARGB32_Premultiplied); img.fill(0); QPainter p(&img); p.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::SmoothPixmapTransform); p.scale(xScale, yScale); renderer->render(&p, QRectF(QPointF(), QSizeF(docSize))); if (size) *size = QSize(w, h); return img; } } QPixmap SvgImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) { return QPixmap::fromImage(requestImage(id, size, requestedSize)); } /*! \fn SvgImageProvider::scaledElementBounds(const QString &svgFile, const QString &element) Returns the bound of \a element in logical coordinates, scalled to the default size of svg document (so the bounds of whole doc would be (0,0,1,1) ). */ QRectF SvgImageProvider::scaledElementBounds(const QString &svgFile, const QString &elementName) { QSvgRenderer *renderer = loadRenderer(svgFile); if (!renderer) return QRectF(); if (!renderer->elementExists(elementName)) { qWarning() << "invalid element:" << elementName << "of" << svgFile; return QRectF(); } QRectF elementBounds = renderer->boundsOnElement(elementName); QMatrix matrix = renderer->matrixForElement(elementName); elementBounds = matrix.mapRect(elementBounds); QSize docSize = renderer->defaultSize(); return QRectF(elementBounds.x()/docSize.width(), elementBounds.y()/docSize.height(), elementBounds.width()/docSize.width(), elementBounds.height()/docSize.height()); }