diff options
Diffstat (limited to 'src/gui/painting')
-rw-r--r-- | src/gui/painting/painting.pri | 3 | ||||
-rw-r--r-- | src/gui/painting/qcolorprofile.cpp (renamed from src/gui/painting/qgammatables.cpp) | 47 | ||||
-rw-r--r-- | src/gui/painting/qcolorprofile_p.h | 157 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 146 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper_p.h | 12 |
5 files changed, 242 insertions, 123 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 86e35c39f8..63e345545c 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -8,6 +8,7 @@ HEADERS += \ painting/qbrush.h \ painting/qcolor.h \ painting/qcolor_p.h \ + painting/qcolorprofile_p.h \ painting/qcosmeticstroker_p.h \ painting/qdatabuffer_p.h \ painting/qdrawhelper_p.h \ @@ -63,11 +64,11 @@ SOURCES += \ painting/qblittable.cpp \ painting/qbrush.cpp \ painting/qcolor.cpp \ + painting/qcolorprofile.cpp \ painting/qcompositionfunctions.cpp \ painting/qcosmeticstroker.cpp \ painting/qdrawhelper.cpp \ painting/qemulationpaintengine.cpp \ - painting/qgammatables.cpp \ painting/qgrayraster.c \ painting/qimagescale.cpp \ painting/qmatrix.cpp \ diff --git a/src/gui/painting/qgammatables.cpp b/src/gui/painting/qcolorprofile.cpp index 1d76f7ee3c..3b7b0a248b 100644 --- a/src/gui/painting/qgammatables.cpp +++ b/src/gui/painting/qcolorprofile.cpp @@ -37,28 +37,51 @@ ** ****************************************************************************/ -#include <private/qdrawhelper_p.h> +#include "qcolorprofile_p.h" +#include <qmath.h> QT_BEGIN_NAMESPACE +QColorProfile *QColorProfile::fromGamma(qreal gamma) +{ + QColorProfile *cp = new QColorProfile; + + for (int i = 0; i <= (255 * 16); ++i) { + cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256))); + cp->m_fromLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), qreal(1) / gamma) * (255 * 256))); + } + + return cp; +} -QDrawHelperGammaTables::QDrawHelperGammaTables(qreal smoothing) +static qreal srgbToLinear(qreal v) { - const qreal gray_gamma = 2.31; - for (int i=0; i<256; ++i) - qt_pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); - for (int i=0; i<2048; ++i) - qt_pow_invgamma[i] = uchar(qRound(qPow(i / qreal(2047.0), 1 / gray_gamma) * 255)); + const qreal a = 0.055; + if (v <= qreal(0.04045)) + return v / qreal(12.92); + else + return qPow((v + a) / (qreal(1) + a), qreal(2.4)); +} - refresh(smoothing); +static qreal linearToSrgb(qreal v) +{ + const qreal a = 0.055; + if (v <= qreal(0.0031308)) + return v * qreal(12.92); + else + return (qreal(1) + a) * qPow(v, qreal(1.0 / 2.4)) - a; } -void QDrawHelperGammaTables::refresh(qreal smoothing) +QColorProfile *QColorProfile::fromSRgb() { - for (int i=0; i<256; ++i) { - qt_pow_rgb_gamma[i] = uchar(qRound(qPow(i / qreal(255.0), smoothing) * 255)); - qt_pow_rgb_invgamma[i] = uchar(qRound(qPow(i / qreal(255.), 1 / smoothing) * 255)); + QColorProfile *cp = new QColorProfile; + + for (int i = 0; i <= (255 * 16); ++i) { + cp->m_toLinear[i] = ushort(qRound(srgbToLinear(i / qreal(255 * 16)) * (255 * 256))); + cp->m_fromLinear[i] = ushort(qRound(linearToSrgb(i / qreal(255 * 16)) * (255 * 256))); } + + return cp; } QT_END_NAMESPACE diff --git a/src/gui/painting/qcolorprofile_p.h b/src/gui/painting/qcolorprofile_p.h new file mode 100644 index 0000000000..ca1786ee6d --- /dev/null +++ b/src/gui/painting/qcolorprofile_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORPROFILE_P_H +#define QCOLORPROFILE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qtguiglobal_p.h> +#include <QtGui/qrgb.h> +#include <QtGui/qrgba64.h> + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QColorProfile +{ +public: + static QColorProfile *fromGamma(qreal gamma); + static QColorProfile *fromSRgb(); + + // The following methods all convert opaque or unpremultiplied colors: + + QRgba64 toLinear64(QRgb rgb32) const + { + ushort r = m_toLinear[qRed(rgb32) << 4]; + ushort g = m_toLinear[qGreen(rgb32) << 4]; + ushort b = m_toLinear[qBlue(rgb32) << 4]; + r = r + (r >> 8); + g = g + (g >> 8); + b = b + (b >> 8); + return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257); + } + + QRgb toLinear(QRgb rgb32) const + { + uchar r = (m_toLinear[qRed(rgb32) << 4] + 0x80) >> 8; + uchar g = (m_toLinear[qGreen(rgb32) << 4] + 0x80) >> 8; + uchar b = (m_toLinear[qBlue(rgb32) << 4] + 0x80) >> 8; + return qRgba(r, g, b, qAlpha(rgb32)); + } + + QRgba64 toLinear(QRgba64 rgb64) const + { + ushort r = rgb64.red(); + ushort g = rgb64.green(); + ushort b = rgb64.blue(); + r = r - (r >> 8); + g = g - (g >> 8); + b = b - (b >> 8); + r = m_toLinear[r >> 4]; + g = m_toLinear[g >> 4]; + b = m_toLinear[b >> 4]; + r = r + (r >> 8); + g = g + (g >> 8); + b = b + (b >> 8); + return QRgba64::fromRgba64(r, g, b, rgb64.alpha()); + } + + QRgb fromLinear64(QRgba64 rgb64) const + { + ushort r = rgb64.red(); + ushort g = rgb64.green(); + ushort b = rgb64.blue(); + r = r - (r >> 8); + g = g - (g >> 8); + b = b - (b >> 8); + r = (m_fromLinear[r >> 4] + 0x80) >> 8; + g = (m_fromLinear[g >> 4] + 0x80) >> 8; + b = (m_fromLinear[b >> 4] + 0x80) >> 8; + return qRgba(r, g, b, rgb64.alpha8()); + } + + QRgb fromLinear(QRgb rgb32) const + { + uchar r = (m_fromLinear[qRed(rgb32) << 4] + 0x80) >> 8; + uchar g = (m_fromLinear[qGreen(rgb32) << 4] + 0x80) >> 8; + uchar b = (m_fromLinear[qBlue(rgb32) << 4] + 0x80) >> 8; + return qRgba(r, g, b, qAlpha(rgb32)); + } + + QRgba64 fromLinear(QRgba64 rgb64) const + { + ushort r = rgb64.red(); + ushort g = rgb64.green(); + ushort b = rgb64.blue(); + r = r - (r >> 8); + g = g - (g >> 8); + b = b - (b >> 8); + r = m_fromLinear[r >> 4]; + g = m_fromLinear[g >> 4]; + b = m_fromLinear[b >> 4]; + r = r + (r >> 8); + g = g + (g >> 8); + b = b + (b >> 8); + return QRgba64::fromRgba64(r, g, b, rgb64.alpha()); + } + +private: + QColorProfile() { } + + // We translate to 0-65280 (255*256) instead to 0-65535 to make simple + // shifting an accurate conversion. + // We translate from 0-4080 (255*16) for the same speed up, and to keep + // the tables small enough to fit in most inner caches. + ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + +}; + +QT_END_NAMESPACE + +#endif // QCOLORPROFILE_P_H diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index fe1ff6603d..bd853ad934 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -43,6 +43,7 @@ #include <qstylehints.h> #include <qguiapplication.h> #include <qatomic.h> +#include <private/qcolorprofile_p.h> #include <private/qdrawhelper_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> @@ -5508,80 +5509,35 @@ static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, } } -static inline void rgbBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb, const uchar *gamma, const uchar *invgamma) +static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile) { - // Do a gray alphablend... - int da = qAlpha(*dst); - int dr = qRed(*dst); - int dg = qGreen(*dst); - int db = qBlue(*dst); + // Do a gammacorrected RGB alphablend... + const int mr = qRed(coverage); + const int mg = qGreen(coverage); + const int mb = qBlue(coverage); - if (da != 255 - ) { + const QRgba64 dlinear = colorProfile->toLinear64(*dst); - int a = qGray(coverage); - sr = qt_div_255(invgamma[sr] * a); - sg = qt_div_255(invgamma[sg] * a); - sb = qt_div_255(invgamma[sb] * a); + QRgba64 blend; + blend.setAlpha(65535); + blend.setRed (qt_div_255(slinear.red() * mr + dlinear.red() * (255 - mr))); + blend.setGreen(qt_div_255(slinear.green() * mg + dlinear.green() * (255 - mg))); + blend.setBlue (qt_div_255(slinear.blue() * mb + dlinear.blue() * (255 - mb))); - int ia = 255 - a; - dr = qt_div_255(dr * ia); - dg = qt_div_255(dg * ia); - db = qt_div_255(db * ia); - - *dst = ((a + qt_div_255((255 - a) * da)) << 24) - | ((sr + dr) << 16) - | ((sg + dg) << 8) - | ((sb + db)); - return; - } - - int mr = qRed(coverage); - int mg = qGreen(coverage); - int mb = qBlue(coverage); - - dr = gamma[dr]; - dg = gamma[dg]; - db = gamma[db]; - - int nr = qt_div_255(sr * mr + dr * (255 - mr)); - int ng = qt_div_255(sg * mg + dg * (255 - mg)); - int nb = qt_div_255(sb * mb + db * (255 - mb)); - - nr = invgamma[nr]; - ng = invgamma[ng]; - nb = invgamma[nb]; - - *dst = qRgb(nr, ng, nb); + *dst = colorProfile->fromLinear64(blend); } -#if defined(Q_OS_WIN) Q_GUI_EXPORT bool qt_needs_a8_gamma_correction = false; -static inline void grayBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb, const uint *gamma, const uchar *invgamma) +static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile) { // Do a gammacorrected gray alphablend... - int dr = qRed(*dst); - int dg = qGreen(*dst); - int db = qBlue(*dst); - - dr = gamma[dr]; - dg = gamma[dg]; - db = gamma[db]; - - int alpha = coverage; - int ialpha = 255 - alpha; - int nr = qt_div_255(sr * alpha + dr * ialpha); - int ng = qt_div_255(sg * alpha + dg * ialpha); - int nb = qt_div_255(sb * alpha + db * ialpha); + const QRgba64 dlinear = colorProfile->toLinear64(*dst); - nr = invgamma[nr]; - ng = invgamma[ng]; - nb = invgamma[nb]; + QRgba64 blend = interpolate255(slinear, coverage, dlinear, 255 - coverage); - *dst = qRgb(nr, ng, nb); + *dst = colorProfile->fromLinear64(blend); } -#endif static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, int x, int y, quint32 color, @@ -5592,21 +5548,14 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, const quint32 c = color; const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); -#if defined(Q_OS_WIN) - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) + const QColorProfile *colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); + if (!colorProfile) return; - const uint *gamma = tables->qt_pow_gamma; - const uchar *invgamma = tables->qt_pow_invgamma; - - int sr = gamma[qRed(color)]; - int sg = gamma[qGreen(color)]; - int sb = gamma[qBlue(color)]; + const QRgba64 slinear = colorProfile->toLinear64(c); bool opaque_src = (qAlpha(color) == 255); bool doGrayBlendPixel = opaque_src && qt_needs_a8_gamma_correction; -#endif if (!clip) { quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; @@ -5619,13 +5568,9 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, } else if (coverage == 255) { dest[i] = c; } else { -#if defined(Q_OS_WIN) - if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && doGrayBlendPixel - && qAlpha(dest[i]) == 255) { - grayBlendPixel(dest+i, coverage, sr, sg, sb, gamma, invgamma); - } else -#endif - { + if (doGrayBlendPixel && qAlpha(dest[i]) == 255) { + grayBlendPixel(dest+i, coverage, slinear, colorProfile); + } else { int ialpha = 255 - coverage; dest[i] = INTERPOLATE_PIXEL_255(c, coverage, dest[i], ialpha); } @@ -5660,13 +5605,9 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, } else if (coverage == 255) { dest[xp] = c; } else { -#if defined(Q_OS_WIN) - if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && doGrayBlendPixel - && qAlpha(dest[xp]) == 255) { - grayBlendPixel(dest+xp, coverage, sr, sg, sb, gamma, invgamma); - } else -#endif - { + if (doGrayBlendPixel && qAlpha(dest[xp]) == 255) { + grayBlendPixel(dest+xp, coverage, slinear, colorProfile); + } else { int ialpha = 255 - coverage; dest[xp] = INTERPOLATE_PIXEL_255(c, coverage, dest[xp], ialpha); } @@ -5700,6 +5641,11 @@ static void qt_alphamapblit_rgba8888(QRasterBuffer *rasterBuffer, } #endif +inline static int qRgbAvg(QRgb rgb) +{ + return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16; +} + static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uint *src, int mapWidth, int mapHeight, int srcStride, @@ -5707,21 +5653,13 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, { const quint32 c = color.toArgb32(); - int sr = qRed(c); - int sg = qGreen(c); - int sb = qBlue(c); int sa = qAlpha(c); - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) + const QColorProfile *colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + if (!colorProfile) return; - const uchar *gamma = tables->qt_pow_rgb_gamma; - const uchar *invgamma = tables->qt_pow_rgb_invgamma; - - sr = gamma[sr]; - sg = gamma[sg]; - sb = gamma[sb]; + const QRgba64 slinear = colorProfile->toLinear64(c); if (sa == 0) return; @@ -5735,7 +5673,13 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, if (coverage == 0xffffffff) { dst[i] = c; } else if (coverage != 0xff000000) { - rgbBlendPixel(dst+i, coverage, sr, sg, sb, gamma, invgamma); + if (dst[i] >= 0xff000000) { + rgbBlendPixel(dst+i, coverage, slinear, colorProfile); + } else { + // Give up and do a naive blend. + const int a = qRgbAvg(coverage); + dst[i] = INTERPOLATE_PIXEL_255(c, a, dst[i], 255 - a); + } } } @@ -5765,7 +5709,13 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, if (coverage == 0xffffffff) { dst[xp] = c; } else if (coverage != 0xff000000) { - rgbBlendPixel(dst+xp, coverage, sr, sg, sb, gamma, invgamma); + if (dst[xp] >= 0xff000000) { + rgbBlendPixel(dst+xp, coverage, slinear, colorProfile); + } else { + // Give up and do a naive blend. + const int a = qRgbAvg(coverage); + dst[xp] = INTERPOLATE_PIXEL_255(c, a, dst[xp], 255 - coverage); + } } } } // for (i -> line.count) diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index e537c343bb..f28b2821d6 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -345,18 +345,6 @@ struct QSpanData void adjustSpanMethods(); }; -struct QDrawHelperGammaTables -{ - explicit QDrawHelperGammaTables(qreal smoothing); - - void refresh(qreal smoothing); - - uchar qt_pow_rgb_gamma[256]; - uchar qt_pow_rgb_invgamma[256]; - uint qt_pow_gamma[256]; - uchar qt_pow_invgamma[2048]; -}; - static inline uint qt_gradient_clamp(const QGradientData *data, int ipos) { if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { |