From eee6a70fbb8228c96bb31af829edae2b650ff6eb Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 21 Apr 2016 16:03:39 +0200 Subject: [PATCH] QtGui: Add qt_imageToWinHBITMAP(), qt_imageFromWinHBITMAP(). Add functions for converting QImage to HBITMAP and back supporting additional formats of QImage (RGB888, RGB555, Indexed8 and Mono). Add test with roundtrip to tst_qimage similar to tst_QPixmap::toWinHBITMAP(). Task-number: QTBUG-51124 Change-Id: Ib568898e7162686bfa527d828785628eb0b78e21 --- src/gui/image/qpixmap_win.cpp | 388 +++++++++++++++++++++++------ tests/auto/gui/image/qimage/qimage.pro | 1 + tests/auto/gui/image/qimage/tst_qimage.cpp | 112 +++++++++ 3 files changed, 423 insertions(+), 78 deletions(-) diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index 92f6964..651e6f6 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -42,36 +42,73 @@ #include #include "qpixmap_raster_p.h" -#include +#include #include #include +#include +#include + QT_BEGIN_NAMESPACE -static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, BITMAPINFOHEADER *bih) +template +static inline Int pad4(Int v) +{ + return (v + Int(3)) & ~Int(3); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const BITMAPINFOHEADER &bih) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "BITMAPINFOHEADER(" << bih.biWidth << 'x' << qAbs(bih.biHeight) + << (bih.biHeight < 0 ? ", top-down" : ", bottom-up") + << ", planes=" << bih.biPlanes << ", bitCount=" << bih.biBitCount + << ", compression=" << bih.biCompression << ", size=" + << bih.biSizeImage << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, + DWORD compression, DWORD bitCount, + BITMAPINFOHEADER *bih) { memset(bih, 0, sizeof(BITMAPINFOHEADER)); bih->biSize = sizeof(BITMAPINFOHEADER); bih->biWidth = width; bih->biHeight = topToBottom ? -height : height; bih->biPlanes = 1; - bih->biBitCount = 32; - bih->biCompression = BI_RGB; - bih->biSizeImage = width * height * 4; + bih->biBitCount = WORD(bitCount); + bih->biCompression = compression; + // scan lines are word-aligned (unless RLE) + const DWORD bytesPerLine = pad4(DWORD(width) * bitCount / 8); + bih->biSizeImage = bytesPerLine * DWORD(height); } -static inline void initBitMapInfo(int width, int height, bool topToBottom, BITMAPINFO *bmi) +enum { Indexed8ColorTableSize = 256 }; + +struct BITMAPINFO_COLORTABLE256 { // BITMAPINFO with 256 entry color table for Indexed 8 format + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[Indexed8ColorTableSize]; +}; + +template // BITMAPINFO, BITMAPINFO_COLORTABLE256 +static inline void initBitMapInfo(int width, int height, bool topToBottom, + DWORD compression, DWORD bitCount, + BITMAPINFO_T *bmi) { - initBitMapInfoHeader(width, height, topToBottom, &bmi->bmiHeader); - memset(bmi->bmiColors, 0, sizeof(RGBQUAD)); + initBitMapInfoHeader(width, height, topToBottom, compression, bitCount, &bmi->bmiHeader); + memset(bmi->bmiColors, 0, sizeof(bmi->bmiColors)); } static inline uchar *getDiBits(HDC hdc, HBITMAP bitmap, int width, int height, bool topToBottom = true) { BITMAPINFO bmi; - initBitMapInfo(width, height, topToBottom, &bmi); + initBitMapInfo(width, height, topToBottom, BI_RGB, 32u, &bmi); uchar *result = new uchar[bmi.bmiHeader.biSizeImage]; - if (!GetDIBits(hdc, bitmap, 0, height, result, &bmi, DIB_RGB_COLORS)) { + if (!GetDIBits(hdc, bitmap, 0, UINT(height), result, &bmi, DIB_RGB_COLORS)) { delete [] result; qErrnoWarning("%s: GetDIBits() failed to get bitmap bits.", __FUNCTION__); return 0; @@ -98,18 +135,92 @@ static inline void copyImageDataCreateAlpha(const uchar *data, QImage *target) } } -static inline void copyImageData(const uchar *data, QImage *target) +// Flip RGB triplets from DIB to QImage formats. Scan lines +// are padded to 32bit both in QImage and DIB. +static inline void flipRgb3(uchar *p, int width, int height) { - const int height = target->height(); - const int bytesPerLine = target->bytesPerLine(); + const int lineSize = 3 * width; + const int linePad = pad4(lineSize) - lineSize; for (int y = 0; y < height; ++y) { - void *dest = static_cast(target->scanLine(y)); - const void *src = data + y * bytesPerLine; - memcpy(dest, src, bytesPerLine); + uchar *end = p + lineSize; + for ( ; p < end; p += 3) + std::swap(*p, *(p + 2)); + p += linePad; + } +} + +static inline RGBQUAD qRgbToRgbQuad(QRgb qrgb) +{ + RGBQUAD result = {BYTE(qBlue(qrgb)), BYTE(qGreen(qrgb)), BYTE(qRed(qrgb)), 0}; + return result; +} + +static inline QRgb rgbQuadToQRgb(RGBQUAD quad) +{ + return QRgb(quad.rgbBlue) + (QRgb(quad.rgbGreen) << 8) + (QRgb(quad.rgbRed) << 16) + + 0xff000000; +} + +// Helper for imageFromWinHBITMAP_*(), create image in desired format +static QImage copyImageData(const BITMAPINFOHEADER &header, const RGBQUAD *colorTableIn, + const void *data, QImage::Format format) +{ + const QSize size = QSize(header.biWidth, qAbs(header.biHeight)); + QImage image(size, format); + + int colorTableSize = 0; + switch (format) { + case QImage::Format_Mono: + colorTableSize = 2; + break; + case QImage::Format_Indexed8: + colorTableSize = Indexed8ColorTableSize; + break; + default: + break; + } + if (colorTableSize) { + Q_ASSERT(colorTableIn); + QVector colorTable; + colorTable.reserve(colorTableSize); + std::transform(colorTableIn, colorTableIn + colorTableSize, + std::back_inserter(colorTable), rgbQuadToQRgb); + image.setColorTable(colorTable); } + switch (header.biBitCount) { + case 32: + copyImageDataCreateAlpha(static_cast(data), &image); + break; + case 1: + case 8: + case 16: + case 24: + Q_ASSERT(DWORD(image.byteCount()) == header.biSizeImage); + memcpy(image.bits(), data, header.biSizeImage); + if (format == QImage::Format_RGB888) + flipRgb3(image.bits(), size.width(), size.height()); + break; + default: + Q_UNREACHABLE(); + break; + } + return image; } +class DisplayHdc +{ + Q_DISABLE_COPY(DisplayHdc) +public: + DisplayHdc() : m_displayDc(GetDC(0)) {} + ~DisplayHdc() { ReleaseDC(0, m_displayDc); } + + operator HDC() const { return m_displayDc; } + +private: + const HDC m_displayDc; +}; + enum HBitmapFormat { HBitmapNoAlpha, @@ -131,34 +242,93 @@ Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap) return hbm; } -Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0) +static inline QImage::Format format32(int hbitmapFormat) { - if (p.isNull()) + switch (hbitmapFormat) { + case HBitmapNoAlpha: + return QImage::Format_RGB32; + case HBitmapAlpha: + return QImage::Format_ARGB32; + default: + break; + } + return QImage::Format_ARGB32_Premultiplied; +} + +Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat = 0) +{ + if (imageIn.isNull()) return 0; - HBITMAP bitmap = 0; - if (p.handle()->classId() != QPlatformPixmap::RasterClass) { - QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ? - QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType); - data->fromImage(p.toImage(), Qt::AutoColor); - return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat); - } + // Define the header + DWORD compression = 0; + DWORD bitCount = 0; - QRasterPlatformPixmap *d = static_cast(p.handle()); - const QImage *rasterImage = d->buffer(); - const int w = rasterImage->width(); - const int h = rasterImage->height(); + // Copy over the data + QImage image = imageIn; + switch (image.format()) { + case QImage::Format_Mono: + bitCount = 1u; + break; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: { + compression = BI_RGB; + bitCount = 32u; + const QImage::Format targetFormat = format32(hbitmapFormat); + if (targetFormat != image.format()) + image = image.convertToFormat(targetFormat); + } + break; + case QImage::Format_RGB888: + compression = BI_RGB; + bitCount = 24u; + break; + case QImage::Format_Indexed8: + bitCount = 8u; + break; + case QImage::Format_RGB555: + bitCount = 16u; + break; + default: { + QImage::Format fallbackFormat = QImage::Format_ARGB32_Premultiplied; + switch (image.format()) { // Convert to a suitable format. + case QImage::Format_MonoLSB: + fallbackFormat = QImage::Format_Mono; + break; + case QImage::Format_RGB16: + fallbackFormat = QImage::Format_RGB555; + break; + case QImage::Format_Grayscale8: + fallbackFormat = QImage::Format_Indexed8; + break; + default: + break; + } // switch conversion format + return qt_imageToWinHBITMAP(imageIn.convertToFormat(fallbackFormat), hbitmapFormat); + } + } - HDC display_dc = GetDC(0); + const int w = image.width(); + const int h = image.height(); - // Define the header - BITMAPINFO bmi; - initBitMapInfo(w, h, true, &bmi); + BITMAPINFO_COLORTABLE256 bmiColorTable256; + initBitMapInfo(w, h, true, compression, bitCount, &bmiColorTable256); + BITMAPINFO &bmi = reinterpret_cast(bmiColorTable256); + switch (image.format()) { + case QImage::Format_Mono: // Color table with 2 entries + case QImage::Format_Indexed8: + std::transform(image.colorTable().constBegin(), image.colorTable().constEnd(), + bmiColorTable256.bmiColors, qRgbToRgbQuad); + break; + default: + break; + } // Create the pixmap - uchar *pixels = 0; - bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0); - ReleaseDC(0, display_dc); + uchar *pixels = nullptr; + const HBITMAP bitmap = CreateDIBSection(0, &bmi, DIB_RGB_COLORS, + reinterpret_cast(&pixels), 0, 0); if (!bitmap) { qErrnoWarning("%s, failed to create dibsection", __FUNCTION__); return 0; @@ -167,59 +337,121 @@ Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = qErrnoWarning("%s, did not allocate pixel data", __FUNCTION__); return 0; } - - // Copy over the data - QImage::Format imageFormat = QImage::Format_RGB32; - if (hbitmapFormat == HBitmapAlpha) - imageFormat = QImage::Format_ARGB32; - else if (hbitmapFormat == HBitmapPremultipliedAlpha) - imageFormat = QImage::Format_ARGB32_Premultiplied; - const QImage image = rasterImage->convertToFormat(imageFormat); - const int bytes_per_line = w * 4; - for (int y=0; y < h; ++y) - memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line); - + memcpy(pixels, image.constBits(), bmi.bmiHeader.biSizeImage); + if (image.format() == QImage::Format_RGB888) + flipRgb3(pixels, w, h); return bitmap; } +Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0) +{ + if (p.isNull()) + return 0; -Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) + QPlatformPixmap *platformPixmap = p.handle(); + if (platformPixmap->classId() != QPlatformPixmap::RasterClass) { + QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ? + QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType); + data->fromImage(p.toImage(), Qt::AutoColor); + return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat); + } + + return qt_imageToWinHBITMAP(*static_cast(platformPixmap)->buffer(), hbitmapFormat); +} + +static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header, int hbitmapFormat) { - // Verify size - BITMAP bitmap_info; - memset(&bitmap_info, 0, sizeof(BITMAP)); + QImage::Format result = QImage::Format_Invalid; + switch (header.biBitCount) { + case 32: + result = hbitmapFormat == HBitmapNoAlpha + ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; + break; + case 24: + result = QImage::Format_RGB888; + break; + case 16: + result = QImage::Format_RGB555; + break; + case 8: + result = QImage::Format_Indexed8; + break; + case 1: + result = QImage::Format_Mono; + break; + } + return result; +} - const int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info); - if (!res) { - qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info"); - return QPixmap(); +// Fast path for creating a QImage directly from a HBITMAP created by CreateDIBSection(), +// not requiring memory allocation. +static QImage imageFromWinHBITMAP_DibSection(HBITMAP bitmap, int hbitmapFormat) +{ + DIBSECTION dibSection; + memset(&dibSection, 0, sizeof(dibSection)); + dibSection.dsBmih.biSize = sizeof(dibSection.dsBmih); + + if (!GetObject(bitmap, sizeof(dibSection), &dibSection) + || !dibSection.dsBm.bmBits + || dibSection.dsBmih.biBitCount <= 8 // Cannot access the color table for Indexed8, Mono + || dibSection.dsBmih.biCompression != BI_RGB) { + return QImage(); } - const int w = bitmap_info.bmWidth; - const int h = bitmap_info.bmHeight; - - // Get bitmap bits - HDC display_dc = GetDC(0); - QScopedArrayPointer data(getDiBits(display_dc, bitmap, w, h, true)); - if (data.isNull()) { - ReleaseDC(0, display_dc); - return QPixmap(); + + const QImage::Format imageFormat = imageFromWinHBITMAP_Format(dibSection.dsBmih, hbitmapFormat); + if (imageFormat == QImage::Format_Invalid) + return QImage(); + + return copyImageData(dibSection.dsBmih, nullptr, dibSection.dsBm.bmBits, imageFormat); +} + +// Create QImage from a HBITMAP using GetDIBits(), potentially with conversion. +static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int hbitmapFormat) +{ + BITMAPINFO_COLORTABLE256 bmiColorTable256; + BITMAPINFO &info = reinterpret_cast(bmiColorTable256); + memset(&info, 0, sizeof(info)); + info.bmiHeader.biSize = sizeof(info.bmiHeader); + + DisplayHdc displayDc; + if (!GetDIBits(displayDc, bitmap, 0, 1, 0, &info, DIB_RGB_COLORS)) { + qErrnoWarning("%s: GetDIBits() failed to query data.", __FUNCTION__); + return QImage(); } - const QImage::Format imageFormat = hbitmapFormat == HBitmapNoAlpha ? - QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; + if (info.bmiHeader.biHeight > 0) // Force top-down + info.bmiHeader.biHeight = -info.bmiHeader.biHeight; + info.bmiHeader.biCompression = BI_RGB; // Extract using no compression (can be BI_BITFIELD) + if (forceQuads) + info.bmiHeader.biBitCount = 32; - // Create image and copy data into image. - QImage image(w, h, imageFormat); - if (image.isNull()) { // failed to alloc? - ReleaseDC(0, display_dc); - qWarning("%s, failed create image of %dx%d", __FUNCTION__, w, h); - return QPixmap(); + const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat); + if (imageFormat == QImage::Format_Invalid) { + qWarning().nospace() << __FUNCTION__ << ": unsupported image format:" << info.bmiHeader; + return QImage(); } - copyImageDataCreateAlpha(data.data(), &image); - ReleaseDC(0, display_dc); - return QPixmap::fromImage(image); + + QScopedPointer data(new uchar[info.bmiHeader.biSizeImage]); + if (!GetDIBits(displayDc, bitmap, 0, qAbs(info.bmiHeader.biHeight), data.data(), &info, DIB_RGB_COLORS)) { + qErrnoWarning("%s: GetDIBits() failed to get data.", __FUNCTION__); + return QImage(); + } + return copyImageData(info.bmiHeader, bmiColorTable256.bmiColors, data.data(), imageFormat); } +Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) +{ + QImage result = imageFromWinHBITMAP_DibSection(bitmap, hbitmapFormat); + if (result.isNull()) + result = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ false, hbitmapFormat); + return result; +} + +Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) +{ + const QImage image = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ true, hbitmapFormat); + return image.isNull() ? QPixmap() : QPixmap::fromImage(image); +} Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p) { @@ -267,7 +499,7 @@ static QImage qt_imageFromWinIconHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) QScopedArrayPointer data(getDiBits(hdc, bitmap, w, h, true)); if (data.isNull()) return QImage(); - copyImageData(data.data(), &image); + memcpy(image.bits(), data.data(), image.byteCount()); return image; } @@ -303,7 +535,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon) const int h = iconinfo.yHotspot * 2; BITMAPINFOHEADER bitmapInfo; - initBitMapInfoHeader(w, h, false, &bitmapInfo); + initBitMapInfoHeader(w, h, false, BI_RGB, 32u, &bitmapInfo); DWORD* bits; HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0); diff --git a/tests/auto/gui/image/qimage/qimage.pro b/tests/auto/gui/image/qimage/qimage.pro index 36d64a2..b00e836 100644 --- a/tests/auto/gui/image/qimage/qimage.pro +++ b/tests/auto/gui/image/qimage/qimage.pro @@ -6,5 +6,6 @@ QT += core-private gui-private testlib contains(QT_CONFIG, c++11): CONFIG += c++11 android: RESOURCES+=qimage.qrc +win32:!winrt: LIBS += -lgdi32 -luser32 TESTDATA += images/* diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index bc48869..0e7e6e1 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -39,6 +39,10 @@ #include #include +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +# include +#endif + Q_DECLARE_METATYPE(QImage::Format) Q_DECLARE_METATYPE(Qt::GlobalColor) @@ -198,6 +202,11 @@ private slots: void ditherGradient_data(); void ditherGradient(); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + void toWinHBITMAP_data(); + void toWinHBITMAP(); +#endif // Q_OS_WIN && !Q_OS_WINRT + private: const QString m_prefix; }; @@ -3190,5 +3199,108 @@ void tst_QImage::ditherGradient() QVERIFY(observedGradientSteps >= minimumExpectedGradient); } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +QT_BEGIN_NAMESPACE +Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &p, int hbitmapFormat = 0); +Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0); +QT_END_NAMESPACE + +static inline QColor COLORREFToQColor(COLORREF cr) +{ + return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); +} + +void tst_QImage::toWinHBITMAP_data() +{ + QTest::addColumn("format"); + QTest::addColumn("color"); + QTest::addColumn("bottomRightColor"); + + const QColor red(Qt::red); + const QColor green(Qt::green); + const QColor blue(Qt::blue); + const QColor gray(Qt::gray); + const QColor gray555(0x5a, 0x5a, 0x5a); // Note: Interpolation 8<->5 bit occurs. + const QColor white(Qt::white); + const QColor black(Qt::black); + + QTest::newRow("argb32p-red") << QImage::Format_ARGB32_Premultiplied << red << gray; + QTest::newRow("argb32p-green") << QImage::Format_ARGB32_Premultiplied << green << gray; + QTest::newRow("argb32p-blue") << QImage::Format_ARGB32_Premultiplied << blue << gray; + QTest::newRow("rgb888-red") << QImage::Format_RGB888 << red << gray; + QTest::newRow("rgb888-green") << QImage::Format_RGB888 << green << gray; + QTest::newRow("rgb888-blue") << QImage::Format_RGB888 << blue << gray; + QTest::newRow("indexed8-red") << QImage::Format_Indexed8 << red << gray; + QTest::newRow("indexed8-green") << QImage::Format_Indexed8 << green << gray; + QTest::newRow("indexed8-blue") << QImage::Format_Indexed8 << blue << gray; + QTest::newRow("rgb555-red") << QImage::Format_RGB555 << red << gray555; + QTest::newRow("rgb555-green") << QImage::Format_RGB555 << green << gray555; + QTest::newRow("rgb555-blue") << QImage::Format_RGB555 << blue << gray555; + QTest::newRow("mono") << QImage::Format_Mono << white << black; +} + +// Test image filled with color, black pixel at botttom right corner. +static inline QImage createTestImage(QImage::Format format, int width, int height, + const QColor &fillColor, const QColor &bottomRightColor) +{ + QImage image(QSize(width, height), format); + image.fill(fillColor); + QPainter painter(&image); + QPen pen = painter.pen(); + pen.setColor(bottomRightColor); + painter.setPen(pen); + painter.drawPoint(width -1, height - 1); + return image; +} + +void tst_QImage::toWinHBITMAP() +{ + static const int width = 73; + static const int height = 57; + + QFETCH(QImage::Format, format); + QFETCH(QColor, color); + QFETCH(QColor, bottomRightColor); + + // Cannot paint on indexed/mono images. + const QImage image = format == QImage::Format_Indexed8 || format == QImage::Format_Mono + ? createTestImage(QImage::Format_RGB32, width, height, color, bottomRightColor).convertToFormat(format) + : createTestImage(format, width, height, color, bottomRightColor); + + const HBITMAP bitmap = qt_imageToWinHBITMAP(image); + + QVERIFY(bitmap != 0); + + // Verify size + BITMAP bitmapInfo; + memset(&bitmapInfo, 0, sizeof(BITMAP)); + + const int res = GetObject(bitmap, sizeof(BITMAP), &bitmapInfo); + QVERIFY(res); + QCOMPARE(width, int(bitmapInfo.bmWidth)); + QCOMPARE(height, int(bitmapInfo.bmHeight)); + + const HDC displayDc = GetDC(0); + const HDC bitmapDc = CreateCompatibleDC(displayDc); + + const HBITMAP nullBitmap = static_cast(SelectObject(bitmapDc, bitmap)); + + QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, 0, 0)), color); + QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, width - 1, 3)), color); + QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, 3, height - 1)), color); + QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, width - 1, height - 1)), bottomRightColor); + + const QImage convertedBack = qt_imageFromWinHBITMAP(bitmap); + QCOMPARE(convertedBack.convertToFormat(QImage::Format_ARGB32_Premultiplied), + image.convertToFormat(QImage::Format_ARGB32_Premultiplied)); + + // Clean up + SelectObject(bitmapDc, nullBitmap); + DeleteObject(bitmap); + DeleteDC(bitmapDc); + ReleaseDC(0, displayDc); +} +#endif // Q_OS_WIN && !Q_OS_WINRT + QTEST_GUILESS_MAIN(tst_QImage) #include "tst_qimage.moc" -- 2.8.1.windows.1