diff options
Diffstat (limited to 'src/gui/image/qimage.cpp')
-rw-r--r-- | src/gui/image/qimage.cpp | 246 |
1 files changed, 160 insertions, 86 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 6699e516a0..8a0f570f47 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -47,7 +47,6 @@ #include <ctype.h> #include <stdlib.h> #include <limits.h> -#include <math.h> #include <qpa/qplatformpixmap.h> #include <private/qdrawhelper_p.h> #include <private/qmemrotate_p.h> @@ -653,7 +652,8 @@ bool QImageData::checkForAlphaPixels() const The following image formats are available in Qt. Values from Format_ARGB8565_Premultiplied to Format_ARGB4444_Premultiplied were added in Qt 4.4. Values Format_RGBX8888, Format_RGBA8888 and Format_RGBA8888_Premultiplied were added in Qt 5.2. Values Format_BGR30, Format_A2BGR30_Premultiplied, - Format_RGB30, Format_A2RGB30_Premultiplied were added in Qt 5.4. + Format_RGB30, Format_A2RGB30_Premultiplied were added in Qt 5.4. Format_Alpha8 and Format_Grayscale8 + were added in Qt 5.5. See the notes after the table. \value Format_Invalid The image is invalid. @@ -709,6 +709,8 @@ bool QImageData::checkForAlphaPixels() const \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). + \value Format_Alpha8 The image is stored using an 8-bit alpha only format. + \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. \note Drawing into a QImage with QImage::Format_Indexed8 is not supported. @@ -732,7 +734,7 @@ bool QImageData::checkForAlphaPixels() const \sa isNull() */ -QImage::QImage() +QImage::QImage() Q_DECL_NOEXCEPT : QPaintDevice() { d = 0; @@ -1079,11 +1081,21 @@ void QImage::detach() if (d->ref.load() != 1 || d->ro_data) *this = copy(); - ++d->detach_no; + if (d) + ++d->detach_no; } } +static void copyMetadata(QImageData *dst, const QImageData *src) +{ + // Doesn't copy colortable and alpha_clut, or offset. + dst->dpmx = src->dpmx; + dst->dpmy = src->dpmy; + dst->devicePixelRatio = src->devicePixelRatio; + dst->text = src->text; +} + /*! \fn QImage QImage::copy(int x, int y, int width, int height) const \overload @@ -1133,12 +1145,9 @@ QImage QImage::copy(const QRect& r) const } else memcpy(image.bits(), bits(), d->nbytes); image.d->colortable = d->colortable; - image.d->dpmx = d->dpmx; - image.d->dpmy = d->dpmy; - image.d->devicePixelRatio = d->devicePixelRatio; image.d->offset = d->offset; image.d->has_alpha_clut = d->has_alpha_clut; - image.d->text = d->text; + copyMetadata(image.d, d); return image; } @@ -1224,12 +1233,9 @@ QImage QImage::copy(const QRect& r) const } } - image.d->dpmx = dotsPerMeterX(); - image.d->dpmy = dotsPerMeterY(); - image.d->devicePixelRatio = devicePixelRatio(); + copyMetadata(image.d, d); image.d->offset = offset(); image.d->has_alpha_clut = d->has_alpha_clut; - image.d->text = d->text; return image; } @@ -1349,7 +1355,11 @@ int QImage::colorCount() const \sa colorTable(), setColor(), {QImage#Image Transformations}{Image Transformations} */ +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +void QImage::setColorTable(const QVector<QRgb> &colors) +#else void QImage::setColorTable(const QVector<QRgb> colors) +#endif { if (!d) return; @@ -1359,7 +1369,11 @@ void QImage::setColorTable(const QVector<QRgb> colors) if (!d) return; +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + d->colortable = colors; +#else d->colortable = qMove(const_cast<QVector<QRgb>&>(colors)); +#endif d->has_alpha_clut = false; for (int i = 0; i < d->colortable.size(); ++i) { if (qAlpha(d->colortable.at(i)) != 255) { @@ -1423,6 +1437,10 @@ void QImage::setDevicePixelRatio(qreal scaleFactor) { if (!d) return; + + if (scaleFactor == d->devicePixelRatio) + return; + detach(); d->devicePixelRatio = scaleFactor; } @@ -1968,19 +1986,19 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl QIMAGE_SANITYCHECK_MEMORY(image); - image.setDotsPerMeterY(dotsPerMeterY()); - image.setDotsPerMeterX(dotsPerMeterX()); - image.setDevicePixelRatio(devicePixelRatio()); - - image.d->text = d->text; + image.d->offset = offset(); + copyMetadata(image.d, d); converter(image.d, d, flags); return image; } - // Convert indexed formats over ARGB32 to the final format. - Q_ASSERT(format != QImage::Format_ARGB32); - Q_ASSERT(d->format != QImage::Format_ARGB32); + // Convert indexed formats over ARGB32 or RGB32 to the final format. + Q_ASSERT(format != QImage::Format_ARGB32 && format != QImage::Format_RGB32); + Q_ASSERT(d->format != QImage::Format_ARGB32 && d->format != QImage::Format_RGB32); + + if (!hasAlphaChannel()) + return convertToFormat(Format_RGB32, flags).convertToFormat(format, flags); return convertToFormat(Format_ARGB32, flags).convertToFormat(format, flags); } @@ -2039,7 +2057,7 @@ static QImage convertWithPalette(const QImage &src, QImage::Format format, if (format == QImage::Format_Indexed8) { for (int y=0; y<h; ++y) { - QRgb *src_pixels = (QRgb *) src.scanLine(y); + const QRgb *src_pixels = (const QRgb *) src.scanLine(y); uchar *dest_pixels = (uchar *) dest.scanLine(y); for (int x=0; x<w; ++x) { int src_pixel = src_pixels[x]; @@ -2055,7 +2073,7 @@ static QImage convertWithPalette(const QImage &src, QImage::Format format, QVector<QRgb> table = clut; table.resize(2); for (int y=0; y<h; ++y) { - QRgb *src_pixels = (QRgb *) src.scanLine(y); + const QRgb *src_pixels = (const QRgb *) src.scanLine(y); for (int x=0; x<w; ++x) { int src_pixel = src_pixels[x]; int value = cache.value(src_pixel, -1); @@ -2097,9 +2115,9 @@ QImage QImage::convertToFormat(Format format, const QVector<QRgb> &colorTable, Q QImage image(d->width, d->height, format); QIMAGE_SANITYCHECK_MEMORY(image); - image.setDevicePixelRatio(devicePixelRatio()); - image.d->text = d->text; + image.d->offset = offset(); + copyMetadata(image.d, d); converter(image.d, d, flags); return image; @@ -2352,6 +2370,10 @@ bool QImage::allGray() const return false; } return true; + case Format_Alpha8: + return false; + case Format_Grayscale8: + return true; case Format_RGB32: case Format_ARGB32: case Format_ARGB32_Premultiplied: @@ -2405,9 +2427,9 @@ bool QImage::allGray() const /*! For 32-bit images, this function is equivalent to allGray(). - For 8-bpp images, this function returns \c true if color(i) is - QRgb(i, i, i) for all indexes of the color table; otherwise - returns \c false. + For color indexed images, this function returns \c true if + color(i) is QRgb(i, i, i) for all indexes of the color table; + otherwise returns \c false. \sa allGray(), {QImage#Image Formats}{Image Formats} */ @@ -2416,12 +2438,19 @@ bool QImage::isGrayscale() const if (!d) return false; + if (d->format == QImage::Format_Alpha8) + return false; + + if (d->format == QImage::Format_Grayscale8) + return true; + switch (depth()) { case 32: case 24: case 16: return allGray(); case 8: { + Q_ASSERT(d->format == QImage::Format_Indexed8); for (int i = 0; i < colorCount(); i++) if (d->colortable.at(i) != qRgb(i,i,i)) return false; @@ -2655,7 +2684,7 @@ QImage QImage::createHeuristicMask(bool clipTight) const return img32.createHeuristicMask(clipTight); } -#define PIX(x,y) (*((QRgb*)scanLine(y)+x) & 0x00ffffff) +#define PIX(x,y) (*((const QRgb*)scanLine(y)+x) & 0x00ffffff) int w = width(); int h = height(); @@ -2689,7 +2718,7 @@ QImage QImage::createHeuristicMask(bool clipTight) const ypp = ypc; ypc = ypn; ypn = (y == h-1) ? 0 : m.scanLine(y+1); - QRgb *p = (QRgb *)scanLine(y); + const QRgb *p = (const QRgb *)scanLine(y); for (x = 0; x < w; x++) { // slowness here - it's possible to do six of these tests // together in one go. oh well. @@ -2715,7 +2744,7 @@ QImage QImage::createHeuristicMask(bool clipTight) const ypp = ypc; ypc = ypn; ypn = (y == h-1) ? 0 : m.scanLine(y+1); - QRgb *p = (QRgb *)scanLine(y); + const QRgb *p = (const QRgb *)scanLine(y); for (x = 0; x < w; x++) { if ((*p & 0x00ffffff) != background) { if (x > 0) @@ -2759,7 +2788,7 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const if (depth() == 32) { for (int h = 0; h < d->height; h++) { - const uint *sl = (uint *) scanLine(h); + const uint *sl = (const uint *) scanLine(h); for (int w = 0; w < d->width; w++) { if (sl[w] == color) *(s + (w >> 3)) |= (1 << (w & 7)); @@ -2799,14 +2828,22 @@ template<class T> inline void do_mirror_data(QImageData *dst, QImageData *src, if (dst == src) { // When mirroring in-place, stop in the middle for one of the directions, since we // are swapping the bytes instead of merely copying. - const int srcXEnd = dstX0 ? w / 2 : w; - const int srcYEnd = !dstX0 && dstY0 ? h / 2 : h; + const int srcXEnd = (dstX0 && !dstY0) ? w / 2 : w; + const int srcYEnd = dstY0 ? h / 2 : h; for (int srcY = 0, dstY = dstY0; srcY < srcYEnd; ++srcY, dstY += dstYIncr) { T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line); T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line); for (int srcX = 0, dstX = dstX0; srcX < srcXEnd; ++srcX, dstX += dstXIncr) std::swap(srcPtr[srcX], dstPtr[dstX]); } + // If mirroring both ways, the middle line needs to be mirrored horizontally only. + if (dstX0 && dstY0 && (h & 1)) { + int srcY = h / 2; + int srcXEnd2 = w / 2; + T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line); + for (int srcX = 0, dstX = dstX0; srcX < srcXEnd2; ++srcX, dstX += dstXIncr) + std::swap(srcPtr[srcX], srcPtr[dstX]); + } } else { for (int srcY = 0, dstY = dstY0; srcY < h; ++srcY, dstY += dstYIncr) { T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line); @@ -2918,9 +2955,7 @@ QImage QImage::mirrored_helper(bool horizontal, bool vertical) const result.d->colortable = d->colortable; result.d->has_alpha_clut = d->has_alpha_clut; - result.d->devicePixelRatio = d->devicePixelRatio; - result.d->dpmx = d->dpmx; - result.d->dpmy = d->dpmy; + copyMetadata(result.d, d); do_mirror(result.d, d, horizontal, vertical); @@ -2998,6 +3033,9 @@ QImage QImage::rgbSwapped_helper() const case NImageFormats: Q_ASSERT(false); break; + case Format_Alpha8: + case Format_Grayscale8: + return *this; case Format_Mono: case Format_MonoLSB: case Format_Indexed8: @@ -3066,6 +3104,7 @@ QImage QImage::rgbSwapped_helper() const rgbSwapped_generic(d->width, d->height, this, &res, &qPixelLayouts[d->format]); break; } + copyMetadata(res.d, d); return res; } @@ -3084,6 +3123,9 @@ void QImage::rgbSwapped_inplace() case NImageFormats: Q_ASSERT(false); break; + case Format_Alpha8: + case Format_Grayscale8: + return; case Format_Mono: case Format_MonoLSB: case Format_Indexed8: @@ -3846,7 +3888,7 @@ bool qt_xForm_helper(const QTransform &trueMat, int xoffset, int type, int depth case 16: // 16 bpp transform while (dptr < maxp) { if (trigx < maxws && trigy < maxhs) - *((ushort*)dptr) = *((ushort *)(sptr+sbpl*(trigy>>12) + + *((ushort*)dptr) = *((const ushort *)(sptr+sbpl*(trigy>>12) + ((trigx>>12)<<1))); trigx += m11; trigy += m12; @@ -3872,7 +3914,7 @@ bool qt_xForm_helper(const QTransform &trueMat, int xoffset, int type, int depth case 32: // 32 bpp transform while (dptr < maxp) { if (trigx < maxws && trigy < maxhs) - *((uint*)dptr) = *((uint *)(sptr+sbpl*(trigy>>12) + + *((uint*)dptr) = *((const uint *)(sptr+sbpl*(trigy>>12) + ((trigx>>12)<<2))); trigx += m11; trigy += m12; @@ -4017,9 +4059,9 @@ void QImage::setAlphaChannel(const QImage &alphaChannel) return; // Slight optimization since alphachannels are returned as 8-bit grays. - if (alphaChannel.d->depth == 8 && alphaChannel.isGrayscale()) { + if (alphaChannel.format() == QImage::Format_Alpha8 ||( alphaChannel.d->depth == 8 && alphaChannel.isGrayscale())) { const uchar *src_data = alphaChannel.d->data; - const uchar *dest_data = d->data; + uchar *dest_data = d->data; for (int y=0; y<h; ++y) { const uchar *src = src_data; QRgb *dest = (QRgb *)dest_data; @@ -4040,7 +4082,7 @@ void QImage::setAlphaChannel(const QImage &alphaChannel) } else { const QImage sourceImage = alphaChannel.convertToFormat(QImage::Format_RGB32); const uchar *src_data = sourceImage.d->data; - const uchar *dest_data = d->data; + uchar *dest_data = d->data; for (int y=0; y<h; ++y) { const QRgb *src = (const QRgb *) src_data; QRgb *dest = (QRgb *) dest_data; @@ -4075,9 +4117,13 @@ void QImage::setAlphaChannel(const QImage &alphaChannel) Most usecases for this function can be replaced with QPainter and using composition modes. + Note this returns a color-indexed image if you want the alpha channel in + the alpha8 format instead use convertToFormat(Format_Alpha8) on the source + image. + \warning This is an expensive function. - \sa setAlphaChannel(), hasAlphaChannel(), + \sa setAlphaChannel(), hasAlphaChannel(), convertToFormat(), {QPixmap#Pixmap Information}{Pixmap}, {QImage#Image Transformations}{Image Transformations} */ @@ -4087,6 +4133,9 @@ QImage QImage::alphaChannel() const if (!d) return QImage(); + if (d->format == QImage::Format_Alpha8) + return *this; + int w = d->width; int h = d->height; @@ -4207,23 +4256,27 @@ int QImage::bitPlaneCount() const return bpc; } -static QImage smoothScaled(const QImage &source, int w, int h) { - QImage src = source; - bool canSkipConversion = (src.format() == QImage::Format_RGB32 || src.format() == QImage::Format_ARGB32_Premultiplied); +QImage QImage::smoothScaled(int w, int h) const { + QImage src = *this; + switch (src.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - canSkipConversion = canSkipConversion || (src.format() == QImage::Format_RGBX8888 || src.format() == QImage::Format_RGBA8888_Premultiplied); + case QImage::Format_RGBX8888: #endif - if (!canSkipConversion) { + case QImage::Format_RGBA8888_Premultiplied: + break; + default: if (src.hasAlphaChannel()) src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied); else src = src.convertToFormat(QImage::Format_RGB32); } - - return qSmoothScaleImage(src, w, h); + src = qSmoothScaleImage(src, w, h); + copyMetadata(src.d, d); + return src; } - static QImage rotated90(const QImage &image) { QImage out(image.height(), image.width(), image.format()); if (image.colorCount() > 0) @@ -4264,6 +4317,8 @@ static QImage rotated90(const QImage &image) { reinterpret_cast<quint16*>(out.bits()), out.bytesPerLine()); break; + case QImage::Format_Alpha8: + case QImage::Format_Grayscale8: case QImage::Format_Indexed8: qt_memrotate270(reinterpret_cast<const quint8*>(image.bits()), w, h, image.bytesPerLine(), @@ -4330,6 +4385,8 @@ static QImage rotated270(const QImage &image) { reinterpret_cast<quint16*>(out.bits()), out.bytesPerLine()); break; + case QImage::Format_Alpha8: + case QImage::Format_Grayscale8: case QImage::Format_Indexed8: qt_memrotate90(reinterpret_cast<const quint8*>(image.bits()), w, h, image.bytesPerLine(), @@ -4420,13 +4477,13 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode // Make use of the optimized algorithm when we're scaling if (scale_xform && mode == Qt::SmoothTransformation) { if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip - return smoothScaled(mirrored(true, true), wd, hd); + return smoothScaled(wd, hd).mirrored(true, true); } else if (mat.m11() < 0.0F) { // horizontal flip - return smoothScaled(mirrored(true, false), wd, hd); + return smoothScaled(wd, hd).mirrored(true, false); } else if (mat.m22() < 0.0F) { // vertical flip - return smoothScaled(mirrored(false, true), wd, hd); + return smoothScaled(wd, hd).mirrored(false, true); } else { // no flipping - return smoothScaled(*this, wd, hd); + return smoothScaled(wd, hd); } } @@ -4478,27 +4535,17 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode dImage.d->has_alpha_clut = d->has_alpha_clut | complex_xform; } - dImage.d->dpmx = dotsPerMeterX(); - dImage.d->dpmy = dotsPerMeterY(); - - switch (bpp) { - // initizialize the data - case 8: - if (dImage.d->colortable.size() < 256) { - // colors are left in the color table, so pick that one as transparent - dImage.d->colortable.append(0x0); - memset(dImage.bits(), dImage.d->colortable.size() - 1, dImage.byteCount()); - } else { - memset(dImage.bits(), 0, dImage.byteCount()); - } - break; - case 1: - case 16: - case 24: - case 32: - memset(dImage.bits(), 0x00, dImage.byteCount()); - break; - } + // initizialize the data + if (d->format == QImage::Format_Indexed8) { + if (dImage.d->colortable.size() < 256) { + // colors are left in the color table, so pick that one as transparent + dImage.d->colortable.append(0x0); + memset(dImage.bits(), dImage.d->colortable.size() - 1, dImage.byteCount()); + } else { + memset(dImage.bits(), 0, dImage.byteCount()); + } + } else + memset(dImage.bits(), 0x00, dImage.byteCount()); if (target_format >= QImage::Format_RGB32) { // Prevent QPainter from applying devicePixelRatio corrections @@ -4525,8 +4572,8 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode int dbpl = dImage.bytesPerLine(); qt_xForm_helper(mat, 0, type, bpp, dImage.bits(), dbpl, 0, hd, sptr, sbpl, ws, hs); } + copyMetadata(dImage.d, d); - dImage.d->devicePixelRatio = devicePixelRatio(); return dImage; } @@ -4568,11 +4615,12 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla if (ref.load() > 1 || ro_data) return false; - const InPlace_Image_Converter *const converterPtr = &qimage_inplace_converter_map[format][newFormat]; - InPlace_Image_Converter converter = *converterPtr; + InPlace_Image_Converter converter = qimage_inplace_converter_map[format][newFormat]; if (converter) return converter(this, flags); - else if (format > QImage::Format_Indexed8 && newFormat > QImage::Format_Indexed8) + else if (format > QImage::Format_Indexed8 && newFormat > QImage::Format_Indexed8 && !qimage_converter_map[format][newFormat]) + // Convert inplace generic, but only if there are no direct converters, + // any direct ones are probably better even if not inplace. return convert_generic_inplace(this, newFormat, flags); else return false; @@ -4942,6 +4990,32 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = { /*PREMULTIPLIED*/ QPixelFormat::Premultiplied, /*INTERPRETATION*/ QPixelFormat::UnsignedInteger, /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_Alpha8: + QPixelFormat(QPixelFormat::Alpha, + /*First*/ 0, + /*SECOND*/ 0, + /*THIRD*/ 0, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 8, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtBeginning, + /*PREMULTIPLIED*/ QPixelFormat::Premultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedByte, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_Grayscale8: + QPixelFormat(QPixelFormat::Grayscale, + /*GRAY*/ 8, + /*SECOND*/ 0, + /*THIRD*/ 0, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 0, + /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtBeginning, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedByte, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), }; Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats); |