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 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>bb10
parent
5e7a64d1e3
commit
2268b6bcdd
|
|
@ -42,36 +42,73 @@
|
|||
#include <qpa/qplatformpixmap.h>
|
||||
#include "qpixmap_raster_p.h"
|
||||
|
||||
#include <qglobal.h>
|
||||
#include <qdebug.h>
|
||||
#include <QScopedArrayPointer>
|
||||
#include <qt_windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, BITMAPINFOHEADER *bih)
|
||||
template <typename Int>
|
||||
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 <class BITMAPINFO_T> // 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<void *>(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<QRgb> 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<const uchar *>(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)
|
||||
image = image.rgbSwapped();
|
||||
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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
QRasterPlatformPixmap *d = static_cast<QRasterPlatformPixmap*>(p.handle());
|
||||
const QImage *rasterImage = d->buffer();
|
||||
const int w = rasterImage->width();
|
||||
const int h = rasterImage->height();
|
||||
const int w = image.width();
|
||||
const int h = image.height();
|
||||
|
||||
HDC display_dc = GetDC(0);
|
||||
|
||||
// Define the header
|
||||
BITMAPINFO bmi;
|
||||
initBitMapInfo(w, h, true, &bmi);
|
||||
BITMAPINFO_COLORTABLE256 bmiColorTable256;
|
||||
initBitMapInfo(w, h, true, compression, bitCount, &bmiColorTable256);
|
||||
BITMAPINFO &bmi = reinterpret_cast<BITMAPINFO &>(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<void **>(&pixels), 0, 0);
|
||||
if (!bitmap) {
|
||||
qErrnoWarning("%s, failed to create dibsection", __FUNCTION__);
|
||||
return 0;
|
||||
|
|
@ -167,60 +337,122 @@ 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;
|
||||
|
||||
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<QRasterPlatformPixmap*>(platformPixmap)->buffer(), hbitmapFormat);
|
||||
}
|
||||
|
||||
static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header, int hbitmapFormat)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 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<BITMAPINFO &>(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();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat);
|
||||
if (imageFormat == QImage::Format_Invalid) {
|
||||
qWarning().nospace() << __FUNCTION__ << ": unsupported image format:" << info.bmiHeader;
|
||||
return QImage();
|
||||
}
|
||||
|
||||
QScopedPointer<uchar> 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)
|
||||
{
|
||||
// Verify size
|
||||
BITMAP bitmap_info;
|
||||
memset(&bitmap_info, 0, sizeof(BITMAP));
|
||||
|
||||
const int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info);
|
||||
if (!res) {
|
||||
qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info");
|
||||
return QPixmap();
|
||||
}
|
||||
const int w = bitmap_info.bmWidth;
|
||||
const int h = bitmap_info.bmHeight;
|
||||
|
||||
// Get bitmap bits
|
||||
HDC display_dc = GetDC(0);
|
||||
QScopedArrayPointer<uchar> data(getDiBits(display_dc, bitmap, w, h, true));
|
||||
if (data.isNull()) {
|
||||
ReleaseDC(0, display_dc);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
const QImage::Format imageFormat = hbitmapFormat == HBitmapNoAlpha ?
|
||||
QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied;
|
||||
|
||||
// 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();
|
||||
}
|
||||
copyImageDataCreateAlpha(data.data(), &image);
|
||||
ReleaseDC(0, display_dc);
|
||||
const QImage image = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ true, hbitmapFormat);
|
||||
return QPixmap::fromImage(image);
|
||||
}
|
||||
|
||||
|
||||
Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p)
|
||||
{
|
||||
if (p.isNull())
|
||||
|
|
@ -267,7 +499,7 @@ static QImage qt_imageFromWinIconHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h)
|
|||
QScopedArrayPointer<uchar> 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);
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ qtConfig(c++11): CONFIG += c++11
|
|||
|
||||
android:!android-embedded: RESOURCES += qimage.qrc
|
||||
|
||||
win32:!winrt: LIBS += -lgdi32 -luser32
|
||||
|
||||
TESTDATA += images/*
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@
|
|||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||
# include <qt_windows.h>
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(QImage::Format)
|
||||
Q_DECLARE_METATYPE(Qt::GlobalColor)
|
||||
|
||||
|
|
@ -223,6 +227,11 @@ private slots:
|
|||
|
||||
void hugeQImage();
|
||||
|
||||
#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;
|
||||
};
|
||||
|
|
@ -3487,5 +3496,108 @@ void tst_QImage::hugeQImage()
|
|||
#endif
|
||||
}
|
||||
|
||||
#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<QImage::Format>("format");
|
||||
QTest::addColumn<QColor>("color");
|
||||
QTest::addColumn<QColor>("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<HBITMAP>(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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue