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 "qpixmap_blitter_p.h"
5
6#include <qpainter.h>
7#include <qimage.h>
8#include <qrandom.h>
9#include <qscreen.h>
10
11#include <private/qguiapplication_p.h>
12#include <private/qblittable_p.h>
13
14#include <private/qdrawhelper_p.h>
15#include <private/qfont_p.h>
16
17#ifndef QT_NO_BLITTABLE
18QT_BEGIN_NAMESPACE
19
20static int global_ser_no = 0;
21
22QBlittablePlatformPixmap::QBlittablePlatformPixmap()
23 : QPlatformPixmap(QPlatformPixmap::PixmapType,BlitterClass)
24 , m_alpha(false)
25 , m_devicePixelRatio(1.0)
26#ifdef QT_BLITTER_RASTEROVERLAY
27 ,m_rasterOverlay(0), m_unmergedCopy(0)
28#endif //QT_BLITTER_RASTEROVERLAY
29{
30 setSerialNumber(++global_ser_no);
31}
32
33QBlittablePlatformPixmap::~QBlittablePlatformPixmap()
34{
35#ifdef QT_BLITTER_RASTEROVERLAY
36 delete m_rasterOverlay;
37 delete m_unmergedCopy;
38#endif //QT_BLITTER_RASTEROVERLAY
39}
40
41QBlittable *QBlittablePlatformPixmap::blittable() const
42{
43 if (!m_blittable) {
44 QBlittablePlatformPixmap *that = const_cast<QBlittablePlatformPixmap *>(this);
45 that->m_blittable.reset(other: this->createBlittable(size: QSize(w, h), alpha: m_alpha));
46 }
47
48 return m_blittable.data();
49}
50
51void QBlittablePlatformPixmap::setBlittable(QBlittable *blittable)
52{
53 resize(width: blittable->size().width(),height: blittable->size().height());
54 m_blittable.reset(other: blittable);
55}
56
57void QBlittablePlatformPixmap::resize(int width, int height)
58{
59 m_blittable.reset(other: nullptr);
60 m_engine.reset(other: nullptr);
61 d = QGuiApplication::primaryScreen()->depth();
62 w = width;
63 h = height;
64 is_null = (w <= 0 || h <= 0);
65 setSerialNumber(++global_ser_no);
66}
67
68int QBlittablePlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
69{
70 switch (metric) {
71 case QPaintDevice::PdmWidth:
72 return w;
73 case QPaintDevice::PdmHeight:
74 return h;
75 case QPaintDevice::PdmWidthMM:
76 return qRound(d: w * 25.4 / qt_defaultDpiX());
77 case QPaintDevice::PdmHeightMM:
78 return qRound(d: h * 25.4 / qt_defaultDpiY());
79 case QPaintDevice::PdmDepth:
80 return 32;
81 case QPaintDevice::PdmDpiX:
82 case QPaintDevice::PdmPhysicalDpiX:
83 return qt_defaultDpiX();
84 case QPaintDevice::PdmDpiY:
85 case QPaintDevice::PdmPhysicalDpiY:
86 return qt_defaultDpiY();
87 case QPaintDevice::PdmDevicePixelRatio:
88 return devicePixelRatio();
89 case QPaintDevice::PdmDevicePixelRatioScaled:
90 return devicePixelRatio() * QPaintDevice::devicePixelRatioFScale();
91 default:
92 qWarning(msg: "QRasterPlatformPixmap::metric(): Unhandled metric type %d", metric);
93 break;
94 }
95
96 return 0;
97}
98
99void QBlittablePlatformPixmap::fill(const QColor &color)
100{
101 if (blittable()->capabilities() & QBlittable::AlphaFillRectCapability) {
102 blittable()->unlock();
103 blittable()->alphaFillRect(rect: QRectF(0,0,w,h),color,cmode: QPainter::CompositionMode_Source);
104 } else if (color.alpha() == 255 && blittable()->capabilities() & QBlittable::SolidRectCapability) {
105 blittable()->unlock();
106 blittable()->fillRect(rect: QRectF(0,0,w,h),color);
107 } else {
108 // Need to be backed with an alpha channel now. It would be nice
109 // if we could just change the format, e.g. when going from
110 // RGB32 -> ARGB8888.
111 if (color.alpha() != 255 && !hasAlphaChannel()) {
112 m_blittable.reset(other: nullptr);
113 m_engine.reset(other: nullptr);
114 m_alpha = true;
115 }
116
117 blittable()->lock()->fill(color);
118 }
119
120}
121
122QImage *QBlittablePlatformPixmap::buffer()
123{
124 return blittable()->lock();
125}
126
127QImage QBlittablePlatformPixmap::toImage() const
128{
129 return blittable()->lock()->copy();
130}
131
132bool QBlittablePlatformPixmap::hasAlphaChannel() const
133{
134 return blittable()->lock()->hasAlphaChannel();
135}
136
137void QBlittablePlatformPixmap::fromImage(const QImage &image,
138 Qt::ImageConversionFlags flags)
139{
140 m_alpha = image.hasAlphaChannel();
141 m_devicePixelRatio = image.devicePixelRatio();
142 resize(width: image.width(),height: image.height());
143 markRasterOverlay(rect: QRect(0,0,w,h));
144 QImage *thisImg = buffer();
145
146 QImage correctFormatPic = image;
147 if (correctFormatPic.format() != thisImg->format())
148 correctFormatPic = correctFormatPic.convertToFormat(f: thisImg->format(), flags);
149
150 uchar *mem = thisImg->bits();
151 const uchar *bits = correctFormatPic.constBits();
152 qsizetype bytesCopied = 0;
153 while (bytesCopied < correctFormatPic.sizeInBytes()) {
154 memcpy(dest: mem,src: bits,n: correctFormatPic.bytesPerLine());
155 mem += thisImg->bytesPerLine();
156 bits += correctFormatPic.bytesPerLine();
157 bytesCopied+=correctFormatPic.bytesPerLine();
158 }
159}
160
161qreal QBlittablePlatformPixmap::devicePixelRatio() const
162{
163 return m_devicePixelRatio;
164}
165
166void QBlittablePlatformPixmap::setDevicePixelRatio(qreal scaleFactor)
167{
168 m_devicePixelRatio = scaleFactor;
169}
170
171QPaintEngine *QBlittablePlatformPixmap::paintEngine() const
172{
173 if (!m_engine) {
174 QBlittablePlatformPixmap *that = const_cast<QBlittablePlatformPixmap *>(this);
175 that->m_engine.reset(other: new QBlitterPaintEngine(that));
176 }
177 return m_engine.data();
178}
179
180#ifdef QT_BLITTER_RASTEROVERLAY
181
182static bool showRasterOverlay = !qEnvironmentVariableIsEmpty("QT_BLITTER_RASTEROVERLAY");
183
184void QBlittablePlatformPixmap::mergeOverlay()
185{
186 if (m_unmergedCopy || !showRasterOverlay)
187 return;
188 m_unmergedCopy = new QImage(buffer()->copy());
189 QPainter p(buffer());
190 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
191 p.drawImage(0,0,*overlay());
192 p.end();
193}
194
195void QBlittablePlatformPixmap::unmergeOverlay()
196{
197 if (!m_unmergedCopy || !showRasterOverlay)
198 return;
199 QPainter p(buffer());
200 p.setCompositionMode(QPainter::CompositionMode_Source);
201 p.drawImage(0,0,*m_unmergedCopy);
202 p.end();
203
204 delete m_unmergedCopy;
205 m_unmergedCopy = 0;
206}
207
208QImage *QBlittablePlatformPixmap::overlay()
209{
210 if (!m_rasterOverlay||
211 m_rasterOverlay->size() != QSize(w,h)){
212 m_rasterOverlay = new QImage(w,h,QImage::Format_ARGB32_Premultiplied);
213 m_rasterOverlay->fill(0x00000000);
214 uint color = QRandomGenerator::global()->bounded(11)+7;
215 m_overlayColor = QColor(Qt::GlobalColor(color));
216 m_overlayColor.setAlpha(0x88);
217
218 }
219 return m_rasterOverlay;
220}
221
222void QBlittablePlatformPixmap::markRasterOverlayImpl(const QRectF &rect)
223{
224 if (!showRasterOverlay)
225 return;
226 QRectF transformationRect = clipAndTransformRect(rect);
227 if (!transformationRect.isEmpty()) {
228 QPainter p(overlay());
229 p.setBrush(m_overlayColor);
230 p.setCompositionMode(QPainter::CompositionMode_Source);
231 p.fillRect(transformationRect,QBrush(m_overlayColor));
232 }
233}
234
235void QBlittablePlatformPixmap::unmarkRasterOverlayImpl(const QRectF &rect)
236{
237 if (!showRasterOverlay)
238 return;
239 QRectF transformationRect = clipAndTransformRect(rect);
240 if (!transformationRect.isEmpty()) {
241 QPainter p(overlay());
242 QColor color(0x00,0x00,0x00,0x00);
243 p.setBrush(color);
244 p.setCompositionMode(QPainter::CompositionMode_Source);
245 p.fillRect(transformationRect,QBrush(color));
246 }
247}
248
249QRectF QBlittablePlatformPixmap::clipAndTransformRect(const QRectF &rect) const
250{
251 QRectF transformationRect = rect;
252 paintEngine();
253 if (m_engine->state()) {
254 transformationRect = m_engine->state()->matrix.mapRect(rect);
255 const QClipData *clipData = m_engine->clip();
256 if (clipData) {
257 if (clipData->hasRectClip) {
258 transformationRect &= clipData->clipRect;
259 } else if (clipData->hasRegionClip) {
260 for (const QRect &rect : clipData->clipRegion)
261 transformationRect &= rect;
262 }
263 }
264 }
265 return transformationRect;
266}
267
268#endif //QT_BLITTER_RASTEROVERLAY
269
270QT_END_NAMESPACE
271
272#endif //QT_NO_BLITTABLE
273

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