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
parent
6dca11e1af
commit
8f44aeb103
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue