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
27QT_BEGIN_NAMESPACE
28
29void 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
83const char *qt_mfhdr_tag = "QPIC"; // header tag
84static const quint16 mfhdr_maj = QDataStream::Qt_DefaultCompiledVersion; // major version #
85static 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
102QPicture::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
127QPicture::QPicture(const QPicture &pic)
128 : QPaintDevice(), d_ptr(pic.d_ptr)
129{
130}
131
132/*! \internal */
133QPicture::QPicture(QPicturePrivate &dptr)
134 : QPaintDevice(),
135 d_ptr(&dptr)
136{
137}
138
139/*!
140 Destroys the picture.
141*/
142QPicture::~QPicture()
143{
144}
145
146/*!
147 \internal
148*/
149int 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
180bool QPicture::isNull() const
181{
182 return d_func()->pictb.buffer().isNull();
183}
184
185uint QPicture::size() const
186{
187 return d_func()->pictb.buffer().size();
188}
189
190const char* QPicture::data() const
191{
192 return d_func()->pictb.buffer();
193}
194
195void QPicture::detach()
196{
197 d_ptr.detach();
198}
199
200bool 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
212void 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
227bool 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
243bool 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
259bool 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
279bool 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
296QRect 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
314void 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
329bool 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//
368class QFakeDevice : public QPaintDevice
369{
370public:
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
389private:
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
400bool 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
857int 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*/
920QPicture& 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*/
939QPicturePrivate::QPicturePrivate()
940 : in_memory_only(false)
941{
942}
943
944/*!
945 \internal
946
947 Copy-Constructs a QPicturePrivate. Needed when detaching.
948*/
949QPicturePrivate::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
970void 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*/
985bool 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 */
1052QPaintEngine *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
1071QDataStream &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
1090QDataStream &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
1111QT_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

source code of qtbase/src/gui/image/qpicture.cpp