diff options
-rw-r--r-- | src/gui/image/qbmphandler.cpp | 30 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsmime.cpp | 140 |
2 files changed, 43 insertions, 127 deletions
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index 74df9820e4..6d265ded67 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -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; diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 95c9a890ef..3321845411 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -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 |