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 "qbitmap.h" |
41 | #include "qpixmap.h" |
42 | #include <qpa/qplatformpixmap.h> |
43 | #include "qpixmap_raster_p.h" |
44 | |
45 | #include <qdebug.h> |
46 | #include <QScopedArrayPointer> |
47 | #include <qt_windows.h> |
48 | |
49 | #include <algorithm> |
50 | #include <iterator> |
51 | |
52 | QT_BEGIN_NAMESPACE |
53 | |
54 | template <typename Int> |
55 | static inline Int pad4(Int v) |
56 | { |
57 | return (v + Int(3)) & ~Int(3); |
58 | } |
59 | |
60 | #ifndef QT_NO_DEBUG_STREAM |
61 | QDebug operator<<(QDebug d, const BITMAPINFOHEADER &bih) |
62 | { |
63 | QDebugStateSaver saver(d); |
64 | d.nospace(); |
65 | d << "BITMAPINFOHEADER(" << bih.biWidth << 'x' << qAbs(bih.biHeight) |
66 | << (bih.biHeight < 0 ? ", top-down" : ", bottom-up" ) |
67 | << ", planes=" << bih.biPlanes << ", bitCount=" << bih.biBitCount |
68 | << ", compression=" << bih.biCompression << ", size=" |
69 | << bih.biSizeImage << ')'; |
70 | return d; |
71 | } |
72 | #endif // !QT_NO_DEBUG_STREAM |
73 | |
74 | static inline void (int width, int height, bool topToBottom, |
75 | DWORD compression, DWORD bitCount, |
76 | BITMAPINFOHEADER *bih) |
77 | { |
78 | memset(bih, 0, sizeof(BITMAPINFOHEADER)); |
79 | bih->biSize = sizeof(BITMAPINFOHEADER); |
80 | bih->biWidth = width; |
81 | bih->biHeight = topToBottom ? -height : height; |
82 | bih->biPlanes = 1; |
83 | bih->biBitCount = WORD(bitCount); |
84 | bih->biCompression = compression; |
85 | // scan lines are word-aligned (unless RLE) |
86 | const DWORD bytesPerLine = pad4(DWORD(width) * bitCount / 8); |
87 | bih->biSizeImage = bytesPerLine * DWORD(height); |
88 | } |
89 | |
90 | enum { Indexed8ColorTableSize = 256 }; |
91 | |
92 | struct BITMAPINFO_COLORTABLE256 { // BITMAPINFO with 256 entry color table for Indexed 8 format |
93 | BITMAPINFOHEADER ; |
94 | RGBQUAD bmiColors[Indexed8ColorTableSize]; |
95 | }; |
96 | |
97 | template <class BITMAPINFO_T> // BITMAPINFO, BITMAPINFO_COLORTABLE256 |
98 | static inline void initBitMapInfo(int width, int height, bool topToBottom, |
99 | DWORD compression, DWORD bitCount, |
100 | BITMAPINFO_T *bmi) |
101 | { |
102 | initBitMapInfoHeader(width, height, topToBottom, compression, bitCount, &bmi->bmiHeader); |
103 | memset(bmi->bmiColors, 0, sizeof(bmi->bmiColors)); |
104 | } |
105 | |
106 | static inline uchar *getDiBits(HDC hdc, HBITMAP bitmap, int width, int height, bool topToBottom = true) |
107 | { |
108 | BITMAPINFO bmi; |
109 | initBitMapInfo(width, height, topToBottom, BI_RGB, 32u, &bmi); |
110 | uchar *result = new uchar[bmi.bmiHeader.biSizeImage]; |
111 | if (!GetDIBits(hdc, bitmap, 0, UINT(height), result, &bmi, DIB_RGB_COLORS)) { |
112 | delete [] result; |
113 | qErrnoWarning("%s: GetDIBits() failed to get bitmap bits." , __FUNCTION__); |
114 | return nullptr; |
115 | } |
116 | return result; |
117 | } |
118 | |
119 | static inline void copyImageDataCreateAlpha(const uchar *data, QImage *target) |
120 | { |
121 | const uint mask = target->format() == QImage::Format_RGB32 ? 0xff000000 : 0; |
122 | const int height = target->height(); |
123 | const int width = target->width(); |
124 | const qsizetype bytesPerLine = width * sizeof(QRgb); |
125 | for (int y = 0; y < height; ++y) { |
126 | QRgb *dest = reinterpret_cast<QRgb *>(target->scanLine(y)); |
127 | const QRgb *src = reinterpret_cast<const QRgb *>(data + y * bytesPerLine); |
128 | for (int x = 0; x < width; ++x) { |
129 | const uint pixel = src[x]; |
130 | if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0) |
131 | dest[x] = pixel | 0xff000000; |
132 | else |
133 | dest[x] = pixel | mask; |
134 | } |
135 | } |
136 | } |
137 | |
138 | // Flip RGB triplets from DIB to QImage formats. Scan lines are padded to 32bit |
139 | // both in QImage and DIB. |
140 | static inline void flipRgb3(uchar *p, int width, int height) |
141 | { |
142 | const int lineSize = 3 * width; |
143 | const int linePad = pad4(lineSize) - lineSize; |
144 | for (int y = 0; y < height; ++y) { |
145 | uchar *end = p + lineSize; |
146 | for ( ; p < end; p += 3) |
147 | std::swap(*p, *(p + 2)); |
148 | p += linePad; |
149 | } |
150 | } |
151 | |
152 | static inline RGBQUAD qRgbToRgbQuad(QRgb qrgb) |
153 | { |
154 | RGBQUAD result = {BYTE(qBlue(qrgb)), BYTE(qGreen(qrgb)), BYTE(qRed(qrgb)), 0}; |
155 | return result; |
156 | } |
157 | |
158 | static inline QRgb rgbQuadToQRgb(RGBQUAD quad) |
159 | { |
160 | return QRgb(quad.rgbBlue) + (QRgb(quad.rgbGreen) << 8) + (QRgb(quad.rgbRed) << 16) |
161 | + 0xff000000; |
162 | } |
163 | |
164 | // Helper for imageFromWinHBITMAP_*(), create image in desired format |
165 | static QImage copyImageData(const BITMAPINFOHEADER &, const RGBQUAD *colorTableIn, |
166 | const void *data, QImage::Format format) |
167 | { |
168 | const QSize size = QSize(header.biWidth, qAbs(header.biHeight)); |
169 | QImage image(size, format); |
170 | |
171 | int colorTableSize = 0; |
172 | switch (format) { |
173 | case QImage::Format_Mono: |
174 | colorTableSize = 2; |
175 | break; |
176 | case QImage::Format_Indexed8: |
177 | colorTableSize = Indexed8ColorTableSize; |
178 | break; |
179 | default: |
180 | break; |
181 | } |
182 | if (colorTableSize) { |
183 | Q_ASSERT(colorTableIn); |
184 | QList<QRgb> colorTable; |
185 | colorTable.reserve(colorTableSize); |
186 | std::transform(colorTableIn, colorTableIn + colorTableSize, |
187 | std::back_inserter(colorTable), rgbQuadToQRgb); |
188 | image.setColorTable(colorTable); |
189 | } |
190 | |
191 | switch (header.biBitCount) { |
192 | case 32: |
193 | copyImageDataCreateAlpha(static_cast<const uchar *>(data), &image); |
194 | break; |
195 | case 1: |
196 | case 8: |
197 | case 16: |
198 | case 24: |
199 | Q_ASSERT(DWORD(image.sizeInBytes()) == header.biSizeImage); |
200 | memcpy(image.bits(), data, header.biSizeImage); |
201 | if (format == QImage::Format_RGB888) |
202 | image = image.rgbSwapped(); |
203 | break; |
204 | default: |
205 | Q_UNREACHABLE(); |
206 | break; |
207 | } |
208 | return image; |
209 | } |
210 | |
211 | class DisplayHdc |
212 | { |
213 | Q_DISABLE_COPY_MOVE(DisplayHdc) |
214 | public: |
215 | DisplayHdc() : m_displayDc(GetDC(nullptr)) {} |
216 | ~DisplayHdc() { ReleaseDC(nullptr, m_displayDc); } |
217 | |
218 | operator HDC() const { return m_displayDc; } |
219 | |
220 | private: |
221 | const HDC m_displayDc; |
222 | }; |
223 | |
224 | enum HBitmapFormat |
225 | { |
226 | HBitmapNoAlpha, |
227 | HBitmapPremultipliedAlpha, |
228 | HBitmapAlpha |
229 | }; |
230 | |
231 | static HBITMAP qt_createIconMask(QImage bm) |
232 | { |
233 | Q_ASSERT(bm.format() == QImage::Format_Mono); |
234 | const int w = bm.width(); |
235 | const int h = bm.height(); |
236 | const int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment |
237 | QScopedArrayPointer<uchar> bits(new uchar[size_t(bpl * h)]); |
238 | bm.invertPixels(); |
239 | for (int y = 0; y < h; ++y) |
240 | memcpy(bits.data() + y * bpl, bm.constScanLine(y), size_t(bpl)); |
241 | HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits.data()); |
242 | return hbm; |
243 | } |
244 | |
245 | Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap) |
246 | { |
247 | return qt_createIconMask(bitmap.toImage().convertToFormat(QImage::Format_Mono)); |
248 | } |
249 | |
250 | static inline QImage::Format format32(int hbitmapFormat) |
251 | { |
252 | switch (hbitmapFormat) { |
253 | case HBitmapNoAlpha: |
254 | return QImage::Format_RGB32; |
255 | case HBitmapAlpha: |
256 | return QImage::Format_ARGB32; |
257 | default: |
258 | break; |
259 | } |
260 | return QImage::Format_ARGB32_Premultiplied; |
261 | } |
262 | |
263 | Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat = 0) |
264 | { |
265 | if (imageIn.isNull()) |
266 | return nullptr; |
267 | |
268 | // Define the header |
269 | DWORD compression = 0; |
270 | DWORD bitCount = 0; |
271 | |
272 | // Copy over the data |
273 | QImage image = imageIn; |
274 | switch (image.format()) { |
275 | case QImage::Format_Mono: |
276 | bitCount = 1u; |
277 | break; |
278 | case QImage::Format_RGB32: |
279 | case QImage::Format_ARGB32: |
280 | case QImage::Format_ARGB32_Premultiplied: { |
281 | compression = BI_RGB; |
282 | bitCount = 32u; |
283 | const QImage::Format targetFormat = format32(hbitmapFormat); |
284 | if (targetFormat != image.format()) |
285 | image = image.convertToFormat(targetFormat); |
286 | } |
287 | break; |
288 | case QImage::Format_RGB888: |
289 | case QImage::Format_BGR888: |
290 | compression = BI_RGB; |
291 | bitCount = 24u; |
292 | break; |
293 | case QImage::Format_Indexed8: |
294 | bitCount = 8u; |
295 | break; |
296 | case QImage::Format_RGB555: |
297 | bitCount = 16u; |
298 | break; |
299 | default: { |
300 | QImage::Format fallbackFormat = QImage::Format_ARGB32_Premultiplied; |
301 | switch (image.format()) { // Convert to a suitable format. |
302 | case QImage::Format_MonoLSB: |
303 | fallbackFormat = QImage::Format_Mono; |
304 | break; |
305 | case QImage::Format_RGB16: |
306 | fallbackFormat = QImage::Format_RGB555; |
307 | break; |
308 | case QImage::Format_Grayscale8: |
309 | fallbackFormat = QImage::Format_Indexed8; |
310 | break; |
311 | default: |
312 | break; |
313 | } // switch conversion format |
314 | return qt_imageToWinHBITMAP(imageIn.convertToFormat(fallbackFormat), hbitmapFormat); |
315 | } |
316 | } |
317 | |
318 | const int w = image.width(); |
319 | const int h = image.height(); |
320 | |
321 | BITMAPINFO_COLORTABLE256 bmiColorTable256; |
322 | initBitMapInfo(w, h, true, compression, bitCount, &bmiColorTable256); |
323 | BITMAPINFO &bmi = reinterpret_cast<BITMAPINFO &>(bmiColorTable256); |
324 | switch (image.format()) { |
325 | case QImage::Format_Mono: // Color table with 2 entries |
326 | case QImage::Format_Indexed8: |
327 | std::transform(image.colorTable().constBegin(), image.colorTable().constEnd(), |
328 | bmiColorTable256.bmiColors, qRgbToRgbQuad); |
329 | break; |
330 | default: |
331 | break; |
332 | } |
333 | |
334 | // Create the pixmap |
335 | uchar *pixels = nullptr; |
336 | const HBITMAP bitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, |
337 | reinterpret_cast<void **>(&pixels), nullptr, 0); |
338 | if (!bitmap) { |
339 | qErrnoWarning("%s, failed to create dibsection" , __FUNCTION__); |
340 | return nullptr; |
341 | } |
342 | if (!pixels) { |
343 | qErrnoWarning("%s, did not allocate pixel data" , __FUNCTION__); |
344 | return nullptr; |
345 | } |
346 | memcpy(pixels, image.constBits(), bmi.bmiHeader.biSizeImage); |
347 | if (image.format() == QImage::Format_RGB888) |
348 | flipRgb3(pixels, w, h); |
349 | return bitmap; |
350 | } |
351 | |
352 | /*! |
353 | \since 6.0 |
354 | |
355 | \brief Creates a \c HBITMAP equivalent of the QImage. |
356 | |
357 | Returns the \c HBITMAP handle. |
358 | |
359 | It is the caller's responsibility to free the \c HBITMAP data |
360 | after use. |
361 | |
362 | For usage with with standard GDI calls, such as \c BitBlt(), the image |
363 | should have the format QImage::Format_RGB32. |
364 | |
365 | When using the resulting HBITMAP for the \c AlphaBlend() GDI function, |
366 | the image should have the format QImage::Format_ARGB32_Premultiplied |
367 | (use convertToFormat()). |
368 | |
369 | When using the resulting HBITMAP as application icon or a systray icon, |
370 | the image should have the format QImage::Format_ARGB32. |
371 | |
372 | \ingroup platform-type-conversions |
373 | |
374 | \sa fromHBITMAP(), convertToFormat() |
375 | */ |
376 | HBITMAP QImage::toHBITMAP() const |
377 | { |
378 | switch (format()) { |
379 | case QImage::Format_ARGB32: |
380 | return qt_imageToWinHBITMAP(*this, HBitmapAlpha); |
381 | case QImage::Format_ARGB32_Premultiplied: |
382 | return qt_imageToWinHBITMAP(*this, HBitmapPremultipliedAlpha); |
383 | default: |
384 | break; |
385 | } |
386 | return qt_imageToWinHBITMAP(*this); |
387 | } |
388 | |
389 | Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0) |
390 | { |
391 | if (p.isNull()) |
392 | return nullptr; |
393 | |
394 | QPlatformPixmap *platformPixmap = p.handle(); |
395 | if (platformPixmap->classId() != QPlatformPixmap::RasterClass) { |
396 | QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ? |
397 | QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType); |
398 | data->fromImage(p.toImage(), Qt::AutoColor); |
399 | return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat); |
400 | } |
401 | |
402 | return qt_imageToWinHBITMAP(*static_cast<QRasterPlatformPixmap*>(platformPixmap)->buffer(), hbitmapFormat); |
403 | } |
404 | |
405 | static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &, int hbitmapFormat) |
406 | { |
407 | QImage::Format result = QImage::Format_Invalid; |
408 | switch (header.biBitCount) { |
409 | case 32: |
410 | result = hbitmapFormat == HBitmapNoAlpha |
411 | ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; |
412 | break; |
413 | case 24: |
414 | result = QImage::Format_BGR888; |
415 | break; |
416 | case 16: |
417 | result = QImage::Format_RGB555; |
418 | break; |
419 | case 8: |
420 | result = QImage::Format_Indexed8; |
421 | break; |
422 | case 1: |
423 | result = QImage::Format_Mono; |
424 | break; |
425 | } |
426 | return result; |
427 | } |
428 | |
429 | // Fast path for creating a QImage directly from a HBITMAP created by CreateDIBSection(), |
430 | // not requiring memory allocation. |
431 | static QImage imageFromWinHBITMAP_DibSection(HBITMAP bitmap, int hbitmapFormat) |
432 | { |
433 | DIBSECTION dibSection; |
434 | memset(&dibSection, 0, sizeof(dibSection)); |
435 | dibSection.dsBmih.biSize = sizeof(dibSection.dsBmih); |
436 | |
437 | if (!GetObject(bitmap, sizeof(dibSection), &dibSection) |
438 | || !dibSection.dsBm.bmBits |
439 | || dibSection.dsBmih.biBitCount <= 8 // Cannot access the color table for Indexed8, Mono |
440 | || dibSection.dsBmih.biCompression != BI_RGB) { |
441 | return QImage(); |
442 | } |
443 | |
444 | const QImage::Format imageFormat = imageFromWinHBITMAP_Format(dibSection.dsBmih, hbitmapFormat); |
445 | if (imageFormat == QImage::Format_Invalid) |
446 | return QImage(); |
447 | |
448 | return copyImageData(dibSection.dsBmih, nullptr, dibSection.dsBm.bmBits, imageFormat); |
449 | } |
450 | |
451 | // Create QImage from a HBITMAP using GetDIBits(), potentially with conversion. |
452 | static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int hbitmapFormat) |
453 | { |
454 | BITMAPINFO_COLORTABLE256 bmiColorTable256; |
455 | BITMAPINFO &info = reinterpret_cast<BITMAPINFO &>(bmiColorTable256); |
456 | memset(&info, 0, sizeof(info)); |
457 | info.bmiHeader.biSize = sizeof(info.bmiHeader); |
458 | |
459 | DisplayHdc displayDc; |
460 | if (!GetDIBits(displayDc, bitmap, 0, 1, 0, &info, DIB_RGB_COLORS)) { |
461 | qErrnoWarning("%s: GetDIBits() failed to query data." , __FUNCTION__); |
462 | return QImage(); |
463 | } |
464 | |
465 | if (info.bmiHeader.biHeight > 0) // Force top-down |
466 | info.bmiHeader.biHeight = -info.bmiHeader.biHeight; |
467 | info.bmiHeader.biCompression = BI_RGB; // Extract using no compression (can be BI_BITFIELD) |
468 | size_t allocSize = info.bmiHeader.biSizeImage; |
469 | if (forceQuads) { |
470 | info.bmiHeader.biBitCount = 32; |
471 | allocSize = info.bmiHeader.biWidth * qAbs(info.bmiHeader.biHeight) * 4; |
472 | } |
473 | |
474 | const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat); |
475 | if (imageFormat == QImage::Format_Invalid) { |
476 | qWarning().nospace() << __FUNCTION__ << ": unsupported image format:" << info.bmiHeader; |
477 | return QImage(); |
478 | } |
479 | |
480 | QScopedArrayPointer<uchar> data(new uchar[allocSize]); |
481 | if (!GetDIBits(displayDc, bitmap, 0, qAbs(info.bmiHeader.biHeight), data.data(), &info, DIB_RGB_COLORS)) { |
482 | qErrnoWarning("%s: GetDIBits() failed to get data." , __FUNCTION__); |
483 | return QImage(); |
484 | } |
485 | return copyImageData(info.bmiHeader, bmiColorTable256.bmiColors, data.data(), imageFormat); |
486 | } |
487 | |
488 | Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) |
489 | { |
490 | QImage result = imageFromWinHBITMAP_DibSection(bitmap, hbitmapFormat); |
491 | if (result.isNull()) |
492 | result = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ false, hbitmapFormat); |
493 | return result; |
494 | } |
495 | |
496 | /*! |
497 | \since 6.0 |
498 | |
499 | \brief Returns a QImage that is equivalent to the given \a hbitmap. |
500 | |
501 | HBITMAP does not store information about the alpha channel. |
502 | |
503 | In the standard case, the alpha channel is ignored and a fully |
504 | opaque image is created (typically of format QImage::Format_RGB32). |
505 | |
506 | There are cases where the alpha channel is used, though, for example |
507 | for application icon or systray icons. In that case, |
508 | \c reinterpretAsFormat(QImage::Format_ARGB32) should be called |
509 | on the returned image to ensure the format is correct. |
510 | |
511 | \ingroup platform-type-conversions |
512 | |
513 | \sa toHBITMAP(), reinterpretAsFormat() |
514 | */ |
515 | QImage QImage::fromHBITMAP(HBITMAP hbitmap) |
516 | { |
517 | return qt_imageFromWinHBITMAP(hbitmap); |
518 | } |
519 | |
520 | Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) |
521 | { |
522 | return QPixmap::fromImage(imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ true, hbitmapFormat)); |
523 | } |
524 | |
525 | /*! |
526 | \since 6.0 |
527 | |
528 | \brief Creates a \c HICON equivalent of the QPixmap, applying the mask |
529 | \a mask. |
530 | |
531 | If \a mask is not null, it needs to be of format QImage::Format_Mono. |
532 | Returns the \c HICON handle. |
533 | |
534 | It is the caller's responsibility to free the \c HICON data after use. |
535 | |
536 | \ingroup platform-type-conversions |
537 | |
538 | \sa fromHICON() |
539 | */ |
540 | HICON QImage::toHICON(const QImage &mask) const |
541 | { |
542 | if (!mask.isNull() && mask.format() != QImage::Format_Mono) { |
543 | qWarning("QImage::toHICON(): Mask must be empty or have format Format_Mono" ); |
544 | return nullptr; |
545 | } |
546 | |
547 | if (isNull()) |
548 | return nullptr; |
549 | |
550 | auto effectiveMask = mask; |
551 | if (effectiveMask.isNull()) { |
552 | effectiveMask = QImage(size(), QImage::Format_Mono); |
553 | effectiveMask.fill(Qt::color1); |
554 | } |
555 | |
556 | ICONINFO ii; |
557 | ii.fIcon = true; |
558 | ii.hbmMask = qt_createIconMask(effectiveMask); |
559 | ii.hbmColor = qt_imageToWinHBITMAP(*this, HBitmapAlpha); |
560 | ii.xHotspot = 0; |
561 | ii.yHotspot = 0; |
562 | |
563 | HICON hIcon = CreateIconIndirect(&ii); |
564 | |
565 | DeleteObject(ii.hbmColor); |
566 | DeleteObject(ii.hbmMask); |
567 | |
568 | return hIcon; |
569 | } |
570 | |
571 | Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p) |
572 | { |
573 | QImage mask; |
574 | QBitmap maskBitmap = p.mask(); |
575 | if (!maskBitmap.isNull()) |
576 | mask = maskBitmap.toImage().convertToFormat(QImage::Format_Mono); |
577 | return p.toImage().toHICON(mask); |
578 | } |
579 | |
580 | Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) |
581 | { |
582 | QImage image(w, h, QImage::Format_ARGB32_Premultiplied); |
583 | if (image.isNull()) |
584 | return image; |
585 | QScopedArrayPointer<uchar> data(getDiBits(hdc, bitmap, w, h, true)); |
586 | if (data.isNull()) |
587 | return QImage(); |
588 | copyImageDataCreateAlpha(data.data(), &image); |
589 | return image; |
590 | } |
591 | |
592 | static QImage qt_imageFromWinIconHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) |
593 | { |
594 | QImage image(w, h, QImage::Format_ARGB32_Premultiplied); |
595 | if (image.isNull()) |
596 | return image; |
597 | QScopedArrayPointer<uchar> data(getDiBits(hdc, bitmap, w, h, true)); |
598 | if (data.isNull()) |
599 | return QImage(); |
600 | memcpy(image.bits(), data.data(), size_t(image.sizeInBytes())); |
601 | return image; |
602 | } |
603 | |
604 | static inline bool hasAlpha(const QImage &image) |
605 | { |
606 | const int w = image.width(); |
607 | const int h = image.height(); |
608 | for (int y = 0; y < h; ++y) { |
609 | const QRgb *scanLine = reinterpret_cast<const QRgb *>(image.scanLine(y)); |
610 | for (int x = 0; x < w; ++x) { |
611 | if (qAlpha(scanLine[x]) != 0) |
612 | return true; |
613 | } |
614 | } |
615 | return false; |
616 | } |
617 | |
618 | /*! |
619 | \since 6.0 |
620 | |
621 | \brief Returns a QImage that is equivalent to the given \a icon. |
622 | |
623 | \ingroup platform-type-conversions |
624 | |
625 | \sa toHICON() |
626 | */ |
627 | QImage QImage::fromHICON(HICON icon) |
628 | { |
629 | HDC screenDevice = GetDC(nullptr); |
630 | HDC hdc = CreateCompatibleDC(screenDevice); |
631 | ReleaseDC(nullptr, screenDevice); |
632 | |
633 | ICONINFO iconinfo; |
634 | const bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center |
635 | if (!result) { |
636 | qErrnoWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()" ); |
637 | DeleteDC(hdc); |
638 | return {}; |
639 | } |
640 | |
641 | const int w = int(iconinfo.xHotspot) * 2; |
642 | const int h = int(iconinfo.yHotspot) * 2; |
643 | |
644 | BITMAPINFOHEADER bitmapInfo; |
645 | initBitMapInfoHeader(w, h, false, BI_RGB, 32u, &bitmapInfo); |
646 | DWORD* bits; |
647 | |
648 | HBITMAP winBitmap = CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO *>(&bitmapInfo), |
649 | DIB_RGB_COLORS, reinterpret_cast<VOID **>(&bits), |
650 | nullptr, 0); |
651 | HGDIOBJ oldhdc = static_cast<HBITMAP>(SelectObject(hdc, winBitmap)); |
652 | DrawIconEx(hdc, 0, 0, icon, w, h, 0, nullptr, DI_NORMAL); |
653 | QImage image = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h); |
654 | |
655 | if (!image.isNull() && !hasAlpha(image)) { //If no alpha was found, we use the mask to set alpha values |
656 | DrawIconEx( hdc, 0, 0, icon, w, h, 0, nullptr, DI_MASK); |
657 | const QImage mask = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h); |
658 | |
659 | for (int y = 0 ; y < h ; y++){ |
660 | QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y)); |
661 | const QRgb *scanlineMask = mask.isNull() ? nullptr : reinterpret_cast<const QRgb *>(mask.scanLine(y)); |
662 | for (int x = 0; x < w ; x++){ |
663 | if (scanlineMask && qRed(scanlineMask[x]) != 0) |
664 | scanlineImage[x] = 0; //mask out this pixel |
665 | else |
666 | scanlineImage[x] |= 0xff000000; // set the alpha channel to 255 |
667 | } |
668 | } |
669 | } |
670 | //dispose resources created by iconinfo call |
671 | DeleteObject(iconinfo.hbmMask); |
672 | DeleteObject(iconinfo.hbmColor); |
673 | |
674 | SelectObject(hdc, oldhdc); //restore state |
675 | DeleteObject(winBitmap); |
676 | DeleteDC(hdc); |
677 | return image; |
678 | } |
679 | |
680 | Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon) |
681 | { |
682 | return QPixmap::fromImage(QImage::fromHICON(icon)); |
683 | } |
684 | |
685 | QT_END_NAMESPACE |
686 | |