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 | |
4 | #include "qpicture.h" |
5 | #include <private/qpicture_p.h> |
6 | |
7 | #ifndef QT_NO_PICTURE |
8 | |
9 | #include <private/qfactoryloader_p.h> |
10 | #include <private/qpaintengine_pic_p.h> |
11 | #include <private/qfont_p.h> |
12 | #include <qguiapplication.h> |
13 | |
14 | #include "qdatastream.h" |
15 | #include "qfile.h" |
16 | #include "qimage.h" |
17 | #include "qmutex.h" |
18 | #include "qpainter.h" |
19 | #include "qpainterpath.h" |
20 | #include "qpixmap.h" |
21 | #include "qregion.h" |
22 | #include "qdebug.h" |
23 | #include <QtCore/private/qlocking_p.h> |
24 | |
25 | #include <algorithm> |
26 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | void qt_format_text(const QFont &fnt, const QRectF &_r, |
30 | int tf, const QTextOption *opt, const QString& str, QRectF *brect, |
31 | int tabstops, int *, int tabarraylen, |
32 | QPainter *painter); |
33 | |
34 | /*! |
35 | \class QPicture |
36 | \brief The QPicture class is a paint device that records and |
37 | replays QPainter commands. |
38 | |
39 | \inmodule QtGui |
40 | \ingroup shared |
41 | |
42 | |
43 | A picture serializes painter commands to an IO device in a |
44 | platform-independent format. They are sometimes referred to as meta-files. |
45 | |
46 | Qt pictures use a proprietary binary format. Unlike native picture |
47 | (meta-file) formats on many window systems, Qt pictures have no |
48 | limitations regarding their contents. Everything that can be |
49 | painted on a widget or pixmap (e.g., fonts, pixmaps, regions, |
50 | transformed graphics, etc.) can also be stored in a picture. |
51 | |
52 | QPicture is resolution independent, i.e. a QPicture can be |
53 | displayed on different devices (for example svg, pdf, ps, printer |
54 | and screen) looking the same. This is, for instance, needed for |
55 | WYSIWYG print preview. QPicture runs in the default system dpi, |
56 | and scales the painter to match differences in resolution |
57 | depending on the window system. |
58 | |
59 | Example of how to record a picture: |
60 | \snippet picture/picture.cpp 0 |
61 | |
62 | Note that the list of painter commands is reset on each call to |
63 | the QPainter::begin() function. |
64 | |
65 | Example of how to replay a picture: |
66 | \snippet picture/picture.cpp 1 |
67 | |
68 | Pictures can also be drawn using play(). Some basic data about a |
69 | picture is available, for example, size(), isNull() and |
70 | boundingRect(). |
71 | |
72 | \sa QMovie |
73 | */ |
74 | |
75 | /*! |
76 | \fn QPicture &QPicture::operator=(QPicture &&other) |
77 | |
78 | Move-assigns \a other to this QPicture instance. |
79 | |
80 | \since 5.2 |
81 | */ |
82 | |
83 | const char *qt_mfhdr_tag = "QPIC" ; // header tag |
84 | static const quint16 mfhdr_maj = QDataStream::Qt_DefaultCompiledVersion; // major version # |
85 | static const quint16 mfhdr_min = 0; // minor version # |
86 | |
87 | /*! |
88 | Constructs an empty picture. |
89 | |
90 | The \a formatVersion parameter may be used to \e create a QPicture |
91 | that can be read by applications that are compiled with earlier |
92 | versions of Qt. |
93 | |
94 | Note that the default formatVersion is -1 which signifies the |
95 | current release, i.e. for Qt 4.0 a formatVersion of 7 is the same |
96 | as the default formatVersion of -1. |
97 | |
98 | Reading pictures generated by earlier versions of Qt is not |
99 | supported in Qt 4.0. |
100 | */ |
101 | |
102 | QPicture::QPicture(int formatVersion) |
103 | : QPaintDevice(), |
104 | d_ptr(new QPicturePrivate) |
105 | { |
106 | Q_D(QPicture); |
107 | |
108 | if (formatVersion == 0) |
109 | qWarning(msg: "QPicture: invalid format version 0" ); |
110 | |
111 | // still accept the 0 default from before Qt 3.0. |
112 | if (formatVersion > 0 && formatVersion != (int)mfhdr_maj) { |
113 | d->formatMajor = formatVersion; |
114 | d->formatMinor = 0; |
115 | d->formatOk = false; |
116 | } else { |
117 | d->resetFormat(); |
118 | } |
119 | } |
120 | |
121 | /*! |
122 | Constructs a copy of \a pic. |
123 | |
124 | This constructor is fast thanks to \l{implicit sharing}. |
125 | */ |
126 | |
127 | QPicture::QPicture(const QPicture &pic) |
128 | : QPaintDevice(), d_ptr(pic.d_ptr) |
129 | { |
130 | } |
131 | |
132 | /*! \internal */ |
133 | QPicture::QPicture(QPicturePrivate &dptr) |
134 | : QPaintDevice(), |
135 | d_ptr(&dptr) |
136 | { |
137 | } |
138 | |
139 | /*! |
140 | Destroys the picture. |
141 | */ |
142 | QPicture::~QPicture() |
143 | { |
144 | } |
145 | |
146 | /*! |
147 | \internal |
148 | */ |
149 | int QPicture::devType() const |
150 | { |
151 | return QInternal::Picture; |
152 | } |
153 | |
154 | /*! |
155 | \fn bool QPicture::isNull() const |
156 | |
157 | Returns \c true if the picture contains no data; otherwise returns |
158 | false. |
159 | */ |
160 | |
161 | /*! |
162 | \fn uint QPicture::size() const |
163 | |
164 | Returns the size of the picture data. |
165 | |
166 | \sa data() |
167 | */ |
168 | |
169 | /*! |
170 | \fn const char* QPicture::data() const |
171 | |
172 | Returns a pointer to the picture data. The pointer is only valid |
173 | until the next non-const function is called on this picture. The |
174 | returned pointer is 0 if the picture contains no data. |
175 | |
176 | \sa size(), isNull() |
177 | */ |
178 | |
179 | |
180 | bool QPicture::isNull() const |
181 | { |
182 | return d_func()->pictb.buffer().isNull(); |
183 | } |
184 | |
185 | uint QPicture::size() const |
186 | { |
187 | return d_func()->pictb.buffer().size(); |
188 | } |
189 | |
190 | const char* QPicture::data() const |
191 | { |
192 | return d_func()->pictb.buffer(); |
193 | } |
194 | |
195 | void QPicture::detach() |
196 | { |
197 | d_ptr.detach(); |
198 | } |
199 | |
200 | bool QPicture::isDetached() const |
201 | { |
202 | return d_func()->ref.loadRelaxed() == 1; |
203 | } |
204 | |
205 | /*! |
206 | Sets the picture data directly from \a data and \a size. This |
207 | function copies the input data. |
208 | |
209 | \sa data(), size() |
210 | */ |
211 | |
212 | void QPicture::setData(const char* data, uint size) |
213 | { |
214 | detach(); |
215 | d_func()->pictb.setData(data, len: size); |
216 | d_func()->resetFormat(); // we'll have to check |
217 | } |
218 | |
219 | |
220 | /*! |
221 | Loads a picture from the file specified by \a fileName and returns |
222 | true if successful; otherwise invalidates the picture and returns \c false. |
223 | |
224 | \sa save() |
225 | */ |
226 | |
227 | bool QPicture::load(const QString &fileName) |
228 | { |
229 | QFile f(fileName); |
230 | if (!f.open(flags: QIODevice::ReadOnly)) { |
231 | operator=(other: QPicture()); |
232 | return false; |
233 | } |
234 | return load(dev: &f); |
235 | } |
236 | |
237 | /*! |
238 | \overload |
239 | |
240 | \a dev is the device to use for loading. |
241 | */ |
242 | |
243 | bool QPicture::load(QIODevice *dev) |
244 | { |
245 | detach(); |
246 | QByteArray a = dev->readAll(); |
247 | |
248 | d_func()->pictb.setData(a); // set byte array in buffer |
249 | return d_func()->checkFormat(); |
250 | } |
251 | |
252 | /*! |
253 | Saves a picture to the file specified by \a fileName and returns |
254 | true if successful; otherwise returns \c false. |
255 | |
256 | \sa load() |
257 | */ |
258 | |
259 | bool QPicture::save(const QString &fileName) |
260 | { |
261 | if (paintingActive()) { |
262 | qWarning(msg: "QPicture::save: still being painted on. " |
263 | "Call QPainter::end() first" ); |
264 | return false; |
265 | } |
266 | |
267 | QFile f(fileName); |
268 | if (!f.open(flags: QIODevice::WriteOnly)) |
269 | return false; |
270 | return save(dev: &f); |
271 | } |
272 | |
273 | /*! |
274 | \overload |
275 | |
276 | \a dev is the device to use for saving. |
277 | */ |
278 | |
279 | bool QPicture::save(QIODevice *dev) |
280 | { |
281 | if (paintingActive()) { |
282 | qWarning(msg: "QPicture::save: still being painted on. " |
283 | "Call QPainter::end() first" ); |
284 | return false; |
285 | } |
286 | |
287 | dev->write(data: d_func()->pictb.buffer(), len: d_func()->pictb.buffer().size()); |
288 | return true; |
289 | } |
290 | |
291 | /*! |
292 | Returns the picture's bounding rectangle or an invalid rectangle |
293 | if the picture contains no data. |
294 | */ |
295 | |
296 | QRect QPicture::boundingRect() const |
297 | { |
298 | Q_D(const QPicture); |
299 | // Use override rect where possible. |
300 | if (!d->override_rect.isEmpty()) |
301 | return d->override_rect; |
302 | |
303 | if (!d->formatOk) |
304 | d_ptr->checkFormat(); |
305 | |
306 | return d->brect; |
307 | } |
308 | |
309 | /*! |
310 | Sets the picture's bounding rectangle to \a r. The automatically |
311 | calculated value is overridden. |
312 | */ |
313 | |
314 | void QPicture::setBoundingRect(const QRect &r) |
315 | { |
316 | d_func()->override_rect = r; |
317 | } |
318 | |
319 | /*! |
320 | Replays the picture using \a painter, and returns \c true if |
321 | successful; otherwise returns \c false. |
322 | |
323 | This function does exactly the same as QPainter::drawPicture() |
324 | with (x, y) = (0, 0). |
325 | |
326 | \note The state of the painter isn't preserved by this function. |
327 | */ |
328 | |
329 | bool QPicture::play(QPainter *painter) |
330 | { |
331 | Q_D(QPicture); |
332 | |
333 | if (d->pictb.size() == 0) // nothing recorded |
334 | return true; |
335 | |
336 | if (!d->formatOk && !d->checkFormat()) |
337 | return false; |
338 | |
339 | d->pictb.open(openMode: QIODevice::ReadOnly); // open buffer device |
340 | QDataStream s; |
341 | s.setDevice(&d->pictb); // attach data stream to buffer |
342 | s.device()->seek(pos: 10); // go directly to the data |
343 | s.setVersion(d->formatMajor == 4 ? 3 : d->formatMajor); |
344 | |
345 | quint8 c, clen; |
346 | quint32 nrecords; |
347 | s >> c >> clen; |
348 | Q_ASSERT(c == QPicturePrivate::PdcBegin); |
349 | // bounding rect was introduced in ver 4. Read in checkFormat(). |
350 | if (d->formatMajor >= 4) { |
351 | qint32 dummy; |
352 | s >> dummy >> dummy >> dummy >> dummy; |
353 | } |
354 | s >> nrecords; |
355 | if (!exec(p: painter, ds&: s, i: nrecords)) { |
356 | qWarning(msg: "QPicture::play: Format error" ); |
357 | d->pictb.close(); |
358 | return false; |
359 | } |
360 | d->pictb.close(); |
361 | return true; // no end-command |
362 | } |
363 | |
364 | |
365 | // |
366 | // QFakeDevice is used to create fonts with a custom DPI |
367 | // |
368 | class QFakeDevice : public QPaintDevice |
369 | { |
370 | public: |
371 | QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); } |
372 | void setDpiX(int dpi) { dpi_x = dpi; } |
373 | void setDpiY(int dpi) { dpi_y = dpi; } |
374 | QPaintEngine *paintEngine() const override { return nullptr; } |
375 | int metric(PaintDeviceMetric m) const override |
376 | { |
377 | switch(m) { |
378 | case PdmPhysicalDpiX: |
379 | case PdmDpiX: |
380 | return dpi_x; |
381 | case PdmPhysicalDpiY: |
382 | case PdmDpiY: |
383 | return dpi_y; |
384 | default: |
385 | return QPaintDevice::metric(metric: m); |
386 | } |
387 | } |
388 | |
389 | private: |
390 | int dpi_x; |
391 | int dpi_y; |
392 | }; |
393 | |
394 | /*! |
395 | \internal |
396 | Iterates over the internal picture data and draws the picture using |
397 | \a painter. |
398 | */ |
399 | |
400 | bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) |
401 | { |
402 | Q_D(QPicture); |
403 | #if defined(QT_DEBUG) |
404 | int strm_pos; |
405 | #endif |
406 | quint8 c; // command id |
407 | quint8 tiny_len; // 8-bit length descriptor |
408 | qint32 len; // 32-bit length descriptor |
409 | qint16 i_16, i1_16, i2_16; // parameters... |
410 | qint8 i_8; |
411 | quint32 ul; |
412 | double dbl; |
413 | bool bl; |
414 | QByteArray str1; |
415 | QString str; |
416 | QPointF p, p1, p2; |
417 | QPoint ip, ip1, ip2; |
418 | QRect ir; |
419 | QRectF r; |
420 | QPolygonF a; |
421 | QPolygon ia; |
422 | QColor color; |
423 | QFont font; |
424 | QPen pen; |
425 | QBrush brush; |
426 | QRegion rgn; |
427 | qreal wmatrix[6]; |
428 | QTransform matrix; |
429 | |
430 | QTransform worldMatrix = painter->transform(); |
431 | worldMatrix.scale(sx: qreal(painter->device()->logicalDpiX()) / qreal(qt_defaultDpiX()), |
432 | sy: qreal(painter->device()->logicalDpiY()) / qreal(qt_defaultDpiY())); |
433 | painter->setTransform(transform: worldMatrix); |
434 | |
435 | while (nrecords-- && !s.atEnd()) { |
436 | s >> c; // read cmd |
437 | s >> tiny_len; // read param length |
438 | if (tiny_len == 255) // longer than 254 bytes |
439 | s >> len; |
440 | else |
441 | len = tiny_len; |
442 | #if defined(QT_DEBUG) |
443 | strm_pos = s.device()->pos(); |
444 | #endif |
445 | switch (c) { // exec cmd |
446 | case QPicturePrivate::PdcNOP: |
447 | break; |
448 | case QPicturePrivate::PdcDrawPoint: |
449 | if (d->formatMajor <= 5) { |
450 | s >> ip; |
451 | painter->drawPoint(p: ip); |
452 | } else { |
453 | s >> p; |
454 | painter->drawPoint(p); |
455 | } |
456 | break; |
457 | case QPicturePrivate::PdcDrawPoints: |
458 | // ## implement me in the picture paint engine |
459 | // s >> a >> i1_32 >> i2_32; |
460 | // painter->drawPoints(a.mid(i1_32, i2_32)); |
461 | break; |
462 | case QPicturePrivate::PdcDrawPath: { |
463 | QPainterPath path; |
464 | s >> path; |
465 | painter->drawPath(path); |
466 | break; |
467 | } |
468 | case QPicturePrivate::PdcDrawLine: |
469 | if (d->formatMajor <= 5) { |
470 | s >> ip1 >> ip2; |
471 | painter->drawLine(p1: ip1, p2: ip2); |
472 | } else { |
473 | s >> p1 >> p2; |
474 | painter->drawLine(p1, p2); |
475 | } |
476 | break; |
477 | case QPicturePrivate::PdcDrawRect: |
478 | if (d->formatMajor <= 5) { |
479 | s >> ir; |
480 | painter->drawRect(r: ir); |
481 | } else { |
482 | s >> r; |
483 | painter->drawRect(rect: r); |
484 | } |
485 | break; |
486 | case QPicturePrivate::PdcDrawRoundRect: |
487 | if (d->formatMajor <= 5) { |
488 | s >> ir >> i1_16 >> i2_16; |
489 | painter->drawRoundedRect(rect: ir, xRadius: i1_16, yRadius: i2_16, mode: Qt::RelativeSize); |
490 | } else { |
491 | s >> r >> i1_16 >> i2_16; |
492 | painter->drawRoundedRect(rect: r, xRadius: i1_16, yRadius: i2_16, mode: Qt::RelativeSize); |
493 | } |
494 | break; |
495 | case QPicturePrivate::PdcDrawEllipse: |
496 | if (d->formatMajor <= 5) { |
497 | s >> ir; |
498 | painter->drawEllipse(r: ir); |
499 | } else { |
500 | s >> r; |
501 | painter->drawEllipse(r); |
502 | } |
503 | break; |
504 | case QPicturePrivate::PdcDrawArc: |
505 | if (d->formatMajor <= 5) { |
506 | s >> ir; |
507 | r = ir; |
508 | } else { |
509 | s >> r; |
510 | } |
511 | s >> i1_16 >> i2_16; |
512 | painter->drawArc(rect: r, a: i1_16, alen: i2_16); |
513 | break; |
514 | case QPicturePrivate::PdcDrawPie: |
515 | if (d->formatMajor <= 5) { |
516 | s >> ir; |
517 | r = ir; |
518 | } else { |
519 | s >> r; |
520 | } |
521 | s >> i1_16 >> i2_16; |
522 | painter->drawPie(rect: r, a: i1_16, alen: i2_16); |
523 | break; |
524 | case QPicturePrivate::PdcDrawChord: |
525 | if (d->formatMajor <= 5) { |
526 | s >> ir; |
527 | r = ir; |
528 | } else { |
529 | s >> r; |
530 | } |
531 | s >> i1_16 >> i2_16; |
532 | painter->drawChord(rect: r, a: i1_16, alen: i2_16); |
533 | break; |
534 | case QPicturePrivate::PdcDrawLineSegments: |
535 | s >> ia; |
536 | painter->drawLines(pointPairs: ia); |
537 | ia.clear(); |
538 | break; |
539 | case QPicturePrivate::PdcDrawPolyline: |
540 | if (d->formatMajor <= 5) { |
541 | s >> ia; |
542 | painter->drawPolyline(polyline: ia); |
543 | ia.clear(); |
544 | } else { |
545 | s >> a; |
546 | painter->drawPolyline(polyline: a); |
547 | a.clear(); |
548 | } |
549 | break; |
550 | case QPicturePrivate::PdcDrawPolygon: |
551 | if (d->formatMajor <= 5) { |
552 | s >> ia >> i_8; |
553 | painter->drawPolygon(polygon: ia, fillRule: i_8 ? Qt::WindingFill : Qt::OddEvenFill); |
554 | ia.clear(); |
555 | } else { |
556 | s >> a >> i_8; |
557 | painter->drawPolygon(polygon: a, fillRule: i_8 ? Qt::WindingFill : Qt::OddEvenFill); |
558 | a.clear(); |
559 | } |
560 | break; |
561 | case QPicturePrivate::PdcDrawCubicBezier: { |
562 | s >> ia; |
563 | QPainterPath path; |
564 | Q_ASSERT(ia.size() == 4); |
565 | path.moveTo(p: ia.value(i: 0)); |
566 | path.cubicTo(ctrlPt1: ia.value(i: 1), ctrlPt2: ia.value(i: 2), endPt: ia.value(i: 3)); |
567 | painter->strokePath(path, pen: painter->pen()); |
568 | ia.clear(); |
569 | } |
570 | break; |
571 | case QPicturePrivate::PdcDrawText: |
572 | s >> ip >> str1; |
573 | painter->drawText(p: ip, s: QString::fromLatin1(ba: str1)); |
574 | break; |
575 | case QPicturePrivate::PdcDrawTextFormatted: |
576 | s >> ir >> i_16 >> str1; |
577 | painter->drawText(r: ir, flags: i_16, text: QString::fromLatin1(ba: str1)); |
578 | break; |
579 | case QPicturePrivate::PdcDrawText2: |
580 | if (d->formatMajor <= 5) { |
581 | s >> ip >> str; |
582 | painter->drawText(p: ip, s: str); |
583 | } else { |
584 | s >> p >> str; |
585 | painter->drawText(p, s: str); |
586 | } |
587 | break; |
588 | case QPicturePrivate::PdcDrawText2Formatted: |
589 | s >> ir; |
590 | s >> i_16; |
591 | s >> str; |
592 | painter->drawText(r: ir, flags: i_16, text: str); |
593 | break; |
594 | case QPicturePrivate::PdcDrawTextItem: { |
595 | s >> p >> str >> font >> ul; |
596 | |
597 | // the text layout direction is not used here because it's already |
598 | // aligned when QPicturePaintEngine::drawTextItem() serializes the |
599 | // drawText() call, therefore ul is unsed in this context |
600 | |
601 | if (d->formatMajor >= 9) { |
602 | s >> dbl; |
603 | QFont fnt(font); |
604 | if (dbl != 1.0) { |
605 | QFakeDevice fake; |
606 | fake.setDpiX(qRound(d: dbl*qt_defaultDpiX())); |
607 | fake.setDpiY(qRound(d: dbl*qt_defaultDpiY())); |
608 | fnt = QFont(font, &fake); |
609 | } |
610 | |
611 | qreal justificationWidth; |
612 | s >> justificationWidth; |
613 | |
614 | int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight; |
615 | |
616 | QSizeF size(1, 1); |
617 | if (justificationWidth > 0) { |
618 | size.setWidth(justificationWidth); |
619 | flags |= Qt::TextJustificationForced; |
620 | flags |= Qt::AlignJustify; |
621 | } |
622 | |
623 | QFontMetrics fm(fnt); |
624 | QPointF pt(p.x(), p.y() - fm.ascent()); |
625 | qt_format_text(fnt, r: QRectF(pt, size), tf: flags, /*opt*/nullptr, |
626 | str, /*brect=*/nullptr, /*tabstops=*/0, /*...*/nullptr, /*tabarraylen=*/0, painter); |
627 | } else { |
628 | qt_format_text(fnt: font, r: QRectF(p, QSizeF(1, 1)), tf: Qt::TextSingleLine | Qt::TextDontClip, /*opt*/nullptr, |
629 | str, /*brect=*/nullptr, /*tabstops=*/0, /*...*/nullptr, /*tabarraylen=*/0, painter); |
630 | } |
631 | |
632 | break; |
633 | } |
634 | case QPicturePrivate::PdcDrawPixmap: { |
635 | QPixmap pixmap; |
636 | if (d->formatMajor < 4) { |
637 | s >> ip >> pixmap; |
638 | painter->drawPixmap(p: ip, pm: pixmap); |
639 | } else if (d->formatMajor <= 5) { |
640 | s >> ir >> pixmap; |
641 | painter->drawPixmap(r: ir, pm: pixmap); |
642 | } else { |
643 | QRectF sr; |
644 | if (d->in_memory_only) { |
645 | int index; |
646 | s >> r >> index >> sr; |
647 | Q_ASSERT(index < d->pixmap_list.size()); |
648 | pixmap = d->pixmap_list.value(i: index); |
649 | } else { |
650 | s >> r >> pixmap >> sr; |
651 | } |
652 | painter->drawPixmap(targetRect: r, pixmap, sourceRect: sr); |
653 | } |
654 | } |
655 | break; |
656 | case QPicturePrivate::PdcDrawTiledPixmap: { |
657 | QPixmap pixmap; |
658 | if (d->in_memory_only) { |
659 | int index; |
660 | s >> r >> index >> p; |
661 | Q_ASSERT(index < d->pixmap_list.size()); |
662 | pixmap = d->pixmap_list.value(i: index); |
663 | } else { |
664 | s >> r >> pixmap >> p; |
665 | } |
666 | painter->drawTiledPixmap(rect: r, pm: pixmap, offset: p); |
667 | } |
668 | break; |
669 | case QPicturePrivate::PdcDrawImage: { |
670 | QImage image; |
671 | if (d->formatMajor < 4) { |
672 | s >> p >> image; |
673 | painter->drawImage(p, image); |
674 | } else if (d->formatMajor <= 5){ |
675 | s >> ir >> image; |
676 | painter->drawImage(targetRect: ir, image, sourceRect: QRect(0, 0, ir.width(), ir.height())); |
677 | } else { |
678 | QRectF sr; |
679 | if (d->in_memory_only) { |
680 | int index; |
681 | s >> r >> index >> sr >> ul; |
682 | Q_ASSERT(index < d->image_list.size()); |
683 | image = d->image_list.value(i: index); |
684 | } else { |
685 | s >> r >> image >> sr >> ul; |
686 | } |
687 | painter->drawImage(targetRect: r, image, sourceRect: sr, flags: Qt::ImageConversionFlags(ul)); |
688 | } |
689 | } |
690 | break; |
691 | case QPicturePrivate::PdcBegin: |
692 | s >> ul; // number of records |
693 | if (!exec(painter, s, nrecords: ul)) |
694 | return false; |
695 | break; |
696 | case QPicturePrivate::PdcEnd: |
697 | if (nrecords == 0) |
698 | return true; |
699 | break; |
700 | case QPicturePrivate::PdcSave: |
701 | painter->save(); |
702 | break; |
703 | case QPicturePrivate::PdcRestore: |
704 | painter->restore(); |
705 | break; |
706 | case QPicturePrivate::PdcSetBkColor: |
707 | s >> color; |
708 | painter->setBackground(color); |
709 | break; |
710 | case QPicturePrivate::PdcSetBkMode: |
711 | s >> i_8; |
712 | painter->setBackgroundMode((Qt::BGMode)i_8); |
713 | break; |
714 | case QPicturePrivate::PdcSetROP: // NOP |
715 | s >> i_8; |
716 | break; |
717 | case QPicturePrivate::PdcSetBrushOrigin: |
718 | if (d->formatMajor <= 5) { |
719 | s >> ip; |
720 | painter->setBrushOrigin(ip); |
721 | } else { |
722 | s >> p; |
723 | painter->setBrushOrigin(p); |
724 | } |
725 | break; |
726 | case QPicturePrivate::PdcSetFont: |
727 | s >> font; |
728 | painter->setFont(font); |
729 | break; |
730 | case QPicturePrivate::PdcSetPen: |
731 | if (d->in_memory_only) { |
732 | int index; |
733 | s >> index; |
734 | Q_ASSERT(index < d->pen_list.size()); |
735 | pen = d->pen_list.value(i: index); |
736 | } else { |
737 | s >> pen; |
738 | } |
739 | painter->setPen(pen); |
740 | break; |
741 | case QPicturePrivate::PdcSetBrush: |
742 | if (d->in_memory_only) { |
743 | int index; |
744 | s >> index; |
745 | Q_ASSERT(index < d->brush_list.size()); |
746 | brush = d->brush_list.value(i: index); |
747 | } else { |
748 | s >> brush; |
749 | } |
750 | painter->setBrush(brush); |
751 | break; |
752 | case QPicturePrivate::PdcSetVXform: |
753 | s >> i_8; |
754 | painter->setViewTransformEnabled(i_8); |
755 | break; |
756 | case QPicturePrivate::PdcSetWindow: |
757 | if (d->formatMajor <= 5) { |
758 | s >> ir; |
759 | painter->setWindow(ir); |
760 | } else { |
761 | s >> r; |
762 | painter->setWindow(r.toRect()); |
763 | } |
764 | break; |
765 | case QPicturePrivate::PdcSetViewport: |
766 | if (d->formatMajor <= 5) { |
767 | s >> ir; |
768 | painter->setViewport(ir); |
769 | } else { |
770 | s >> r; |
771 | painter->setViewport(r.toRect()); |
772 | } |
773 | break; |
774 | case QPicturePrivate::PdcSetWXform: |
775 | s >> i_8; |
776 | painter->setWorldMatrixEnabled(i_8); |
777 | break; |
778 | case QPicturePrivate::PdcSetWMatrix: |
779 | if (d->formatMajor >= 8) { |
780 | s >> matrix >> i_8; |
781 | } else { |
782 | s >> wmatrix[0] >> wmatrix[1] |
783 | >> wmatrix[2] >> wmatrix[3] |
784 | >> wmatrix[4] >> wmatrix[5] >> i_8; |
785 | matrix = QTransform(wmatrix[0], wmatrix[1], |
786 | wmatrix[2], wmatrix[3], |
787 | wmatrix[4], wmatrix[5]); |
788 | } |
789 | // i_8 is always false due to updateXForm() in qpaintengine_pic.cpp |
790 | painter->setTransform(transform: matrix * worldMatrix, combine: i_8); |
791 | break; |
792 | case QPicturePrivate::PdcSetClip: |
793 | s >> i_8; |
794 | painter->setClipping(i_8); |
795 | break; |
796 | case QPicturePrivate::PdcSetClipRegion: |
797 | s >> rgn >> i_8; |
798 | if (d->formatMajor >= 9) { |
799 | painter->setClipRegion(rgn, op: Qt::ClipOperation(i_8)); |
800 | } else { |
801 | painter->setClipRegion(rgn); |
802 | } |
803 | break; |
804 | case QPicturePrivate::PdcSetClipPath: |
805 | { |
806 | QPainterPath path; |
807 | s >> path >> i_8; |
808 | painter->setClipPath(path, op: Qt::ClipOperation(i_8)); |
809 | break; |
810 | } |
811 | case QPicturePrivate::PdcSetRenderHint: |
812 | s >> ul; |
813 | painter->setRenderHint(hint: QPainter::Antialiasing, |
814 | on: bool(ul & QPainter::Antialiasing)); |
815 | painter->setRenderHint(hint: QPainter::SmoothPixmapTransform, |
816 | on: bool(ul & QPainter::SmoothPixmapTransform)); |
817 | painter->setRenderHint(hint: QPainter::NonCosmeticBrushPatterns, |
818 | on: bool(ul & QPainter::NonCosmeticBrushPatterns)); |
819 | break; |
820 | case QPicturePrivate::PdcSetCompositionMode: |
821 | s >> ul; |
822 | painter->setCompositionMode((QPainter::CompositionMode)ul); |
823 | break; |
824 | case QPicturePrivate::PdcSetClipEnabled: |
825 | s >> bl; |
826 | painter->setClipping(bl); |
827 | break; |
828 | case QPicturePrivate::PdcSetOpacity: |
829 | s >> dbl; |
830 | painter->setOpacity(qreal(dbl)); |
831 | break; |
832 | default: |
833 | qWarning(msg: "QPicture::play: Invalid command %d" , c); |
834 | if (len > 0) // skip unknown command |
835 | s.device()->seek(pos: s.device()->pos()+len); |
836 | } |
837 | #if defined(QT_DEBUG) |
838 | //qDebug("device->at(): %i, strm_pos: %i len: %i", (int)s.device()->pos(), strm_pos, len); |
839 | Q_ASSERT(qint32(s.device()->pos() - strm_pos) == len); |
840 | #endif |
841 | } |
842 | return false; |
843 | } |
844 | |
845 | /*! |
846 | \internal |
847 | |
848 | Internal implementation of the virtual QPaintDevice::metric() |
849 | function. |
850 | |
851 | A picture has the following hard-coded values: numcolors=16777216 |
852 | and depth=24. |
853 | |
854 | \a m is the metric to get. |
855 | */ |
856 | |
857 | int QPicture::metric(PaintDeviceMetric m) const |
858 | { |
859 | int val; |
860 | QRect brect = boundingRect(); |
861 | switch (m) { |
862 | case PdmWidth: |
863 | val = brect.width(); |
864 | break; |
865 | case PdmHeight: |
866 | val = brect.height(); |
867 | break; |
868 | case PdmWidthMM: |
869 | val = int(25.4/qt_defaultDpiX()*brect.width()); |
870 | break; |
871 | case PdmHeightMM: |
872 | val = int(25.4/qt_defaultDpiY()*brect.height()); |
873 | break; |
874 | case PdmDpiX: |
875 | case PdmPhysicalDpiX: |
876 | val = qt_defaultDpiX(); |
877 | break; |
878 | case PdmDpiY: |
879 | case PdmPhysicalDpiY: |
880 | val = qt_defaultDpiY(); |
881 | break; |
882 | case PdmNumColors: |
883 | val = 16777216; |
884 | break; |
885 | case PdmDepth: |
886 | val = 24; |
887 | break; |
888 | case PdmDevicePixelRatio: |
889 | val = 1; |
890 | break; |
891 | case PdmDevicePixelRatioScaled: |
892 | val = 1 * QPaintDevice::devicePixelRatioFScale(); |
893 | break; |
894 | default: |
895 | val = 0; |
896 | qWarning(msg: "QPicture::metric: Invalid metric command" ); |
897 | } |
898 | return val; |
899 | } |
900 | |
901 | /*! |
902 | \fn void QPicture::detach() |
903 | \internal |
904 | Detaches from shared picture data and makes sure that this picture |
905 | is the only one referring to the data. |
906 | |
907 | If multiple pictures share common data, this picture makes a copy |
908 | of the data and detaches itself from the sharing mechanism. |
909 | Nothing is done if there is just a single reference. |
910 | */ |
911 | |
912 | /*! \fn bool QPicture::isDetached() const |
913 | \internal |
914 | */ |
915 | |
916 | /*! |
917 | Assigns picture \a p to this picture and returns a reference to |
918 | this picture. |
919 | */ |
920 | QPicture& QPicture::operator=(const QPicture &p) |
921 | { |
922 | d_ptr = p.d_ptr; |
923 | return *this; |
924 | } |
925 | |
926 | /*! |
927 | \fn void QPicture::swap(QPicture &other) |
928 | \since 4.8 |
929 | |
930 | Swaps picture \a other with this picture. This operation is very |
931 | fast and never fails. |
932 | */ |
933 | |
934 | /*! |
935 | \internal |
936 | |
937 | Constructs a QPicturePrivate |
938 | */ |
939 | QPicturePrivate::QPicturePrivate() |
940 | : in_memory_only(false) |
941 | { |
942 | } |
943 | |
944 | /*! |
945 | \internal |
946 | |
947 | Copy-Constructs a QPicturePrivate. Needed when detaching. |
948 | */ |
949 | QPicturePrivate::QPicturePrivate(const QPicturePrivate &other) |
950 | : trecs(other.trecs), |
951 | formatOk(other.formatOk), |
952 | formatMinor(other.formatMinor), |
953 | brect(other.brect), |
954 | override_rect(other.override_rect), |
955 | in_memory_only(false) |
956 | { |
957 | pictb.setData(data: other.pictb.data(), len: other.pictb.size()); |
958 | if (other.pictb.isOpen()) { |
959 | pictb.open(openMode: other.pictb.openMode()); |
960 | pictb.seek(off: other.pictb.pos()); |
961 | } |
962 | } |
963 | |
964 | /*! |
965 | \internal |
966 | |
967 | Sets formatOk to false and resets the format version numbers to default |
968 | */ |
969 | |
970 | void QPicturePrivate::resetFormat() |
971 | { |
972 | formatOk = false; |
973 | formatMajor = mfhdr_maj; |
974 | formatMinor = mfhdr_min; |
975 | } |
976 | |
977 | |
978 | /*! |
979 | \internal |
980 | |
981 | Checks data integrity and format version number. Set formatOk to |
982 | true on success, to false otherwise. Returns the resulting formatOk |
983 | value. |
984 | */ |
985 | bool QPicturePrivate::checkFormat() |
986 | { |
987 | resetFormat(); |
988 | |
989 | // can't check anything in an empty buffer |
990 | if (pictb.size() == 0 || pictb.isOpen()) |
991 | return false; |
992 | |
993 | pictb.open(openMode: QIODevice::ReadOnly); // open buffer device |
994 | QDataStream s; |
995 | s.setDevice(&pictb); // attach data stream to buffer |
996 | |
997 | char mf_id[4]; // picture header tag |
998 | s.readRawData(mf_id, len: 4); // read actual tag |
999 | int bufSize = pictb.buffer().size(); |
1000 | if (memcmp(s1: mf_id, s2: qt_mfhdr_tag, n: 4) != 0 || bufSize < 12) { // wrong header id or size |
1001 | qWarning(msg: "QPicturePaintEngine::checkFormat: Incorrect header" ); |
1002 | pictb.close(); |
1003 | return false; |
1004 | } |
1005 | |
1006 | int cs_start = sizeof(quint32); // pos of checksum word |
1007 | int data_start = cs_start + sizeof(quint16); |
1008 | quint16 cs,ccs; |
1009 | const QByteArray buf = pictb.buffer(); // pointer to data |
1010 | |
1011 | s >> cs; // read checksum |
1012 | ccs = (quint16) qChecksum(data: QByteArrayView(buf.constData() + data_start, buf.size() - data_start)); |
1013 | if (ccs != cs) { |
1014 | qWarning(msg: "QPicturePaintEngine::checkFormat: Invalid checksum %x, %x expected" , |
1015 | ccs, cs); |
1016 | pictb.close(); |
1017 | return false; |
1018 | } |
1019 | |
1020 | quint16 major, minor; |
1021 | s >> major >> minor; // read version number |
1022 | if (major > mfhdr_maj) { // new, incompatible version |
1023 | qWarning(msg: "QPicturePaintEngine::checkFormat: Incompatible version %d.%d" , |
1024 | major, minor); |
1025 | pictb.close(); |
1026 | return false; |
1027 | } |
1028 | s.setVersion(major != 4 ? major : 3); |
1029 | |
1030 | quint8 c, clen; |
1031 | s >> c >> clen; |
1032 | if (c == QPicturePrivate::PdcBegin) { |
1033 | if (!(major >= 1 && major <= 3)) { |
1034 | qint32 l, t, w, h; |
1035 | s >> l >> t >> w >> h; |
1036 | brect = QRect(l, t, w, h); |
1037 | } |
1038 | } else { |
1039 | qWarning(msg: "QPicturePaintEngine::checkFormat: Format error" ); |
1040 | pictb.close(); |
1041 | return false; |
1042 | } |
1043 | pictb.close(); |
1044 | |
1045 | formatOk = true; // picture seems to be ok |
1046 | formatMajor = major; |
1047 | formatMinor = minor; |
1048 | return true; |
1049 | } |
1050 | |
1051 | /*! \internal */ |
1052 | QPaintEngine *QPicture::paintEngine() const |
1053 | { |
1054 | if (!d_func()->paintEngine) |
1055 | const_cast<QPicture*>(this)->d_func()->paintEngine.reset(other: new QPicturePaintEngine); |
1056 | return d_func()->paintEngine.data(); |
1057 | } |
1058 | |
1059 | /***************************************************************************** |
1060 | QPicture stream functions |
1061 | *****************************************************************************/ |
1062 | |
1063 | #ifndef QT_NO_DATASTREAM |
1064 | /*! |
1065 | \relates QPicture |
1066 | |
1067 | Writes picture \a r to the stream \a s and returns a reference to |
1068 | the stream. |
1069 | */ |
1070 | |
1071 | QDataStream &operator<<(QDataStream &s, const QPicture &r) |
1072 | { |
1073 | quint32 size = r.d_func()->pictb.buffer().size(); |
1074 | s << size; |
1075 | // null picture ? |
1076 | if (size == 0) |
1077 | return s; |
1078 | // just write the whole buffer to the stream |
1079 | s.writeRawData (r.d_func()->pictb.buffer(), len: r.d_func()->pictb.buffer().size()); |
1080 | return s; |
1081 | } |
1082 | |
1083 | /*! |
1084 | \relates QPicture |
1085 | |
1086 | Reads a picture from the stream \a s into picture \a r and returns |
1087 | a reference to the stream. |
1088 | */ |
1089 | |
1090 | QDataStream &operator>>(QDataStream &s, QPicture &r) |
1091 | { |
1092 | QDataStream sr; |
1093 | |
1094 | // "init"; this code is similar to the beginning of QPicture::cmd() |
1095 | sr.setDevice(&r.d_func()->pictb); |
1096 | sr.setVersion(r.d_func()->formatMajor); |
1097 | quint32 len; |
1098 | s >> len; |
1099 | QByteArray data; |
1100 | if (len > 0) { |
1101 | data.resize(size: len); |
1102 | s.readRawData(data.data(), len); |
1103 | } |
1104 | |
1105 | r.d_func()->pictb.setData(data); |
1106 | r.d_func()->resetFormat(); |
1107 | return s; |
1108 | } |
1109 | #endif // QT_NO_DATASTREAM |
1110 | |
1111 | QT_END_NAMESPACE |
1112 | |
1113 | #endif // QT_NO_PICTURE |
1114 | |
1115 | /*! |
1116 | \typedef QPicture::DataPtr |
1117 | \internal |
1118 | */ |
1119 | |
1120 | /*! |
1121 | \fn DataPtr &QPicture::data_ptr() |
1122 | \internal |
1123 | */ |
1124 | |