kit/Kit.cpp | 143 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 39 deletions(-)
New commits: commit e0a0fe823d23efd457ef92ffd167c3c74c803a7b Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> Date: Sun Dec 10 21:54:07 2017 -0500 wsd: improve watermark visibility on dark and grey backgrounds The watermark text is now rendered on a blurred version of itself, using inverted colors. This makes it look sharp on light backgrounds, but inverted in a blurry haze when on dark backgrounds, thereby being readable on top of any background. Change-Id: Ia6daf987674c484980f1fdec4f74e579ed87c213 Reviewed-on: https://gerrit.libreoffice.org/46491 Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> Tested-by: Ashod Nakashian <ashnak...@gmail.com> Reviewed-on: https://gerrit.libreoffice.org/46526 Reviewed-by: Jan Holesovsky <ke...@collabora.com> Tested-by: Jan Holesovsky <ke...@collabora.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 5c6eb920..5cb82801 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -489,7 +489,6 @@ public: , _font("Liberation Sans") , _width(0) , _height(0) - , _color{64, 64, 64} , _alphaLevel(0.2) , _pixmap(nullptr) { @@ -505,7 +504,7 @@ public: int offsetX, int offsetY, int tilesPixmapWidth, int tilesPixmapHeight, int tileWidth, int tileHeight, - LibreOfficeKitTileMode mode) + LibreOfficeKitTileMode /*mode*/) { // set requested watermark size a little bit smaller than tile size int width = tileWidth * 0.9; @@ -515,42 +514,48 @@ public: if (pixmap && tilePixmap) { - unsigned int pixmapSize = tilesPixmapWidth * tilesPixmapHeight * 4; - int maxX = std::min(tileWidth, _width); - int maxY = std::min(tileHeight, _height); - // center watermark + const int maxX = std::min(tileWidth, _width); + const int maxY = std::min(tileHeight, _height); offsetX += (tileWidth - maxX) / 2; offsetY += (tileHeight - maxY) / 2; - for (int y = 0; y < maxY; ++y) - { - for (int x = 0; x < maxX; ++x) - { - unsigned int i = (y * _width + x) * 4; - unsigned int alpha = pixmap[i + 3]; - if (alpha) - { - for (int h = 0; h < 3; ++h) - { - unsigned int j = ((y + offsetY) * tilesPixmapWidth + (x + offsetX)) * 4 + h; - if (j < pixmapSize) - { - unsigned int color = (mode == LOK_TILEMODE_BGRA) ? _color[2 - h] : _color[h]; - - // original alpha blending for smoothing text edges - color = ((color * alpha) + tilePixmap[j] * (255 - alpha)) / 255; - // blending between document tile and watermark - tilePixmap[j] = color * _alphaLevel + tilePixmap[j] * (1 - _alphaLevel); - } - } - } - } - } + alphaBlend(pixmap, _width, _height, offsetX, offsetY, tilePixmap, tilesPixmapWidth, tilesPixmapHeight); } } private: + /// Alpha blend 'pixel_count' pixels from 'from' over the 'to'. + void alphaBlend(const unsigned char* from, int from_width, int from_height, int from_offset_x, int from_offset_y, + unsigned char* to, int to_width, int to_height) + { + for (int to_y = from_offset_y, from_y = 0; (to_y < to_height) && (from_y < from_height) ; ++to_y, ++from_y) + for (int to_x = from_offset_x, from_x = 0; (to_x < to_width) && (from_x < from_width); ++to_x, ++from_x) + { + const unsigned char* f = from + 4 * (from_y * from_width + from_x); + double src_r = f[0]; + double src_g = f[1]; + double src_b = f[2]; + double src_a = f[3] / 255.0; + + unsigned char* t = to + 4 * (to_y * to_width + to_x); + double dst_r = t[0]; + double dst_g = t[1]; + double dst_b = t[2]; + double dst_a = t[3] / 255.0; + + double out_a = src_a + dst_a * (1.0 - src_a); + unsigned char out_r = src_r + dst_r * (1.0 - src_a); + unsigned char out_g = src_g + dst_g * (1.0 - src_a); + unsigned char out_b = src_b + dst_b * (1.0 - src_a); + + t[0] = out_r; + t[1] = out_g; + t[2] = out_b; + t[3] = static_cast<unsigned char>(out_a * 255.0); + } + } + const unsigned char* getPixmap(int width, int height) { if (_pixmap && width == _width && height == _height) @@ -572,13 +577,74 @@ private: // are always set to 0 (black) and the alpha level is 0 everywhere // except on the text area; the alpha level take into account of // performing anti-aliasing over the text edges. - _pixmap = _loKitDoc->renderFont(_font.c_str(), _text.c_str(), &_width, &_height); + unsigned char* text = _loKitDoc->renderFont(_font.c_str(), _text.c_str(), &_width, &_height); - if (!_pixmap) + if (!text) { LOG_ERR("Watermark: rendering failed."); } + const unsigned int pixel_count = width * height * 4; + + // Create the blurred background; first a white text + _pixmap = static_cast<unsigned char*>(malloc(pixel_count)); + unsigned char* from = text; + unsigned char* to = _pixmap; + for (; to < _pixmap + pixel_count; from += 4, to += 4) + { + // Pre-multiplied alpha! + const double alpha = from[3] / 255.0; + to[0] = 0xff * alpha; + to[1] = 0xff * alpha; + to[2] = 0xff * alpha; + to[3] = from[3]; + } + + // Use box blur, which is fast, though crude. + unsigned char* buffer = static_cast<unsigned char*>(malloc(pixel_count)); + memcpy(buffer, _pixmap, pixel_count); + + // Repeat an even number of times to smooth out. + const int r = 2; + const double weight = (r+1) * (r+1); + for (int y = r; y < height - r; ++y) + { + for (int x = r; x < width - r; ++x) + { + double t = 0; + for (int ky = y - r; ky <= y + r; ++ky) + { + for (int kx = x - r; kx <= x + r; ++kx) + { + t += buffer[4 * (ky * width + kx) + 3]; + } + } + + // Clamp the result. + double avg = t / weight; + if (avg > 255.0) + avg = 255.0; + const double alpha = avg / 255.0; + _pixmap[4 * (y * width + x) + 0] = 0xff * alpha; + _pixmap[4 * (y * width + x) + 1] = 0xff * alpha; + _pixmap[4 * (y * width + x) + 2] = 0xff * alpha; + _pixmap[4 * (y * width + x) + 3] = static_cast<unsigned char>(avg < 255.0 ? avg : 255); + } + } + + // Now copy text over the blur + alphaBlend(text, _width, _height, 0, 0, _pixmap, _width, _height); + + // No longer needed. + std::free(buffer); + std::free(text); + + // Make the resulting pixmap semi-transparent + for (unsigned char* p = _pixmap; p < _pixmap + pixel_count; p++) + { + *p = static_cast<unsigned char>(*p * _alphaLevel); + } + return _pixmap; } @@ -588,7 +654,6 @@ private: std::string _font; int _width; int _height; - unsigned char _color[3]; double _alphaLevel; unsigned char* _pixmap; }; @@ -918,7 +983,10 @@ public: const auto pixelWidth = tileCombined.getWidth(); const auto pixelHeight = tileCombined.getHeight(); - const uint64_t hash = Png::hashSubBuffer(pixmap.data(), positionX * pixelWidth, positionY * pixelHeight, + const int offsetX = positionX * pixelWidth; + const int offsetY = positionY * pixelHeight; + + const uint64_t hash = Png::hashSubBuffer(pixmap.data(), offsetX, offsetY, pixelWidth, pixelHeight, pixmapWidth, pixmapHeight); TileWireId wireId = _pngCache.hashToWireId(hash); @@ -932,16 +1000,13 @@ public: continue; } - int offsetX = positionX * pixelWidth; - int offsetY = positionY * pixelHeight; - if (_docWatermark) _docWatermark->blending(pixmap.data(), offsetX, offsetY, pixmapWidth, pixmapHeight, - tileCombined.getWidth(), tileCombined.getHeight(), + pixelWidth, pixelHeight, mode); - if (!_pngCache.encodeSubBufferToPNG(pixmap.data(), positionX * pixelWidth, positionY * pixelHeight, + if (!_pngCache.encodeSubBufferToPNG(pixmap.data(), offsetX, offsetY, pixelWidth, pixelHeight, pixmapWidth, pixmapHeight, output, mode, hash, wireId, oldWireId)) { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits