Add Extended RGB model to QColor

Can be used to represent wide-gamut and HDR colors.

Change-Id: I1cca442069ce2f2c070f723fe930fe1f2f5580ad
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Allan Sandfeld Jensen 2018-07-02 13:20:39 +02:00
parent 6dca11e1af
commit 8f44aeb103
6 changed files with 411 additions and 157 deletions

View File

@ -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());
}
/*!
@ -1932,6 +2003,30 @@ qreal QColor::blackF() const noexcept
return ct.acmyk.black / qreal(USHRT_MAX);
}
/*!
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.
@ -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)

View File

@ -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;

View File

@ -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);
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);
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->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->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_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].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());

View File

@ -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);

View File

@ -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 <= 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(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);
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-255
int a = A >> 8;
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(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);
QColor c(0, 0, 0);
c.setAlpha(a);
QCOMPARE(c.alpha(), a);
}
{
// 0.0-1.0
qreal a = A / qreal(USHRT_MAX);
@ -719,24 +771,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);
@ -750,24 +784,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);
@ -781,24 +797,6 @@ void tst_QColor::setRgb()
}
for (int B = 0; B <= USHRT_MAX; ++B) {
{
// 0-255
int b = B >> 8;
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);
}
{
// 0.0-1.0
qreal b = B / qreal(USHRT_MAX);
@ -810,6 +808,45 @@ void tst_QColor::setRgb()
QCOMPARE(b2, b);
}
}
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));
qreal r2, g, b, a;
color.getRgbF(&r2, &g, &b, &a);
QCOMPARE(qfloat16(r2), qfloat16(r));
}
}
for (int G = -128; G <= 512; ++G) {
{
// 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(qfloat16(color.blueF()), qfloat16(b));
qreal r, g, b2, a;
color.getRgbF(&r, &g, &b2, &a);
QCOMPARE(qfloat16(b2), qfloat16(b));
}
}
}
void tst_QColor::setRgba()

View File

@ -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)