summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2016-11-24 10:46:03 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-02-03 16:24:24 +0000
commit21d3771922c1731534db72915ad9fd54a063ace3 (patch)
tree3bf21e0df5c59b33e9bb6593d68f7fc728414318 /src/gui/painting
parentff34633bd0ed169b635b520f33740a31bbafaf46 (diff)
Implement generic text blend routines
Implements a generic version of alphamapblit and alphargbblit so we can have gamma-corrected blending of text outside of only RGB32 formats. Change-Id: Ide960276357546558dd713aab66d2af0f2a09a2a Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
Diffstat (limited to 'src/gui/painting')
-rw-r--r--src/gui/painting/qdrawhelper.cpp306
1 files changed, 270 insertions, 36 deletions
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index b7d3920599..fab7beb74f 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -5543,6 +5543,104 @@ inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
map, mapWidth, mapHeight, mapStride);
}
+static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile)
+{
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[x] = src;
+ } else {
+ QRgba64 dstColor = dest[x];
+ if (colorProfile) {
+ if (dstColor.isOpaque())
+ dstColor = colorProfile->toLinear(dstColor);
+ else if (!dstColor.isTransparent())
+ dstColor = colorProfile->toLinear(dstColor.unpremultiplied()).premultiplied();
+ }
+
+ dstColor = interpolate255(srcLinear, coverage, dstColor, 255 - coverage);
+ if (colorProfile) {
+ if (dstColor.isOpaque())
+ dstColor = colorProfile->fromLinear(dstColor);
+ else if (!dstColor.isTransparent())
+ dstColor = colorProfile->fromLinear(dstColor.unpremultiplied()).premultiplied();
+ }
+ dest[x] = dstColor;
+ }
+}
+
+static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
+ int x, int y, const QRgba64 &color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip, bool useGammaCorrection)
+{
+ if (color.isTransparent())
+ return;
+
+ const QColorProfile *colorProfile = nullptr;
+
+ if (useGammaCorrection)
+ colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
+
+ QRgba64 srcColor = color;
+ if (colorProfile) {
+ if (color.isOpaque())
+ srcColor = colorProfile->toLinear(srcColor);
+ else
+ srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied();
+ }
+
+ QRgba64 buffer[buffer_size];
+ const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
+ const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
+
+ if (!clip) {
+ for (int ly = 0; ly < mapHeight; ++ly) {
+ int i = x;
+ int length = mapWidth;
+ while (length > 0) {
+ int l = qMin(buffer_size, length);
+ QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
+ for (int j=0; j < l; ++j) {
+ const int coverage = map[j + (i - x)];
+ alphamapblend_generic(coverage, dest, j, srcColor, color, colorProfile);
+ }
+ destStore64(rasterBuffer, i, y + ly, buffer, l);
+ length -= l;
+ i += l;
+ }
+ map += mapStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ map += (top - y) * mapStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+ Q_ASSERT(clip.len <= buffer_size);
+ QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, clip.len);
+
+ for (int xp=start; xp<end; ++xp) {
+ const int coverage = map[xp - x];
+ alphamapblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
+ }
+ destStore64(rasterBuffer, start, clip.y, dest, clip.len);
+ } // for (i -> line.count)
+ map += mapStride;
+ } // for (yp -> bottom)
+ }
+}
+
static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor)
{
if (coverage == 0) {
@@ -5559,8 +5657,13 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *map,
int mapWidth, int mapHeight, int mapStride,
- const QClipData *clip, bool /*useGammaCorrection*/)
+ const QClipData *clip, bool useGammaCorrection)
{
+ if (useGammaCorrection) {
+ qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection);
+ return;
+ }
+
const quint16 c = color.toRgb16();
if (!clip) {
@@ -5719,19 +5822,119 @@ static void qt_alphamapblit_rgba8888(QRasterBuffer *rasterBuffer,
}
#endif
-inline static int qRgbAvg(QRgb rgb)
+static inline int qRgbAvg(QRgb rgb)
{
return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16;
}
+static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile)
+{
+ if (coverage == 0xff000000) {
+ // nothing
+ } else if (coverage == 0xffffffff) {
+ dest[x] = src;
+ } else {
+ QRgba64 dstColor = dest[x];
+ if (dstColor.isOpaque()) {
+ if (colorProfile)
+ dstColor = colorProfile->toLinear(dstColor);
+ dstColor = rgbBlend(dstColor, srcLinear, coverage);
+ if (colorProfile)
+ dstColor = colorProfile->fromLinear(dstColor);
+ dest[x] = dstColor;
+ } else {
+ // Give up and do a gray alphablend.
+ if (colorProfile && !dstColor.isTransparent())
+ dstColor = colorProfile->toLinear(dstColor.unpremultiplied()).premultiplied();
+ const int a = qRgbAvg(coverage);
+ dstColor = interpolate255(srcLinear, coverage, dstColor, 255 - a);
+ if (colorProfile && !dstColor.isTransparent())
+ dstColor = colorProfile->fromLinear(dstColor.unpremultiplied()).premultiplied();
+ dest[x] = dstColor;
+ }
+ }
+}
+
+static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
+ int x, int y, const QRgba64 &color,
+ const uint *src, int mapWidth, int mapHeight, int srcStride,
+ const QClipData *clip, bool useGammaCorrection)
+{
+ if (color.isTransparent())
+ return;
+
+ const QColorProfile *colorProfile = nullptr;
+
+ if (useGammaCorrection)
+ colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
+
+ QRgba64 srcColor = color;
+ if (colorProfile) {
+ if (color.isOpaque())
+ srcColor = colorProfile->toLinear(srcColor);
+ else
+ srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied();
+ }
+
+ QRgba64 buffer[buffer_size];
+ const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
+ const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
+
+ if (!clip) {
+ for (int ly = 0; ly < mapHeight; ++ly) {
+ int i = x;
+ int length = mapWidth;
+ while (length > 0) {
+ int l = qMin(buffer_size, length);
+ QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
+ for (int j=0; j < l; ++j) {
+ const uint coverage = src[j + (i - x)];
+ alphargbblend_generic(coverage, dest, j, srcColor, color, colorProfile);
+ }
+ destStore64(rasterBuffer, i, y + ly, buffer, l);
+ length -= l;
+ i += l;
+ }
+ src += srcStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ src += (top - y) * srcStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+ Q_ASSERT(clip.len <= buffer_size);
+ QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, clip.len);
+
+ for (int xp=start; xp<end; ++xp) {
+ const uint coverage = src[xp - x];
+ alphargbblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
+ }
+ destStore64(rasterBuffer, start, clip.y, dest, clip.len);
+ } // for (i -> line.count)
+ src += srcStride;
+ } // for (yp -> bottom)
+ }
+}
+
static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uint *src, int mapWidth, int mapHeight, int srcStride,
const QClipData *clip, bool useGammaCorrection)
{
- const quint32 c = color.toArgb32();
+ if (color.isTransparent())
+ return;
- int sa = qAlpha(c);
+ const quint32 c = color.toArgb32();
const QColorProfile *colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
if (!colorProfile)
@@ -5739,9 +5942,6 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
const QRgba64 slinear = useGammaCorrection ? colorProfile->toLinear64(c) : color;
- if (sa == 0)
- return;
-
if (!clip) {
quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
@@ -5754,9 +5954,12 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
if (dst[i] >= 0xff000000) {
rgbBlendPixel(dst+i, coverage, slinear, colorProfile, useGammaCorrection);
} else {
- // Give up and do a naive blend.
+ // Give up and do a gray blend.
const int a = qRgbAvg(coverage);
- dst[i] = INTERPOLATE_PIXEL_255(c, a, dst[i], 255 - a);
+ if (useGammaCorrection)
+ grayBlendPixel(dst+i, a, slinear, colorProfile);
+ else
+ dst[i] = INTERPOLATE_PIXEL_255(c, a, dst[i], 255 - a);
}
}
}
@@ -5790,9 +5993,12 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
if (dst[xp] >= 0xff000000) {
rgbBlendPixel(dst+xp, coverage, slinear, colorProfile, useGammaCorrection);
} else {
- // Give up and do a naive blend.
+ // Give up and do a gray blend.
const int a = qRgbAvg(coverage);
- dst[xp] = INTERPOLATE_PIXEL_255(c, a, dst[xp], 255 - coverage);
+ if (useGammaCorrection)
+ grayBlendPixel(dst+xp, a, slinear, colorProfile);
+ else
+ dst[xp] = INTERPOLATE_PIXEL_255(c, a, dst[xp], 255 - coverage);
}
}
}
@@ -5926,56 +6132,80 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
qt_gradient_quint16,
qt_bitmapblit_quint16,
qt_alphamapblit_quint16,
- 0,
+ qt_alphargbblit_generic,
qt_rectfill_quint16
},
// Format_ARGB8565_Premultiplied
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_RGB666
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_ARGB6666_Premultiplied
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_RGB555
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_ARGB8555_Premultiplied
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_RGB888
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_RGB444
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_ARGB4444_Premultiplied
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0, 0
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
+ 0
},
// Format_RGBX8888
{
@@ -5985,9 +6215,9 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
qt_alphamapblit_rgba8888,
#else
- 0,
+ qt_alphamapblit_generic,
#endif
- 0,
+ qt_alphargbblit_generic,
qt_rectfill_rgba
},
// Format_RGBA8888
@@ -5998,9 +6228,9 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
qt_alphamapblit_rgba8888,
#else
- 0,
+ qt_alphamapblit_generic,
#endif
- 0,
+ qt_alphargbblit_generic,
qt_rectfill_nonpremul_rgba
},
// Format_RGB8888_Premultiplied
@@ -6011,9 +6241,9 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
qt_alphamapblit_rgba8888,
#else
- 0,
+ qt_alphamapblit_generic,
#endif
- 0,
+ qt_alphargbblit_generic,
qt_rectfill_rgba
},
// Format_BGR30
@@ -6021,8 +6251,8 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
blend_color_generic_rgb64,
blend_src_generic_rgb64,
qt_bitmapblit_rgb30<PixelOrderBGR>,
- 0,
- 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
qt_rectfill_rgb30<PixelOrderBGR>
},
// Format_A2BGR30_Premultiplied
@@ -6030,8 +6260,8 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
blend_color_generic_rgb64,
blend_src_generic_rgb64,
qt_bitmapblit_rgb30<PixelOrderBGR>,
- 0,
- 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
qt_rectfill_rgb30<PixelOrderBGR>
},
// Format_RGB30
@@ -6039,8 +6269,8 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
blend_color_generic_rgb64,
blend_src_generic_rgb64,
qt_bitmapblit_rgb30<PixelOrderRGB>,
- 0,
- 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
qt_rectfill_rgb30<PixelOrderRGB>
},
// Format_A2RGB30_Premultiplied
@@ -6048,22 +6278,26 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
blend_color_generic_rgb64,
blend_src_generic_rgb64,
qt_bitmapblit_rgb30<PixelOrderRGB>,
- 0,
- 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
qt_rectfill_rgb30<PixelOrderRGB>
},
// Format_Alpha8
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0,
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
qt_rectfill_alpha
},
// Format_Grayscale8
{
blend_color_generic,
blend_src_generic,
- 0, 0, 0,
+ 0,
+ qt_alphamapblit_generic,
+ qt_alphargbblit_generic,
qt_rectfill_gray
},
};