diff options
Diffstat (limited to 'src/gui/image/qbmphandler.cpp')
-rw-r--r-- | src/gui/image/qbmphandler.cpp | 178 |
1 files changed, 91 insertions, 87 deletions
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index 6d265ded67..798ba7f4b3 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "private/qbmphandler_p.h" @@ -104,6 +68,7 @@ const int BMP_RGB = 0; // no compression const int BMP_RLE8 = 1; // run-length encoded, 8 bits const int BMP_RLE4 = 2; // run-length encoded, 4 bits const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields +const int BMP_ALPHABITFIELDS = 4; // RGBA values encoded in data as bit-fields static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) @@ -170,16 +135,42 @@ static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) return s; } -static int calc_shift(uint mask) +static uint calc_shift(uint mask) { - int result = 0; - while (mask && !(mask & 1)) { + uint result = 0; + while ((mask >= 0x100) || (!(mask & 1) && mask)) { result++; mask >>= 1; } return result; } +static uint calc_scale(uint low_mask) +{ + uint result = 8; + while (low_mask && result) { + result--; + low_mask >>= 1; + } + return result; +} + +static inline uint apply_scale(uint value, uint scale) +{ + if (!(scale & 0x07)) // return immediately if scale == 8 or 0 + return value; + + uint filled = 8 - scale; + uint result = value << scale; + + do { + result |= result >> filled; + filled <<= 1; + } while (filled < 8); + + return result; +} + static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf) { // read BMP file header @@ -216,13 +207,13 @@ static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi) return true; } -static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, qint64 startpos, QImage &image) +static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 datapos, qint64 startpos, QImage &image) { QIODevice* d = s.device(); if (d->atEnd()) // end of stream/file return false; #if 0 - qDebug("offset...........%lld", offset); + qDebug("offset...........%lld", datapos); qDebug("startpos.........%lld", startpos); qDebug("biSize...........%d", bi.biSize); qDebug("biWidth..........%d", bi.biWidth); @@ -242,33 +233,36 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, uint green_mask = 0; uint blue_mask = 0; uint alpha_mask = 0; - int red_shift = 0; - int green_shift = 0; - int blue_shift = 0; - int alpha_shift = 0; - int red_scale = 0; - int green_scale = 0; - int blue_scale = 0; - int alpha_scale = 0; + uint red_shift = 0; + uint green_shift = 0; + uint blue_shift = 0; + uint alpha_shift = 0; + uint red_scale = 0; + uint green_scale = 0; + uint blue_scale = 0; + uint alpha_scale = 0; + bool bitfields = comp == BMP_BITFIELDS || comp == BMP_ALPHABITFIELDS; if (!d->isSequential()) - d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap or masks + d->seek(startpos + bi.biSize); // goto start of colormap or masks if (bi.biSize >= BMP_WIN4) { red_mask = bi.biRedMask; green_mask = bi.biGreenMask; blue_mask = bi.biBlueMask; alpha_mask = bi.biAlphaMask; - } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) { + } else if (bitfields && (nbits == 16 || nbits == 32)) { if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask)) return false; if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask)) return false; if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask)) return false; + if (comp == BMP_ALPHABITFIELDS && d->read((char *)&alpha_mask, sizeof(alpha_mask)) != sizeof(alpha_mask)) + return false; } - bool transp = comp == BMP_BITFIELDS || (comp == BMP_RGB && nbits == 32 && alpha_mask == 0xff000000); + bool transp = bitfields || (comp == BMP_RGB && nbits == 32 && alpha_mask == 0xff000000); transp = transp && alpha_mask; int ncols = 0; @@ -317,23 +311,23 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, if (d->atEnd()) // truncated file return false; } - } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) { + } else if (bitfields && (nbits == 16 || nbits == 32)) { red_shift = calc_shift(red_mask); if (((red_mask >> red_shift) + 1) == 0) return false; - red_scale = 256 / ((red_mask >> red_shift) + 1); + red_scale = calc_scale(red_mask >> red_shift); green_shift = calc_shift(green_mask); if (((green_mask >> green_shift) + 1) == 0) return false; - green_scale = 256 / ((green_mask >> green_shift) + 1); + green_scale = calc_scale(green_mask >> green_shift); blue_shift = calc_shift(blue_mask); if (((blue_mask >> blue_shift) + 1) == 0) return false; - blue_scale = 256 / ((blue_mask >> blue_shift) + 1); + blue_scale = calc_scale(blue_mask >> blue_shift); alpha_shift = calc_shift(alpha_mask); if (((alpha_mask >> alpha_shift) + 1) == 0) return false; - alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); + alpha_scale = calc_scale(alpha_mask >> alpha_shift); } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) { blue_mask = 0x000000ff; green_mask = 0x0000ff00; @@ -341,23 +335,21 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, blue_shift = 0; green_shift = 8; red_shift = 16; - blue_scale = green_scale = red_scale = 1; + blue_scale = green_scale = red_scale = 0; 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); + alpha_scale = calc_scale(alpha_mask >> alpha_shift); } } else if (comp == BMP_RGB && nbits == 16) { blue_mask = 0x001f; green_mask = 0x03e0; red_mask = 0x7c00; blue_shift = 0; - green_shift = 2; - red_shift = 7; - red_scale = 1; - green_scale = 1; - blue_scale = 8; + green_shift = 5; + red_shift = 10; + blue_scale = green_scale = red_scale = 3; } image.setDotsPerMeterX(bi.biXPelsPerMeter); @@ -370,10 +362,9 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, qDebug("Amask: %08x Ashift: %08x Ascale:%08x", alpha_mask, alpha_shift, alpha_scale); #endif - // offset can be bogus, be careful - if (offset>=0 && startpos + offset > d->pos()) { + if (datapos >= 0 && datapos > d->pos()) { if (!d->isSequential()) - d->seek(startpos + offset); // start of image data + d->seek(datapos); // start of image data } int bpl = image.bytesPerLine(); @@ -566,10 +557,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, c |= *(uchar*)(b+2)<<16; if (nbits > 24) c |= *(uchar*)(b+3)<<24; - *p++ = qRgba(((c & red_mask) >> red_shift) * red_scale, - ((c & green_mask) >> green_shift) * green_scale, - ((c & blue_mask) >> blue_shift) * blue_scale, - transp ? ((c & alpha_mask) >> alpha_shift) * alpha_scale : 0xff); + *p++ = qRgba(apply_scale((c & red_mask) >> red_shift, red_scale), + apply_scale((c & green_mask) >> green_shift, green_scale), + apply_scale((c & blue_mask) >> blue_shift, blue_scale), + transp ? apply_scale((c & alpha_mask) >> alpha_shift, alpha_scale) : 0xff); b += nbits/8; } } @@ -591,7 +582,6 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, return true; } -// this is also used in qmime_win.cpp bool qt_write_dib(QDataStream &s, const QImage &image, int bpl, int bpl_bmp, int nbits) { QIODevice* d = s.device(); @@ -618,7 +608,7 @@ bool qt_write_dib(QDataStream &s, const QImage &image, int bpl, int bpl_bmp, int if (image.depth() != 32) { // write color table uchar *color_table = new uchar[4*image.colorCount()]; uchar *rgb = color_table; - QList<QRgb> c = image.colorTable(); + const QList<QRgb> c = image.colorTable(); for (int i = 0; i < image.colorCount(); i++) { *rgb++ = qBlue (c[i]); *rgb++ = qGreen(c[i]); @@ -678,15 +668,6 @@ bool qt_write_dib(QDataStream &s, const QImage &image, int bpl, int bpl_bmp, int return true; } -// this is also used in qmime_win.cpp -bool qt_read_dib(QDataStream &s, QImage &image) -{ - BMP_INFOHDR bi; - if (!read_dib_infoheader(s, bi)) - return false; - return read_dib_body(s, bi, -1, -BMP_FILEHDR_SIZE, image); -} - QBmpHandler::QBmpHandler(InternalFormat fmt) : m_format(fmt), state(Ready) { @@ -769,9 +750,32 @@ bool QBmpHandler::read(QImage *image) s.setByteOrder(QDataStream::LittleEndian); // read image + qint64 datapos = startpos; + if (m_format == BmpFormat) { + datapos += fileHeader.bfOffBits; + } else { + // QTBUG-100351: We have no file header when reading dib format so we have to depend on the size of the + // buffer and the biSizeImage value to find where the pixel data starts since there's sometimes optional + // color mask values after biSize, like for example when pasting from the windows snipping tool. + if (infoHeader.biSizeImage > 0 && infoHeader.biSizeImage < d->size()) { + datapos = d->size() - infoHeader.biSizeImage; + } else { + // And sometimes biSizeImage is not filled in like when pasting from Microsoft Edge, so then we just + // have to assume the optional color mask values are there. + datapos += infoHeader.biSize; + + if (infoHeader.biBitCount == 16 || infoHeader.biBitCount == 32) { + if (infoHeader.biCompression == BMP_BITFIELDS) { + datapos += 12; + } else if (infoHeader.biCompression == BMP_ALPHABITFIELDS) { + datapos += 16; + } + } + } + } const bool readSuccess = m_format == BmpFormat ? - read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image) : - read_dib_body(s, infoHeader, -1, startpos - BMP_FILEHDR_SIZE, *image); + read_dib_body(s, infoHeader, datapos, startpos + BMP_FILEHDR_SIZE, *image) : + read_dib_body(s, infoHeader, datapos, startpos, *image); if (!readSuccess) return false; @@ -875,7 +879,7 @@ QVariant QBmpHandler::option(ImageOption option) const case 32: case 24: case 16: - if (infoHeader.biCompression == BMP_BITFIELDS && infoHeader.biSize >= BMP_WIN4 && infoHeader.biAlphaMask) + if ((infoHeader.biCompression == BMP_BITFIELDS || infoHeader.biCompression == BMP_ALPHABITFIELDS) && infoHeader.biSize >= BMP_WIN4 && infoHeader.biAlphaMask) format = QImage::Format_ARGB32; else format = QImage::Format_RGB32; |