diff options
author | aavit <qt-info@nokia.com> | 2010-03-30 11:41:18 (GMT) |
---|---|---|
committer | aavit <qt-info@nokia.com> | 2010-03-31 11:49:56 (GMT) |
commit | 2fe059c863377befdcf65084a07a7f4841beef0d (patch) | |
tree | 4598ca69aa0419db69e9f3787f2d076baa3addb6 /src/plugins/imageformats | |
parent | 0056c0a654a930fe082787ec7e8579a6e74d9ad5 (diff) | |
download | Qt-2fe059c863377befdcf65084a07a7f4841beef0d.zip Qt-2fe059c863377befdcf65084a07a7f4841beef0d.tar.gz Qt-2fe059c863377befdcf65084a07a7f4841beef0d.tar.bz2 |
Total makeover of SVG image reader
Improved/fixed canRead(). Added svgz support. Implemented ClipRect,
ScaledClipRect and BackgroundColor support. Avoid data copy when reading
from memory. Improved support reading from sequential devices. Added svg
and svgz files to the qimagereader autotests.
Task-number: QTBUG-8227 and QTBUG-9053
Reviewed-by: Kim
Diffstat (limited to 'src/plugins/imageformats')
-rw-r--r-- | src/plugins/imageformats/svg/main.cpp | 16 | ||||
-rw-r--r-- | src/plugins/imageformats/svg/qsvgiohandler.cpp | 157 |
2 files changed, 106 insertions, 67 deletions
diff --git a/src/plugins/imageformats/svg/main.cpp b/src/plugins/imageformats/svg/main.cpp index dbbd3b7..329e9d4 100644 --- a/src/plugins/imageformats/svg/main.cpp +++ b/src/plugins/imageformats/svg/main.cpp @@ -62,26 +62,18 @@ public: QStringList QSvgPlugin::keys() const { - return QStringList() << QLatin1String("svg"); + return QStringList() << QLatin1String("svg") << QLatin1String("svgz"); } QImageIOPlugin::Capabilities QSvgPlugin::capabilities(QIODevice *device, const QByteArray &format) const { - //### canRead disabled for now because it's hard to detect - // whether the file is actually svg without parsing it - //if (device->isReadable() && QSvgIOHandler::canRead(device)) - - if (format == "svg") + if (format == "svg" || format == "svgz") return Capabilities(CanRead); - else - return 0; - - - if (!device->isOpen()) + if (!format.isEmpty()) return 0; Capabilities cap; - if (device->isReadable()) + if (device->isReadable() && QSvgIOHandler::canRead(device)) cap |= CanRead; return cap; } diff --git a/src/plugins/imageformats/svg/qsvgiohandler.cpp b/src/plugins/imageformats/svg/qsvgiohandler.cpp index f3670c6..8155569 100644 --- a/src/plugins/imageformats/svg/qsvgiohandler.cpp +++ b/src/plugins/imageformats/svg/qsvgiohandler.cpp @@ -48,6 +48,7 @@ #include "qpixmap.h" #include "qpainter.h" #include "qvariant.h" +#include "qbuffer.h" #include "qdebug.h" QT_BEGIN_NAMESPACE @@ -55,67 +56,54 @@ QT_BEGIN_NAMESPACE class QSvgIOHandlerPrivate { public: - QSvgIOHandlerPrivate() - : r(new QSvgRenderer()), loaded(false) + QSvgIOHandlerPrivate(QSvgIOHandler *qq) + : q(qq), loaded(false), readDone(false), backColor(Qt::transparent) {} - ~QSvgIOHandlerPrivate() - { - delete r; - } bool load(QIODevice *device); - static bool findSvgTag(QIODevice *device); - QSvgRenderer *r; - QSize defaultSize; - QSize currentSize; - bool loaded; + QSvgIOHandler *q; + QSvgRenderer r; + QXmlStreamReader xmlReader; + QSize defaultSize; + QRect clipRect; + QSize scaledSize; + QRect scaledClipRect; + bool loaded; + bool readDone; + QColor backColor; }; + bool QSvgIOHandlerPrivate::load(QIODevice *device) { if (loaded) return true; + if (q->format().isEmpty()) + q->canRead(); + + bool res = false; + QBuffer *buf = qobject_cast<QBuffer *>(device); + if (buf) { + res = r.load(buf->data()); + } else if (q->format() == "svgz") { + res = r.load(device->readAll()); // ### can't stream svgz + } else { + xmlReader.setDevice(device); + res = r.load(&xmlReader); //### doesn't leave pos() correctly + } - if (r->load(device->readAll())) { - defaultSize = QSize(r->viewBox().width(), r->viewBox().height()); - if (currentSize.isEmpty()) - currentSize = defaultSize; + if (res) { + defaultSize = QSize(r.viewBox().width(), r.viewBox().height()); + loaded = true; } - loaded = r->isValid(); return loaded; } -bool QSvgIOHandlerPrivate::findSvgTag(QIODevice *device) -{ - qint64 pos = device->pos(); - device->seek(0); - char buffer[256]; - const char svg_tag[] = "<svg"; - - while (1) { - int size = device->read(buffer, 256); - for (int i=0; i<size - 5; ++i) { - if (!memcmp(buffer + i, svg_tag, 4)) { - if (buffer[i+4] == ' ' || buffer[i+4] == '\t' - || buffer[i+4] == '\n' || buffer[i+4] == '\r') - { - device->seek(pos); - return true; - } - } - } - if (device->atEnd()) - break; - device->seek(device->pos()-4); - } - device->seek(pos); - return false; -} QSvgIOHandler::QSvgIOHandler() - : d(new QSvgIOHandlerPrivate()) + : d(new QSvgIOHandlerPrivate(this)) { } @@ -129,7 +117,20 @@ QSvgIOHandler::~QSvgIOHandler() bool QSvgIOHandler::canRead() const { - return QSvgIOHandlerPrivate::findSvgTag(device()); + if (!device()) + return false; + if (d->loaded && !d->readDone) + return true; // Will happen if we have been asked for the size + + QByteArray buf = device()->peek(8); + if (buf.startsWith("\x1f\x8b")) { + setFormat("svgz"); + return true; + } else if (buf.contains("<?xml") || buf.contains("<svg")) { + setFormat("svg"); + return true; + } + return false; } @@ -141,14 +142,41 @@ QByteArray QSvgIOHandler::name() const bool QSvgIOHandler::read(QImage *image) { - if (d->load(device())) { - *image = QImage(d->currentSize, QImage::Format_ARGB32_Premultiplied); - if (!d->currentSize.isEmpty()) { - image->fill(0x00000000); + if (!d->readDone && d->load(device())) { + bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid()); + QSize finalSize = d->defaultSize; + QRectF bounds; + if (xform && !d->defaultSize.isEmpty()) { + bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize)); + QPoint tr1, tr2; + QSizeF sc(1, 1); + if (d->clipRect.isValid()) { + tr1 = -d->clipRect.topLeft(); + finalSize = d->clipRect.size(); + } + if (d->scaledSize.isValid()) { + sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(), + qreal(d->scaledSize.height()) / finalSize.height()); + finalSize = d->scaledSize; + } + if (d->scaledClipRect.isValid()) { + tr2 = -d->scaledClipRect.topLeft(); + finalSize = d->scaledClipRect.size(); + } + QTransform t; + t.translate(tr2.x(), tr2.y()); + t.scale(sc.width(), sc.height()); + t.translate(tr1.x(), tr1.y()); + bounds = t.mapRect(bounds); + } + *image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied); + if (!finalSize.isEmpty()) { + image->fill(d->backColor.rgba()); QPainter p(image); - d->r->render(&p); + d->r.render(&p, bounds); p.end(); } + d->readDone = true; return true; } @@ -166,8 +194,17 @@ QVariant QSvgIOHandler::option(ImageOption option) const d->load(device()); return d->defaultSize; break; + case ClipRect: + return d->clipRect; + break; case ScaledSize: - return d->currentSize; + return d->scaledSize; + break; + case ScaledClipRect: + return d->scaledClipRect; + break; + case BackgroundColor: + return d->backColor; break; default: break; @@ -179,12 +216,17 @@ QVariant QSvgIOHandler::option(ImageOption option) const void QSvgIOHandler::setOption(ImageOption option, const QVariant & value) { switch(option) { - case Size: - d->defaultSize = value.toSize(); - d->currentSize = value.toSize(); + case ClipRect: + d->clipRect = value.toRect(); break; case ScaledSize: - d->currentSize = value.toSize(); + d->scaledSize = value.toSize(); + break; + case ScaledClipRect: + d->scaledClipRect = value.toRect(); + break; + case BackgroundColor: + d->backColor = value.value<QColor>(); break; default: break; @@ -198,7 +240,10 @@ bool QSvgIOHandler::supportsOption(ImageOption option) const { case ImageFormat: case Size: + case ClipRect: case ScaledSize: + case ScaledClipRect: + case BackgroundColor: return true; default: break; @@ -206,9 +251,11 @@ bool QSvgIOHandler::supportsOption(ImageOption option) const return false; } + bool QSvgIOHandler::canRead(QIODevice *device) { - return QSvgIOHandlerPrivate::findSvgTag(device); + QByteArray buf = device->peek(8); + return buf.startsWith("\x1f\x8b") || buf.contains("<?xml") || buf.contains("<svg"); } QT_END_NAMESPACE |