qt6-bb10/src/gui/image/qimage_p.h

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