[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-cd' - kit/Kit.cpp

Ashod Nakashian ashod.nakashian at collabora.co.uk
Fri Dec 15 02:11:54 UTC 2017


 kit/Kit.cpp |  143 +++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 104 insertions(+), 39 deletions(-)

New commits:
commit 1866fd8c0878320bb737788cadb7c426a1a53bff
Author: Ashod Nakashian <ashod.nakashian at 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 <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index bdb3ae79..38b91111 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -435,7 +435,6 @@ public:
         , _font("Liberation Sans")
         , _width(0)
         , _height(0)
-        , _color{64, 64, 64}
         , _alphaLevel(0.2)
         , _pixmap(nullptr)
     {
@@ -451,7 +450,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;
@@ -461,42 +460,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)
@@ -518,13 +523,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;
     }
 
@@ -534,7 +600,6 @@ private:
     std::string _font;
     int _width;
     int _height;
-    unsigned char _color[3];
     double _alphaLevel;
     unsigned char* _pixmap;
 };
@@ -857,7 +922,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);
 
             if (hash != 0 && tiles[tileIndex].getOldHash() == hash)
@@ -869,16 +937,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))
             {
                 //FIXME: Return error.


More information about the Libreoffice-commits mailing list