summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qbmphandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image/qbmphandler.cpp')
-rw-r--r--src/gui/image/qbmphandler.cpp178
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;