Introduce QRgba64 structure for 64bit RGBA values

This structure is meant to replace QRgb where higher precision is
needed.

Change-Id: I49d441e2133371a8b91c2e6af0c137bcc5fcb9ed
Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
This commit is contained in:
Allan Sandfeld Jensen 2015-02-11 11:12:43 +01:00
parent 395e205f2f
commit e2c91dd77b
6 changed files with 603 additions and 6 deletions

View File

@ -43,6 +43,7 @@ HEADERS += \
painting/qrasterizer_p.h \
painting/qregion.h \
painting/qrgb.h \
painting/qrgba64.h \
painting/qstroker_p.h \
painting/qtextureglyphcache_p.h \
painting/qtransform.h \

View File

@ -416,6 +416,18 @@ QColor::QColor(QRgb color)
ct.argb.pad = 0;
}
/*!
\since 5.6
Constructs a color with the value \a rgba64.
\sa fromRgba64()
*/
QColor::QColor(QRgba64 rgba64)
{
setRgba64(rgba64);
}
/*!
\internal
@ -941,7 +953,7 @@ void QColor::setRgb(int r, int g, int b, int a)
For an invalid color, the alpha value of the returned color is unspecified.
\sa setRgba(), rgb()
\sa setRgba(), rgb(), rgba64()
*/
QRgb QColor::rgba() const
@ -954,7 +966,7 @@ QRgb QColor::rgba() const
/*!
Sets the RGB value to \a rgba, including its alpha.
\sa rgba(), rgb()
\sa rgba(), rgb(), setRgba64()
*/
void QColor::setRgba(QRgb rgba)
{
@ -966,6 +978,40 @@ void QColor::setRgba(QRgb rgba)
ct.argb.pad = 0;
}
/*!
\since 5.6
Returns the RGB64 value of the color, including its alpha.
For an invalid color, the alpha value of the returned color is unspecified.
\sa setRgba64(), rgba(), rgb()
*/
QRgba64 QColor::rgba64() const
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().rgba64();
return qRgba64(ct.argb.red, ct.argb.green, ct.argb.blue, ct.argb.alpha);
}
/*!
\since 5.6
Sets the RGB64 value to \a rgba, including its alpha.
\sa \setRgba(), rgba64()
*/
void QColor::setRgba64(QRgba64 rgba)
{
cspec = Rgb;
ct.argb.alpha = rgba.alpha();
ct.argb.red = rgba.red();
ct.argb.green = rgba.green();
ct.argb.blue = rgba.blue();
ct.argb.pad = 0;
}
/*!
\fn QRgb QColor::rgb() const
@ -1850,7 +1896,7 @@ QColor QColor::fromRgb(QRgb rgb)
Unlike the fromRgb() function, the alpha-channel specified by the given
QRgb value is included.
\sa fromRgb(), isValid()
\sa fromRgb(), fromRgba64(), isValid()
*/
QColor QColor::fromRgba(QRgb rgba)
@ -1865,7 +1911,7 @@ QColor QColor::fromRgba(QRgb rgba)
All the values must be in the range 0-255.
\sa toRgb(), fromRgbF(), isValid()
\sa toRgb(), fromRgba64(), fromRgbF(), isValid()
*/
QColor QColor::fromRgb(int r, int g, int b, int a)
{
@ -1894,7 +1940,7 @@ QColor QColor::fromRgb(int r, int g, int b, int a)
All the values must be in the range 0.0-1.0.
\sa fromRgb(), toRgb(), isValid()
\sa fromRgb(), fromRgba64(), toRgb(), isValid()
*/
QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a)
{
@ -1916,6 +1962,38 @@ QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a)
return color;
}
/*!
\since 5.6
Static convenience function that returns a QColor constructed from the RGBA64
color values, \a r (red), \a g (green), \a b (blue), and \a a
(alpha-channel, i.e. transparency).
\sa fromRgb(), fromRgbF(), toRgb(), isValid()
*/
QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a)
{
QColor color;
color.setRgba64(qRgba64(r, g, b, a));
return color;
}
/*!
\since 5.6
Static convenience function that returns a QColor constructed from the
given QRgba64 value \a rgba64.
\sa fromRgb(), fromRgbF(), toRgb(), isValid()
*/
QColor QColor::fromRgba64(QRgba64 rgba64)
{
QColor color;
color.setRgba64(rgba64);
return color;
}
/*!
Static convenience function that returns a QColor constructed from the HSV
color values, \a h (hue), \a s (saturation), \a v (value), and \a a

View File

@ -37,6 +37,7 @@
#include <QtGui/qrgb.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qstringlist.h>
#include <QtGui/qrgba64.h>
QT_BEGIN_NAMESPACE
@ -63,6 +64,7 @@ public:
QColor(Qt::GlobalColor color);
QColor(int r, int g, int b, int a = 255);
QColor(QRgb rgb);
QColor(QRgba64 rgba64);
QColor(const QString& name);
QColor(const char *name);
QColor(const QColor &color); // ### Qt 6: remove, the trivial one is fine.
@ -109,6 +111,9 @@ public:
QRgb rgba() const;
void setRgba(QRgb rgba);
QRgba64 rgba64() const;
void setRgba64(QRgba64 rgba);
QRgb rgb() const;
void setRgb(QRgb rgb);
@ -173,6 +178,9 @@ public:
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);
static QColor fromRgba64(QRgba64 rgba);
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);

186
src/gui/painting/qrgba64.h Normal file
View File

@ -0,0 +1,186 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://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 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRGBA64_H
#define QRGBA64_H
#include <QtCore/qglobal.h>
#include <QtCore/qprocessordetection.h>
QT_BEGIN_NAMESPACE
class QRgba64 {
union {
struct {
quint16 red;
quint16 green;
quint16 blue;
quint16 alpha;
} c;
quint64 rgba;
};
public:
// No constructors are allowed, since this needs to be usable in a union in no-c++11 mode.
// When c++11 is mandatory, we can add all but a copy constructor.
Q_DECL_RELAXED_CONSTEXPR static QRgba64 fromRgba64(quint16 red, quint16 green, quint16 blue, quint16 alpha)
{
QRgba64 rgba64;
rgba64.c.red = red;
rgba64.c.green = green;
rgba64.c.blue = blue;
rgba64.c.alpha = alpha;
return rgba64;
}
Q_DECL_RELAXED_CONSTEXPR static QRgba64 fromRgba64(quint64 c)
{
QRgba64 rgba64;
rgba64.rgba = c;
return rgba64;
}
Q_DECL_RELAXED_CONSTEXPR static QRgba64 fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha)
{
QRgba64 rgb64 = fromRgba64(red, green, blue, alpha);
// Expand the range so that 0x00 maps to 0x0000 and 0xff maps to 0xffff.
rgb64.rgba |= rgb64.rgba << 8;
return rgb64;
}
Q_DECL_RELAXED_CONSTEXPR static QRgba64 fromArgb32(uint rgb)
{
return fromRgba(rgb >> 16, rgb >> 8, rgb, rgb >> 24);
}
Q_DECL_CONSTEXPR quint16 red() const { return c.red; }
Q_DECL_CONSTEXPR quint16 green() const { return c.green; }
Q_DECL_CONSTEXPR quint16 blue() const { return c.blue; }
Q_DECL_CONSTEXPR quint16 alpha() const { return c.alpha; }
void setRed(quint16 _red) { c.red = _red; }
void setGreen(quint16 _green) { c.green = _green; }
void setBlue(quint16 _blue) { c.blue = _blue; }
void setAlpha(quint16 _alpha) { c.alpha = _alpha; }
Q_DECL_CONSTEXPR quint8 red8() const { return div_257(c.red); }
Q_DECL_CONSTEXPR quint8 green8() const { return div_257(c.green); }
Q_DECL_CONSTEXPR quint8 blue8() const { return div_257(c.blue); }
Q_DECL_CONSTEXPR quint8 alpha8() const { return div_257(c.alpha); }
Q_DECL_CONSTEXPR uint toArgb32() const
{
return (alpha8() << 24) | (red8() << 16) | (green8() << 8) | blue8();
}
Q_DECL_CONSTEXPR ushort toRgb16() const
{
return (c.red & 0xf800) | ((c.green >> 10) << 5) | (c.blue >> 11);
}
Q_DECL_RELAXED_CONSTEXPR QRgba64 premultiplied() const
{
const quint32 a = c.alpha;
const quint16 r = div_65535(c.red * a);
const quint16 g = div_65535(c.green * a);
const quint16 b = div_65535(c.blue * a);
return fromRgba64(r, g, b, a);
}
Q_DECL_RELAXED_CONSTEXPR QRgba64 unpremultiplied() const
{
#if Q_PROCESSOR_WORDSIZE < 8
return unpremultiplied_32bit();
#else
return unpremultiplied_64bit();
#endif
}
Q_DECL_CONSTEXPR operator quint64() const
{
return rgba;
}
private:
static Q_DECL_CONSTEXPR uint div_257_floor(uint x) { return (x - (x >> 8)) >> 8; }
static Q_DECL_CONSTEXPR uint div_257(uint x) { return div_257_floor(x + 128); }
static Q_DECL_CONSTEXPR uint div_65535(uint x) { return (x + (x>>16) + 0x8000U) >> 16; }
Q_DECL_RELAXED_CONSTEXPR QRgba64 unpremultiplied_32bit() const
{
if (c.alpha == 0xffff || c.alpha == 0)
return *this;
const quint16 r = (quint32(c.red) * 0xffff + c.alpha/2) / c.alpha;
const quint16 g = (quint32(c.green) * 0xffff + c.alpha/2) / c.alpha;
const quint16 b = (quint32(c.blue) * 0xffff + c.alpha/2) / c.alpha;
return fromRgba64(r, g, b, c.alpha);
}
Q_DECL_RELAXED_CONSTEXPR QRgba64 unpremultiplied_64bit() const
{
if (c.alpha == 0xffff || c.alpha == 0)
return *this;
const quint64 fa = (Q_UINT64_C(0xffff00008000) + c.alpha/2) / c.alpha;
const quint16 r = (c.red * fa + 0x80000000) >> 32;
const quint16 g = (c.green * fa + 0x80000000) >> 32;
const quint16 b = (c.blue * fa + 0x80000000) >> 32;
return fromRgba64(r, g, b, c.alpha);
}
};
Q_DECL_RELAXED_CONSTEXPR inline QRgba64 qRgba64(quint16 r, quint16 g, quint16 b, quint16 a)
{
return QRgba64::fromRgba64(r, g, b, a);
}
Q_DECL_RELAXED_CONSTEXPR inline QRgba64 qRgba64(quint64 c)
{
return QRgba64::fromRgba64(c);
}
Q_DECL_RELAXED_CONSTEXPR inline QRgba64 qPremultiply(QRgba64 c)
{
return c.premultiplied();
}
Q_DECL_RELAXED_CONSTEXPR inline QRgba64 qUnpremultiply(QRgba64 c)
{
return c.unpremultiplied();
}
inline Q_DECL_CONSTEXPR uint qRed(QRgba64 rgb)
{ return rgb.red8(); }
inline Q_DECL_CONSTEXPR uint qGreen(QRgba64 rgb)
{ return rgb.green8(); }
inline Q_DECL_CONSTEXPR uint qBlue(QRgba64 rgb)
{ return rgb.blue8(); }
inline Q_DECL_CONSTEXPR uint qAlpha(QRgba64 rgb)
{ return rgb.alpha8(); }
QT_END_NAMESPACE
#endif // QRGBA64_H

View File

@ -0,0 +1,241 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\class QRgba64
\brief The QRgba64 struct contains a 64-bit RGB color.
\since 5.6
\ingroup painting
\inmodule QtGui
QRgba64 is a 64-bit data-structure containing four 16-bit color channels: Red, green, blue and alpha.
QRgba64 can be used a replacement for QRgb when higher precision is needed. In particular a
premultiplied QRgba64 can operate on unpremultipled QRgb without loss of precision except
for alpha 0.
\sa QRgb, QColor
*/
/*!
\fn static QRgba64 QRgba64::fromRgba64(quint16 r, quint16 g, quint16 b, quint16 a)
Returns the QRgba64 quadruplet (\a{r}, \a{g}, \a{b}, \a{a}).
\sa fromRgba()
*/
/*!
\fn static QRgba64 QRgba64::fromRgba64(quint64 c)
Returns \a c as a QRgba64 struct.
\sa fromArgb32()
*/
/*!
\fn static QRgba64 QRgba64::fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha)
Constructs a QRgba64 value from the four 8-bit color channels \a red, \a green, \a blue and \a alpha.
\sa fromArgb32()
*/
/*!
\fn static QRgba64 QRgba64::fromArgb32(uint rgb)
Constructs a QRgba64 value from the 32bit ARGB value \a rgb.
\sa fromRgba()
*/
/*!
\fn quint16 QRgba64::red() const
Returns the 16-bit red color component.
*/
/*!
\fn quint16 QRgba64::green() const
Returns the 16-bit green color component.
*/
/*!
\fn quint16 QRgba64::blue() const
Returns the 16-bit blue color component.
*/
/*!
\fn quint16 QRgba64::alpha() const
Returns the 16-bit alpha channel.
*/
/*!
\fn quint8 QRgba64::red8() const
Returns the red color component as an 8-bit.
*/
/*!
\fn quint8 QRgba64::green8() const
Returns the green color component as an 8-bit.
*/
/*!
\fn quint8 QRgba64::blue8() const
Returns the blue color component as an 8-bit.
*/
/*!
\fn quint8 QRgba64::alpha8() const
Returns the alpha channel as an 8-bit.
*/
/*!
\fn uint QRgba64::toArgb32() const
Returns the color as a 32-bit ARGB value.
\sa fromArgb32()
*/
/*!
\fn ushort QRgba64::toRgb16() const
Returns the color as a 16-bit RGB value.
\sa toArgb32()
*/
/*!
\fn QRgba64 QRgba64::premultiplied() const
Returns the color with the alpha premultiplied.
\sa unpremultiplied()
*/
/*!
\fn QRgba64 QRgba64::unpremultiplied() const
Returns the color with the alpha unpremultiplied.
\sa premultiplied()
*/
/*!
\fn QRgba64::operator quint64() const
Returns the color as a 64bit unsigned integer
*/
/*!
\fn QRgba64 qRgba64(quint16 r, quint16 g, quint16 b, quint16 a)
\relates QColor
\since 5.6
Returns the QRgba64 quadruplet (\a{r}, \a{g}, \a{b}, \a{a}).
\sa qRgba()
*/
/*!
\fn QRgba64 qRgba64(quint64 c)
\relates QColor
\since 5.6
Returns \a c as a QRgba64 struct.
\sa qRgba()
*/
/*!
\fn QRgba64 qPremultiply(QRgba64 rgba64)
\since 5.6
\relates QColor
Converts an unpremultiplied QRgba64 quadruplet \a rgba64 into a premultiplied QRgba64 quadruplet.
\sa QRgba64::premultiplied(), qUnpremultiply()
*/
/*!
\fn QRgba64 qUnpremultiply(QRgba64 rgba64)
\since 5.6
\relates QColor
Converts a premultiplied QRgba64 quadruplet \a rgba64 into an unpremultiplied QRgba64 quadruplet.
\sa QRgba64::unpremultiplied(), qPremultiply()
*/
/*!
\fn uint qRed(QRgba64 rgba64)
\since 5.6
\relates QColor
Returns the red component of \a rgba64 as an 8-bit value.
\sa QRgba64::red8(), QColor::red()
*/
/*!
\fn uint qGreen(QRgba64 rgba64)
\since 5.6
\relates QColor
Returns the green component of \a rgba64 as an 8-bit value.
\sa QRgba64::green8(), QColor::green()
*/
/*!
\fn uint qBlue(QRgba64 rgba64)
\since 5.6
\relates QColor
Returns the blue component of \a rgba64 as an 8-bit value.
\sa QRgba64::blue8(), QColor::blue()
*/
/*!
\fn uint qAlpha(QRgba64 rgba64)
\since 5.6
\relates QColor
Returns the alpha component of \a rgba64 as an 8-bit value.
\sa QRgba64::alpha8(), QColor::alpha()
*/

View File

@ -38,6 +38,7 @@
#include <qcolor.h>
#include <qdebug.h>
#include <qrgba64.h>
#include <private/qdrawingprimitive_sse2_p.h>
class tst_QColor : public QObject
@ -105,6 +106,9 @@ private slots:
void premultiply();
void unpremultiply_sse4();
void qrgba64();
void qrgba64Premultiply();
void qrgba64Equivalence();
#ifdef Q_DEAD_CODE_FROM_QT4_X11
void setallowX11ColorNames();
@ -1455,7 +1459,7 @@ void tst_QColor::unpremultiply_sse4()
for (uint a = 0; a < 256; a++) {
for (uint c = 0; c <= a; c++) {
QRgb p = qRgba(c, a-c, c, a);
QCOMPARE(qUnpremultiply(p), qUnpremultiply_sse4(p));
QCOMPARE(qUnpremultiply_sse4(p), qUnpremultiply(p));
}
}
return;
@ -1464,5 +1468,84 @@ void tst_QColor::unpremultiply_sse4()
QSKIP("SSE4 not supported on this CPU.");
}
void tst_QColor::qrgba64()
{
QRgba64 rgb64 = QRgba64::fromRgba(0x22, 0x33, 0x44, 0xff);
QCOMPARE(rgb64.red(), quint16(0x2222));
QCOMPARE(rgb64.green(), quint16(0x3333));
QCOMPARE(rgb64.blue(), quint16(0x4444));
QCOMPARE(rgb64.alpha(), quint16(0xffff));
QColor c(rgb64);
QCOMPARE(c.red(), 0x22);
QCOMPARE(c.green(), 0x33);
QCOMPARE(c.blue(), 0x44);
QCOMPARE(c.rgba64(), rgb64);
QColor c2 = QColor::fromRgb(0x22, 0x33, 0x44, 0xff);
QCOMPARE(c, c2);
QCOMPARE(c2.rgba64(), rgb64);
rgb64.setAlpha(0x8000);
rgb64.setGreen(0x8844);
rgb64 = rgb64.premultiplied();
QCOMPARE(rgb64.red(), quint16(0x1111));
QCOMPARE(rgb64.blue(), quint16(0x2222));
QCOMPARE(rgb64.green(), quint16(0x4422));
}
void tst_QColor::qrgba64Premultiply()
{
// Tests that qPremultiply(qUnpremultiply(rgba64)) returns rgba64.
for (uint a = 0; a < 0x10000; a+=7) {
const uint step = std::max(a/1024, 1u);
for (uint c = 0; c <= a; c+=step) {
QRgba64 p = qRgba64(c, a-c, a-c/2, a);
QRgba64 pp = qPremultiply(qUnpremultiply(p));
QCOMPARE(pp, p);
}
}
}
void tst_QColor::qrgba64Equivalence()
{
// Any ARGB32 converted back and forth.
for (uint a = 0; a < 256; a++) {
for (uint c = 0; c < 256; c++) {
QRgb p1 = qRgba(c, 255-c, 255-c, a);
QRgba64 p64 = QRgba64::fromArgb32(p1);
QCOMPARE(p64.toArgb32(), p1);
}
}
// Any unpremultiplied ARGB32 value premultipled in RGB64 (except alpha 0).
for (uint a = 1; a < 256; a++) {
for (uint c = 0; c < 256; c++) {
QRgb p1 = qRgba(c, 255-c, 255-c, a);
QRgb pp1 = qPremultiply(p1);
QRgba64 pp64 = qPremultiply(QRgba64::fromArgb32(p1));
QRgb pp2 = pp64.toArgb32();
// 64bit premultiplied is more accurate than 32bit, so allow slight difference.
QCOMPARE(qAlpha(pp2), qAlpha(pp1));
QVERIFY(qAbs(qRed(pp2)-qRed(pp1)) <= 1);
QVERIFY(qAbs(qGreen(pp2)-qGreen(pp1)) <= 1);
QVERIFY(qAbs(qBlue(pp2)-qBlue(pp1)) <= 1);
// But verify the added accuracy means we can return to accurate unpremultiplied ARGB32.
QRgba64 pu64 = qUnpremultiply(pp64);
QRgb p2 = pu64.toArgb32();
QCOMPARE(p2, p1);
}
}
// Any premultiplied ARGB32 value unpremultipled in RGB64.
for (uint a = 0; a < 256; a++) {
for (uint c = 0; c <= a; c++) {
QRgb pp = qRgba(c, a-c, a-c, a);
QRgb pu = qUnpremultiply(pp);
QRgba64 pu64 = qUnpremultiply(QRgba64::fromArgb32(pp));
QCOMPARE(pu64.toArgb32(), pu);
}
}
}
QTEST_MAIN(tst_QColor)
#include "tst_qcolor.moc"