From 21d3771922c1731534db72915ad9fd54a063ace3 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 24 Nov 2016 10:46:03 +0100 Subject: 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 --- src/gui/painting/qdrawhelper.cpp | 306 ++++++++++++++++++++++++++++++++++----- 1 file changed, 270 insertions(+), 36 deletions(-) (limited to 'src/gui/painting/qdrawhelper.cpp') 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(clip)->initialize(); + for (int yp = top; ypm_clipLines[yp]; + + for (int i=0; i(x, clip.x); + int end = qMin(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 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(clip)->initialize(); + for (int yp = top; ypm_clipLines[yp]; + + for (int i=0; i(x, clip.x); + int end = qMin(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 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(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, - 0, - 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, qt_rectfill_rgb30 }, // Format_A2BGR30_Premultiplied @@ -6030,8 +6260,8 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = blend_color_generic_rgb64, blend_src_generic_rgb64, qt_bitmapblit_rgb30, - 0, - 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, qt_rectfill_rgb30 }, // Format_RGB30 @@ -6039,8 +6269,8 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = blend_color_generic_rgb64, blend_src_generic_rgb64, qt_bitmapblit_rgb30, - 0, - 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, qt_rectfill_rgb30 }, // Format_A2RGB30_Premultiplied @@ -6048,22 +6278,26 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = blend_color_generic_rgb64, blend_src_generic_rgb64, qt_bitmapblit_rgb30, - 0, - 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, qt_rectfill_rgb30 }, // 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 }, }; -- cgit v1.2.3