Clean up code that handle clipboard image conversion on Windows

This commit remove old legacy code that try to deal with clipboard
image conversion in the qwindowsmime class. It now uses
qbmphandler under the hood which is much more complete.
It also add a small fix for the conversion
of BI_RGB clipboard image with an explicit alpha layer (which Firefox
use on Windows).

Change-Id: Iae026378831799dc676e1aba7d5bd6a0d1c01e7f
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
Camille Viot 2020-11-30 18:30:56 +01:00
parent e7370d0583
commit 8fa91c75ad
2 changed files with 43 additions and 127 deletions

View File

@ -147,6 +147,26 @@ static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
s << bi.biSizeImage;
s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
s << bi.biClrUsed << bi.biClrImportant;
if (bi.biSize >= BMP_WIN4) {
s << bi.biRedMask << bi.biGreenMask << bi.biBlueMask << bi.biAlphaMask;
s << bi.biCSType;
for (int i = 0; i < 9; i++)
s << bi.biEndpoints[i];
s << bi.biGammaRed;
s << bi.biGammaGreen;
s << bi.biGammaBlue;
}
if (bi.biSize >= BMP_WIN5) {
s << bi.biIntent;
s << bi.biProfileData;
s << bi.biProfileSize;
s << bi.biReserved;
}
return s;
}
@ -248,7 +268,9 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset,
return false;
}
bool transp = (comp == BMP_BITFIELDS) && alpha_mask;
bool transp = comp == BMP_BITFIELDS || (comp == BMP_RGB && nbits == 32 && alpha_mask == 0xff000000);
transp = transp && alpha_mask;
int ncols = 0;
int depth = 0;
QImage::Format format;
@ -320,6 +342,12 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset,
green_shift = 8;
red_shift = 16;
blue_scale = green_scale = red_scale = 1;
if (transp) {
alpha_shift = calc_shift(alpha_mask);
if (((alpha_mask >> alpha_shift) + 1) == 0)
return false;
alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1);
}
} else if (comp == BMP_RGB && nbits == 16) {
blue_mask = 0x001f;
green_mask = 0x03e0;

View File

@ -114,16 +114,15 @@ static inline QByteArray msgConversionError(const char *func, const char *format
return msg;
}
static inline QImage readDib(QByteArray data)
static inline bool readDib(QBuffer &buffer, QImage &img)
{
QBuffer buffer(&data);
buffer.open(QIODevice::ReadOnly);
QImageReader reader(&buffer, dibFormatC);
if (!reader.canRead()) {
qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData());
return QImage();
qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData());
return false;
}
return reader.read();
img = reader.read();
return true;
}
static QByteArray writeDib(const QImage &img)
@ -213,94 +212,6 @@ static bool qt_write_dibv5(QDataStream &s, QImage image)
return true;
}
static int calc_shift(int mask)
{
int result = 0;
while (!(mask & 1)) {
result++;
mask >>= 1;
}
return result;
}
//Supports only 32 bit DIBV5
static bool qt_read_dibv5(QDataStream &s, QImage &image)
{
BMP_BITMAPV5HEADER bi;
QIODevice* d = s.device();
if (d->atEnd())
return false;
d->read(reinterpret_cast<char *>(&bi), sizeof(bi)); // read BITMAPV5HEADER header
if (s.status() != QDataStream::Ok)
return false;
const int nbits = bi.bV5BitCount;
if (nbits != 32 || bi.bV5Planes != 1 || bi.bV5Compression != BMP_BITFIELDS)
return false; //Unsupported DIBV5 format
const int w = bi.bV5Width;
int h = bi.bV5Height;
const int red_mask = int(bi.bV5RedMask);
const int green_mask = int(bi.bV5GreenMask);
const int blue_mask = int(bi.bV5BlueMask);
const int alpha_mask = int(bi.bV5AlphaMask);
const QImage::Format format = QImage::Format_ARGB32;
if (bi.bV5Height < 0)
h = -h; // support images with negative height
if (image.size() != QSize(w, h) || image.format() != format) {
image = QImage(w, h, format);
if (image.isNull()) // could not create image
return false;
}
image.setDotsPerMeterX(bi.bV5XPelsPerMeter);
image.setDotsPerMeterY(bi.bV5YPelsPerMeter);
const int red_shift = calc_shift(red_mask);
const int green_shift = calc_shift(green_mask);
const int blue_shift = calc_shift(blue_mask);
const int alpha_shift = alpha_mask ? calc_shift(alpha_mask) : 0u;
const qsizetype bpl = image.bytesPerLine();
uchar *data = image.bits();
auto *buf24 = new uchar[bpl];
const qsizetype bpl24 = ((qsizetype(w) * nbits + 31) / 32) * 4;
while (--h >= 0) {
QRgb *p = reinterpret_cast<QRgb *>(data + h * bpl);
QRgb *end = p + w;
if (d->read(reinterpret_cast<char *>(buf24), bpl24) != bpl24)
break;
const uchar *b = buf24;
while (p < end) {
const int c = *b | (*(b + 1)) << 8 | (*(b + 2)) << 16 | (*(b + 3)) << 24;
*p++ = qRgba(((c & red_mask) >> red_shift) ,
((c & green_mask) >> green_shift),
((c & blue_mask) >> blue_shift),
((c & alpha_mask) >> alpha_shift));
b += 4;
}
}
delete[] buf24;
if (bi.bV5Height < 0) {
// Flip the image
auto *buf = new uchar[bpl];
h = -bi.bV5Height;
for (int y = 0; y < h/2; ++y) {
memcpy(buf, data + y * bpl, size_t(bpl));
memcpy(data + y*bpl, data + (h - y -1) * bpl, size_t(bpl));
memcpy(data + (h - y -1 ) * bpl, buf, size_t(bpl));
}
delete [] buf;
}
return true;
}
// helpers for using global memory
static int getCf(const FORMATETC &formatetc)
@ -1018,7 +929,6 @@ public:
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override;
QString mimeForFormat(const FORMATETC &formatetc) const override;
private:
bool hasOriginalDIBV5(IDataObject *pDataObj) const;
UINT CF_PNG;
};
@ -1100,44 +1010,20 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD
return false;
}
bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
{
bool isSynthesized = true;
IEnumFORMATETC *pEnum = nullptr;
HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
if (res == S_OK && pEnum) {
FORMATETC fc;
while ((res = pEnum->Next(1, &fc, nullptr)) == S_OK) {
if (fc.ptd)
CoTaskMemFree(fc.ptd);
if (fc.cfFormat == CF_DIB)
break;
if (fc.cfFormat == CF_DIBV5) {
isSynthesized = false;
break;
}
}
pEnum->Release();
}
return !isSynthesized;
}
QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const
{
Q_UNUSED(preferredType);
QVariant result;
if (mimeType != u"application/x-qt-image")
return result;
//Try to convert from a format which has more data
//DIBV5, use only if its is not synthesized
if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) {
//Try to convert from DIBV5 as it is the most
//widespread format that support transparency
if (canGetData(CF_DIBV5, pDataObj)) {
QImage img;
QByteArray data = getData(CF_DIBV5, pDataObj);
QDataStream s(&data, QIODevice::ReadOnly);
s.setByteOrder(QDataStream::LittleEndian);
if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5
QBuffer buffer(&data);
if (readDib(buffer, img))
return img;
}
}
//PNG, MS Office place this (undocumented)
if (canGetData(CF_PNG, pDataObj)) {
@ -1149,8 +1035,10 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *
}
//Fallback to DIB
if (canGetData(CF_DIB, pDataObj)) {
const QImage img = readDib(getData(CF_DIB, pDataObj));
if (!img.isNull())
QImage img;
QByteArray data = getData(CF_DIBV5, pDataObj);
QBuffer buffer(&data);
if (readDib(buffer, img))
return img;
}
// Failed