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.h"
5
6#include <private/qfont_p.h>
7
8#include "qpixmap_raster_p.h"
9#include "qimage_p.h"
10#include "qpaintengine.h"
11
12#include "qbitmap.h"
13#include "qimage.h"
14#include <QBuffer>
15#include <QImageReader>
16#include <QGuiApplication>
17#include <QScreen>
18#include <private/qsimd_p.h>
19#include <private/qdrawhelper_p.h>
20#include <qpa/qplatformscreen.h>
21
22QT_BEGIN_NAMESPACE
23
24QPixmap qt_toRasterPixmap(const QImage &image)
25{
26 QPlatformPixmap *data =
27 new QRasterPlatformPixmap(image.depth() == 1
28 ? QPlatformPixmap::BitmapType
29 : QPlatformPixmap::PixmapType);
30
31 data->fromImage(image, flags: Qt::AutoColor);
32
33 return QPixmap(data);
34}
35
36QPixmap qt_toRasterPixmap(const QPixmap &pixmap)
37{
38 if (pixmap.isNull())
39 return QPixmap();
40
41 if (QPixmap(pixmap).data_ptr()->classId() == QPlatformPixmap::RasterClass)
42 return pixmap;
43
44 return qt_toRasterPixmap(image: pixmap.toImage());
45}
46
47QRasterPlatformPixmap::QRasterPlatformPixmap(PixelType type)
48 : QPlatformPixmap(type, RasterClass)
49{
50}
51
52QRasterPlatformPixmap::~QRasterPlatformPixmap()
53{
54}
55
56QImage::Format QRasterPlatformPixmap::systemNativeFormat()
57{
58 if (!QGuiApplication::primaryScreen())
59 return QImage::Format_RGB32;
60 return QGuiApplication::primaryScreen()->handle()->format();
61}
62
63QPlatformPixmap *QRasterPlatformPixmap::createCompatiblePlatformPixmap() const
64{
65 return new QRasterPlatformPixmap(pixelType());
66}
67
68void QRasterPlatformPixmap::resize(int width, int height)
69{
70 QImage::Format format;
71 if (pixelType() == BitmapType)
72 format = QImage::Format_MonoLSB;
73 else
74 format = systemNativeFormat();
75
76 image = QImage(width, height, format);
77 w = width;
78 h = height;
79 d = image.depth();
80 is_null = (w <= 0 || h <= 0);
81
82 if (pixelType() == BitmapType && !image.isNull()) {
83 image.setColorCount(2);
84 image.setColor(i: 0, c: QColor(Qt::color0).rgba());
85 image.setColor(i: 1, c: QColor(Qt::color1).rgba());
86 }
87
88 setSerialNumber(image.cacheKey() >> 32);
89}
90
91bool QRasterPlatformPixmap::fromData(const uchar *buffer, uint len, const char *format,
92 Qt::ImageConversionFlags flags)
93{
94 QByteArray a = QByteArray::fromRawData(data: reinterpret_cast<const char *>(buffer), size: len);
95 QBuffer b(&a);
96 b.open(openMode: QIODevice::ReadOnly);
97 QImage image = QImageReader(&b, format).read();
98 if (image.isNull())
99 return false;
100
101 createPixmapForImage(sourceImage: std::move(image), flags);
102 return !isNull();
103}
104
105void QRasterPlatformPixmap::fromImage(const QImage &sourceImage,
106 Qt::ImageConversionFlags flags)
107{
108 QImage image = sourceImage;
109 createPixmapForImage(sourceImage: std::move(image), flags);
110}
111
112void QRasterPlatformPixmap::fromImageInPlace(QImage &sourceImage,
113 Qt::ImageConversionFlags flags)
114{
115 createPixmapForImage(sourceImage: std::move(sourceImage), flags);
116}
117
118void QRasterPlatformPixmap::fromImageReader(QImageReader *imageReader,
119 Qt::ImageConversionFlags flags)
120{
121 Q_UNUSED(flags);
122 QImage image = imageReader->read();
123 if (image.isNull())
124 return;
125
126 createPixmapForImage(sourceImage: std::move(image), flags);
127}
128
129// from qbackingstore.cpp
130extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
131
132void QRasterPlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
133{
134 fromImage(sourceImage: data->toImage(rect).copy(), flags: Qt::NoOpaqueDetection);
135}
136
137bool QRasterPlatformPixmap::scroll(int dx, int dy, const QRect &rect)
138{
139 if (!image.isNull())
140 qt_scrollRectInImage(img&: image, rect, offset: QPoint(dx, dy));
141 return true;
142}
143
144void QRasterPlatformPixmap::fill(const QColor &color)
145{
146 uint pixel;
147
148 if (image.depth() == 1) {
149 int gray = qGray(rgb: color.rgba());
150 // Pick the best approximate color in the image's colortable.
151 if (qAbs(t: qGray(rgb: image.color(i: 0)) - gray) < qAbs(t: qGray(rgb: image.color(i: 1)) - gray))
152 pixel = 0;
153 else
154 pixel = 1;
155 } else if (image.depth() >= 15) {
156 int alpha = color.alpha();
157 if (alpha != 255) {
158 if (!image.hasAlphaChannel()) {
159 QImage::Format toFormat = qt_alphaVersionForPainting(format: image.format());
160 if (!image.reinterpretAsFormat(f: toFormat))
161 image = QImage(image.width(), image.height(), toFormat);
162 }
163 }
164 image.fill(color);
165 return;
166 } else if (image.format() == QImage::Format_Alpha8) {
167 pixel = qAlpha(rgb: color.rgba());
168 } else if (image.format() == QImage::Format_Grayscale8) {
169 pixel = qGray(rgb: color.rgba());
170 } else if (image.format() == QImage::Format_Grayscale16) {
171 QRgba64 c = color.rgba64();
172 pixel = qGray(r: c.red(), g: c.green(), b: c.blue());
173 } else
174 {
175 pixel = 0;
176 // ### what about 8 bit indexed?
177 }
178
179 image.fill(pixel);
180}
181
182bool QRasterPlatformPixmap::hasAlphaChannel() const
183{
184 return image.hasAlphaChannel();
185}
186
187QImage QRasterPlatformPixmap::toImage() const
188{
189 if (!image.isNull()) {
190 QImageData *data = const_cast<QImage &>(image).data_ptr();
191 if (data->paintEngine && data->paintEngine->isActive()
192 && data->paintEngine->paintDevice() == &image)
193 {
194 return image.copy();
195 }
196 }
197
198 return image;
199}
200
201QImage QRasterPlatformPixmap::toImage(const QRect &rect) const
202{
203 if (rect.isNull())
204 return image;
205
206 QRect clipped = rect.intersected(other: QRect(0, 0, w, h));
207 const uint du = uint(d);
208 if ((du % 8 == 0) && (((uint(clipped.x()) * du)) % 32 == 0)) {
209 QImage newImage(image.scanLine(clipped.y()) + clipped.x() * (du / 8),
210 clipped.width(), clipped.height(),
211 image.bytesPerLine(), image.format());
212 newImage.setDevicePixelRatio(image.devicePixelRatio());
213 return newImage;
214 } else {
215 return image.copy(rect: clipped);
216 }
217}
218
219QPaintEngine* QRasterPlatformPixmap::paintEngine() const
220{
221 return image.paintEngine();
222}
223
224int QRasterPlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
225{
226 QImageData *d = image.d;
227 if (!d)
228 return 0;
229
230 // override the image dpi with the screen dpi when rendering to a pixmap
231 switch (metric) {
232 case QPaintDevice::PdmWidth:
233 return w;
234 case QPaintDevice::PdmHeight:
235 return h;
236 case QPaintDevice::PdmWidthMM:
237 return qRound(d: d->width * 25.4 / qt_defaultDpiX());
238 case QPaintDevice::PdmHeightMM:
239 return qRound(d: d->height * 25.4 / qt_defaultDpiY());
240 case QPaintDevice::PdmNumColors:
241 return d->colortable.size();
242 case QPaintDevice::PdmDepth:
243 return this->d;
244 case QPaintDevice::PdmDpiX:
245 return qt_defaultDpiX();
246 case QPaintDevice::PdmPhysicalDpiX:
247 return qt_defaultDpiX();
248 case QPaintDevice::PdmDpiY:
249 return qt_defaultDpiY();
250 case QPaintDevice::PdmPhysicalDpiY:
251 return qt_defaultDpiY();
252 case QPaintDevice::PdmDevicePixelRatio:
253 return image.devicePixelRatio();
254 case QPaintDevice::PdmDevicePixelRatioScaled:
255 return image.devicePixelRatio() * QPaintDevice::devicePixelRatioFScale();
256
257 default:
258 qWarning(msg: "QRasterPlatformPixmap::metric(): Unhandled metric type %d", metric);
259 break;
260 }
261
262 return 0;
263}
264
265void QRasterPlatformPixmap::createPixmapForImage(QImage sourceImage, Qt::ImageConversionFlags flags)
266{
267 QImage::Format format;
268 if (flags & Qt::NoFormatConversion)
269 format = sourceImage.format();
270 else
271 if (pixelType() == BitmapType) {
272 format = QImage::Format_MonoLSB;
273 } else {
274 if (sourceImage.depth() == 1) {
275 format = sourceImage.hasAlphaChannel()
276 ? QImage::Format_ARGB32_Premultiplied
277 : QImage::Format_RGB32;
278 } else {
279 QImage::Format nativeFormat = systemNativeFormat();
280 QImage::Format opaqueFormat = qt_opaqueVersionForPainting(format: nativeFormat);
281 QImage::Format alphaFormat = qt_alphaVersionForPainting(format: nativeFormat);
282
283 if (!sourceImage.hasAlphaChannel()) {
284 format = opaqueFormat;
285 } else if ((flags & Qt::NoOpaqueDetection) == 0
286 && !sourceImage.data_ptr()->checkForAlphaPixels())
287 {
288 format = opaqueFormat;
289 } else {
290 format = alphaFormat;
291 }
292 }
293 }
294
295 // image has alpha format but is really opaque, so try to do a
296 // more efficient conversion
297 if (format == QImage::Format_RGB32 && (sourceImage.format() == QImage::Format_ARGB32
298 || sourceImage.format() == QImage::Format_ARGB32_Premultiplied))
299 {
300 image = std::move(sourceImage);
301 image.reinterpretAsFormat(f: QImage::Format_RGB32);
302 } else {
303 image = std::move(sourceImage).convertToFormat(f: format, flags);
304 }
305
306 if (image.d) {
307 w = image.d->width;
308 h = image.d->height;
309 d = image.d->depth;
310 } else {
311 w = h = d = 0;
312 }
313 is_null = (w <= 0 || h <= 0);
314
315 //ensure the pixmap and the image resulting from toImage() have the same cacheKey();
316 setSerialNumber(image.cacheKey() >> 32);
317 if (image.d)
318 setDetachNumber(image.d->detach_no);
319}
320
321QImage* QRasterPlatformPixmap::buffer()
322{
323 return &image;
324}
325
326qreal QRasterPlatformPixmap::devicePixelRatio() const
327{
328 return image.devicePixelRatio();
329}
330
331void QRasterPlatformPixmap::setDevicePixelRatio(qreal scaleFactor)
332{
333 image.setDevicePixelRatio(scaleFactor);
334}
335
336QT_END_NAMESPACE
337

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