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
52QT_BEGIN_NAMESPACE
53
54template <typename Int>
55static inline Int pad4(Int v)
56{
57 return (v + Int(3)) & ~Int(3);
58}
59
60#ifndef QT_NO_DEBUG_STREAM
61QDebug 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
74static inline void initBitMapInfoHeader(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
90enum { Indexed8ColorTableSize = 256 };
91
92struct BITMAPINFO_COLORTABLE256 { // BITMAPINFO with 256 entry color table for Indexed 8 format
93 BITMAPINFOHEADER bmiHeader;
94 RGBQUAD bmiColors[Indexed8ColorTableSize];
95};
96
97template <class BITMAPINFO_T> // BITMAPINFO, BITMAPINFO_COLORTABLE256
98static 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
106static 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
119static 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.
140static 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
152static inline RGBQUAD qRgbToRgbQuad(QRgb qrgb)
153{
154 RGBQUAD result = {BYTE(qBlue(qrgb)), BYTE(qGreen(qrgb)), BYTE(qRed(qrgb)), 0};
155 return result;
156}
157
158static 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
165static QImage copyImageData(const BITMAPINFOHEADER &header, 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
211class DisplayHdc
212{
213 Q_DISABLE_COPY_MOVE(DisplayHdc)
214public:
215 DisplayHdc() : m_displayDc(GetDC(nullptr)) {}
216 ~DisplayHdc() { ReleaseDC(nullptr, m_displayDc); }
217
218 operator HDC() const { return m_displayDc; }
219
220private:
221 const HDC m_displayDc;
222};
223
224enum HBitmapFormat
225{
226 HBitmapNoAlpha,
227 HBitmapPremultipliedAlpha,
228 HBitmapAlpha
229};
230
231static 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
245Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap)
246{
247 return qt_createIconMask(bitmap.toImage().convertToFormat(QImage::Format_Mono));
248}
249
250static 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
263Q_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*/
376HBITMAP 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
389Q_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
405static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header, 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.
431static 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.
452static 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
488Q_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*/
515QImage QImage::fromHBITMAP(HBITMAP hbitmap)
516{
517 return qt_imageFromWinHBITMAP(hbitmap);
518}
519
520Q_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*/
540HICON 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
571Q_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
580Q_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
592static 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
604static 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*/
627QImage 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
680Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
681{
682 return QPixmap::fromImage(QImage::fromHICON(icon));
683}
684
685QT_END_NAMESPACE
686