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

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