diff options
Diffstat (limited to 'src/gui/image')
28 files changed, 1719 insertions, 949 deletions
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 76aba944b2..70fccbc378 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -9,6 +9,7 @@ HEADERS += \ image/qimage_p.h \ image/qimageiohandler.h \ image/qimagereader.h \ + image/qimagereaderwriterhelpers_p.h \ image/qimagewriter.h \ image/qpaintengine_pic_p.h \ image/qpicture.h \ @@ -33,6 +34,7 @@ SOURCES += \ image/qimage_conversions.cpp \ image/qimageiohandler.cpp \ image/qimagereader.cpp \ + image/qimagereaderwriterhelpers.cpp \ image/qimagewriter.cpp \ image/qpaintengine_pic.cpp \ image/qpicture.cpp \ @@ -80,10 +82,7 @@ qtConfig(png) { } # SIMD -SSE2_SOURCES += image/qimage_sse2.cpp SSSE3_SOURCES += image/qimage_ssse3.cpp -SSE4_1_SOURCES += image/qimage_sse4.cpp -AVX2_SOURCES += image/qimage_avx2.cpp NEON_SOURCES += image/qimage_neon.cpp MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp index e8405a6d11..2453242fa8 100644 --- a/src/gui/image/qbitmap.cpp +++ b/src/gui/image/qbitmap.cpp @@ -189,9 +189,7 @@ QBitmap &QBitmap::operator=(const QPixmap &pixmap) } else if (pixmap.depth() == 1) { // 1-bit pixmap QPixmap::operator=(pixmap); // shallow assignment } else { // n-bit depth pixmap - QImage image; - image = pixmap.toImage(); // convert pixmap to image - *this = fromImage(image); // will dither image + *this = fromImage(pixmap.toImage()); // will dither image } return *this; } @@ -223,6 +221,24 @@ QBitmap::operator QVariant() const return QVariant(QVariant::Bitmap, this); } +static QBitmap makeBitmap(QImage &&image, Qt::ImageConversionFlags flags) +{ + // make sure image.color(0) == Qt::color0 (white) + // and image.color(1) == Qt::color1 (black) + const QRgb c0 = QColor(Qt::black).rgb(); + const QRgb c1 = QColor(Qt::white).rgb(); + if (image.color(0) == c0 && image.color(1) == c1) { + image.invertPixels(); + image.setColor(0, c1); + image.setColor(1, c0); + } + + QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::BitmapType)); + + data->fromImageInPlace(image, flags | Qt::MonoOnly); + return QPixmap(data.take()); +} + /*! Returns a copy of the given \a image converted to a bitmap using the specified image conversion \a flags. @@ -234,22 +250,24 @@ QBitmap QBitmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags) if (image.isNull()) return QBitmap(); - QImage img = image.convertToFormat(QImage::Format_MonoLSB, flags); + return makeBitmap(image.convertToFormat(QImage::Format_MonoLSB, flags), flags); +} - // make sure image.color(0) == Qt::color0 (white) - // and image.color(1) == Qt::color1 (black) - const QRgb c0 = QColor(Qt::black).rgb(); - const QRgb c1 = QColor(Qt::white).rgb(); - if (img.color(0) == c0 && img.color(1) == c1) { - img.invertPixels(); - img.setColor(0, c1); - img.setColor(1, c0); - } +/*! + \since 5.12 + \overload - QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::BitmapType)); + Returns a copy of the given \a image converted to a bitmap using + the specified image conversion \a flags. - data->fromImage(img, flags | Qt::MonoOnly); - return QPixmap(data.take()); + \sa fromData() +*/ +QBitmap QBitmap::fromImage(QImage &&image, Qt::ImageConversionFlags flags) +{ + if (image.isNull()) + return QBitmap(); + + return makeBitmap(std::move(image).convertToFormat(QImage::Format_MonoLSB, flags), flags); } /*! @@ -277,7 +295,7 @@ QBitmap QBitmap::fromData(const QSize &size, const uchar *bits, QImage::Format m int bytesPerLine = (size.width() + 7) / 8; for (int y = 0; y < size.height(); ++y) memcpy(image.scanLine(y), bits + bytesPerLine * y, bytesPerLine); - return QBitmap::fromImage(image); + return QBitmap::fromImage(std::move(image)); } /*! diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h index 6a8c8b3457..188064fccf 100644 --- a/src/gui/image/qbitmap.h +++ b/src/gui/image/qbitmap.h @@ -72,6 +72,7 @@ public: inline void clear() { fill(Qt::color0); } static QBitmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor); + static QBitmap fromImage(QImage &&image, Qt::ImageConversionFlags flags = Qt::AutoColor); static QBitmap fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat = QImage::Format_MonoLSB); diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 32fa9e75ac..14a0248600 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -1194,7 +1194,7 @@ void QIcon::setFallbackSearchPaths(const QStringList &paths) The \a name should correspond to a directory name in the themeSearchPath() containing an index.theme - file describing it's contents. + file describing its contents. \sa themeSearchPaths(), themeName() */ @@ -1220,6 +1220,37 @@ QString QIcon::themeName() } /*! + \since 5.12 + + Returns the name of the fallback icon theme. + + On X11, if not set, the fallback icon theme depends on your desktop + settings. On other platforms it is not set by default. + + \sa setFallbackThemeName(), themeName() +*/ +QString QIcon::fallbackThemeName() +{ + return QIconLoader::instance()->fallbackThemeName(); +} + +/*! + \since 5.12 + + Sets the fallback icon theme to \a name. + + The \a name should correspond to a directory name in the + themeSearchPath() containing an index.theme + file describing its contents. + + \sa fallbackThemeName(), themeSearchPaths(), themeName() +*/ +void QIcon::setFallbackThemeName(const QString &name) +{ + QIconLoader::instance()->setFallbackThemeName(name); +} + +/*! \since 4.6 Returns the QIcon corresponding to \a name in the current diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h index 653ba6fda4..6a4fc8927a 100644 --- a/src/gui/image/qicon.h +++ b/src/gui/image/qicon.h @@ -124,6 +124,9 @@ public: static QString themeName(); static void setThemeName(const QString &path); + static QString fallbackThemeName(); + static void setFallbackThemeName(const QString &name); + Q_DUMMY_COMPARISON_OPERATOR(QIcon) private: diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 1ea4f1340b..228de3adc3 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance) /* Theme to use in last resort, if the theme does not have the icon, neither the parents */ -static QString fallbackTheme() +static QString systemFallbackThemeName() { if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName); @@ -117,7 +117,7 @@ void QIconLoader::ensureInitialized() m_systemTheme = systemThemeName(); if (m_systemTheme.isEmpty()) - m_systemTheme = fallbackTheme(); + m_systemTheme = systemFallbackThemeName(); if (qt_iconEngineFactoryLoader()->keyMap().key(QLatin1String("svg"), -1) != -1) m_supportsSvg = true; } @@ -137,7 +137,7 @@ void QIconLoader::updateSystemTheme() if (m_userTheme.isEmpty()) { QString theme = systemThemeName(); if (theme.isEmpty()) - theme = fallbackTheme(); + theme = fallbackThemeName(); if (theme != m_systemTheme) { m_systemTheme = theme; invalidateKey(); @@ -151,6 +151,16 @@ void QIconLoader::setThemeName(const QString &themeName) invalidateKey(); } +QString QIconLoader::fallbackThemeName() const +{ + return m_userFallbackTheme.isEmpty() ? systemFallbackThemeName() : m_userFallbackTheme; +} + +void QIconLoader::setFallbackThemeName(const QString &themeName) +{ + m_userFallbackTheme = themeName; +} + void QIconLoader::setThemeSearchPath(const QStringList &searchPaths) { m_iconDirs = searchPaths; @@ -388,7 +398,7 @@ QIconTheme::QIconTheme(const QString &themeName) // Ensure a default platform fallback for all themes if (m_parents.isEmpty()) { - const QString fallback = fallbackTheme(); + const QString fallback = QIconLoader::instance()->fallbackThemeName(); if (!fallback.isEmpty()) m_parents.append(fallback); } @@ -414,7 +424,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, if (!theme.isValid()) { theme = QIconTheme(themeName); if (!theme.isValid()) - theme = QIconTheme(fallbackTheme()); + theme = QIconTheme(fallbackThemeName()); } const QStringList contentDirs = theme.contentDirs(); diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index 746e871fb1..fac18b5d79 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -177,6 +177,8 @@ public: QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; } void setThemeName(const QString &themeName); + QString fallbackThemeName() const; + void setFallbackThemeName(const QString &themeName); QIconTheme theme() { return themeList.value(themeName()); } void setThemeSearchPath(const QStringList &searchPaths); QStringList themeSearchPaths() const; @@ -200,6 +202,7 @@ private: bool m_initialized; mutable QString m_userTheme; + mutable QString m_userFallbackTheme; mutable QString m_systemTheme; mutable QStringList m_iconDirs; mutable QHash <QString, QIconTheme> themeList; diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 7fcae12cbd..8a4c6b7fda 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -277,6 +277,16 @@ bool QImageData::checkForAlphaPixels() const bits += bytes_per_line; } } break; + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: { + uchar *bits = data; + for (int y=0; y<height && !has_alpha_pixels; ++y) { + for (int x=0; x<width; ++x) { + has_alpha_pixels |= !(((QRgba64 *)bits)[x].isOpaque()); + } + bits += bytes_per_line; + } + } break; case QImage::Format_RGB32: case QImage::Format_RGB16: @@ -288,6 +298,7 @@ bool QImageData::checkForAlphaPixels() const case QImage::Format_BGR30: case QImage::Format_RGB30: case QImage::Format_Grayscale8: + case QImage::Format_RGBX64: break; case QImage::Format_Invalid: case QImage::NImageFormats: @@ -651,11 +662,7 @@ bool QImageData::checkForAlphaPixels() const /*! \enum QImage::Format - 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_Alpha8 and Format_Grayscale8 - were added in Qt 5.5. + The following image formats are available in Qt. See the notes after the table. \value Format_Invalid The image is invalid. @@ -699,29 +706,32 @@ bool QImageData::checkForAlphaPixels() const \value Format_ARGB4444_Premultiplied The image is stored using a premultiplied 16-bit ARGB format (4-4-4-4). \value Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8). - This is the same as the Format_RGBA8888 except alpha must always be 255. + This is the same as the Format_RGBA8888 except alpha must always be 255. (added in Qt 5.2) \value Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8). Unlike ARGB32 this is a byte-ordered format, which means the 32bit encoding differs between big endian and little endian architectures, being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors - is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. + is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. (added in Qt 5.2) \value Format_RGBA8888_Premultiplied The image is stored using a - premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). - \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). - \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. + premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). (added in Qt 5.2) + \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). (added in Qt 5.4) + \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). (added in Qt 5.4) + \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). (added in Qt 5.4) + \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). (added in Qt 5.4) + \value Format_Alpha8 The image is stored using an 8-bit alpha only format. (added in Qt 5.5) + \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. (added in Qt 5.5) + \value Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16). + This is the same as the Format_RGBX64 except alpha must always be 65535. (added in Qt 5.12) + \value Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12) + \value Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered + RGBA format (16-16-16-16). (added in Qt 5.12) \note Drawing into a QImage with QImage::Format_Indexed8 is not supported. - \note Do not render into ARGB32 images using QPainter. Using - QImage::Format_ARGB32_Premultiplied is significantly faster. - - \note Formats with more than 8 bit per color channel will only be processed by the raster engine using 8 bit - per color. + \note Avoid most rendering directly to most of these formats using QPainter. Rendering + is best optimized to the \c Format_RGB32 and \c Format_ARGB32_Premultiplied formats, and secondarily for rendering to the + \c Format_RGB16, \c Format_RGBX8888, \c Format_RGBA8888_Premultiplied, \c Format_RGBX64 and \c Format_RGBA64_Premultiplied formats \sa format(), convertToFormat() */ @@ -1708,6 +1718,10 @@ void QImage::fill(uint pixel) qt_rectfill<quint24>(reinterpret_cast<quint24*>(d->data), pixel, 0, 0, d->width, d->height, d->bytes_per_line); return; + } else if (d->depth == 64) { + qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), QRgba64::fromArgb32(pixel), + 0, 0, d->width, d->height, d->bytes_per_line); + return; } if (d->format == Format_RGB32) @@ -1815,6 +1829,19 @@ void QImage::fill(const QColor &color) else fill((uint) 0); break; + case QImage::Format_RGBX64: { + QRgba64 c = color.rgba64(); + c.setAlpha(65535); + qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), c, + 0, 0, d->width, d->height, d->bytes_per_line); + break; + + } + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), color.rgba64(), + 0, 0, d->width, d->height, d->bytes_per_line); + break; default: { QPainter p(this); p.setCompositionMode(QPainter::CompositionMode_Source); @@ -1838,7 +1865,8 @@ void QImage::fill(const QColor &color) changed. If the image has a premultiplied alpha channel, the image is first - converted to ARGB32 to be inverted and then converted back. + converted to an unpremultiplied image format to be inverted and + then converted back. \sa {QImage#Image Transformations}{Image Transformations} */ @@ -1857,8 +1885,13 @@ void QImage::invertPixels(InvertMode mode) QImage::Format originalFormat = d->format; // Inverting premultiplied pixels would produce invalid image data. if (hasAlphaChannel() && qPixelLayouts[d->format].premultiplied) { - if (!d->convertInPlace(QImage::Format_ARGB32, 0)) - *this = convertToFormat(QImage::Format_ARGB32); + if (depth() > 32) { + if (!d->convertInPlace(QImage::Format_RGBA64, 0)) + *this = convertToFormat(QImage::Format_RGBA64); + } else { + if (!d->convertInPlace(QImage::Format_ARGB32, 0)) + *this = convertToFormat(QImage::Format_ARGB32); + } } if (depth() < 32) { @@ -1871,6 +1904,20 @@ void QImage::invertPixels(InvertMode mode) *sl++ ^= 0xff; sl += pad; } + } + else if (depth() == 64) { + quint16 *p = (quint16*)d->data; + quint16 *end = (quint16*)(d->data + d->nbytes); + quint16 xorbits = 0xffff; + while (p < end) { + *p++ ^= xorbits; + *p++ ^= xorbits; + *p++ ^= xorbits; + if (mode == InvertRgba) + *p++ ^= xorbits; + else + p++; + } } else { quint32 *p = (quint32*)d->data; quint32 *end = (quint32*)(d->data + d->nbytes); @@ -1987,6 +2034,26 @@ QImage::Format QImage::format() const \sa {Image Formats} */ +static bool highColorPrecision(QImage::Format format) +{ + // Formats with higher color precision than ARGB32_Premultiplied. + switch (format) { + case QImage::Format_ARGB32: + case QImage::Format_RGBA8888: + case QImage::Format_BGR30: + case QImage::Format_RGB30: + case QImage::Format_A2BGR30_Premultiplied: + case QImage::Format_A2RGB30_Premultiplied: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + return true; + default: + break; + } + return false; +} + /*! \internal */ @@ -1999,8 +2066,18 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl return QImage(); Image_Converter converter = qimage_converter_map[d->format][format]; - if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) - converter = convert_generic; + if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) { + if (highColorPrecision(format) && highColorPrecision(d->format)) { + // Convert over RGBA64_Premultiplied + if (format == QImage::Format_RGBA64_Premultiplied) + converter = convert_generic_to_rgb64; + else { + Q_ASSERT(d->format != QImage::Format_RGBA64_Premultiplied); + return convertToFormat(Format_RGBA64_Premultiplied, flags).convertToFormat(format, flags); + } + } else + converter = convert_generic; + } if (converter) { QImage image(d->width, d->height, format); @@ -2298,13 +2375,16 @@ QRgb QImage::pixel(int x, int y) const return qConvertA2rgb30ToArgb32<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x]); case Format_RGB16: return qConvertRgb16To32(reinterpret_cast<const quint16 *>(s)[x]); + case Format_RGBX64: + case Format_RGBA64: // Match ARGB32 behavior. + case Format_RGBA64_Premultiplied: + return reinterpret_cast<const QRgba64 *>(s)[x].toArgb32(); default: break; } const QPixelLayout *layout = &qPixelLayouts[d->format]; uint result; - const uint *ptr = qFetchPixels[layout->bpp](&result, s, x, 1); - return *layout->convertToARGB32PM(&result, ptr, 1, 0, 0); + return *layout->fetchToARGB32PM(&result, s, x, 1, nullptr, nullptr); } /*! @@ -2405,9 +2485,7 @@ void QImage::setPixel(int x, int y, uint index_or_rgb) } const QPixelLayout *layout = &qPixelLayouts[d->format]; - uint result; - const uint *ptr = layout->convertFromARGB32PM(&result, &index_or_rgb, 1, 0, 0); - qStorePixels[layout->bpp](s, ptr, x, 1); + layout->storeFromARGB32PM(s, &index_or_rgb, x, 1, nullptr, nullptr); } /*! @@ -2450,6 +2528,11 @@ QColor QImage::pixelColor(int x, int y) const case Format_A2RGB30_Premultiplied: c = qConvertA2rgb30ToRgb64<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x]); break; + case Format_RGBX64: + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + c = reinterpret_cast<const QRgba64 *>(s)[x]; + break; default: c = QRgba64::fromArgb32(pixel(x, y)); break; @@ -2520,6 +2603,14 @@ void QImage::setPixelColor(int x, int y, const QColor &color) case Format_A2RGB30_Premultiplied: ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(c); return; + case Format_RGBX64: + ((QRgba64 *)s)[x] = color.rgba64(); + ((QRgba64 *)s)[x].setAlpha(65535); + return; + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + ((QRgba64 *)s)[x] = color.rgba64(); + return; default: setPixel(x, y, c.toArgb32()); return; @@ -2582,17 +2673,15 @@ bool QImage::allGray() const break; } - const int buffer_size = 2048; - uint buffer[buffer_size]; + uint buffer[BufferSize]; const QPixelLayout *layout = &qPixelLayouts[d->format]; - FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; + const auto fetch = layout->fetchToARGB32PM; for (int j = 0; j < d->height; ++j) { const uchar *b = constScanLine(j); int x = 0; while (x < d->width) { - int l = qMin(d->width - x, buffer_size); - const uint *ptr = fetch(buffer, b, x, l); - ptr = layout->convertToARGB32PM(buffer, ptr, l, 0, 0); + int l = qMin(d->width - x, BufferSize); + const uint *ptr = fetch(buffer, b, x, l, nullptr, nullptr); for (int i = 0; i < l; ++i) { if (!qIsGray(ptr[i])) return false; @@ -2780,6 +2869,13 @@ QMatrix QImage::trueMatrix(const QMatrix &matrix, int w, int h) Returns a copy of the image that is transformed using the given transformation \a matrix and transformation \a mode. + The returned image will normally have the same {Image Formats}{format} as + the original image. However, a complex transformation may result in an + image where not all pixels are covered by the transformed pixels of the + original image. In such cases, those background pixels will be assigned a + transparent color value, and the transformed image will be given a format + with an alpha channel, even if the orginal image did not have that. + The transformation \a matrix is internally adjusted to compensate for unwanted translation; i.e. the image produced is the smallest image that contains all the transformed points of the original @@ -3098,6 +3194,9 @@ inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool ve } switch (depth) { + case 64: + do_mirror_data<quint64>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h); + break; case 32: do_mirror_data<quint32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h); break; @@ -3210,33 +3309,18 @@ void QImage::mirrored_inplace(bool horizontal, bool vertical) inline void rgbSwapped_generic(int width, int height, const QImage *src, QImage *dst, const QPixelLayout* layout) { - Q_ASSERT(layout->redWidth == layout->blueWidth); - FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; - StorePixelsFunc store = qStorePixels[layout->bpp]; - - const uint redBlueMask = (1 << layout->redWidth) - 1; - const uint alphaGreenMask = (((1 << layout->alphaWidth) - 1) << layout->alphaShift) - | (((1 << layout->greenWidth) - 1) << layout->greenShift); + const RbSwapFunc func = layout->rbSwap; + if (!func) { + qWarning("Trying to rb-swap an image format where it doesn't make sense"); + if (src != dst) + *dst = *src; + return; + } - const int buffer_size = 2048; - uint buffer[buffer_size]; for (int i = 0; i < height; ++i) { uchar *q = dst->scanLine(i); const uchar *p = src->constScanLine(i); - int x = 0; - while (x < width) { - int l = qMin(width - x, buffer_size); - const uint *ptr = fetch(buffer, p, x, l); - for (int j = 0; j < l; ++j) { - uint red = (ptr[j] >> layout->redShift) & redBlueMask; - uint blue = (ptr[j] >> layout->blueShift) & redBlueMask; - buffer[j] = (ptr[j] & alphaGreenMask) - | (red << layout->blueShift) - | (blue << layout->redShift); - } - store(q, buffer, x, l); - x += l; - } + func(q, p, width); } } @@ -3321,18 +3405,18 @@ QImage QImage::rgbSwapped_helper() const } } break; - case Format_BGR30: - case Format_A2BGR30_Premultiplied: - case Format_RGB30: - case Format_A2RGB30_Premultiplied: + case Format_RGBX64: + case Format_RGBA64: + case Format_RGBA64_Premultiplied: res = QImage(d->width, d->height, d->format); QIMAGE_SANITYCHECK_MEMORY(res); for (int i = 0; i < d->height; i++) { - uint *q = (uint*)res.scanLine(i); - const uint *p = (const uint*)constScanLine(i); - const uint *end = p + d->width; + QRgba64 *q = reinterpret_cast<QRgba64 *>(res.scanLine(i)); + const QRgba64 *p = reinterpret_cast<const QRgba64 *>(constScanLine(i)); + const QRgba64 *end = p + d->width; while (p < end) { - *q = qRgbSwapRgb30(*p); + QRgba64 c = *p; + *q = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha()); p++; q++; } @@ -3430,6 +3514,19 @@ void QImage::rgbSwapped_inplace() } } break; + case Format_RGBX64: + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + for (int i = 0; i < d->height; i++) { + QRgba64 *p = reinterpret_cast<QRgba64 *>(scanLine(i)); + QRgba64 *end = p + d->width; + while (p < end) { + QRgba64 c = *p; + *p = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha()); + p++; + } + } + break; default: rgbSwapped_generic(d->width, d->height, this, this, &qPixelLayouts[d->format]); break; @@ -3457,8 +3554,7 @@ void QImage::rgbSwapped_inplace() bool QImage::load(const QString &fileName, const char* format) { - QImage image = QImageReader(fileName, format).read(); - operator=(image); + *this = QImageReader(fileName, format).read(); return !isNull(); } @@ -3471,8 +3567,7 @@ bool QImage::load(const QString &fileName, const char* format) bool QImage::load(QIODevice* device, const char* format) { - QImage image = QImageReader(device, format).read(); - operator=(image); + *this = QImageReader(device, format).read(); return !isNull(); } @@ -3492,8 +3587,7 @@ bool QImage::load(QIODevice* device, const char* format) bool QImage::loadFromData(const uchar *data, int len, const char *format) { - QImage image = fromData(data, len, format); - operator=(image); + *this = fromData(data, len, format); return !isNull(); } @@ -4506,6 +4600,9 @@ int QImage::bitPlaneCount() const case QImage::Format_RGB444: bpc = 12; break; + case QImage::Format_RGBX64: + bpc = 48; + break; default: bpc = qt_depthForFormat(d->format); break; @@ -4526,6 +4623,11 @@ QImage QImage::smoothScaled(int w, int h) const { case QImage::Format_RGBX8888: #endif case QImage::Format_RGBA8888_Premultiplied: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64_Premultiplied: + break; + case QImage::Format_RGBA64: + src = src.convertToFormat(QImage::Format_RGBA64_Premultiplied); break; default: if (src.hasAlphaChannel()) @@ -4610,6 +4712,13 @@ static QImage rotated270(const QImage &image) Returns a copy of the image that is transformed using the given transformation \a matrix and transformation \a mode. + The returned image will normally have the same {Image Formats}{format} as + the original image. However, a complex transformation may result in an + image where not all pixels are covered by the transformed pixels of the + original image. In such cases, those background pixels will be assigned a + transparent color value, and the transformed image will be given a format + with an alpha channel, even if the orginal image did not have that. + The transformation \a matrix is internally adjusted to compensate for unwanted translation; i.e. the image produced is the smallest image that contains all the transformed points of the original @@ -5197,6 +5306,45 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = { /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, /*INTERPRETATION*/ QPixelFormat::UnsignedByte, /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBX64: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 16, + /*GREEN*/ 16, + /*BLUE*/ 16, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 16, + /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA64: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 16, + /*GREEN*/ 16, + /*BLUE*/ 16, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 16, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA64_Premultiplied: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 16, + /*GREEN*/ 16, + /*BLUE*/ 16, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 16, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::Premultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), }; Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats); diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 9b76b62f24..7764c19452 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -96,6 +96,7 @@ typedef void (*QImageCleanupFunction)(void*); class Q_GUI_EXPORT QImage : public QPaintDevice { + Q_GADGET public: enum InvertMode { InvertRgb, InvertRgba }; enum Format { @@ -124,6 +125,9 @@ public: Format_A2RGB30_Premultiplied, Format_Alpha8, Format_Grayscale8, + Format_RGBX64, + Format_RGBA64, + Format_RGBA64_Premultiplied, #if 0 // reserved for future use Format_Grayscale16, @@ -132,6 +136,7 @@ public: NImageFormats #endif }; + Q_ENUM(Format) QImage() Q_DECL_NOEXCEPT; QImage(const QSize &size, Format format); diff --git a/src/gui/image/qimage_avx2.cpp b/src/gui/image/qimage_avx2.cpp deleted file mode 100644 index 0519f17c5d..0000000000 --- a/src/gui/image/qimage_avx2.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include <qimage.h> -#include <private/qdrawhelper_p.h> -#include <private/qimage_p.h> -#include <private/qsimd_p.h> - -#ifdef QT_COMPILER_SUPPORTS_AVX2 - -QT_BEGIN_NAMESPACE - -void convert_ARGB_to_ARGB_PM_avx2(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888); - Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const uint *src_data = (uint *) src->data; - uint *dest_data = (uint *) dest->data; - for (int i = 0; i < src->height; ++i) { - qt_convertARGB32ToARGB32PM(dest_data, src_data, src->width); - src_data += src->bytes_per_line >> 2; - dest_data += dest->bytes_per_line >> 2; - } -} - -QT_END_NAMESPACE - -#endif // QT_COMPILER_SUPPORTS_AVX2 diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index d981c43711..e1f66dceee 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -118,26 +118,35 @@ void qGamma_correct_back_to_linear_cs(QImage *image) Internal routines for converting image depth. *****************************************************************************/ -// The drawhelper conversions from/to RGB32 are passthroughs which is not always correct for general image conversion. -static const uint *QT_FASTCALL convertRGB32FromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +// The drawhelper conversions from/to RGB32 are passthroughs which is not always correct for general image conversion +static void QT_FASTCALL storeRGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { + uint *d = reinterpret_cast<uint *>(dest) + index; for (int i = 0; i < count; ++i) - buffer[i] = 0xff000000 | qUnpremultiply(src[i]); - return buffer; + d[i] = 0xff000000 | qUnpremultiply(src[i]); +} + +static void QT_FASTCALL storeRGB32FromARGB32(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = reinterpret_cast<uint *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = 0xff000000 | src[i]; } -static const uint *QT_FASTCALL maskRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static const uint *QT_FASTCALL fetchRGB32ToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { + const uint *s = reinterpret_cast<const uint *>(src) + index; for (int i = 0; i < count; ++i) - buffer[i] = 0xff000000 |src[i]; + buffer[i] = 0xff000000 | s[i]; return buffer; } #ifdef QT_COMPILER_SUPPORTS_SSE4_1 -extern const uint *QT_FASTCALL convertRGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *); +extern void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); #endif void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags flags) @@ -145,42 +154,39 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio // Cannot be used with indexed formats. Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - const int buffer_size = 2048; - uint buf[buffer_size]; + uint buf[BufferSize]; uint *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; const uchar *srcData = src->data; uchar *destData = dest->data; - const FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp]; - const StorePixelsFunc store = qStorePixels[destLayout->bpp]; - ConvertFunc convertToARGB32PM = srcLayout->convertToARGB32PM; - ConvertFunc convertFromARGB32PM = destLayout->convertFromARGB32PM; - if (srcLayout->alphaWidth == 0 && destLayout->convertFromRGB32) { - // If the source doesn't have an alpha channel, we can use the faster convertFromRGB32 method. - convertFromARGB32PM = destLayout->convertFromRGB32; + FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; + ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; + if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) { + // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method. + store = destLayout->storeFromRGB32; } else { // The drawhelpers do not mask the alpha value in RGB32, we want to here. if (src->format == QImage::Format_RGB32) - convertToARGB32PM = maskRGB32; + fetch = fetchRGB32ToARGB32PM; if (dest->format == QImage::Format_RGB32) { #ifdef QT_COMPILER_SUPPORTS_SSE4_1 if (qCpuHasFeature(SSE4_1)) - convertFromARGB32PM = convertRGB32FromARGB32PM_sse4; + store = storeRGB32FromARGB32PM_sse4; else #endif - convertFromARGB32PM = convertRGB32FromARGB32PM; + store = storeRGB32FromARGB32PM; } } - if ((src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888) && - destLayout->alphaWidth == 0 && destLayout->convertFromRGB32) { + if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && + !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) { // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format. - convertToARGB32PM = qPixelLayouts[src->format + 1].convertToARGB32PM; + fetch = qPixelLayouts[src->format + 1].fetchToARGB32PM; if (dest->format == QImage::Format_RGB32) - convertFromARGB32PM = maskRGB32; + store = storeRGB32FromARGB32; else - convertFromARGB32PM = destLayout->convertFromRGB32; + store = destLayout->storeFromRGB32; } QDitherInfo dither; QDitherInfo *ditherPtr = 0; @@ -196,12 +202,9 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio if (destLayout->bpp == QPixelLayout::BPP32) buffer = reinterpret_cast<uint *>(destData) + x; else - l = qMin(l, buffer_size); - const uint *ptr = fetch(buffer, srcData, x, l); - ptr = convertToARGB32PM(buffer, ptr, l, 0, ditherPtr); - ptr = convertFromARGB32PM(buffer, ptr, l, 0, ditherPtr); - if (ptr != reinterpret_cast<uint *>(destData)) - store(destData, ptr, x, l); + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, 0, ditherPtr); + store(destData, ptr, x, l, 0, ditherPtr); x += l; } srcData += src->bytes_per_line; @@ -209,6 +212,26 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio } } +void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(src->format > QImage::Format_Indexed8); + const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; + const uchar *srcData = src->data; + uchar *destData = dest->data; + + const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM; + + for (int y = 0; y < src->height; ++y) { + const QRgba64 *ptr = fetch((QRgba64*)destData, srcData, 0, src->width, nullptr, nullptr); + if (ptr != (const QRgba64*)destData) { + memcpy(destData, ptr, dest->bytes_per_line); + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; + } +} + bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags) { // Cannot be used with indexed formats or between formats with different pixel depths. @@ -217,39 +240,39 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im if (data->depth != qt_depthForFormat(dst_format)) return false; - const int buffer_size = 2048; - uint buffer[buffer_size]; + uint buf[BufferSize]; + uint *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[data->format]; const QPixelLayout *destLayout = &qPixelLayouts[dst_format]; uchar *srcData = data->data; - const FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp]; - const StorePixelsFunc store = qStorePixels[destLayout->bpp]; - ConvertFunc convertToARGB32PM = srcLayout->convertToARGB32PM; - ConvertFunc convertFromARGB32PM = destLayout->convertFromARGB32PM; - if (srcLayout->alphaWidth == 0 && destLayout->convertFromRGB32) { - // If the source doesn't have an alpha channel, we can use the faster convertFromRGB32 method. - convertFromARGB32PM = destLayout->convertFromRGB32; + Q_ASSERT(srcLayout->bpp == destLayout->bpp); + Q_ASSERT(srcLayout->bpp != QPixelLayout::BPP64); + FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; + ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; + if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) { + // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method. + store = destLayout->storeFromRGB32; } else { if (data->format == QImage::Format_RGB32) - convertToARGB32PM = maskRGB32; + fetch = fetchRGB32ToARGB32PM; if (dst_format == QImage::Format_RGB32) { #ifdef QT_COMPILER_SUPPORTS_SSE4_1 if (qCpuHasFeature(SSE4_1)) - convertFromARGB32PM = convertRGB32FromARGB32PM_sse4; + store = storeRGB32FromARGB32PM_sse4; else #endif - convertFromARGB32PM = convertRGB32FromARGB32PM; + store = storeRGB32FromARGB32PM; } } - if ((data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888) && - destLayout->alphaWidth == 0 && destLayout->convertFromRGB32) { + if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && + !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) { // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format. - convertToARGB32PM = qPixelLayouts[data->format + 1].convertToARGB32PM; - if (dst_format == QImage::Format_RGB32) - convertFromARGB32PM = maskRGB32; + fetch = qPixelLayouts[data->format + 1].fetchToARGB32PM; + if (data->format == QImage::Format_RGB32) + store = storeRGB32FromARGB32; else - convertFromARGB32PM = destLayout->convertFromRGB32; + store = destLayout->storeFromRGB32; } QDitherInfo dither; QDitherInfo *ditherPtr = 0; @@ -261,13 +284,13 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im int x = 0; while (x < data->width) { dither.x = x; - int l = qMin(data->width - x, buffer_size); - const uint *ptr = fetch(buffer, srcData, x, l); - ptr = convertToARGB32PM(buffer, ptr, l, 0, ditherPtr); - ptr = convertFromARGB32PM(buffer, ptr, l, 0, ditherPtr); - // The conversions might be passthrough and not use the buffer, in that case we are already done. - if (srcData != (const uchar*)ptr) - store(srcData, ptr, x, l); + int l = data->width - x; + if (destLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(srcData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); + store(srcData, ptr, x, l, nullptr, ditherPtr); x += l; } srcData += data->bytes_per_line; @@ -281,20 +304,15 @@ static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::Ima Q_ASSERT(src->width == dest->width); Q_ASSERT(src->height == dest->height); - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; + const int src_bpl = src->bytes_per_line; + const int dest_bpl = dest->bytes_per_line; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = *src_data; - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; + memcpy(dest_data, src_data, src_bpl); + src_data += src_bpl; + dest_data += dest_bpl; } } @@ -305,30 +323,6 @@ static bool convert_passthrough_inplace(QImageData *data, Qt::ImageConversionFla return true; } -static void convert_ARGB_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888); - Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = qPremultiply(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32(quint32 *dest_data, const uchar *src_data, int len) { int pixel = 0; @@ -431,33 +425,6 @@ static void convert_RGB888_to_RGB(QImageData *dest, const QImageData *src, Qt::I } } -#ifdef __SSE2__ -extern bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags); -#else -static bool convert_ARGB_to_ARGB_PM_inplace(QImageData *data,Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888); - - const int pad = (data->bytes_per_line >> 2) - data->width; - QRgb *rgb_data = (QRgb *) data->data; - - for (int i = 0; i < data->height; ++i) { - const QRgb *end = rgb_data + data->width; - while (rgb_data < end) { - *rgb_data = qPremultiply(*rgb_data); - ++rgb_data; - } - rgb_data += pad; - } - - if (data->format == QImage::Format_ARGB32) - data->format = QImage::Format_ARGB32_Premultiplied; - else - data->format = QImage::Format_RGBA8888_Premultiplied; - return true; -} -#endif - static void convert_ARGB_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(src->format == QImage::Format_ARGB32); @@ -573,11 +540,12 @@ static bool convert_RGBA_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFl return true; } -template<QtPixelOrder PixelOrder> +template<QtPixelOrder PixelOrder, bool RGBA> static void convert_RGB_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { - Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32); + Q_ASSERT(RGBA || src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || src->format == QImage::Format_RGBX8888 || src->format == QImage::Format_RGBA8888); Q_ASSERT(dest->format == QImage::Format_BGR30 || dest->format == QImage::Format_RGB30); Q_ASSERT(src->width == dest->width); Q_ASSERT(src->height == dest->height); @@ -590,7 +558,10 @@ static void convert_RGB_to_RGB30(QImageData *dest, const QImageData *src, Qt::Im for (int i = 0; i < src->height; ++i) { const quint32 *end = src_data + src->width; while (src_data < end) { - *dest_data = qConvertRgb32ToRgb30<PixelOrder>(*src_data); + QRgb c = *src_data; + if (RGBA) + c = RGBA2ARGB(c); + *dest_data = qConvertRgb32ToRgb30<PixelOrder>(c); ++src_data; ++dest_data; } @@ -599,10 +570,11 @@ static void convert_RGB_to_RGB30(QImageData *dest, const QImageData *src, Qt::Im } } -template<QtPixelOrder PixelOrder> +template<QtPixelOrder PixelOrder, bool RGBA> static bool convert_RGB_to_RGB30_inplace(QImageData *data, Qt::ImageConversionFlags) { - Q_ASSERT(data->format == QImage::Format_RGB32 || data->format == QImage::Format_ARGB32); + Q_ASSERT(RGBA || (data->format == QImage::Format_RGB32 || data->format == QImage::Format_ARGB32)); + Q_ASSERT(!RGBA || (data->format == QImage::Format_RGBX8888 || data->format == QImage::Format_RGBA8888)); const int pad = (data->bytes_per_line >> 2) - data->width; QRgb *rgb_data = (QRgb *) data->data; @@ -610,7 +582,10 @@ static bool convert_RGB_to_RGB30_inplace(QImageData *data, Qt::ImageConversionFl for (int i = 0; i < data->height; ++i) { const QRgb *end = rgb_data + data->width; while (rgb_data < end) { - *rgb_data = qConvertRgb32ToRgb30<PixelOrder>(*rgb_data); + QRgb c = *rgb_data; + if (RGBA) + c = RGBA2ARGB(c); + *rgb_data = qConvertRgb32ToRgb30<PixelOrder>(c); ++rgb_data; } rgb_data += pad; @@ -771,11 +746,11 @@ static bool convert_BGR30_to_A2RGB30_inplace(QImageData *data, Qt::ImageConversi return true; } -template<QtPixelOrder PixelOrder> +template<QtPixelOrder PixelOrder, bool RGBA> static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(src->format == QImage::Format_A2RGB30_Premultiplied || src->format == QImage::Format_A2BGR30_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_ARGB32); + Q_ASSERT(RGBA ? dest->format == QImage::Format_RGBA8888 : dest->format == QImage::Format_ARGB32); Q_ASSERT(src->width == dest->width); Q_ASSERT(src->height == dest->height); @@ -788,6 +763,8 @@ static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src, const quint32 *end = src_data + src->width; while (src_data < end) { *dest_data = qConvertA2rgb30ToArgb32<PixelOrder>(qUnpremultiplyRgb30(*src_data)); + if (RGBA) + *dest_data = ARGB2RGBA(*dest_data); ++src_data; ++dest_data; } @@ -796,7 +773,7 @@ static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src, } } -template<QtPixelOrder PixelOrder> +template<QtPixelOrder PixelOrder, bool RGBA> static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags) { Q_ASSERT(data->format == QImage::Format_A2RGB30_Premultiplied || data->format == QImage::Format_A2BGR30_Premultiplied); @@ -808,11 +785,16 @@ static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConver const uint *end = rgb_data + data->width; while (rgb_data < end) { *rgb_data = qConvertA2rgb30ToArgb32<PixelOrder>(qUnpremultiplyRgb30(*rgb_data)); + if (RGBA) + *rgb_data = ARGB2RGBA(*rgb_data); ++rgb_data; } rgb_data += pad; } - data->format = QImage::Format_ARGB32; + if (RGBA) + data->format = QImage::Format_RGBA8888; + else + data->format = QImage::Format_ARGB32; return true; } @@ -1188,6 +1170,270 @@ static bool mask_alpha_converter_rgbx_inplace(QImageData *data, Qt::ImageConvers #endif } +template<bool RGBA> +static void convert_RGBA64_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64); + Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const uchar *srcData = src->data; + uchar *destData = dest->data; + + for (int i = 0; i < src->height; ++i) { + uint *d = reinterpret_cast<uint *>(destData); + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(srcData); + qt_convertRGBA64ToARGB32<RGBA>(d, s, src->width); + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; + } +} + +template<bool RGBA> +static void convert_RGBA64PM_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + uint *dest_data = reinterpret_cast<uint *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + QRgba64 s = src_data->unpremultiplied(); + *dest_data = RGBA ? ARGB2RGBA(s.toArgb32()) : s.toArgb32(); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<bool RGBA> +static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(RGBA || src->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || src->format == QImage::Format_RGBA8888); + Q_ASSERT(dest->format == QImage::Format_RGBA64); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const uint *src_data = reinterpret_cast<const uint *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const uint *end = src_data + src->width; + while (src_data < end) { + if (RGBA) + *dest_data = QRgba64::fromArgb32(RGBA2ARGB(*src_data)); + else + *dest_data = QRgba64::fromArgb32(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<QtPixelOrder PixelOrder> +static void convert_RGBA64PM_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGB30 || dest->format == QImage::Format_BGR30); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + uint *dest_data = reinterpret_cast<uint *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = 0xc0000000 | qConvertRgb64ToRgb30<PixelOrder>(src_data->unpremultiplied()); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<QtPixelOrder PixelOrder> +static void convert_RGBA64PM_to_A2RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_A2RGB30_Premultiplied + || dest->format == QImage::Format_A2BGR30_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + uint *dest_data = reinterpret_cast<uint *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = qConvertRgb64ToRgb30<PixelOrder>(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGBA64_to_RGBx64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64); + Q_ASSERT(dest->format == QImage::Format_RGBX64); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = *src_data; + dest_data->setAlpha(65535); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_RGBA64_to_RGBx64_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA64); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba64 *end = rgb_data + data->width; + while (rgb_data < end) { + rgb_data->setAlpha(65535); + ++rgb_data; + } + rgb_data += pad; + } + data->format = QImage::Format_RGBX64; + return true; +} + +static void convert_RGBA64_to_RGBA64PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64); + Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = src_data->premultiplied(); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_RGBA64_to_RGBA64PM_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA64); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba64 *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = rgb_data->premultiplied(); + ++rgb_data; + } + rgb_data += pad; + } + data->format = QImage::Format_RGBA64_Premultiplied; + return true; +} + +template<bool MaskAlpha> +static void convert_RGBA64PM_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = src_data->unpremultiplied(); + if (MaskAlpha) + dest_data->setAlpha(65535); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<bool MaskAlpha> +static bool convert_RGBA64PM_to_RGBA64_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA64_Premultiplied); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba64 *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = rgb_data->unpremultiplied(); + if (MaskAlpha) + rgb_data->setAlpha(65535); + ++rgb_data; + } + rgb_data += pad; + } + data->format = MaskAlpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; + return true; +} + static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format) { QVector<QRgb> colorTable = ctbl; @@ -2032,7 +2278,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, @@ -2053,7 +2299,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { @@ -2075,7 +2321,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { @@ -2100,6 +2346,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8, convert_Indexed8_to_Grayscale8, + 0, 0, 0 }, // Format_Indexed8 { @@ -2122,11 +2369,12 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - convert_RGB_to_RGB30<PixelOrderBGR>, + convert_RGB_to_RGB30<PixelOrderBGR, false>, 0, - convert_RGB_to_RGB30<PixelOrderRGB>, + convert_RGB_to_RGB30<PixelOrderRGB, false>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB32 { @@ -2136,7 +2384,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_ARGB_to_Indexed8, mask_alpha_converter, 0, - convert_ARGB_to_ARGB_PM, + 0, 0, 0, 0, @@ -2149,11 +2397,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_ARGB_to_RGBx, convert_ARGB_to_RGBA, 0, - convert_RGB_to_RGB30<PixelOrderBGR>, + convert_RGB_to_RGB30<PixelOrderBGR, false>, 0, - convert_RGB_to_RGB30<PixelOrderRGB>, + convert_RGB_to_RGB30<PixelOrderRGB, false>, 0, - 0, 0 + 0, 0, + 0, + convert_ARGB32_to_RGBA64<false>, + 0 }, // Format_ARGB32 { @@ -2176,11 +2427,9 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, convert_ARGB_to_RGBA, - 0, - 0, - 0, - 0, - 0, 0 + 0, 0, 0, 0, + 0, 0, + 0, 0, 0 }, // Format_ARGB32_Premultiplied { @@ -2202,7 +2451,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { @@ -2224,7 +2473,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { @@ -2246,7 +2495,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { @@ -2268,7 +2517,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { @@ -2290,7 +2539,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { @@ -2312,7 +2561,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { @@ -2335,7 +2584,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB888 { @@ -2357,7 +2606,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { @@ -2378,7 +2627,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -2398,9 +2647,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - mask_alpha_converter_RGBx, - mask_alpha_converter_RGBx, - 0, 0, 0, 0, 0, 0 + convert_passthrough, + convert_passthrough, + convert_RGB_to_RGB30<PixelOrderBGR, true>, + 0, + convert_RGB_to_RGB30<PixelOrderRGB, true>, + 0, + 0, 0, + 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -2420,14 +2674,16 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, mask_alpha_converter_RGBx, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN 0, - convert_ARGB_to_ARGB_PM, -#else 0, + convert_RGB_to_RGB30<PixelOrderBGR, true>, 0, -#endif - 0, 0, 0, 0, 0, 0 + convert_RGB_to_RGB30<PixelOrderRGB, true>, + 0, + 0, 0, + 0, + convert_ARGB32_to_RGBA64<true>, + 0 }, // Format_RGBA8888 { @@ -2449,7 +2705,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { @@ -2476,7 +2732,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_passthrough, convert_BGR30_to_RGB30, convert_BGR30_to_RGB30, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_BGR30 { 0, @@ -2484,8 +2741,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - convert_A2RGB30_PM_to_ARGB<PixelOrderBGR>, - 0, + convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, false>, 0, 0, 0, @@ -2497,12 +2753,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, + convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, true>, 0, convert_A2RGB30_PM_to_RGB30<false>, 0, convert_A2RGB30_PM_to_RGB30<true>, convert_BGR30_to_RGB30, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_BGR30A2_Premultiplied { 0, @@ -2528,7 +2786,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, 0, convert_passthrough, - 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -2536,8 +2794,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - convert_A2RGB30_PM_to_ARGB<PixelOrderRGB>, - 0, + convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, false>, 0, 0, 0, @@ -2549,12 +2806,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, + convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, true>, 0, convert_A2RGB30_PM_to_RGB30<true>, convert_BGR30_to_RGB30, convert_A2RGB30_PM_to_RGB30<false>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB30A2_Premultiplied { 0, @@ -2574,7 +2833,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Alpha8 { 0, @@ -2594,20 +2853,81 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 - } // Format_Grayscale8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_Grayscale8 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // self + convert_passthrough, + convert_passthrough + }, // Format_RGBX64 + { + 0, + 0, + 0, + 0, + 0, + convert_RGBA64_to_ARGB32<false>, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGBA64_to_ARGB32<true>, + 0, + 0, 0, 0, 0, + 0, 0, + convert_RGBA64_to_RGBx64, + 0, // self + convert_RGBA64_to_RGBA64PM + }, // Format_RGBA64 + { + 0, + 0, + 0, + 0, + 0, + convert_RGBA64PM_to_ARGB32<false>, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGBA64PM_to_ARGB32<true>, + 0, + convert_RGBA64PM_to_RGB30<PixelOrderBGR>, + convert_RGBA64PM_to_A2RGB30<PixelOrderBGR>, + convert_RGBA64PM_to_RGB30<PixelOrderRGB>, + convert_RGBA64PM_to_A2RGB30<PixelOrderRGB>, + 0, 0, + convert_RGBA64PM_to_RGBA64<true>, + convert_RGBA64PM_to_RGBA64<false>, + 0 // self + } // Format_RGBA64_Premultiplied }; InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { 0, @@ -2631,6 +2951,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8_inplace, convert_Indexed8_to_Grayscale8_inplace, + 0, 0, 0 }, // Format_Indexed8 { 0, @@ -2652,11 +2973,12 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - convert_RGB_to_RGB30_inplace<PixelOrderBGR>, + convert_RGB_to_RGB30_inplace<PixelOrderBGR, false>, 0, - convert_RGB_to_RGB30_inplace<PixelOrderRGB>, + convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB32 { 0, @@ -2665,11 +2987,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, mask_alpha_converter_inplace<QImage::Format_RGB32>, 0, -#ifdef __SSE2__ - convert_ARGB_to_ARGB_PM_inplace_sse2, -#else - convert_ARGB_to_ARGB_PM_inplace, -#endif + 0, 0, 0, 0, @@ -2682,11 +3000,12 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_ARGB_to_RGBA_inplace<QImage::Format_RGBX8888>, convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888>, 0, - convert_RGB_to_RGB30_inplace<PixelOrderBGR>, + convert_RGB_to_RGB30_inplace<PixelOrderBGR, false>, 0, - convert_RGB_to_RGB30_inplace<PixelOrderRGB>, + convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_ARGB32 { 0, @@ -2708,38 +3027,36 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>, - 0, - 0, - 0, - 0, - 0, 0 + 0, 0, 0, 0, + 0, 0, + 0, 0, 0 }, // Format_ARGB32_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB888 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -2761,7 +3078,12 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, convert_passthrough_inplace<QImage::Format_RGBA8888>, convert_passthrough_inplace<QImage::Format_RGBA8888_Premultiplied>, - 0, 0, 0, 0, 0, 0 + convert_RGB_to_RGB30_inplace<PixelOrderBGR, true>, + 0, + convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>, + 0, + 0, 0, + 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -2782,14 +3104,13 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, mask_alpha_converter_rgbx_inplace, 0, -#ifdef __SSE2__ - convert_ARGB_to_ARGB_PM_inplace_sse2, -#elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN - convert_ARGB_to_ARGB_PM_inplace, -#else 0, -#endif - 0, 0, 0, 0, 0, 0 + convert_RGB_to_RGB30_inplace<PixelOrderBGR, true>, + 0, + convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>, + 0, + 0, 0, + 0, 0, 0 }, // Format_RGBA8888 { 0, @@ -2811,7 +3132,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { 0, @@ -2837,7 +3158,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_passthrough_inplace<QImage::Format_A2BGR30_Premultiplied>, convert_BGR30_to_RGB30_inplace, convert_BGR30_to_A2RGB30_inplace, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_BGR30 { 0, @@ -2845,8 +3167,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR>, - 0, + convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, false>, 0, 0, 0, @@ -2858,12 +3179,13 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, + convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, true>, 0, convert_A2RGB30_PM_to_RGB30_inplace<false>, 0, // self convert_A2RGB30_PM_to_RGB30_inplace<true>, convert_BGR30_to_RGB30_inplace, - 0, 0 + 0, 0, 0, 0, 0 }, // Format_BGR30A2_Premultiplied { 0, @@ -2889,7 +3211,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_A2RGB30_inplace, 0, // self convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>, - 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -2897,8 +3219,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB>, - 0, + convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, false>, 0, 0, 0, @@ -2910,12 +3231,14 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, + convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, true>, 0, convert_A2RGB30_PM_to_RGB30_inplace<true>, convert_BGR30_to_RGB30_inplace, convert_A2RGB30_PM_to_RGB30_inplace<false>, 0, // self - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB30A2_Premultiplied { 0, @@ -2937,11 +3260,10 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, + 0, 0, 0, 0, + 0, // self 0, - 0, - 0, - 0, - 0, 0 + 0, 0, 0 }, // Format_Alpha8 { 0, @@ -2963,12 +3285,29 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, + 0, 0, 0, 0, 0, - 0, - 0, - 0, - 0, 0 - } // Format_Grayscale8 + 0, // self + 0, 0, 0 + }, // Format_Grayscale8 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // self + convert_passthrough_inplace<QImage::Format_RGBA64>, + convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>, + }, // Format_RGBX64 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGBA64_to_RGBx64_inplace, + 0, // self + convert_RGBA64_to_RGBA64PM_inplace + }, // Format_RGBA64 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGBA64PM_to_RGBA64_inplace<true>, + convert_RGBA64PM_to_RGBA64_inplace<false>, + 0 // self + } // Format_RGBA64_Premultiplied }; static void qInitImageConversions() @@ -2982,22 +3321,6 @@ static void qInitImageConversions() } #endif -#if defined(QT_COMPILER_SUPPORTS_SSE4_1) && !defined(__SSE4_1__) - if (qCpuHasFeature(SSE4_1)) { - extern void convert_ARGB_to_ARGB_PM_sse4(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); - qimage_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_sse4; - qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_ARGB_PM_sse4; - } -#endif - -#if defined(QT_COMPILER_SUPPORTS_AVX2) && !defined(__AVX2__) - if (qCpuHasFeature(AVX2)) { - extern void convert_ARGB_to_ARGB_PM_avx2(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); - qimage_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_avx2; - qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_ARGB_PM_avx2; - } -#endif - #if defined(__ARM_NEON__) extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon; diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index f5fea2ed00..2fe29a88d3 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -113,6 +113,7 @@ extern Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImag extern InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats]; void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); +void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags); void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha); @@ -164,6 +165,11 @@ inline int qt_depthForFormat(QImage::Format format) case QImage::Format_RGB888: depth = 24; break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + depth = 64; + break; } return depth; } @@ -190,6 +196,9 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format) return QImage::Format_BGR30; case QImage::Format_A2RGB30_Premultiplied: return QImage::Format_RGB30; + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + return QImage::Format_RGBX64; case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB32: default: @@ -214,6 +223,8 @@ inline QImage::Format qt_alphaVersion(QImage::Format format) return QImage::Format_A2BGR30_Premultiplied; case QImage::Format_RGB30: return QImage::Format_A2RGB30_Premultiplied; + case QImage::Format_RGBX64: + return QImage::Format_RGBA64_Premultiplied; default: break; } diff --git a/src/gui/image/qimage_sse2.cpp b/src/gui/image/qimage_sse2.cpp deleted file mode 100644 index 8f7195e0b5..0000000000 --- a/src/gui/image/qimage_sse2.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include "qimage.h" -#include <private/qimage_p.h> -#include <private/qsimd_p.h> -#include <private/qdrawhelper_p.h> -#include <private/qdrawingprimitive_sse2_p.h> - -#ifdef QT_COMPILER_SUPPORTS_SSE2 - -QT_BEGIN_NAMESPACE - -bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888); - - const int width = data->width; - const int height = data->height; - const int bpl = data->bytes_per_line; - - const __m128i alphaMask = _mm_set1_epi32(0xff000000); - const __m128i nullVector = _mm_setzero_si128(); - const __m128i half = _mm_set1_epi16(0x80); - const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); - - uchar *d = data->data; - for (int y = 0; y < height; ++y) { - int i = 0; - quint32 *d32 = reinterpret_cast<quint32 *>(d); - ALIGNMENT_PROLOGUE_16BYTES(d, i, width) { - const quint32 p = d32[i]; - if (p <= 0x00ffffff) - d32[i] = 0; - else if (p < 0xff000000) - d32[i] = qPremultiply(p); - } - __m128i *d128 = reinterpret_cast<__m128i *>(d32 + i); - for (; i < (width - 3); i += 4) { - const __m128i srcVector = _mm_load_si128(d128); -#ifdef __SSE4_1__ - if (_mm_testc_si128(srcVector, alphaMask)) { - // opaque, data is unchanged - } else if (_mm_testz_si128(srcVector, alphaMask)) { - // fully transparent - _mm_store_si128(d128, nullVector); - } else { - const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); -#else - const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); - if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { - // opaque, data is unchanged - } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) == 0xffff) { - // fully transparent - _mm_store_si128(d128, nullVector); - } else { -#endif - __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); - alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); - - __m128i result; - BYTE_MUL_SSE2(result, srcVector, alphaChannel, colorMask, half); - result = _mm_or_si128(_mm_andnot_si128(alphaMask, result), srcVectorAlpha); - _mm_store_si128(d128, result); - } - d128++; - } - - SIMD_EPILOGUE(i, width, 3) { - const quint32 p = d32[i]; - if (p <= 0x00ffffff) - d32[i] = 0; - else if (p < 0xff000000) - d32[i] = qPremultiply(p); - } - - d += bpl; - } - - if (data->format == QImage::Format_ARGB32) - data->format = QImage::Format_ARGB32_Premultiplied; - else - data->format = QImage::Format_RGBA8888_Premultiplied; - return true; -} - -QT_END_NAMESPACE - -#endif // QT_COMPILER_SUPPORTS_SSE2 diff --git a/src/gui/image/qimage_sse4.cpp b/src/gui/image/qimage_sse4.cpp deleted file mode 100644 index 0e2c2f492e..0000000000 --- a/src/gui/image/qimage_sse4.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include <qimage.h> -#include <private/qdrawhelper_p.h> -#include <private/qdrawingprimitive_sse2_p.h> -#include <private/qimage_p.h> -#include <private/qsimd_p.h> - -#ifdef QT_COMPILER_SUPPORTS_SSE4_1 - -QT_BEGIN_NAMESPACE - -const uint *QT_FASTCALL convertRGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) -{ - for (int i = 0; i < count; ++i) - buffer[i] = 0xff000000 | qUnpremultiply_sse4(src[i]); - return buffer; -} - -void convert_ARGB_to_ARGB_PM_sse4(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888); - Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const uint *src_data = (uint *) src->data; - uint *dest_data = (uint *) dest->data; - for (int i = 0; i < src->height; ++i) { - qt_convertARGB32ToARGB32PM(dest_data, src_data, src->width); - src_data += src->bytes_per_line >> 2; - dest_data += dest->bytes_per_line >> 2; - } -} - -QT_END_NAMESPACE - -#endif // QT_COMPILER_SUPPORTS_SSE4_1 diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 7086e102ea..6d358984d6 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -164,73 +164,13 @@ #include <private/qpnghandler_p.h> #endif +#include <private/qimagereaderwriterhelpers_p.h> + #include <algorithm> QT_BEGIN_NAMESPACE -#ifndef QT_NO_IMAGEFORMATPLUGIN -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, - (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats"))) -#endif - -enum _qt_BuiltInFormatType { -#ifndef QT_NO_IMAGEFORMAT_PNG - _qt_PngFormat, -#endif -#ifndef QT_NO_IMAGEFORMAT_BMP - _qt_BmpFormat, -#endif -#ifndef QT_NO_IMAGEFORMAT_PPM - _qt_PpmFormat, - _qt_PgmFormat, - _qt_PbmFormat, -#endif -#ifndef QT_NO_IMAGEFORMAT_XBM - _qt_XbmFormat, -#endif -#ifndef QT_NO_IMAGEFORMAT_XPM - _qt_XpmFormat, -#endif - _qt_NumFormats, - _qt_NoFormat = -1 -}; - -#if !defined(QT_NO_IMAGEFORMAT_PPM) -# define MAX_MT_SIZE 20 -#elif !defined(QT_NO_IMAGEFORMAT_XBM) || !defined(QT_NO_IMAGEFORMAT_XPM) -# define MAX_MT_SIZE 10 -#else -# define MAX_MT_SIZE 4 -#endif - -struct _qt_BuiltInFormatStruct -{ - char extension[4]; - char mimeType[MAX_MT_SIZE]; -}; - -#undef MAX_MT_SIZE - -static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = { -#ifndef QT_NO_IMAGEFORMAT_PNG - {"png", "png"}, -#endif -#ifndef QT_NO_IMAGEFORMAT_BMP - {"bmp", "bmp"}, -#endif -#ifndef QT_NO_IMAGEFORMAT_PPM - {"ppm", "x-portable-pixmap"}, - {"pgm", "x-portable-graymap"}, - {"pbm", "x-portable-bitmap"}, -#endif -#ifndef QT_NO_IMAGEFORMAT_XBM - {"xbm", "x-xbitmap"}, -#endif -#ifndef QT_NO_IMAGEFORMAT_XPM - {"xpm", "x-xpixmap"}, -#endif -}; -Q_STATIC_ASSERT(_qt_NumFormats == sizeof _qt_BuiltInFormats / sizeof *_qt_BuiltInFormats); +using namespace QImageReaderWriterHelpers; static QImageIOHandler *createReadHandlerHelper(QIODevice *device, const QByteArray &format, @@ -251,7 +191,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device, typedef QMultiMap<int, QString> PluginKeyMap; // check if we have plugins that support the image format - QFactoryLoader *l = loader(); + auto l = QImageReaderWriterHelpers::pluginLoader(); const PluginKeyMap keyMap = l->keyMap(); #ifdef QIMAGEREADER_DEBUG @@ -1565,16 +1505,6 @@ QByteArray QImageReader::imageFormat(QIODevice *device) return format; } -#ifndef QT_NO_IMAGEFORMATPLUGIN -void supportedImageHandlerFormats(QFactoryLoader *loader, - QImageIOPlugin::Capability cap, - QList<QByteArray> *result); - -void supportedImageHandlerMimeTypes(QFactoryLoader *loader, - QImageIOPlugin::Capability cap, - QList<QByteArray> *result); -#endif - /*! Returns the list of image formats supported by QImageReader. @@ -1605,18 +1535,7 @@ void supportedImageHandlerMimeTypes(QFactoryLoader *loader, QList<QByteArray> QImageReader::supportedImageFormats() { - QList<QByteArray> formats; - formats.reserve(_qt_NumFormats); - for (int i = 0; i < _qt_NumFormats; ++i) - formats << _qt_BuiltInFormats[i].extension; - -#ifndef QT_NO_IMAGEFORMATPLUGIN - supportedImageHandlerFormats(loader(), QImageIOPlugin::CanRead, &formats); -#endif // QT_NO_IMAGEFORMATPLUGIN - - std::sort(formats.begin(), formats.end()); - formats.erase(std::unique(formats.begin(), formats.end()), formats.end()); - return formats; + return QImageReaderWriterHelpers::supportedImageFormats(QImageReaderWriterHelpers::CanRead); } /*! @@ -1630,18 +1549,24 @@ QList<QByteArray> QImageReader::supportedImageFormats() QList<QByteArray> QImageReader::supportedMimeTypes() { - QList<QByteArray> mimeTypes; - mimeTypes.reserve(_qt_NumFormats); - for (const auto &fmt : _qt_BuiltInFormats) - mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType); + return QImageReaderWriterHelpers::supportedMimeTypes(QImageReaderWriterHelpers::CanRead); +} -#ifndef QT_NO_IMAGEFORMATPLUGIN - supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanRead, &mimeTypes); -#endif // QT_NO_IMAGEFORMATPLUGIN +/*! + \since 5.12 - std::sort(mimeTypes.begin(), mimeTypes.end()); - mimeTypes.erase(std::unique(mimeTypes.begin(), mimeTypes.end()), mimeTypes.end()); - return mimeTypes; + Returns the list of image formats corresponding to \a mimeType. + + Note that the QGuiApplication instance must be created before this function is + called. + + \sa supportedImageFormats(), supportedMimeTypes() +*/ + +QList<QByteArray> QImageReader::imageFormatsForMimeType(const QByteArray &mimeType) +{ + return QImageReaderWriterHelpers::imageFormatsForMimeType(mimeType, + QImageReaderWriterHelpers::CanRead); } QT_END_NAMESPACE diff --git a/src/gui/image/qimagereader.h b/src/gui/image/qimagereader.h index 9d6c1e0b1e..4e9a08b6e6 100644 --- a/src/gui/image/qimagereader.h +++ b/src/gui/image/qimagereader.h @@ -144,6 +144,7 @@ public: static QByteArray imageFormat(QIODevice *device); static QList<QByteArray> supportedImageFormats(); static QList<QByteArray> supportedMimeTypes(); + static QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType); private: Q_DISABLE_COPY(QImageReader) diff --git a/src/gui/image/qimagereaderwriterhelpers.cpp b/src/gui/image/qimagereaderwriterhelpers.cpp new file mode 100644 index 0000000000..a5b7fb6449 --- /dev/null +++ b/src/gui/image/qimagereaderwriterhelpers.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +#include "private/qimagereaderwriterhelpers_p.h" + +#include <qjsonarray.h> +#include <qmutex.h> +#include <private/qfactoryloader_p.h> + +QT_BEGIN_NAMESPACE + +namespace QImageReaderWriterHelpers { + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats"))) +Q_GLOBAL_STATIC(QMutex, loaderMutex) + +static void appendImagePluginFormats(QFactoryLoader *loader, + QImageIOPlugin::Capability cap, + QList<QByteArray> *result) +{ + typedef QMultiMap<int, QString> PluginKeyMap; + typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator; + + const PluginKeyMap keyMap = loader->keyMap(); + const PluginKeyMapConstIterator cend = keyMap.constEnd(); + int i = -1; + QImageIOPlugin *plugin = 0; + result->reserve(result->size() + keyMap.size()); + for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) { + if (it.key() != i) { + i = it.key(); + plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i)); + } + const QByteArray key = it.value().toLatin1(); + if (plugin && (plugin->capabilities(0, key) & cap) != 0) + result->append(key); + } +} + +static void appendImagePluginMimeTypes(QFactoryLoader *loader, + QImageIOPlugin::Capability cap, + QList<QByteArray> *result, + QList<QByteArray> *resultKeys = nullptr) +{ + QList<QJsonObject> metaDataList = loader->metaData(); + + const int pluginCount = metaDataList.size(); + for (int i = 0; i < pluginCount; ++i) { + const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject(); + const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray(); + const QJsonArray mimeTypes = metaData.value(QLatin1String("MimeTypes")).toArray(); + QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i)); + const int keyCount = keys.size(); + for (int k = 0; k < keyCount; ++k) { + const QByteArray key = keys.at(k).toString().toLatin1(); + if (plugin && (plugin->capabilities(0, key) & cap) != 0) { + result->append(mimeTypes.at(k).toString().toLatin1()); + if (resultKeys) + resultKeys->append(key); + } + } + } +} + +QSharedPointer<QFactoryLoader> pluginLoader() +{ + loaderMutex()->lock(); + return QSharedPointer<QFactoryLoader>(loader(), [](QFactoryLoader *) { + loaderMutex()->unlock(); + }); +} + +static inline QImageIOPlugin::Capability pluginCapability(Capability cap) +{ + return cap == CanRead ? QImageIOPlugin::CanRead : QImageIOPlugin::CanWrite; +} + +#endif // QT_NO_IMAGEFORMATPLUGIN + +QList<QByteArray> supportedImageFormats(Capability cap) +{ + QList<QByteArray> formats; + formats.reserve(_qt_NumFormats); + for (int i = 0; i < _qt_NumFormats; ++i) + formats << _qt_BuiltInFormats[i].extension; + +#ifndef QT_NO_IMAGEFORMATPLUGIN + appendImagePluginFormats(loader(), pluginCapability(cap), &formats); +#endif // QT_NO_IMAGEFORMATPLUGIN + + std::sort(formats.begin(), formats.end()); + formats.erase(std::unique(formats.begin(), formats.end()), formats.end()); + return formats; +} + +QList<QByteArray> supportedMimeTypes(Capability cap) +{ + QList<QByteArray> mimeTypes; + mimeTypes.reserve(_qt_NumFormats); + for (const auto &fmt : _qt_BuiltInFormats) + mimeTypes.append(QByteArrayLiteral("image/") + fmt.mimeType); + +#ifndef QT_NO_IMAGEFORMATPLUGIN + appendImagePluginMimeTypes(loader(), pluginCapability(cap), &mimeTypes); +#endif // QT_NO_IMAGEFORMATPLUGIN + + std::sort(mimeTypes.begin(), mimeTypes.end()); + mimeTypes.erase(std::unique(mimeTypes.begin(), mimeTypes.end()), mimeTypes.end()); + return mimeTypes; +} + +QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability cap) +{ + QList<QByteArray> formats; + if (mimeType.startsWith("image/")) { + const QByteArray type = mimeType.mid(sizeof("image/") - 1); + for (const auto &fmt : _qt_BuiltInFormats) { + if (fmt.mimeType == type && !formats.contains(fmt.extension)) + formats << fmt.extension; + } + } + +#ifndef QT_NO_IMAGEFORMATPLUGIN + QList<QByteArray> mimeTypes; + QList<QByteArray> keys; + appendImagePluginMimeTypes(loader(), pluginCapability(cap), &mimeTypes, &keys); + for (int i = 0; i < mimeTypes.size(); ++i) { + if (mimeTypes.at(i) == mimeType) { + const auto &key = keys.at(i); + if (!formats.contains(key)) + formats << key; + } + } +#endif // QT_NO_IMAGEFORMATPLUGIN + + return formats; +} + +} // QImageReaderWriterHelpers + +QT_END_NAMESPACE diff --git a/src/gui/image/qimagereaderwriterhelpers_p.h b/src/gui/image/qimagereaderwriterhelpers_p.h new file mode 100644 index 0000000000..6fe418a8ab --- /dev/null +++ b/src/gui/image/qimagereaderwriterhelpers_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +#ifndef QIMAGEREADERWRITERHELPERS_P_H +#define QIMAGEREADERWRITERHELPERS_P_H + +#include <QtGui/private/qtguiglobal_p.h> +#include <qsharedpointer.h> +#include "qimageiohandler.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QFactoryLoader; + +namespace QImageReaderWriterHelpers { + +enum _qt_BuiltInFormatType { +#ifndef QT_NO_IMAGEFORMAT_PNG + _qt_PngFormat, +#endif +#ifndef QT_NO_IMAGEFORMAT_BMP + _qt_BmpFormat, +#endif +#ifndef QT_NO_IMAGEFORMAT_PPM + _qt_PpmFormat, + _qt_PgmFormat, + _qt_PbmFormat, +#endif +#ifndef QT_NO_IMAGEFORMAT_XBM + _qt_XbmFormat, +#endif +#ifndef QT_NO_IMAGEFORMAT_XPM + _qt_XpmFormat, +#endif + _qt_NumFormats, + _qt_NoFormat = -1 +}; + +#if !defined(QT_NO_IMAGEFORMAT_PPM) +# define MAX_MT_SIZE 20 +#elif !defined(QT_NO_IMAGEFORMAT_XBM) || !defined(QT_NO_IMAGEFORMAT_XPM) +# define MAX_MT_SIZE 10 +#else +# define MAX_MT_SIZE 4 +#endif + +struct _qt_BuiltInFormatStruct +{ + char extension[4]; + char mimeType[MAX_MT_SIZE]; +}; + +#undef MAX_MT_SIZE + +static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = { +#ifndef QT_NO_IMAGEFORMAT_PNG + {"png", "png"}, +#endif +#ifndef QT_NO_IMAGEFORMAT_BMP + {"bmp", "bmp"}, +#endif +#ifndef QT_NO_IMAGEFORMAT_PPM + {"ppm", "x-portable-pixmap"}, + {"pgm", "x-portable-graymap"}, + {"pbm", "x-portable-bitmap"}, +#endif +#ifndef QT_NO_IMAGEFORMAT_XBM + {"xbm", "x-xbitmap"}, +#endif +#ifndef QT_NO_IMAGEFORMAT_XPM + {"xpm", "x-xpixmap"}, +#endif +}; +Q_STATIC_ASSERT(_qt_NumFormats == sizeof _qt_BuiltInFormats / sizeof *_qt_BuiltInFormats); + +#ifndef QT_NO_IMAGEFORMATPLUGIN +QSharedPointer<QFactoryLoader> pluginLoader(); +#endif + +enum Capability { + CanRead, + CanWrite +}; +QList<QByteArray> supportedImageFormats(Capability cap); +QList<QByteArray> supportedMimeTypes(Capability cap); +QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType, Capability cap); + +} + +QT_END_NAMESPACE + +#endif // QIMAGEREADERWRITERHELPERS_P_H diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index a39d204677..62eeb74727 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -102,7 +102,6 @@ #include <qfileinfo.h> #include <qimage.h> #include <qimageiohandler.h> -#include <qjsonarray.h> #include <qset.h> #include <qvariant.h> @@ -119,15 +118,12 @@ #include <private/qpnghandler_p.h> #endif +#include <private/qimagereaderwriterhelpers_p.h> + #include <algorithm> QT_BEGIN_NAMESPACE -#ifndef QT_NO_IMAGEFORMATPLUGIN -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, - (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats"))) -#endif - static QImageIOHandler *createWriteHandlerHelper(QIODevice *device, const QByteArray &format) { @@ -139,7 +135,7 @@ static QImageIOHandler *createWriteHandlerHelper(QIODevice *device, typedef QMultiMap<int, QString> PluginKeyMap; // check if any plugins can write the image - QFactoryLoader *l = loader(); + auto l = QImageReaderWriterHelpers::pluginLoader(); const PluginKeyMap keyMap = l->keyMap(); int suffixPluginIndex = -1; #endif @@ -824,52 +820,6 @@ bool QImageWriter::supportsOption(QImageIOHandler::ImageOption option) const return d->handler->supportsOption(option); } - -#ifndef QT_NO_IMAGEFORMATPLUGIN -void supportedImageHandlerFormats(QFactoryLoader *loader, - QImageIOPlugin::Capability cap, - QList<QByteArray> *result) -{ - typedef QMultiMap<int, QString> PluginKeyMap; - typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator; - - const PluginKeyMap keyMap = loader->keyMap(); - const PluginKeyMapConstIterator cend = keyMap.constEnd(); - int i = -1; - QImageIOPlugin *plugin = 0; - result->reserve(result->size() + keyMap.size()); - for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) { - if (it.key() != i) { - i = it.key(); - plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i)); - } - const QByteArray key = it.value().toLatin1(); - if (plugin && (plugin->capabilities(0, key) & cap) != 0) - result->append(key); - } -} - -void supportedImageHandlerMimeTypes(QFactoryLoader *loader, - QImageIOPlugin::Capability cap, - QList<QByteArray> *result) -{ - QList<QJsonObject> metaDataList = loader->metaData(); - - const int pluginCount = metaDataList.size(); - for (int i = 0; i < pluginCount; ++i) { - const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject(); - const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray(); - const QJsonArray mimeTypes = metaData.value(QLatin1String("MimeTypes")).toArray(); - QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(loader->instance(i)); - const int keyCount = keys.size(); - for (int k = 0; k < keyCount; ++k) { - if (plugin && (plugin->capabilities(0, keys.at(k).toString().toLatin1()) & cap) != 0) - result->append(mimeTypes.at(k).toString().toLatin1()); - } - } -} -#endif // QT_NO_IMAGEFORMATPLUGIN - /*! Returns the list of image formats supported by QImageWriter. @@ -897,30 +847,7 @@ void supportedImageHandlerMimeTypes(QFactoryLoader *loader, */ QList<QByteArray> QImageWriter::supportedImageFormats() { - QList<QByteArray> formats; -#ifndef QT_NO_IMAGEFORMAT_BMP - formats << "bmp"; -#endif -#ifndef QT_NO_IMAGEFORMAT_PPM - formats << "pbm" << "pgm" << "ppm"; -#endif -#ifndef QT_NO_IMAGEFORMAT_XBM - formats << "xbm"; -#endif -#ifndef QT_NO_IMAGEFORMAT_XPM - formats << "xpm"; -#endif -#ifndef QT_NO_IMAGEFORMAT_PNG - formats << "png"; -#endif - -#ifndef QT_NO_IMAGEFORMATPLUGIN - supportedImageHandlerFormats(loader(), QImageIOPlugin::CanWrite, &formats); -#endif // QT_NO_IMAGEFORMATPLUGIN - - std::sort(formats.begin(), formats.end()); - formats.erase(std::unique(formats.begin(), formats.end()), formats.end()); - return formats; + return QImageReaderWriterHelpers::supportedImageFormats(QImageReaderWriterHelpers::CanWrite); } /*! @@ -933,32 +860,24 @@ QList<QByteArray> QImageWriter::supportedImageFormats() */ QList<QByteArray> QImageWriter::supportedMimeTypes() { - QList<QByteArray> mimeTypes; -#ifndef QT_NO_IMAGEFORMAT_BMP - mimeTypes << "image/bmp"; -#endif -#ifndef QT_NO_IMAGEFORMAT_PPM - mimeTypes << "image/x-portable-bitmap"; - mimeTypes << "image/x-portable-graymap"; - mimeTypes << "image/x-portable-pixmap"; -#endif -#ifndef QT_NO_IMAGEFORMAT_XBM - mimeTypes << "image/x-xbitmap"; -#endif -#ifndef QT_NO_IMAGEFORMAT_XPM - mimeTypes << "image/x-xpixmap"; -#endif -#ifndef QT_NO_IMAGEFORMAT_PNG - mimeTypes << "image/png"; -#endif + return QImageReaderWriterHelpers::supportedMimeTypes(QImageReaderWriterHelpers::CanWrite); +} -#ifndef QT_NO_IMAGEFORMATPLUGIN - supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanWrite, &mimeTypes); -#endif // QT_NO_IMAGEFORMATPLUGIN +/*! + \since 5.12 - std::sort(mimeTypes.begin(), mimeTypes.end()); - mimeTypes.erase(std::unique(mimeTypes.begin(), mimeTypes.end()), mimeTypes.end()); - return mimeTypes; + Returns the list of image formats corresponding to \a mimeType. + + Note that the QGuiApplication instance must be created before this function is + called. + + \sa supportedImageFormats(), supportedMimeTypes() +*/ + +QList<QByteArray> QImageWriter::imageFormatsForMimeType(const QByteArray &mimeType) +{ + return QImageReaderWriterHelpers::imageFormatsForMimeType(mimeType, + QImageReaderWriterHelpers::CanWrite); } QT_END_NAMESPACE diff --git a/src/gui/image/qimagewriter.h b/src/gui/image/qimagewriter.h index fd1fdd07e8..29c06ccdd2 100644 --- a/src/gui/image/qimagewriter.h +++ b/src/gui/image/qimagewriter.h @@ -116,6 +116,7 @@ public: static QList<QByteArray> supportedImageFormats(); static QList<QByteArray> supportedMimeTypes(); + static QList<QByteArray> imageFormatsForMimeType(const QByteArray &mimeType); private: Q_DISABLE_COPY(QImageWriter) diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 1ea503a268..4b2334ae52 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -248,9 +248,9 @@ QPixmap::QPixmap(const char * const xpm[]) QImage image(xpm); if (!image.isNull()) { if (data && data->pixelType() == QPlatformPixmap::BitmapType) - *this = QBitmap::fromImage(image); + *this = QBitmap::fromImage(std::move(image)); else - *this = fromImage(image); + *this = fromImage(std::move(image)); } } #endif @@ -691,7 +691,7 @@ QBitmap QPixmap::createHeuristicMask(bool clipTight) const QBitmap QPixmap::createMaskFromColor(const QColor &maskColor, Qt::MaskMode mode) const { QImage image = toImage().convertToFormat(QImage::Format_ARGB32); - return QBitmap::fromImage(image.createMaskFromColor(maskColor.rgba(), mode)); + return QBitmap::fromImage(std::move(image).createMaskFromColor(maskColor.rgba(), mode)); } /*! @@ -1018,9 +1018,9 @@ QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap) if (image.isNull()) { pixmap = QPixmap(); } else if (image.depth() == 1) { - pixmap = QBitmap::fromImage(image); + pixmap = QBitmap::fromImage(std::move(image)); } else { - pixmap = QPixmap::fromImage(image); + pixmap = QPixmap::fromImage(std::move(image)); } return stream; } diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp index 646e737afa..649a25250c 100644 --- a/src/gui/image/qpixmap_blitter.cpp +++ b/src/gui/image/qpixmap_blitter.cpp @@ -150,13 +150,7 @@ void QBlittablePlatformPixmap::fill(const QColor &color) m_alpha = true; } - uint pixel = qPremultiply(color.rgba()); - const QPixelLayout *layout = &qPixelLayouts[blittable()->lock()->format()]; - Q_ASSERT(layout->convertFromARGB32PM); - layout->convertFromARGB32PM(&pixel, &pixel, 1, 0, 0); - - //so premultiplied formats are supported and ARGB32 and RGB32 - blittable()->lock()->fill(pixel); + blittable()->lock()->fill(color); } } diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index 431002d032..13c1c29d5b 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -193,17 +193,12 @@ void QRasterPlatformPixmap::fill(const QColor &color) if (alpha != 255) { if (!image.hasAlphaChannel()) { QImage::Format toFormat = qt_alphaVersionForPainting(image.format()); - if (!image.isNull() && qt_depthForFormat(image.format()) == qt_depthForFormat(toFormat)) { - image.detach(); - image.d->format = toFormat; - } else { + if (!image.reinterpretAsFormat(toFormat)) image = QImage(image.width(), image.height(), toFormat); - } } } - pixel = qPremultiply(color.rgba()); - const QPixelLayout *layout = &qPixelLayouts[image.format()]; - layout->convertFromARGB32PM(&pixel, &pixel, 1, 0, 0); + image.fill(color); + return; } else if (image.format() == QImage::Format_Alpha8) { pixel = qAlpha(color.rgba()); } else if (image.format() == QImage::Format_Grayscale8) { diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index 92f6964783..a9e472f8c4 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -42,39 +42,76 @@ #include <qpa/qplatformpixmap.h> #include "qpixmap_raster_p.h" -#include <qglobal.h> +#include <qdebug.h> #include <QScopedArrayPointer> #include <qt_windows.h> +#include <algorithm> +#include <iterator> + QT_BEGIN_NAMESPACE -static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, BITMAPINFOHEADER *bih) +template <typename Int> +static inline Int pad4(Int v) +{ + return (v + Int(3)) & ~Int(3); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const BITMAPINFOHEADER &bih) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "BITMAPINFOHEADER(" << bih.biWidth << 'x' << qAbs(bih.biHeight) + << (bih.biHeight < 0 ? ", top-down" : ", bottom-up") + << ", planes=" << bih.biPlanes << ", bitCount=" << bih.biBitCount + << ", compression=" << bih.biCompression << ", size=" + << bih.biSizeImage << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +static inline void initBitMapInfoHeader(int width, int height, bool topToBottom, + DWORD compression, DWORD bitCount, + BITMAPINFOHEADER *bih) { memset(bih, 0, sizeof(BITMAPINFOHEADER)); bih->biSize = sizeof(BITMAPINFOHEADER); bih->biWidth = width; bih->biHeight = topToBottom ? -height : height; bih->biPlanes = 1; - bih->biBitCount = 32; - bih->biCompression = BI_RGB; - bih->biSizeImage = width * height * 4; + bih->biBitCount = WORD(bitCount); + bih->biCompression = compression; + // scan lines are word-aligned (unless RLE) + const DWORD bytesPerLine = pad4(DWORD(width) * bitCount / 8); + bih->biSizeImage = bytesPerLine * DWORD(height); } -static inline void initBitMapInfo(int width, int height, bool topToBottom, BITMAPINFO *bmi) +enum { Indexed8ColorTableSize = 256 }; + +struct BITMAPINFO_COLORTABLE256 { // BITMAPINFO with 256 entry color table for Indexed 8 format + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[Indexed8ColorTableSize]; +}; + +template <class BITMAPINFO_T> // BITMAPINFO, BITMAPINFO_COLORTABLE256 +static inline void initBitMapInfo(int width, int height, bool topToBottom, + DWORD compression, DWORD bitCount, + BITMAPINFO_T *bmi) { - initBitMapInfoHeader(width, height, topToBottom, &bmi->bmiHeader); - memset(bmi->bmiColors, 0, sizeof(RGBQUAD)); + initBitMapInfoHeader(width, height, topToBottom, compression, bitCount, &bmi->bmiHeader); + memset(bmi->bmiColors, 0, sizeof(bmi->bmiColors)); } static inline uchar *getDiBits(HDC hdc, HBITMAP bitmap, int width, int height, bool topToBottom = true) { BITMAPINFO bmi; - initBitMapInfo(width, height, topToBottom, &bmi); + initBitMapInfo(width, height, topToBottom, BI_RGB, 32u, &bmi); uchar *result = new uchar[bmi.bmiHeader.biSizeImage]; - if (!GetDIBits(hdc, bitmap, 0, height, result, &bmi, DIB_RGB_COLORS)) { + if (!GetDIBits(hdc, bitmap, 0, UINT(height), result, &bmi, DIB_RGB_COLORS)) { delete [] result; qErrnoWarning("%s: GetDIBits() failed to get bitmap bits.", __FUNCTION__); - return 0; + return nullptr; } return result; } @@ -98,18 +135,92 @@ static inline void copyImageDataCreateAlpha(const uchar *data, QImage *target) } } -static inline void copyImageData(const uchar *data, QImage *target) +// Flip RGB triplets from DIB to QImage formats. Scan lines are padded to 32bit +// both in QImage and DIB. +static inline void flipRgb3(uchar *p, int width, int height) { - const int height = target->height(); - const int bytesPerLine = target->bytesPerLine(); + const int lineSize = 3 * width; + const int linePad = pad4(lineSize) - lineSize; for (int y = 0; y < height; ++y) { - void *dest = static_cast<void *>(target->scanLine(y)); - const void *src = data + y * bytesPerLine; - memcpy(dest, src, bytesPerLine); + uchar *end = p + lineSize; + for ( ; p < end; p += 3) + std::swap(*p, *(p + 2)); + p += linePad; + } +} + +static inline RGBQUAD qRgbToRgbQuad(QRgb qrgb) +{ + RGBQUAD result = {BYTE(qBlue(qrgb)), BYTE(qGreen(qrgb)), BYTE(qRed(qrgb)), 0}; + return result; +} + +static inline QRgb rgbQuadToQRgb(RGBQUAD quad) +{ + return QRgb(quad.rgbBlue) + (QRgb(quad.rgbGreen) << 8) + (QRgb(quad.rgbRed) << 16) + + 0xff000000; +} + +// Helper for imageFromWinHBITMAP_*(), create image in desired format +static QImage copyImageData(const BITMAPINFOHEADER &header, const RGBQUAD *colorTableIn, + const void *data, QImage::Format format) +{ + const QSize size = QSize(header.biWidth, qAbs(header.biHeight)); + QImage image(size, format); + + int colorTableSize = 0; + switch (format) { + case QImage::Format_Mono: + colorTableSize = 2; + break; + case QImage::Format_Indexed8: + colorTableSize = Indexed8ColorTableSize; + break; + default: + break; + } + if (colorTableSize) { + Q_ASSERT(colorTableIn); + QVector<QRgb> colorTable; + colorTable.reserve(colorTableSize); + std::transform(colorTableIn, colorTableIn + colorTableSize, + std::back_inserter(colorTable), rgbQuadToQRgb); + image.setColorTable(colorTable); } + switch (header.biBitCount) { + case 32: + copyImageDataCreateAlpha(static_cast<const uchar *>(data), &image); + break; + case 1: + case 8: + case 16: + case 24: + Q_ASSERT(DWORD(image.sizeInBytes()) == header.biSizeImage); + memcpy(image.bits(), data, header.biSizeImage); + if (format == QImage::Format_RGB888) + image = image.rgbSwapped(); + break; + default: + Q_UNREACHABLE(); + break; + } + return image; } +class DisplayHdc +{ + Q_DISABLE_COPY(DisplayHdc) +public: + DisplayHdc() : m_displayDc(GetDC(nullptr)) {} + ~DisplayHdc() { ReleaseDC(nullptr, m_displayDc); } + + operator HDC() const { return m_displayDc; } + +private: + const HDC m_displayDc; +}; + enum HBitmapFormat { HBitmapNoAlpha, @@ -123,108 +234,229 @@ Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap) const int w = bm.width(); const int h = bm.height(); const int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment - QScopedArrayPointer<uchar> bits(new uchar[bpl * h]); + QScopedArrayPointer<uchar> bits(new uchar[size_t(bpl * h)]); bm.invertPixels(); for (int y = 0; y < h; ++y) - memcpy(bits.data() + y * bpl, bm.constScanLine(y), bpl); + memcpy(bits.data() + y * bpl, bm.constScanLine(y), size_t(bpl)); HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits.data()); return hbm; } -Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0) +static inline QImage::Format format32(int hbitmapFormat) { - if (p.isNull()) - return 0; - - HBITMAP bitmap = 0; - if (p.handle()->classId() != QPlatformPixmap::RasterClass) { - QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ? - QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType); - data->fromImage(p.toImage(), Qt::AutoColor); - return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat); + switch (hbitmapFormat) { + case HBitmapNoAlpha: + return QImage::Format_RGB32; + case HBitmapAlpha: + return QImage::Format_ARGB32; + default: + break; } + return QImage::Format_ARGB32_Premultiplied; +} - QRasterPlatformPixmap *d = static_cast<QRasterPlatformPixmap*>(p.handle()); - const QImage *rasterImage = d->buffer(); - const int w = rasterImage->width(); - const int h = rasterImage->height(); - - HDC display_dc = GetDC(0); +Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapFormat = 0) +{ + if (imageIn.isNull()) + return nullptr; // Define the header - BITMAPINFO bmi; - initBitMapInfo(w, h, true, &bmi); + DWORD compression = 0; + DWORD bitCount = 0; + + // Copy over the data + QImage image = imageIn; + switch (image.format()) { + case QImage::Format_Mono: + bitCount = 1u; + break; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: { + compression = BI_RGB; + bitCount = 32u; + const QImage::Format targetFormat = format32(hbitmapFormat); + if (targetFormat != image.format()) + image = image.convertToFormat(targetFormat); + } + break; + case QImage::Format_RGB888: + compression = BI_RGB; + bitCount = 24u; + break; + case QImage::Format_Indexed8: + bitCount = 8u; + break; + case QImage::Format_RGB555: + bitCount = 16u; + break; + default: { + QImage::Format fallbackFormat = QImage::Format_ARGB32_Premultiplied; + switch (image.format()) { // Convert to a suitable format. + case QImage::Format_MonoLSB: + fallbackFormat = QImage::Format_Mono; + break; + case QImage::Format_RGB16: + fallbackFormat = QImage::Format_RGB555; + break; + case QImage::Format_Grayscale8: + fallbackFormat = QImage::Format_Indexed8; + break; + default: + break; + } // switch conversion format + return qt_imageToWinHBITMAP(imageIn.convertToFormat(fallbackFormat), hbitmapFormat); + } + } + + const int w = image.width(); + const int h = image.height(); + + BITMAPINFO_COLORTABLE256 bmiColorTable256; + initBitMapInfo(w, h, true, compression, bitCount, &bmiColorTable256); + BITMAPINFO &bmi = reinterpret_cast<BITMAPINFO &>(bmiColorTable256); + switch (image.format()) { + case QImage::Format_Mono: // Color table with 2 entries + case QImage::Format_Indexed8: + std::transform(image.colorTable().constBegin(), image.colorTable().constEnd(), + bmiColorTable256.bmiColors, qRgbToRgbQuad); + break; + default: + break; + } // Create the pixmap - uchar *pixels = 0; - bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0); - ReleaseDC(0, display_dc); + uchar *pixels = nullptr; + const HBITMAP bitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, + reinterpret_cast<void **>(&pixels), nullptr, 0); if (!bitmap) { qErrnoWarning("%s, failed to create dibsection", __FUNCTION__); - return 0; + return nullptr; } if (!pixels) { qErrnoWarning("%s, did not allocate pixel data", __FUNCTION__); - return 0; + return nullptr; } - - // Copy over the data - QImage::Format imageFormat = QImage::Format_RGB32; - if (hbitmapFormat == HBitmapAlpha) - imageFormat = QImage::Format_ARGB32; - else if (hbitmapFormat == HBitmapPremultipliedAlpha) - imageFormat = QImage::Format_ARGB32_Premultiplied; - const QImage image = rasterImage->convertToFormat(imageFormat); - const int bytes_per_line = w * 4; - for (int y=0; y < h; ++y) - memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line); - + memcpy(pixels, image.constBits(), bmi.bmiHeader.biSizeImage); + if (image.format() == QImage::Format_RGB888) + flipRgb3(pixels, w, h); return bitmap; } +Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0) +{ + if (p.isNull()) + return nullptr; -Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) + QPlatformPixmap *platformPixmap = p.handle(); + if (platformPixmap->classId() != QPlatformPixmap::RasterClass) { + QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ? + QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType); + data->fromImage(p.toImage(), Qt::AutoColor); + return qt_pixmapToWinHBITMAP(QPixmap(data), hbitmapFormat); + } + + return qt_imageToWinHBITMAP(*static_cast<QRasterPlatformPixmap*>(platformPixmap)->buffer(), hbitmapFormat); +} + +static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header, int hbitmapFormat) { - // Verify size - BITMAP bitmap_info; - memset(&bitmap_info, 0, sizeof(BITMAP)); + QImage::Format result = QImage::Format_Invalid; + switch (header.biBitCount) { + case 32: + result = hbitmapFormat == HBitmapNoAlpha + ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; + break; + case 24: + result = QImage::Format_RGB888; + break; + case 16: + result = QImage::Format_RGB555; + break; + case 8: + result = QImage::Format_Indexed8; + break; + case 1: + result = QImage::Format_Mono; + break; + } + return result; +} - const int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info); - if (!res) { - qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info"); - return QPixmap(); +// Fast path for creating a QImage directly from a HBITMAP created by CreateDIBSection(), +// not requiring memory allocation. +static QImage imageFromWinHBITMAP_DibSection(HBITMAP bitmap, int hbitmapFormat) +{ + DIBSECTION dibSection; + memset(&dibSection, 0, sizeof(dibSection)); + dibSection.dsBmih.biSize = sizeof(dibSection.dsBmih); + + if (!GetObject(bitmap, sizeof(dibSection), &dibSection) + || !dibSection.dsBm.bmBits + || dibSection.dsBmih.biBitCount <= 8 // Cannot access the color table for Indexed8, Mono + || dibSection.dsBmih.biCompression != BI_RGB) { + return QImage(); } - const int w = bitmap_info.bmWidth; - const int h = bitmap_info.bmHeight; - - // Get bitmap bits - HDC display_dc = GetDC(0); - QScopedArrayPointer<uchar> data(getDiBits(display_dc, bitmap, w, h, true)); - if (data.isNull()) { - ReleaseDC(0, display_dc); - return QPixmap(); + + const QImage::Format imageFormat = imageFromWinHBITMAP_Format(dibSection.dsBmih, hbitmapFormat); + if (imageFormat == QImage::Format_Invalid) + return QImage(); + + return copyImageData(dibSection.dsBmih, nullptr, dibSection.dsBm.bmBits, imageFormat); +} + +// Create QImage from a HBITMAP using GetDIBits(), potentially with conversion. +static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int hbitmapFormat) +{ + BITMAPINFO_COLORTABLE256 bmiColorTable256; + BITMAPINFO &info = reinterpret_cast<BITMAPINFO &>(bmiColorTable256); + memset(&info, 0, sizeof(info)); + info.bmiHeader.biSize = sizeof(info.bmiHeader); + + DisplayHdc displayDc; + if (!GetDIBits(displayDc, bitmap, 0, 1, 0, &info, DIB_RGB_COLORS)) { + qErrnoWarning("%s: GetDIBits() failed to query data.", __FUNCTION__); + return QImage(); } - const QImage::Format imageFormat = hbitmapFormat == HBitmapNoAlpha ? - QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; + if (info.bmiHeader.biHeight > 0) // Force top-down + info.bmiHeader.biHeight = -info.bmiHeader.biHeight; + info.bmiHeader.biCompression = BI_RGB; // Extract using no compression (can be BI_BITFIELD) + if (forceQuads) + info.bmiHeader.biBitCount = 32; - // Create image and copy data into image. - QImage image(w, h, imageFormat); - if (image.isNull()) { // failed to alloc? - ReleaseDC(0, display_dc); - qWarning("%s, failed create image of %dx%d", __FUNCTION__, w, h); - return QPixmap(); + const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat); + if (imageFormat == QImage::Format_Invalid) { + qWarning().nospace() << __FUNCTION__ << ": unsupported image format:" << info.bmiHeader; + return QImage(); } - copyImageDataCreateAlpha(data.data(), &image); - ReleaseDC(0, display_dc); - return QPixmap::fromImage(image); + + QScopedPointer<uchar> data(new uchar[info.bmiHeader.biSizeImage]); + if (!GetDIBits(displayDc, bitmap, 0, qAbs(info.bmiHeader.biHeight), data.data(), &info, DIB_RGB_COLORS)) { + qErrnoWarning("%s: GetDIBits() failed to get data.", __FUNCTION__); + return QImage(); + } + return copyImageData(info.bmiHeader, bmiColorTable256.bmiColors, data.data(), imageFormat); } +Q_GUI_EXPORT QImage qt_imageFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) +{ + QImage result = imageFromWinHBITMAP_DibSection(bitmap, hbitmapFormat); + if (result.isNull()) + result = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ false, hbitmapFormat); + return result; +} + +Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0) +{ + const QImage image = imageFromWinHBITMAP_GetDiBits(bitmap, /* forceQuads */ true, hbitmapFormat); + return QPixmap::fromImage(image); +} Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p) { if (p.isNull()) - return 0; + return nullptr; QBitmap maskBitmap = p.mask(); if (maskBitmap.isNull()) { @@ -267,7 +499,7 @@ static QImage qt_imageFromWinIconHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) QScopedArrayPointer<uchar> data(getDiBits(hdc, bitmap, w, h, true)); if (data.isNull()) return QImage(); - copyImageData(data.data(), &image); + memcpy(image.bits(), data.data(), size_t(image.sizeInBytes())); return image; } @@ -287,9 +519,9 @@ static inline bool hasAlpha(const QImage &image) Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon) { - HDC screenDevice = GetDC(0); + HDC screenDevice = GetDC(nullptr); HDC hdc = CreateCompatibleDC(screenDevice); - ReleaseDC(0, screenDevice); + ReleaseDC(nullptr, screenDevice); ICONINFO iconinfo; const bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center @@ -299,25 +531,27 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon) return QPixmap(); } - const int w = iconinfo.xHotspot * 2; - const int h = iconinfo.yHotspot * 2; + const int w = int(iconinfo.xHotspot) * 2; + const int h = int(iconinfo.yHotspot) * 2; BITMAPINFOHEADER bitmapInfo; - initBitMapInfoHeader(w, h, false, &bitmapInfo); + initBitMapInfoHeader(w, h, false, BI_RGB, 32u, &bitmapInfo); DWORD* bits; - HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0); - HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap); - DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL); + HBITMAP winBitmap = CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO *>(&bitmapInfo), + DIB_RGB_COLORS, reinterpret_cast<VOID **>(&bits), + nullptr, 0); + HGDIOBJ oldhdc = static_cast<HBITMAP>(SelectObject(hdc, winBitmap)); + DrawIconEx(hdc, 0, 0, icon, w, h, 0, nullptr, DI_NORMAL); QImage image = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h); if (!image.isNull() && !hasAlpha(image)) { //If no alpha was found, we use the mask to set alpha values - DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK); + DrawIconEx( hdc, 0, 0, icon, w, h, 0, nullptr, DI_MASK); const QImage mask = qt_imageFromWinIconHBITMAP(hdc, winBitmap, w, h); for (int y = 0 ; y < h ; y++){ QRgb *scanlineImage = reinterpret_cast<QRgb *>(image.scanLine(y)); - const QRgb *scanlineMask = mask.isNull() ? 0 : reinterpret_cast<const QRgb *>(mask.scanLine(y)); + const QRgb *scanlineMask = mask.isNull() ? nullptr : reinterpret_cast<const QRgb *>(mask.scanLine(y)); for (int x = 0; x < w ; x++){ if (scanlineMask && qRed(scanlineMask[x]) != 0) scanlineImage[x] = 0; //mask out this pixel diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp index 4b8b1203d6..3d1652f68b 100644 --- a/src/gui/image/qpixmapcache.cpp +++ b/src/gui/image/qpixmapcache.cpp @@ -86,7 +86,17 @@ QT_BEGIN_NAMESPACE \sa QCache, QPixmap */ -static int cache_limit = 10240; // 10 MB cache limit +static const int cache_limit_default = 10240; // 10 MB cache limit + +static inline int cost(const QPixmap &pixmap) +{ + // make sure to do a 64bit calculation + const qint64 costKb = static_cast<qint64>(pixmap.width()) * + pixmap.height() * pixmap.depth() / (8 * 1024); + const qint64 costMax = std::numeric_limits<int>::max(); + // a small pixmap should have at least a cost of 1(kb) + return static_cast<int>(qBound(1LL, costKb, costMax)); +} /*! \class QPixmapCache::Key @@ -237,7 +247,7 @@ uint qHash(const QPixmapCache::Key &k) QPMCache::QPMCache() : QObject(0), - QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit * 1024), + QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit_default), keyArray(0), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false) { } @@ -553,7 +563,7 @@ bool QPixmapCache::find(const Key &key, QPixmap* pixmap) bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap) { - return pm_cache()->insert(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8); + return pm_cache()->insert(key, pixmap, cost(pixmap)); } /*! @@ -573,7 +583,7 @@ bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap) */ QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap) { - return pm_cache()->insert(pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8); + return pm_cache()->insert(pixmap, cost(pixmap)); } /*! @@ -590,7 +600,7 @@ bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap) //The key is not valid anymore, a flush happened before probably if (!key.d || !key.d->isValid) return false; - return pm_cache()->replace(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8); + return pm_cache()->replace(key, pixmap, cost(pixmap)); } /*! @@ -603,7 +613,7 @@ bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap) int QPixmapCache::cacheLimit() { - return cache_limit; + return pm_cache()->maxCost(); } /*! @@ -616,8 +626,7 @@ int QPixmapCache::cacheLimit() void QPixmapCache::setCacheLimit(int n) { - cache_limit = n; - pm_cache()->setMaxCost(1024 * cache_limit); + pm_cache()->setMaxCost(n); } /*! diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 42b9e71087..94479b4f8e 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -352,6 +352,24 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal ); i++; } + // Qt==ARGB==Big(ARGB)==Little(BGRA) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + png_set_bgr(png_ptr); + } + } else if (bit_depth == 16 && (color_type & PNG_COLOR_MASK_COLOR)) { + QImage::Format format = QImage::Format_RGBA64; + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER); + format = QImage::Format_RGBX64; + } + if (image.size() != QSize(width, height) || image.format() != format) { + image = QImage(width, height, format); + if (image.isNull()) + return; + } + png_read_update_info(png_ptr, info_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + png_set_swap(png_ptr); } else { // 32-bit if (bit_depth == 16) @@ -388,12 +406,12 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal if (QSysInfo::ByteOrder == QSysInfo::BigEndian) png_set_swap_alpha(png_ptr); - png_read_update_info(png_ptr, info_ptr); - } + // Qt==ARGB==Big(ARGB)==Little(BGRA) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + png_set_bgr(png_ptr); + } - // Qt==ARGB==Big(ARGB)==Little(BGRA) - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { - png_set_bgr(png_ptr); + png_read_update_info(png_ptr, info_ptr); } } @@ -678,6 +696,10 @@ QImage::Format QPngHandlerPrivate::readImageFormat() { // 1-bit and 8-bit color format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8; + } else if (bit_depth == 16 && (color_type & PNG_COLOR_MASK_COLOR)) { + format = QImage::Format_RGBA64; + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + format = QImage::Format_RGBX64; } else { // 32-bit format = QImage::Format_ARGB32; @@ -843,8 +865,24 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c else color_type = PNG_COLOR_TYPE_RGB; + int bpc = 0; + switch (image.format()) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + bpc = 1; + break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + bpc = 16; + break; + default: + bpc = 8; + break; + } + png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), - image.depth() == 1 ? 1 : 8, // per channel + bpc, // per channel color_type, 0, 0, 0); // sets #channels if (gamma != 0.0) { @@ -880,13 +918,31 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c // Swap ARGB to RGBA (normal PNG format) before saving on // BigEndian machines if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - png_set_swap_alpha(png_ptr); + switch (image.format()) { + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + break; + default: + png_set_swap_alpha(png_ptr); + } } // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian - && image.format() != QImage::Format_RGB888) { - png_set_bgr(png_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + switch (image.format()) { + case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + break; + default: + png_set_bgr(png_ptr); + } } if (off_x || off_y) { @@ -909,10 +965,32 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c if (image.depth() != 1) png_set_packing(png_ptr); - if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888) - png_set_filler(png_ptr, 0, - QSysInfo::ByteOrder == QSysInfo::BigEndian ? - PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_RGB) { + switch (image.format()) { + case QImage::Format_RGB888: + break; + case QImage::Format_RGBX8888: + case QImage::Format_RGBX64: + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + break; + default: + png_set_filler(png_ptr, 0, + QSysInfo::ByteOrder == QSysInfo::BigEndian ? + PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + } + } + + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + switch (image.format()) { + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + png_set_swap(png_ptr); + break; + default: + break; + } + } if (looping >= 0 && frames_written == 0) { uchar data[13] = "NETSCAPE2.0"; @@ -940,6 +1018,10 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: { png_bytep* row_pointers = new png_bytep[height]; for (int y=0; y<height; y++) @@ -948,6 +1030,17 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c delete [] row_pointers; } break; + case QImage::Format_RGBA64_Premultiplied: + { + QImage row; + png_bytep row_pointers[1]; + for (int y=0; y<height; y++) { + row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64); + row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0)); + png_write_rows(png_ptr, row_pointers, 1); + } + } + break; default: { QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32; diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp index 155a4f88b4..24d86e116d 100644 --- a/src/gui/image/qxbmhandler.cpp +++ b/src/gui/image/qxbmhandler.cpp @@ -241,7 +241,7 @@ static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const } } } -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#ifdef Q_CC_MSVC strcpy_s(p, sizeof(" };\n"), " };\n"); #else strcpy(p, " };\n"); diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index 9c54b9ada4..17272ffe69 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -741,10 +741,6 @@ static const struct XPMRGBData { { QRGB(139,139, 0), "yellow4" }, { QRGB(154,205, 50), "yellowgreen" } }; -#if defined(Q_CC_MSVC) && _MSC_VER < 1600 -inline bool operator<(const XPMRGBData &data1, const XPMRGBData &data2) -{ return qstrcmp(data1.name, data2.name) < 0; } -#endif inline bool operator<(const char *name, const XPMRGBData &data) { return qstrcmp(name, data.name) < 0; } |