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 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | QPixmap 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 | |
36 | QPixmap 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 | |
47 | QRasterPlatformPixmap::QRasterPlatformPixmap(PixelType type) |
48 | : QPlatformPixmap(type, RasterClass) |
49 | { |
50 | } |
51 | |
52 | QRasterPlatformPixmap::~QRasterPlatformPixmap() |
53 | { |
54 | } |
55 | |
56 | QImage::Format QRasterPlatformPixmap::systemNativeFormat() |
57 | { |
58 | if (!QGuiApplication::primaryScreen()) |
59 | return QImage::Format_RGB32; |
60 | return QGuiApplication::primaryScreen()->handle()->format(); |
61 | } |
62 | |
63 | QPlatformPixmap *QRasterPlatformPixmap::createCompatiblePlatformPixmap() const |
64 | { |
65 | return new QRasterPlatformPixmap(pixelType()); |
66 | } |
67 | |
68 | void 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 | |
91 | bool 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 | |
105 | void QRasterPlatformPixmap::fromImage(const QImage &sourceImage, |
106 | Qt::ImageConversionFlags flags) |
107 | { |
108 | QImage image = sourceImage; |
109 | createPixmapForImage(sourceImage: std::move(image), flags); |
110 | } |
111 | |
112 | void QRasterPlatformPixmap::fromImageInPlace(QImage &sourceImage, |
113 | Qt::ImageConversionFlags flags) |
114 | { |
115 | createPixmapForImage(sourceImage: std::move(sourceImage), flags); |
116 | } |
117 | |
118 | void 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 |
130 | extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); |
131 | |
132 | void QRasterPlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect) |
133 | { |
134 | fromImage(sourceImage: data->toImage(rect).copy(), flags: Qt::NoOpaqueDetection); |
135 | } |
136 | |
137 | bool 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 | |
144 | void 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 | |
182 | bool QRasterPlatformPixmap::hasAlphaChannel() const |
183 | { |
184 | return image.hasAlphaChannel(); |
185 | } |
186 | |
187 | QImage 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 | |
201 | QImage 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 | |
219 | QPaintEngine* QRasterPlatformPixmap::paintEngine() const |
220 | { |
221 | return image.paintEngine(); |
222 | } |
223 | |
224 | int 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 | |
265 | void 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 | |
321 | QImage* QRasterPlatformPixmap::buffer() |
322 | { |
323 | return ℑ |
324 | } |
325 | |
326 | qreal QRasterPlatformPixmap::devicePixelRatio() const |
327 | { |
328 | return image.devicePixelRatio(); |
329 | } |
330 | |
331 | void QRasterPlatformPixmap::setDevicePixelRatio(qreal scaleFactor) |
332 | { |
333 | image.setDevicePixelRatio(scaleFactor); |
334 | } |
335 | |
336 | QT_END_NAMESPACE |
337 | |