diff options
Diffstat (limited to 'src/gui/painting')
73 files changed, 5702 insertions, 1194 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index a90abed4c0..fcf6488edd 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -8,7 +8,15 @@ HEADERS += \ painting/qbrush.h \ painting/qcolor.h \ painting/qcolor_p.h \ - painting/qcolorprofile_p.h \ + painting/qcolormatrix_p.h \ + painting/qcolorspace.h \ + painting/qcolorspace_p.h \ + painting/qcolortransferfunction_p.h \ + painting/qcolortransfertable_p.h \ + painting/qcolortransform.h \ + painting/qcolortransform_p.h \ + painting/qcolortrc_p.h \ + painting/qcolortrclut_p.h \ painting/qcosmeticstroker_p.h \ painting/qdatabuffer_p.h \ painting/qdrawhelper_p.h \ @@ -17,6 +25,7 @@ HEADERS += \ painting/qemulationpaintengine_p.h \ painting/qfixed_p.h \ painting/qgrayraster_p.h \ + painting/qicc_p.h \ painting/qmatrix.h \ painting/qmemrotate_p.h \ painting/qoutlinemapper_p.h \ @@ -64,12 +73,15 @@ SOURCES += \ painting/qblittable.cpp \ painting/qbrush.cpp \ painting/qcolor.cpp \ - painting/qcolorprofile.cpp \ + painting/qcolorspace.cpp \ + painting/qcolortransform.cpp \ + painting/qcolortrclut.cpp \ painting/qcompositionfunctions.cpp \ painting/qcosmeticstroker.cpp \ painting/qdrawhelper.cpp \ painting/qemulationpaintengine.cpp \ painting/qgrayraster.c \ + painting/qicc.cpp \ painting/qimagescale.cpp \ painting/qmatrix.cpp \ painting/qmemrotate.cpp \ @@ -123,23 +135,41 @@ gcc:equals(QT_GCC_MAJOR_VERSION, 5) { NO_PCH_SOURCES += painting/qdrawhelper.cpp } -SSE2_SOURCES += painting/qdrawhelper_sse2.cpp -SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp -SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \ - painting/qimagescale_sse4.cpp -ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp +!android { + SSE2_SOURCES += painting/qdrawhelper_sse2.cpp + SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp + SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \ + painting/qimagescale_sse4.cpp + ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp -NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp -NEON_HEADERS += painting/qdrawhelper_neon_p.h + NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp + NEON_HEADERS += painting/qdrawhelper_neon_p.h +} !uikit:!win32:contains(QT_ARCH, "arm"): CONFIG += no_clang_integrated_as -!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") { +!android:!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") { NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S DEFINES += ENABLE_PIXMAN_DRAWHELPERS } -MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp -MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h -MIPS_DSP_ASM += painting/qdrawhelper_mips_dsp_asm.S -MIPS_DSPR2_ASM += painting/qdrawhelper_mips_dspr2_asm.S +!android { + MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp + MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h + MIPS_DSP_ASM += painting/qdrawhelper_mips_dsp_asm.S + MIPS_DSPR2_ASM += painting/qdrawhelper_mips_dspr2_asm.S +} else { + # see https://developer.android.com/ndk/guides/abis + x86 | x86_64 { + DEFINES += QT_COMPILER_SUPPORTS_SSE2 QT_COMPILER_SUPPORTS_SSE3 QT_COMPILER_SUPPORTS_SSSE3 + SOURCES += painting/qdrawhelper_sse2.cpp painting/qdrawhelper_ssse3.cpp + } + x86_64 { + DEFINES += QT_COMPILER_SUPPORTS_SSE4_1 QT_COMPILER_SUPPORTS_SSE4_2 + SOURCES += painting/qdrawhelper_sse4.cpp painting/qimagescale_sse4.cpp + } + arm64-v8a { + SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp + HEADERS += painting/qdrawhelper_neon_p.h + } +} include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index 65e6063fe4..7622262da9 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -47,6 +47,8 @@ #include <private/qnumeric_p.h> +#include <tuple> // for std::tie() + QT_BEGIN_NAMESPACE //#define QDEBUG_BEZIER @@ -54,24 +56,6 @@ QT_BEGIN_NAMESPACE /*! \internal */ -QBezier QBezier::fromPoints(const QPointF &p1, const QPointF &p2, - const QPointF &p3, const QPointF &p4) -{ - QBezier b; - b.x1 = p1.x(); - b.y1 = p1.y(); - b.x2 = p2.x(); - b.y2 = p2.y(); - b.x3 = p3.x(); - b.y3 = p3.y(); - b.x4 = p4.x(); - b.y4 = p4.y(); - return b; -} - -/*! - \internal -*/ QPolygonF QBezier::toPolygon(qreal bezier_flattening_threshold) const { // flattening is done by splitting the bezier until we can replace the segment by a straight @@ -145,7 +129,7 @@ void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold --top; } else { // split, second half of the polygon goes lower into the stack - b->split(b+1, b); + std::tie(b[1], b[0]) = b->split(); levels[top + 1] = --levels[top]; ++top; } @@ -181,7 +165,7 @@ void QBezier::addToPolygon(QDataBuffer<QPointF> &polygon, qreal bezier_flattenin --top; } else { // split, second half of the polygon goes lower into the stack - b->split(b+1, b); + std::tie(b[1], b[0]) = b->split(); levels[top + 1] = --levels[top]; ++top; } @@ -436,7 +420,7 @@ redo: o += 2; --b; } else { - b->split(b+1, b); + std::tie(b[1], b[0]) = b->split(); ++b; } } @@ -478,8 +462,6 @@ qreal QBezier::length(qreal error) const void QBezier::addIfClose(qreal *length, qreal error) const { - QBezier left, right; /* bez poly splits */ - qreal len = qreal(0.0); /* arc length */ qreal chord; /* chord length */ @@ -490,9 +472,9 @@ void QBezier::addIfClose(qreal *length, qreal error) const chord = QLineF(QPointF(x1, y1),QPointF(x4, y4)).length(); if((len-chord) > error) { - split(&left, &right); /* split in two */ - left.addIfClose(length, error); /* try left side */ - right.addIfClose(length, error); /* try right side */ + const auto halves = split(); /* split in two */ + halves.first.addIfClose(length, error); /* try left side */ + halves.second.addIfClose(length, error); /* try right side */ return; } diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h index f8a91e9ef3..1c49f82416 100644 --- a/src/gui/painting/qbezier_p.h +++ b/src/gui/painting/qbezier_p.h @@ -69,7 +69,8 @@ class Q_GUI_EXPORT QBezier { public: static QBezier fromPoints(const QPointF &p1, const QPointF &p2, - const QPointF &p3, const QPointF &p4); + const QPointF &p3, const QPointF &p4) + { return {p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y(), p4.x(), p4.y()}; } static void coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d); @@ -106,7 +107,7 @@ public: inline QLineF endTangent() const; inline void parameterSplitLeft(qreal t, QBezier *left); - inline void split(QBezier *firstHalf, QBezier *secondHalf) const; + inline std::pair<QBezier, QBezier> split() const; int shifted(QBezier *curveSegments, int maxSegmets, qreal offset, float threshold) const; @@ -222,28 +223,21 @@ inline QPointF QBezier::secondDerivedAt(qreal t) const a * y1 + b * y2 + c * y3 + d * y4); } -inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const +std::pair<QBezier, QBezier> QBezier::split() const { - Q_ASSERT(firstHalf); - Q_ASSERT(secondHalf); - - qreal c = (x2 + x3)*.5; - firstHalf->x2 = (x1 + x2)*.5; - secondHalf->x3 = (x3 + x4)*.5; - firstHalf->x1 = x1; - secondHalf->x4 = x4; - firstHalf->x3 = (firstHalf->x2 + c)*.5; - secondHalf->x2 = (secondHalf->x3 + c)*.5; - firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5; - - c = (y2 + y3)/2; - firstHalf->y2 = (y1 + y2)*.5; - secondHalf->y3 = (y3 + y4)*.5; - firstHalf->y1 = y1; - secondHalf->y4 = y4; - firstHalf->y3 = (firstHalf->y2 + c)*.5; - secondHalf->y2 = (secondHalf->y3 + c)*.5; - firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5; + const auto mid = [](QPointF lhs, QPointF rhs) { return (lhs + rhs) * .5; }; + + const QPointF mid_12 = mid(pt1(), pt2()); + const QPointF mid_23 = mid(pt2(), pt3()); + const QPointF mid_34 = mid(pt3(), pt4()); + const QPointF mid_12_23 = mid(mid_12, mid_23); + const QPointF mid_23_34 = mid(mid_23, mid_34); + const QPointF mid_12_23__23_34 = mid(mid_12_23, mid_23_34); + + return { + fromPoints(pt1(), mid_12, mid_12_23, mid_12_23__23_34), + fromPoints(mid_12_23__23_34, mid_23_34, mid_34, pt4()), + }; } inline void QBezier::parameterSplitLeft(qreal t, QBezier *left) diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h index 5ea78cdde2..080da98ec4 100644 --- a/src/gui/painting/qblendfunctions_p.h +++ b/src/gui/painting/qblendfunctions_p.h @@ -71,10 +71,10 @@ void qt_scale_image_16bit(uchar *destPixels, int dbpl, const int ix = 0x00010000 * sx; const int iy = 0x00010000 * sy; -// qDebug() << "scale:" << endl -// << " - target" << targetRect << endl -// << " - source" << srcRect << endl -// << " - clip" << clip << endl +// qDebug() << "scale:" << Qt::endl +// << " - target" << targetRect << Qt::endl +// << " - source" << srcRect << Qt::endl +// << " - clip" << clip << Qt::endl // << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; QRect tr = targetRect.normalized().toRect(); @@ -162,10 +162,10 @@ template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl, const int ix = 0x00010000 * sx; const int iy = 0x00010000 * sy; -// qDebug() << "scale:" << endl -// << " - target" << targetRect << endl -// << " - source" << srcRect << endl -// << " - clip" << clip << endl +// qDebug() << "scale:" << Qt::endl +// << " - target" << targetRect << Qt::endl +// << " - source" << srcRect << Qt::endl +// << " - clip" << clip << Qt::endl // << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; QRect tr = targetRect.normalized().toRect(); diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index f56be55325..abb3268dfa 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -352,7 +352,7 @@ public: QBrushData *brush; QNullBrushData() : brush(new QBrushData) { - brush->ref.store(1); + brush->ref.storeRelaxed(1); brush->style = Qt::BrushStyle(0); brush->color = Qt::black; } @@ -411,7 +411,7 @@ void QBrush::init(const QColor &color, Qt::BrushStyle style) d.reset(new QBrushData); break; } - d->ref.store(1); + d->ref.storeRelaxed(1); d->style = style; d->color = color; } @@ -585,7 +585,7 @@ static Q_DECL_CONSTEXPR inline bool use_same_brushdata(Qt::BrushStyle lhs, Qt::B void QBrush::detach(Qt::BrushStyle newStyle) { - if (use_same_brushdata(newStyle, d->style) && d->ref.load() == 1) { + if (use_same_brushdata(newStyle, d->style) && d->ref.loadRelaxed() == 1) { d->style = newStyle; return; } @@ -625,7 +625,7 @@ void QBrush::detach(Qt::BrushStyle newStyle) x.reset(new QBrushData); break; } - x->ref.store(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op + x->ref.storeRelaxed(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op x->style = newStyle; x->color = d->color; x->transform = d->transform; @@ -1140,11 +1140,11 @@ QDataStream &operator>>(QDataStream &s, QBrush &b) if (s.version() >= QDataStream::Qt_5_5) { QImage img; s >> img; - b.setTextureImage(qMove(img)); + b.setTextureImage(std::move(img)); } else { QPixmap pm; s >> pm; - b.setTexture(qMove(pm)); + b.setTexture(std::move(pm)); } } else if (style == Qt::LinearGradientPattern || style == Qt::RadialGradientPattern @@ -1385,22 +1385,29 @@ QGradient::QGradient(Preset preset) setCoordinateMode(ObjectMode); setSpread(PadSpread); - const QJsonValue start = presetData[QLatin1Literal("start")]; - const QJsonValue end = presetData[QLatin1Literal("end")]; - m_data.linear.x1 = start[QLatin1Literal("x")].toDouble(); - m_data.linear.y1 = start[QLatin1Literal("y")].toDouble(); - m_data.linear.x2 = end[QLatin1Literal("x")].toDouble(); - m_data.linear.y2 = end[QLatin1Literal("y")].toDouble(); + const QJsonValue start = presetData[QLatin1String("start")]; + const QJsonValue end = presetData[QLatin1String("end")]; + m_data.linear.x1 = start[QLatin1String("x")].toDouble(); + m_data.linear.y1 = start[QLatin1String("y")].toDouble(); + m_data.linear.x2 = end[QLatin1String("x")].toDouble(); + m_data.linear.y2 = end[QLatin1String("y")].toDouble(); for (const QJsonValue &stop : presetData[QLatin1String("stops")].toArray()) { - setColorAt(stop[QLatin1Literal("position")].toDouble(), - QColor(QRgb(stop[QLatin1Literal("color")].toInt()))); + setColorAt(stop[QLatin1String("position")].toDouble(), + QColor(QRgb(stop[QLatin1String("color")].toInt()))); } cachedPresets.insert(preset, *this); } } +/*! + \internal +*/ +QGradient::~QGradient() +{ +} + QT_END_NAMESPACE static void initGradientPresets() { Q_INIT_RESOURCE(qmake_webgradients); } Q_CONSTRUCTOR_FUNCTION(initGradientPresets); @@ -1785,6 +1792,12 @@ QLinearGradient::QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, q { } +/*! + \internal +*/ +QLinearGradient::~QLinearGradient() +{ +} /*! Returns the start point of this linear gradient in logical coordinates. @@ -2056,6 +2069,13 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal f } /*! + \internal +*/ +QRadialGradient::~QRadialGradient() +{ +} + +/*! Returns the center of this radial gradient in logical coordinates. \sa QGradient::stops() @@ -2301,6 +2321,13 @@ QConicalGradient::QConicalGradient(qreal cx, qreal cy, qreal angle) { } +/*! + \internal +*/ +QConicalGradient::~QConicalGradient() +{ +} + /*! Constructs a conical with center at (0, 0) starting the diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h index aee51c526d..6a4ffab1c5 100644 --- a/src/gui/painting/qbrush.h +++ b/src/gui/painting/qbrush.h @@ -79,11 +79,9 @@ public: ~QBrush(); QBrush &operator=(const QBrush &brush); -#ifdef Q_COMPILER_RVALUE_REFS - inline QBrush &operator=(QBrush &&other) Q_DECL_NOEXCEPT + inline QBrush &operator=(QBrush &&other) noexcept { qSwap(d, other.d); return *this; } -#endif - inline void swap(QBrush &other) Q_DECL_NOEXCEPT + inline void swap(QBrush &other) noexcept { qSwap(d, other.d); } operator QVariant() const; @@ -161,7 +159,7 @@ inline Qt::BrushStyle QBrush::style() const { return d->style; } inline const QColor &QBrush::color() const { return d->color; } inline const QMatrix &QBrush::matrix() const { return d->transform.toAffine(); } inline QTransform QBrush::transform() const { return d->transform; } -inline bool QBrush::isDetached() const { return d->ref.load() == 1; } +inline bool QBrush::isDetached() const { return d->ref.loadRelaxed() == 1; } /******************************************************************************* @@ -373,11 +371,14 @@ public: GagarinView = 178, FabledSunset = 179, PerfectBlue = 180, + + NumPresets }; Q_ENUM(Preset) QGradient(); QGradient(Preset); + ~QGradient(); Type type() const { return m_type; } @@ -431,6 +432,7 @@ public: QLinearGradient(); QLinearGradient(const QPointF &start, const QPointF &finalStop); QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop); + ~QLinearGradient(); QPointF start() const; void setStart(const QPointF &start); @@ -455,6 +457,8 @@ public: QRadialGradient(const QPointF ¢er, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius); QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius); + ~QRadialGradient(); + QPointF center() const; void setCenter(const QPointF ¢er); inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); } @@ -480,6 +484,7 @@ public: QConicalGradient(); QConicalGradient(const QPointF ¢er, qreal startAngle); QConicalGradient(qreal cx, qreal cy, qreal startAngle); + ~QConicalGradient(); QPointF center() const; void setCenter(const QPointF ¢er); diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index 07202f5b1c..8780cce223 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -39,6 +39,7 @@ #include "qcolor.h" #include "qcolor_p.h" +#include "qfloat16.h" #include "qnamespace.h" #include "qdatastream.h" #include "qvariant.h" @@ -146,6 +147,7 @@ static bool get_hex_rgb(const QChar *str, size_t len, QRgba64 *rgb) #endif #define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b) +// keep this is in sync with QColorConstants static const struct RGBData { const char name[21]; uint value; @@ -474,25 +476,43 @@ static QStringList get_colornames() \section1 Predefined Colors - There are 20 predefined QColors described by the Qt::GlobalColor enum, - including black, white, primary and secondary colors, darker versions - of these colors and three shades of gray. QColor also recognizes a - variety of color names; the static colorNames() function returns a - QStringList color names that QColor knows about. + There are 20 predefined QColor objects in the \c{QColorConstants} + namespace, including black, white, primary and secondary colors, + darker versions of these colors, and three shades of gray. + Furthermore, the \c{QColorConstants::Svg} namespace defines QColor + objects for the standard \l{https://www.w3.org/TR/SVG11/types.html#ColorKeywords}{SVG color keyword names}. \image qt-colors.png Qt Colors - Additionally, the Qt::color0, Qt::color1 and Qt::transparent colors - are used for special purposes. + The \c{QColorConstants::Color0}, \c{QColorConstants::Color1} and + \c{QColorConstants::Transparent} colors are used for special + purposes. - Qt::color0 (zero pixel value) and Qt::color1 (non-zero pixel value) - are special colors for drawing in QBitmaps. Painting with Qt::color0 - sets the bitmap bits to 0 (transparent; i.e., background), and painting - with Qt::color1 sets the bits to 1 (opaque; i.e., foreground). + \c{QColorConstants::Color0} (zero pixel value) and + \c{QColorConstants::Color1} (non-zero pixel value) are special + colors for drawing in QBitmaps. Painting with + \c{QColorConstants::Color0} sets the bitmap bits to 0 (transparent; + i.e., background), and painting with c{QColorConstants::Color1} + sets the bits to 1 (opaque; i.e., foreground). - Qt::transparent is used to indicate a transparent pixel. When painting - with this value, a pixel value will be used that is appropriate for the - underlying pixel format in use. + \c{QColorConstants::Transparent} is used to indicate a transparent + pixel. When painting with this value, a pixel value will be used + that is appropriate for the underlying pixel format in use. + + For historical reasons, the 20 predefined colors are also available + in the Qt::GlobalColor enumeration. + + Finally, QColor recognizes a variety of color names (as strings); + the static colorNames() function returns a QStringList color names + that QColor knows about. + + \section1 The Extended RGB Color Model + + The extended RGB color model, also known as the scRGB color space, + is the same the RGB color model except it allows values under 0.0, + and over 1.0. This makes it possible to represent colors that would + otherwise be outside the range of the RGB colorspace but still use + the same values for colors inside the RGB colorspace. \section1 The HSV Color Model @@ -577,7 +597,7 @@ static QStringList get_colornames() alpha-channel to feature \l {QColor#Alpha-Blended Drawing}{alpha-blended drawing}. - \sa QPalette, QBrush + \sa QPalette, QBrush, QColorConstants */ #define QCOLOR_INT_RANGE_CHECK(fn, var) \ @@ -603,12 +623,13 @@ static QStringList get_colornames() /*! \enum QColor::Spec - The type of color specified, either RGB, HSV, CMYK or HSL. + The type of color specified, either RGB, extended RGB, HSV, CMYK or HSL. \value Rgb \value Hsv \value Cmyk \value Hsl + \value ExtendedRgb \value Invalid \sa spec(), convertTo() @@ -653,7 +674,7 @@ static QStringList get_colornames() \sa isValid(), {QColor#Predefined Colors}{Predefined Colors} */ -QColor::QColor(Qt::GlobalColor color) Q_DECL_NOTHROW +QColor::QColor(Qt::GlobalColor color) noexcept { #define QRGB(r, g, b) \ QRgb(((0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))) @@ -728,7 +749,7 @@ QColor::QColor(Qt::GlobalColor color) Q_DECL_NOTHROW \sa fromRgb(), isValid() */ -QColor::QColor(QRgb color) Q_DECL_NOTHROW +QColor::QColor(QRgb color) noexcept { cspec = Rgb; ct.argb.alpha = 0xffff; @@ -746,7 +767,7 @@ QColor::QColor(QRgb color) Q_DECL_NOTHROW \sa fromRgba64() */ -QColor::QColor(QRgba64 rgba64) Q_DECL_NOTHROW +QColor::QColor(QRgba64 rgba64) noexcept { setRgba64(rgba64); } @@ -760,7 +781,7 @@ QColor::QColor(QRgba64 rgba64) Q_DECL_NOTHROW becomes a valid color by accident. */ -QColor::QColor(Spec spec) Q_DECL_NOTHROW +QColor::QColor(Spec spec) noexcept { switch (spec) { case Invalid: @@ -778,6 +799,10 @@ QColor::QColor(Spec spec) Q_DECL_NOTHROW case Hsl: setHsl(0, 0, 0, 0); break; + case ExtendedRgb: + cspec = spec; + setRgbF(0, 0, 0, 0); + break; } } @@ -872,7 +897,8 @@ QString QColor::name(NameFormat format) const \li #AARRGGBB (Since 5.2) \li #RRRGGGBBB \li #RRRRGGGGBBBB - \li A name from the list of colors defined in the list of \l{http://www.w3.org/TR/SVG/types.html#ColorKeywords}{SVG color keyword names} + \li A name from the list of colors defined in the list of + \l{https://www.w3.org/TR/SVG11/types.html#ColorKeywords}{SVG color keyword names} provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro". These color names work on all platforms. Note that these color names are \e not the same as defined by the Qt::GlobalColor enums, e.g. "green" and Qt::green does not @@ -933,7 +959,7 @@ bool QColor::isValidColor(const QString &name) \overload \since 5.10 */ -bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW +bool QColor::isValidColor(QStringView name) noexcept { return name.size() && QColor().setColorFromString(name); } @@ -942,7 +968,7 @@ bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW \overload \since 5.8 */ -bool QColor::isValidColor(QLatin1String name) Q_DECL_NOTHROW +bool QColor::isValidColor(QLatin1String name) noexcept { return name.size() && QColor().setColorFromString(name); } @@ -1211,6 +1237,17 @@ void QColor::setHsl(int h, int s, int l, int a) ct.ahsl.pad = 0; } +static inline qfloat16 &castF16(quint16 &v) +{ + // this works because qfloat16 internally is a quint16 + return *reinterpret_cast<qfloat16 *>(&v); +} + +static inline const qfloat16 &castF16(const quint16 &v) +{ + return *reinterpret_cast<const qfloat16 *>(&v); +} + /*! Sets the contents pointed to by \a r, \a g, \a b, and \a a, to the red, green, blue, and alpha-channel (transparency) components of the color's @@ -1226,18 +1263,27 @@ void QColor::getRgbF(qreal *r, qreal *g, qreal *b, qreal *a) const if (!r || !g || !b) return; - if (cspec != Invalid && cspec != Rgb) { + if (cspec == Invalid) + return; + + if (cspec != Rgb && cspec != ExtendedRgb) { toRgb().getRgbF(r, g, b, a); return; } - *r = ct.argb.red / qreal(USHRT_MAX); - *g = ct.argb.green / qreal(USHRT_MAX); - *b = ct.argb.blue / qreal(USHRT_MAX); - - if (a) - *a = ct.argb.alpha / qreal(USHRT_MAX); - + if (cspec == Rgb) { + *r = ct.argb.red / qreal(USHRT_MAX); + *g = ct.argb.green / qreal(USHRT_MAX); + *b = ct.argb.blue / qreal(USHRT_MAX); + if (a) + *a = ct.argb.alpha / qreal(USHRT_MAX); + } else { + *r = castF16(ct.argbExtended.redF16); + *g = castF16(ct.argbExtended.greenF16); + *b = castF16(ct.argbExtended.blueF16); + if (a) + *a = castF16(ct.argbExtended.alphaF16); + } } /*! @@ -1274,26 +1320,35 @@ void QColor::getRgb(int *r, int *g, int *b, int *a) const Sets the color channels of this color to \a r (red), \a g (green), \a b (blue) and \a a (alpha, transparency). - All values must be in the range 0.0-1.0. + The alpha value must be in the range 0.0-1.0. + If any of the other values are outside the range of 0.0-1.0 the + color model will be set as \c ExtendedRgb. \sa rgb(), getRgbF(), setRgb() */ void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a) { - if (r < qreal(0.0) || r > qreal(1.0) - || g < qreal(0.0) || g > qreal(1.0) - || b < qreal(0.0) || b > qreal(1.0) - || a < qreal(0.0) || a > qreal(1.0)) { - qWarning("QColor::setRgbF: RGB parameters out of range"); + if (a < qreal(0.0) || a > qreal(1.0)) { + qWarning("QColor::setRgbF: Alpha parameter is out of range"); invalidate(); return; } - + if (r < qreal(0.0) || r > qreal(1.0) || + g < qreal(0.0) || g > qreal(1.0) || + b < qreal(0.0) || b > qreal(1.0) || cspec == ExtendedRgb) { + cspec = ExtendedRgb; + castF16(ct.argbExtended.redF16) = qfloat16(r); + castF16(ct.argbExtended.greenF16) = qfloat16(g); + castF16(ct.argbExtended.blueF16) = qfloat16(b); + castF16(ct.argbExtended.alphaF16) = qfloat16(a); + ct.argbExtended.pad = 0; + return; + } cspec = Rgb; - ct.argb.alpha = qRound(a * USHRT_MAX); ct.argb.red = qRound(r * USHRT_MAX); ct.argb.green = qRound(g * USHRT_MAX); ct.argb.blue = qRound(b * USHRT_MAX); + ct.argb.alpha = qRound(a * USHRT_MAX); ct.argb.pad = 0; } @@ -1306,7 +1361,7 @@ void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a) */ void QColor::setRgb(int r, int g, int b, int a) { - if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) { + if (!isRgbaValid(r, g, b, a)) { qWarning("QColor::setRgb: RGB parameters out of range"); invalidate(); return; @@ -1330,7 +1385,7 @@ void QColor::setRgb(int r, int g, int b, int a) \sa setRgba(), rgb(), rgba64() */ -QRgb QColor::rgba() const Q_DECL_NOTHROW +QRgb QColor::rgba() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().rgba(); @@ -1342,7 +1397,7 @@ QRgb QColor::rgba() const Q_DECL_NOTHROW \sa rgba(), rgb(), setRgba64() */ -void QColor::setRgba(QRgb rgba) Q_DECL_NOTHROW +void QColor::setRgba(QRgb rgba) noexcept { cspec = Rgb; ct.argb.alpha = qAlpha(rgba) * 0x101; @@ -1362,7 +1417,7 @@ void QColor::setRgba(QRgb rgba) Q_DECL_NOTHROW \sa setRgba64(), rgba(), rgb() */ -QRgba64 QColor::rgba64() const Q_DECL_NOTHROW +QRgba64 QColor::rgba64() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().rgba64(); @@ -1376,7 +1431,7 @@ QRgba64 QColor::rgba64() const Q_DECL_NOTHROW \sa setRgba(), rgba64() */ -void QColor::setRgba64(QRgba64 rgba) Q_DECL_NOTHROW +void QColor::setRgba64(QRgba64 rgba) noexcept { cspec = Rgb; ct.argb.alpha = rgba.alpha(); @@ -1393,7 +1448,7 @@ void QColor::setRgba64(QRgba64 rgba) Q_DECL_NOTHROW \sa getRgb(), rgba() */ -QRgb QColor::rgb() const Q_DECL_NOTHROW +QRgb QColor::rgb() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().rgb(); @@ -1405,7 +1460,7 @@ QRgb QColor::rgb() const Q_DECL_NOTHROW Sets the RGB value to \a rgb. The alpha value is set to opaque. */ -void QColor::setRgb(QRgb rgb) Q_DECL_NOTHROW +void QColor::setRgb(QRgb rgb) noexcept { cspec = Rgb; ct.argb.alpha = 0xffff; @@ -1420,8 +1475,12 @@ void QColor::setRgb(QRgb rgb) Q_DECL_NOTHROW \sa setAlpha(), alphaF(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} */ -int QColor::alpha() const Q_DECL_NOTHROW -{ return ct.argb.alpha >> 8; } +int QColor::alpha() const noexcept +{ + if (cspec == ExtendedRgb) + return qRound(qreal(castF16(ct.argbExtended.alphaF16)) * 255); + return ct.argb.alpha >> 8; +} /*! @@ -1434,6 +1493,11 @@ int QColor::alpha() const Q_DECL_NOTHROW void QColor::setAlpha(int alpha) { QCOLOR_INT_RANGE_CHECK("QColor::setAlpha", alpha); + if (cspec == ExtendedRgb) { + constexpr qreal f = qreal(1.0) / 255; + castF16(ct.argbExtended.alphaF16) = alpha * f; + return; + } ct.argb.alpha = alpha * 0x101; } @@ -1442,8 +1506,12 @@ void QColor::setAlpha(int alpha) \sa setAlphaF(), alpha(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} */ -qreal QColor::alphaF() const Q_DECL_NOTHROW -{ return ct.argb.alpha / qreal(USHRT_MAX); } +qreal QColor::alphaF() const noexcept +{ + if (cspec == ExtendedRgb) + return castF16(ct.argbExtended.alphaF16); + return ct.argb.alpha / qreal(USHRT_MAX); +} /*! Sets the alpha of this color to \a alpha. qreal alpha is specified in the @@ -1455,6 +1523,10 @@ qreal QColor::alphaF() const Q_DECL_NOTHROW void QColor::setAlphaF(qreal alpha) { QCOLOR_REAL_RANGE_CHECK("QColor::setAlphaF", alpha); + if (cspec == ExtendedRgb) { + castF16(ct.argbExtended.alphaF16) = alpha; + return; + } qreal tmp = alpha * USHRT_MAX; ct.argb.alpha = qRound(tmp); } @@ -1465,7 +1537,7 @@ void QColor::setAlphaF(qreal alpha) \sa setRed(), redF(), getRgb() */ -int QColor::red() const Q_DECL_NOTHROW +int QColor::red() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().red(); @@ -1492,7 +1564,7 @@ void QColor::setRed(int red) \sa setGreen(), greenF(), getRgb() */ -int QColor::green() const Q_DECL_NOTHROW +int QColor::green() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().green(); @@ -1520,7 +1592,7 @@ void QColor::setGreen(int green) \sa setBlue(), blueF(), getRgb() */ -int QColor::blue() const Q_DECL_NOTHROW +int QColor::blue() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().blue(); @@ -1548,27 +1620,31 @@ void QColor::setBlue(int blue) \sa setRedF(), red(), getRgbF() */ -qreal QColor::redF() const Q_DECL_NOTHROW +qreal QColor::redF() const noexcept { - if (cspec != Invalid && cspec != Rgb) - return toRgb().redF(); - return ct.argb.red / qreal(USHRT_MAX); + if (cspec == Rgb || cspec == Invalid) + return ct.argb.red / qreal(USHRT_MAX); + if (cspec == ExtendedRgb) + return castF16(ct.argbExtended.redF16); + + return toRgb().redF(); } /*! - Sets the red color component of this color to \a red. Float components - are specified in the range 0.0-1.0. + Sets the red color component of this color to \a red. If \a red lies outside + the 0.0-1.0 range, the color model will be changed to \c ExtendedRgb. \sa redF(), red(), setRgbF() */ void QColor::setRedF(qreal red) { - QCOLOR_REAL_RANGE_CHECK("QColor::setRedF", red); - if (cspec != Rgb) - setRgbF(red, greenF(), blueF(), alphaF()); - else + if (cspec == Rgb && red >= qreal(0.0) && red <= qreal(1.0)) ct.argb.red = qRound(red * USHRT_MAX); + else if (cspec == ExtendedRgb) + castF16(ct.argbExtended.redF16) = red; + else + setRgbF(red, greenF(), blueF(), alphaF()); } /*! @@ -1576,27 +1652,31 @@ void QColor::setRedF(qreal red) \sa setGreenF(), green(), getRgbF() */ -qreal QColor::greenF() const Q_DECL_NOTHROW +qreal QColor::greenF() const noexcept { - if (cspec != Invalid && cspec != Rgb) - return toRgb().greenF(); - return ct.argb.green / qreal(USHRT_MAX); + if (cspec == Rgb || cspec == Invalid) + return ct.argb.green / qreal(USHRT_MAX); + if (cspec == ExtendedRgb) + return castF16(ct.argbExtended.greenF16); + + return toRgb().greenF(); } /*! - Sets the green color component of this color to \a green. Float components - are specified in the range 0.0-1.0. + Sets the green color component of this color to \a green. If \a green lies outside + the 0.0-1.0 range, the color model will be changed to \c ExtendedRgb. \sa greenF(), green(), setRgbF() */ void QColor::setGreenF(qreal green) { - QCOLOR_REAL_RANGE_CHECK("QColor::setGreenF", green); - if (cspec != Rgb) - setRgbF(redF(), green, blueF(), alphaF()); - else + if (cspec == Rgb && green >= qreal(0.0) && green <= qreal(1.0)) ct.argb.green = qRound(green * USHRT_MAX); + else if (cspec == ExtendedRgb) + castF16(ct.argbExtended.greenF16) = green; + else + setRgbF(redF(), green, blueF(), alphaF()); } /*! @@ -1604,26 +1684,29 @@ void QColor::setGreenF(qreal green) \sa setBlueF(), blue(), getRgbF() */ -qreal QColor::blueF() const Q_DECL_NOTHROW +qreal QColor::blueF() const noexcept { - if (cspec != Invalid && cspec != Rgb) - return toRgb().blueF(); - return ct.argb.blue / qreal(USHRT_MAX); + if (cspec == Rgb || cspec == Invalid) + return ct.argb.blue / qreal(USHRT_MAX); + if (cspec == ExtendedRgb) + return castF16(ct.argbExtended.blueF16); + + return toRgb().blueF(); } /*! - Sets the blue color component of this color to \a blue. Float components - are specified in the range 0.0-1.0. - + Sets the blue color component of this color to \a blue. If \a blue lies outside + the 0.0-1.0 range, the color model will be changed to \c ExtendedRgb. \sa blueF(), blue(), setRgbF() */ void QColor::setBlueF(qreal blue) { - QCOLOR_REAL_RANGE_CHECK("QColor::setBlueF", blue); - if (cspec != Rgb) - setRgbF(redF(), greenF(), blue, alphaF()); - else + if (cspec == Rgb && blue >= qreal(0.0) && blue <= qreal(1.0)) ct.argb.blue = qRound(blue * USHRT_MAX); + else if (cspec == ExtendedRgb) + castF16(ct.argbExtended.blueF16) = blue; + else + setRgbF(redF(), greenF(), blue, alphaF()); } /*! @@ -1634,7 +1717,7 @@ void QColor::setBlueF(qreal blue) \sa hsvHue(), hslHue(), hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::hue() const Q_DECL_NOTHROW +int QColor::hue() const noexcept { return hsvHue(); } @@ -1644,7 +1727,7 @@ int QColor::hue() const Q_DECL_NOTHROW \sa hueF(), hslHue(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::hsvHue() const Q_DECL_NOTHROW +int QColor::hsvHue() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().hue(); @@ -1660,7 +1743,7 @@ int QColor::hsvHue() const Q_DECL_NOTHROW Model} */ -int QColor::saturation() const Q_DECL_NOTHROW +int QColor::saturation() const noexcept { return hsvSaturation(); } @@ -1670,7 +1753,7 @@ int QColor::saturation() const Q_DECL_NOTHROW \sa saturationF(), hslSaturation(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::hsvSaturation() const Q_DECL_NOTHROW +int QColor::hsvSaturation() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().saturation(); @@ -1682,7 +1765,7 @@ int QColor::hsvSaturation() const Q_DECL_NOTHROW \sa valueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::value() const Q_DECL_NOTHROW +int QColor::value() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().value(); @@ -1696,7 +1779,7 @@ int QColor::value() const Q_DECL_NOTHROW \sa hsvHueF(), hslHueF(), hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::hueF() const Q_DECL_NOTHROW +qreal QColor::hueF() const noexcept { return hsvHueF(); } @@ -1707,7 +1790,7 @@ qreal QColor::hueF() const Q_DECL_NOTHROW \sa hue(), hslHueF(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::hsvHueF() const Q_DECL_NOTHROW +qreal QColor::hsvHueF() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().hueF(); @@ -1722,7 +1805,7 @@ qreal QColor::hsvHueF() const Q_DECL_NOTHROW \sa hsvSaturationF(), hslSaturationF(), saturation(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::saturationF() const Q_DECL_NOTHROW +qreal QColor::saturationF() const noexcept { return hsvSaturationF(); } @@ -1732,7 +1815,7 @@ qreal QColor::saturationF() const Q_DECL_NOTHROW \sa saturation(), hslSaturationF(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::hsvSaturationF() const Q_DECL_NOTHROW +qreal QColor::hsvSaturationF() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().saturationF(); @@ -1744,7 +1827,7 @@ qreal QColor::hsvSaturationF() const Q_DECL_NOTHROW \sa value(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::valueF() const Q_DECL_NOTHROW +qreal QColor::valueF() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().valueF(); @@ -1758,7 +1841,7 @@ qreal QColor::valueF() const Q_DECL_NOTHROW \sa hslHueF(), hsvHue(), getHsl(), {QColor#The HSL Color Model}{The HSL Color Model} */ -int QColor::hslHue() const Q_DECL_NOTHROW +int QColor::hslHue() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslHue(); @@ -1772,7 +1855,7 @@ int QColor::hslHue() const Q_DECL_NOTHROW \sa hslSaturationF(), hsvSaturation(), getHsl(), {QColor#The HSL Color Model}{The HSL Color Model} */ -int QColor::hslSaturation() const Q_DECL_NOTHROW +int QColor::hslSaturation() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslSaturation(); @@ -1786,7 +1869,7 @@ int QColor::hslSaturation() const Q_DECL_NOTHROW \sa lightnessF(), getHsl() */ -int QColor::lightness() const Q_DECL_NOTHROW +int QColor::lightness() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().lightness(); @@ -1800,7 +1883,7 @@ int QColor::lightness() const Q_DECL_NOTHROW \sa hslHue(), hsvHueF(), getHslF() */ -qreal QColor::hslHueF() const Q_DECL_NOTHROW +qreal QColor::hslHueF() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslHueF(); @@ -1814,7 +1897,7 @@ qreal QColor::hslHueF() const Q_DECL_NOTHROW \sa hslSaturation(), hsvSaturationF(), getHslF(), {QColor#The HSL Color Model}{The HSL Color Model} */ -qreal QColor::hslSaturationF() const Q_DECL_NOTHROW +qreal QColor::hslSaturationF() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslSaturationF(); @@ -1828,7 +1911,7 @@ qreal QColor::hslSaturationF() const Q_DECL_NOTHROW \sa value(), getHslF() */ -qreal QColor::lightnessF() const Q_DECL_NOTHROW +qreal QColor::lightnessF() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().lightnessF(); @@ -1840,7 +1923,7 @@ qreal QColor::lightnessF() const Q_DECL_NOTHROW \sa cyanF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::cyan() const Q_DECL_NOTHROW +int QColor::cyan() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().cyan(); @@ -1852,7 +1935,7 @@ int QColor::cyan() const Q_DECL_NOTHROW \sa magentaF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::magenta() const Q_DECL_NOTHROW +int QColor::magenta() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().magenta(); @@ -1864,7 +1947,7 @@ int QColor::magenta() const Q_DECL_NOTHROW \sa yellowF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::yellow() const Q_DECL_NOTHROW +int QColor::yellow() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().yellow(); @@ -1877,7 +1960,7 @@ int QColor::yellow() const Q_DECL_NOTHROW \sa blackF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::black() const Q_DECL_NOTHROW +int QColor::black() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().black(); @@ -1889,7 +1972,7 @@ int QColor::black() const Q_DECL_NOTHROW \sa cyan(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::cyanF() const Q_DECL_NOTHROW +qreal QColor::cyanF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().cyanF(); @@ -1901,7 +1984,7 @@ qreal QColor::cyanF() const Q_DECL_NOTHROW \sa magenta(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::magentaF() const Q_DECL_NOTHROW +qreal QColor::magentaF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().magentaF(); @@ -1913,7 +1996,7 @@ qreal QColor::magentaF() const Q_DECL_NOTHROW \sa yellow(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::yellowF() const Q_DECL_NOTHROW +qreal QColor::yellowF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().yellowF(); @@ -1925,7 +2008,7 @@ qreal QColor::yellowF() const Q_DECL_NOTHROW \sa black(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::blackF() const Q_DECL_NOTHROW +qreal QColor::blackF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().blackF(); @@ -1933,18 +2016,43 @@ qreal QColor::blackF() const Q_DECL_NOTHROW } /*! + Create and returns an extended RGB QColor based on this color. + \since 5.14 + + \sa toRgb, convertTo() +*/ +QColor QColor::toExtendedRgb() const noexcept +{ + if (!isValid() || cspec == ExtendedRgb) + return *this; + if (cspec != Rgb) + return toRgb().toExtendedRgb(); + + constexpr qreal f = qreal(1.0) / USHRT_MAX; + QColor color; + color.cspec = ExtendedRgb; + castF16(color.ct.argbExtended.alphaF16) = qfloat16(ct.argb.alpha * f); + castF16(color.ct.argbExtended.redF16) = qfloat16(ct.argb.red * f); + castF16(color.ct.argbExtended.greenF16) = qfloat16(ct.argb.green * f); + castF16(color.ct.argbExtended.blueF16) = qfloat16(ct.argb.blue * f); + color.ct.argbExtended.pad = 0; + return color; +} + +/*! Create and returns an RGB QColor based on this color. \sa fromRgb(), convertTo(), isValid() */ -QColor QColor::toRgb() const Q_DECL_NOTHROW +QColor QColor::toRgb() const noexcept { if (!isValid() || cspec == Rgb) return *this; QColor color; color.cspec = Rgb; - color.ct.argb.alpha = ct.argb.alpha; + if (cspec != ExtendedRgb) + color.ct.argb.alpha = ct.argb.alpha; color.ct.argb.pad = 0; switch (cspec) { @@ -2066,6 +2174,12 @@ QColor QColor::toRgb() const Q_DECL_NOTHROW color.ct.argb.blue = qRound((qreal(1.0) - (y * (qreal(1.0) - k) + k)) * USHRT_MAX); break; } + case ExtendedRgb: + color.ct.argb.alpha = qRound(USHRT_MAX * qreal(castF16(ct.argbExtended.alphaF16))); + color.ct.argb.red = qRound(USHRT_MAX * qBound(qreal(0.0), qreal(castF16(ct.argbExtended.redF16)), qreal(1.0))); + color.ct.argb.green = qRound(USHRT_MAX * qBound(qreal(0.0), qreal(castF16(ct.argbExtended.greenF16)), qreal(1.0))); + color.ct.argb.blue = qRound(USHRT_MAX * qBound(qreal(0.0), qreal(castF16(ct.argbExtended.blueF16)), qreal(1.0))); + break; default: break; } @@ -2083,7 +2197,7 @@ QColor QColor::toRgb() const Q_DECL_NOTHROW \sa fromHsv(), convertTo(), isValid(), {QColor#The HSV Color Model}{The HSV Color Model} */ -QColor QColor::toHsv() const Q_DECL_NOTHROW +QColor QColor::toHsv() const noexcept { if (!isValid() || cspec == Hsv) return *this; @@ -2134,7 +2248,7 @@ QColor QColor::toHsv() const Q_DECL_NOTHROW \sa fromHsl(), convertTo(), isValid(), {QColor#The HSL Color Model}{The HSL Color Model} */ -QColor QColor::toHsl() const Q_DECL_NOTHROW +QColor QColor::toHsl() const noexcept { if (!isValid() || cspec == Hsl) return *this; @@ -2190,7 +2304,7 @@ QColor QColor::toHsl() const Q_DECL_NOTHROW \sa fromCmyk(), convertTo(), isValid(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -QColor QColor::toCmyk() const Q_DECL_NOTHROW +QColor QColor::toCmyk() const noexcept { if (!isValid() || cspec == Cmyk) return *this; @@ -2231,13 +2345,15 @@ QColor QColor::toCmyk() const Q_DECL_NOTHROW return color; } -QColor QColor::convertTo(QColor::Spec colorSpec) const Q_DECL_NOTHROW +QColor QColor::convertTo(QColor::Spec colorSpec) const noexcept { if (colorSpec == cspec) return *this; switch (colorSpec) { case Rgb: return toRgb(); + case ExtendedRgb: + return toExtendedRgb(); case Hsv: return toHsv(); case Cmyk: @@ -2262,7 +2378,7 @@ QColor QColor::convertTo(QColor::Spec colorSpec) const Q_DECL_NOTHROW \sa fromRgba(), fromRgbF(), toRgb(), isValid() */ -QColor QColor::fromRgb(QRgb rgb) Q_DECL_NOTHROW +QColor QColor::fromRgb(QRgb rgb) noexcept { return fromRgb(qRed(rgb), qGreen(rgb), qBlue(rgb)); } @@ -2278,7 +2394,7 @@ QColor QColor::fromRgb(QRgb rgb) Q_DECL_NOTHROW \sa fromRgb(), fromRgba64(), isValid() */ -QColor QColor::fromRgba(QRgb rgba) Q_DECL_NOTHROW +QColor QColor::fromRgba(QRgb rgba) noexcept { return fromRgb(qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba)); } @@ -2294,10 +2410,7 @@ QColor QColor::fromRgba(QRgb rgba) Q_DECL_NOTHROW */ QColor QColor::fromRgb(int r, int g, int b, int a) { - if (r < 0 || r > 255 - || g < 0 || g > 255 - || b < 0 || b > 255 - || a < 0 || a > 255) { + if (!isRgbaValid(r, g, b, a)) { qWarning("QColor::fromRgb: RGB parameters out of range"); return QColor(); } @@ -2317,20 +2430,32 @@ QColor QColor::fromRgb(int r, int g, int b, int a) color values, \a r (red), \a g (green), \a b (blue), and \a a (alpha-channel, i.e. transparency). - All the values must be in the range 0.0-1.0. + The alpha value must be in the range 0.0-1.0. + If any of the other values are outside the range of 0.0-1.0 the + color model will be set as \c ExtendedRgb. \sa fromRgb(), fromRgba64(), toRgb(), isValid() */ QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a) { - if (r < qreal(0.0) || r > qreal(1.0) - || g < qreal(0.0) || g > qreal(1.0) - || b < qreal(0.0) || b > qreal(1.0) - || a < qreal(0.0) || a > qreal(1.0)) { - qWarning("QColor::fromRgbF: RGB parameters out of range"); + if (a < qreal(0.0) || a > qreal(1.0)) { + qWarning("QColor::fromRgbF: Alpha parameter out of range"); return QColor(); } + if (r < qreal(0.0) || r > qreal(1.0) + || g < qreal(0.0) || g > qreal(1.0) + || b < qreal(0.0) || b > qreal(1.0)) { + QColor color; + color.cspec = ExtendedRgb; + castF16(color.ct.argbExtended.alphaF16) = qfloat16(a); + castF16(color.ct.argbExtended.redF16) = qfloat16(r); + castF16(color.ct.argbExtended.greenF16) = qfloat16(g); + castF16(color.ct.argbExtended.blueF16) = qfloat16(b); + color.ct.argbExtended.pad = 0; + return color; + } + QColor color; color.cspec = Rgb; color.ct.argb.alpha = qRound(a * USHRT_MAX); @@ -2351,7 +2476,7 @@ QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a) \sa fromRgb(), fromRgbF(), toRgb(), isValid() */ -QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) Q_DECL_NOTHROW +QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) noexcept { QColor color; color.setRgba64(qRgba64(r, g, b, a)); @@ -2366,7 +2491,7 @@ QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) Q_DECL_NOTHROW \sa fromRgb(), fromRgbF(), toRgb(), isValid() */ -QColor QColor::fromRgba64(QRgba64 rgba64) Q_DECL_NOTHROW +QColor QColor::fromRgba64(QRgba64 rgba64) noexcept { QColor color; color.setRgba64(rgba64); @@ -2714,7 +2839,7 @@ QColor QColor::fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a) \sa darker(), isValid() */ -QColor QColor::lighter(int factor) const Q_DECL_NOTHROW +QColor QColor::lighter(int factor) const noexcept { if (factor <= 0) // invalid lightness factor return *this; @@ -2759,7 +2884,7 @@ QColor QColor::lighter(int factor) const Q_DECL_NOTHROW \sa lighter(), isValid() */ -QColor QColor::darker(int factor) const Q_DECL_NOTHROW +QColor QColor::darker(int factor) const noexcept { if (factor <= 0) // invalid darkness factor return *this; @@ -2779,7 +2904,7 @@ QColor QColor::darker(int factor) const Q_DECL_NOTHROW Use lighter(\a factor) instead. */ -QColor QColor::light(int factor) const Q_DECL_NOTHROW +QColor QColor::light(int factor) const noexcept { return lighter(factor); } @@ -2789,7 +2914,7 @@ QColor QColor::light(int factor) const Q_DECL_NOTHROW Use darker(\a factor) instead. */ -QColor QColor::dark(int factor) const Q_DECL_NOTHROW +QColor QColor::dark(int factor) const noexcept { return darker(factor); } @@ -2799,7 +2924,7 @@ QColor QColor::dark(int factor) const Q_DECL_NOTHROW /*! Assigns a copy of \a color to this color, and returns a reference to it. */ -QColor &QColor::operator=(const QColor &color) Q_DECL_NOTHROW +QColor &QColor::operator=(const QColor &color) noexcept { cspec = color.cspec; ct.argb = color.ct.argb; @@ -2810,7 +2935,7 @@ QColor &QColor::operator=(const QColor &color) Q_DECL_NOTHROW /*! \overload Assigns a copy of \a color and returns a reference to this color. */ -QColor &QColor::operator=(Qt::GlobalColor color) Q_DECL_NOTHROW +QColor &QColor::operator=(Qt::GlobalColor color) noexcept { return operator=(QColor(color)); } @@ -2819,7 +2944,7 @@ QColor &QColor::operator=(Qt::GlobalColor color) Q_DECL_NOTHROW Returns \c true if this color has the same RGB and alpha values as \a color; otherwise returns \c false. */ -bool QColor::operator==(const QColor &color) const Q_DECL_NOTHROW +bool QColor::operator==(const QColor &color) const noexcept { if (cspec == Hsl && cspec == color.cspec) { return (ct.argb.alpha == color.ct.argb.alpha @@ -2846,7 +2971,7 @@ bool QColor::operator==(const QColor &color) const Q_DECL_NOTHROW Returns \c true if this color has a different RGB and alpha values from \a color; otherwise returns \c false. */ -bool QColor::operator!=(const QColor &color) const Q_DECL_NOTHROW +bool QColor::operator!=(const QColor &color) const noexcept { return !operator==(color); } @@ -2863,7 +2988,7 @@ QColor::operator QVariant() const Marks the color as invalid and sets all components to zero (alpha is set to fully opaque for compatibility with Qt 3). */ -void QColor::invalidate() Q_DECL_NOTHROW +void QColor::invalidate() noexcept { cspec = Invalid; ct.argb.alpha = USHRT_MAX; @@ -2885,6 +3010,8 @@ QDebug operator<<(QDebug dbg, const QColor &c) dbg.nospace() << "QColor(Invalid)"; else if (c.spec() == QColor::Rgb) dbg.nospace() << "QColor(ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ')'; + else if (c.spec() == QColor::ExtendedRgb) + dbg.nospace() << "QColor(Ext. ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ')'; else if (c.spec() == QColor::Hsv) dbg.nospace() << "QColor(AHSV " << c.alphaF() << ", " << c.hueF() << ", " << c.saturationF() << ", " << c.valueF() << ')'; else if (c.spec() == QColor::Cmyk) @@ -3134,4 +3261,41 @@ const uint qt_inv_premul_factor[256] = { \sa QColor::rgb(), QColor::rgba() */ +/*! + \namespace QColorConstants + \inmodule QtGui + + \brief The QColorConstants namespace contains QColor predefined constants. + + These constants are usable everywhere a QColor object is expected: + + \code + painter.setBrush(QColorConstants::Svg::lightblue); + \endcode + + Their usage is much cheaper than e.g. passing a string to QColor's constructor, + as they don't require any parsing of the string, and always result in a valid + QColor object: + + \badcode + object.setColor(QColor("lightblue")); // expensive + \endcode + + \section1 Qt Colors + + The following colors are defined in the \c{QColorConstants} namespace: + + \include qt-colors.qdocinc + + \section1 SVG Colors + + The following table lists the available + \l {http://www.w3.org/TR/SVG/types.html#ColorKeywords}{SVG colors}. + They are available in the \c{QColorConstants::Svg} inner namespace. + + \include svg-colors.qdocinc + + \sa QColor, Qt::GlobalColor +*/ + QT_END_NAMESPACE diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index 4822612cf8..f0d7dd23ad 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -64,35 +64,43 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &); class Q_GUI_EXPORT QColor { public: - enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl }; + enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl, ExtendedRgb }; enum NameFormat { HexRgb, HexArgb }; - inline QColor() Q_DECL_NOTHROW; - QColor(Qt::GlobalColor color) Q_DECL_NOTHROW; - inline QColor(int r, int g, int b, int a = 255); - QColor(QRgb rgb) Q_DECL_NOTHROW; - QColor(QRgba64 rgba64) Q_DECL_NOTHROW; + Q_DECL_CONSTEXPR QColor() noexcept + : cspec(Invalid), ct(USHRT_MAX, 0, 0, 0, 0) {} + QColor(Qt::GlobalColor color) noexcept; + Q_DECL_CONSTEXPR QColor(int r, int g, int b, int a = 255) noexcept + : cspec(isRgbaValid(r, g, b, a) ? Rgb : Invalid), + ct(cspec == Rgb ? a * 0x0101 : 0, + cspec == Rgb ? r * 0x0101 : 0, + cspec == Rgb ? g * 0x0101 : 0, + cspec == Rgb ? b * 0x0101 : 0, + 0) {} + QColor(QRgb rgb) noexcept; + QColor(QRgba64 rgba64) noexcept; #if QT_STRINGVIEW_LEVEL < 2 inline QColor(const QString& name); #endif explicit inline QColor(QStringView name); inline QColor(const char *aname) : QColor(QLatin1String(aname)) {} inline QColor(QLatin1String name); - QColor(Spec spec) Q_DECL_NOTHROW; + QColor(Spec spec) noexcept; #if QT_VERSION < QT_VERSION_CHECK(6,0,0) - inline QColor(const QColor &color) Q_DECL_NOTHROW; // ### Qt 6: remove all of these, the trivial ones are fine. -# ifdef Q_COMPILER_RVALUE_REFS - QColor(QColor &&other) Q_DECL_NOTHROW : cspec(other.cspec), ct(other.ct) {} - QColor &operator=(QColor &&other) Q_DECL_NOTHROW + // ### Qt 6: remove all of these, the trivial ones are fine. + Q_DECL_CONSTEXPR QColor(const QColor &color) noexcept + : cspec(color.cspec), ct(color.ct) + {} + Q_DECL_CONSTEXPR QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {} + QColor &operator=(QColor &&other) noexcept { cspec = other.cspec; ct = other.ct; return *this; } -# endif - QColor &operator=(const QColor &) Q_DECL_NOTHROW; + QColor &operator=(const QColor &) noexcept; #endif // Qt < 6 - QColor &operator=(Qt::GlobalColor color) Q_DECL_NOTHROW; + QColor &operator=(Qt::GlobalColor color) noexcept; - bool isValid() const Q_DECL_NOTHROW; + bool isValid() const noexcept; // ### Qt 6: merge overloads QString name() const; @@ -106,25 +114,25 @@ public: static QStringList colorNames(); - inline Spec spec() const Q_DECL_NOTHROW + inline Spec spec() const noexcept { return cspec; } - int alpha() const Q_DECL_NOTHROW; + int alpha() const noexcept; void setAlpha(int alpha); - qreal alphaF() const Q_DECL_NOTHROW; + qreal alphaF() const noexcept; void setAlphaF(qreal alpha); - int red() const Q_DECL_NOTHROW; - int green() const Q_DECL_NOTHROW; - int blue() const Q_DECL_NOTHROW; + int red() const noexcept; + int green() const noexcept; + int blue() const noexcept; void setRed(int red); void setGreen(int green); void setBlue(int blue); - qreal redF() const Q_DECL_NOTHROW; - qreal greenF() const Q_DECL_NOTHROW; - qreal blueF() const Q_DECL_NOTHROW; + qreal redF() const noexcept; + qreal greenF() const noexcept; + qreal blueF() const noexcept; void setRedF(qreal red); void setGreenF(qreal green); void setBlueF(qreal blue); @@ -135,26 +143,26 @@ public: void getRgbF(qreal *r, qreal *g, qreal *b, qreal *a = nullptr) const; void setRgbF(qreal r, qreal g, qreal b, qreal a = 1.0); - QRgba64 rgba64() const Q_DECL_NOTHROW; - void setRgba64(QRgba64 rgba) Q_DECL_NOTHROW; + QRgba64 rgba64() const noexcept; + void setRgba64(QRgba64 rgba) noexcept; - QRgb rgba() const Q_DECL_NOTHROW; - void setRgba(QRgb rgba) Q_DECL_NOTHROW; + QRgb rgba() const noexcept; + void setRgba(QRgb rgba) noexcept; - QRgb rgb() const Q_DECL_NOTHROW; - void setRgb(QRgb rgb) Q_DECL_NOTHROW; + QRgb rgb() const noexcept; + void setRgb(QRgb rgb) noexcept; - int hue() const Q_DECL_NOTHROW; // 0 <= hue < 360 - int saturation() const Q_DECL_NOTHROW; - int hsvHue() const Q_DECL_NOTHROW; // 0 <= hue < 360 - int hsvSaturation() const Q_DECL_NOTHROW; - int value() const Q_DECL_NOTHROW; + int hue() const noexcept; // 0 <= hue < 360 + int saturation() const noexcept; + int hsvHue() const noexcept; // 0 <= hue < 360 + int hsvSaturation() const noexcept; + int value() const noexcept; - qreal hueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0 - qreal saturationF() const Q_DECL_NOTHROW; - qreal hsvHueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0 - qreal hsvSaturationF() const Q_DECL_NOTHROW; - qreal valueF() const Q_DECL_NOTHROW; + qreal hueF() const noexcept; // 0.0 <= hueF < 360.0 + qreal saturationF() const noexcept; + qreal hsvHueF() const noexcept; // 0.0 <= hueF < 360.0 + qreal hsvSaturationF() const noexcept; + qreal valueF() const noexcept; void getHsv(int *h, int *s, int *v, int *a = nullptr) const; void setHsv(int h, int s, int v, int a = 255); @@ -162,15 +170,15 @@ public: void getHsvF(qreal *h, qreal *s, qreal *v, qreal *a = nullptr) const; void setHsvF(qreal h, qreal s, qreal v, qreal a = 1.0); - int cyan() const Q_DECL_NOTHROW; - int magenta() const Q_DECL_NOTHROW; - int yellow() const Q_DECL_NOTHROW; - int black() const Q_DECL_NOTHROW; + int cyan() const noexcept; + int magenta() const noexcept; + int yellow() const noexcept; + int black() const noexcept; - qreal cyanF() const Q_DECL_NOTHROW; - qreal magentaF() const Q_DECL_NOTHROW; - qreal yellowF() const Q_DECL_NOTHROW; - qreal blackF() const Q_DECL_NOTHROW; + qreal cyanF() const noexcept; + qreal magentaF() const noexcept; + qreal yellowF() const noexcept; + qreal blackF() const noexcept; void getCmyk(int *c, int *m, int *y, int *k, int *a = nullptr); // ### Qt 6: remove void getCmyk(int *c, int *m, int *y, int *k, int *a = nullptr) const; @@ -180,13 +188,13 @@ public: void getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a = nullptr) const; void setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0); - int hslHue() const Q_DECL_NOTHROW; // 0 <= hue < 360 - int hslSaturation() const Q_DECL_NOTHROW; - int lightness() const Q_DECL_NOTHROW; + int hslHue() const noexcept; // 0 <= hue < 360 + int hslSaturation() const noexcept; + int lightness() const noexcept; - qreal hslHueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0 - qreal hslSaturationF() const Q_DECL_NOTHROW; - qreal lightnessF() const Q_DECL_NOTHROW; + qreal hslHueF() const noexcept; // 0.0 <= hueF < 360.0 + qreal hslSaturationF() const noexcept; + qreal lightnessF() const noexcept; void getHsl(int *h, int *s, int *l, int *a = nullptr) const; void setHsl(int h, int s, int l, int a = 255); @@ -194,21 +202,22 @@ public: void getHslF(qreal *h, qreal *s, qreal *l, qreal *a = nullptr) const; void setHslF(qreal h, qreal s, qreal l, qreal a = 1.0); - QColor toRgb() const Q_DECL_NOTHROW; - QColor toHsv() const Q_DECL_NOTHROW; - QColor toCmyk() const Q_DECL_NOTHROW; - QColor toHsl() const Q_DECL_NOTHROW; + QColor toRgb() const noexcept; + QColor toHsv() const noexcept; + QColor toCmyk() const noexcept; + QColor toHsl() const noexcept; + QColor toExtendedRgb() const noexcept; - Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const Q_DECL_NOTHROW; + Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const noexcept; - static QColor fromRgb(QRgb rgb) Q_DECL_NOTHROW; - static QColor fromRgba(QRgb rgba) Q_DECL_NOTHROW; + static QColor fromRgb(QRgb rgb) noexcept; + static QColor fromRgba(QRgb rgba) noexcept; static QColor fromRgb(int r, int g, int b, int a = 255); static QColor fromRgbF(qreal r, qreal g, qreal b, qreal a = 1.0); - static QColor fromRgba64(ushort r, ushort g, ushort b, ushort a = USHRT_MAX) Q_DECL_NOTHROW; - static QColor fromRgba64(QRgba64 rgba) Q_DECL_NOTHROW; + static QColor fromRgba64(ushort r, ushort g, ushort b, ushort a = USHRT_MAX) noexcept; + static QColor fromRgba64(QRgba64 rgba) noexcept; static QColor fromHsv(int h, int s, int v, int a = 255); static QColor fromHsvF(qreal h, qreal s, qreal v, qreal a = 1.0); @@ -221,32 +230,42 @@ public: #if QT_DEPRECATED_SINCE(5, 13) QT_DEPRECATED_X("Use QColor::lighter() instead") - Q_REQUIRED_RESULT QColor light(int f = 150) const Q_DECL_NOTHROW; + Q_REQUIRED_RESULT QColor light(int f = 150) const noexcept; QT_DEPRECATED_X("Use QColor::darker() instead") - Q_REQUIRED_RESULT QColor dark(int f = 200) const Q_DECL_NOTHROW; + Q_REQUIRED_RESULT QColor dark(int f = 200) const noexcept; #endif - Q_REQUIRED_RESULT QColor lighter(int f = 150) const Q_DECL_NOTHROW; - Q_REQUIRED_RESULT QColor darker(int f = 200) const Q_DECL_NOTHROW; + Q_REQUIRED_RESULT QColor lighter(int f = 150) const noexcept; + Q_REQUIRED_RESULT QColor darker(int f = 200) const noexcept; - bool operator==(const QColor &c) const Q_DECL_NOTHROW; - bool operator!=(const QColor &c) const Q_DECL_NOTHROW; + bool operator==(const QColor &c) const noexcept; + bool operator!=(const QColor &c) const noexcept; operator QVariant() const; #if QT_STRINGVIEW_LEVEL < 2 static bool isValidColor(const QString &name); #endif - static bool isValidColor(QStringView) Q_DECL_NOTHROW; - static bool isValidColor(QLatin1String) Q_DECL_NOTHROW; + static bool isValidColor(QStringView) noexcept; + static bool isValidColor(QLatin1String) noexcept; private: - void invalidate() Q_DECL_NOTHROW; + void invalidate() noexcept; template <typename String> bool setColorFromString(String name); + static Q_DECL_CONSTEXPR bool isRgbaValid(int r, int g, int b, int a = 255) noexcept Q_DECL_CONST_FUNCTION + { + return uint(r) <= 255 && uint(g) <= 255 && uint(b) <= 255 && uint(a) <= 255; + } + Spec cspec; - union { + union CT { +#ifdef Q_COMPILER_UNIFORM_INIT + CT() {} // doesn't init anything, thus can't be constexpr + Q_DECL_CONSTEXPR explicit CT(ushort a1, ushort a2, ushort a3, ushort a4, ushort a5) noexcept + : array{a1, a2, a3, a4, a5} {} +#endif struct { ushort alpha; ushort red; @@ -275,6 +294,13 @@ private: ushort lightness; ushort pad; } ahsl; + struct { + ushort alphaF16; + ushort redF16; + ushort greenF16; + ushort blueF16; + ushort pad; + } argbExtended; ushort array[5]; } ct; @@ -283,15 +309,15 @@ private: friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &); friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &); #endif + +#ifdef Q_COMPILER_UNIFORM_INIT +public: // can't give friendship to a namespace, so it needs to be public + Q_DECL_CONSTEXPR explicit QColor(Spec spec, ushort a1, ushort a2, ushort a3, ushort a4, ushort a5=0) noexcept + : cspec(spec), ct(a1, a2, a3, a4, a5) {} +#endif // Q_COMPILER_UNIFORM_INIT }; Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE); -inline QColor::QColor() Q_DECL_NOTHROW -{ invalidate(); } - -inline QColor::QColor(int r, int g, int b, int a) -{ setRgb(r, g, b, a); } - inline QColor::QColor(QLatin1String aname) { setNamedColor(aname); } @@ -303,15 +329,190 @@ inline QColor::QColor(const QString& aname) { setNamedColor(aname); } #endif -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) -inline QColor::QColor(const QColor &acolor) Q_DECL_NOTHROW - : cspec(acolor.cspec) -{ ct.argb = acolor.ct.argb; } -#endif - -inline bool QColor::isValid() const Q_DECL_NOTHROW +inline bool QColor::isValid() const noexcept { return cspec != Invalid; } +// define these namespaces even if the contents are ifdef'd out +namespace QColorConstants +{ +namespace Svg {} + +#if defined(Q_COMPILER_CONSTEXPR) & defined(Q_COMPILER_UNIFORM_INIT) + // Qt::GlobalColor names + constexpr Q_DECL_UNUSED QColor Color0 {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Color1 {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Black {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor White {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkGray {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor Gray {QColor::Rgb, 0xff * 0x101, 0xa0 * 0x101, 0xa0 * 0x101, 0xa4 * 0x101}; + constexpr Q_DECL_UNUSED QColor LightGray {QColor::Rgb, 0xff * 0x101, 0xc0 * 0x101, 0xc0 * 0x101, 0xc0 * 0x101}; + constexpr Q_DECL_UNUSED QColor Red {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Green {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Blue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Cyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Magenta {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Yellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkRed {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkGreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkBlue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkCyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkMagenta {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkYellow {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Transparent {QColor::Rgb, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + + // SVG names supported by QColor (see qcolor.cpp). +namespace Svg { + constexpr Q_DECL_UNUSED QColor aliceblue {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xf8 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor antiquewhite {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xeb * 0x101, 0xd7 * 0x101}; + constexpr Q_DECL_UNUSED QColor aqua {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor aquamarine {QColor::Rgb, 0xff * 0x101, 0x7f * 0x101, 0xff * 0x101, 0xd4 * 0x101}; + constexpr Q_DECL_UNUSED QColor azure {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor beige {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xf5 * 0x101, 0xdc * 0x101}; + constexpr Q_DECL_UNUSED QColor bisque {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xc4 * 0x101}; + constexpr Q_DECL_UNUSED QColor black {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor blanchedalmond {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xeb * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor blue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor blueviolet {QColor::Rgb, 0xff * 0x101, 0x8a * 0x101, 0x2b * 0x101, 0xe2 * 0x101}; + constexpr Q_DECL_UNUSED QColor brown {QColor::Rgb, 0xff * 0x101, 0xa5 * 0x101, 0x2a * 0x101, 0x2a * 0x101}; + constexpr Q_DECL_UNUSED QColor burlywood {QColor::Rgb, 0xff * 0x101, 0xde * 0x101, 0xb8 * 0x101, 0x87 * 0x101}; + constexpr Q_DECL_UNUSED QColor cadetblue {QColor::Rgb, 0xff * 0x101, 0x5f * 0x101, 0x9e * 0x101, 0xa0 * 0x101}; + constexpr Q_DECL_UNUSED QColor chartreuse {QColor::Rgb, 0xff * 0x101, 0x7f * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor chocolate {QColor::Rgb, 0xff * 0x101, 0xd2 * 0x101, 0x69 * 0x101, 0x1e * 0x101}; + constexpr Q_DECL_UNUSED QColor coral {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x7f * 0x101, 0x50 * 0x101}; + constexpr Q_DECL_UNUSED QColor cornflowerblue {QColor::Rgb, 0xff * 0x101, 0x64 * 0x101, 0x95 * 0x101, 0xed * 0x101}; + constexpr Q_DECL_UNUSED QColor cornsilk {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf8 * 0x101, 0xdc * 0x101}; + constexpr Q_DECL_UNUSED QColor crimson {QColor::Rgb, 0xff * 0x101, 0xdc * 0x101, 0x14 * 0x101, 0x3c * 0x101}; + constexpr Q_DECL_UNUSED QColor cyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor darkblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkcyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x8b * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgoldenrod {QColor::Rgb, 0xff * 0x101, 0xb8 * 0x101, 0x86 * 0x101, 0x0b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgray {QColor::Rgb, 0xff * 0x101, 0xa9 * 0x101, 0xa9 * 0x101, 0xa9 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x64 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgrey {QColor::Rgb, 0xff * 0x101, 0xa9 * 0x101, 0xa9 * 0x101, 0xa9 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkkhaki {QColor::Rgb, 0xff * 0x101, 0xbd * 0x101, 0xb7 * 0x101, 0x6b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkmagenta {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x00 * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkolivegreen {QColor::Rgb, 0xff * 0x101, 0x55 * 0x101, 0x6b * 0x101, 0x2f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkorange {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x8c * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkorchid {QColor::Rgb, 0xff * 0x101, 0x99 * 0x101, 0x32 * 0x101, 0xcc * 0x101}; + constexpr Q_DECL_UNUSED QColor darkred {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor darksalmon {QColor::Rgb, 0xff * 0x101, 0xe9 * 0x101, 0x96 * 0x101, 0x7a * 0x101}; + constexpr Q_DECL_UNUSED QColor darkseagreen {QColor::Rgb, 0xff * 0x101, 0x8f * 0x101, 0xbc * 0x101, 0x8f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkslateblue {QColor::Rgb, 0xff * 0x101, 0x48 * 0x101, 0x3d * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkslategray {QColor::Rgb, 0xff * 0x101, 0x2f * 0x101, 0x4f * 0x101, 0x4f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkslategrey {QColor::Rgb, 0xff * 0x101, 0x2f * 0x101, 0x4f * 0x101, 0x4f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkturquoise {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xce * 0x101, 0xd1 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkviolet {QColor::Rgb, 0xff * 0x101, 0x94 * 0x101, 0x00 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor deeppink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x14 * 0x101, 0x93 * 0x101}; + constexpr Q_DECL_UNUSED QColor deepskyblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xbf * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor dimgray {QColor::Rgb, 0xff * 0x101, 0x69 * 0x101, 0x69 * 0x101, 0x69 * 0x101}; + constexpr Q_DECL_UNUSED QColor dimgrey {QColor::Rgb, 0xff * 0x101, 0x69 * 0x101, 0x69 * 0x101, 0x69 * 0x101}; + constexpr Q_DECL_UNUSED QColor dodgerblue {QColor::Rgb, 0xff * 0x101, 0x1e * 0x101, 0x90 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor firebrick {QColor::Rgb, 0xff * 0x101, 0xb2 * 0x101, 0x22 * 0x101, 0x22 * 0x101}; + constexpr Q_DECL_UNUSED QColor floralwhite {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xf0 * 0x101}; + constexpr Q_DECL_UNUSED QColor forestgreen {QColor::Rgb, 0xff * 0x101, 0x22 * 0x101, 0x8b * 0x101, 0x22 * 0x101}; + constexpr Q_DECL_UNUSED QColor fuchsia {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor gainsboro {QColor::Rgb, 0xff * 0x101, 0xdc * 0x101, 0xdc * 0x101, 0xdc * 0x101}; + constexpr Q_DECL_UNUSED QColor ghostwhite {QColor::Rgb, 0xff * 0x101, 0xf8 * 0x101, 0xf8 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor gold {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xd7 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor goldenrod {QColor::Rgb, 0xff * 0x101, 0xda * 0x101, 0xa5 * 0x101, 0x20 * 0x101}; + constexpr Q_DECL_UNUSED QColor gray {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor green {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor greenyellow {QColor::Rgb, 0xff * 0x101, 0xad * 0x101, 0xff * 0x101, 0x2f * 0x101}; + constexpr Q_DECL_UNUSED QColor grey {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor honeydew {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xff * 0x101, 0xf0 * 0x101}; + constexpr Q_DECL_UNUSED QColor hotpink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x69 * 0x101, 0xb4 * 0x101}; + constexpr Q_DECL_UNUSED QColor indianred {QColor::Rgb, 0xff * 0x101, 0xcd * 0x101, 0x5c * 0x101, 0x5c * 0x101}; + constexpr Q_DECL_UNUSED QColor indigo {QColor::Rgb, 0xff * 0x101, 0x4b * 0x101, 0x00 * 0x101, 0x82 * 0x101}; + constexpr Q_DECL_UNUSED QColor ivory {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xf0 * 0x101}; + constexpr Q_DECL_UNUSED QColor khaki {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xe6 * 0x101, 0x8c * 0x101}; + constexpr Q_DECL_UNUSED QColor lavender {QColor::Rgb, 0xff * 0x101, 0xe6 * 0x101, 0xe6 * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor lavenderblush {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf0 * 0x101, 0xf5 * 0x101}; + constexpr Q_DECL_UNUSED QColor lawngreen {QColor::Rgb, 0xff * 0x101, 0x7c * 0x101, 0xfc * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor lemonchiffon {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor lightblue {QColor::Rgb, 0xff * 0x101, 0xad * 0x101, 0xd8 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightcoral {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightcyan {QColor::Rgb, 0xff * 0x101, 0xe0 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgoldenrodyellow {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xfa * 0x101, 0xd2 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgray {QColor::Rgb, 0xff * 0x101, 0xd3 * 0x101, 0xd3 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgreen {QColor::Rgb, 0xff * 0x101, 0x90 * 0x101, 0xee * 0x101, 0x90 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgrey {QColor::Rgb, 0xff * 0x101, 0xd3 * 0x101, 0xd3 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightpink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xb6 * 0x101, 0xc1 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightsalmon {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xa0 * 0x101, 0x7a * 0x101}; + constexpr Q_DECL_UNUSED QColor lightseagreen {QColor::Rgb, 0xff * 0x101, 0x20 * 0x101, 0xb2 * 0x101, 0xaa * 0x101}; + constexpr Q_DECL_UNUSED QColor lightskyblue {QColor::Rgb, 0xff * 0x101, 0x87 * 0x101, 0xce * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor lightslategray {QColor::Rgb, 0xff * 0x101, 0x77 * 0x101, 0x88 * 0x101, 0x99 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightslategrey {QColor::Rgb, 0xff * 0x101, 0x77 * 0x101, 0x88 * 0x101, 0x99 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightsteelblue {QColor::Rgb, 0xff * 0x101, 0xb0 * 0x101, 0xc4 * 0x101, 0xde * 0x101}; + constexpr Q_DECL_UNUSED QColor lightyellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xe0 * 0x101}; + constexpr Q_DECL_UNUSED QColor lime {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor limegreen {QColor::Rgb, 0xff * 0x101, 0x32 * 0x101, 0xcd * 0x101, 0x32 * 0x101}; + constexpr Q_DECL_UNUSED QColor linen {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xf0 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor magenta {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor maroon {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumaquamarine {QColor::Rgb, 0xff * 0x101, 0x66 * 0x101, 0xcd * 0x101, 0xaa * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumorchid {QColor::Rgb, 0xff * 0x101, 0xba * 0x101, 0x55 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumpurple {QColor::Rgb, 0xff * 0x101, 0x93 * 0x101, 0x70 * 0x101, 0xdb * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumseagreen {QColor::Rgb, 0xff * 0x101, 0x3c * 0x101, 0xb3 * 0x101, 0x71 * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumslateblue {QColor::Rgb, 0xff * 0x101, 0x7b * 0x101, 0x68 * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumspringgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xfa * 0x101, 0x9a * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumturquoise {QColor::Rgb, 0xff * 0x101, 0x48 * 0x101, 0xd1 * 0x101, 0xcc * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumvioletred {QColor::Rgb, 0xff * 0x101, 0xc7 * 0x101, 0x15 * 0x101, 0x85 * 0x101}; + constexpr Q_DECL_UNUSED QColor midnightblue {QColor::Rgb, 0xff * 0x101, 0x19 * 0x101, 0x19 * 0x101, 0x70 * 0x101}; + constexpr Q_DECL_UNUSED QColor mintcream {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xff * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor mistyrose {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xe1 * 0x101}; + constexpr Q_DECL_UNUSED QColor moccasin {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xb5 * 0x101}; + constexpr Q_DECL_UNUSED QColor navajowhite {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xde * 0x101, 0xad * 0x101}; + constexpr Q_DECL_UNUSED QColor navy {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor oldlace {QColor::Rgb, 0xff * 0x101, 0xfd * 0x101, 0xf5 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor olive {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor olivedrab {QColor::Rgb, 0xff * 0x101, 0x6b * 0x101, 0x8e * 0x101, 0x23 * 0x101}; + constexpr Q_DECL_UNUSED QColor orange {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xa5 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor orangered {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x45 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor orchid {QColor::Rgb, 0xff * 0x101, 0xda * 0x101, 0x70 * 0x101, 0xd6 * 0x101}; + constexpr Q_DECL_UNUSED QColor palegoldenrod {QColor::Rgb, 0xff * 0x101, 0xee * 0x101, 0xe8 * 0x101, 0xaa * 0x101}; + constexpr Q_DECL_UNUSED QColor palegreen {QColor::Rgb, 0xff * 0x101, 0x98 * 0x101, 0xfb * 0x101, 0x98 * 0x101}; + constexpr Q_DECL_UNUSED QColor paleturquoise {QColor::Rgb, 0xff * 0x101, 0xaf * 0x101, 0xee * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor palevioletred {QColor::Rgb, 0xff * 0x101, 0xdb * 0x101, 0x70 * 0x101, 0x93 * 0x101}; + constexpr Q_DECL_UNUSED QColor papayawhip {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xef * 0x101, 0xd5 * 0x101}; + constexpr Q_DECL_UNUSED QColor peachpuff {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xda * 0x101, 0xb9 * 0x101}; + constexpr Q_DECL_UNUSED QColor peru {QColor::Rgb, 0xff * 0x101, 0xcd * 0x101, 0x85 * 0x101, 0x3f * 0x101}; + constexpr Q_DECL_UNUSED QColor pink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xc0 * 0x101, 0xcb * 0x101}; + constexpr Q_DECL_UNUSED QColor plum {QColor::Rgb, 0xff * 0x101, 0xdd * 0x101, 0xa0 * 0x101, 0xdd * 0x101}; + constexpr Q_DECL_UNUSED QColor powderblue {QColor::Rgb, 0xff * 0x101, 0xb0 * 0x101, 0xe0 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor purple {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor red {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor rosybrown {QColor::Rgb, 0xff * 0x101, 0xbc * 0x101, 0x8f * 0x101, 0x8f * 0x101}; + constexpr Q_DECL_UNUSED QColor royalblue {QColor::Rgb, 0xff * 0x101, 0x41 * 0x101, 0x69 * 0x101, 0xe1 * 0x101}; + constexpr Q_DECL_UNUSED QColor saddlebrown {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x45 * 0x101, 0x13 * 0x101}; + constexpr Q_DECL_UNUSED QColor salmon {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0x80 * 0x101, 0x72 * 0x101}; + constexpr Q_DECL_UNUSED QColor sandybrown {QColor::Rgb, 0xff * 0x101, 0xf4 * 0x101, 0xa4 * 0x101, 0x60 * 0x101}; + constexpr Q_DECL_UNUSED QColor seagreen {QColor::Rgb, 0xff * 0x101, 0x2e * 0x101, 0x8b * 0x101, 0x57 * 0x101}; + constexpr Q_DECL_UNUSED QColor seashell {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf5 * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor sienna {QColor::Rgb, 0xff * 0x101, 0xa0 * 0x101, 0x52 * 0x101, 0x2d * 0x101}; + constexpr Q_DECL_UNUSED QColor silver {QColor::Rgb, 0xff * 0x101, 0xc0 * 0x101, 0xc0 * 0x101, 0xc0 * 0x101}; + constexpr Q_DECL_UNUSED QColor skyblue {QColor::Rgb, 0xff * 0x101, 0x87 * 0x101, 0xce * 0x101, 0xeb * 0x101}; + constexpr Q_DECL_UNUSED QColor slateblue {QColor::Rgb, 0xff * 0x101, 0x6a * 0x101, 0x5a * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor slategray {QColor::Rgb, 0xff * 0x101, 0x70 * 0x101, 0x80 * 0x101, 0x90 * 0x101}; + constexpr Q_DECL_UNUSED QColor slategrey {QColor::Rgb, 0xff * 0x101, 0x70 * 0x101, 0x80 * 0x101, 0x90 * 0x101}; + constexpr Q_DECL_UNUSED QColor snow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor springgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x7f * 0x101}; + constexpr Q_DECL_UNUSED QColor steelblue {QColor::Rgb, 0xff * 0x101, 0x46 * 0x101, 0x82 * 0x101, 0xb4 * 0x101}; + constexpr Q_DECL_UNUSED QColor tan {QColor::Rgb, 0xff * 0x101, 0xd2 * 0x101, 0xb4 * 0x101, 0x8c * 0x101}; + constexpr Q_DECL_UNUSED QColor teal {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor thistle {QColor::Rgb, 0xff * 0x101, 0xd8 * 0x101, 0xbf * 0x101, 0xd8 * 0x101}; + constexpr Q_DECL_UNUSED QColor tomato {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x63 * 0x101, 0x47 * 0x101}; + constexpr Q_DECL_UNUSED QColor turquoise {QColor::Rgb, 0xff * 0x101, 0x40 * 0x101, 0xe0 * 0x101, 0xd0 * 0x101}; + constexpr Q_DECL_UNUSED QColor violet {QColor::Rgb, 0xff * 0x101, 0xee * 0x101, 0x82 * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor wheat {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xde * 0x101, 0xb3 * 0x101}; + constexpr Q_DECL_UNUSED QColor white {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor whitesmoke {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xf5 * 0x101, 0xf5 * 0x101}; + constexpr Q_DECL_UNUSED QColor yellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor yellowgreen {QColor::Rgb, 0xff * 0x101, 0x9a * 0x101, 0xcd * 0x101, 0x32 * 0x101}; +} // namespace Svg +#endif // Q_COMPILER_CONSTEXPR && Q_COMPILER_UNIFORM_INIT +} // namespace QColorLiterals + QT_END_NAMESPACE #endif // QCOLOR_H diff --git a/src/gui/painting/qcolormatrix_p.h b/src/gui/painting/qcolormatrix_p.h new file mode 100644 index 0000000000..edb2d32258 --- /dev/null +++ b/src/gui/painting/qcolormatrix_p.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** 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 QCOLORMATRIX_H +#define QCOLORMATRIX_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/qtguiglobal.h> +#include <QtCore/qpoint.h> +#include <cmath> + +QT_BEGIN_NAMESPACE + +// An abstract 3 value color +class QColorVector +{ +public: + QColorVector() = default; + Q_DECL_CONSTEXPR QColorVector(float x, float y, float z) : x(x), y(y), z(z) { } + explicit Q_DECL_CONSTEXPR QColorVector(const QPointF &chr) // from XY chromaticity + : x(chr.x() / chr.y()) + , y(1.0f) + , z((1.0 - chr.x() - chr.y()) / chr.y()) + { } + float x = 0.0f; // X, x or red + float y = 0.0f; // Y, y or green + float z = 0.0f; // Z, Y or blue + float _unused = 0.0f; + + friend inline bool operator==(const QColorVector &v1, const QColorVector &v2); + friend inline bool operator!=(const QColorVector &v1, const QColorVector &v2); + bool isNull() const + { + return !x && !y && !z; + } + + static bool isValidChromaticity(const QPointF &chr) + { + if (chr.x() < qreal(0.0) || chr.x() > qreal(1.0)) + return false; + if (chr.y() <= qreal(0.0) || chr.y() > qreal(1.0)) + return false; + if (chr.x() + chr.y() > qreal(1.0)) + return false; + return true; + } + + // Common whitepoints: + static Q_DECL_CONSTEXPR QPointF D50Chromaticity() { return QPointF(0.34567, 0.35850); } + static Q_DECL_CONSTEXPR QPointF D65Chromaticity() { return QPointF(0.31271, 0.32902); } + static Q_DECL_CONSTEXPR QColorVector D50() { return QColorVector(D50Chromaticity()); } + static Q_DECL_CONSTEXPR QColorVector D65() { return QColorVector(D65Chromaticity()); } +}; + +inline bool operator==(const QColorVector &v1, const QColorVector &v2) +{ + return (std::abs(v1.x - v2.x) < (1.0f / 2048.0f)) + && (std::abs(v1.y - v2.y) < (1.0f / 2048.0f)) + && (std::abs(v1.z - v2.z) < (1.0f / 2048.0f)); +} + +inline bool operator!=(const QColorVector &v1, const QColorVector &v2) +{ + return !(v1 == v2); +} + + +// A matrix mapping 3 value colors. +// Not using QMatrix because only floats are needed and performance is critical. +class QColorMatrix +{ +public: + // We are storing the matrix transposed as that is more convenient: + QColorVector r; + QColorVector g; + QColorVector b; + + friend inline bool operator==(const QColorMatrix &m1, const QColorMatrix &m2); + friend inline bool operator!=(const QColorMatrix &m1, const QColorMatrix &m2); + + bool isNull() const + { + return r.isNull() && g.isNull() && b.isNull(); + } + bool isValid() const + { + // A color matrix must be invertible + float det = r.x * (b.z * g.y - g.z * b.y) - + r.y * (b.z * g.x - g.z * b.x) + + r.z * (b.y * g.x - g.y * b.x); + return !qFuzzyIsNull(det); + } + + QColorMatrix inverted() const + { + float det = r.x * (b.z * g.y - g.z * b.y) - + r.y * (b.z * g.x - g.z * b.x) + + r.z * (b.y * g.x - g.y * b.x); + det = 1.0f / det; + QColorMatrix inv; + inv.r.x = (g.y * b.z - b.y * g.z) * det; + inv.r.y = (b.y * r.z - r.y * b.z) * det; + inv.r.z = (r.y * g.z - g.y * r.z) * det; + inv.g.x = (b.x * g.z - g.x * b.z) * det; + inv.g.y = (r.x * b.z - b.x * r.z) * det; + inv.g.z = (g.x * r.z - r.x * g.z) * det; + inv.b.x = (g.x * b.y - b.x * g.y) * det; + inv.b.y = (b.x * r.y - r.x * b.y) * det; + inv.b.z = (r.x * g.y - g.x * r.y) * det; + return inv; + } + QColorMatrix operator*(const QColorMatrix &o) const + { + QColorMatrix comb; + comb.r.x = r.x * o.r.x + g.x * o.r.y + b.x * o.r.z; + comb.g.x = r.x * o.g.x + g.x * o.g.y + b.x * o.g.z; + comb.b.x = r.x * o.b.x + g.x * o.b.y + b.x * o.b.z; + + comb.r.y = r.y * o.r.x + g.y * o.r.y + b.y * o.r.z; + comb.g.y = r.y * o.g.x + g.y * o.g.y + b.y * o.g.z; + comb.b.y = r.y * o.b.x + g.y * o.b.y + b.y * o.b.z; + + comb.r.z = r.z * o.r.x + g.z * o.r.y + b.z * o.r.z; + comb.g.z = r.z * o.g.x + g.z * o.g.y + b.z * o.g.z; + comb.b.z = r.z * o.b.x + g.z * o.b.y + b.z * o.b.z; + return comb; + + } + QColorVector map(const QColorVector &c) const + { + return QColorVector { c.x * r.x + c.y * g.x + c.z * b.x, + c.x * r.y + c.y * g.y + c.z * b.y, + c.x * r.z + c.y * g.z + c.z * b.z }; + } + QColorMatrix transposed() const + { + return QColorMatrix { { r.x, g.x, b.x }, + { r.y, g.y, b.y }, + { r.z, g.z, b.z } }; + } + + static QColorMatrix identity() + { + return { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; + } + static QColorMatrix fromScale(QColorVector v) + { + return QColorMatrix { { v.x, 0.0f, 0.0f }, + { 0.0f, v.y, 0.0f }, + { 0.0f, 0.0f, v.z } }; + } + // These are used to recognize matrices from ICC profiles: + static QColorMatrix toXyzFromSRgb() + { + return QColorMatrix { { 0.4360217452f, 0.2224751115f, 0.0139281144f }, + { 0.3851087987f, 0.7169067264f, 0.0971015394f }, + { 0.1430812478f, 0.0606181994f, 0.7141585946f } }; + } + static QColorMatrix toXyzFromAdobeRgb() + { + return QColorMatrix { { 0.6097189188f, 0.3111021519f, 0.0194766335f }, + { 0.2052682191f, 0.6256770492f, 0.0608891509f }, + { 0.1492247432f, 0.0632209629f, 0.7448224425f } }; + } + static QColorMatrix toXyzFromDciP3D65() + { + return QColorMatrix { { 0.5150973201f, 0.2411795557f, -0.0010491034f }, + { 0.2919696569f, 0.6922441125f, 0.0418830328f }, + { 0.1571449190f, 0.0665764511f, 0.7843542695f } }; + } + static QColorMatrix toXyzFromProPhotoRgb() + { + return QColorMatrix { { 0.7976672649f, 0.2880374491f, 0.0000000000f }, + { 0.1351922452f, 0.7118769884f, 0.0000000000f }, + { 0.0313525312f, 0.0000856627f, 0.8251883388f } }; + } +}; + +inline bool operator==(const QColorMatrix &m1, const QColorMatrix &m2) +{ + return (m1.r == m2.r) && (m1.g == m2.g) && (m1.b == m2.b); +} + +inline bool operator!=(const QColorMatrix &m1, const QColorMatrix &m2) +{ + return !(m1 == m2); +} + +QT_END_NAMESPACE + +#endif // QCOLORMATRIX_P_H diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp new file mode 100644 index 0000000000..937bb505c9 --- /dev/null +++ b/src/gui/painting/qcolorspace.cpp @@ -0,0 +1,796 @@ +/**************************************************************************** +** +** 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 "qcolorspace.h" +#include "qcolorspace_p.h" + +#include "qcolortransform.h" +#include "qcolormatrix_p.h" +#include "qcolortransferfunction_p.h" +#include "qcolortransform_p.h" +#include "qicc_p.h" + +#include <qmath.h> +#include <qtransform.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QBasicMutex QColorSpacePrivate::s_lutWriteLock; + +QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries) +{ + switch (primaries) { + case QColorSpace::Primaries::SRgb: + redPoint = QPointF(0.640, 0.330); + greenPoint = QPointF(0.300, 0.600); + bluePoint = QPointF(0.150, 0.060); + whitePoint = QColorVector::D65Chromaticity(); + break; + case QColorSpace::Primaries::DciP3D65: + redPoint = QPointF(0.680, 0.320); + greenPoint = QPointF(0.265, 0.690); + bluePoint = QPointF(0.150, 0.060); + whitePoint = QColorVector::D65Chromaticity(); + break; + case QColorSpace::Primaries::AdobeRgb: + redPoint = QPointF(0.640, 0.330); + greenPoint = QPointF(0.210, 0.710); + bluePoint = QPointF(0.150, 0.060); + whitePoint = QColorVector::D65Chromaticity(); + break; + case QColorSpace::Primaries::ProPhotoRgb: + redPoint = QPointF(0.7347, 0.2653); + greenPoint = QPointF(0.1596, 0.8404); + bluePoint = QPointF(0.0366, 0.0001); + whitePoint = QColorVector::D50Chromaticity(); + break; + default: + Q_UNREACHABLE(); + } +} + +bool QColorSpacePrimaries::areValid() const +{ + if (!QColorVector::isValidChromaticity(redPoint)) + return false; + if (!QColorVector::isValidChromaticity(greenPoint)) + return false; + if (!QColorVector::isValidChromaticity(bluePoint)) + return false; + if (!QColorVector::isValidChromaticity(whitePoint)) + return false; + return true; +} + +QColorMatrix QColorSpacePrimaries::toXyzMatrix() const +{ + // This converts to XYZ in some undefined scale. + QColorMatrix toXyz = { QColorVector(redPoint), + QColorVector(greenPoint), + QColorVector(bluePoint) }; + + // Since the white point should be (1.0, 1.0, 1.0) in the + // input, we can figure out the scale by using the + // inverse conversion on the white point. + QColorVector wXyz(whitePoint); + QColorVector whiteScale = toXyz.inverted().map(wXyz); + + // Now we have scaled conversion to XYZ relative to the given whitepoint + toXyz = toXyz * QColorMatrix::fromScale(whiteScale); + + // But we want a conversion to XYZ relative to D50 + QColorVector wXyzD50 = QColorVector::D50(); + + if (wXyz != wXyzD50) { + // Do chromatic adaptation to map our white point to XYZ D50. + + // The Bradford method chromatic adaptation matrix: + QColorMatrix abrad = { { 0.8951f, -0.7502f, 0.0389f }, + { 0.2664f, 1.7135f, -0.0685f }, + { -0.1614f, 0.0367f, 1.0296f } }; + QColorMatrix abradinv = { { 0.9869929f, 0.4323053f, -0.0085287f }, + { -0.1470543f, 0.5183603f, 0.0400428f }, + { 0.1599627f, 0.0492912f, 0.9684867f } }; + + QColorVector srcCone = abrad.map(wXyz); + QColorVector dstCone = abrad.map(wXyzD50); + + QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 }, + { 0, dstCone.y / srcCone.y, 0 }, + { 0, 0, dstCone.z / srcCone.z } }; + + + QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad); + toXyz = chromaticAdaptation * toXyz; + } + + return toXyz; +} + +QColorSpacePrivate::QColorSpacePrivate() +{ +} + +QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace) + : namedColorSpace(namedColorSpace) +{ + switch (namedColorSpace) { + case QColorSpace::SRgb: + primaries = QColorSpace::Primaries::SRgb; + transferFunction = QColorSpace::TransferFunction::SRgb; + description = QStringLiteral("sRGB"); + break; + case QColorSpace::SRgbLinear: + primaries = QColorSpace::Primaries::SRgb; + transferFunction = QColorSpace::TransferFunction::Linear; + description = QStringLiteral("Linear sRGB"); + break; + case QColorSpace::AdobeRgb: + primaries = QColorSpace::Primaries::AdobeRgb; + transferFunction = QColorSpace::TransferFunction::Gamma; + gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf + description = QStringLiteral("Adobe RGB"); + break; + case QColorSpace::DisplayP3: + primaries = QColorSpace::Primaries::DciP3D65; + transferFunction = QColorSpace::TransferFunction::SRgb; + description = QStringLiteral("Display P3"); + break; + case QColorSpace::ProPhotoRgb: + primaries = QColorSpace::Primaries::ProPhotoRgb; + transferFunction = QColorSpace::TransferFunction::ProPhotoRgb; + description = QStringLiteral("ProPhoto RGB"); + break; + default: + Q_UNREACHABLE(); + } + initialize(); +} + +QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma) + : primaries(primaries) + , transferFunction(fun) + , gamma(gamma) +{ + identifyColorSpace(); + initialize(); +} + +QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, + QColorSpace::TransferFunction fun, + float gamma) + : primaries(QColorSpace::Primaries::Custom) + , transferFunction(fun) + , gamma(gamma) +{ + Q_ASSERT(primaries.areValid()); + toXyz = primaries.toXyzMatrix(); + whitePoint = QColorVector(primaries.whitePoint); + identifyColorSpace(); + setTransferFunction(); +} + +void QColorSpacePrivate::identifyColorSpace() +{ + switch (primaries) { + case QColorSpace::Primaries::SRgb: + if (transferFunction == QColorSpace::TransferFunction::SRgb) { + namedColorSpace = QColorSpace::SRgb; + if (description.isEmpty()) + description = QStringLiteral("sRGB"); + return; + } + if (transferFunction == QColorSpace::TransferFunction::Linear) { + namedColorSpace = QColorSpace::SRgbLinear; + if (description.isEmpty()) + description = QStringLiteral("Linear sRGB"); + return; + } + break; + case QColorSpace::Primaries::AdobeRgb: + if (transferFunction == QColorSpace::TransferFunction::Gamma) { + if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) { + namedColorSpace = QColorSpace::AdobeRgb; + if (description.isEmpty()) + description = QStringLiteral("Adobe RGB"); + return; + } + } + break; + case QColorSpace::Primaries::DciP3D65: + if (transferFunction == QColorSpace::TransferFunction::SRgb) { + namedColorSpace = QColorSpace::DisplayP3; + if (description.isEmpty()) + description = QStringLiteral("Display P3"); + return; + } + break; + case QColorSpace::Primaries::ProPhotoRgb: + if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) { + namedColorSpace = QColorSpace::ProPhotoRgb; + if (description.isEmpty()) + description = QStringLiteral("ProPhoto RGB"); + return; + } + if (transferFunction == QColorSpace::TransferFunction::Gamma) { + // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision. + if (qAbs(gamma - 1.8f) < (1/1024.0f)) { + namedColorSpace = QColorSpace::ProPhotoRgb; + if (description.isEmpty()) + description = QStringLiteral("ProPhoto RGB"); + return; + } + } + break; + default: + break; + } + + namedColorSpace = Unknown; +} + +void QColorSpacePrivate::initialize() +{ + setToXyzMatrix(); + setTransferFunction(); +} + +void QColorSpacePrivate::setToXyzMatrix() +{ + if (primaries == QColorSpace::Primaries::Custom) { + toXyz = QColorMatrix(); + whitePoint = QColorVector::D50(); + return; + } + QColorSpacePrimaries colorSpacePrimaries(primaries); + toXyz = colorSpacePrimaries.toXyzMatrix(); + whitePoint = QColorVector(colorSpacePrimaries.whitePoint); +} + +void QColorSpacePrivate::setTransferFunction() +{ + switch (transferFunction) { + case QColorSpace::TransferFunction::Linear: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction(); + if (qFuzzyIsNull(gamma)) + gamma = 1.0f; + break; + case QColorSpace::TransferFunction::Gamma: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromGamma(gamma); + break; + case QColorSpace::TransferFunction::SRgb: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromSRgb(); + if (qFuzzyIsNull(gamma)) + gamma = 2.31f; + break; + case QColorSpace::TransferFunction::ProPhotoRgb: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb(); + if (qFuzzyIsNull(gamma)) + gamma = 1.8f; + break; + case QColorSpace::TransferFunction::Custom: + break; + default: + Q_UNREACHABLE(); + break; + } + trc[1] = trc[0]; + trc[2] = trc[0]; +} + +QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const +{ + Q_ASSERT(out); + QColorTransform combined; + auto ptr = new QColorTransformPrivate; + combined.d = ptr; + combined.d->ref.ref(); + ptr->colorSpaceIn = this; + ptr->colorSpaceOut = out; + ptr->colorMatrix = out->toXyz.inverted() * toXyz; + return combined; +} + +/*! + \class QColorSpace + \brief The QColorSpace class provides a color space abstraction. + \since 5.14 + + \ingroup painting + \ingroup appearance + \inmodule QtGui + + Color values can be interpreted in different ways, and based on the interpretation + can live in different spaces. We call this \e {color spaces}. + + QColorSpace provides access to creating several predefined color spaces and + can generate QColorTransforms for converting colors from one color space to + another. + + QColorSpace can also represent color spaces defined by ICC profiles or embedded + in images, that do not otherwise fit the predefined color spaces. + + A color space can generally speaking be conceived as a combination of set of primary + colors and a transfer function. The primaries defines the axes of the color space, and + the transfer function how values are mapped on the axes. + The primaries are defined by three primary colors that represent exactly how red, green, + and blue look in this particular color space, and a white color that represents where + and how bright pure white is. The range of colors expressable by the primary colors is + called the gamut, and a color space that can represent a wider range of colors is also + known as a wide-gamut color space. + + The transfer function or gamma curve determines how each component in the + color space is encoded. These are used because human perception does not operate + linearly, and the transfer functions try to ensure that colors will seem evenly + spaced to human eyes. +*/ + + +/*! + \enum QColorSpace::NamedColorSpace + + Predefined color spaces. + + \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation + of how most classic monitors operate, and a mode most software and hardware support. + \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}. + \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending. + \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2. + \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)} + \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer + function of sRGB. Common in modern wide-gamut screens. + \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3} + \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space. + \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB} +*/ + +/*! + \enum QColorSpace::Primaries + + Predefined sets of primary colors. + + \value Custom The primaries are undefined or does not match any predefined sets. + \value SRgb The sRGB primaries + \value AdobeRgb The Adobe RGB primaries + \value DciP3D65 The DCI-P3 primaries with the D65 whitepoint + \value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint +*/ + +/*! + \enum QColorSpace::TransferFunction + + Predefined transfer functions or gamma curves. + + \value Custom The custom or null transfer function + \value Linear The linear transfer functions + \value Gamma A transfer function that is a real gamma curve based on the value of gamma() + \value SRgb The sRGB transfer function, composed of linear and gamma parts + \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts +*/ + +/*! + Creates a new colorspace object that represents an undefined and invalid colorspace. + */ +QColorSpace::QColorSpace() +{ +} + +/*! + Creates a new colorspace object that represents a \a namedColorSpace. + */ +QColorSpace::QColorSpace(NamedColorSpace namedColorSpace) +{ + static QColorSpacePrivate *predefinedColorspacePrivates[QColorSpace::ProPhotoRgb + 1]; + if (!predefinedColorspacePrivates[namedColorSpace]) { + predefinedColorspacePrivates[namedColorSpace] = new QColorSpacePrivate(namedColorSpace); + predefinedColorspacePrivates[namedColorSpace]->ref.ref(); + } + d_ptr = predefinedColorspacePrivates[namedColorSpace]; + d_ptr->ref.ref(); + Q_ASSERT(isValid()); +} + +/*! + Creates a custom color space with the primaries \a primaries, using the transfer function \a fun and + optionally \a gamma. + */ +QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma) + : d_ptr(new QColorSpacePrivate(primaries, fun, gamma)) +{ + d_ptr->ref.ref(); +} + +/*! + Creates a custom color space with the primaries \a primaries, using a gamma transfer function of + \a gamma. + */ +QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma) + : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma)) +{ + d_ptr->ref.ref(); +} + +/*! + Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint, + \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a fun and optionally \a gamma. + */ +QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint, + QColorSpace::TransferFunction fun, float gamma) +{ + QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint); + if (!primaries.areValid()) { + qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint; + d_ptr = nullptr; + return; + } + d_ptr = new QColorSpacePrivate(primaries, fun, gamma); + d_ptr->ref.ref(); +} + +QColorSpace::~QColorSpace() +{ + if (d_ptr && !d_ptr->ref.deref()) + delete d_ptr; +} + +QColorSpace::QColorSpace(const QColorSpace &colorSpace) + : d_ptr(colorSpace.d_ptr) +{ + if (d_ptr) + d_ptr->ref.ref(); +} + +QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace) +{ + QColorSpacePrivate *oldD = d_ptr; + d_ptr = colorSpace.d_ptr; + if (d_ptr) + d_ptr->ref.ref(); + if (oldD && !oldD->ref.deref()) + delete oldD; + return *this; +} + +/*! \fn void QColorSpace::swap(QColorSpace &other) + + Swaps color space \a other with this color space. This operation is very fast and + never fails. +*/ + +/*! + Returns the predefined primaries of the color space + or \c primaries::Custom if it doesn't match any of them. +*/ +QColorSpace::Primaries QColorSpace::primaries() const noexcept +{ + if (Q_UNLIKELY(!d_ptr)) + return QColorSpace::Primaries::Custom; + return d_ptr->primaries; +} + +/*! + Returns the predefined transfer function of the color space + or \c TransferFunction::Custom if it doesn't match any of them. + + \sa gamma(), setTransferFunction(), withTransferFunction() +*/ +QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept +{ + if (Q_UNLIKELY(!d_ptr)) + return QColorSpace::TransferFunction::Custom; + return d_ptr->transferFunction; +} + +/*! + Returns the gamma value of color spaces with \c TransferFunction::Gamma, + an approximate gamma value for other predefined color spaces, or + 0.0 if no approximate gamma is known. + + \sa transferFunction() +*/ +float QColorSpace::gamma() const noexcept +{ + if (Q_UNLIKELY(!d_ptr)) + return 0.0f; + return d_ptr->gamma; +} + +/*! + Sets the transfer function to \a transferFunction and \a gamma. + + \sa transferFunction(), gamma(), withTransferFunction() +*/ +void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) +{ + if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + return; + if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) + return; + QColorSpacePrivate::getWritable(*this); // detach + d_ptr->description.clear(); + d_ptr->transferFunction = transferFunction; + d_ptr->gamma = gamma; + d_ptr->identifyColorSpace(); + d_ptr->setTransferFunction(); +} + +/*! + Returns a copy of this color space, except using the transfer function + \a transferFunction and \a gamma. + + \sa transferFunction(), gamma(), setTransferFunction() +*/ +QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const +{ + if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + return *this; + if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) + return *this; + QColorSpace out(*this); + out.setTransferFunction(transferFunction, gamma); + return out; +} + +/*! + Sets the primaries to those of the \a primariesId set. + + \sa primaries() +*/ +void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId) +{ + if (!isValid() || primariesId == QColorSpace::Primaries::Custom) + return; + if (d_ptr->primaries == primariesId) + return; + QColorSpacePrivate::getWritable(*this); // detach + d_ptr->description.clear(); + d_ptr->primaries = primariesId; + d_ptr->identifyColorSpace(); + d_ptr->setToXyzMatrix(); +} + +/*! + Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint + and \a bluePoint. + + \sa primaries() +*/ +void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint) +{ + if (!isValid()) + return; + QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint); + if (!primaries.areValid()) + return; + QColorMatrix toXyz = primaries.toXyzMatrix(); + if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz) + return; + QColorSpacePrivate::getWritable(*this); // detach + d_ptr->description.clear(); + d_ptr->primaries = QColorSpace::Primaries::Custom; + d_ptr->toXyz = toXyz; + d_ptr->whitePoint = QColorVector(primaries.whitePoint); + d_ptr->identifyColorSpace(); +} + +/*! + Returns an ICC profile representing the color space. + + If the color space was generated from an ICC profile, that profile + is returned, otherwise one is generated. + + \note Even invalid color spaces may return the ICC profile if they + were generated from one, to allow applications to implement wider + support themselves. + + \sa fromIccProfile() +*/ +QByteArray QColorSpace::iccProfile() const +{ + if (Q_UNLIKELY(!d_ptr)) + return QByteArray(); + if (!d_ptr->iccProfile.isEmpty()) + return d_ptr->iccProfile; + if (!isValid()) + return QByteArray(); + return QIcc::toIccProfile(*this); +} + +/*! + Creates a QColorSpace from ICC profile \a iccProfile. + + \note Not all ICC profiles are supported. QColorSpace only supports + RGB-XYZ ICC profiles that are three-component matrix-based. + + If the ICC profile is not supported an invalid QColorSpace is returned + where you can still read the original ICC profile using iccProfile(). + + \sa iccProfile() +*/ +QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile) +{ + QColorSpace colorSpace; + if (QIcc::fromIccProfile(iccProfile, &colorSpace)) + return colorSpace; + QColorSpacePrivate *d = QColorSpacePrivate::getWritable(colorSpace); + d->iccProfile = iccProfile; + return colorSpace; +} + +/*! + Returns \c true if the color space is valid. +*/ +bool QColorSpace::isValid() const noexcept +{ + return d_ptr + && d_ptr->toXyz.isValid() + && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid(); +} + +/*! + \relates QColorSpace + Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2; + otherwise returns \c false +*/ +bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) +{ + if (colorSpace1.d_ptr == colorSpace2.d_ptr) + return true; + if (!colorSpace1.d_ptr || !colorSpace2.d_ptr) + return false; + + if (colorSpace1.d_ptr->namedColorSpace && colorSpace2.d_ptr->namedColorSpace) + return colorSpace1.d_ptr->namedColorSpace == colorSpace2.d_ptr->namedColorSpace; + + const bool valid1 = colorSpace1.isValid(); + const bool valid2 = colorSpace2.isValid(); + if (!valid1 && !valid2) + return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile; + else if (!valid1 || !valid2) + return false; + + // At this point one or both color spaces are unknown but valid, and must be compared in detail instead + + if (colorSpace1.primaries() != QColorSpace::Primaries::Custom && colorSpace2.primaries() != QColorSpace::Primaries::Custom) { + if (colorSpace1.primaries() != colorSpace2.primaries()) + return false; + } else { + if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz) + return false; + } + + if (colorSpace1.transferFunction() != QColorSpace::TransferFunction::Custom && + colorSpace2.transferFunction() != QColorSpace::TransferFunction::Custom) { + if (colorSpace1.transferFunction() != colorSpace2.transferFunction()) + return false; + if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma) + return (qAbs(colorSpace1.gamma() - colorSpace2.gamma()) <= (1.0f / 512.0f)); + return true; + } + + if (colorSpace1.d_ptr->trc[0] != colorSpace2.d_ptr->trc[0] || + colorSpace1.d_ptr->trc[1] != colorSpace2.d_ptr->trc[1] || + colorSpace1.d_ptr->trc[2] != colorSpace2.d_ptr->trc[2]) + return false; + + return true; +} + +/*! + \fn bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) + \relates QColorSpace + + Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2; + otherwise returns \c false +*/ + +/*! + Generates and returns a color space transformation from this color space to + \a colorspace. +*/ +QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const +{ + if (!isValid() || !colorspace.isValid()) + return QColorTransform(); + + return d_ptr->transformationToColorSpace(colorspace.d_ptr); +} + +/***************************************************************************** + QColorSpace stream functions + *****************************************************************************/ +#if !defined(QT_NO_DATASTREAM) +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace) + \relates QColorSpace + + Writes the given \a colorSpace to the given \a stream as an ICC profile. + + \sa QColorSpace::iccProfile(), {Serializing Qt Data Types} +*/ + +QDataStream &operator<<(QDataStream &s, const QColorSpace &image) +{ + s << image.iccProfile(); + return s; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace) + \relates QColorSpace + + Reads a color space from the given \a stream and stores it in the given + \a colorSpace. + + \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace) +{ + QByteArray iccProfile; + s >> iccProfile; + colorSpace = QColorSpace::fromIccProfile(iccProfile); + return s; +} +#endif // QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace) +{ + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QColorSpace("; + if (colorSpace.d_ptr->namedColorSpace) + dbg << colorSpace.d_ptr->namedColorSpace << ", "; + dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction(); + dbg << ", gamma=" << colorSpace.gamma(); + dbg << ')'; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h new file mode 100644 index 0000000000..e6bc62d58a --- /dev/null +++ b/src/gui/painting/qcolorspace.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** 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 QCOLORSPACE_H +#define QCOLORSPACE_H + +#include <QtGui/qtguiglobal.h> +#include <QtGui/qcolortransform.h> +#include <QtCore/qobjectdefs.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QColorSpacePrivate; +class QPointF; + +class Q_GUI_EXPORT QColorSpace +{ + Q_GADGET +public: + enum NamedColorSpace { + SRgb = 1, + SRgbLinear, + AdobeRgb, + DisplayP3, + ProPhotoRgb + }; + Q_ENUM(NamedColorSpace) + enum class Primaries { + Custom = 0, + SRgb, + AdobeRgb, + DciP3D65, + ProPhotoRgb + }; + Q_ENUM(Primaries) + enum class TransferFunction { + Custom = 0, + Linear, + Gamma, + SRgb, + ProPhotoRgb + }; + Q_ENUM(TransferFunction) + + QColorSpace(); + QColorSpace(NamedColorSpace namedColorSpace); + QColorSpace(Primaries primaries, TransferFunction fun, float gamma = 0.0f); + QColorSpace(Primaries primaries, float gamma); + QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint, + TransferFunction fun, float gamma = 0.0f); + ~QColorSpace(); + + QColorSpace(const QColorSpace &colorSpace); + QColorSpace &operator=(const QColorSpace &colorSpace); + + QColorSpace(QColorSpace &&colorSpace) noexcept + : d_ptr(qExchange(colorSpace.d_ptr, nullptr)) + { } + QColorSpace &operator=(QColorSpace &&colorSpace) noexcept + { + // Make the deallocation of this->d_ptr happen in ~QColorSpace() + QColorSpace(std::move(colorSpace)).swap(*this); + return *this; + } + + void swap(QColorSpace &colorSpace) noexcept + { qSwap(d_ptr, colorSpace.d_ptr); } + + Primaries primaries() const noexcept; + TransferFunction transferFunction() const noexcept; + float gamma() const noexcept; + + void setTransferFunction(TransferFunction transferFunction, float gamma = 0.0f); + QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma = 0.0f) const; + + void setPrimaries(Primaries primariesId); + void setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint); + + bool isValid() const noexcept; + + friend Q_GUI_EXPORT bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); + friend inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); + + static QColorSpace fromIccProfile(const QByteArray &iccProfile); + QByteArray iccProfile() const; + + QColorTransform transformationToColorSpace(const QColorSpace &colorspace) const; + + +private: + Q_DECLARE_PRIVATE(QColorSpace) + QColorSpacePrivate *d_ptr = nullptr; + +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace); +#endif +}; + +bool Q_GUI_EXPORT operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); +inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) +{ + return !(colorSpace1 == colorSpace2); +} + +Q_DECLARE_SHARED(QColorSpace) + +// QColorSpace stream functions +#if !defined(QT_NO_DATASTREAM) +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColorSpace &); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColorSpace &); +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QColorSpace &); +#endif + +QT_END_NAMESPACE + +#endif // QCOLORSPACE_P_H diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h new file mode 100644 index 0000000000..e7add19ed3 --- /dev/null +++ b/src/gui/painting/qcolorspace_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 QCOLORSPACE_P_H +#define QCOLORSPACE_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 "qcolorspace.h" +#include "qcolormatrix_p.h" +#include "qcolortrc_p.h" +#include "qcolortrclut_p.h" + +#include <QtCore/qmutex.h> +#include <QtCore/qpoint.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QColorSpacePrimaries +{ +public: + QColorSpacePrimaries() = default; + QColorSpacePrimaries(QColorSpace::Primaries primaries); + QColorSpacePrimaries(QPointF whitePoint, + QPointF redPoint, + QPointF greenPoint, + QPointF bluePoint) + : whitePoint(whitePoint) + , redPoint(redPoint) + , greenPoint(greenPoint) + , bluePoint(bluePoint) + { } + + QColorMatrix toXyzMatrix() const; + bool areValid() const; + + QPointF whitePoint; + QPointF redPoint; + QPointF greenPoint; + QPointF bluePoint; +}; + +class QColorSpacePrivate : public QSharedData +{ +public: + QColorSpacePrivate(); + QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace); + QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma); + QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma); + QColorSpacePrivate(const QColorSpacePrivate &other) = default; + + // named different from get to avoid accidental detachs + static QColorSpacePrivate *getWritable(QColorSpace &colorSpace) + { + if (!colorSpace.d_ptr) { + colorSpace.d_ptr = new QColorSpacePrivate; + colorSpace.d_ptr->ref.ref(); + } else if (colorSpace.d_ptr->ref.loadRelaxed() != 1) { + colorSpace.d_ptr->ref.deref(); + colorSpace.d_ptr = new QColorSpacePrivate(*colorSpace.d_ptr); + colorSpace.d_ptr->ref.ref(); + } + Q_ASSERT(colorSpace.d_ptr->ref.loadRelaxed() == 1); + return colorSpace.d_ptr; + } + + static const QColorSpacePrivate *get(const QColorSpace &colorSpace) + { + return colorSpace.d_ptr; + } + + void initialize(); + void setToXyzMatrix(); + void setTransferFunction(); + void identifyColorSpace(); + QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const; + + static constexpr QColorSpace::NamedColorSpace Unknown = QColorSpace::NamedColorSpace(0); + QColorSpace::NamedColorSpace namedColorSpace = Unknown; + + QColorSpace::Primaries primaries = QColorSpace::Primaries::Custom; + QColorSpace::TransferFunction transferFunction = QColorSpace::TransferFunction::Custom; + float gamma = 0.0f; + QColorVector whitePoint; + + QColorTrc trc[3]; + QColorMatrix toXyz; + + QString description; + QByteArray iccProfile; + + static QBasicMutex s_lutWriteLock; + struct LUT { + LUT() = default; + ~LUT() = default; + LUT(const LUT &other) + { + if (other.generated.loadAcquire()) { + table[0] = other.table[0]; + table[1] = other.table[1]; + table[2] = other.table[2]; + generated.storeRelaxed(1); + } + } + QSharedPointer<QColorTrcLut> &operator[](int i) { return table[i]; } + const QSharedPointer<QColorTrcLut> &operator[](int i) const { return table[i]; } + QSharedPointer<QColorTrcLut> table[3]; + QAtomicInt generated; + } mutable lut; +}; + +QT_END_NAMESPACE + +#endif // QCOLORSPACE_P_H diff --git a/src/gui/painting/qcolortransferfunction_p.h b/src/gui/painting/qcolortransferfunction_p.h new file mode 100644 index 0000000000..0575dbd888 --- /dev/null +++ b/src/gui/painting/qcolortransferfunction_p.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** 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 QCOLORTRANSFERFUNCTION_P_H +#define QCOLORTRANSFERFUNCTION_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 <cmath> + +QT_BEGIN_NAMESPACE + +// Defines a ICC parametric curve type 4 +class Q_GUI_EXPORT QColorTransferFunction +{ +public: + QColorTransferFunction() noexcept + : m_a(1.0f), m_b(0.0f), m_c(1.0f), m_d(0.0f), m_e(0.0f), m_f(0.0f), m_g(1.0f), m_flags(0) + { } + QColorTransferFunction(float a, float b, float c, float d, float e, float f, float g) noexcept + : m_a(a), m_b(b), m_c(c), m_d(d), m_e(e), m_f(f), m_g(g), m_flags(0) + { } + + bool isGamma() const + { + updateHints(); + return m_flags & quint32(Hints::IsGamma); + } + bool isLinear() const + { + updateHints(); + return m_flags & quint32(Hints::IsLinear); + } + bool isSRgb() const + { + updateHints(); + return m_flags & quint32(Hints::IsSRgb); + } + + float apply(float x) const + { + if (x < m_d) + return m_c * x + m_f; + else + return std::pow(m_a * x + m_b, m_g) + m_e; + } + + QColorTransferFunction inverted() const + { + float a, b, c, d, e, f, g; + + d = m_c * m_d + m_f; + + if (!qFuzzyIsNull(m_c)) { + c = 1.0f / m_c; + f = -m_f / m_c; + } else { + c = 0.0f; + f = 0.0f; + } + + if (!qFuzzyIsNull(m_a) && !qFuzzyIsNull(m_g)) { + a = std::pow(1.0f / m_a, m_g); + b = -a * m_e; + e = -m_b / m_a; + g = 1.0f / m_g; + } else { + a = 0.0f; + b = 0.0f; + e = 1.0f; + g = 1.0f; + } + + return QColorTransferFunction(a, b, c, d, e, f, g); + } + + // A few predefined curves: + static QColorTransferFunction fromGamma(float gamma) + { + return QColorTransferFunction(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, gamma); + } + static QColorTransferFunction fromSRgb() + { + return QColorTransferFunction(1.0f / 1.055f, 0.055f / 1.055f, 1.0f / 12.92f, 0.04045f, 0.0f, 0.0f, 2.4f); + } + static QColorTransferFunction fromProPhotoRgb() + { + return QColorTransferFunction(1.0f, 0.0f, 1.0f / 16.0f, 16.0f / 512.0f, 0.0f, 0.0f, 1.8f); + } + bool matches(const QColorTransferFunction &o) const + { + return paramCompare(m_a, o.m_a) && paramCompare(m_b, o.m_b) + && paramCompare(m_c, o.m_c) && paramCompare(m_d, o.m_d) + && paramCompare(m_e, o.m_e) && paramCompare(m_f, o.m_f) + && paramCompare(m_g, o.m_g); + } + friend inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2); + friend inline bool operator!=(const QColorTransferFunction &f1, const QColorTransferFunction &f2); + + float m_a; + float m_b; + float m_c; + float m_d; + float m_e; + float m_f; + float m_g; + +private: + static inline bool paramCompare(float p1, float p2) + { + // Much fuzzier than fuzzy compare. + // It tries match parameters that has been passed through a 8.8 + // fixed point form. + return (qAbs(p1 - p2) <= (1.0f / 512.0f)); + } + + void updateHints() const + { + if (m_flags & quint32(Hints::Calculated)) + return; + // We do not consider the case with m_d = 1.0f linear or simple, + // since it wouldn't be linear for applyExtended(). + bool simple = paramCompare(m_a, 1.0f) && paramCompare(m_b, 0.0f) + && paramCompare(m_d, 0.0f) + && paramCompare(m_e, 0.0f); + if (simple) { + m_flags |= quint32(Hints::IsGamma); + if (qFuzzyCompare(m_g, 1.0f)) + m_flags |= quint32(Hints::IsLinear); + } else { + if (*this == fromSRgb()) + m_flags |= quint32(Hints::IsSRgb); + } + m_flags |= quint32(Hints::Calculated); + } + enum class Hints : quint32 { + Calculated = 1, + IsGamma = 2, + IsLinear = 4, + IsSRgb = 8 + }; + mutable quint32 m_flags; +}; + +inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2) +{ + return f1.matches(f2); +} +inline bool operator!=(const QColorTransferFunction &f1, const QColorTransferFunction &f2) +{ + return !f1.matches(f2); +} + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFERFUNCTION_P_H diff --git a/src/gui/painting/qcolortransfertable_p.h b/src/gui/painting/qcolortransfertable_p.h new file mode 100644 index 0000000000..c8b2f7bd92 --- /dev/null +++ b/src/gui/painting/qcolortransfertable_p.h @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** 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 QCOLORTRANSFERTABLE_P_H +#define QCOLORTRANSFERTABLE_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 "qcolortransferfunction_p.h" + +#include <QVector> +#include <cmath> + +QT_BEGIN_NAMESPACE + +// Defines either an ICC TRC 'curve' or a lut8/lut16 A or B table +class Q_GUI_EXPORT QColorTransferTable +{ +public: + QColorTransferTable() noexcept + : m_tableSize(0) + { } + QColorTransferTable(uint32_t size, const QVector<uint8_t> &table) noexcept + : m_tableSize(size) + , m_table8(table) + { } + QColorTransferTable(uint32_t size, const QVector<uint16_t> &table) noexcept + : m_tableSize(size) + , m_table16(table) + { } + + bool isValid() const + { + if (m_tableSize < 2) + return false; + +#if !defined(QT_NO_DEBUG) + // The table must describe an injective curve: + if (!m_table8.isEmpty()) { + uint8_t val = 0; + for (uint i = 0; i < m_tableSize; ++i) { + Q_ASSERT(m_table8[i] >= val); + val = m_table8[i]; + } + } + if (!m_table16.isEmpty()) { + uint16_t val = 0; + for (uint i = 0; i < m_tableSize; ++i) { + Q_ASSERT(m_table16[i] >= val); + val = m_table16[i]; + } + } +#endif + return !m_table8.isEmpty() || !m_table16.isEmpty(); + } + + float apply(float x) const + { + x = std::min(std::max(x, 0.0f), 1.0f); + x *= m_tableSize - 1; + uint32_t lo = (int)std::floor(x); + uint32_t hi = std::min(lo + 1, m_tableSize); + float frac = x - lo; + if (!m_table16.isEmpty()) + return (m_table16[lo] * (1.0f - frac) + m_table16[hi] * frac) * (1.0f/65535.0f); + if (!m_table8.isEmpty()) + return (m_table8[lo] * (1.0f - frac) + m_table8[hi] * frac) * (1.0f/255.0f); + return x; + } + + // Apply inverse, optimized by giving a previous result a value < x. + float applyInverse(float x, float resultLargerThan = 0.0f) const + { + Q_ASSERT(resultLargerThan >= 0.0f && resultLargerThan <= 1.0f); + if (x <= 0.0f) + return 0.0f; + if (x >= 1.0f) + return 1.0f; + if (!m_table16.isEmpty()) { + float v = x * 65535.0f; + uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1; + for ( ; i < m_tableSize; ++i) { + if (m_table16[i] > v) + break; + } + if (i >= m_tableSize - 1) + return 1.0f; + float y1 = m_table16[i - 1]; + float y2 = m_table16[i]; + Q_ASSERT(x >= y1 && x < y2); + float fr = (v - y1) / (y2 - y1); + return (i + fr) * (1.0f / (m_tableSize - 1)); + + } + if (!m_table8.isEmpty()) { + float v = x * 255.0f; + uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1; + for ( ; i < m_tableSize; ++i) { + if (m_table8[i] > v) + break; + } + if (i >= m_tableSize - 1) + return 1.0f; + float y1 = m_table8[i - 1]; + float y2 = m_table8[i]; + Q_ASSERT(x >= y1 && x < y2); + float fr = (v - y1) / (y2 - y1); + return (i + fr) * (1.0f / (m_tableSize - 1)); + } + return x; + } + + bool asColorTransferFunction(QColorTransferFunction *transferFn) + { + Q_ASSERT(isValid()); + Q_ASSERT(transferFn); + if (!m_table8.isEmpty() && (m_table8[0] != 0 || m_table8[m_tableSize - 1] != 255)) + return false; + if (!m_table16.isEmpty() && (m_table16[0] != 0 || m_table16[m_tableSize - 1] != 65535)) + return false; + if (m_tableSize == 2) { + *transferFn = QColorTransferFunction(); // Linear + return true; + } + // The following heuristics are based on those from Skia: + if (m_tableSize == 26 && !m_table16.isEmpty()) { + // code.facebook.com/posts/411525055626587/under-the-hood-improving-facebook-photos + if (m_table16[6] != 3062) + return false; + if (m_table16[12] != 12824) + return false; + if (m_table16[18] != 31237) + return false; + *transferFn = QColorTransferFunction::fromSRgb(); + return true; + } + if (m_tableSize == 1024 && !m_table16.isEmpty()) { + // HP and Canon sRGB gamma tables: + if (m_table16[257] != 3366) + return false; + if (m_table16[513] != 14116) + return false; + if (m_table16[768] != 34318) + return false; + *transferFn = QColorTransferFunction::fromSRgb(); + return true; + } + if (m_tableSize == 4096 && !m_table16.isEmpty()) { + // Nikon, Epson, and lcms2 sRGB gamma tables: + if (m_table16[515] != 960) + return false; + if (m_table16[1025] != 3342) + return false; + if (m_table16[2051] != 14079) + return false; + *transferFn = QColorTransferFunction::fromSRgb(); + return true; + } + return false; + } + friend inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2); + friend inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2); + + uint32_t m_tableSize; + QVector<uint8_t> m_table8; + QVector<uint16_t> m_table16; +}; + +inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2) +{ + if (t1.m_tableSize != t2.m_tableSize) + return true; + if (t1.m_table8.isEmpty() != t2.m_table8.isEmpty()) + return true; + if (t1.m_table16.isEmpty() != t2.m_table16.isEmpty()) + return true; + if (!t1.m_table8.isEmpty()) { + for (quint32 i = 0; i < t1.m_tableSize; ++i) { + if (t1.m_table8[i] != t2.m_table8[i]) + return true; + } + } + if (!t1.m_table16.isEmpty()) { + for (quint32 i = 0; i < t1.m_tableSize; ++i) { + if (t1.m_table16[i] != t2.m_table16[i]) + return true; + } + } + return false; +} + +inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2) +{ + return !(t1 != t2); +} + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFERTABLE_P_H diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp new file mode 100644 index 0000000000..10ccefed74 --- /dev/null +++ b/src/gui/painting/qcolortransform.cpp @@ -0,0 +1,720 @@ +/**************************************************************************** +** +** 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 "qcolortransform.h" +#include "qcolortransform_p.h" + +#include "qcolormatrix_p.h" +#include "qcolorspace_p.h" +#include "qcolortrc_p.h" +#include "qcolortrclut_p.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qmath.h> +#include <QtGui/qcolor.h> +#include <QtGui/qtransform.h> +#include <QtCore/private/qsimd_p.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QColorTrcLut *lutFromTrc(const QColorTrc &trc) +{ + if (trc.m_type == QColorTrc::Type::Table) + return QColorTrcLut::fromTransferTable(trc.m_table); + if (trc.m_type == QColorTrc::Type::Function) + return QColorTrcLut::fromTransferFunction(trc.m_fun); + qWarning() << "TRC uninitialized"; + return nullptr; +} + +void QColorTransformPrivate::updateLutsIn() const +{ + if (colorSpaceIn->lut.generated.loadAcquire()) + return; + QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock); + if (colorSpaceIn->lut.generated.loadRelaxed()) + return; + + for (int i = 0; i < 3; ++i) { + if (!colorSpaceIn->trc[i].isValid()) + return; + } + + if (colorSpaceIn->trc[0] == colorSpaceIn->trc[1] && colorSpaceIn->trc[0] == colorSpaceIn->trc[2]) { + colorSpaceIn->lut[0].reset(lutFromTrc(colorSpaceIn->trc[0])); + colorSpaceIn->lut[1] = colorSpaceIn->lut[0]; + colorSpaceIn->lut[2] = colorSpaceIn->lut[0]; + } else { + for (int i = 0; i < 3; ++i) + colorSpaceIn->lut[i].reset(lutFromTrc(colorSpaceIn->trc[i])); + } + + colorSpaceIn->lut.generated.storeRelease(1); +} + +void QColorTransformPrivate::updateLutsOut() const +{ + if (colorSpaceOut->lut.generated.loadAcquire()) + return; + QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock); + if (colorSpaceOut->lut.generated.loadRelaxed()) + return; + for (int i = 0; i < 3; ++i) { + if (!colorSpaceOut->trc[i].isValid()) + return; + } + + if (colorSpaceOut->trc[0] == colorSpaceOut->trc[1] && colorSpaceOut->trc[0] == colorSpaceOut->trc[2]) { + colorSpaceOut->lut[0].reset(lutFromTrc(colorSpaceOut->trc[0])); + colorSpaceOut->lut[1] = colorSpaceOut->lut[0]; + colorSpaceOut->lut[2] = colorSpaceOut->lut[0]; + } else { + for (int i = 0; i < 3; ++i) + colorSpaceOut->lut[i].reset(lutFromTrc(colorSpaceOut->trc[i])); + } + + colorSpaceOut->lut.generated.storeRelease(1); +} + +/*! + \class QColorTransform + \brief The QColorTransform class is a transformation between color spaces. + \since 5.14 + + \ingroup painting + \ingroup appearance + \inmodule QtGui + + QColorTransform is an instantiation of a transformation between color spaces. + It can be applied on color and pixels to convert them from one color space to + another. + + Setting up a QColorTransform takes some preprocessing, so keeping around + QColorTransforms that you need often is recommended, instead of generating + them on the fly. +*/ + + +QColorTransform::QColorTransform(const QColorTransform &colorTransform) noexcept + : d(colorTransform.d) +{ + if (d) + d->ref.ref(); +} + + +QColorTransform::~QColorTransform() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Applies the color transformation on the QRgb value \a argb. + + The input should be opaque or unpremultiplied. +*/ +QRgb QColorTransform::map(QRgb argb) const +{ + if (!d) + return argb; + constexpr float f = 1.0f / 255.0f; + QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f }; + c.x = d->colorSpaceIn->trc[0].apply(c.x); + c.y = d->colorSpaceIn->trc[1].apply(c.y); + c.z = d->colorSpaceIn->trc[2].apply(c.z); + c = d->colorMatrix.map(c); + c.x = std::max(0.0f, std::min(1.0f, c.x)); + c.y = std::max(0.0f, std::min(1.0f, c.y)); + c.z = std::max(0.0f, std::min(1.0f, c.z)); + if (d->colorSpaceOut->lut.generated.loadAcquire()) { + c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); + c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); + c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); + } else { + c.x = d->colorSpaceOut->trc[0].applyInverse(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + } + + return qRgba(c.x * 255 + 0.5f, c.y * 255 + 0.5f, c.z * 255 + 0.5f, qAlpha(argb)); +} + +/*! + Applies the color transformation on the QRgba64 value \a rgba64. + + The input should be opaque or unpremultiplied. +*/ +QRgba64 QColorTransform::map(QRgba64 rgba64) const +{ + if (!d) + return rgba64; + constexpr float f = 1.0f / 65535.0f; + QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f }; + c.x = d->colorSpaceIn->trc[0].apply(c.x); + c.y = d->colorSpaceIn->trc[1].apply(c.y); + c.z = d->colorSpaceIn->trc[2].apply(c.z); + c = d->colorMatrix.map(c); + c.x = std::max(0.0f, std::min(1.0f, c.x)); + c.y = std::max(0.0f, std::min(1.0f, c.y)); + c.z = std::max(0.0f, std::min(1.0f, c.z)); + if (d->colorSpaceOut->lut.generated.loadAcquire()) { + c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); + c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); + c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); + } else { + c.x = d->colorSpaceOut->trc[0].applyInverse(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + } + + return QRgba64::fromRgba64(c.x * 65535, c.y * 65535, c.z * 65535, rgba64.alpha()); +} + +/*! + Applies the color transformation on the QColor value \a color. + +*/ +QColor QColorTransform::map(const QColor &color) const +{ + if (!d) + return color; + QColor clr = color; + if (color.spec() != QColor::ExtendedRgb || color.spec() != QColor::Rgb) + clr = clr.toRgb(); + + QColorVector c = { (float)clr.redF(), (float)clr.greenF(), (float)clr.blueF() }; + if (clr.spec() == QColor::ExtendedRgb) { + c.x = d->colorSpaceIn->trc[0].applyExtended(c.x); + c.y = d->colorSpaceIn->trc[1].applyExtended(c.y); + c.z = d->colorSpaceIn->trc[2].applyExtended(c.z); + } else { + c.x = d->colorSpaceIn->trc[0].apply(c.x); + c.y = d->colorSpaceIn->trc[1].apply(c.y); + c.z = d->colorSpaceIn->trc[2].apply(c.z); + } + c = d->colorMatrix.map(c); + bool inGamut = c.x >= 0.0f && c.x <= 1.0f && c.y >= 0.0f && c.y <= 1.0f && c.z >= 0.0f && c.z <= 1.0f; + if (inGamut) { + if (d->colorSpaceOut->lut.generated.loadAcquire()) { + c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); + c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); + c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); + } else { + c.x = d->colorSpaceOut->trc[0].applyInverse(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + } + } else { + c.x = d->colorSpaceOut->trc[0].applyInverseExtended(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverseExtended(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverseExtended(c.z); + } + QColor out; + out.setRgbF(c.x, c.y, c.z, color.alphaF()); + return out; +} + +// Optimized sub-routines for fast block based conversion: + +static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorMatrix &colorMatrix) +{ +#if defined(__SSE2__) + const __m128 minV = _mm_set1_ps(0.0f); + const __m128 maxV = _mm_set1_ps(1.0f); + const __m128 xMat = _mm_loadu_ps(&colorMatrix.r.x); + const __m128 yMat = _mm_loadu_ps(&colorMatrix.g.x); + const __m128 zMat = _mm_loadu_ps(&colorMatrix.b.x); + for (qsizetype j = 0; j < len; ++j) { + __m128 c = _mm_loadu_ps(&buffer[j].x); + __m128 cx = _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 cy = _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 cz = _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)); + cx = _mm_mul_ps(cx, xMat); + cy = _mm_mul_ps(cy, yMat); + cz = _mm_mul_ps(cz, zMat); + cx = _mm_add_ps(cx, cy); + cx = _mm_add_ps(cx, cz); + // Clamp: + cx = _mm_min_ps(cx, maxV); + cx = _mm_max_ps(cx, minV); + _mm_storeu_ps(&buffer[j].x, cx); + } +#else + for (int j = 0; j < len; ++j) { + const QColorVector cv = colorMatrix.map(buffer[j]); + buffer[j].x = std::max(0.0f, std::min(1.0f, cv.x)); + buffer[j].y = std::max(0.0f, std::min(1.0f, cv.y)); + buffer[j].z = std::max(0.0f, std::min(1.0f, cv.z)); + } +#endif +} + +template<typename T> +static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr); +template<typename T> +static void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr); + +#if defined(__SSE2__) +// Load to [0-alpha] in 4x32 SIMD +template<typename T> +static inline void loadP(const T &p, __m128i &v); + +template<> +inline void loadP<QRgb>(const QRgb &p, __m128i &v) +{ + v = _mm_cvtsi32_si128(p); +#if defined(__SSE4_1__) + v = _mm_cvtepu8_epi32(v); +#else + v = _mm_unpacklo_epi8(v, _mm_setzero_si128()); + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif +} + +template<> +inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v) +{ + v = _mm_loadl_epi64((const __m128i *)&p); +#if defined(__SSE4_1__) + v = _mm_cvtepu16_epi32(v); +#else + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif + // Shuffle to ARGB as the template below expects it + v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2)); +} + +template<typename T> +static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + const __m128 v4080 = _mm_set1_ps(4080.f); + const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + for (qsizetype i = 0; i < len; ++i) { + __m128i v; + loadP<T>(src[i], v); + __m128 vf = _mm_cvtepi32_ps(v); + // Approximate 1/a: + __m128 va = _mm_shuffle_ps(vf, vf, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 via = _mm_rcp_ps(va); + via = _mm_sub_ps(_mm_add_ps(via, via), _mm_mul_ps(via, _mm_mul_ps(via, va))); + // v * (1/a) + vf = _mm_mul_ps(vf, via); + + // Handle zero alpha + __m128 vAlphaMask = _mm_cmpeq_ps(va, _mm_set1_ps(0.0f)); + vf = _mm_andnot_ps(vAlphaMask, vf); + + // LUT + v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + const int ridx = _mm_extract_epi16(v, 4); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4); + vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00); + + _mm_storeu_ps(&buffer[i].x, vf); + } +} + +// Load to [0-4080] in 4x32 SIMD +template<typename T> +static inline void loadPU(const T &p, __m128i &v); + +template<> +inline void loadPU<QRgb>(const QRgb &p, __m128i &v) +{ + v = _mm_cvtsi32_si128(p); +#if defined(__SSE4_1__) + v = _mm_cvtepu8_epi32(v); +#else + v = _mm_unpacklo_epi8(v, _mm_setzero_si128()); + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif + v = _mm_slli_epi32(v, 4); +} + +template<> +inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v) +{ + v = _mm_loadl_epi64((const __m128i *)&p); + v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8)); +#if defined(__SSE4_1__) + v = _mm_cvtepu16_epi32(v); +#else + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif + v = _mm_srli_epi32(v, 4); + // Shuffle to ARGB as the template below expects it + v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2)); +} + +template<typename T> +void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + for (qsizetype i = 0; i < len; ++i) { + __m128i v; + loadPU<T>(src[i], v); + const int ridx = _mm_extract_epi16(v, 4); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4); + __m128 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00); + _mm_storeu_ps(&buffer[i].x, vf); + } +} + +#else +template<> +void loadPremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const uint p = src[i]; + const int a = qAlpha(p); + if (a) { + const float ia = 4080.0f / a; + const int ridx = int(qRed(p) * ia + 0.5f); + const int gidx = int(qGreen(p) * ia + 0.5f); + const int bidx = int(qBlue(p) * ia + 0.5f); + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256)); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256)); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256)); + } else { + buffer[i].x = buffer[i].y = buffer[i].z = 0.0f; + } + } +} + +template<> +void loadPremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const QRgba64 &p = src[i]; + const int a = p.alpha(); + if (a) { + const float ia = 4080.0f / a; + const int ridx = int(p.red() * ia + 0.5f); + const int gidx = int(p.green() * ia + 0.5f); + const int bidx = int(p.blue() * ia + 0.5f); + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256)); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256)); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256)); + } else { + buffer[i].x = buffer[i].y = buffer[i].z = 0.0f; + } + } +} + +template<> +void loadUnpremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const uint p = src[i]; + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(qRed(p)); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u8ToLinearF32(qGreen(p)); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u8ToLinearF32(qBlue(p)); + } +} + +template<> +void loadUnpremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const QRgba64 &p = src[i]; + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(p.red()); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u16ToLinearF32(p.green()); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u16ToLinearF32(p.blue()); + } +} +#endif + +static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ +#if defined(__SSE2__) + const __m128 v4080 = _mm_set1_ps(4080.f); + const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + for (qsizetype i = 0; i < len; ++i) { + const int a = qAlpha(src[i]); + __m128 vf = _mm_loadu_ps(&buffer[i].x); + __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + __m128 va = _mm_set1_ps(a); + va = _mm_mul_ps(va, iFF00); + const int ridx = _mm_extract_epi16(v, 0); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 4); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 4); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0); + vf = _mm_cvtepi32_ps(v); + vf = _mm_mul_ps(vf, va); + v = _mm_cvtps_epi32(vf); + v = _mm_packs_epi32(v, v); + v = _mm_insert_epi16(v, a, 3); + v = _mm_packus_epi16(v, v); + dst[i] = _mm_cvtsi128_si32(v); + } +#else + for (qsizetype i = 0; i < len; ++i) { + const int a = qAlpha(src[i]); + const float fa = a / (255.0f * 256.0f); + const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)]; + const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)]; + const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)]; + dst[i] = qRgba(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a); + } +#endif +} + +static void storeUnpremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ +#if defined(__SSE2__) + const __m128 v4080 = _mm_set1_ps(4080.f); + for (qsizetype i = 0; i < len; ++i) { + const int a = qAlpha(src[i]); + __m128 vf = _mm_loadu_ps(&buffer[i].x); + __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + const int ridx = _mm_extract_epi16(v, 0); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 4); + v = _mm_setzero_si128(); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0); + v = _mm_add_epi16(v, _mm_set1_epi16(0x80)); + v = _mm_srli_epi16(v, 8); + v = _mm_insert_epi16(v, a, 3); + v = _mm_packus_epi16(v, v); + dst[i] = _mm_cvtsi128_si32(v); + } +#else + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z); + dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0); + } +#endif +} + +static void storeOpaque(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + Q_UNUSED(src); +#if defined(__SSE2__) + const __m128 v4080 = _mm_set1_ps(4080.f); + for (qsizetype i = 0; i < len; ++i) { + __m128 vf = _mm_loadu_ps(&buffer[i].x); + __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + const int ridx = _mm_extract_epi16(v, 0); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 4); + v = _mm_setzero_si128(); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0); + v = _mm_add_epi16(v, _mm_set1_epi16(0x80)); + v = _mm_srli_epi16(v, 8); + v = _mm_insert_epi16(v, 255, 3); + v = _mm_packus_epi16(v, v); + dst[i] = _mm_cvtsi128_si32(v); + } +#else + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z); + dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0); + } +#endif +} + +static void storePremultiplied(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const int a = src[i].alpha(); + const float fa = a / (255.0f * 256.0f); + const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)]; + const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)]; + const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)]; + dst[i] = qRgba64(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a); + } +} + +static void storeUnpremultiplied(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z); + dst[i] = qRgba64(r, g, b, src[i].alpha()); + } +} + +static void storeOpaque(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + Q_UNUSED(src); + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z); + dst[i] = qRgba64(r, g, b, 0xFFFF); + } +} + +static constexpr qsizetype WorkBlockSize = 256; + +template <typename T, int Count = 1> +class QUninitialized +{ +public: + operator T*() { return reinterpret_cast<T *>(this); } +private: + alignas(T) char data[sizeof(T) * Count]; +}; + +template<typename T> +void QColorTransformPrivate::apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const +{ + if (!colorMatrix.isValid()) + return; + + updateLutsIn(); + updateLutsOut(); + + bool doApplyMatrix = (colorMatrix != QColorMatrix::identity()); + + QUninitialized<QColorVector, WorkBlockSize> buffer; + + qsizetype i = 0; + while (i < count) { + const qsizetype len = qMin(count - i, WorkBlockSize); + if (flags & InputPremultiplied) + loadPremultiplied(buffer, src + i, len, this); + else + loadUnpremultiplied(buffer, src + i, len, this); + + if (doApplyMatrix) + applyMatrix(buffer, len, colorMatrix); + + if (flags & InputOpaque) + storeOpaque(dst + i, src + i, buffer, len, this); + else if (flags & OutputPremultiplied) + storePremultiplied(dst + i, src + i, buffer, len, this); + else + storeUnpremultiplied(dst + i, src + i, buffer, len, this); + + i += len; + } +} + +/*! + \internal + \enum QColorTransformPrivate::TransformFlag + + Defines how the transform is to be applied. + + \value Unpremultiplied The input and output should both be unpremultiplied. + \value InputOpaque The input is guaranteed to be opaque. + \value InputPremultiplied The input is premultiplied. + \value OutputPremultiplied The output should be premultiplied. + \value Premultiplied Both input and output should both be premultiplied. +*/ + +/*! + \internal + Prepares a color transformation for fast application. You do not need to + call this explicitly as it will be called implicitly on the first transforms, but + if you want predictable performance on the first transforms, you can perform it + in advance. + + \sa QColorTransform::map(), apply() +*/ +void QColorTransformPrivate::prepare() +{ + updateLutsIn(); + updateLutsOut(); +} + +/*! + \internal + Applies the color transformation on \a count QRgb pixels starting from + \a src and stores the result in \a dst. + + Thread-safe if prepare() has been called first. + + Assumes unpremultiplied data by default. Set \a flags to change defaults. + + \sa prepare() +*/ +void QColorTransformPrivate::apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const +{ + apply<QRgb>(dst, src, count, flags); +} + +/*! + \internal + Applies the color transformation on \a count QRgba64 pixels starting from + \a src and stores the result in \a dst. + + Thread-safe if prepare() has been called first. + + Assumes unpremultiplied data by default. Set \a flags to change defaults. + + \sa prepare() +*/ +void QColorTransformPrivate::apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const +{ + apply<QRgba64>(dst, src, count, flags); +} + + +QT_END_NAMESPACE diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h new file mode 100644 index 0000000000..94b6b3a385 --- /dev/null +++ b/src/gui/painting/qcolortransform.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 QCOLORTRANSFORM_H +#define QCOLORTRANSFORM_H + +#include <QtGui/qtguiglobal.h> +#include <QtGui/qrgb.h> + +QT_BEGIN_NAMESPACE + +class QColor; +class QRgba64; +class QColorSpacePrivate; +class QColorTransformPrivate; + +class QColorTransform +{ +public: + QColorTransform() noexcept : d(nullptr) { } + Q_GUI_EXPORT ~QColorTransform(); + Q_GUI_EXPORT QColorTransform(const QColorTransform &colorTransform) noexcept; + QColorTransform(QColorTransform &&colorTransform) noexcept + : d{qExchange(colorTransform.d, nullptr)} + { } + QColorTransform &operator=(const QColorTransform &other) noexcept + { + QColorTransform{other}.swap(*this); + return *this; + } + QColorTransform &operator=(QColorTransform &&other) noexcept + { + QColorTransform{std::move(other)}.swap(*this); + return *this; + } + + void swap(QColorTransform &other) noexcept { qSwap(d, other.d); } + + Q_GUI_EXPORT QRgb map(QRgb argb) const; + Q_GUI_EXPORT QRgba64 map(QRgba64 rgba64) const; + Q_GUI_EXPORT QColor map(const QColor &color) const; + +private: + friend class QColorSpace; + friend class QColorSpacePrivate; + friend class QImage; + + const QColorTransformPrivate *d; +}; + +Q_DECLARE_SHARED(QColorTransform) + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFORM_H diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h new file mode 100644 index 0000000000..5d7116248d --- /dev/null +++ b/src/gui/painting/qcolortransform_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 QCOLORTRANSFORM_P_H +#define QCOLORTRANSFORM_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 "qcolormatrix_p.h" +#include "qcolorspace_p.h" + +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QColorTransformPrivate : public QSharedData +{ +public: + QColorMatrix colorMatrix; + QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceIn; + QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceOut; + + void updateLutsIn() const; + void updateLutsOut() const; + bool simpleGammaCorrection() const; + + void prepare(); + enum TransformFlag { + Unpremultiplied = 0, + InputOpaque = 1, + InputPremultiplied = 2, + OutputPremultiplied = 4, + Premultiplied = (InputPremultiplied | OutputPremultiplied) + }; + Q_DECLARE_FLAGS(TransformFlags, TransformFlag) + + void apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags = Unpremultiplied) const; + void apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags = Unpremultiplied) const; + + template<typename T> + void apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const; +}; + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFORM_P_H diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h new file mode 100644 index 0000000000..3ef9d442fc --- /dev/null +++ b/src/gui/painting/qcolortrc_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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 QCOLORTRC_P_H +#define QCOLORTRC_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 "qcolortransferfunction_p.h" +#include "qcolortransfertable_p.h" + +QT_BEGIN_NAMESPACE + + +// Defines an ICC TRC (Tone Reproduction Curve) +class Q_GUI_EXPORT QColorTrc +{ +public: + QColorTrc() noexcept : m_type(Type::Uninitialized) + { } + QColorTrc(const QColorTransferFunction &fun) : m_type(Type::Function), m_fun(fun) + { } + QColorTrc(const QColorTransferTable &table) : m_type(Type::Table), m_table(table) + { } + + enum class Type { + Uninitialized, + Function, + Table + }; + + bool isLinear() const + { + return m_type == Type::Uninitialized || (m_type == Type::Function && m_fun.isLinear()); + } + bool isValid() const + { + return m_type != Type::Uninitialized; + } + float apply(float x) const + { + if (m_type == Type::Table) + return m_table.apply(x); + if (m_type == Type::Function) + return m_fun.apply(x); + return x; + } + float applyExtended(float x) const + { + if (x >= 0.0f && x <= 1.0f) + return apply(x); + if (m_type == Type::Function) + return std::copysign(m_fun.apply(std::abs(x)), x); + if (m_type == Type::Table) + return x < 0.0f ? 0.0f : 1.0f; + return x; + } + float applyInverse(float x) const + { + if (m_type == Type::Table) + return m_table.applyInverse(x); + if (m_type == Type::Function) + return m_fun.inverted().apply(x); + return x; + } + float applyInverseExtended(float x) const + { + if (x >= 0.0f && x <= 1.0f) + return applyInverse(x); + if (m_type == Type::Function) + return std::copysign(applyInverse(x), x); + if (m_type == Type::Table) + return x < 0.0f ? 0.0f : 1.0f; + return x; + } + + friend inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2); + friend inline bool operator==(const QColorTrc &o1, const QColorTrc &o2); + + Type m_type; + QColorTransferFunction m_fun; + QColorTransferTable m_table; +}; + +inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2) +{ + if (o1.m_type != o2.m_type) + return true; + if (o1.m_type == QColorTrc::Type::Function) + return o1.m_fun != o2.m_fun; + if (o1.m_type == QColorTrc::Type::Table) + return o1.m_table != o2.m_table; + return false; +} +inline bool operator==(const QColorTrc &o1, const QColorTrc &o2) +{ + return !(o1 != o2); +} + +QT_END_NAMESPACE + +#endif // QCOLORTRC diff --git a/src/gui/painting/qcolorprofile.cpp b/src/gui/painting/qcolortrclut.cpp index 3b7b0a248b..268d7252b4 100644 --- a/src/gui/painting/qcolorprofile.cpp +++ b/src/gui/painting/qcolortrclut.cpp @@ -37,14 +37,16 @@ ** ****************************************************************************/ -#include "qcolorprofile_p.h" +#include "qcolortrclut_p.h" +#include "qcolortransferfunction_p.h" +#include "qcolortransfertable_p.h" #include <qmath.h> QT_BEGIN_NAMESPACE -QColorProfile *QColorProfile::fromGamma(qreal gamma) +QColorTrcLut *QColorTrcLut::fromGamma(qreal gamma) { - QColorProfile *cp = new QColorProfile; + QColorTrcLut *cp = new QColorTrcLut; for (int i = 0; i <= (255 * 16); ++i) { cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256))); @@ -54,31 +56,28 @@ QColorProfile *QColorProfile::fromGamma(qreal gamma) return cp; } -static qreal srgbToLinear(qreal v) +QColorTrcLut *QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun) { - const qreal a = 0.055; - if (v <= qreal(0.04045)) - return v / qreal(12.92); - else - return qPow((v + a) / (qreal(1) + a), qreal(2.4)); -} + QColorTrcLut *cp = new QColorTrcLut; + QColorTransferFunction inv = fun.inverted(); -static qreal linearToSrgb(qreal v) -{ - const qreal a = 0.055; - if (v <= qreal(0.0031308)) - return v * qreal(12.92); - else - return (qreal(1) + a) * qPow(v, qreal(1.0 / 2.4)) - a; + for (int i = 0; i <= (255 * 16); ++i) { + cp->m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(255 * 16)) * (255 * 256))); + cp->m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(255 * 16)) * (255 * 256))); + } + + return cp; } -QColorProfile *QColorProfile::fromSRgb() +QColorTrcLut *QColorTrcLut::fromTransferTable(const QColorTransferTable &table) { - QColorProfile *cp = new QColorProfile; + QColorTrcLut *cp = new QColorTrcLut; + float minInverse = 0.0f; for (int i = 0; i <= (255 * 16); ++i) { - cp->m_toLinear[i] = ushort(qRound(srgbToLinear(i / qreal(255 * 16)) * (255 * 256))); - cp->m_fromLinear[i] = ushort(qRound(linearToSrgb(i / qreal(255 * 16)) * (255 * 256))); + cp->m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(255 * 16)) * (255 * 256)), 65280)); + minInverse = table.applyInverse(i / qreal(255 * 16), minInverse); + cp->m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280)); } return cp; diff --git a/src/gui/painting/qcolorprofile_p.h b/src/gui/painting/qcolortrclut_p.h index 425e9abace..76a6a60803 100644 --- a/src/gui/painting/qcolorprofile_p.h +++ b/src/gui/painting/qcolortrclut_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QCOLORPROFILE_P_H -#define QCOLORPROFILE_P_H +#ifndef QCOLORTRCLUT_P_H +#define QCOLORTRCLUT_P_H // // W A R N I N G @@ -52,21 +52,29 @@ // #include <QtGui/private/qtguiglobal_p.h> +#include <QtCore/qsharedpointer.h> #include <QtGui/qrgb.h> #include <QtGui/qrgba64.h> +#include <cmath> + #if defined(__SSE2__) #include <emmintrin.h> #elif defined(__ARM_NEON__) || defined(__ARM_NEON) #include <arm_neon.h> #endif + QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QColorProfile +class QColorTransferFunction; +class QColorTransferTable; + +class Q_GUI_EXPORT QColorTrcLut : public QEnableSharedFromThis<QColorTrcLut> { public: - static QColorProfile *fromGamma(qreal gamma); - static QColorProfile *fromSRgb(); + static QColorTrcLut *fromGamma(qreal gamma); + static QColorTrcLut *fromTransferFunction(const QColorTransferFunction &transfn); + static QColorTrcLut *fromTransferTable(const QColorTransferTable &transTable); // The following methods all convert opaque or unpremultiplied colors: @@ -121,6 +129,25 @@ public: return convertWithTable(rgb64, m_toLinear); } + float u8ToLinearF32(int c) const + { + ushort v = m_toLinear[c << 4]; + return v * (1.0f / (255*256)); + } + + float u16ToLinearF32(int c) const + { + c -= (c >> 8); + ushort v = m_toLinear[c >> 4]; + return v * (1.0f / (255*256)); + } + + float toLinear(float f) const + { + ushort v = m_toLinear[(int)(f * (255 * 16) + 0.5f)]; + return v * (1.0f / (255*256)); + } + QRgb fromLinear64(QRgba64 rgb64) const { #if defined(__SSE2__) @@ -176,8 +203,31 @@ public: return convertWithTable(rgb64, m_fromLinear); } + int u8FromLinearF32(float f) const + { + ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)]; + return (v + 0x80) >> 8; + } + int u16FromLinearF32(float f) const + { + ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)]; + return v + (v >> 8); + } + float fromLinear(float f) const + { + ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)]; + return v * (1.0f / (255*256)); + } + + // We translate to 0-65280 (255*256) instead to 0-65535 to make simple + // shifting an accurate conversion. + // We translate from 0-4080 (255*16) for the same speed up, and to keep + // the tables small enough to fit in most inner caches. + ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + private: - QColorProfile() { } + QColorTrcLut() { } Q_ALWAYS_INLINE static QRgb convertWithTable(QRgb rgb32, const ushort *table) { @@ -230,16 +280,8 @@ private: return QRgba64::fromRgba64(r, g, b, rgb64.alpha()); #endif } - - // We translate to 0-65280 (255*256) instead to 0-65535 to make simple - // shifting an accurate conversion. - // We translate from 0-4080 (255*16) for the same speed up, and to keep - // the tables small enough to fit in most inner caches. - ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] - ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] - }; QT_END_NAMESPACE -#endif // QCOLORPROFILE_P_H +#endif // QCOLORTRCLUT_P_H diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp index 0ce4cfb297..ced213e36d 100644 --- a/src/gui/painting/qcompositionfunctions.cpp +++ b/src/gui/painting/qcompositionfunctions.cpp @@ -137,6 +137,7 @@ struct Rgba64OperationsBase { ::memcpy(dest, src, len * sizeof(Type)); } }; +#if QT_CONFIG(raster_64bit) const Rgba64OperationsBase::Type Rgba64OperationsBase::clear = QRgba64::fromRgba64(0); struct Rgba64OperationsC : public Rgba64OperationsBase @@ -309,10 +310,8 @@ struct Rgba64OperationsNEON : public Rgba64OperationsBase return interpolate65535(x, a1, y, a2); } }; - #endif -typedef Argb32OperationsC Argb32Operations; #if defined(__SSE2__) typedef Rgba64OperationsSSE2 Rgba64Operations; #elif defined(__ARM_NEON__) @@ -320,6 +319,9 @@ typedef Rgba64OperationsNEON Rgba64Operations; #else typedef Rgba64OperationsC Rgba64Operations; #endif +#endif // QT_CONFIG(raster_64bit) + +typedef Argb32OperationsC Argb32Operations; /* result = 0 @@ -343,20 +345,23 @@ void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_ comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); } -void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha) +void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) { - comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); + comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); } -void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha) { - comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); + comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); } void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int length, uint const_alpha) { comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); } +#endif + /* result = s @@ -399,36 +404,40 @@ void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint comp_func_solid_Source_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_Source_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) { } -void QT_FASTCALL comp_func_solid_Destination_rgb64(QRgba64 *, int, QRgba64, uint) +void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) { } -void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Destination_rgb64(QRgba64 *, int, QRgba64, uint) { } void QT_FASTCALL comp_func_Destination_rgb64(QRgba64 *, const QRgba64 *, int, uint) { } +#endif /* result = s + d * sia @@ -483,20 +492,22 @@ void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, comp_func_solid_SourceOver_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceOver_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d + s * dia @@ -542,20 +553,22 @@ void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint co comp_func_solid_DestinationOver_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationOver_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = s * da @@ -606,20 +619,22 @@ void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, ui comp_func_solid_SourceIn_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceIn_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d * sa @@ -665,20 +680,22 @@ void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint colo comp_func_solid_DestinationIn_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationIn_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = s * dia @@ -727,20 +744,22 @@ void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, u comp_func_solid_SourceOut_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceOut_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d * sia @@ -786,20 +805,22 @@ void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint col comp_func_solid_DestinationOut_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationOut_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = s*da + d*sia @@ -845,20 +866,22 @@ void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, comp_func_solid_SourceAtop_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceAtop_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d*sa + s*dia @@ -909,20 +932,22 @@ void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint co comp_func_solid_DestinationAtop_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationAtop_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d*sia + s*dia @@ -969,20 +994,22 @@ void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint co comp_func_solid_XOR_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_XOR_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_XOR_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif struct QFullCoverage { inline void store(uint *dest, const uint src) const @@ -1078,20 +1105,22 @@ void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint c comp_func_solid_Plus_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_Plus_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1101,11 +1130,6 @@ static inline int multiply_op(int dst, int src, int da, int sa) return qt_div_255(src * dst + src * (255 - da) + dst * (255 - sa)); } -static inline uint multiply_op_rgb64(uint dst, uint src, uint da, uint sa) -{ - return qt_div_65535(src * dst + src * (65535 - da) + dst * (65535 - sa)); -} - template <typename T> static inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1129,6 +1153,20 @@ static inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint co } } +void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint multiply_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + return qt_div_65535(src * dst + src * (65535 - da) + dst * (65535 - sa)); +} + template <typename T> static inline void comp_func_solid_Multiply_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -1152,14 +1190,6 @@ static inline void comp_func_solid_Multiply_impl(QRgba64 *dest, int length, QRgb } } -void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1167,6 +1197,7 @@ void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba else comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -1189,6 +1220,15 @@ static inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uin } } +void QT_FASTCALL comp_func_Multiply(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Multiply_impl(dest, src, length, QFullCoverage()); + else + comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Multiply_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1210,14 +1250,6 @@ static inline void comp_func_Multiply_impl(QRgba64 *Q_DECL_RESTRICT dest, const } } -void QT_FASTCALL comp_func_Multiply(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Multiply_impl(dest, src, length, QFullCoverage()); - else - comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1225,6 +1257,7 @@ void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q else comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1253,6 +1286,15 @@ static inline void comp_func_solid_Screen_impl(uint *dest, int length, uint colo } } +void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Screen_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -1276,14 +1318,6 @@ static inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba6 } } -void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Screen_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1291,6 +1325,7 @@ void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 else comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -1313,6 +1348,15 @@ static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint } } +void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Screen_impl(dest, src, length, QFullCoverage()); + else + comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1334,14 +1378,6 @@ static inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RESTRICT dest, const QR } } -void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Screen_impl(dest, src, length, QFullCoverage()); - else - comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1349,6 +1385,7 @@ void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int l else comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if 2.Dca < Da @@ -1365,15 +1402,6 @@ static inline int overlay_op(int dst, int src, int da, int sa) return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp); } -static inline uint overlay_op_rgb64(uint dst, uint src, uint da, uint sa) -{ - const uint temp = src * (65535 - da) + dst * (65535 - sa); - if (2 * dst < da) - return qt_div_65535(2 * src * dst + temp); - else - return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp); -} - template <typename T> static inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1397,6 +1425,24 @@ static inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint col } } +void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint overlay_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + const uint temp = src * (65535 - da) + dst * (65535 - sa); + if (2 * dst < da) + return qt_div_65535(2 * src * dst + temp); + else + return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp); +} + template <typename T> static inline void comp_func_solid_Overlay_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -1420,14 +1466,6 @@ static inline void comp_func_solid_Overlay_impl(QRgba64 *dest, int length, QRgba } } -void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1435,6 +1473,7 @@ void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba6 else comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -1457,6 +1496,15 @@ static inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint } } +void QT_FASTCALL comp_func_Overlay(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Overlay_impl(dest, src, length, QFullCoverage()); + else + comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Overlay_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1478,14 +1526,6 @@ static inline void comp_func_Overlay_impl(QRgba64 *Q_DECL_RESTRICT dest, const Q } } -void QT_FASTCALL comp_func_Overlay(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Overlay_impl(dest, src, length, QFullCoverage()); - else - comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1493,6 +1533,7 @@ void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR else comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1503,11 +1544,6 @@ static inline int darken_op(int dst, int src, int da, int sa) return qt_div_255(qMin(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa)); } -static inline uint darken_op_rgb64(uint dst, uint src, uint da, uint sa) -{ - return qt_div_65535(qMin(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa)); -} - template <typename T> static inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1531,6 +1567,20 @@ static inline void comp_func_solid_Darken_impl(uint *dest, int length, uint colo } } +void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Darken_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint darken_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + return qt_div_65535(qMin(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa)); +} + template <typename T> static inline void comp_func_solid_Darken_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -1554,14 +1604,6 @@ static inline void comp_func_solid_Darken_impl(QRgba64 *dest, int length, QRgba6 } } -void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Darken_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1569,6 +1611,7 @@ void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 else comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -1591,6 +1634,15 @@ static inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint } } +void QT_FASTCALL comp_func_Darken(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Darken_impl(dest, src, length, QFullCoverage()); + else + comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Darken_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1612,14 +1664,6 @@ static inline void comp_func_Darken_impl(QRgba64 *Q_DECL_RESTRICT dest, const QR } } -void QT_FASTCALL comp_func_Darken(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Darken_impl(dest, src, length, QFullCoverage()); - else - comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1627,6 +1671,7 @@ void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRg else comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1637,11 +1682,6 @@ static inline int lighten_op(int dst, int src, int da, int sa) return qt_div_255(qMax(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa)); } -static inline uint lighten_op_rgb64(uint dst, uint src, uint da, uint sa) -{ - return qt_div_65535(qMax(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa)); -} - template <typename T> static inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1665,6 +1705,21 @@ static inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint col } } +void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + + +#if QT_CONFIG(raster_64bit) +static inline uint lighten_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + return qt_div_65535(qMax(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa)); +} + template <typename T> static inline void comp_func_solid_Lighten_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -1688,14 +1743,6 @@ static inline void comp_func_solid_Lighten_impl(QRgba64 *dest, int length, QRgba } } -void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1703,6 +1750,7 @@ void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba6 else comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -1725,6 +1773,15 @@ static inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint } } +void QT_FASTCALL comp_func_Lighten(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Lighten_impl(dest, src, length, QFullCoverage()); + else + comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Lighten_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1746,14 +1803,6 @@ static inline void comp_func_Lighten_impl(QRgba64 *Q_DECL_RESTRICT dest, const Q } } -void QT_FASTCALL comp_func_Lighten(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Lighten_impl(dest, src, length, QFullCoverage()); - else - comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1761,6 +1810,7 @@ void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR else comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if Sca.Da + Dca.Sa > Sa.Da @@ -1785,21 +1835,6 @@ static inline int color_dodge_op(int dst, int src, int da, int sa) return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp); } -static inline uint color_dodge_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) -{ - const qint64 sa_da = sa * da; - const qint64 dst_sa = dst * sa; - const qint64 src_da = src * da; - - const qint64 temp = src * (65535 - da) + dst * (65535 - sa); - if (src_da + dst_sa > sa_da) - return qt_div_65535(sa_da + temp); - else if (src == sa || sa == 0) - return qt_div_65535(temp); - else - return qt_div_65535(65535 * dst_sa / (65535 - 65535 * src / sa) + temp); -} - template <typename T> static inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1823,6 +1858,30 @@ static inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint } } +void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint color_dodge_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + const qint64 sa_da = sa * da; + const qint64 dst_sa = dst * sa; + const qint64 src_da = src * da; + + const qint64 temp = src * (65535 - da) + dst * (65535 - sa); + if (src_da + dst_sa > sa_da) + return qt_div_65535(sa_da + temp); + else if (src == sa || sa == 0) + return qt_div_65535(temp); + else + return qt_div_65535(65535 * dst_sa / (65535 - 65535 * src / sa) + temp); +} + template <typename T> static inline void comp_func_solid_ColorDodge_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -1846,14 +1905,6 @@ static inline void comp_func_solid_ColorDodge_impl(QRgba64 *dest, int length, QR } } -void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1861,6 +1912,7 @@ void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRg else comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -1883,6 +1935,15 @@ static inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const u } } +void QT_FASTCALL comp_func_ColorDodge(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_ColorDodge_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1904,14 +1965,6 @@ static inline void comp_func_ColorDodge_impl(QRgba64 *Q_DECL_RESTRICT dest, cons } } -void QT_FASTCALL comp_func_ColorDodge(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); - else - comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1919,6 +1972,7 @@ void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if Sca.Da + Dca.Sa < Sa.Da @@ -1943,21 +1997,6 @@ static inline int color_burn_op(int dst, int src, int da, int sa) return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp); } -static inline uint color_burn_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) -{ - const qint64 src_da = src * da; - const qint64 dst_sa = dst * sa; - const qint64 sa_da = sa * da; - - const qint64 temp = src * (65535 - da) + dst * (65535 - sa); - - if (src_da + dst_sa < sa_da) - return qt_div_65535(temp); - else if (src == 0) - return qt_div_65535(dst_sa + temp); - return qt_div_65535(sa * (src_da + dst_sa - sa_da) / src + temp); -} - template <typename T> static inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1981,6 +2020,30 @@ static inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint c } } +void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint color_burn_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + const qint64 src_da = src * da; + const qint64 dst_sa = dst * sa; + const qint64 sa_da = sa * da; + + const qint64 temp = src * (65535 - da) + dst * (65535 - sa); + + if (src_da + dst_sa < sa_da) + return qt_div_65535(temp); + else if (src == 0) + return qt_div_65535(dst_sa + temp); + return qt_div_65535(sa * (src_da + dst_sa - sa_da) / src + temp); +} + template <typename T> static inline void comp_func_solid_ColorBurn_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -2004,14 +2067,6 @@ static inline void comp_func_solid_ColorBurn_impl(QRgba64 *dest, int length, QRg } } -void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2019,6 +2074,7 @@ void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgb else comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -2041,6 +2097,15 @@ static inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const ui } } +void QT_FASTCALL comp_func_ColorBurn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_ColorBurn_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2062,14 +2127,6 @@ static inline void comp_func_ColorBurn_impl(QRgba64 *Q_DECL_RESTRICT dest, const } } -void QT_FASTCALL comp_func_ColorBurn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); - else - comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -2077,6 +2134,7 @@ void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if 2.Sca < Sa @@ -2094,16 +2152,6 @@ static inline uint hardlight_op(int dst, int src, int da, int sa) return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp); } -static inline uint hardlight_op_rgb64(uint dst, uint src, uint da, uint sa) -{ - const uint temp = src * (65535 - da) + dst * (65535 - sa); - - if (2 * src < sa) - return qt_div_65535(2 * src * dst + temp); - else - return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp); -} - template <typename T> static inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage) { @@ -2127,6 +2175,25 @@ static inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint c } } +void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint hardlight_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + const uint temp = src * (65535 - da) + dst * (65535 - sa); + + if (2 * src < sa) + return qt_div_65535(2 * src * dst + temp); + else + return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp); +} + template <typename T> static inline void comp_func_solid_HardLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -2150,14 +2217,6 @@ static inline void comp_func_solid_HardLight_impl(QRgba64 *dest, int length, QRg } } -void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2165,6 +2224,7 @@ void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgb else comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -2187,6 +2247,15 @@ static inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const ui } } +void QT_FASTCALL comp_func_HardLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_HardLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_HardLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2208,14 +2277,6 @@ static inline void comp_func_HardLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const } } -void QT_FASTCALL comp_func_HardLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_HardLight_impl(dest, src, length, QFullCoverage()); - else - comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -2223,6 +2284,7 @@ void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if 2.Sca <= Sa @@ -2247,22 +2309,6 @@ static inline int soft_light_op(int dst, int src, int da, int sa) } } -static inline uint soft_light_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) -{ - const qint64 src2 = src << 1; - const qint64 dst_np = da != 0 ? (65535 * dst) / da : 0; - const qint64 temp = (src * (65535 - da) + dst * (65535 - sa)) * 65535; - const qint64 factor = qint64(65535) * 65535; - - if (src2 < sa) - return (dst * (sa * 65535 + (src2 - sa) * (65535 - dst_np)) + temp) / factor; - else if (4 * dst <= da) - return (dst * sa * 65535 + da * (src2 - sa) * ((((16 * dst_np - 12 * 65535) * dst_np + 3 * factor) * dst_np) / factor) + temp) / factor; - else { - return (dst * sa * 65535 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 65535))) - dst_np) + temp) / factor; - } -} - template <typename T> static inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage) { @@ -2286,6 +2332,23 @@ static inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint c } } +#if QT_CONFIG(raster_64bit) +static inline uint soft_light_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + const qint64 src2 = src << 1; + const qint64 dst_np = da != 0 ? (65535 * dst) / da : 0; + const qint64 temp = (src * (65535 - da) + dst * (65535 - sa)) * 65535; + const qint64 factor = qint64(65535) * 65535; + + if (src2 < sa) + return (dst * (sa * 65535 + (src2 - sa) * (65535 - dst_np)) + temp) / factor; + else if (4 * dst <= da) + return (dst * sa * 65535 + da * (src2 - sa) * ((((16 * dst_np - 12 * 65535) * dst_np + 3 * factor) * dst_np) / factor) + temp) / factor; + else { + return (dst * sa * 65535 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 65535))) - dst_np) + temp) / factor; + } +} + template <typename T> static inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -2308,6 +2371,7 @@ static inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRg coverage.store(&dest[i], qRgba64(r, g, b, a)); } } +#endif void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) { @@ -2317,14 +2381,6 @@ void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, u comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); } -void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - template <typename T> static inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2346,6 +2402,23 @@ static inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const ui } } +void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> static inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2367,14 +2440,6 @@ static inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const } } -void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); - else - comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -2382,6 +2447,7 @@ void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -2392,11 +2458,6 @@ static inline int difference_op(int dst, int src, int da, int sa) return src + dst - qt_div_255(2 * qMin(src * da, dst * sa)); } -static inline uint difference_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) -{ - return src + dst - qt_div_65535(2 * qMin(src * da, dst * sa)); -} - template <typename T> static inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage) { @@ -2420,6 +2481,20 @@ static inline void comp_func_solid_Difference_impl(uint *dest, int length, uint } } +void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Difference_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +static inline uint difference_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + return src + dst - qt_div_65535(2 * qMin(src * da, dst * sa)); +} + template <typename T> static inline void comp_func_solid_Difference_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -2443,14 +2518,6 @@ static inline void comp_func_solid_Difference_impl(QRgba64 *dest, int length, QR } } -void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Difference_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2458,6 +2525,7 @@ void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRg else comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -2480,6 +2548,15 @@ static inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const u } } +void QT_FASTCALL comp_func_Difference(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Difference_impl(dest, src, length, QFullCoverage()); + else + comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Difference_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2501,14 +2578,6 @@ static inline void comp_func_Difference_impl(QRgba64 *Q_DECL_RESTRICT dest, cons } } -void QT_FASTCALL comp_func_Difference(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Difference_impl(dest, src, length, QFullCoverage()); - else - comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -2516,6 +2585,7 @@ void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -2543,6 +2613,15 @@ static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int le } } +void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { @@ -2567,14 +2646,6 @@ static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba64 *dest, int } -void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2582,6 +2653,7 @@ void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgb else comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -2604,6 +2676,15 @@ static inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const ui } } +void QT_FASTCALL comp_func_Exclusion(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); + else + comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> static inline void comp_func_Exclusion_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2625,14 +2706,6 @@ static inline void comp_func_Exclusion_impl(QRgba64 *Q_DECL_RESTRICT dest, const } } -void QT_FASTCALL comp_func_Exclusion(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); - else - comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); -} - void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -2640,6 +2713,7 @@ void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, int length, @@ -2989,6 +3063,7 @@ CompositionFunctionSolid qt_functionForModeSolid_C[] = { }; CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = { +#if QT_CONFIG(raster_64bit) comp_func_solid_SourceOver_rgb64, comp_func_solid_DestinationOver_rgb64, comp_func_solid_Clear_rgb64, @@ -3013,6 +3088,10 @@ CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = { comp_func_solid_SoftLight_rgb64, comp_func_solid_Difference_rgb64, comp_func_solid_Exclusion_rgb64, +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -3059,6 +3138,7 @@ CompositionFunction qt_functionForMode_C[] = { }; CompositionFunction64 qt_functionForMode64_C[] = { +#if QT_CONFIG(raster_64bit) comp_func_SourceOver_rgb64, comp_func_DestinationOver_rgb64, comp_func_Clear_rgb64, @@ -3083,6 +3163,10 @@ CompositionFunction64 qt_functionForMode64_C[] = { comp_func_SoftLight_rgb64, comp_func_Difference_rgb64, comp_func_Exclusion_rgb64, +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h index 082ddee30f..8571b0476a 100644 --- a/src/gui/painting/qcosmeticstroker_p.h +++ b/src/gui/painting/qcosmeticstroker_p.h @@ -98,8 +98,8 @@ public: : state(s), deviceRect(dr_unclipped), clip(dr), - pattern(0), - reversePattern(0), + pattern(nullptr), + reversePattern(nullptr), patternSize(0), patternLength(0), patternOffset(0), diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index 28d5f6d6c5..181d19da0b 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -69,7 +69,7 @@ public: buffer = (Type*) malloc(capacity * sizeof(Type)); Q_CHECK_PTR(buffer); } else { - buffer = 0; + buffer = nullptr; } siz = 0; } @@ -128,7 +128,7 @@ public: Q_CHECK_PTR(buffer); } else { free(buffer); - buffer = 0; + buffer = nullptr; } } diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 533ad39b86..e8d129d047 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -43,7 +43,7 @@ #include <qstylehints.h> #include <qguiapplication.h> #include <qatomic.h> -#include <private/qcolorprofile_p.h> +#include <private/qcolortrclut_p.h> #include <private/qdrawhelper_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> @@ -88,6 +88,7 @@ template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB444>() { return 4; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; } @@ -101,6 +102,7 @@ template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB444>() { return 8; template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB555>() { return 10; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB666>() { return 12; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB888>() { return 16; } +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_BGR888>() { return 0; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB4444_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8555_Premultiplied>() { return 18; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8565_Premultiplied>() { return 19; } @@ -119,6 +121,7 @@ template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB444>() { return 4; template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8565_Premultiplied>() { return 6; } @@ -132,6 +135,7 @@ template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB444>() { return 4; template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8555_Premultiplied>() { return 13; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8565_Premultiplied>() { return 13; } @@ -150,6 +154,7 @@ template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB444>() { return 4; template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; } @@ -163,6 +168,7 @@ template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB444>() { return 0; template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB555>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB666>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_BGR888>() { return 16; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB4444_Premultiplied>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8555_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8565_Premultiplied>() { return 8; } @@ -181,6 +187,7 @@ template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB444>() { return 0; template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB555>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB666>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_BGR888>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8555_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8565_Premultiplied>() { return 8; } @@ -194,6 +201,7 @@ template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB444>() { return 0; template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB555>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB666>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_BGR888>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB4444_Premultiplied>() { return 12; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8555_Premultiplied>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8565_Premultiplied>() { return 0; } @@ -214,6 +222,7 @@ template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { r template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_BGR888>() { return QPixelLayout::BPP24; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; } @@ -660,8 +669,7 @@ static void QT_FASTCALL rbSwap_rgb30(uchar *d, const uchar *s, int count) { const uint *src = reinterpret_cast<const uint *>(s); uint *dest = reinterpret_cast<uint *>(d); - for (int i = 0; i < count; ++i) - dest[i] = qRgbSwapRgb30(src[i]); + UNALIASED_CONVERSION_LOOP(dest, src, count, qRgbSwapRgb30); } template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutRGB() @@ -1117,6 +1125,7 @@ static const QRgba64 *QT_FASTCALL fetchARGB32PMToRGBA64PM(QRgba64 *buffer, const return convertARGB32PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); } +#if QT_CONFIG(raster_64bit) static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) { for (int i = 0; i < count; ++i) @@ -1126,6 +1135,7 @@ static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) static void convertRGBA64PMToRGBA64PM(QRgba64 *, int) { } +#endif static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) @@ -1526,7 +1536,8 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { { false, false, QPixelLayout::BPP16, nullptr, convertGrayscale16ToRGB32, convertGrayscale16ToRGBA64, fetchGrayscale16ToRGB32, fetchGrayscale16ToRGBA64, - storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 } // Format_Grayscale16 + storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 }, // Format_Grayscale16 + pixelLayoutRGB<QImage::Format_BGR888>(), }; Q_STATIC_ASSERT(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats); @@ -1641,7 +1652,8 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { storeRGBX64FromRGBA64PM, storeRGBA64FromRGBA64PM, storeRGBA64PMFromRGBA64PM, - storeGray16FromRGBA64PM + storeGray16FromRGBA64PM, + storeGenericFromRGBA64PM<QImage::Format_BGR888>, }; /* @@ -1699,22 +1711,6 @@ static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int, return buffer; } -static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) -{ - const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); -} - -static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int) -{ - return (QRgba64 *)rasterBuffer->scanLine(y) + x; -} - -static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int) -{ - return buffer; -} - static DestFetchProc destFetchProc[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1746,8 +1742,26 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] = destFetch, // Format_RGBA64 destFetch, // Format_RGBA64_Premultiplied destFetch, // Format_Grayscale16 + destFetch, // Format_BGR888 }; +#if QT_CONFIG(raster_64bit) +static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; + return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); +} + +static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int) +{ + return (QRgba64 *)rasterBuffer->scanLine(y) + x; +} + +static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int) +{ + return buffer; +} + static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1779,7 +1793,9 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = destFetch64, // Format_RGBA64 destFetchRGB64, // Format_RGBA64_Premultiplied destFetch64, // Format_Grayscale16 + destFetch64, // Format_BGR888 }; +#endif /* Returns the color in the mono destination color table @@ -1887,21 +1903,6 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con store(dest, buffer, x, length, nullptr, nullptr); } -static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) -{ - auto store = qStoreFromRGBA64PM[rasterBuffer->format]; - uchar *dest = rasterBuffer->scanLine(y); - store(dest, buffer, x, length, nullptr, nullptr); -} - -static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) -{ - QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x; - for (int i = 0; i < length; ++i) { - dest[i] = buffer[i].unpremultiplied(); - } -} - static DestStoreProc destStoreProc[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1933,8 +1934,25 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] = destStore, // Format_RGBA64 destStore, // Format_RGBA64_Premultiplied destStore, // Format_Grayscale16 + destStore, // Format_BGR888 }; +#if QT_CONFIG(raster_64bit) +static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +{ + auto store = qStoreFromRGBA64PM[rasterBuffer->format]; + uchar *dest = rasterBuffer->scanLine(y); + store(dest, buffer, x, length, nullptr, nullptr); +} + +static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +{ + QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x; + for (int i = 0; i < length; ++i) { + dest[i] = buffer[i].unpremultiplied(); + } +} + static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1966,7 +1984,9 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = destStore64RGBA64, // Format_RGBA64 0, // Format_RGBA64_Premultiplied destStore64, // Format_Grayscale16 + destStore64, // Format_BGR888 }; +#endif /* Source fetches @@ -2017,6 +2037,7 @@ static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Opera return buffer; } +#if QT_CONFIG(raster_64bit) static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { @@ -2030,6 +2051,7 @@ static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Op const uchar *scanLine = data->texture.scanLine(y); return reinterpret_cast<const QRgba64 *>(scanLine) + x; } +#endif template<TextureBlendType blendType> inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v) @@ -2237,6 +2259,7 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, return buffer; } +#if QT_CONFIG(raster_64bit) template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) @@ -2257,6 +2280,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper convertRGBA64ToRGBA64PM(buffer, length); return buffer; } +#endif /** \internal interpolate 4 argb pixels with the distx and disty factor. @@ -3558,6 +3582,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper return buffer; } +#if QT_CONFIG(raster_64bit) template<TextureBlendType blendType> static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data, int y, int x, int length) @@ -3878,6 +3903,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length); return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length); } +#endif // FetchUntransformed can have more specialized methods added depending on SIMD features. static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { @@ -3910,6 +3936,7 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { fetchUntransformed, // RGBA64 fetchUntransformed, // RGBA64_Premultiplied fetchUntransformed, // Grayscale16 + fetchUntransformed, // BGR888 }; static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = { @@ -3948,6 +3975,20 @@ static SourceFetchProc sourceFetchAny32[NBlendTypes] = { fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled }; +static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format) +{ + if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied) + return sourceFetchARGB32PM[blendType]; + if (blendType == BlendUntransformed || blendType == BlendTiled) + return sourceFetchUntransformed[format]; + if (qPixelLayouts[format].bpp == QPixelLayout::BPP16) + return sourceFetchAny16[blendType]; + if (qPixelLayouts[format].bpp == QPixelLayout::BPP32) + return sourceFetchAny32[blendType]; + return sourceFetchGeneric[blendType]; +} + +#if QT_CONFIG(raster_64bit) static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = { fetchUntransformed64, // Untransformed fetchUntransformed64, // Tiled @@ -3966,25 +4007,13 @@ static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = { fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled }; -static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format) -{ - if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied) - return sourceFetchARGB32PM[blendType]; - if (blendType == BlendUntransformed || blendType == BlendTiled) - return sourceFetchUntransformed[format]; - if (qPixelLayouts[format].bpp == QPixelLayout::BPP16) - return sourceFetchAny16[blendType]; - if (qPixelLayouts[format].bpp == QPixelLayout::BPP32) - return sourceFetchAny32[blendType]; - return sourceFetchGeneric[blendType]; -} - static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format) { if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied) return sourceFetchRGBA64PM[blendType]; return sourceFetchGeneric64[blendType]; } +#endif #define FIXPT_BITS 8 @@ -3996,11 +4025,13 @@ static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos) return data->colorTable32[qt_gradient_clamp(data, ipos)]; } +#if QT_CONFIG(raster_64bit) static const QRgba64& qt_gradient_pixel64_fixed(const QGradientData *data, int fixed_pos) { int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; return data->colorTable64[qt_gradient_clamp(data, ipos)]; } +#endif static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data) { @@ -4034,6 +4065,7 @@ public: } }; +#if QT_CONFIG(raster_64bit) class GradientBase64 { public: @@ -4052,6 +4084,7 @@ public: qt_memfill64((quint64*)buffer, fill, length); } }; +#endif template<class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template( @@ -4129,11 +4162,13 @@ static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Ope return qt_fetch_linear_gradient_template<GradientBase32, uint>(buffer, op, data, y, x, length); } +#if QT_CONFIG(raster_64bit) static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { return qt_fetch_linear_gradient_template<GradientBase64, QRgba64>(buffer, op, data, y, x, length); } +#endif static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data) { @@ -4195,11 +4230,13 @@ const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Oper static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain; +#if QT_CONFIG(raster_64bit) const QRgba64 * QT_FASTCALL qt_fetch_radial_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase64>, QRgba64>(buffer, op, data, y, x, length); } +#endif template <class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template( @@ -4258,23 +4295,29 @@ static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Op return qt_fetch_conical_gradient_template<GradientBase32, uint>(buffer, data, y, x, length); } +#if QT_CONFIG(raster_64bit) static const QRgba64 * QT_FASTCALL qt_fetch_conical_gradient_rgb64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { return qt_fetch_conical_gradient_template<GradientBase64, QRgba64>(buffer, data, y, x, length); } +#endif extern CompositionFunctionSolid qt_functionForModeSolid_C[]; extern CompositionFunctionSolid64 qt_functionForModeSolid64_C[]; static const CompositionFunctionSolid *functionForModeSolid = qt_functionForModeSolid_C; +#if QT_CONFIG(raster_64bit) static const CompositionFunctionSolid64 *functionForModeSolid64 = qt_functionForModeSolid64_C; +#endif extern CompositionFunction qt_functionForMode_C[]; extern CompositionFunction64 qt_functionForMode64_C[]; static const CompositionFunction *functionForMode = qt_functionForMode_C; +#if QT_CONFIG(raster_64bit) static const CompositionFunction64 *functionForMode64 = qt_functionForMode64_C; +#endif static TextureBlendType getBlendType(const QSpanData *data) { @@ -4306,41 +4349,58 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in case QSpanData::Solid: solidSource = data->solidColor.isOpaque(); op.srcFetch = 0; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = 0; +#endif break; case QSpanData::LinearGradient: solidSource = !data->gradient.alphaColor; getLinearGradientValues(&op.linear, data); op.srcFetch = qt_fetch_linear_gradient; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_linear_gradient_rgb64; +#endif break; case QSpanData::RadialGradient: solidSource = !data->gradient.alphaColor; getRadialGradientValues(&op.radial, data); op.srcFetch = qt_fetch_radial_gradient; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_radial_gradient_rgb64; +#endif break; case QSpanData::ConicalGradient: solidSource = !data->gradient.alphaColor; op.srcFetch = qt_fetch_conical_gradient; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_conical_gradient_rgb64; +#endif break; case QSpanData::Texture: solidSource = !data->texture.hasAlpha; op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format); +#if QT_CONFIG(raster_64bit) op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);; +#endif break; default: Q_UNREACHABLE(); break; } +#if !QT_CONFIG(raster_64bit) + op.srcFetch64 = 0; +#endif op.mode = data->rasterBuffer->compositionMode; if (op.mode == QPainter::CompositionMode_SourceOver && solidSource) op.mode = QPainter::CompositionMode_Source; op.destFetch = destFetchProc[data->rasterBuffer->format]; +#if QT_CONFIG(raster_64bit) op.destFetch64 = destFetchProc64[data->rasterBuffer->format]; +#else + op.destFetch64 = 0; +#endif if (op.mode == QPainter::CompositionMode_Source && (data->type != QSpanData::Texture || data->texture.const_alpha == 256)) { const QSpan *lastSpan = spans + spanCount; @@ -4357,18 +4417,25 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in // But don't clear passthrough destFetch as they are just as fast and save destStore. if (op.destFetch != destFetchARGB32P) op.destFetch = destFetchUndefined; +#if QT_CONFIG(raster_64bit) if (op.destFetch64 != destFetchRGB64) op.destFetch64 = destFetch64Undefined; +#endif } } op.destStore = destStoreProc[data->rasterBuffer->format]; - op.destStore64 = destStoreProc64[data->rasterBuffer->format]; - op.funcSolid = functionForModeSolid[op.mode]; - op.funcSolid64 = functionForModeSolid64[op.mode]; op.func = functionForMode[op.mode]; +#if QT_CONFIG(raster_64bit) + op.destStore64 = destStoreProc64[data->rasterBuffer->format]; + op.funcSolid64 = functionForModeSolid64[op.mode]; op.func64 = functionForMode64[op.mode]; +#else + op.destStore64 = 0; + op.funcSolid64 = 0; + op.func64 = 0; +#endif return op; } @@ -4475,6 +4542,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) { +#if QT_CONFIG(raster_64bit) QSpanData *data = reinterpret_cast<QSpanData *>(userData); Operator op = getOperator(data, nullptr, 0); if (!op.funcSolid64) { @@ -4509,6 +4577,9 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) } ++spans; } +#else + blend_color_generic(count, spans, userData); +#endif } static void blend_color_rgb16(int count, const QSpan *spans, void *userData) @@ -4689,6 +4760,7 @@ public: } }; +#if QT_CONFIG(raster_64bit) class BlendSrcGenericRGB64 : public QBlendBase<QRgba64> { public: @@ -4719,6 +4791,7 @@ public: op.destStore64(data->rasterBuffer, x, y, dest, len); } }; +#endif static void blend_src_generic(int count, const QSpan *spans, void *userData) { @@ -4727,6 +4800,7 @@ static void blend_src_generic(int count, const QSpan *spans, void *userData) handleSpans(count, spans, data, blend); } +#if QT_CONFIG(raster_64bit) static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4740,6 +4814,7 @@ static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userDat handleSpans(count, spans, data, blend32); } } +#endif static void blend_untransformed_generic(int count, const QSpan *spans, void *userData) { @@ -4787,6 +4862,7 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use } } +#if QT_CONFIG(raster_64bit) static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4836,6 +4912,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi } } } +#endif static void blend_untransformed_argb(int count, const QSpan *spans, void *userData) { @@ -5036,6 +5113,7 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData) } } +#if QT_CONFIG(raster_64bit) static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -5131,6 +5209,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD ++spans; } } +#endif static void blend_tiled_argb(int count, const QSpan *spans, void *userData) { @@ -5307,6 +5386,7 @@ static const ProcessSpans processTextureSpansGeneric[NBlendTypes] = { blend_src_generic // TransformedBilinearTiled }; +#if QT_CONFIG(raster_64bit) static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = { blend_untransformed_generic_rgb64, // Untransformed blend_tiled_generic_rgb64, // Tiled @@ -5315,6 +5395,7 @@ static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = { blend_src_generic_rgb64, // TransformedBilinear blend_src_generic_rgb64 // TransformedBilinearTiled }; +#endif void qBlendTexture(int count, const QSpan *spans, void *userData) { @@ -5328,6 +5409,7 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_RGB16: proc = processTextureSpansRGB16[blendType]; break; +#if QT_CONFIG(raster_64bit) #if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8) case QImage::Format_ARGB32: case QImage::Format_RGBA8888: @@ -5342,6 +5424,7 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_Grayscale16: proc = processTextureSpansGeneric64[blendType]; break; +#endif // QT_CONFIG(raster_64bit) case QImage::Format_Invalid: Q_UNREACHABLE(); return; @@ -5409,7 +5492,11 @@ static void blend_vertical_gradient(int count, const QSpan *spans, void *userDat while (count--) { int y = spans->y; +#if QT_CONFIG(raster_64bit) data->solidColor = qt_gradient_pixel64_fixed(&data->gradient, yinc * y + off); +#else + data->solidColor = QRgba64::fromArgb32(qt_gradient_pixel_fixed(&data->gradient, yinc * y + off)); +#endif blend_color(1, spans, userData); ++spans; } @@ -5432,6 +5519,7 @@ void qBlendGradient(int count, const QSpan *spans, void *userData) if (isVerticalGradient) return blend_vertical_gradient_argb(count, spans, userData); return blend_src_generic(count, spans, userData); +#if QT_CONFIG(raster_64bit) #if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8) case QImage::Format_ARGB32: case QImage::Format_RGBA8888: @@ -5446,6 +5534,7 @@ void qBlendGradient(int count, const QSpan *spans, void *userData) if (isVerticalGradient) return blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData); return blend_src_generic_rgb64(count, spans, userData); +#endif // QT_CONFIG(raster_64bit) case QImage::Format_Invalid: break; default: @@ -5555,29 +5644,77 @@ inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer, map, mapWidth, mapHeight, mapStride); } -static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile) +static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile) +{ + // Do a gammacorrected gray alphablend... + const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); + + QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage); + + *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); +} + +static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile) { if (coverage == 0) { // nothing - } else if (coverage == 255) { - dest[x] = src; + } else if (coverage == 255 || !colorProfile) { + blend_pixel(*dst, src, coverage); + } else if (*dst < 0xff000000) { + // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571 + blend_pixel(*dst, src, coverage); + } else if (src >= 0xff000000) { + grayBlendPixel(dst, coverage, srcLinear, colorProfile); } else { - QRgba64 dstColor = dest[x]; - if (colorProfile) { - if (dstColor.isOpaque()) - dstColor = colorProfile->toLinear(dstColor); - else if (!dstColor.isTransparent()) - dstColor = colorProfile->toLinear(dstColor.unpremultiplied()).premultiplied(); - } + // First do naive blend with text-color + QRgb s = *dst; + blend_pixel(s, src); + // Then gamma-corrected blend with glyph shape + QRgba64 s64 = colorProfile ? colorProfile->toLinear64(s) : QRgba64::fromArgb32(s); + grayBlendPixel(dst, coverage, s64, colorProfile); + } +} - dstColor = interpolate255(srcLinear, coverage, dstColor, 255 - coverage); - if (colorProfile) { - if (dstColor.isOpaque()) - dstColor = colorProfile->fromLinear(dstColor); - else if (!dstColor.isTransparent()) - dstColor = colorProfile->fromLinear(dstColor.unpremultiplied()).premultiplied(); - } - dest[x] = dstColor; +#if QT_CONFIG(raster_64bit) + +static inline void grayBlendPixel(QRgba64 &dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile) +{ + // Do a gammacorrected gray alphablend... + QRgba64 dstColor = dst; + if (colorProfile) { + if (dstColor.isOpaque()) + dstColor = colorProfile->toLinear(dstColor); + else if (!dstColor.isTransparent()) + dstColor = colorProfile->toLinear(dstColor.unpremultiplied()).premultiplied(); + } + + blend_pixel(dstColor, srcLinear, coverage); + + if (colorProfile) { + if (dstColor.isOpaque()) + dstColor = colorProfile->fromLinear(dstColor); + else if (!dstColor.isTransparent()) + dstColor = colorProfile->fromLinear(dstColor.unpremultiplied()).premultiplied(); + } + dst = dstColor; +} + +static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile) +{ + if (coverage == 0) { + // nothing + } else if (coverage == 255) { + blend_pixel(dest[x], src); + } else if (src.isOpaque()) { + grayBlendPixel(dest[x], coverage, srcLinear, colorProfile); + } else { + // First do naive blend with text-color + QRgba64 s = dest[x]; + blend_pixel(s, src); + // Then gamma-corrected blend with glyph shape + if (colorProfile) + s = colorProfile->toLinear(s); + grayBlendPixel(dest[x], coverage, s, colorProfile); } } @@ -5590,18 +5727,14 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, if (color.isTransparent()) return; - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); QRgba64 srcColor = color; - if (colorProfile) { - if (color.isOpaque()) - srcColor = colorProfile->toLinear(srcColor); - else - srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); - } + if (colorProfile && color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); alignas(8) QRgba64 buffer[BufferSize]; const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format]; @@ -5656,6 +5789,81 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, } // for (yp -> bottom) } } +#else +static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, + int x, int y, const QRgba64 &color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride, + const QClipData *clip, bool useGammaCorrection) +{ + if (color.isTransparent()) + return; + + const quint32 c = color.toArgb32(); + + const QColorTrcLut *colorProfile = nullptr; + + if (useGammaCorrection) + colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); + + QRgba64 srcColor = color; + if (colorProfile && color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); + + quint32 buffer[BufferSize]; + const DestFetchProc destFetch = destFetchProc[rasterBuffer->format]; + const DestStoreProc destStore = destStoreProc[rasterBuffer->format]; + + if (!clip) { + for (int ly = 0; ly < mapHeight; ++ly) { + int i = x; + int length = mapWidth; + while (length > 0) { + int l = qMin(BufferSize, length); + quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l); + for (int j=0; j < l; ++j) { + const int coverage = map[j + (i - x)]; + alphamapblend_argb32(dest + j, coverage, srcColor, c, colorProfile); + } + if (destStore) + destStore(rasterBuffer, i, y + ly, dest, l); + length -= l; + i += l; + } + map += mapStride; + } + } else { + int bottom = qMin(y + mapHeight, rasterBuffer->height()); + + int top = qMax(y, 0); + map += (top - y) * mapStride; + + const_cast<QClipData *>(clip)->initialize(); + for (int yp = top; yp<bottom; ++yp) { + const QClipData::ClipLine &line = clip->m_clipLines[yp]; + + for (int i=0; i<line.count; ++i) { + const QSpan &clip = line.spans[i]; + + int start = qMax<int>(x, clip.x); + int end = qMin<int>(x + mapWidth, clip.x + clip.len); + if (end <= start) + continue; + Q_ASSERT(end - start <= BufferSize); + quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start); + + for (int xp=start; xp<end; ++xp) { + const int coverage = map[xp - x]; + alphamapblend_argb32(dest + xp - x, coverage, srcColor, color, colorProfile); + } + if (destStore) + destStore(rasterBuffer, start, clip.y, dest, end - start); + } // for (i -> line.count) + map += mapStride; + } // for (yp -> bottom) + } +} +#endif static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor) { @@ -5675,7 +5883,7 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, int mapWidth, int mapHeight, int mapStride, const QClipData *clip, bool useGammaCorrection) { - if (useGammaCorrection) { + if (useGammaCorrection || !color.isOpaque()) { qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection); return; } @@ -5716,44 +5924,6 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, } } -static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile) -{ - // Do a gammacorrected RGB alphablend... - const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); - - QRgba64 blend = rgbBlend(dlinear, slinear, coverage); - - *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); -} - -static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorProfile *colorProfile) -{ - // Do a gammacorrected gray alphablend... - const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); - - QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage); - - *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); -} - -static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorProfile *colorProfile) -{ - if (coverage == 0) { - // nothing - } else if (coverage == 255) { - *dst = src; - } else if (!colorProfile) { - *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage); - } else { - if (*dst >= 0xff000000) { - grayBlendPixel(dst, coverage, srcLinear, colorProfile); - } else { - // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571 - *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage); - } - } -} - static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uchar *map, @@ -5766,18 +5936,14 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer, if (color.isTransparent()) return; - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); QRgba64 srcColor = color; - if (colorProfile) { - if (color.isOpaque()) - srcColor = colorProfile->toLinear(srcColor); - else - srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); - } + if (colorProfile && color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); if (!clip) { quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; @@ -5822,6 +5988,16 @@ static inline int qRgbAvg(QRgb rgb) return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16; } +static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile) +{ + // Do a gammacorrected RGB alphablend... + const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); + + QRgba64 blend = rgbBlend(dlinear, slinear, coverage); + + *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); +} + static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha) { #if defined(__SSE2__) @@ -5862,42 +6038,59 @@ static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha) #endif } -static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile) +static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorTrcLut *colorProfile) { if (coverage == 0xff000000) { // nothing - } else if (coverage == 0xffffffff) { - dest[x] = src; + } else if (coverage == 0xffffffff && qAlpha(src) == 255) { + blend_pixel(*dst, src); + } else if (*dst < 0xff000000) { + // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571 + blend_pixel(*dst, src, qRgbAvg(coverage)); + } else if (!colorProfile) { + *dst = rgbBlend(*dst, src, coverage); + } else if (srcLinear.isOpaque()) { + rgbBlendPixel(dst, coverage, srcLinear, colorProfile); } else { - QRgba64 dstColor = dest[x]; - if (dstColor.isOpaque()) { - if (colorProfile) - dstColor = colorProfile->toLinear(dstColor); - dstColor = rgbBlend(dstColor, srcLinear, coverage); - if (colorProfile) - dstColor = colorProfile->fromLinear(dstColor); - dest[x] = dstColor; - } else { - // Do a gray alphablend. - alphamapblend_generic(qRgbAvg(coverage), dest, x, srcLinear, src, colorProfile); - } + // First do naive blend with text-color + QRgb s = *dst; + blend_pixel(s, src); + // Then gamma-corrected blend with glyph shape + QRgba64 s64 = colorProfile ? colorProfile->toLinear64(s) : QRgba64::fromArgb32(s); + rgbBlendPixel(dst, coverage, s64, colorProfile); } } -static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorProfile *colorProfile) +#if QT_CONFIG(raster_64bit) +static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile) +{ + // Do a gammacorrected RGB alphablend... + const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(dst) : dst; + + QRgba64 blend = rgbBlend(dlinear, slinear, coverage); + + dst = colorProfile ? colorProfile->fromLinear(blend) : blend; +} + +static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile) { if (coverage == 0xff000000) { // nothing } else if (coverage == 0xffffffff) { - *dst = src; - } else if (*dst < 0xff000000) { - // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571 - const int a = qRgbAvg(coverage); - *dst = INTERPOLATE_PIXEL_255(src, a, *dst, 255 - a); - } else if (!colorProfile) { - *dst = rgbBlend(*dst, src, coverage); - } else { - rgbBlendPixel(dst, coverage, srcLinear, colorProfile); + blend_pixel(dest[x], src); + } else if (!dest[x].isOpaque()) { + // Do a gray alphablend. + alphamapblend_generic(qRgbAvg(coverage), dest, x, srcLinear, src, colorProfile); + } else if (src.isOpaque()) { + rgbBlendPixel(dest[x], coverage, srcLinear, colorProfile); + } else { + // First do naive blend with text-color + QRgba64 s = dest[x]; + blend_pixel(s, src); + // Then gamma-corrected blend with glyph shape + if (colorProfile) + s = colorProfile->toLinear(s); + rgbBlendPixel(dest[x], coverage, s, colorProfile); } } @@ -5909,18 +6102,14 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, if (color.isTransparent()) return; - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); QRgba64 srcColor = color; - if (colorProfile) { - if (color.isOpaque()) - srcColor = colorProfile->toLinear(srcColor); - else - srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); - } + if (colorProfile && color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); alignas(8) QRgba64 buffer[BufferSize]; const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format]; @@ -5975,6 +6164,80 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, } // for (yp -> bottom) } } +#else +static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, + int x, int y, const QRgba64 &color, + const uint *src, int mapWidth, int mapHeight, int srcStride, + const QClipData *clip, bool useGammaCorrection) +{ + if (color.isTransparent()) + return; + + const quint32 c = color.toArgb32(); + + const QColorTrcLut *colorProfile = nullptr; + + if (useGammaCorrection) + colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + + QRgba64 srcColor = color; + if (colorProfile && color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); + + quint32 buffer[BufferSize]; + const DestFetchProc destFetch = destFetchProc[rasterBuffer->format]; + const DestStoreProc destStore = destStoreProc[rasterBuffer->format]; + + if (!clip) { + for (int ly = 0; ly < mapHeight; ++ly) { + int i = x; + int length = mapWidth; + while (length > 0) { + int l = qMin(BufferSize, length); + quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l); + for (int j=0; j < l; ++j) { + const uint coverage = src[j + (i - x)]; + alphargbblend_argb32(dest + j, coverage, srcColor, c, colorProfile); + } + if (destStore) + destStore(rasterBuffer, i, y + ly, dest, l); + length -= l; + i += l; + } + src += srcStride; + } + } else { + int bottom = qMin(y + mapHeight, rasterBuffer->height()); + + int top = qMax(y, 0); + src += (top - y) * srcStride; + + const_cast<QClipData *>(clip)->initialize(); + for (int yp = top; yp<bottom; ++yp) { + const QClipData::ClipLine &line = clip->m_clipLines[yp]; + + for (int i=0; i<line.count; ++i) { + const QSpan &clip = line.spans[i]; + + int start = qMax<int>(x, clip.x); + int end = qMin<int>(x + mapWidth, clip.x + clip.len); + if (end <= start) + continue; + Q_ASSERT(end - start <= BufferSize); + quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start); + + for (int xp=start; xp<end; ++xp) { + const uint coverage = src[xp - x]; + alphargbblend_argb32(dest + xp - start, coverage, srcColor, c, colorProfile); + } + if (destStore) + destStore(rasterBuffer, start, clip.y, dest, end - start); + } // for (i -> line.count) + src += srcStride; + } // for (yp -> bottom) + } +} +#endif static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, @@ -5986,18 +6249,14 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, const quint32 c = color.toArgb32(); - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); QRgba64 srcColor = color; - if (colorProfile) { - if (color.isOpaque()) - srcColor = colorProfile->toLinear(srcColor); - else - srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); - } + if (colorProfile && color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); if (!clip) { quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; @@ -6354,6 +6613,14 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = qt_alphargbblit_generic, qt_rectfill_quint16 }, + // Format_BGR888 + { + blend_color_generic, + 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint24 + }, }; #if !defined(__SSE2__) @@ -6515,6 +6782,9 @@ static void qInitDrawhelperFunctions() qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3; + extern void QT_FASTCALL rbSwap_888_ssse3(uchar *dst, const uchar *src, int count); + qPixelLayouts[QImage::Format_RGB888].rbSwap = rbSwap_888_ssse3; + qPixelLayouts[QImage::Format_BGR888].rbSwap = rbSwap_888_ssse3; } #endif // SSSE3 @@ -6565,8 +6835,10 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>; qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4; qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4; +#if QT_CONFIG(raster_64bit) destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4; destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4; +#endif } #endif @@ -6590,18 +6862,19 @@ static void qInitDrawhelperFunctions() qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2; extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); - extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); - extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha); - extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha); - qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_avx2; - qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2; qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_avx2; - qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2; qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2; +#if QT_CONFIG(raster_64bit) + extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha); + qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2; + qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2; qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2; +#endif extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int /*fdy*/); @@ -6625,6 +6898,7 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_avx2; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2; +#if QT_CONFIG(raster_64bit) extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *, const uint *, int, const QVector<QRgb> *, QDitherInfo *); extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uint *, int count, const QVector<QRgb> *, QDitherInfo *); extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QVector<QRgb> *, QDitherInfo *); @@ -6633,6 +6907,7 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_avx2; qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2; qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2; +#endif } #endif diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index 2b3cc9b226..fb50cb6a50 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -396,6 +396,7 @@ void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixe BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(dst, src, length, const_alpha); } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, int length, uint const_alpha) { Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] @@ -453,6 +454,7 @@ void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *sr blend_pixel(dst[x], src[x], const_alpha); } } +#endif void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, uint const_alpha) { @@ -485,6 +487,7 @@ void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, u } } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, int length, uint const_alpha) { Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] @@ -517,6 +520,7 @@ void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, i dst[x] = interpolate65535(src[x], ca, dst[x], cia); } } +#endif void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha) { @@ -549,6 +553,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, u } } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha) { Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] @@ -579,6 +584,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int destPixels[x] = color + multiplyAlpha65535(destPixels[x], minusAlphaOfColor); } } +#endif #define interpolate_4_pixels_16_avx2(tlr1, tlr2, blr1, blr2, distx, disty, colorMask, v_256, b) \ { \ diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 37108949d6..dd42b96d79 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -296,7 +296,9 @@ struct QGradientData #define GRADIENT_STOPTABLE_SIZE 1024 #define GRADIENT_STOPTABLE_SIZE_SHIFT 10 +#if QT_CONFIG(raster_64bit) const QRgba64 *colorTable64; //[GRADIENT_STOPTABLE_SIZE]; +#endif const QRgb *colorTable32; //[GRADIENT_STOPTABLE_SIZE]; uint alphaColor : 1; @@ -328,7 +330,7 @@ struct QTextureData struct QSpanData { - QSpanData() : tempImage(0) {} + QSpanData() : tempImage(nullptr) {} ~QSpanData() { delete tempImage; } QRasterBuffer *rasterBuffer; @@ -402,11 +404,13 @@ static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos) return data->colorTable32[qt_gradient_clamp(data, ipos)]; } +#if QT_CONFIG(raster_64bit) static inline const QRgba64& qt_gradient_pixel64(const QGradientData *data, qreal pos) { int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5)); return data->colorTable64[qt_gradient_clamp(data, ipos)]; } +#endif static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c) { @@ -667,6 +671,8 @@ static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src) static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src, const int const_alpha) { + if (const_alpha == 255) + return blend_pixel(dst, src); if (src != 0) { const quint32 s = BYTE_MUL(src, const_alpha); dst = s + BYTE_MUL(dst, qAlpha(~s)); diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index e387026972..68d887ae6d 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -417,6 +417,7 @@ void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, i d[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]); } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) { uint *dest = (uint*)rasterBuffer->scanLine(y) + x; @@ -428,6 +429,7 @@ void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, in uint *dest = (uint*)rasterBuffer->scanLine(y) + x; convertARGBFromRGBA64PM_sse4<true>(dest, buffer, length); } +#endif void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp index 35d61c3e6c..14d7047bb6 100644 --- a/src/gui/painting/qdrawhelper_ssse3.cpp +++ b/src/gui/painting/qdrawhelper_ssse3.cpp @@ -40,7 +40,7 @@ #include <private/qdrawhelper_x86_p.h> -#ifdef QT_COMPILER_SUPPORTS_SSSE3 +#if defined(QT_COMPILER_SUPPORTS_SSSE3) #include <private/qdrawingprimitive_sse2_p.h> @@ -254,6 +254,49 @@ void qt_memfill24_ssse3(quint24 *dest, quint24 color, qsizetype count) } } +void QT_FASTCALL rbSwap_888_ssse3(uchar *dst, const uchar *src, int count) +{ + int i = 0; + + const static __m128i shuffleMask1 = _mm_setr_epi8(2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, /*!!*/15); + const static __m128i shuffleMask2 = _mm_setr_epi8(0, /*!!*/1, 4, 3, 2, 7, 6, 5, 10, 9, 8, 13, 12, 11, /*!!*/14, 15); + const static __m128i shuffleMask3 = _mm_setr_epi8(/*!!*/0, 3, 2, 1, 6, 5, 4, 9, 8, 7, 12, 11, 10, 15, 14, 13); + + for (; i + 15 < count; i += 16) { + __m128i s1 = _mm_loadu_si128((const __m128i *)src); + __m128i s2 = _mm_loadu_si128((const __m128i *)(src + 16)); + __m128i s3 = _mm_loadu_si128((const __m128i *)(src + 32)); + s1 = _mm_shuffle_epi8(s1, shuffleMask1); + s2 = _mm_shuffle_epi8(s2, shuffleMask2); + s3 = _mm_shuffle_epi8(s3, shuffleMask3); + _mm_storeu_si128((__m128i *)dst, s1); + _mm_storeu_si128((__m128i *)(dst + 16), s2); + _mm_storeu_si128((__m128i *)(dst + 32), s3); + + // Now fix the last four misplaced values + std::swap(dst[15], dst[17]); + std::swap(dst[30], dst[32]); + + src += 48; + dst += 48; + } + + if (src != dst) { + SIMD_EPILOGUE(i, count, 15) { + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst += 3; + src += 3; + } + } else { + SIMD_EPILOGUE(i, count, 15) { + std::swap(dst[0], dst[2]); + dst += 3; + } + } +} + QT_END_NAMESPACE #endif // QT_COMPILER_SUPPORTS_SSSE3 diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp new file mode 100644 index 0000000000..18f212f8e9 --- /dev/null +++ b/src/gui/painting/qicc.cpp @@ -0,0 +1,776 @@ +/**************************************************************************** +** +** 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 "qicc_p.h" + +#include <qbuffer.h> +#include <qbytearray.h> +#include <qdatastream.h> +#include <qendian.h> +#include <qloggingcategory.h> +#include <qstring.h> + +#include "qcolorspace_p.h" +#include "qcolortrc_p.h" + +QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcIcc, "qt.gui.icc") + +struct ICCProfileHeader +{ + quint32_be profileSize; + + quint32_be preferredCmmType; + + quint32_be profileVersion; + quint32_be profileClass; + quint32_be inputColorSpace; + quint32_be pcs; + quint32_be datetime[3]; + quint32_be signature; + quint32_be platformSignature; + quint32_be flags; + quint32_be deviceManufacturer; + quint32_be deviceModel; + quint32_be deviceAttributes[2]; + + quint32_be renderingIntent; + qint32_be illuminantXyz[3]; + + quint32_be creatorSignature; + quint32_be profileId[4]; + + quint32_be reserved[7]; + +// Technically after the header, but easier to include here: + quint32_be tagCount; +}; + +constexpr quint32 IccTag(uchar a, uchar b, uchar c, uchar d) +{ + return (a << 24) | (b << 16) | (c << 8) | d; +} + +enum class ProfileClass : quint32 { + Input = IccTag('s', 'c', 'r', 'n'), + Display = IccTag('m', 'n', 't', 'r'), + // Not supported: + Output = IccTag('p', 'r', 't', 'r'), + ColorSpace = IccTag('s', 'p', 'a', 'c'), +}; + +enum class Tag : quint32 { + acsp = IccTag('a', 'c', 's', 'p'), + RGB_ = IccTag('R', 'G', 'B', ' '), + XYZ_ = IccTag('X', 'Y', 'Z', ' '), + rXYZ = IccTag('r', 'X', 'Y', 'Z'), + gXYZ = IccTag('g', 'X', 'Y', 'Z'), + bXYZ = IccTag('b', 'X', 'Y', 'Z'), + rTRC = IccTag('r', 'T', 'R', 'C'), + gTRC = IccTag('g', 'T', 'R', 'C'), + bTRC = IccTag('b', 'T', 'R', 'C'), + A2B0 = IccTag('A', '2', 'B', '0'), + A2B1 = IccTag('A', '2', 'B', '1'), + B2A0 = IccTag('B', '2', 'A', '0'), + B2A1 = IccTag('B', '2', 'A', '1'), + desc = IccTag('d', 'e', 's', 'c'), + text = IccTag('t', 'e', 'x', 't'), + cprt = IccTag('c', 'p', 'r', 't'), + curv = IccTag('c', 'u', 'r', 'v'), + para = IccTag('p', 'a', 'r', 'a'), + wtpt = IccTag('w', 't', 'p', 't'), + bkpt = IccTag('b', 'k', 'p', 't'), + mft1 = IccTag('m', 'f', 't', '1'), + mft2 = IccTag('m', 'f', 't', '2'), + mluc = IccTag('m', 'l', 'u', 'c'), + mAB_ = IccTag('m', 'A', 'B', ' '), + mBA_ = IccTag('m', 'B', 'A', ' '), + chad = IccTag('c', 'h', 'a', 'd'), + sf32 = IccTag('s', 'f', '3', '2'), + + // Apple extensions for ICCv2: + aarg = IccTag('a', 'a', 'r', 'g'), + aagg = IccTag('a', 'a', 'g', 'g'), + aabg = IccTag('a', 'a', 'b', 'g'), +}; + +inline uint qHash(const Tag &key, uint seed = 0) +{ + return qHash(quint32(key), seed); +} + +namespace QIcc { + +struct TagTableEntry +{ + quint32_be signature; + quint32_be offset; + quint32_be size; +}; + +struct GenericTagData { + quint32_be type; + quint32_be null; +}; + +struct XYZTagData : GenericTagData { + qint32_be fixedX; + qint32_be fixedY; + qint32_be fixedZ; +}; + +struct CurvTagData : GenericTagData { + quint32_be valueCount; + quint16_be value[1]; +}; + +struct ParaTagData : GenericTagData { + quint16_be curveType; + quint16_be null2; + quint32_be parameter[1]; +}; + +struct DescTagData : GenericTagData { + quint32_be asciiDescriptionLength; + char asciiDescription[1]; + // .. we ignore the rest +}; + +struct MlucTagRecord { + quint16_be languageCode; + quint16_be countryCode; + quint32_be size; + quint32_be offset; +}; + +struct MlucTagData : GenericTagData { + quint32_be recordCount; + quint32_be recordSize; // = sizeof(MlucTagRecord) + MlucTagRecord records[1]; +}; + +// For both mAB and mBA +struct mABTagData : GenericTagData { + quint8 inputChannels; + quint8 outputChannels; + quint8 padding[2]; + quint32_be bCurvesOffset; + quint32_be matrixOffset; + quint32_be mCurvesOffset; + quint32_be clutOffset; + quint32_be aCurvesOffset; +}; + +struct Sf32TagData : GenericTagData { + quint32_be value[1]; +}; + +static int toFixedS1516(float x) +{ + return int(x * 65536.0f + 0.5f); +} + +static float fromFixedS1516(int x) +{ + return x * (1.0f / 65536.0f); +} + +static bool isValidIccProfile(const ICCProfileHeader &header) +{ + if (header.signature != uint(Tag::acsp)) { + qCWarning(lcIcc, "Failed ICC signature test"); + return false; + } + + // Don't overflow 32bit integers: + if (header.tagCount >= INT32_MAX / sizeof(TagTableEntry)) + return false; + if (header.profileSize - sizeof(ICCProfileHeader) < header.tagCount * sizeof(TagTableEntry)) { + qCWarning(lcIcc, "Failed basic size sanity"); + return false; + } + + if (header.profileClass != uint(ProfileClass::Input) + && header.profileClass != uint(ProfileClass::Display)) { + qCWarning(lcIcc, "Unsupported ICC profile class %x", quint32(header.profileClass)); + return false; + } + if (header.inputColorSpace != 0x52474220 /* 'RGB '*/) { + qCWarning(lcIcc, "Unsupported ICC input color space %x", quint32(header.inputColorSpace)); + return false; + } + if (header.pcs != 0x58595a20 /* 'XYZ '*/) { + // ### support PCSLAB + qCWarning(lcIcc, "Unsupported ICC profile connection space %x", quint32(header.pcs)); + return false; + } + + QColorVector illuminant; + illuminant.x = fromFixedS1516(header.illuminantXyz[0]); + illuminant.y = fromFixedS1516(header.illuminantXyz[1]); + illuminant.z = fromFixedS1516(header.illuminantXyz[2]); + if (illuminant != QColorVector::D50()) { + qCWarning(lcIcc, "Invalid ICC illuminant"); + return false; + } + + return true; +} + +static int writeColorTrc(QDataStream &stream, const QColorTrc &trc) +{ + if (trc.isLinear()) { + stream << uint(Tag::curv) << uint(0); + stream << uint(0); + return 12; + } + + if (trc.m_type == QColorTrc::Type::Function) { + const QColorTransferFunction &fun = trc.m_fun; + stream << uint(Tag::para) << uint(0); + if (fun.isGamma()) { + stream << ushort(0) << ushort(0); + stream << toFixedS1516(fun.m_g); + return 12 + 4; + } + bool type3 = qFuzzyIsNull(fun.m_e) && qFuzzyIsNull(fun.m_f); + stream << ushort(type3 ? 3 : 4) << ushort(0); + stream << toFixedS1516(fun.m_g); + stream << toFixedS1516(fun.m_a); + stream << toFixedS1516(fun.m_b); + stream << toFixedS1516(fun.m_c); + stream << toFixedS1516(fun.m_d); + if (type3) + return 12 + 5 * 4; + stream << toFixedS1516(fun.m_e); + stream << toFixedS1516(fun.m_f); + return 12 + 7 * 4; + } + + Q_ASSERT(trc.m_type == QColorTrc::Type::Table); + stream << uint(Tag::curv) << uint(0); + stream << uint(trc.m_table.m_tableSize); + if (!trc.m_table.m_table16.isEmpty()) { + for (uint i = 0; i < trc.m_table.m_tableSize; ++i) { + stream << ushort(trc.m_table.m_table16[i]); + } + } else { + for (uint i = 0; i < trc.m_table.m_tableSize; ++i) { + stream << ushort(trc.m_table.m_table8[i] * 257U); + } + } + return 12 + 2 * trc.m_table.m_tableSize; +} + +QByteArray toIccProfile(const QColorSpace &space) +{ + if (!space.isValid()) + return QByteArray(); + + const QColorSpacePrivate *spaceDPtr = QColorSpacePrivate::get(space); + + constexpr int tagCount = 9; + constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount; + constexpr uint variableTagTableOffsets = 128 + 4 + 12 * 5; + uint currentOffset = 0; + uint rTrcOffset, gTrcOffset, bTrcOffset; + uint rTrcSize, gTrcSize, bTrcSize; + uint descOffset, descSize; + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QDataStream stream(&buffer); + + // Profile header: + stream << uint(0); // Size, we will update this later + stream << uint(0); + stream << uint(0x02400000); // Version 2.4 (note we use 'para' from version 4) + stream << uint(ProfileClass::Display); + stream << uint(Tag::RGB_); + stream << uint(Tag::XYZ_); + stream << uint(0) << uint(0) << uint(0); + stream << uint(Tag::acsp); + stream << uint(0) << uint(0) << uint(0); + stream << uint(0) << uint(0) << uint(0); + stream << uint(1); // Rendering intent + stream << uint(0x0000f6d6); // D50 X + stream << uint(0x00010000); // D50 Y + stream << uint(0x0000d32d); // D50 Z + stream << IccTag('Q','t', QT_VERSION_MAJOR, QT_VERSION_MINOR); + stream << uint(0) << uint(0) << uint(0) << uint(0); + stream << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0); + + // Tag table: + stream << uint(tagCount); + stream << uint(Tag::rXYZ) << uint(profileDataOffset + 00) << uint(20); + stream << uint(Tag::gXYZ) << uint(profileDataOffset + 20) << uint(20); + stream << uint(Tag::bXYZ) << uint(profileDataOffset + 40) << uint(20); + stream << uint(Tag::wtpt) << uint(profileDataOffset + 60) << uint(20); + stream << uint(Tag::cprt) << uint(profileDataOffset + 80) << uint(12); + // From here the offset and size will be updated later: + stream << uint(Tag::rTRC) << uint(0) << uint(0); + stream << uint(Tag::gTRC) << uint(0) << uint(0); + stream << uint(Tag::bTRC) << uint(0) << uint(0); + stream << uint(Tag::desc) << uint(0) << uint(0); + // TODO: consider adding 'chad' tag (required in ICC >=4 when we have non-D50 whitepoint) + currentOffset = profileDataOffset; + + // Tag data: + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->toXyz.r.x); + stream << toFixedS1516(spaceDPtr->toXyz.r.y); + stream << toFixedS1516(spaceDPtr->toXyz.r.z); + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->toXyz.g.x); + stream << toFixedS1516(spaceDPtr->toXyz.g.y); + stream << toFixedS1516(spaceDPtr->toXyz.g.z); + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->toXyz.b.x); + stream << toFixedS1516(spaceDPtr->toXyz.b.y); + stream << toFixedS1516(spaceDPtr->toXyz.b.z); + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->whitePoint.x); + stream << toFixedS1516(spaceDPtr->whitePoint.y); + stream << toFixedS1516(spaceDPtr->whitePoint.z); + stream << uint(Tag::text) << uint(0); + stream << uint(IccTag('N', '/', 'A', '\0')); + currentOffset += 92; + + // From now on the data is variable sized: + rTrcOffset = currentOffset; + rTrcSize = writeColorTrc(stream, spaceDPtr->trc[0]); + currentOffset += rTrcSize; + if (spaceDPtr->trc[0] == spaceDPtr->trc[1]) { + gTrcOffset = rTrcOffset; + gTrcSize = rTrcSize; + } else { + gTrcOffset = currentOffset; + gTrcSize = writeColorTrc(stream, spaceDPtr->trc[1]); + currentOffset += gTrcSize; + } + if (spaceDPtr->trc[0] == spaceDPtr->trc[2]) { + bTrcOffset = rTrcOffset; + bTrcSize = rTrcSize; + } else { + bTrcOffset = currentOffset; + bTrcSize = writeColorTrc(stream, spaceDPtr->trc[2]); + currentOffset += bTrcSize; + } + + descOffset = currentOffset; + QByteArray description = spaceDPtr->description.toUtf8(); + stream << uint(Tag::desc) << uint(0); + stream << uint(description.size() + 1); + stream.writeRawData(description.constData(), description.size() + 1); + stream << uint(0) << uint(0); + stream << ushort(0) << uchar(0); + QByteArray macdesc(67, '\0'); + stream.writeRawData(macdesc.constData(), 67); + descSize = 90 + description.size() + 1; + currentOffset += descSize; + + buffer.close(); + QByteArray iccProfile = buffer.buffer(); + // Now write final size + *(quint32_be *)iccProfile.data() = iccProfile.size(); + // And the final indices and sizes of variable size tags: + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 4) = rTrcOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 8) = rTrcSize; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 12 + 4) = gTrcOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 12 + 8) = gTrcSize; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 2 * 12 + 4) = bTrcOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 2 * 12 + 8) = bTrcSize; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 3 * 12 + 4) = descOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 3 * 12 + 8) = descSize; + +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + const ICCProfileHeader *iccHeader = (const ICCProfileHeader *)iccProfile.constData(); + Q_ASSERT(qsizetype(iccHeader->profileSize) == qsizetype(iccProfile.size())); + Q_ASSERT(isValidIccProfile(*iccHeader)); +#endif + + return iccProfile; +} + +struct TagEntry { + quint32 offset; + quint32 size; +}; + +bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector &colorVector) +{ + if (tagEntry.size < sizeof(XYZTagData)) { + qCWarning(lcIcc) << "Undersized XYZ tag"; + return false; + } + const XYZTagData *xyz = reinterpret_cast<const XYZTagData *>(data.constData() + tagEntry.offset); + if (xyz->type != quint32(Tag::XYZ_)) { + qCWarning(lcIcc) << "Bad XYZ content type"; + return false; + } + const float x = fromFixedS1516(xyz->fixedX); + const float y = fromFixedS1516(xyz->fixedY); + const float z = fromFixedS1516(xyz->fixedZ); + + colorVector = QColorVector(x, y, z); + return true; +} + +bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma) +{ + const GenericTagData *trcData = reinterpret_cast<const GenericTagData *>(data.constData() + tagEntry.offset); + if (trcData->type == quint32(Tag::curv)) { + const CurvTagData *curv = static_cast<const CurvTagData *>(trcData); + if (curv->valueCount > (1 << 16)) + return false; + if (tagEntry.size - 12 < 2 * curv->valueCount) + return false; + if (curv->valueCount == 0) { + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(); // Linear + } else if (curv->valueCount == 1) { + float g = curv->value[0] * (1.0f / 256.0f); + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction::fromGamma(g); + } else { + QVector<quint16> tabl; + tabl.resize(curv->valueCount); + for (uint i = 0; i < curv->valueCount; ++i) + tabl[i] = curv->value[i]; + QColorTransferTable table = QColorTransferTable(curv->valueCount, std::move(tabl)); + QColorTransferFunction curve; + if (!table.asColorTransferFunction(&curve)) { + gamma.m_type = QColorTrc::Type::Table; + gamma.m_table = table; + } else { + qCDebug(lcIcc) << "Detected curv table as function"; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = curve; + } + } + return true; + } + if (trcData->type == quint32(Tag::para)) { + if (tagEntry.size < sizeof(ParaTagData)) + return false; + const ParaTagData *para = static_cast<const ParaTagData *>(trcData); + switch (para->curveType) { + case 0: { + float g = fromFixedS1516(para->parameter[0]); + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction::fromGamma(g); + break; + } + case 1: { + if (tagEntry.size < sizeof(ParaTagData) + 2 * 4) + return false; + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float d = -b / a; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g); + break; + } + case 2: { + if (tagEntry.size < sizeof(ParaTagData) + 3 * 4) + return false; + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float c = fromFixedS1516(para->parameter[3]); + float d = -b / a; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g); + break; + } + case 3: { + if (tagEntry.size < sizeof(ParaTagData) + 4 * 4) + return false; + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float c = fromFixedS1516(para->parameter[3]); + float d = fromFixedS1516(para->parameter[4]); + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g); + break; + } + case 4: { + if (tagEntry.size < sizeof(ParaTagData) + 6 * 4) + return false; + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float c = fromFixedS1516(para->parameter[3]); + float d = fromFixedS1516(para->parameter[4]); + float e = fromFixedS1516(para->parameter[5]); + float f = fromFixedS1516(para->parameter[6]); + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g); + break; + } + default: + qCWarning(lcIcc) << "Unknown para type" << uint(para->curveType); + return false; + } + return true; + } + qCWarning(lcIcc) << "Invalid TRC data type"; + return false; +} + +bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descName) +{ + const GenericTagData *tag = (const GenericTagData *)(data.constData() + tagEntry.offset); + + // Either 'desc' (ICCv2) or 'mluc' (ICCv4) + if (tag->type == quint32(Tag::desc)) { + if (tagEntry.size < sizeof(DescTagData)) + return false; + const DescTagData *desc = (const DescTagData *)(data.constData() + tagEntry.offset); + const quint32 len = desc->asciiDescriptionLength; + if (len < 1) + return false; + if (tagEntry.size - 12 < len) + return false; + if (desc->asciiDescription[len - 1] != '\0') + return false; + descName = QString::fromLatin1(desc->asciiDescription, len - 1); + return true; + } + if (tag->type != quint32(Tag::mluc)) + return false; + + if (tagEntry.size < sizeof(MlucTagData)) + return false; + const MlucTagData *mluc = (const MlucTagData *)(data.constData() + tagEntry.offset); + if (mluc->recordCount < 1) + return false; + if (mluc->recordSize < 12) + return false; + // We just use the primary record regardless of language or country. + const quint32 stringOffset = mluc->records[0].offset; + const quint32 stringSize = mluc->records[0].size; + if (tagEntry.size < stringOffset || tagEntry.size - stringOffset < stringSize ) + return false; + if ((stringSize | stringOffset) & 1) + return false; + quint32 stringLen = stringSize / 2; + const ushort *unicodeString = (const ushort *)(data.constData() + tagEntry.offset + stringOffset); + // The given length shouldn't include 0-termination, but might. + if (stringLen > 1 && unicodeString[stringLen - 1] == 0) + --stringLen; + QVarLengthArray<quint16> utf16hostendian(stringLen); + qFromBigEndian<ushort>(unicodeString, stringLen, utf16hostendian.data()); + descName = QString::fromUtf16(utf16hostendian.data(), stringLen); + return true; +} + +bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) +{ + if (data.size() < qsizetype(sizeof(ICCProfileHeader))) { + qCWarning(lcIcc) << "fromIccProfile: failed size sanity 1"; + return false; + } + const ICCProfileHeader *header = (const ICCProfileHeader *)data.constData(); + if (!isValidIccProfile(*header)) { + qCWarning(lcIcc) << "fromIccProfile: failed general sanity check"; + return false; + } + if (qsizetype(header->profileSize) > data.size()) { + qCWarning(lcIcc) << "fromIccProfile: failed size sanity 2"; + return false; + } + + // Read tag index + const TagTableEntry *tagTable = (const TagTableEntry *)(data.constData() + sizeof(ICCProfileHeader)); + const qsizetype offsetToData = sizeof(ICCProfileHeader) + header->tagCount * sizeof(TagTableEntry); + if (offsetToData > data.size()) { + qCWarning(lcIcc) << "fromIccProfile: failed index size sanity"; + return false; + } + + QHash<Tag, TagEntry> tagIndex; + for (uint i = 0; i < header->tagCount; ++i) { + // Sanity check tag sizes and offsets: + if (qsizetype(tagTable[i].offset) < offsetToData) { + qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 1"; + return false; + } + // Checked separately from (+ size) to handle overflow. + if (tagTable[i].offset > header->profileSize) { + qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2"; + return false; + } + if (tagTable[i].size < 12) { + qCWarning(lcIcc) << "fromIccProfile: failed minimal tag size sanity"; + return false; + } + if (tagTable[i].size > header->profileSize - tagTable[i].offset) { + qCWarning(lcIcc) << "fromIccProfile: failed tag offset + size sanity"; + return false; + } + if (tagTable[i].offset & 0x03) { + qCWarning(lcIcc) << "fromIccProfile: invalid tag offset alignment"; + return false; + } +// printf("'%4s' %d %d\n", (const char *)&tagTable[i].signature, +// quint32(tagTable[i].offset), +// quint32(tagTable[i].size)); + tagIndex.insert(Tag(quint32(tagTable[i].signature)), { tagTable[i].offset, tagTable[i].size }); + } + + // Check the profile is three-component matrix based (what we currently support): + if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) || + !tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) || + !tagIndex.contains(Tag::wtpt)) { + qCWarning(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based"; + return false; + } + + QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::getWritable(*colorSpace); + + // Parse XYZ tags + if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r)) + return false; + if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g)) + return false; + if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b)) + return false; + if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint)) + return false; + + colorspaceDPtr->primaries = QColorSpace::Primaries::Custom; + if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) { + qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) { + qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) { + qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65; + } + if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) { + qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb; + } + // Reset the matrix to our canonical values: + if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom) + colorspaceDPtr->setToXyzMatrix(); + + // Parse TRC tags + TagEntry rTrc; + TagEntry gTrc; + TagEntry bTrc; + if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) { + // Apple extension for parametric version of TRCs in ICCv2: + rTrc = tagIndex[Tag::aarg]; + gTrc = tagIndex[Tag::aagg]; + bTrc = tagIndex[Tag::aabg]; + } else { + rTrc = tagIndex[Tag::rTRC]; + gTrc = tagIndex[Tag::gTRC]; + bTrc = tagIndex[Tag::bTRC]; + } + + QColorTrc rCurve; + QColorTrc gCurve; + QColorTrc bCurve; + if (!parseTRC(data, rTrc, rCurve)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid rTRC"; + return false; + } + if (!parseTRC(data, gTrc, gCurve)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid gTRC"; + return false; + } + if (!parseTRC(data, bTrc, bCurve)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid bTRC"; + return false; + } + if (rCurve == gCurve && gCurve == bCurve && rCurve.m_type == QColorTrc::Type::Function) { + if (rCurve.m_fun.isLinear()) { + qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected"; + colorspaceDPtr->trc[0] = QColorTransferFunction(); + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Linear; + colorspaceDPtr->gamma = 1.0f; + } else if (rCurve.m_fun.isGamma()) { + qCDebug(lcIcc) << "fromIccProfile: Simple gamma detected"; + colorspaceDPtr->trc[0] = QColorTransferFunction::fromGamma(rCurve.m_fun.m_g); + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma; + colorspaceDPtr->gamma = rCurve.m_fun.m_g; + } else if (rCurve.m_fun.isSRgb()) { + qCDebug(lcIcc) << "fromIccProfile: sRGB gamma detected"; + colorspaceDPtr->trc[0] = QColorTransferFunction::fromSRgb(); + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::SRgb; + } else { + colorspaceDPtr->trc[0] = rCurve; + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom; + } + + colorspaceDPtr->trc[1] = colorspaceDPtr->trc[0]; + colorspaceDPtr->trc[2] = colorspaceDPtr->trc[0]; + } else { + colorspaceDPtr->trc[0] = rCurve; + colorspaceDPtr->trc[1] = gCurve; + colorspaceDPtr->trc[2] = bCurve; + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom; + } + + if (tagIndex.contains(Tag::desc)) { + if (!parseDesc(data, tagIndex[Tag::desc], colorspaceDPtr->description)) + qCWarning(lcIcc) << "fromIccProfile: Failed to parse description"; + else + qCDebug(lcIcc) << "fromIccProfile: Description" << colorspaceDPtr->description; + } + + colorspaceDPtr->identifyColorSpace(); + if (colorspaceDPtr->namedColorSpace) + qCDebug(lcIcc) << "fromIccProfile: Named colorspace detected: " << QColorSpace::NamedColorSpace(colorspaceDPtr->namedColorSpace); + + colorspaceDPtr->iccProfile = data; + + return true; +} + +} // namespace QIcc + +QT_END_NAMESPACE diff --git a/src/gui/painting/qicc_p.h b/src/gui/painting/qicc_p.h new file mode 100644 index 0000000000..2a4658b84b --- /dev/null +++ b/src/gui/painting/qicc_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 QICC_P_H +#define QICC_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 <QtCore/qbytearray.h> +#include <QtGui/qtguiglobal.h> + +QT_BEGIN_NAMESPACE + +class QColorSpace; + +namespace QIcc { + +bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace); +QByteArray toIccProfile(const QColorSpace &space); + +} + +QT_END_NAMESPACE + +#endif // QICC_P_H diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp index 8a5274bd37..0d7205b483 100644 --- a/src/gui/painting/qimagescale.cpp +++ b/src/gui/painting/qimagescale.cpp @@ -528,6 +528,7 @@ static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *des } } +#if QT_CONFIG(raster_64bit) static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest, int dw, int dh, int dow, int sow); @@ -728,6 +729,7 @@ static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, } } } +#endif static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, int dw, int dh, int dow, int sow); @@ -945,10 +947,13 @@ QImage qSmoothScaleImage(const QImage &src, int dw, int dh) return QImage(); } +#if QT_CONFIG(raster_64bit) if (src.depth() > 32) qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0), dw, dh, dw, src.bytesPerLine() / 8); - else if (src.hasAlphaChannel()) + else +#endif + if (src.hasAlphaChannel()) qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0), dw, dh, dw, src.bytesPerLine() / 4); else diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp index 24b33243da..b1f01332b6 100644 --- a/src/gui/painting/qmatrix.cpp +++ b/src/gui/painting/qmatrix.cpp @@ -248,7 +248,7 @@ QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) /*! Constructs a matrix that is a copy of the given \a matrix. */ -QMatrix::QMatrix(const QMatrix &matrix) Q_DECL_NOTHROW +QMatrix::QMatrix(const QMatrix &matrix) noexcept : _m11(matrix._m11) , _m12(matrix._m12) , _m21(matrix._m21) @@ -989,7 +989,7 @@ bool QMatrix::operator==(const QMatrix &m) const Returns the hash value for \a key, using \a seed to seed the calculation. */ -uint qHash(const QMatrix &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QMatrix &key, uint seed) noexcept { QtPrivate::QHashCombine hash; seed = hash(seed, key.m11()); @@ -1068,7 +1068,7 @@ QMatrix QMatrix::operator *(const QMatrix &m) const /*! Assigns the given \a matrix's values to this matrix. */ -QMatrix &QMatrix::operator=(const QMatrix &matrix) Q_DECL_NOTHROW +QMatrix &QMatrix::operator=(const QMatrix &matrix) noexcept { _m11 = matrix._m11; _m12 = matrix._m12; diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h index d8a4fcfb1c..a167260ade 100644 --- a/src/gui/painting/qmatrix.h +++ b/src/gui/painting/qmatrix.h @@ -64,12 +64,12 @@ public: #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove; the compiler-generated ones are fine! - QMatrix &operator=(QMatrix &&other) Q_DECL_NOTHROW // = default + QMatrix &operator=(QMatrix &&other) noexcept // = default { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QMatrix)); return *this; } - QMatrix &operator=(const QMatrix &) Q_DECL_NOTHROW; // = default - QMatrix(QMatrix &&other) Q_DECL_NOTHROW // = default + QMatrix &operator=(const QMatrix &) noexcept; // = default + QMatrix(QMatrix &&other) noexcept // = default { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QMatrix)); } - QMatrix(const QMatrix &other) Q_DECL_NOTHROW; // = default + QMatrix(const QMatrix &other) noexcept; // = default #endif void setMatrix(qreal m11, qreal m12, qreal m21, qreal m22, @@ -140,7 +140,7 @@ private: }; Q_DECLARE_TYPEINFO(QMatrix, Q_MOVABLE_TYPE); -Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QMatrix &key, uint seed = 0) Q_DECL_NOTHROW; +Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QMatrix &key, uint seed = 0) noexcept; // mathematical semantics inline QPoint operator*(const QPoint &p, const QMatrix &m) diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp index b2d02182c3..2074f98069 100644 --- a/src/gui/painting/qoutlinemapper.cpp +++ b/src/gui/painting/qoutlinemapper.cpp @@ -42,6 +42,7 @@ #include "qbezier_p.h" #include "qmath.h" #include "qpainterpath_p.h" +#include "qscopedvaluerollback.h" #include <stdlib.h> @@ -354,7 +355,7 @@ void QOutlineMapper::clipElements(const QPointF *elements, // instead of going through convenience functionallity, but since // this part of code hardly every used, it shouldn't matter. - m_in_clip_elements = true; + QScopedValueRollback<bool> in_clip_elements(m_in_clip_elements, true); QPainterPath path; @@ -397,8 +398,6 @@ void QOutlineMapper::clipElements(const QPointF *elements, convertPath(clippedPath); m_transform = oldTransform; } - - m_in_clip_elements = false; } QT_END_NAMESPACE diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h index 71999fbdee..04a68797c2 100644 --- a/src/gui/painting/qoutlinemapper_p.h +++ b/src/gui/painting/qoutlinemapper_p.h @@ -180,13 +180,13 @@ public: QT_FT_Outline *outline() { if (m_valid) return &m_outline; - return 0; + return nullptr; } QT_FT_Outline *convertPath(const QPainterPath &path); QT_FT_Outline *convertPath(const QVectorPath &path); - inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? 0 : m_element_types.data(); } + inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? nullptr : m_element_types.data(); } public: QDataBuffer<QPainterPath::ElementType> m_element_types; diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp index 0c2123f9a6..3fdd0206b7 100644 --- a/src/gui/painting/qpagedpaintdevice.cpp +++ b/src/gui/painting/qpagedpaintdevice.cpp @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE +// ### Qt 6: remove when the deprecated constructor is removed class QDummyPagedPaintDevicePrivate : public QPagedPaintDevicePrivate { bool setPageLayout(const QPageLayout &newPageLayout) override diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h index 1c37c17fa3..21e23e0eb4 100644 --- a/src/gui/painting/qpagedpaintdevice.h +++ b/src/gui/painting/qpagedpaintdevice.h @@ -213,6 +213,7 @@ public: Envelope10 = Comm10E }; + // keep in sync with QPdfEngine::PdfVersion! enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b, PdfVersion_1_6 }; // ### Qt6 Make these virtual diff --git a/src/gui/painting/qpagelayout.h b/src/gui/painting/qpagelayout.h index b41689d33b..7ee0ce7a76 100644 --- a/src/gui/painting/qpagelayout.h +++ b/src/gui/painting/qpagelayout.h @@ -82,13 +82,11 @@ public: const QMarginsF &margins, Unit units = Point, const QMarginsF &minMargins = QMarginsF(0, 0, 0, 0)); QPageLayout(const QPageLayout &other); -#ifdef Q_COMPILER_RVALUE_REFS - QPageLayout &operator=(QPageLayout &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QPageLayout &operator=(QPageLayout &&other) noexcept { swap(other); return *this; } QPageLayout &operator=(const QPageLayout &other); ~QPageLayout(); - void swap(QPageLayout &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QPageLayout &other) noexcept { qSwap(d, other.d); } friend Q_GUI_EXPORT bool operator==(const QPageLayout &lhs, const QPageLayout &rhs); bool isEquivalentTo(const QPageLayout &other) const; diff --git a/src/gui/painting/qpagesize.cpp b/src/gui/painting/qpagesize.cpp index a1c9f6e417..c98ca8a1fb 100644 --- a/src/gui/painting/qpagesize.cpp +++ b/src/gui/painting/qpagesize.cpp @@ -222,8 +222,6 @@ static const int qt_windowsConversion[][2] = { {DMPAPER_PENV_10_ROTATED, DMPAPER_PENV_10} // Is = DMPAPER_LAST, use as loop terminator }; -static const int windowsConversionCount = int(sizeof(qt_windowsConversion) / sizeof(qt_windowsConversion[0])); - // Standard sizes data struct StandardPageSize { QPageSize::PageSizeId id; @@ -423,9 +421,9 @@ static QPageSize::PageSizeId qt_idForWindowsID(int windowsId, QSize *match = 0) if (windowsId <= DMPAPER_NONE || windowsId > DMPAPER_LAST) return QPageSize::Custom; // Check if one of the unsupported values, convert to valid value if is - for (int i = 0; i < windowsConversionCount; ++i) { - if (qt_windowsConversion[i][0] == windowsId) { - windowsId = qt_windowsConversion[i][1]; + for (const auto &it : qt_windowsConversion) { + if (it[0] == windowsId) { + windowsId = it[1]; break; } } diff --git a/src/gui/painting/qpagesize.h b/src/gui/painting/qpagesize.h index 82054824b4..133274760f 100644 --- a/src/gui/painting/qpagesize.h +++ b/src/gui/painting/qpagesize.h @@ -236,14 +236,12 @@ public: const QString &name = QString(), SizeMatchPolicy matchPolicy = FuzzyMatch); QPageSize(const QPageSize &other); -#ifdef Q_COMPILER_RVALUE_REFS - QPageSize &operator=(QPageSize &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QPageSize &operator=(QPageSize &&other) noexcept { swap(other); return *this; } QPageSize &operator=(const QPageSize &other); ~QPageSize(); - void swap(QPageSize &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QPageSize &other) noexcept { qSwap(d, other.d); } friend Q_GUI_EXPORT bool operator==(const QPageSize &lhs, const QPageSize &rhs); bool isEquivalentTo(const QPageSize &other) const; diff --git a/src/gui/painting/qpaintdevice.cpp b/src/gui/painting/qpaintdevice.cpp index faaa316085..0ddfba6ee9 100644 --- a/src/gui/painting/qpaintdevice.cpp +++ b/src/gui/painting/qpaintdevice.cpp @@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE -QPaintDevice::QPaintDevice() Q_DECL_NOEXCEPT +QPaintDevice::QPaintDevice() noexcept { reserved = 0; painters = 0; diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h index 9458b4ba9a..5f8dad205d 100644 --- a/src/gui/painting/qpaintdevice.h +++ b/src/gui/painting/qpaintdevice.h @@ -90,7 +90,7 @@ public: static inline qreal devicePixelRatioFScale() { return 0x10000; } protected: - QPaintDevice() Q_DECL_NOEXCEPT; + QPaintDevice() noexcept; virtual int metric(PaintDeviceMetric metric) const; virtual void initPainter(QPainter *painter) const; virtual QPaintDevice *redirected(QPoint *offset) const; diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h index 8ac3fcff5c..40b9474165 100644 --- a/src/gui/painting/qpaintengine_p.h +++ b/src/gui/painting/qpaintengine_p.h @@ -65,7 +65,7 @@ class Q_GUI_EXPORT QPaintEnginePrivate { Q_DECLARE_PUBLIC(QPaintEngine) public: - QPaintEnginePrivate() : pdev(0), q_ptr(0), currentClipDevice(0), hasSystemTransform(0), + QPaintEnginePrivate() : pdev(nullptr), q_ptr(nullptr), currentClipDevice(nullptr), hasSystemTransform(0), hasSystemViewport(0) {} virtual ~QPaintEnginePrivate(); @@ -138,8 +138,8 @@ public: static QPaintEnginePrivate *get(QPaintEngine *paintEngine) { return paintEngine->d_func(); } - virtual QPaintEngine *aggregateEngine() { return 0; } - virtual Qt::HANDLE nativeHandle() { return 0; } + virtual QPaintEngine *aggregateEngine() { return nullptr; } + virtual Qt::HANDLE nativeHandle() { return nullptr; } }; QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 5bdf3299c9..40c822076b 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -555,35 +555,6 @@ bool QRasterPaintEngine::end() /*! \internal */ -void QRasterPaintEngine::releaseBuffer() -{ - Q_D(QRasterPaintEngine); - d->rasterBuffer.reset(new QRasterBuffer); -} - -/*! - \internal -*/ -QSize QRasterPaintEngine::size() const -{ - Q_D(const QRasterPaintEngine); - return QSize(d->rasterBuffer->width(), d->rasterBuffer->height()); -} - -/*! - \internal -*/ -#ifndef QT_NO_DEBUG -void QRasterPaintEngine::saveBuffer(const QString &s) const -{ - Q_D(const QRasterPaintEngine); - d->rasterBuffer->bufferImage().save(s, "PNG"); -} -#endif - -/*! - \internal -*/ void QRasterPaintEngine::updateMatrix(const QTransform &matrix) { QRasterPaintEngineState *s = state(); @@ -871,8 +842,8 @@ void QRasterPaintEngine::updateRasterState() const QPainter::CompositionMode mode = s->composition_mode; s->flags.fast_text = (s->penData.type == QSpanData::Solid) && s->intOpacity == 256 - && (mode == QPainter::CompositionMode_Source - || (mode == QPainter::CompositionMode_SourceOver + && (mode == QPainter::CompositionMode_SourceOver + || (mode == QPainter::CompositionMode_Source && s->penData.solidColor.isOpaque())); } @@ -927,13 +898,20 @@ void QRasterPaintEngine::renderHintsChanged() QRasterPaintEngineState *s = state(); #ifdef QT_DEBUG_DRAW - qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints; + qDebug() << "QRasterPaintEngine::renderHintsChanged()" << Qt::hex << s->renderHints; #endif bool was_aa = s->flags.antialiased; bool was_bilinear = s->flags.bilinear; - s->flags.antialiased = bool(s->renderHints & (QPainter::Antialiasing | QPainter::HighQualityAntialiasing)); + s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing); +#if QT_DEPRECATED_SINCE(5, 14) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + if (s->renderHints & QPainter::HighQualityAntialiasing) + s->flags.antialiased = true; +QT_WARNING_POP +#endif s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform); s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting); @@ -1341,7 +1319,7 @@ void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) QPaintEngineEx::clip(rect, op); return; - } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) { + } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(QRectF(rect)).toRect(), op)) { QPaintEngineEx::clip(rect, op); return; } @@ -1774,7 +1752,7 @@ void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) QRectF rf = path.controlPointRect(); qDebug() << "QRasterPaintEngine::fill(): " << "size=" << path.elementCount() - << ", hints=" << hex << path.hints() + << ", hints=" << Qt::hex << path.hints() << rf << brush; #endif @@ -2239,7 +2217,7 @@ void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img) const QClipData *clip = d->clip(); QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy()); - if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img)) { + if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, img.rect())) { if (!clip) { d->blitImage(pt, img, d->deviceRect); return; @@ -2310,7 +2288,12 @@ namespace { return NoRotation; } - inline bool isPixelAligned(const QRectF &rect) { + inline bool isPixelAligned(const QPointF &pt) + { + return QPointF(pt.toPoint()) == pt; + } + inline bool isPixelAligned(const QRectF &rect) + { return QRectF(rect.toRect()) == rect; } } @@ -2378,17 +2361,12 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe const QClipData *clip = d->clip(); - if (s->matrix.type() > QTransform::TxTranslate + if (s->matrix.type() == QTransform::TxRotate && !stretch_sr && (!clip || clip->hasRectClip) && s->intOpacity == 256 && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver - || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source) - && d->rasterBuffer->format == img.format() - && (d->rasterBuffer->format == QImage::Format_RGB16 - || d->rasterBuffer->format == QImage::Format_RGB32 - || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied - && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))) + || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)) { RotationType rotationType = qRotationType(s->matrix); const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp; @@ -2396,9 +2374,7 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) { QRectF transformedTargetRect = s->matrix.mapRect(r); - if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing)) - || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr))) - { + if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, transformedTargetRect.topRight(), sr)) { QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect); if (clippedTransformedTargetRect.isNull()) return; @@ -2532,8 +2508,8 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe fillPath(path, &d->image_filler_xform); s->matrix = m; } else { - if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img)) { - QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy()); + QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy()); + if (d->canUseImageBlitting(d->rasterBuffer->compositionMode, img, pt, sr)) { if (!clip) { d->blitImage(pt, img, d->deviceRect, sr.toRect()); return; @@ -2544,7 +2520,6 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe } else if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) { SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()]; if (func) { - QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy()); if (!clip) { d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect()); return; @@ -3779,12 +3754,22 @@ bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMod && !image.hasAlphaChannel())); } -bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image) const +bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image, const QPointF &pt, const QRectF &sr) const { Q_Q(const QRasterPaintEngine); + + if (!(mode == QPainter::CompositionMode_Source + || (mode == QPainter::CompositionMode_SourceOver + && !image.hasAlphaChannel()))) + return false; + const QRasterPaintEngineState *s = q->state(); + Q_ASSERT(s->matrix.type() <= QTransform::TxTranslate || s->matrix.type() == QTransform::TxRotate); - if (!s->flags.fast_images || s->intOpacity != 256 || qt_depthForFormat(rasterBuffer->format) < 8) + if (s->intOpacity != 256 + || image.depth() < 8 + || ((s->renderHints & (QPainter::SmoothPixmapTransform | QPainter::Antialiasing)) + && (!isPixelAligned(pt) || !isPixelAligned(sr)))) return false; QImage::Format dFormat = rasterBuffer->format; @@ -3792,18 +3777,13 @@ bool QRasterPaintEnginePrivate::canUseImageBlitting(QPainter::CompositionMode mo // Formats must match or source format must be a subset of destination format if (dFormat != sFormat && image.pixelFormat().alphaUsage() == QPixelFormat::IgnoresAlpha) { if ((sFormat == QImage::Format_RGB32 && dFormat == QImage::Format_ARGB32) - || (sFormat == QImage::Format_RGBX8888 && dFormat == QImage::Format_RGBA8888)) + || (sFormat == QImage::Format_RGBX8888 && dFormat == QImage::Format_RGBA8888) + || (sFormat == QImage::Format_RGBX64 && dFormat == QImage::Format_RGBA64)) sFormat = dFormat; else sFormat = qt_maybeAlphaVersionWithSameDepth(sFormat); // this returns premul formats } - if (dFormat != sFormat) - return false; - - return s->matrix.type() <= QTransform::TxTranslate - && (mode == QPainter::CompositionMode_Source - || (mode == QPainter::CompositionMode_SourceOver - && !image.hasAlphaChannel())); + return (dFormat == sFormat); } QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color) @@ -3860,11 +3840,6 @@ QImage::Format QRasterBuffer::prepare(QImage *image) return format; } -void QRasterBuffer::resetBuffer(int val) -{ - memset(m_buffer, val, m_height*bytes_per_line); -} - QClipData::QClipData(int height) { clipSpanHeight = height; @@ -4291,55 +4266,13 @@ static void qt_span_clip(int count, const QSpan *spans, void *userData) } } -#ifndef QT_NO_DEBUG -QImage QRasterBuffer::bufferImage() const -{ - QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied); - - for (int y = 0; y < m_height; ++y) { - uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y); - - for (int x=0; x<m_width; ++x) { - uint argb = span[x]; - image.setPixel(x, y, argb); - } - } - return image; -} -#endif - - -void QRasterBuffer::flushToARGBImage(QImage *target) const -{ - int w = qMin(m_width, target->width()); - int h = qMin(m_height, target->height()); - - for (int y=0; y<h; ++y) { - uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y); - QRgb *dest = (QRgb *) target->scanLine(y); - for (int x=0; x<w; ++x) { - QRgb pixel = sourceLine[x]; - int alpha = qAlpha(pixel); - if (!alpha) { - dest[x] = 0; - } else { - dest[x] = (alpha << 24) - | ((255*qRed(pixel)/alpha) << 16) - | ((255*qGreen(pixel)/alpha) << 8) - | ((255*qBlue(pixel)/alpha) << 0); - } - } - } -} - - class QGradientCache { public: struct CacheInfo : QSpanData::Pinnable { inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) : - stops(qMove(s)), opacity(op), interpolationMode(mode) {} + stops(std::move(s)), opacity(op), interpolationMode(mode) {} QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE]; QRgb buffer32[GRADIENT_STOPTABLE_SIZE]; QGradientStops stops; @@ -4382,7 +4315,7 @@ protected: QSharedPointer<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) { if (cache.size() == maxCacheSize()) { // may remove more than 1, but OK - cache.erase(cache.begin() + QRandomGenerator::global()->bounded(maxCacheSize())); + cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize()))); } auto cache_entry = QSharedPointer<CacheInfo>::create(gradient.stops(), opacity, gradient.interpolationMode()); generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity); @@ -4626,7 +4559,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; +#if QT_CONFIG(raster_64bit) gradient.colorTable64 = cacheInfo->buffer64; +#endif cachedGradient = std::move(cacheInfo); gradient.spread = g->spread(); @@ -4648,7 +4583,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; +#if QT_CONFIG(raster_64bit) gradient.colorTable64 = cacheInfo->buffer64; +#endif cachedGradient = std::move(cacheInfo); gradient.spread = g->spread(); @@ -4674,7 +4611,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; +#if QT_CONFIG(raster_64bit) gradient.colorTable64 = cacheInfo->buffer64; +#endif cachedGradient = std::move(cacheInfo); gradient.spread = QGradient::RepeatSpread; diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index 5d527207a4..089aadc3f7 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -208,15 +208,6 @@ public: ClipType clipType() const; QRect clipBoundingRect() const; - void releaseBuffer(); - - QSize size() const; - -#ifndef QT_NO_DEBUG - void saveBuffer(const QString &s) const; -#endif - - #ifdef Q_OS_WIN void setDC(HDC hdc); HDC getDC() const; @@ -315,7 +306,7 @@ public: void recalculateFastImages(); bool canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const; - bool canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image) const; + bool canUseImageBlitting(QPainter::CompositionMode mode, const QImage &image, const QPointF &pt, const QRectF &sr) const; QPaintDevice *device; QScopedPointer<QOutlineMapper> outlineMapper; @@ -435,27 +426,16 @@ inline void QClipData::appendSpans(const QSpan *s, int num) class QRasterBuffer { public: - QRasterBuffer() : m_width(0), m_height(0), m_buffer(0) { init(); } + QRasterBuffer() : m_width(0), m_height(0), m_buffer(nullptr) { init(); } ~QRasterBuffer(); void init(); QImage::Format prepare(QImage *image); - QImage::Format prepare(QPixmap *pix); - void prepare(int w, int h); - void prepareBuffer(int w, int h); - - void resetBuffer(int val=0); uchar *scanLine(int y) { Q_ASSERT(y>=0); Q_ASSERT(y<m_height); return m_buffer + y * qsizetype(bytes_per_line); } -#ifndef QT_NO_DEBUG - QImage bufferImage() const; -#endif - - void flushToARGBImage(QImage *image) const; - int width() const { return m_width; } int height() const { return m_height; } int bytesPerLine() const { return bytes_per_line; } diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 22d3fb3001..8314e8bc8a 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -140,7 +140,7 @@ QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path) QDebugStateSaver saver(s); QRectF rf = path.controlPointRect(); s << "QVectorPath(size:" << path.elementCount() - << " hints:" << hex << path.hints() + << " hints:" << Qt::hex << path.hints() << rf << ')'; return s; } diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index f9e9563f10..3ce54c20be 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -283,7 +283,7 @@ bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev) Q_ASSERT(q->d_ptr->state); // Now initialize the painter with correct widget properties. - q->initFrom(pdev); + q->d_ptr->initFrom(pdev); QPoint offset; pdev->redirected(&offset); offset += q->d_ptr->engine->coordinateOffset(); @@ -1560,22 +1560,28 @@ void QPainter::initFrom(const QPaintDevice *device) { Q_ASSERT_X(device, "QPainter::initFrom(const QPaintDevice *device)", "QPaintDevice cannot be 0"); Q_D(QPainter); - if (!d->engine) { + d->initFrom(device); +} +#endif + +void QPainterPrivate::initFrom(const QPaintDevice *device) +{ + if (!engine) { qWarning("QPainter::initFrom: Painter not active, aborted"); return; } - device->initPainter(this); + Q_Q(QPainter); + device->initPainter(q); - if (d->extended) { - d->extended->penChanged(); - } else if (d->engine) { - d->engine->setDirty(QPaintEngine::DirtyPen); - d->engine->setDirty(QPaintEngine::DirtyBrush); - d->engine->setDirty(QPaintEngine::DirtyFont); + if (extended) { + extended->penChanged(); + } else if (engine) { + engine->setDirty(QPaintEngine::DirtyPen); + engine->setDirty(QPaintEngine::DirtyBrush); + engine->setDirty(QPaintEngine::DirtyFont); } } -#endif /*! Saves the current painter state (pushes the state onto a stack). A @@ -1843,7 +1849,7 @@ bool QPainter::begin(QPaintDevice *pd) // Copy painter properties from original paint device, // required for QPixmap::grabWidget() if (d->original_device->devType() == QInternal::Widget) { - initFrom(d->original_device); + d->initFrom(d->original_device); } else { d->state->layoutDirection = Qt::LayoutDirectionAuto; // make sure we have a font compatible with the paintdevice diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 843f24e3e1..3394da63c7 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -89,8 +89,10 @@ public: Antialiasing = 0x01, TextAntialiasing = 0x02, SmoothPixmapTransform = 0x04, - HighQualityAntialiasing = 0x08, - NonCosmeticDefaultPen = 0x10, +#if QT_DEPRECATED_SINCE(5, 14) + HighQualityAntialiasing Q_DECL_ENUMERATOR_DEPRECATED_X("Use Antialiasing instead") = 0x08, + NonCosmeticDefaultPen Q_DECL_ENUMERATOR_DEPRECATED_X("Default pen is non-cosmetic now") = 0x10, +#endif Qt4CompatiblePainting = 0x20, LosslessImageRendering = 0x40, }; diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index 930180e9fa..29d4880eb9 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -54,6 +54,8 @@ #include <QtCore/qvarlengtharray.h> #include <QtGui/private/qtguiglobal_p.h> #include "QtGui/qbrush.h" +#include "QtGui/qcolorspace.h" +#include "QtGui/qcolortransform.h" #include "QtGui/qfont.h" #include "QtGui/qpen.h" #include "QtGui/qregion.h" @@ -191,9 +193,9 @@ class QPainterPrivate Q_DECLARE_PUBLIC(QPainter) public: QPainterPrivate(QPainter *painter) - : q_ptr(painter), d_ptrs(0), state(0), dummyState(0), txinv(0), inDestructor(false), d_ptrs_size(0), - refcount(1), device(0), original_device(0), helper_device(0), engine(0), emulationEngine(0), - extended(0) + : q_ptr(painter), d_ptrs(nullptr), state(nullptr), dummyState(nullptr), txinv(0), inDestructor(false), d_ptrs_size(0), + refcount(1), device(nullptr), original_device(nullptr), helper_device(nullptr), engine(nullptr), emulationEngine(nullptr), + extended(nullptr) { } @@ -254,6 +256,7 @@ public: QTransform hidpiScaleTransform() const; static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev); void detachPainterPrivate(QPainter *q); + void initFrom(const QPaintDevice *device); QPaintDevice *device; QPaintDevice *original_device; diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 8516d73537..1fb37ece56 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -93,7 +93,7 @@ struct QPainterPathPrivateDeleter { static inline void cleanup(QPainterPathPrivate *d) { - // note - we must up-cast to QPainterPathData since QPainterPathPrivate + // note - we must downcast to QPainterPathData since QPainterPathPrivate // has a non-virtual destructor! if (d && !d->ref.deref()) delete static_cast<QPainterPathData *>(d); @@ -545,7 +545,7 @@ void QPainterPath::setElementPositionAt(int i, qreal x, qreal y) /*! Constructs an empty QPainterPath object. */ -QPainterPath::QPainterPath() Q_DECL_NOEXCEPT +QPainterPath::QPainterPath() noexcept : d_ptr(0) { } @@ -578,7 +578,7 @@ QPainterPath::QPainterPath(const QPointF &startPoint) void QPainterPath::detach() { - if (d_ptr->ref.load() != 1) + if (d_ptr->ref.loadRelaxed() != 1) detach_helper(); setDirty(true); } @@ -1855,10 +1855,9 @@ static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, } // split curve and try again... - QBezier first_half, second_half; - bezier.split(&first_half, &second_half); - qt_painterpath_isect_curve(first_half, pt, winding, depth + 1); - qt_painterpath_isect_curve(second_half, pt, winding, depth + 1); + const auto halves = bezier.split(); + qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1); + qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1); } } @@ -2013,10 +2012,9 @@ static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) return true; - QBezier first_half, second_half; - bezier.split(&first_half, &second_half); - if (qt_isect_curve_horizontal(first_half, y, x1, x2, depth + 1) - || qt_isect_curve_horizontal(second_half, y, x1, x2, depth + 1)) + const auto halves = bezier.split(); + if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1) + || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1)) return true; } return false; @@ -2032,10 +2030,9 @@ static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qr if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) return true; - QBezier first_half, second_half; - bezier.split(&first_half, &second_half); - if (qt_isect_curve_vertical(first_half, x, y1, y2, depth + 1) - || qt_isect_curve_vertical(second_half, x, y1, y2, depth + 1)) + const auto halves = bezier.split(); + if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1) + || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1)) return true; } return false; @@ -3520,8 +3517,7 @@ void QPainterPath::setDirty(bool dirty) { d_func()->dirtyBounds = dirty; d_func()->dirtyControlBounds = dirty; - delete d_func()->pathConverter; - d_func()->pathConverter = 0; + d_func()->pathConverter.reset(); d_func()->convex = false; } @@ -3597,10 +3593,10 @@ void QPainterPath::computeControlPointRect() const #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug s, const QPainterPath &p) { - s.nospace() << "QPainterPath: Element count=" << p.elementCount() << endl; + s.nospace() << "QPainterPath: Element count=" << p.elementCount() << Qt::endl; const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"}; for (int i=0; i<p.elementCount(); ++i) { - s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << endl; + s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << Qt::endl; } return s; diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index a69a192767..ed5be667b7 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -88,17 +88,15 @@ public: inline bool operator!=(const Element &e) const { return !operator==(e); } }; - QPainterPath() Q_DECL_NOEXCEPT; + QPainterPath() noexcept; explicit QPainterPath(const QPointF &startPoint); QPainterPath(const QPainterPath &other); QPainterPath &operator=(const QPainterPath &other); -#ifdef Q_COMPILER_RVALUE_REFS - inline QPainterPath &operator=(QPainterPath &&other) Q_DECL_NOEXCEPT + inline QPainterPath &operator=(QPainterPath &&other) noexcept { qSwap(d_ptr, other.d_ptr); return *this; } -#endif ~QPainterPath(); - inline void swap(QPainterPath &other) Q_DECL_NOEXCEPT { d_ptr.swap(other.d_ptr); } + inline void swap(QPainterPath &other) noexcept { d_ptr.swap(other.d_ptr); } void clear(); void reserve(int size); diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h index 567f155210..a420e0b3d9 100644 --- a/src/gui/painting/qpainterpath_p.h +++ b/src/gui/painting/qpainterpath_p.h @@ -62,8 +62,11 @@ #include <private/qvectorpath_p.h> #include <private/qstroker_p.h> +#include <memory> + QT_BEGIN_NAMESPACE +// ### Qt 6: merge with QPainterPathData class QPainterPathPrivate { public: @@ -80,7 +83,19 @@ public: friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &); #endif - QPainterPathPrivate() : ref(1) {} + QPainterPathPrivate() noexcept + : ref(1) + { + } + + QPainterPathPrivate(const QPainterPathPrivate &other) noexcept + : ref(1), + elements(other.elements) + { + } + + QPainterPathPrivate &operator=(const QPainterPathPrivate &) = delete; + ~QPainterPathPrivate() = default; private: QAtomicInt ref; @@ -166,30 +181,30 @@ public: QPainterPathData() : cStart(0), fillRule(Qt::OddEvenFill), + require_moveTo(false), dirtyBounds(false), dirtyControlBounds(false), - pathConverter(0) + convex(false), + pathConverter(nullptr) { - require_moveTo = false; - convex = false; } QPainterPathData(const QPainterPathData &other) : - QPainterPathPrivate(), cStart(other.cStart), fillRule(other.fillRule), + QPainterPathPrivate(other), + cStart(other.cStart), + fillRule(other.fillRule), bounds(other.bounds), controlBounds(other.controlBounds), + require_moveTo(false), dirtyBounds(other.dirtyBounds), dirtyControlBounds(other.dirtyControlBounds), convex(other.convex), - pathConverter(0) + pathConverter(nullptr) { - require_moveTo = false; - elements = other.elements; } - ~QPainterPathData() { - delete pathConverter; - } + QPainterPathData &operator=(const QPainterPathData &) = delete; + ~QPainterPathData() = default; inline bool isClosed() const; inline void close(); @@ -198,7 +213,7 @@ public: const QVectorPath &vectorPath() { if (!pathConverter) - pathConverter = new QVectorPathConverter(elements, fillRule, convex); + pathConverter.reset(new QVectorPathConverter(elements, fillRule, convex)); return pathConverter->path; } @@ -213,7 +228,7 @@ public: uint dirtyControlBounds : 1; uint convex : 1; - QVectorPathConverter *pathConverter; + std::unique_ptr<QVectorPathConverter> pathConverter; }; @@ -266,7 +281,7 @@ inline bool QPainterPathData::isClosed() const inline void QPainterPathData::close() { - Q_ASSERT(ref.load() == 1); + Q_ASSERT(ref.loadRelaxed() == 1); require_moveTo = true; const QPainterPath::Element &first = elements.at(cStart); QPainterPath::Element &last = elements.last(); @@ -293,7 +308,7 @@ inline void QPainterPathData::maybeMoveTo() inline void QPainterPathData::clear() { - Q_ASSERT(ref.load() == 1); + Q_ASSERT(ref.loadRelaxed() == 1); elements.clear(); @@ -307,8 +322,7 @@ inline void QPainterPathData::clear() dirtyControlBounds = false; convex = false; - delete pathConverter; - pathConverter = nullptr; + pathConverter.reset(); } #define KAPPA qreal(0.5522847498) diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h index c25a479807..9444a87b71 100644 --- a/src/gui/painting/qpathclipper_p.h +++ b/src/gui/painting/qpathclipper_p.h @@ -82,7 +82,7 @@ public: bool intersect(); bool contains(); - static bool pathToRect(const QPainterPath &path, QRectF *rect = 0); + static bool pathToRect(const QPainterPath &path, QRectF *rect = nullptr); static QPainterPath intersect(const QPainterPath &path, const QRectF &rect); private: @@ -394,7 +394,7 @@ inline const QPathSegments::Intersection *QPathSegments::intersectionAt(int inde { const int intersection = m_segments.at(index).intersection; if (intersection < 0) - return 0; + return nullptr; else return &m_intersections.at(intersection); } @@ -428,12 +428,12 @@ inline int QWingedEdge::edgeCount() const inline QPathEdge *QWingedEdge::edge(int edge) { - return edge < 0 ? 0 : &m_edges.at(edge); + return edge < 0 ? nullptr : &m_edges.at(edge); } inline const QPathEdge *QWingedEdge::edge(int edge) const { - return edge < 0 ? 0 : &m_edges.at(edge); + return edge < 0 ? nullptr : &m_edges.at(edge); } inline int QWingedEdge::vertexCount() const @@ -449,12 +449,12 @@ inline int QWingedEdge::addVertex(const QPointF &p) inline QPathVertex *QWingedEdge::vertex(int vertex) { - return vertex < 0 ? 0 : &m_vertices.at(vertex); + return vertex < 0 ? nullptr : &m_vertices.at(vertex); } inline const QPathVertex *QWingedEdge::vertex(int vertex) const { - return vertex < 0 ? 0 : &m_vertices.at(vertex); + return vertex < 0 ? nullptr : &m_vertices.at(vertex); } inline QPathEdge::Traversal QWingedEdge::flip(QPathEdge::Traversal traversal) diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 6bdc82a8e9..f560e1f0f0 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -1570,12 +1570,14 @@ void QPdfEnginePrivate::writeHeader() { addXrefEntry(0,false); - static const QHash<QPdfEngine::PdfVersion, const char *> mapping { - {QPdfEngine::Version_1_4, "1.4"}, - {QPdfEngine::Version_A1b, "1.4"}, - {QPdfEngine::Version_1_6, "1.6"} + // Keep in sync with QPdfEngine::PdfVersion! + static const char mapping[][4] = { + "1.4", // Version_1_4 + "1.4", // Version_A1b + "1.6", // Version_1_6 }; - const char *verStr = mapping.value(pdfVersion, "1.4"); + static const size_t numMappings = sizeof mapping / sizeof *mapping; + const char *verStr = mapping[size_t(pdfVersion) < numMappings ? pdfVersion : 0]; xprintf("%%PDF-%s\n", verStr); xprintf("%%\303\242\303\243\n"); @@ -1671,19 +1673,19 @@ int QPdfEnginePrivate::writeXmpMetaData() const QDateTime now = QDateTime::currentDateTime(); const QDate date = now.date(); const QTime time = now.time(); - - QString timeStr; - timeStr.sprintf("%d-%02d-%02dT%02d:%02d:%02d", date.year(), date.month(), date.day(), - time.hour(), time.minute(), time.second()); + const QString timeStr = + QString::asprintf("%d-%02d-%02dT%02d:%02d:%02d", + date.year(), date.month(), date.day(), + time.hour(), time.minute(), time.second()); const int offset = now.offsetFromUtc(); const int hours = (offset / 60) / 60; const int mins = (offset / 60) % 60; QString tzStr; if (offset < 0) - tzStr.sprintf("-%02d:%02d", -hours, -mins); + tzStr = QString::asprintf("-%02d:%02d", -hours, -mins); else if (offset > 0) - tzStr.sprintf("+%02d:%02d", hours , mins); + tzStr = QString::asprintf("+%02d:%02d", hours , mins); else tzStr = QLatin1String("Z"); diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index e337c61f64..89e549614a 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -168,6 +168,7 @@ class Q_GUI_EXPORT QPdfEngine : public QPaintEngine Q_DECLARE_PRIVATE(QPdfEngine) friend class QPdfWriter; public: + // keep in sync with QPagedPaintDevice::PdfVersion and QPdfEnginePrivate::writeHeader()::mapping! enum PdfVersion { Version_1_4, diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp index 258939a763..bf7e2d3dca 100644 --- a/src/gui/painting/qpdfwriter.cpp +++ b/src/gui/painting/qpdfwriter.cpp @@ -170,17 +170,11 @@ void QPdfWriter::setPdfVersion(PdfVersion version) { Q_D(QPdfWriter); - static const QHash<QPdfWriter::PdfVersion, QPdfEngine::PdfVersion> engineMapping { - {QPdfWriter::PdfVersion_1_4, QPdfEngine::Version_1_4}, - {QPdfWriter::PdfVersion_A1b, QPdfEngine::Version_A1b}, - {QPdfWriter::PdfVersion_1_6, QPdfEngine::Version_1_6} - }; - if (d->pdfVersion == version) return; d->pdfVersion = version; - d->engine->setPdfVersion(engineMapping.value(version, QPdfEngine::Version_1_4)); + d->engine->setPdfVersion(static_cast<QPdfEngine::PdfVersion>(static_cast<int>(version))); } /*! @@ -379,6 +373,9 @@ int QPdfWriter::resolution() const */ #endif +#if QT_DEPRECATED_SINCE(5, 14) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED /*! \reimp @@ -404,6 +401,8 @@ void QPdfWriter::setPageSizeMM(const QSizeF &size) { setPageSize(QPageSize(size, QPageSize::Millimeter)); } +QT_WARNING_POP +#endif /*! \internal @@ -427,6 +426,9 @@ bool QPdfWriter::newPage() } +#if QT_DEPRECATED_SINCE(5, 14) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED /*! \reimp @@ -438,6 +440,8 @@ void QPdfWriter::setMargins(const Margins &m) { setPageMargins(QMarginsF(m.left, m.top, m.right, m.bottom), QPageLayout::Millimeter); } +QT_WARNING_POP +#endif QT_END_NAMESPACE diff --git a/src/gui/painting/qpdfwriter.h b/src/gui/painting/qpdfwriter.h index b260805b2b..668081e008 100644 --- a/src/gui/painting/qpdfwriter.h +++ b/src/gui/painting/qpdfwriter.h @@ -86,10 +86,14 @@ public: using QPagedPaintDevice::setPageSize; #endif +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED_X("Use setPageSize(QPageSize(id)) instead") void setPageSize(PageSize size) override; + QT_DEPRECATED_X("Use setPageSize(QPageSize(size, QPageSize::Millimeter)) instead") void setPageSizeMM(const QSizeF &size) override; - + QT_DEPRECATED_X("Use setPageMargins(QMarginsF(l, t, r, b), QPageLayout::Millimeter) instead") void setMargins(const Margins &m) override; +#endif protected: QPaintEngine *paintEngine() const override; diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index 778c770b82..dc6e3e04d0 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -322,7 +322,7 @@ QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c, Constructs a pen that is a copy of the given \a pen. */ -QPen::QPen(const QPen &p) Q_DECL_NOTHROW +QPen::QPen(const QPen &p) noexcept { d = p.d; if (d) @@ -363,13 +363,13 @@ QPen::~QPen() void QPen::detach() { - if (d->ref.load() == 1) + if (d->ref.loadRelaxed() == 1) return; QPenData *x = new QPenData(*static_cast<QPenData *>(d)); if (!d->ref.deref()) delete d; - x->ref.store(1); + x->ref.storeRelaxed(1); d = x; } @@ -381,7 +381,7 @@ void QPen::detach() this pen. */ -QPen &QPen::operator=(const QPen &p) Q_DECL_NOTHROW +QPen &QPen::operator=(const QPen &p) noexcept { QPen(p).swap(*this); return *this; @@ -885,7 +885,7 @@ bool QPen::operator==(const QPen &p) const bool QPen::isDetached() { - return d->ref.load() == 1; + return d->ref.loadRelaxed() == 1; } diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h index 03abfb3d7d..10b11d1d85 100644 --- a/src/gui/painting/qpen.h +++ b/src/gui/painting/qpen.h @@ -65,18 +65,16 @@ public: QPen(const QColor &color); QPen(const QBrush &brush, qreal width, Qt::PenStyle s = Qt::SolidLine, Qt::PenCapStyle c = Qt::SquareCap, Qt::PenJoinStyle j = Qt::BevelJoin); - QPen(const QPen &pen) Q_DECL_NOTHROW; + QPen(const QPen &pen) noexcept; ~QPen(); - QPen &operator=(const QPen &pen) Q_DECL_NOTHROW; -#ifdef Q_COMPILER_RVALUE_REFS - QPen(QPen &&other) Q_DECL_NOTHROW + QPen &operator=(const QPen &pen) noexcept; + QPen(QPen &&other) noexcept : d(other.d) { other.d = nullptr; } - QPen &operator=(QPen &&other) Q_DECL_NOTHROW + QPen &operator=(QPen &&other) noexcept { qSwap(d, other.d); return *this; } -#endif - void swap(QPen &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QPen &other) noexcept { qSwap(d, other.d); } Qt::PenStyle style() const; void setStyle(Qt::PenStyle); diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index c71d82546a..45e90bd99b 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -338,7 +338,16 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i } } - if (!d_ptr->context->makeCurrent(window)) { + bool current = d_ptr->context->makeCurrent(window); + + if (!current && !d_ptr->context->isValid()) { + delete d_ptr->blitter; + d_ptr->blitter = nullptr; + d_ptr->textureId = 0; + current = d_ptr->context->create() && d_ptr->context->makeCurrent(window); + } + + if (!current) { qCWarning(lcQpaBackingStore, "composeAndFlush: makeCurrent() failed"); return; } @@ -446,14 +455,22 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i d_ptr->blitter->setRedBlueSwizzle(false); } - // There is no way to tell if the OpenGL-rendered content is premultiplied or not. - // For compatibility, assume that it is not, and use normal alpha blend always. - if (d_ptr->premultiplied) - funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); - // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. + bool blendIsPremultiplied = d_ptr->premultiplied; for (int i = 0; i < textures->count(); ++i) { - if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) + const QPlatformTextureList::Flags flags = textures->flags(i); + if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending)) { + if (!blendIsPremultiplied) { + funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + blendIsPremultiplied = true; + } + } else { + if (blendIsPremultiplied) { + funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + blendIsPremultiplied = false; + } + } + if (flags.testFlag(QPlatformTextureList::StacksOnTop)) blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb); } diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index de5ba964dc..4f08b0092f 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -81,11 +81,12 @@ class Q_GUI_EXPORT QPlatformTextureList : public QObject public: enum Flag { StacksOnTop = 0x01, - TextureIsSrgb = 0x02 + TextureIsSrgb = 0x02, + NeedsPremultipliedAlphaBlending = 0x04 }; Q_DECLARE_FLAGS(Flags, Flag) - explicit QPlatformTextureList(QObject *parent = 0); + explicit QPlatformTextureList(QObject *parent = nullptr); ~QPlatformTextureList(); int count() const; @@ -99,7 +100,7 @@ public: bool isLocked() const; void appendTexture(void *source, GLuint textureId, const QRect &geometry, - const QRect &clipRect = QRect(), Flags flags = 0); + const QRect &clipRect = QRect(), Flags flags = nullptr); void clear(); Q_SIGNALS: diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h index 8e74a499fd..93fab55aa1 100644 --- a/src/gui/painting/qpolygon.h +++ b/src/gui/painting/qpolygon.h @@ -60,18 +60,14 @@ public: inline ~QPolygon() {} inline explicit QPolygon(int size); inline /*implicit*/ QPolygon(const QVector<QPoint> &v) : QVector<QPoint>(v) {} -#ifdef Q_COMPILER_RVALUE_REFS - /*implicit*/ QPolygon(QVector<QPoint> &&v) Q_DECL_NOTHROW : QVector<QPoint>(std::move(v)) {} -#endif + /*implicit*/ QPolygon(QVector<QPoint> &&v) noexcept : QVector<QPoint>(std::move(v)) {} QPolygon(const QRect &r, bool closed=false); QPolygon(int nPoints, const int *points); QPolygon(const QPolygon &other) : QVector<QPoint>(other) {} -#ifdef Q_COMPILER_RVALUE_REFS - QPolygon(QPolygon &&other) Q_DECL_NOTHROW : QVector<QPoint>(std::move(other)) {} - QPolygon &operator=(QPolygon &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QPolygon(QPolygon &&other) noexcept : QVector<QPoint>(std::move(other)) {} + QPolygon &operator=(QPolygon &&other) noexcept { swap(other); return *this; } QPolygon &operator=(const QPolygon &other) { QVector<QPoint>::operator=(other); return *this; } - void swap(QPolygon &other) Q_DECL_NOTHROW { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps + void swap(QPolygon &other) noexcept { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps operator QVariant() const; @@ -145,16 +141,12 @@ public: inline ~QPolygonF() {} inline explicit QPolygonF(int size); inline /*implicit*/ QPolygonF(const QVector<QPointF> &v) : QVector<QPointF>(v) {} -#ifdef Q_COMPILER_RVALUE_REFS - /* implicit */ QPolygonF(QVector<QPointF> &&v) Q_DECL_NOTHROW : QVector<QPointF>(std::move(v)) {} -#endif + /* implicit */ QPolygonF(QVector<QPointF> &&v) noexcept : QVector<QPointF>(std::move(v)) {} QPolygonF(const QRectF &r); /*implicit*/ QPolygonF(const QPolygon &a); inline QPolygonF(const QPolygonF &a) : QVector<QPointF>(a) {} -#ifdef Q_COMPILER_RVALUE_REFS - QPolygonF(QPolygonF &&other) Q_DECL_NOTHROW : QVector<QPointF>(std::move(other)) {} - QPolygonF &operator=(QPolygonF &&other) Q_DECL_NOTHROW { swap(other); return *this; } -#endif + QPolygonF(QPolygonF &&other) noexcept : QVector<QPointF>(std::move(other)) {} + QPolygonF &operator=(QPolygonF &&other) noexcept { swap(other); return *this; } QPolygonF &operator=(const QPolygonF &other) { QVector<QPointF>::operator=(other); return *this; } inline void swap(QPolygonF &other) { QVector<QPointF>::swap(other); } // prevent QVector<QPointF><->QPolygonF swaps diff --git a/src/gui/painting/qrbtree_p.h b/src/gui/painting/qrbtree_p.h index d3ee23a91c..42e88889a1 100644 --- a/src/gui/painting/qrbtree_p.h +++ b/src/gui/painting/qrbtree_p.h @@ -60,7 +60,7 @@ struct QRBTree { struct Node { - inline Node() : parent(0), left(0), right(0), red(true) { } + inline Node() : parent(nullptr), left(nullptr), right(nullptr), red(true) { } inline ~Node() {if (left) delete left; if (right) delete right;} T data; Node *parent; @@ -69,7 +69,7 @@ struct QRBTree bool red; }; - inline QRBTree() : root(0), freeList(0) { } + inline QRBTree() : root(nullptr), freeList(nullptr) { } inline ~QRBTree(); inline void clear(); @@ -120,7 +120,7 @@ inline QRBTree<T>::~QRBTree() while (freeList) { // Avoid recursively calling the destructor, as this list may become large. Node *next = freeList->right; - freeList->right = 0; + freeList->right = nullptr; delete freeList; freeList = next; } @@ -131,7 +131,7 @@ inline void QRBTree<T>::clear() { if (root) delete root; - root = 0; + root = nullptr; } template <class T> @@ -359,7 +359,7 @@ void QRBTree<T>::detach(Node *node) // call this before removing a node. ref = child; if (child) child->parent = node->parent; - node->left = node->right = node->parent = 0; + node->left = node->right = node->parent = nullptr; } // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. @@ -513,7 +513,7 @@ inline void QRBTree<T>::deleteNode(Node *&node) detach(node); node->right = freeList; freeList = node; - node = 0; + node = nullptr; } template <class T> @@ -522,7 +522,7 @@ inline typename QRBTree<T>::Node *QRBTree<T>::newNode() if (freeList) { Node *node = freeList; freeList = freeList->right; - node->parent = node->left = node->right = 0; + node->parent = node->left = node->right = nullptr; node->red = true; return node; } diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index 69d48fcc58..82f5be2b65 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -1233,10 +1233,10 @@ struct QRegionPrivate { } } - const QRect *begin() const Q_DECL_NOTHROW + const QRect *begin() const noexcept { return numRects == 1 ? &extents : rects.data(); } // avoid vectorize() - const QRect *end() const Q_DECL_NOTHROW + const QRect *end() const noexcept { return begin() + numRects; } inline void append(const QRect *r); @@ -4267,7 +4267,7 @@ QRegion QRegion::xored(const QRegion &r) const } } -QRect QRegion::boundingRect() const Q_DECL_NOTHROW +QRect QRegion::boundingRect() const noexcept { if (isEmpty()) return QRect(); @@ -4325,12 +4325,12 @@ QVector<QRect> QRegion::rects() const } #endif -QRegion::const_iterator QRegion::begin() const Q_DECL_NOTHROW +QRegion::const_iterator QRegion::begin() const noexcept { return d->qt_rgn ? d->qt_rgn->begin() : nullptr; } -QRegion::const_iterator QRegion::end() const Q_DECL_NOTHROW +QRegion::const_iterator QRegion::end() const noexcept { return d->qt_rgn ? d->qt_rgn->end() : nullptr; } @@ -4367,7 +4367,7 @@ void QRegion::setRects(const QRect *rects, int num) } } -int QRegion::rectCount() const Q_DECL_NOTHROW +int QRegion::rectCount() const noexcept { return (d->qt_rgn ? d->qt_rgn->numRects : 0); } diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index 9fe6ed5675..54de916198 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -69,30 +69,28 @@ public: QRegion(const QRect &r, RegionType t = Rectangle); QRegion(const QPolygon &pa, Qt::FillRule fillRule = Qt::OddEvenFill); QRegion(const QRegion ®ion); - QRegion(QRegion &&other) Q_DECL_NOTHROW + QRegion(QRegion &&other) noexcept : d(other.d) { other.d = const_cast<QRegionData*>(&shared_empty); } QRegion(const QBitmap &bitmap); ~QRegion(); QRegion &operator=(const QRegion &); -#ifdef Q_COMPILER_RVALUE_REFS - inline QRegion &operator=(QRegion &&other) Q_DECL_NOEXCEPT + inline QRegion &operator=(QRegion &&other) noexcept { qSwap(d, other.d); return *this; } -#endif - inline void swap(QRegion &other) Q_DECL_NOEXCEPT { qSwap(d, other.d); } + inline void swap(QRegion &other) noexcept { qSwap(d, other.d); } bool isEmpty() const; bool isNull() const; typedef const QRect *const_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; - const_iterator begin() const Q_DECL_NOTHROW; - const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); } - const_iterator end() const Q_DECL_NOTHROW; - const_iterator cend() const Q_DECL_NOTHROW { return end(); } - const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); } - const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); } - const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); } - const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); } + const_iterator begin() const noexcept; + const_iterator cbegin() const noexcept { return begin(); } + const_iterator end() const noexcept; + const_iterator cend() const noexcept { return end(); } + const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept { return rend(); } bool contains(const QPoint &p) const; bool contains(const QRect &r) const; @@ -121,13 +119,13 @@ public: bool intersects(const QRegion &r) const; bool intersects(const QRect &r) const; - QRect boundingRect() const Q_DECL_NOTHROW; + QRect boundingRect() const noexcept; #if QT_DEPRECATED_SINCE(5, 11) QT_DEPRECATED_X("Use begin()/end() instead") QVector<QRect> rects() const; #endif void setRects(const QRect *rect, int num); - int rectCount() const Q_DECL_NOTHROW; + int rectCount() const noexcept; #ifdef Q_COMPILER_MANGLES_RETURN_TYPE // ### Qt 6: remove these, they're kept for MSVC compat const QRegion operator|(const QRegion &r) const; diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h index ca879de27c..d145dbfbea 100644 --- a/src/gui/painting/qrgba64_p.h +++ b/src/gui/painting/qrgba64_p.h @@ -284,6 +284,8 @@ static Q_ALWAYS_INLINE void blend_pixel(QRgba64 &dst, QRgba64 src) static Q_ALWAYS_INLINE void blend_pixel(QRgba64 &dst, QRgba64 src, const int const_alpha) { + if (const_alpha == 255) + return blend_pixel(dst, src); if (!src.isTransparent()) { src = multiplyAlpha255(src, const_alpha); dst = src + multiplyAlpha65535(dst, 65535 - src.alpha()); diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 90c06788e1..271d3ba6bf 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -173,15 +173,12 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker, bool capFirst, QLineF *startTangent); /******************************************************************************* - * QLineF::angle gives us the smalles angle between two lines. Here we - * want to identify the line's angle direction on the unit circle. + * QLineF::angleTo gives us the angle between two lines with respecting the direction. + * Here we want to identify the line's angle direction on the unit circle. */ static inline qreal adapted_angle_on_x(const QLineF &line) { - qreal angle = line.angle(QLineF(0, 0, 1, 0)); - if (line.dy() > 0) - angle = 360 - angle; - return angle; + return QLineF(0, 0, 1, 0).angleTo(line); } QStrokerOps::QStrokerOps() @@ -459,7 +456,7 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); QPointF isect; - QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); + QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect); if (join == FlatJoin) { QLineF shortCut(prevLine.p2(), nextLine.p1()); diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 06fa4bf95e..7a3dd04965 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -73,7 +73,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const if (path.isEmpty()) break; - images[numImages++] = qMove(img); + images[numImages++] = std::move(img); } else { bool found = false; for (int j = 0; j < numImages; ++j) { @@ -83,7 +83,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const } } if (!found) - images[numImages++] = qMove(img); + images[numImages++] = std::move(img); } } @@ -285,6 +285,8 @@ QImageTextureGlyphCache::~QImageTextureGlyphCache() void QImageTextureGlyphCache::resizeTextureData(int width, int height) { m_image = m_image.copy(0, 0, width, height); + // Regions not part of the copy are initialized to 0, and that is just what + // we need. } void QImageTextureGlyphCache::createTextureData(int width, int height) @@ -305,6 +307,12 @@ void QImageTextureGlyphCache::createTextureData(int width, int height) default: Q_UNREACHABLE(); } + + // Regions not touched by the glyphs must be initialized to 0. (such + // locations may in fact be sampled with styled (shifted) text materials) + // When resizing, the QImage copy() does this implicitly but the initial + // contents must be zeroed out explicitly here. + m_image.fill(0); } void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition) diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index c105e89e50..b6fc7230a8 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -75,7 +75,7 @@ class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache { public: QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color = QColor()) - : QFontEngineGlyphCache(format, matrix, color), m_current_fontengine(0), + : QFontEngineGlyphCache(format, matrix, color), m_current_fontengine(nullptr), m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0) { } diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 816514a695..d75b66c50b 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -265,7 +265,9 @@ QTransform::QTransform() , m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxNone) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) , d(nullptr) +#endif { } @@ -284,7 +286,9 @@ QTransform::QTransform(qreal h11, qreal h12, qreal h13, , m_13(h13), m_23(h23), m_33(h33) , m_type(TxNone) , m_dirty(TxProject) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) , d(nullptr) +#endif { } @@ -301,7 +305,9 @@ QTransform::QTransform(qreal h11, qreal h12, qreal h21, , m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxShear) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) , d(nullptr) +#endif { } @@ -317,7 +323,9 @@ QTransform::QTransform(const QMatrix &mtx) m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxShear) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) , d(nullptr) +#endif { } @@ -795,7 +803,7 @@ bool QTransform::operator==(const QTransform &o) const Returns the hash value for \a key, using \a seed to seed the calculation. */ -uint qHash(const QTransform &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QTransform &key, uint seed) noexcept { QtPrivate::QHashCombine hash; seed = hash(seed, key.m11()); @@ -1021,7 +1029,7 @@ QTransform QTransform::operator*(const QTransform &m) const /*! Assigns the given \a matrix's values to this matrix. */ -QTransform & QTransform::operator=(const QTransform &matrix) Q_DECL_NOTHROW +QTransform & QTransform::operator=(const QTransform &matrix) noexcept { affine._m11 = matrix.affine._m11; affine._m12 = matrix.affine._m12; @@ -1521,12 +1529,12 @@ QRegion QTransform::map(const QRegion &r) const QRegion res; if (m11() < 0 || m22() < 0) { for (const QRect &rect : r) - res += mapRect(rect); + res += mapRect(QRectF(rect)).toRect(); } else { QVarLengthArray<QRect, 32> rects; rects.reserve(r.rectCount()); for (const QRect &rect : r) { - QRect nr = mapRect(rect); + QRect nr = mapRect(QRectF(rect)).toRect(); if (!nr.isEmpty()) rects.append(nr); } diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index 1e322d435a..b220770144 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -77,13 +77,13 @@ public: #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove; the compiler-generated ones are fine! - QTransform &operator=(QTransform &&other) Q_DECL_NOTHROW // = default + QTransform &operator=(QTransform &&other) noexcept // = default { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QTransform)); return *this; } - QTransform &operator=(const QTransform &) Q_DECL_NOTHROW; // = default - QTransform(QTransform &&other) Q_DECL_NOTHROW // = default + QTransform &operator=(const QTransform &) noexcept; // = default + QTransform(QTransform &&other) noexcept // = default : affine(Qt::Uninitialized) { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QTransform)); } - QTransform(const QTransform &other) Q_DECL_NOTHROW // = default + QTransform(const QTransform &other) noexcept // = default : affine(Qt::Uninitialized) { memcpy(static_cast<void *>(this), static_cast<const void *>(&other), sizeof(QTransform)); } #endif @@ -176,7 +176,9 @@ private: , m_13(h13), m_23(h23), m_33(h33) , m_type(TxNone) , m_dirty(TxProject) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) , d(nullptr) +#endif { } inline QTransform(bool) @@ -184,7 +186,9 @@ private: , m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxNone) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) , d(nullptr) +#endif { } inline TransformationType inline_type() const; @@ -202,7 +206,7 @@ private: }; Q_DECLARE_TYPEINFO(QTransform, Q_MOVABLE_TYPE); -Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QTransform &key, uint seed = 0) Q_DECL_NOTHROW; +Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QTransform &key, uint seed = 0) noexcept; /******* inlines *****/ inline QTransform::TransformationType QTransform::inline_type() const diff --git a/src/gui/painting/qtriangulator_p.h b/src/gui/painting/qtriangulator_p.h index 0abf87308a..177e5e66ed 100644 --- a/src/gui/painting/qtriangulator_p.h +++ b/src/gui/painting/qtriangulator_p.h @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QVertexIndexVector +class QVertexIndexVector { public: enum Type { @@ -93,19 +93,6 @@ public: return indices16.size(); } - QVertexIndexVector() = default; - QVertexIndexVector(const QVertexIndexVector &) = default; - inline QVertexIndexVector &operator = (const QVertexIndexVector &other) - { - if (t == UnsignedInt) - indices32 = other.indices32; - else - indices16 = other.indices16; - - t = other.t; - return *this; - } - private: Type t; @@ -113,23 +100,15 @@ private: QVector<quint16> indices16; }; -struct Q_GUI_EXPORT QTriangleSet +struct QTriangleSet { - inline QTriangleSet() { } - inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { } - QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;} - // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] QVertexIndexVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] }; -struct Q_GUI_EXPORT QPolylineSet +struct QPolylineSet { - inline QPolylineSet() { } - inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { } - QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;} - QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] QVertexIndexVector indices; // End of polyline is marked with -1. }; diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h index 1b649a5d2a..df5772d4cc 100644 --- a/src/gui/painting/qvectorpath_p.h +++ b/src/gui/painting/qvectorpath_p.h @@ -106,7 +106,7 @@ public: // ### Falcon: introduca a struct XY for points so lars is not so confused... QVectorPath(const qreal *points, int count, - const QPainterPath::ElementType *elements = 0, + const QPainterPath::ElementType *elements = nullptr, uint hints = ArbitraryShapeHint) : m_elements(elements), m_points(points), @@ -128,12 +128,12 @@ public: inline bool hasExplicitOpen() const { return m_hints & ExplicitOpen; } inline bool hasWindingFill() const { return m_hints & WindingFill; } - inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = 0; } + inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = nullptr; } inline uint hints() const { return m_hints; } inline const QPainterPath::ElementType *elements() const { return m_elements; } inline const qreal *points() const { return m_points; } - inline bool isEmpty() const { return m_points == 0; } + inline bool isEmpty() const { return m_points == nullptr; } inline int elementCount() const { return m_count; } inline const QPainterPath convertToPainterPath() const; @@ -165,7 +165,7 @@ public: return e; e = e->next; } - return 0; + return nullptr; } template <typename T> static inline bool isRect(const T *pts, int elementCount) { |