diff options
-rw-r--r-- | src/gui/painting/qcolor.cpp | 226 | ||||
-rw-r--r-- | src/gui/painting/qcolor.h | 10 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform.cpp | 39 | ||||
-rw-r--r-- | src/gui/painting/qcolortrc_p.h | 21 | ||||
-rw-r--r-- | tests/auto/gui/painting/qcolor/tst_qcolor.cpp | 203 | ||||
-rw-r--r-- | tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp | 55 |
6 files changed, 404 insertions, 150 deletions
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index bb42660cf2..174350d884 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" @@ -494,6 +495,14 @@ static QStringList get_colornames() with this value, a pixel value will be used that is appropriate for the underlying pixel format in use. + \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 The RGB model is hardware-oriented. Its representation is close to @@ -603,12 +612,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() @@ -778,6 +788,10 @@ QColor::QColor(Spec spec) noexcept case Hsl: setHsl(0, 0, 0, 0); break; + case ExtendedRgb: + cspec = spec; + setRgbF(0, 0, 0, 0); + break; } } @@ -1211,6 +1225,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 +1251,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 +1308,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; } @@ -1421,7 +1464,11 @@ void QColor::setRgb(QRgb rgb) noexcept \sa setAlpha(), alphaF(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} */ int QColor::alpha() const noexcept -{ return ct.argb.alpha >> 8; } +{ + if (cspec == ExtendedRgb) + return qRound(qreal(castF16(ct.argbExtended.alphaF16)) * 255); + return ct.argb.alpha >> 8; +} /*! @@ -1434,6 +1481,11 @@ int QColor::alpha() const noexcept 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; } @@ -1443,7 +1495,11 @@ void QColor::setAlpha(int alpha) \sa setAlphaF(), alpha(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} */ qreal QColor::alphaF() const noexcept -{ return ct.argb.alpha / qreal(USHRT_MAX); } +{ + 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 +1511,10 @@ qreal QColor::alphaF() const noexcept 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); } @@ -1550,25 +1610,29 @@ void QColor::setBlue(int blue) */ 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()); } /*! @@ -1578,25 +1642,29 @@ void QColor::setRedF(qreal red) */ 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()); } /*! @@ -1606,24 +1674,27 @@ void QColor::setGreenF(qreal green) */ 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()); } /*! @@ -1933,6 +2004,30 @@ qreal QColor::blackF() const noexcept } /*! + 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() @@ -1944,7 +2039,8 @@ QColor QColor::toRgb() const noexcept 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 +2162,12 @@ QColor QColor::toRgb() const noexcept 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; } @@ -2238,6 +2340,8 @@ QColor QColor::convertTo(QColor::Spec colorSpec) const noexcept switch (colorSpec) { case Rgb: return toRgb(); + case ExtendedRgb: + return toExtendedRgb(); case Hsv: return toHsv(); case Cmyk: @@ -2317,20 +2421,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); @@ -2885,6 +3001,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) diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index cbc8b98f9c..77b2d43c40 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -64,7 +64,7 @@ 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() noexcept; @@ -198,6 +198,7 @@ public: QColor toHsv() const noexcept; QColor toCmyk() const noexcept; QColor toHsl() const noexcept; + QColor toExtendedRgb() const noexcept; Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const noexcept; @@ -275,6 +276,13 @@ private: ushort lightness; ushort pad; } ahsl; + struct { + ushort alphaF16; + ushort redF16; + ushort greenF16; + ushort blueF16; + ushort pad; + } argbExtended; ushort array[5]; } ct; diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp index b677c4b36b..c723e12f8a 100644 --- a/src/gui/painting/qcolortransform.cpp +++ b/src/gui/painting/qcolortransform.cpp @@ -204,19 +204,36 @@ QColor QColorTransform::map(const QColor &color) const if (!d_ptr) return color; Q_D(const QColorTransform); - QColorVector c = { (float)color.redF(), (float)color.greenF(), (float)color.blueF() }; - 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); + 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); - if (d_ptr->colorSpaceOut->lutsGenerated.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); + 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_ptr->colorSpaceOut->lutsGenerated.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].applyInverse(c.x); - c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); - c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + 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()); diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h index 3a649f3756..3ef9d442fc 100644 --- a/src/gui/painting/qcolortrc_p.h +++ b/src/gui/painting/qcolortrc_p.h @@ -91,7 +91,16 @@ public: 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) @@ -100,6 +109,16 @@ public: 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); diff --git a/tests/auto/gui/painting/qcolor/tst_qcolor.cpp b/tests/auto/gui/painting/qcolor/tst_qcolor.cpp index 13d0618bd9..4d92bdd382 100644 --- a/tests/auto/gui/painting/qcolor/tst_qcolor.cpp +++ b/tests/auto/gui/painting/qcolor/tst_qcolor.cpp @@ -70,6 +70,7 @@ private slots: void setBlue(); void setRgb(); + void setRgbF(); void setRgba(); void setHsv(); void setCmyk(); @@ -187,28 +188,28 @@ void tst_QColor::getSetCheck() // void QColor::setRedF(qreal) obj1.setRedF(0.0); QCOMPARE(obj1.redF(), qreal(0.0)); - obj1.setRedF(-0.2); - QCOMPARE(obj1.redF(), qreal(0.0)); // range<0.0, 1.0 - obj1.setRedF(1.1); - QCOMPARE(obj1.redF(), qreal(1.0)); // range<0.0, 1.0 + obj1.setRedF(-0.25); + QCOMPARE(obj1.redF(), qreal(-0.25)); + obj1.setRedF(1.25); + QCOMPARE(obj1.redF(), qreal(1.25)); // qreal QColor::greenF() // void QColor::setGreenF(qreal) obj1.setGreenF(0.0); QCOMPARE(obj1.greenF(), qreal(0.0)); - obj1.setGreenF(-0.2); - QCOMPARE(obj1.greenF(), qreal(0.0)); // range<0.0, 1.0 - obj1.setGreenF(1.1); - QCOMPARE(obj1.greenF(), qreal(1.0)); // range<0.0, 1.0 + obj1.setGreenF(-0.25); + QCOMPARE(obj1.greenF(), qreal(-0.25)); + obj1.setGreenF(1.5); + QCOMPARE(obj1.greenF(), qreal(1.5)); // qreal QColor::blueF() // void QColor::setBlueF(qreal) obj1.setBlueF(0.0); QCOMPARE(obj1.blueF(), qreal(0.0)); - obj1.setBlueF(-0.2); - QCOMPARE(obj1.blueF(), qreal(0.0)); // range<0.0, 1.0 - obj1.setBlueF(1.1); - QCOMPARE(obj1.blueF(), qreal(1.0)); // range<0.0, 1.0 + obj1.setBlueF(-0.5); + QCOMPARE(obj1.blueF(), qreal(-0.5)); + obj1.setBlueF(2.0); + QCOMPARE(obj1.blueF(), qreal(2.0)); // QRgb QColor::rgba() // void QColor::setRgba(QRgb) @@ -677,30 +678,81 @@ void tst_QColor::setRgb() { QColor color; - for (int A = 0; A <= USHRT_MAX; ++A) { - { - // 0-255 - int a = A >> 8; - QRgb rgb = qRgba(0, 0, 0, a); + for (int a = 0; a <= 255; ++a) { + QRgb rgb = qRgba(0, 0, 0, a); - color.setRgb(0, 0, 0, a); - QCOMPARE(color.alpha(), a); - QCOMPARE(color.rgb(), qRgb(0, 0, 0)); + color.setRgb(0, 0, 0, a); + QCOMPARE(color.alpha(), a); + QCOMPARE(color.rgb(), qRgb(0, 0, 0)); - color.setRgb(rgb); - QCOMPARE(color.alpha(), 255); - QCOMPARE(color.rgb(), qRgb(0, 0, 0)); + color.setRgb(rgb); + QCOMPARE(color.alpha(), 255); + QCOMPARE(color.rgb(), qRgb(0, 0, 0)); - int r, g, b, a2; - color.setRgb(0, 0, 0, a); - color.getRgb(&r, &g, &b, &a2); - QCOMPARE(a2, a); + int r, g, b, a2; + color.setRgb(0, 0, 0, a); + color.getRgb(&r, &g, &b, &a2); + QCOMPARE(a2, a); - QColor c(0, 0, 0); - c.setAlpha(a); - QCOMPARE(c.alpha(), a); - } + QColor c(0, 0, 0); + c.setAlpha(a); + QCOMPARE(c.alpha(), a); + } + for (int r = 0; r <= 255; ++r) { + QRgb rgb = qRgb(r, 0, 0); + + color.setRgb(r, 0, 0); + QCOMPARE(color.red(), r); + QCOMPARE(color.rgb(), rgb); + + color.setRgb(rgb); + QCOMPARE(color.red(), r); + QCOMPARE(color.rgb(), rgb); + + int r2, g, b, a; + color.getRgb(&r2, &g, &b, &a); + QCOMPARE(r2, r); + } + + for (int g = 0; g <= 255; ++g) { + QRgb rgb = qRgb(0, g, 0); + + color.setRgb(0, g, 0); + QCOMPARE(color.green(), g); + QCOMPARE(color.rgb(), rgb); + + color.setRgb(rgb); + QCOMPARE(color.green(), g); + QCOMPARE(color.rgb(), rgb); + + int r, g2, b, a; + color.getRgb(&r, &g2, &b, &a); + QCOMPARE(g2, g); + } + + for (int b = 0; b <= 255; ++b) { + QRgb rgb = qRgb(0, 0, b); + + color.setRgb(0, 0, b); + QCOMPARE(color.blue(), b); + QCOMPARE(color.rgb(), rgb); + + color.setRgb(rgb); + QCOMPARE(color.blue(), b); + QCOMPARE(color.rgb(), rgb); + + int r, g, b2, a; + color.getRgb(&r, &g, &b2, &a); + QCOMPARE(b2, b); + } +} + +void tst_QColor::setRgbF() +{ + QColor color; + + for (int A = 0; A <= USHRT_MAX; ++A) { { // 0.0-1.0 qreal a = A / qreal(USHRT_MAX); @@ -720,24 +772,6 @@ void tst_QColor::setRgb() for (int R = 0; R <= USHRT_MAX; ++R) { { - // 0-255 - int r = R >> 8; - QRgb rgb = qRgb(r, 0, 0); - - color.setRgb(r, 0, 0); - QCOMPARE(color.red(), r); - QCOMPARE(color.rgb(), rgb); - - color.setRgb(rgb); - QCOMPARE(color.red(), r); - QCOMPARE(color.rgb(), rgb); - - int r2, g, b, a; - color.getRgb(&r2, &g, &b, &a); - QCOMPARE(r2, r); - } - - { // 0.0-1.0 qreal r = R / qreal(USHRT_MAX); color.setRgbF(r, 0.0, 0.0); @@ -751,24 +785,6 @@ void tst_QColor::setRgb() for (int G = 0; G <= USHRT_MAX; ++G) { { - // 0-255 - int g = G >> 8; - QRgb rgb = qRgb(0, g, 0); - - color.setRgb(0, g, 0); - QCOMPARE(color.green(), g); - QCOMPARE(color.rgb(), rgb); - - color.setRgb(rgb); - QCOMPARE(color.green(), g); - QCOMPARE(color.rgb(), rgb); - - int r, g2, b, a; - color.getRgb(&r, &g2, &b, &a); - QCOMPARE(g2, g); - } - - { // 0.0-1.0 qreal g = G / qreal(USHRT_MAX); color.setRgbF(0.0, g, 0.0); @@ -782,32 +798,53 @@ void tst_QColor::setRgb() for (int B = 0; B <= USHRT_MAX; ++B) { { - // 0-255 - int b = B >> 8; - QRgb rgb = qRgb(0, 0, b); + // 0.0-1.0 + qreal b = B / qreal(USHRT_MAX); + color.setRgbF(0.0, 0.0, b); + QCOMPARE(color.blueF(), b); - color.setRgb(0, 0, b); - QCOMPARE(color.blue(), b); - QCOMPARE(color.rgb(), rgb); + qreal r, g, b2, a; + color.getRgbF(&r, &g, &b2, &a); + QCOMPARE(b2, b); + } + } - color.setRgb(rgb); - QCOMPARE(color.blue(), b); - QCOMPARE(color.rgb(), rgb); + for (int R = -128; R <= 512; ++R) { + { + // extended RGB + qreal r = R / qreal(256); + color.setRgbF(r, 0.0, 0.0); + QCOMPARE(qfloat16(color.redF()), qfloat16(r)); - int r, g, b2, a; - color.getRgb(&r, &g, &b2, &a); - QCOMPARE(b2, b); + qreal r2, g, b, a; + color.getRgbF(&r2, &g, &b, &a); + QCOMPARE(qfloat16(r2), qfloat16(r)); } + } + for (int G = -128; G <= 512; ++G) { { - // 0.0-1.0 - qreal b = B / qreal(USHRT_MAX); + // extended RGB + qreal g = G / qreal(256); + color.setRgbF(0.0, g, 0.0); + QCOMPARE(qfloat16(color.greenF()), qfloat16(g)); + + qreal r, g2, b, a; + color.getRgbF(&r, &g2, &b, &a); + QCOMPARE(qfloat16(g2), qfloat16(g)); + } + } + + for (int B = -128; B <= 512; ++B) { + { + // extended RGB + qreal b = B / qreal(256); color.setRgbF(0.0, 0.0, b); - QCOMPARE(color.blueF(), b); + QCOMPARE(qfloat16(color.blueF()), qfloat16(b)); qreal r, g, b2, a; color.getRgbF(&r, &g, &b2, &a); - QCOMPARE(b2, b); + QCOMPARE(qfloat16(b2), qfloat16(b)); } } } diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp index 9bd4b75443..35bca58854 100644 --- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp +++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp @@ -57,6 +57,8 @@ private slots: void imageConversion(); void loadImage(); + + void gamut(); }; tst_QColorSpace::tst_QColorSpace() @@ -232,6 +234,59 @@ void tst_QColorSpace::loadImage() // Test the iccProfile getter returns the ICC profile from the image // which since we didn't write it, isn't identical to our defaults. QVERIFY(defaultProPhotoRgb.iccProfile() != image.colorSpace().iccProfile()); + + QColorTransform transform = image.colorSpace().transformationToColorSpace(QColorSpace::SRgb); + qreal maxRed = 0; + qreal maxBlue = 0; + qreal maxRed2 = 0; + qreal maxBlue2 = 0; + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + QColor p = image.pixelColor(x, y); + maxRed = std::max(maxRed, p.redF()); + maxBlue = std::max(maxBlue, p.blueF()); + p = transform.map(p); + maxRed2 = std::max(maxRed2, p.redF()); + maxBlue2 = std::max(maxBlue2, p.blueF()); + + } + } + // ProPhotoRgb can be a lot more red and blue than SRgb can, so it will have lower values. + QVERIFY(maxRed2 > maxRed); + QVERIFY(maxBlue2 > maxBlue); +} + +void tst_QColorSpace::gamut() +{ + QColor black = QColor::fromRgbF(0.0, 0.0, 0.0); + QColor white = QColor::fromRgbF(1.0, 1.0, 1.0); + QColor red = QColor::fromRgbF(1.0, 0.0, 0.0); + QColor green = QColor::fromRgbF(0.0, 1.0, 0.0); + QColor blue = QColor::fromRgbF(0.0, 0.0, 1.0); + + QColorTransform toAdobeRgb = QColorSpace(QColorSpace::SRgb).transformationToColorSpace(QColorSpace::AdobeRgb); + + QColor tblack = toAdobeRgb.map(black); + QColor twhite = toAdobeRgb.map(white); + QColor tred = toAdobeRgb.map(red); + QColor tgreen = toAdobeRgb.map(green); + QColor tblue = toAdobeRgb.map(blue); + + // Black is black + QCOMPARE(tblack, black); + + // This white hasn't changed + QCOMPARE(twhite, white); + + // Adobe's red and blue gamut corners are the same as sRGB's + // So, a color in the red corner, will stay in the red corner + // the same for blue, but not for green. + QVERIFY(tred.greenF() < 0.001); + QVERIFY(tred.blueF() < 0.001); + QVERIFY(tblue.redF() < 0.001); + QVERIFY(tblue.greenF() < 0.001); + QVERIFY(tgreen.redF() > 0.2); + QVERIFY(tgreen.blueF() > 0.2); } QTEST_MAIN(tst_QColorSpace) |