Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsvgiohandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
7
8#ifndef QT_NO_SVGRENDERER
9
10#include "qsvgrenderer.h"
11#include "private/qsvgdocument_p.h"
12#include "qimage.h"
13#include "qpixmap.h"
14#include "qpainter.h"
15#include "qvariant.h"
16#include "qbuffer.h"
17#include "qdebug.h"
18
20
22{
23public:
27
28 bool load(QIODevice *device);
29
30 QSvgIOHandler *q = nullptr;
34 QRect clipRect;
37 bool loadAttempted = false;
38 bool loadStatus = false;
39 bool readDone = false;
40 int currentFrame = 0;
41 int frameCount = 0;
42 int frameDelay = 0;
44};
45
46
47bool QSvgIOHandlerPrivate::load(QIODevice *device)
48{
49 if (loadAttempted)
50 return loadStatus;
51 loadAttempted = true;
52 if (q->format().isEmpty())
54
55 // # The SVG renderer doesn't handle trailing, unrelated data, so we must
56 // assume that all available data in the device is to be read.
57 bool res = false;
58 QBuffer *buf = qobject_cast<QBuffer *>(device);
59 if (buf) {
60 const QByteArray &ba = buf->data();
61 res = r.load(QByteArray::fromRawData(ba.constData() + buf->pos(), ba.size() - buf->pos()));
62 buf->seek(ba.size());
63#ifndef QT_NO_COMPRESS
64 } else if (q->format() == "svgz") {
65 res = r.load(device->readAll());
66#endif
67 } else {
68 xmlReader.setDevice(device);
69 res = r.load(&xmlReader);
70 }
71
72 if (res) {
73 defaultSize = r.defaultSize();
74 loadStatus = true;
75 if (r.animated()) {
76 const int duration = r.animationDuration();
77 const int fps = r.framesPerSecond();
78 frameCount = qMax(1, static_cast<int>(qint64(duration) * fps / 1000));
79 frameDelay = fps > 0 ? 1000 / fps : 0;
80 }
81 }
82
83 return loadStatus;
84}
85
86
87
93
94
96{
97 delete d;
98}
99
101{
102 if (!device())
103 return false;
104
105 if (d->loadAttempted) {
106 if (!d->loadStatus)
107 return false;
108 if (d->r.animated())
109 return d->currentFrame < d->frameCount;
110 return !d->readDone;
111 }
112
113 // Not yet loaded — probe the device to determine format
114 bool isCompressed = false;
115 if (QSvgDocument::isLikelySvg(device(), &isCompressed)) {
116 setFormat(isCompressed ? "svgz" : "svg");
117 return true;
118 }
119 return false;
120}
121
122bool QSvgIOHandler::read(QImage *image)
123{
124 if (!d->load(device()))
125 return false;
126
127 // For non-animated SVGs, preserve original single-read behavior
128 if (!d->r.animated()) {
129 if (d->readDone)
130 return false;
131 } else {
132 // For animated SVGs, set the current frame on the renderer
134 return false;
135 d->r.setCurrentFrame(d->currentFrame);
136 }
137
138 bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid());
139 QSize finalSize = d->defaultSize;
140 QRectF bounds;
141 if (xform && !d->defaultSize.isEmpty()) {
142 bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize));
143 QPoint tr1, tr2;
144 QSizeF sc(1, 1);
145 if (d->clipRect.isValid()) {
146 tr1 = -d->clipRect.topLeft();
147 finalSize = d->clipRect.size();
148 }
149 if (d->scaledSize.isValid()) {
150 sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(),
151 qreal(d->scaledSize.height()) / finalSize.height());
152 finalSize = d->scaledSize;
153 }
154 if (d->scaledClipRect.isValid()) {
155 tr2 = -d->scaledClipRect.topLeft();
156 finalSize = d->scaledClipRect.size();
157 }
158 QTransform t;
159 t.translate(tr2.x(), tr2.y());
160 t.scale(sc.width(), sc.height());
161 t.translate(tr1.x(), tr1.y());
162 bounds = t.mapRect(bounds);
163 }
164 if (finalSize.isEmpty()) {
165 *image = QImage();
166 } else {
167 if (qMax(finalSize.width(), finalSize.height()) > 0xffff)
168 return false; // Assume corrupted file
169 if (!QImageIOHandler::allocateImage(finalSize, QImage::Format_ARGB32_Premultiplied, image))
170 return false;
171 image->fill(d->backColor.rgba());
172 QPainter p(image);
173 d->r.render(&p, bounds);
174 p.end();
175 }
176
177 d->readDone = true;
178 if (d->r.animated())
180 return true;
181}
182
183
184QVariant QSvgIOHandler::option(ImageOption option) const
185{
186 switch(option) {
187 case ImageFormat:
188 return QImage::Format_ARGB32_Premultiplied;
189 break;
190 case Size:
192 return d->defaultSize;
193 break;
194 case ClipRect:
195 return d->clipRect;
196 break;
197 case ScaledSize:
198 return d->scaledSize;
199 break;
200 case ScaledClipRect:
201 return d->scaledClipRect;
202 break;
203 case BackgroundColor:
204 return d->backColor;
205 break;
206 case Animation:
208 return d->r.animated();
209 break;
210 default:
211 break;
212 }
213 return QVariant();
214}
215
216
217void QSvgIOHandler::setOption(ImageOption option, const QVariant & value)
218{
219 switch(option) {
220 case ClipRect:
221 d->clipRect = value.toRect();
222 break;
223 case ScaledSize:
224 d->scaledSize = value.toSize();
225 break;
226 case ScaledClipRect:
227 d->scaledClipRect = value.toRect();
228 break;
229 case BackgroundColor:
230 d->backColor = value.value<QColor>();
231 break;
232 default:
233 break;
234 }
235}
236
237
238bool QSvgIOHandler::supportsOption(ImageOption option) const
239{
240 switch(option)
241 {
242 case ImageFormat:
243 case Size:
244 case ClipRect:
245 case ScaledSize:
246 case ScaledClipRect:
247 case BackgroundColor:
248 case Animation:
249 return true;
250 default:
251 break;
252 }
253 return false;
254}
255
256
261
262bool QSvgIOHandler::jumpToImage(int imageNumber)
263{
264 if (!d->load(device()) || !d->r.animated())
265 return false;
266 if (imageNumber < 0 || imageNumber >= d->frameCount)
267 return false;
268 d->currentFrame = imageNumber;
269 return true;
270}
271
273{
274 return 0;
275}
276
278{
279 return d->frameCount;
280}
281
283{
284 return d->frameDelay;
285}
286
288{
289 if (d->r.animated())
290 return d->readDone ? d->currentFrame : -1;
291 return 0;
292}
293
294
295bool QSvgIOHandler::canRead(QIODevice *device)
296{
297 return QSvgDocument::isLikelySvg(device);
298}
299
300QT_END_NAMESPACE
301
302#endif // QT_NO_SVGRENDERER
QIODevice * device() const
Returns the device currently assigned to QImageReader, or \nullptr if no device has been assigned.
friend class QPainter
\inmodule QtCore\reentrant
Definition qpoint.h:30
bool load(QIODevice *device)
QXmlStreamReader xmlReader
QSvgIOHandlerPrivate(QSvgIOHandler *qq)
int currentImageNumber() const override
For image formats that support animation, this function returns the sequence number of the current im...
bool jumpToImage(int imageNumber) override
For image formats that support animation, this function jumps to the image whose sequence number is i...
bool canRead() const override
Returns true if an image can be read from the device (i.e., the image format is supported,...
bool read(QImage *image) override
Read an image from the device, and stores it in image.
int imageCount() const override
For image formats that support animation, this function returns the number of images in the animation...
int loopCount() const override
For image formats that support animation, this function returns the number of times the animation sho...
static bool canRead(QIODevice *device)
int nextImageDelay() const override
For image formats that support animation, this function returns the number of milliseconds to wait un...
bool jumpToNextImage() override
For image formats that support animation, this function jumps to the next image.
Combined button and popup list for selecting options.