Windows: Fix qt_imageFromWinHBITMAP().

Factor out common code paths from the QPixmap/QImage conversion
code and use the same algorithm for copying the image data with
alpha correction as does qt_pixmapFromWinHBITMAP().

Rename the previous version of qt_imageFromWinHBITMAP() to
qt_imageFromWinIconHBITMAP() since it is used for HICON conversion.

Task-number: QTBUG-39084
Change-Id: Ia4042c33db485c3604461a5eafd6282968b36e3b
Reviewed-by: Andy Shaw <andy.shaw@digia.com>
This commit is contained in:
Friedemann Kleint 2014-05-20 14:44:26 +02:00 committed by The Qt Project
parent 57d0ed8f1e
commit d3d8ade87b

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal ** Contact: http://www.qt-project.org/legal
** **
** This file is part of the QtGui module of the Qt Toolkit. ** This file is part of the QtGui module of the Qt Toolkit.
@ -133,6 +133,68 @@ int qt_wince_GetDIBits(HDC /*hdc*/ , HBITMAP hSourceBitmap, uint, uint, LPVOID l
} }
#endif #endif
static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, 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;
}
static inline void initBitMapInfo(int width, int height, bool topToBottom, BITMAPINFO *bmi)
{
initBitMapInfoHeader(width, height, topToBottom, &bmi->bmiHeader);
memset(bmi->bmiColors, 0, sizeof(RGBQUAD));
}
static inline uchar *getDiBits(HDC hdc, HBITMAP bitmap, int width, int height, bool topToBottom = true)
{
BITMAPINFO bmi;
initBitMapInfo(width, height, topToBottom, &bmi);
uchar *result = new uchar[bmi.bmiHeader.biSizeImage];
if (!GetDIBits(hdc, bitmap, 0, height, result, &bmi, DIB_RGB_COLORS)) {
delete [] result;
qErrnoWarning("%s: GetDIBits() failed to get bitmap bits.", __FUNCTION__);
return 0;
}
return result;
}
static inline void copyImageDataCreateAlpha(const uchar *data, QImage *target)
{
const uint mask = target->format() == QImage::Format_RGB32 ? 0xff000000 : 0;
const int height = target->height();
const int width = target->width();
const int bytesPerLine = width * int(sizeof(QRgb));
for (int y = 0; y < height; ++y) {
QRgb *dest = reinterpret_cast<QRgb *>(target->scanLine(y));
const QRgb *src = reinterpret_cast<const QRgb *>(data + y * bytesPerLine);
for (int x = 0; x < width; ++x) {
const uint pixel = src[x];
if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0)
dest[x] = pixel | 0xff000000;
else
dest[x] = pixel | mask;
}
}
}
static inline void copyImageData(const uchar *data, QImage *target)
{
const int height = target->height();
const int bytesPerLine = target->bytesPerLine();
for (int y = 0; y < height; ++y) {
void *dest = static_cast<void *>(target->scanLine(y));
const void *src = data + y * bytesPerLine;
memcpy(dest, src, bytesPerLine);
}
}
enum HBitmapFormat enum HBitmapFormat
{ {
HBitmapNoAlpha, HBitmapNoAlpha,
@ -176,14 +238,7 @@ Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat =
// Define the header // Define the header
BITMAPINFO bmi; BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi)); initBitMapInfo(w, h, true, &bmi);
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = w * h * 4;
// Create the pixmap // Create the pixmap
uchar *pixels = 0; uchar *pixels = 0;
@ -227,31 +282,16 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat =
const int w = bitmap_info.bmWidth; const int w = bitmap_info.bmWidth;
const int h = bitmap_info.bmHeight; const int h = bitmap_info.bmHeight;
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = w * h * 4;
// Get bitmap bits // Get bitmap bits
QScopedArrayPointer<uchar> data(new uchar[bmi.bmiHeader.biSizeImage]);
HDC display_dc = GetDC(0); HDC display_dc = GetDC(0);
if (!GetDIBits(display_dc, bitmap, 0, h, data.data(), &bmi, DIB_RGB_COLORS)) { QScopedArrayPointer<uchar> data(getDiBits(display_dc, bitmap, w, h, true));
if (data.isNull()) {
ReleaseDC(0, display_dc); ReleaseDC(0, display_dc);
qWarning("%s, failed to get bitmap bits", __FUNCTION__);
return QPixmap(); return QPixmap();
} }
QImage::Format imageFormat = QImage::Format_ARGB32_Premultiplied; const QImage::Format imageFormat = hbitmapFormat == HBitmapNoAlpha ?
uint mask = 0; QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied;
if (hbitmapFormat == HBitmapNoAlpha) {
imageFormat = QImage::Format_RGB32;
mask = 0xff000000;
}
// Create image and copy data into image. // Create image and copy data into image.
QImage image(w, h, imageFormat); QImage image(w, h, imageFormat);
@ -260,18 +300,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat =
qWarning("%s, failed create image of %dx%d", __FUNCTION__, w, h); qWarning("%s, failed create image of %dx%d", __FUNCTION__, w, h);
return QPixmap(); return QPixmap();
} }
const int bytes_per_line = w * sizeof(QRgb); copyImageDataCreateAlpha(data.data(), &image);
for (int y = 0; y < h; ++y) {
QRgb *dest = (QRgb *) image.scanLine(y);
const QRgb *src = (const QRgb *) (data.data() + y * bytes_per_line);
for (int x = 0; x < w; ++x) {
const uint pixel = src[x];
if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0)
dest[x] = pixel | 0xff000000;
else
dest[x] = pixel | mask;
}
}
ReleaseDC(0, display_dc); ReleaseDC(0, display_dc);
return QPixmap::fromImage(image); return QPixmap::fromImage(image);
} }
@ -307,32 +336,25 @@ Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p)
Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
{ {
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = w * h * 4;
QImage image(w, h, QImage::Format_ARGB32_Premultiplied); QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
if (image.isNull()) if (image.isNull())
return image; return image;
QScopedArrayPointer<uchar> data(getDiBits(hdc, bitmap, w, h, true));
// Get bitmap bits if (data.isNull())
QScopedArrayPointer<uchar> data(new uchar [bmi.bmiHeader.biSizeImage]);
if (!GetDIBits(hdc, bitmap, 0, h, data.data(), &bmi, DIB_RGB_COLORS)) {
qErrnoWarning("%s: failed to get bitmap bits", __FUNCTION__);
return QImage(); return QImage();
} copyImageDataCreateAlpha(data.data(), &image);
// Create image and copy data into image. return image;
for (int y = 0; y < h; ++y) { }
void *dest = (void *) image.scanLine(y);
void *src = data.data() + y * image.bytesPerLine(); static QImage qt_imageFromWinIconHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
memcpy(dest, src, image.bytesPerLine()); {
} QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
if (image.isNull())
return image;
QScopedArrayPointer<uchar> data(getDiBits(hdc, bitmap, w, h, true));
if (data.isNull())
return QImage();
copyImageData(data.data(), &image);
return image; return image;
} }
@ -354,23 +376,13 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
const int h = iconinfo.yHotspot * 2; const int h = iconinfo.yHotspot * 2;
BITMAPINFOHEADER bitmapInfo; BITMAPINFOHEADER bitmapInfo;
bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); initBitMapInfoHeader(w, h, false, &bitmapInfo);
bitmapInfo.biWidth = w;
bitmapInfo.biHeight = h;
bitmapInfo.biPlanes = 1;
bitmapInfo.biBitCount = 32;
bitmapInfo.biCompression = BI_RGB;
bitmapInfo.biSizeImage = 0;
bitmapInfo.biXPelsPerMeter = 0;
bitmapInfo.biYPelsPerMeter = 0;
bitmapInfo.biClrUsed = 0;
bitmapInfo.biClrImportant = 0;
DWORD* bits; DWORD* bits;
HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0); HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap); HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap);
DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL); DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL);
QImage image = qt_imageFromWinHBITMAP(hdc, winBitmap, w, h); QImage image = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h);
for (int y = 0 ; y < h && !foundAlpha ; y++) { for (int y = 0 ; y < h && !foundAlpha ; y++) {
const QRgb *scanLine= reinterpret_cast<const QRgb *>(image.scanLine(y)); const QRgb *scanLine= reinterpret_cast<const QRgb *>(image.scanLine(y));
@ -384,7 +396,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon)
if (!foundAlpha) { if (!foundAlpha) {
//If no alpha was found, we use the mask to set alpha values //If no alpha was found, we use the mask to set alpha values
DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK); DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK);
const QImage mask = qt_imageFromWinHBITMAP(hdc, winBitmap, w, h); const QImage mask = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h);
for (int y = 0 ; y < h ; y++){ for (int y = 0 ; y < h ; y++){
QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y)); QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y));