1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "private/qpaintengine_p.h"
41 #include "private/qpainter_p.h"
42 #include "private/qpicture_p.h"
43 #include "private/qfont_p.h"
44 
45 #ifndef QT_NO_PICTURE
46 
47 #include "qbuffer.h"
48 #include "qbytearray.h"
49 #include "qdatastream.h"
50 #include "qmath.h"
51 #include "qpaintengine_pic_p.h"
52 #include "qpicture.h"
53 #include "qpolygon.h"
54 #include "qrect.h"
55 #include <private/qtextengine_p.h>
56 
57 //#define QT_PICTURE_DEBUG
58 #include <qdebug.h>
59 
60 
61 QT_BEGIN_NAMESPACE
62 
63 class QPicturePaintEnginePrivate : public QPaintEnginePrivate
64 {
65     Q_DECLARE_PUBLIC(QPicturePaintEngine)
66 public:
67     QDataStream s;
68     QPainter *pt;
69     QPicturePrivate *pic_d;
70 };
71 
QPicturePaintEngine()72 QPicturePaintEngine::QPicturePaintEngine()
73     : QPaintEngine(*(new QPicturePaintEnginePrivate), AllFeatures)
74 {
75     Q_D(QPicturePaintEngine);
76     d->pt = nullptr;
77 }
78 
QPicturePaintEngine(QPaintEnginePrivate & dptr)79 QPicturePaintEngine::QPicturePaintEngine(QPaintEnginePrivate &dptr)
80     : QPaintEngine(dptr, AllFeatures)
81 {
82     Q_D(QPicturePaintEngine);
83     d->pt = nullptr;
84 }
85 
~QPicturePaintEngine()86 QPicturePaintEngine::~QPicturePaintEngine()
87 {
88 }
89 
begin(QPaintDevice * pd)90 bool QPicturePaintEngine::begin(QPaintDevice *pd)
91 {
92     Q_D(QPicturePaintEngine);
93 #ifdef QT_PICTURE_DEBUG
94     qDebug("QPicturePaintEngine::begin()");
95 #endif
96     Q_ASSERT(pd);
97     QPicture *pic = static_cast<QPicture *>(pd);
98 
99     d->pdev = pd;
100     d->pic_d = pic->d_func();
101     Q_ASSERT(d->pic_d);
102 
103     d->s.setDevice(&d->pic_d->pictb);
104     d->s.setVersion(d->pic_d->formatMajor);
105 
106     d->pic_d->pictb.open(QIODevice::WriteOnly | QIODevice::Truncate);
107     d->s.writeRawData(qt_mfhdr_tag, 4);
108     d->s << (quint16) 0 << (quint16) d->pic_d->formatMajor << (quint16) d->pic_d->formatMinor;
109     d->s << (quint8) QPicturePrivate::PdcBegin << (quint8) sizeof(qint32);
110     d->pic_d->brect = QRect();
111     if (d->pic_d->formatMajor >= 4) {
112         QRect r = pic->boundingRect();
113         d->s << (qint32) r.left() << (qint32) r.top() << (qint32) r.width()
114              << (qint32) r.height();
115     }
116     d->pic_d->trecs = 0;
117     d->s << (quint32)d->pic_d->trecs; // total number of records
118     d->pic_d->formatOk = false;
119     setActive(true);
120     return true;
121 }
122 
end()123 bool QPicturePaintEngine::end()
124 {
125     Q_D(QPicturePaintEngine);
126 #ifdef QT_PICTURE_DEBUG
127     qDebug("QPicturePaintEngine::end()");
128 #endif
129     d->pic_d->trecs++;
130     d->s << (quint8) QPicturePrivate::PdcEnd << (quint8) 0;
131     int cs_start = sizeof(quint32);                // pos of checksum word
132     int data_start = cs_start + sizeof(quint16);
133     int brect_start = data_start + 2*sizeof(qint16) + 2*sizeof(quint8);
134     int pos = d->pic_d->pictb.pos();
135     d->pic_d->pictb.seek(brect_start);
136     if (d->pic_d->formatMajor >= 4) { // bounding rectangle
137         QRect r = static_cast<QPicture *>(d->pdev)->boundingRect();
138         d->s << (qint32) r.left() << (qint32) r.top() << (qint32) r.width()
139              << (qint32) r.height();
140     }
141     d->s << (quint32) d->pic_d->trecs;                        // write number of records
142     d->pic_d->pictb.seek(cs_start);
143     QByteArray buf = d->pic_d->pictb.buffer();
144     quint16 cs = (quint16) qChecksum(buf.constData() + data_start, pos - data_start);
145     d->s << cs;                                // write checksum
146     d->pic_d->pictb.close();
147     setActive(false);
148     return true;
149 }
150 
151 #define SERIALIZE_CMD(c) \
152     d->pic_d->trecs++; \
153     d->s << (quint8) c; \
154     d->s << (quint8) 0; \
155     pos = d->pic_d->pictb.pos()
156 
updatePen(const QPen & pen)157 void QPicturePaintEngine::updatePen(const QPen &pen)
158 {
159     Q_D(QPicturePaintEngine);
160 #ifdef QT_PICTURE_DEBUG
161     qDebug() << " -> updatePen(): width:" << pen.width() << "style:"
162              << pen.style() << "color:" << pen.color();
163 #endif
164     int pos;
165     SERIALIZE_CMD(QPicturePrivate::PdcSetPen);
166     if (d->pic_d->in_memory_only) {
167         int index = d->pic_d->pen_list.size();
168         d->pic_d->pen_list.append(pen);
169         d->s << index;
170     } else {
171         d->s << pen;
172     }
173     writeCmdLength(pos, QRect(), false);
174 }
175 
updateCompositionMode(QPainter::CompositionMode cmode)176 void QPicturePaintEngine::updateCompositionMode(QPainter::CompositionMode cmode)
177 {
178     Q_D(QPicturePaintEngine);
179 #ifdef QT_PICTURE_DEBUG
180     qDebug() << " -> updateCompositionMode():" << cmode;
181 #endif
182     int pos;
183     SERIALIZE_CMD(QPicturePrivate::PdcSetCompositionMode);
184     d->s << (qint32)cmode;
185     writeCmdLength(pos, QRectF(), false);
186 }
187 
updateClipEnabled(bool enabled)188 void QPicturePaintEngine::updateClipEnabled(bool enabled)
189 {
190     Q_D(QPicturePaintEngine);
191 #ifdef QT_PICTURE_DEBUG
192     qDebug() << " -> updateClipEnabled():" << enabled;
193 #endif
194     int pos;
195     SERIALIZE_CMD(QPicturePrivate::PdcSetClipEnabled);
196     d->s << enabled;
197     writeCmdLength(pos, QRectF(), false);
198 }
199 
updateOpacity(qreal opacity)200 void QPicturePaintEngine::updateOpacity(qreal opacity)
201 {
202     Q_D(QPicturePaintEngine);
203 #ifdef QT_PICTURE_DEBUG
204     qDebug() << " -> updateOpacity():" << opacity;
205 #endif
206     int pos;
207     SERIALIZE_CMD(QPicturePrivate::PdcSetOpacity);
208     d->s << double(opacity);
209     writeCmdLength(pos, QRectF(), false);
210 }
211 
updateBrush(const QBrush & brush)212 void QPicturePaintEngine::updateBrush(const QBrush &brush)
213 {
214     Q_D(QPicturePaintEngine);
215 #ifdef QT_PICTURE_DEBUG
216     qDebug() << " -> updateBrush(): style:" << brush.style();
217 #endif
218     int pos;
219     SERIALIZE_CMD(QPicturePrivate::PdcSetBrush);
220     if (d->pic_d->in_memory_only) {
221         int index = d->pic_d->brush_list.size();
222         d->pic_d->brush_list.append(brush);
223         d->s << index;
224     } else {
225         d->s << brush;
226     }
227     writeCmdLength(pos, QRect(), false);
228 }
229 
updateBrushOrigin(const QPointF & p)230 void QPicturePaintEngine::updateBrushOrigin(const QPointF &p)
231 {
232     Q_D(QPicturePaintEngine);
233 #ifdef QT_PICTURE_DEBUG
234     qDebug() << " -> updateBrushOrigin(): " << p;
235 #endif
236     int pos;
237     SERIALIZE_CMD(QPicturePrivate::PdcSetBrushOrigin);
238     d->s << p;
239     writeCmdLength(pos, QRect(), false);
240 }
241 
updateFont(const QFont & font)242 void QPicturePaintEngine::updateFont(const QFont &font)
243 {
244     Q_D(QPicturePaintEngine);
245 #ifdef QT_PICTURE_DEBUG
246     qDebug() << " -> updateFont(): pt sz:" << font.pointSize();
247 #endif
248     int pos;
249     SERIALIZE_CMD(QPicturePrivate::PdcSetFont);
250     QFont fnt = font;
251     d->s << fnt;
252     writeCmdLength(pos, QRectF(), false);
253 }
254 
updateBackground(Qt::BGMode bgMode,const QBrush & bgBrush)255 void QPicturePaintEngine::updateBackground(Qt::BGMode bgMode, const QBrush &bgBrush)
256 {
257     Q_D(QPicturePaintEngine);
258 #ifdef QT_PICTURE_DEBUG
259     qDebug() << " -> updateBackground(): mode:" << bgMode << "style:" << bgBrush.style();
260 #endif
261     int pos;
262     SERIALIZE_CMD(QPicturePrivate::PdcSetBkColor);
263     d->s << bgBrush.color();
264     writeCmdLength(pos, QRect(), false);
265 
266     SERIALIZE_CMD(QPicturePrivate::PdcSetBkMode);
267     d->s << (qint8) bgMode;
268     writeCmdLength(pos, QRectF(), false);
269 }
270 
updateMatrix(const QTransform & matrix)271 void QPicturePaintEngine::updateMatrix(const QTransform &matrix)
272 {
273     Q_D(QPicturePaintEngine);
274 #ifdef QT_PICTURE_DEBUG
275     qDebug() << " -> updateMatrix():" << matrix;
276 #endif
277     int pos;
278     SERIALIZE_CMD(QPicturePrivate::PdcSetWMatrix);
279     d->s << matrix << (qint8) false;
280     writeCmdLength(pos, QRectF(), false);
281 }
282 
updateClipRegion(const QRegion & region,Qt::ClipOperation op)283 void QPicturePaintEngine::updateClipRegion(const QRegion &region, Qt::ClipOperation op)
284 {
285     Q_D(QPicturePaintEngine);
286 #ifdef QT_PICTURE_DEBUG
287     qDebug() << " -> updateClipRegion(): op:" << op
288              << "bounding rect:" << region.boundingRect();
289 #endif
290     int pos;
291     SERIALIZE_CMD(QPicturePrivate::PdcSetClipRegion);
292     d->s << region << qint8(op);
293     writeCmdLength(pos, QRectF(), false);
294 }
295 
updateClipPath(const QPainterPath & path,Qt::ClipOperation op)296 void QPicturePaintEngine::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
297 {
298     Q_D(QPicturePaintEngine);
299 #ifdef QT_PICTURE_DEBUG
300     qDebug() << " -> updateClipPath(): op:" << op
301              << "bounding rect:" << path.boundingRect();
302 #endif
303     int pos;
304 
305     SERIALIZE_CMD(QPicturePrivate::PdcSetClipPath);
306     d->s << path << qint8(op);
307     writeCmdLength(pos, QRectF(), false);
308 }
309 
updateRenderHints(QPainter::RenderHints hints)310 void QPicturePaintEngine::updateRenderHints(QPainter::RenderHints hints)
311 {
312     Q_D(QPicturePaintEngine);
313 #ifdef QT_PICTURE_DEBUG
314     qDebug() << " -> updateRenderHints(): " << hints;
315 #endif
316     int pos;
317     SERIALIZE_CMD(QPicturePrivate::PdcSetRenderHint);
318     d->s << (quint32) hints;
319     writeCmdLength(pos, QRect(), false);
320 }
321 
writeCmdLength(int pos,const QRectF & r,bool corr)322 void QPicturePaintEngine::writeCmdLength(int pos, const QRectF &r, bool corr)
323 {
324     Q_D(QPicturePaintEngine);
325     int newpos = d->pic_d->pictb.pos();            // new position
326     int length = newpos - pos;
327     QRectF br(r);
328 
329     if (length < 255) {                         // write 8-bit length
330         d->pic_d->pictb.seek(pos - 1);             // position to right index
331         d->s << (quint8)length;
332     } else {                                    // write 32-bit length
333         d->s << (quint32)0;                    // extend the buffer
334         d->pic_d->pictb.seek(pos - 1);             // position to right index
335         d->s << (quint8)255;                   // indicate 32-bit length
336         char *p = d->pic_d->pictb.buffer().data();
337         memmove(p+pos+4, p+pos, length);        // make room for 4 byte
338         d->s << (quint32)length;
339         newpos += 4;
340     }
341     d->pic_d->pictb.seek(newpos);                  // set to new position
342 
343     if (br.width() > 0.0 || br.height() > 0.0) {
344         if (corr) {                             // widen bounding rect
345             int w2 = painter()->pen().width() / 2;
346             br.setCoords(br.left() - w2, br.top() - w2,
347                          br.right() + w2, br.bottom() + w2);
348         }
349         br = painter()->transform().mapRect(br);
350         if (painter()->hasClipping()) {
351             QRectF cr = painter()->clipBoundingRect();
352             br &= cr;
353         }
354 
355         if (br.width() > 0.0 || br.height() > 0.0) {
356             int minx = qFloor(br.left());
357             int miny = qFloor(br.top());
358             int maxx = qCeil(br.right());
359             int maxy = qCeil(br.bottom());
360 
361             if (d->pic_d->brect.width() > 0 || d->pic_d->brect.height() > 0) {
362                 minx = qMin(minx, d->pic_d->brect.left());
363                 miny = qMin(miny, d->pic_d->brect.top());
364                 maxx = qMax(maxx, d->pic_d->brect.x() + d->pic_d->brect.width());
365                 maxy = qMax(maxy, d->pic_d->brect.y() + d->pic_d->brect.height());
366                 d->pic_d->brect = QRect(minx, miny, maxx - minx, maxy - miny);
367             } else {
368                 d->pic_d->brect = QRect(minx, miny, maxx - minx, maxy - miny);
369             }
370         }
371     }
372 }
373 
drawEllipse(const QRectF & rect)374 void QPicturePaintEngine::drawEllipse(const QRectF &rect)
375 {
376     Q_D(QPicturePaintEngine);
377 #ifdef QT_PICTURE_DEBUG
378     qDebug() << " -> drawEllipse():" << rect;
379 #endif
380     int pos;
381     SERIALIZE_CMD(QPicturePrivate::PdcDrawEllipse);
382     d->s << rect;
383     writeCmdLength(pos, rect, true);
384 }
385 
drawPath(const QPainterPath & path)386 void QPicturePaintEngine::drawPath(const QPainterPath &path)
387 {
388     Q_D(QPicturePaintEngine);
389 #ifdef QT_PICTURE_DEBUG
390     qDebug() << " -> drawPath():" << path.boundingRect();
391 #endif
392     int pos;
393     SERIALIZE_CMD(QPicturePrivate::PdcDrawPath);
394     d->s << path;
395     writeCmdLength(pos, path.boundingRect(), true);
396 }
397 
drawPolygon(const QPointF * points,int numPoints,PolygonDrawMode mode)398 void QPicturePaintEngine::drawPolygon(const QPointF *points, int numPoints, PolygonDrawMode mode)
399 {
400     Q_D(QPicturePaintEngine);
401 #ifdef QT_PICTURE_DEBUG
402     qDebug() << " -> drawPolygon(): size=" << numPoints;
403 #endif
404     int pos;
405 
406     QPolygonF polygon;
407     polygon.reserve(numPoints);
408     for (int i=0; i<numPoints; ++i)
409         polygon << points[i];
410 
411     if (mode == PolylineMode) {
412         SERIALIZE_CMD(QPicturePrivate::PdcDrawPolyline);
413         d->s << polygon;
414     } else {
415         SERIALIZE_CMD(QPicturePrivate::PdcDrawPolygon);
416         d->s << polygon;
417         d->s << (qint8)(mode == OddEvenMode ? 0 : 1);
418     }
419 
420     writeCmdLength(pos, polygon.boundingRect(), true);
421 }
422 
drawPixmap(const QRectF & r,const QPixmap & pm,const QRectF & sr)423 void QPicturePaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
424 {
425     Q_D(QPicturePaintEngine);
426 #ifdef QT_PICTURE_DEBUG
427     qDebug() << " -> drawPixmap():" << r;
428 #endif
429     int pos;
430     SERIALIZE_CMD(QPicturePrivate::PdcDrawPixmap);
431 
432     if (d->pic_d->in_memory_only) {
433         int index = d->pic_d->pixmap_list.size();
434         d->pic_d->pixmap_list.append(pm);
435         d->s << r << index << sr;
436     } else {
437         d->s << r << pm << sr;
438     }
439     writeCmdLength(pos, r, false);
440 }
441 
drawTiledPixmap(const QRectF & r,const QPixmap & pixmap,const QPointF & s)442 void QPicturePaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
443 {
444     Q_D(QPicturePaintEngine);
445 #ifdef QT_PICTURE_DEBUG
446     qDebug() << " -> drawTiledPixmap():" << r << s;
447 #endif
448     int pos;
449     SERIALIZE_CMD(QPicturePrivate::PdcDrawTiledPixmap);
450     if (d->pic_d->in_memory_only) {
451         int index = d->pic_d->pixmap_list.size();
452         d->pic_d->pixmap_list.append(pixmap);
453         d->s << r << index << s;
454     } else {
455         d->s << r << pixmap << s;
456     }
457     writeCmdLength(pos, r, false);
458 }
459 
drawImage(const QRectF & r,const QImage & image,const QRectF & sr,Qt::ImageConversionFlags flags)460 void QPicturePaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
461                                     Qt::ImageConversionFlags flags)
462 {
463     Q_D(QPicturePaintEngine);
464 #ifdef QT_PICTURE_DEBUG
465     qDebug() << " -> drawImage():" << r << sr;
466 #endif
467     int pos;
468     SERIALIZE_CMD(QPicturePrivate::PdcDrawImage);
469     if (d->pic_d->in_memory_only) {
470         int index = d->pic_d->image_list.size();
471         d->pic_d->image_list.append(image);
472         d->s << r << index << sr << (quint32) flags;
473     } else {
474         d->s << r << image << sr << (quint32) flags;
475     }
476     writeCmdLength(pos, r, false);
477 }
478 
drawTextItem(const QPointF & p,const QTextItem & ti)479 void QPicturePaintEngine::drawTextItem(const QPointF &p , const QTextItem &ti)
480 {
481     Q_D(QPicturePaintEngine);
482 #ifdef QT_PICTURE_DEBUG
483     qDebug() << " -> drawTextItem():" << p << ti.text();
484 #endif
485 
486     const QTextItemInt &si = static_cast<const QTextItemInt &>(ti);
487     if (si.chars == nullptr)
488         QPaintEngine::drawTextItem(p, ti); // Draw as path
489 
490     if (d->pic_d->formatMajor >= 9) {
491         int pos;
492         SERIALIZE_CMD(QPicturePrivate::PdcDrawTextItem);
493         QFont fnt = ti.font();
494         fnt.setUnderline(false);
495         fnt.setStrikeOut(false);
496         fnt.setOverline(false);
497 
498         qreal justificationWidth = 0;
499         if (si.justified)
500             justificationWidth = si.width.toReal();
501 
502         d->s << p << ti.text() << fnt << ti.renderFlags() << double(fnt.d->dpi)/qt_defaultDpi() << justificationWidth;
503         writeCmdLength(pos, /*brect=*/QRectF(), /*corr=*/false);
504     } else if (d->pic_d->formatMajor >= 8) {
505         // old old (buggy) format
506         int pos;
507         SERIALIZE_CMD(QPicturePrivate::PdcDrawTextItem);
508         d->s << QPointF(p.x(), p.y() - ti.ascent()) << ti.text() << ti.font() << ti.renderFlags();
509         writeCmdLength(pos, /*brect=*/QRectF(), /*corr=*/false);
510     } else {
511         // old (buggy) format
512         int pos;
513         SERIALIZE_CMD(QPicturePrivate::PdcDrawText2);
514         d->s << p << ti.text();
515         writeCmdLength(pos, QRectF(p, QSizeF(1,1)), true);
516     }
517 }
518 
updateState(const QPaintEngineState & state)519 void QPicturePaintEngine::updateState(const QPaintEngineState &state)
520 {
521     QPaintEngine::DirtyFlags flags = state.state();
522     if (flags & DirtyPen) updatePen(state.pen());
523     if (flags & DirtyBrush) updateBrush(state.brush());
524     if (flags & DirtyBrushOrigin) updateBrushOrigin(state.brushOrigin());
525     if (flags & DirtyFont) updateFont(state.font());
526     if (flags & DirtyBackground) updateBackground(state.backgroundMode(), state.backgroundBrush());
527     if (flags & DirtyTransform) updateMatrix(state.transform());
528     if (flags & DirtyClipEnabled) updateClipEnabled(state.isClipEnabled());
529     if (flags & DirtyClipRegion) updateClipRegion(state.clipRegion(), state.clipOperation());
530     if (flags & DirtyClipPath) updateClipPath(state.clipPath(), state.clipOperation());
531     if (flags & DirtyHints) updateRenderHints(state.renderHints());
532     if (flags & DirtyCompositionMode) updateCompositionMode(state.compositionMode());
533     if (flags & DirtyOpacity) updateOpacity(state.opacity());
534 }
535 
536 QT_END_NAMESPACE
537 
538 #endif // QT_NO_PICTURE
539