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};
41 int frameCount{0};
42 int frameDelay{0};
44};
45
46
47bool QSvgIOHandlerPrivate::load(QIODevice *device)
48{
49 if (loadAttempted)
50 return loadStatus;
51
52 if (q->format().isEmpty())
54 loadAttempted = true;
55
56 // # The SVG renderer doesn't handle trailing, unrelated data, so we must
57 // assume that all available data in the device is to be read.
58 bool res = false;
59 QBuffer *buf = qobject_cast<QBuffer *>(device);
60 if (buf) {
61 const QByteArray &ba = buf->data();
62 res = r.load(QByteArray::fromRawData(ba.constData() + buf->pos(), ba.size() - buf->pos()));
63 buf->seek(ba.size());
64#ifndef QT_NO_COMPRESS
65 } else if (q->format() == "svgz") {
66 res = r.load(device->readAll());
67#endif
68 } else {
69 xmlReader.setDevice(device);
70 res = r.load(&xmlReader);
71 }
72
73 if (res) {
74 defaultSize = r.defaultSize();
75 loadStatus = true;
76 if (r.animated()) {
77 const int duration = r.animationDuration();
78 const int fps = r.framesPerSecond();
79 frameCount = qMax(1, static_cast<int>(qint64(duration) * fps / 1000));
80 frameDelay = fps > 0 ? 1000 / fps : 0;
81 }
82 }
83
84 return loadStatus;
85}
86
87
88
94
95
97{
98 delete d;
99}
100
102{
103 if (!device())
104 return false;
105
106 if (d->loadAttempted) {
107 if (!d->loadStatus)
108 return false;
109 if (d->r.animated())
110 return d->currentFrame < d->frameCount;
111 return !d->readDone;
112 }
113
114 // Not yet loaded — probe the device to determine format
115 bool isCompressed = false;
116 if (QSvgDocument::isLikelySvg(device(), &isCompressed)) {
117 setFormat(isCompressed ? "svgz" : "svg");
118 return true;
119 }
120 return false;
121}
122
123bool QSvgIOHandler::read(QImage *image)
124{
125 if (!d->load(device()))
126 return false;
127
128 // For non-animated SVGs, preserve original single-read behavior
129 if (!d->r.animated()) {
130 if (d->readDone)
131 return false;
132 } else {
133 // For animated SVGs, set the current frame on the renderer
135 return false;
136 d->r.setCurrentFrame(d->currentFrame);
137 }
138
139 bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid());
140 QSize finalSize = d->defaultSize;
141 QRectF bounds;
142 if (xform && !d->defaultSize.isEmpty()) {
143 bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize));
144 QPoint tr1, tr2;
145 QSizeF sc(1, 1);
146 if (d->clipRect.isValid()) {
147 tr1 = -d->clipRect.topLeft();
148 finalSize = d->clipRect.size();
149 }
150 if (d->scaledSize.isValid()) {
151 sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(),
152 qreal(d->scaledSize.height()) / finalSize.height());
153 finalSize = d->scaledSize;
154 }
155 if (d->scaledClipRect.isValid()) {
156 tr2 = -d->scaledClipRect.topLeft();
157 finalSize = d->scaledClipRect.size();
158 }
159 QTransform t;
160 t.translate(tr2.x(), tr2.y());
161 t.scale(sc.width(), sc.height());
162 t.translate(tr1.x(), tr1.y());
163 bounds = t.mapRect(bounds);
164 }
165 if (finalSize.isEmpty()) {
166 *image = QImage();
167 } else {
168 if (qMax(finalSize.width(), finalSize.height()) > 0xffff)
169 return false; // Assume corrupted file
170 if (!QImageIOHandler::allocateImage(finalSize, QImage::Format_ARGB32_Premultiplied, image))
171 return false;
172 image->fill(d->backColor.rgba());
173 QPainter p(image);
174 d->r.render(&p, bounds);
175 p.end();
176 }
177
178 d->readDone = true;
179 if (d->r.animated())
181 return true;
182}
183
184
185QVariant QSvgIOHandler::option(ImageOption option) const
186{
187 switch(option) {
188 case ImageFormat:
189 return QImage::Format_ARGB32_Premultiplied;
190 break;
191 case Size:
193 return d->defaultSize;
194 break;
195 case ClipRect:
196 return d->clipRect;
197 break;
198 case ScaledSize:
199 return d->scaledSize;
200 break;
201 case ScaledClipRect:
202 return d->scaledClipRect;
203 break;
204 case BackgroundColor:
205 return d->backColor;
206 break;
207 case Animation:
209 return d->r.animated();
210 break;
211 default:
212 break;
213 }
214 return QVariant();
215}
216
217
218void QSvgIOHandler::setOption(ImageOption option, const QVariant & value)
219{
220 switch(option) {
221 case ClipRect:
222 d->clipRect = value.toRect();
223 break;
224 case ScaledSize:
225 d->scaledSize = value.toSize();
226 break;
227 case ScaledClipRect:
228 d->scaledClipRect = value.toRect();
229 break;
230 case BackgroundColor:
231 d->backColor = value.value<QColor>();
232 break;
233 default:
234 break;
235 }
236}
237
238
239bool QSvgIOHandler::supportsOption(ImageOption option) const
240{
241 switch(option)
242 {
243 case ImageFormat:
244 case Size:
245 case ClipRect:
246 case ScaledSize:
247 case ScaledClipRect:
248 case BackgroundColor:
249 case Animation:
250 return true;
251 default:
252 break;
253 }
254 return false;
255}
256
257
262
263bool QSvgIOHandler::jumpToImage(int imageNumber)
264{
265 if (!d->load(device()) || !d->r.animated())
266 return false;
267 if (imageNumber < 0 || imageNumber >= d->frameCount)
268 return false;
269 d->currentFrame = imageNumber;
270 return true;
271}
272
274{
275 return 0;
276}
277
279{
280 if (!d->load(device()))
281 return 0;
282 if (d->r.animated())
283 return d->frameCount;
284 return 1;
285}
286
288{
289 if (d->load(device()) && d->r.animated())
290 return d->frameDelay;
291 return 0;
292}
293
295{
296 if (d->load(device()) && d->r.animated())
297 return d->currentFrame - 1; // currentFrame is the *next* frame to read
298 return 0;
299}
300
301bool QSvgIOHandler::canRead(QIODevice *device)
302{
303 return QSvgDocument::isLikelySvg(device);
304}
305
306QT_END_NAMESPACE
307
308#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,...
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
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...
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
void setOption(ImageOption option, const QVariant &value) override
Sets the option option with the value value.
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.