410 lines
14 KiB
C++
410 lines
14 KiB
C++
// Copyright (C) 2021 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#ifndef QIMAGE_P_H
|
|
#define QIMAGE_P_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.
|
|
//
|
|
|
|
#include <QtGui/private/qtguiglobal_p.h>
|
|
#include <QtGui/qcolorspace.h>
|
|
#include <QtGui/qimage.h>
|
|
#include <QtCore/private/qnumeric_p.h>
|
|
#include <QtCore/qlist.h>
|
|
#include <QtCore/qmap.h>
|
|
#include <QtCore/qttypetraits.h>
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
class QImageWriter;
|
|
|
|
struct Q_GUI_EXPORT QImageData { // internal image data
|
|
QImageData();
|
|
~QImageData();
|
|
static QImageData *create(const QSize &size, QImage::Format format);
|
|
static QImageData *create(uchar *data, int w, int h, qsizetype bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
|
|
|
|
static QImageData *get(QImage &img) noexcept { return img.d; }
|
|
static const QImageData *get(const QImage &img) noexcept { return img.d; }
|
|
|
|
QAtomicInt ref;
|
|
|
|
int width;
|
|
int height;
|
|
int depth;
|
|
qsizetype nbytes; // number of bytes data
|
|
qreal devicePixelRatio;
|
|
QList<QRgb> colortable;
|
|
uchar *data;
|
|
QImage::Format format;
|
|
qsizetype bytes_per_line;
|
|
int ser_no; // serial number
|
|
int detach_no;
|
|
|
|
qreal dpmx; // dots per meter X (or 0)
|
|
qreal dpmy; // dots per meter Y (or 0)
|
|
QPoint offset; // offset in pixels
|
|
|
|
uint own_data : 1;
|
|
uint ro_data : 1;
|
|
uint has_alpha_clut : 1;
|
|
uint is_cached : 1;
|
|
|
|
QImageCleanupFunction cleanupFunction;
|
|
void* cleanupInfo;
|
|
|
|
bool checkForAlphaPixels() const;
|
|
|
|
// Convert the image in-place, minimizing memory reallocation
|
|
// Return false if the conversion cannot be done in-place.
|
|
bool convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags);
|
|
|
|
QMap<QString, QString> text;
|
|
|
|
bool doImageIO(const QImage *image, QImageWriter* io, int quality) const;
|
|
|
|
QPaintEngine *paintEngine;
|
|
|
|
QColorSpace colorSpace;
|
|
|
|
struct ImageSizeParameters {
|
|
qsizetype bytesPerLine;
|
|
qsizetype totalSize;
|
|
bool isValid() const { return bytesPerLine > 0 && totalSize > 0; }
|
|
};
|
|
static ImageSizeParameters calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth);
|
|
};
|
|
|
|
inline QImageData::ImageSizeParameters
|
|
QImageData::calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth)
|
|
{
|
|
ImageSizeParameters invalid = { -1, -1 };
|
|
if (height <= 0)
|
|
return invalid;
|
|
|
|
// calculate the size, taking care of overflows
|
|
qsizetype bytes_per_line;
|
|
if (qMulOverflow(width, depth, &bytes_per_line))
|
|
return invalid;
|
|
if (qAddOverflow(bytes_per_line, qsizetype(31), &bytes_per_line))
|
|
return invalid;
|
|
// bytes per scanline (must be multiple of 4)
|
|
bytes_per_line = (bytes_per_line >> 5) << 2; // can't overflow
|
|
|
|
qsizetype total_size;
|
|
if (qMulOverflow(height, bytes_per_line, &total_size))
|
|
return invalid;
|
|
qsizetype dummy;
|
|
if (qMulOverflow(height, qsizetype(sizeof(uchar *)), &dummy))
|
|
return invalid; // why is this here?
|
|
#if 1 || QT_VERSION < QT_VERSION_CHECK(6,0,0) // ### can only fix this if QImage dimensions are not int anymore
|
|
// Disallow images where width * depth calculations might overflow
|
|
if (width > (INT_MAX - 31) / depth)
|
|
return invalid;
|
|
#endif
|
|
|
|
return { bytes_per_line, total_size };
|
|
}
|
|
|
|
typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
|
|
typedef bool (*InPlace_Image_Converter)(QImageData *data, Qt::ImageConversionFlags);
|
|
|
|
extern Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats];
|
|
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_over_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
|
|
bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags);
|
|
bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags);
|
|
#if QT_CONFIG(raster_fp)
|
|
void convert_generic_over_rgba32f(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
|
|
bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags);
|
|
#endif
|
|
|
|
void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha);
|
|
|
|
const uchar *qt_get_bitflip_array();
|
|
Q_GUI_EXPORT void qGamma_correct_back_to_linear_cs(QImage *image);
|
|
|
|
#if defined(_M_ARM) && defined(_MSC_VER) // QTBUG-42038
|
|
#pragma optimize("", off)
|
|
#endif
|
|
inline int qt_depthForFormat(QImage::Format format)
|
|
{
|
|
int depth = 0;
|
|
switch(format) {
|
|
case QImage::Format_Invalid:
|
|
case QImage::NImageFormats:
|
|
Q_UNREACHABLE();
|
|
case QImage::Format_Mono:
|
|
case QImage::Format_MonoLSB:
|
|
depth = 1;
|
|
break;
|
|
case QImage::Format_Indexed8:
|
|
case QImage::Format_Alpha8:
|
|
case QImage::Format_Grayscale8:
|
|
depth = 8;
|
|
break;
|
|
case QImage::Format_RGB32:
|
|
case QImage::Format_ARGB32:
|
|
case QImage::Format_ARGB32_Premultiplied:
|
|
case QImage::Format_RGBX8888:
|
|
case QImage::Format_RGBA8888:
|
|
case QImage::Format_RGBA8888_Premultiplied:
|
|
case QImage::Format_BGR30:
|
|
case QImage::Format_A2BGR30_Premultiplied:
|
|
case QImage::Format_RGB30:
|
|
case QImage::Format_A2RGB30_Premultiplied:
|
|
depth = 32;
|
|
break;
|
|
case QImage::Format_RGB555:
|
|
case QImage::Format_RGB16:
|
|
case QImage::Format_RGB444:
|
|
case QImage::Format_ARGB4444_Premultiplied:
|
|
case QImage::Format_Grayscale16:
|
|
depth = 16;
|
|
break;
|
|
case QImage::Format_RGB666:
|
|
case QImage::Format_ARGB6666_Premultiplied:
|
|
case QImage::Format_ARGB8565_Premultiplied:
|
|
case QImage::Format_ARGB8555_Premultiplied:
|
|
case QImage::Format_RGB888:
|
|
case QImage::Format_BGR888:
|
|
depth = 24;
|
|
break;
|
|
case QImage::Format_RGBX64:
|
|
case QImage::Format_RGBA64:
|
|
case QImage::Format_RGBA64_Premultiplied:
|
|
case QImage::Format_RGBX16FPx4:
|
|
case QImage::Format_RGBA16FPx4:
|
|
case QImage::Format_RGBA16FPx4_Premultiplied:
|
|
depth = 64;
|
|
break;
|
|
case QImage::Format_RGBX32FPx4:
|
|
case QImage::Format_RGBA32FPx4:
|
|
case QImage::Format_RGBA32FPx4_Premultiplied:
|
|
depth = 128;
|
|
break;
|
|
}
|
|
return depth;
|
|
}
|
|
|
|
#if defined(_M_ARM) && defined(_MSC_VER)
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
inline QImage::Format qt_opaqueVersion(QImage::Format format)
|
|
{
|
|
switch (format) {
|
|
case QImage::Format_ARGB8565_Premultiplied:
|
|
return QImage::Format_RGB16;
|
|
case QImage::Format_ARGB8555_Premultiplied:
|
|
return QImage::Format_RGB555;
|
|
case QImage::Format_ARGB6666_Premultiplied:
|
|
return QImage::Format_RGB666;
|
|
case QImage::Format_ARGB4444_Premultiplied:
|
|
return QImage::Format_RGB444;
|
|
case QImage::Format_RGBA8888:
|
|
case QImage::Format_RGBA8888_Premultiplied:
|
|
return QImage::Format_RGBX8888;
|
|
case QImage::Format_A2BGR30_Premultiplied:
|
|
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_RGBA16FPx4:
|
|
case QImage::Format_RGBA16FPx4_Premultiplied:
|
|
return QImage::Format_RGBX16FPx4;
|
|
case QImage::Format_RGBA32FPx4:
|
|
case QImage::Format_RGBA32FPx4_Premultiplied:
|
|
return QImage::Format_RGBX32FPx4;
|
|
case QImage::Format_ARGB32_Premultiplied:
|
|
case QImage::Format_ARGB32:
|
|
return QImage::Format_RGB32;
|
|
case QImage::Format_RGB16:
|
|
case QImage::Format_RGB32:
|
|
case QImage::Format_RGB444:
|
|
case QImage::Format_RGB555:
|
|
case QImage::Format_RGB666:
|
|
case QImage::Format_RGB888:
|
|
case QImage::Format_BGR888:
|
|
case QImage::Format_RGBX8888:
|
|
case QImage::Format_BGR30:
|
|
case QImage::Format_RGB30:
|
|
case QImage::Format_RGBX64:
|
|
case QImage::Format_RGBX16FPx4:
|
|
case QImage::Format_RGBX32FPx4:
|
|
case QImage::Format_Grayscale8:
|
|
case QImage::Format_Grayscale16:
|
|
return format;
|
|
case QImage::Format_Mono:
|
|
case QImage::Format_MonoLSB:
|
|
case QImage::Format_Indexed8:
|
|
case QImage::Format_Alpha8:
|
|
case QImage::Format_Invalid:
|
|
case QImage::NImageFormats:
|
|
break;
|
|
}
|
|
return QImage::Format_RGB32;
|
|
}
|
|
|
|
inline QImage::Format qt_alphaVersion(QImage::Format format)
|
|
{
|
|
switch (format) {
|
|
case QImage::Format_RGB32:
|
|
case QImage::Format_ARGB32:
|
|
return QImage::Format_ARGB32_Premultiplied;
|
|
case QImage::Format_RGB16:
|
|
return QImage::Format_ARGB8565_Premultiplied;
|
|
case QImage::Format_RGB555:
|
|
return QImage::Format_ARGB8555_Premultiplied;
|
|
case QImage::Format_RGB666:
|
|
return QImage::Format_ARGB6666_Premultiplied;
|
|
case QImage::Format_RGB444:
|
|
return QImage::Format_ARGB4444_Premultiplied;
|
|
case QImage::Format_RGBX8888:
|
|
case QImage::Format_RGBA8888:
|
|
return QImage::Format_RGBA8888_Premultiplied;
|
|
case QImage::Format_BGR30:
|
|
return QImage::Format_A2BGR30_Premultiplied;
|
|
case QImage::Format_RGB30:
|
|
return QImage::Format_A2RGB30_Premultiplied;
|
|
case QImage::Format_RGBX64:
|
|
case QImage::Format_RGBA64:
|
|
case QImage::Format_Grayscale16:
|
|
return QImage::Format_RGBA64_Premultiplied;
|
|
case QImage::Format_RGBX16FPx4:
|
|
case QImage::Format_RGBA16FPx4:
|
|
return QImage::Format_RGBA16FPx4_Premultiplied;
|
|
case QImage::Format_RGBX32FPx4:
|
|
case QImage::Format_RGBA32FPx4:
|
|
return QImage::Format_RGBA32FPx4_Premultiplied;
|
|
case QImage::Format_ARGB32_Premultiplied:
|
|
case QImage::Format_ARGB8565_Premultiplied:
|
|
case QImage::Format_ARGB8555_Premultiplied:
|
|
case QImage::Format_ARGB6666_Premultiplied:
|
|
case QImage::Format_ARGB4444_Premultiplied:
|
|
case QImage::Format_RGBA8888_Premultiplied:
|
|
case QImage::Format_A2BGR30_Premultiplied:
|
|
case QImage::Format_A2RGB30_Premultiplied:
|
|
case QImage::Format_RGBA64_Premultiplied:
|
|
case QImage::Format_RGBA16FPx4_Premultiplied:
|
|
case QImage::Format_RGBA32FPx4_Premultiplied:
|
|
return format;
|
|
case QImage::Format_Mono:
|
|
case QImage::Format_MonoLSB:
|
|
case QImage::Format_Indexed8:
|
|
case QImage::Format_RGB888:
|
|
case QImage::Format_BGR888:
|
|
case QImage::Format_Alpha8:
|
|
case QImage::Format_Grayscale8:
|
|
case QImage::Format_Invalid:
|
|
case QImage::NImageFormats:
|
|
break;
|
|
}
|
|
return QImage::Format_ARGB32_Premultiplied;
|
|
}
|
|
|
|
constexpr QImage::Format qt_toUnpremultipliedFormat(QImage::Format format)
|
|
{
|
|
// Assumes input is already a premultiplied format with an unpremultiplied counterpart
|
|
// This abuses the fact unpremultiplied formats are always before their premultiplied counterparts.
|
|
return static_cast<QImage::Format>(qToUnderlying(format) - 1);
|
|
}
|
|
|
|
constexpr QImage::Format qt_toPremultipliedFormat(QImage::Format format)
|
|
{
|
|
// Assumes input is already an unpremultiplied format
|
|
// This abuses the fact unpremultiplied formats are always before their premultiplied counterparts.
|
|
return static_cast<QImage::Format>(qToUnderlying(format) + 1);
|
|
}
|
|
|
|
inline bool qt_highColorPrecision(QImage::Format format, bool opaque = false)
|
|
{
|
|
// Formats with higher color precision than ARGB32_Premultiplied.
|
|
switch (format) {
|
|
case QImage::Format_ARGB32:
|
|
case QImage::Format_RGBA8888:
|
|
return !opaque;
|
|
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:
|
|
case QImage::Format_Grayscale16:
|
|
case QImage::Format_RGBX16FPx4:
|
|
case QImage::Format_RGBA16FPx4:
|
|
case QImage::Format_RGBA16FPx4_Premultiplied:
|
|
case QImage::Format_RGBX32FPx4:
|
|
case QImage::Format_RGBA32FPx4:
|
|
case QImage::Format_RGBA32FPx4_Premultiplied:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline bool qt_fpColorPrecision(QImage::Format format)
|
|
{
|
|
switch (format) {
|
|
case QImage::Format_RGBX16FPx4:
|
|
case QImage::Format_RGBA16FPx4:
|
|
case QImage::Format_RGBA16FPx4_Premultiplied:
|
|
case QImage::Format_RGBX32FPx4:
|
|
case QImage::Format_RGBA32FPx4:
|
|
case QImage::Format_RGBA32FPx4_Premultiplied:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline QImage::Format qt_maybeAlphaVersionWithSameDepth(QImage::Format format)
|
|
{
|
|
const QImage::Format toFormat = qt_alphaVersion(format);
|
|
return qt_depthForFormat(format) == qt_depthForFormat(toFormat) ? toFormat : format;
|
|
}
|
|
|
|
inline QImage::Format qt_opaqueVersionForPainting(QImage::Format format)
|
|
{
|
|
QImage::Format toFormat = qt_opaqueVersion(format);
|
|
// If we are switching depth anyway upgrade to RGB32
|
|
if (qt_depthForFormat(format) != qt_depthForFormat(toFormat) && qt_depthForFormat(toFormat) <= 32)
|
|
toFormat = QImage::Format_RGB32;
|
|
return toFormat;
|
|
}
|
|
|
|
inline QImage::Format qt_alphaVersionForPainting(QImage::Format format)
|
|
{
|
|
QImage::Format toFormat = qt_alphaVersion(format);
|
|
#if defined(__ARM_NEON__) || defined(__SSE2__)
|
|
// If we are switching depth anyway and we have optimized ARGB32PM routines, upgrade to that.
|
|
if (qt_depthForFormat(format) != qt_depthForFormat(toFormat) && qt_depthForFormat(toFormat) <= 32)
|
|
toFormat = QImage::Format_ARGB32_Premultiplied;
|
|
#endif
|
|
return toFormat;
|
|
}
|
|
|
|
Q_GUI_EXPORT QMap<QString, QString> qt_getImageText(const QImage &image, const QString &description);
|
|
Q_GUI_EXPORT QMap<QString, QString> qt_getImageTextFromDescription(const QString &description);
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#endif // QIMAGE_P_H
|