[Libreoffice-commits] core.git: vcl/inc vcl/skia

Luboš Luňák (via logerrit) logerrit at kemper.freedesktop.org
Thu Jan 30 14:14:37 UTC 2020


 vcl/inc/skia/win/gdiimpl.hxx |    3 +
 vcl/skia/win/gdiimpl.cxx     |   73 +++++++++++++++++++++++++++++++++----------
 2 files changed, 59 insertions(+), 17 deletions(-)

New commits:
commit 7f56bbe3a09265a62792dc0624fdb44f8c176172
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Thu Jan 30 13:04:23 2020 +0100
Commit:     Luboš Luňák <l.lunak at collabora.com>
CommitDate: Thu Jan 30 15:14:01 2020 +0100

    again finally(?) fix Skia Windows widget drawing (tdf#130051)
    
    I was correct to see in 202146901b6fbab92 that the black bitmap was
    in premultiplied alpha, but what I missed what that some controls
    keep the alpha set at zero (and only some work properly). So go back
    to the algorithm of synthetizing alpha from the red channel, compute
    it properly (before it was using alpha channel by mistake), and
    treat the data properly as premultiplied.
    This hopefully finally makes all Windows control widgets work.
    
    Change-Id: If2716eb8ecf623fcc57ee1db5904edfaee679aa9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/87734
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lunak at collabora.com>

diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx
index 69ee6ba475af..5e8aee2e4523 100644
--- a/vcl/inc/skia/win/gdiimpl.hxx
+++ b/vcl/inc/skia/win/gdiimpl.hxx
@@ -32,8 +32,9 @@ public:
 
     virtual bool wantsTextColorWhite() const override { return true; }
 
-    sk_sp<SkImage> getAsImage(bool fromPremultiplied = false) const;
+    sk_sp<SkImage> getAsImage() const;
     sk_sp<SkImage> getAsMaskImage() const;
+    sk_sp<SkImage> getAsImageDiff(const SkiaCompatibleDC& white) const;
 
     struct Texture;
     struct PackedTexture;
diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index e9889415cc5e..aec2f22e5bb3 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -80,21 +80,15 @@ bool WinSkiaSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey const&
     return true;
 }
 
-bool WinSkiaSalGraphicsImpl::RenderAndCacheNativeControl(CompatibleDC& /*rWhite*/,
-                                                         CompatibleDC& rBlack, int nX, int nY,
+bool WinSkiaSalGraphicsImpl::RenderAndCacheNativeControl(CompatibleDC& rWhite, CompatibleDC& rBlack,
+                                                         int nX, int nY,
                                                          ControlCacheKey& aControlCacheKey)
 {
-    // assert(dynamic_cast<SkiaCompatibleDC*>(&rWhite));
+    assert(dynamic_cast<SkiaCompatibleDC*>(&rWhite));
     assert(dynamic_cast<SkiaCompatibleDC*>(&rBlack));
 
-    // Native widgets are drawn twice on black/white background, which comes from an OpenGL
-    // commit c6b66646870cb2bffaa73565affcf80bf74e0b5c, where it is used to synthetize alpha.
-    // But getting the Windows theming API to draw into an empty area (fully transparent)
-    // actually results in the widget being in the premultiplied alpha format (and I have no
-    // idea why the OpenGL code uses the weird undocumented pixel diffing it does, probably
-    // the author did not realize this). Simply use the black variant as premultiplied data.
-    // TODO Remove the white variant completely once OpenGL code is removed.
-    sk_sp<SkImage> image = static_cast<SkiaCompatibleDC&>(rBlack).getAsImage(true);
+    sk_sp<SkImage> image = static_cast<SkiaCompatibleDC&>(rBlack).getAsImageDiff(
+        static_cast<SkiaCompatibleDC&>(rWhite));
     preDraw();
     mSurface->getCanvas()->drawImage(image, nX, nY);
     postDraw();
@@ -204,13 +198,12 @@ sk_sp<SkImage> SkiaCompatibleDC::getAsMaskImage() const
     return surface->makeImageSnapshot();
 }
 
-sk_sp<SkImage> SkiaCompatibleDC::getAsImage(bool fromPremultiplied) const
+sk_sp<SkImage> SkiaCompatibleDC::getAsImage() const
 {
     SkBitmap tmpBitmap;
-    if (!tmpBitmap.installPixels(
-            SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight, kBGRA_8888_SkColorType,
-                              fromPremultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType),
-            mpData, maRects.mnSrcWidth * 4))
+    if (!tmpBitmap.installPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight,
+                                                   kBGRA_8888_SkColorType, kUnpremul_SkAlphaType),
+                                 mpData, maRects.mnSrcWidth * 4))
         abort();
     tmpBitmap.setImmutable();
     sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(tmpBitmap.width(), tmpBitmap.height());
@@ -230,6 +223,54 @@ sk_sp<SkImage> SkiaCompatibleDC::getAsImage(bool fromPremultiplied) const
     return surface->makeImageSnapshot();
 }
 
+sk_sp<SkImage> SkiaCompatibleDC::getAsImageDiff(const SkiaCompatibleDC& white) const
+{
+    assert(maRects.mnSrcWidth == white.maRects.mnSrcWidth
+           || maRects.mnSrcHeight == white.maRects.mnSrcHeight);
+    SkBitmap tmpBitmap;
+    if (!tmpBitmap.tryAllocPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight,
+                                                    kBGRA_8888_SkColorType, kPremul_SkAlphaType),
+                                  maRects.mnSrcWidth * 4))
+        abort();
+    // Native widgets are drawn twice on black/white background to synthetize alpha
+    // (commit c6b66646870cb2bffaa73565affcf80bf74e0b5c). The problem is that
+    // most widgets when drawn on transparent background are drawn properly (and the result
+    // is in premultiplied alpha format), some such as "Edit" (used by ControlType::Editbox)
+    // keep the alpha channel as transparent. Therefore the alpha is actually computed
+    // from the difference in the premultiplied red channels when drawn one black and on white.
+    // Alpha is computed as "alpha = 1.0 - abs(black.red - white.red)".
+    // TODO I doubt this can be done using Skia, so do it manually here. Fortunately
+    // the bitmaps should be fairly small and are cached.
+    uint32_t* dest = tmpBitmap.getAddr32(0, 0);
+    assert(dest == tmpBitmap.getPixels());
+    const sal_uInt32* src = mpData;
+    const sal_uInt32* whiteSrc = white.mpData;
+    uint32_t* end = dest + tmpBitmap.width() * tmpBitmap.height();
+    while (dest < end)
+    {
+        uint32_t alpha = 255 - abs(int(*src & 0xff) - int(*whiteSrc & 0xff));
+        *dest = (*src & 0x00ffffff) | (alpha << 24);
+        ++dest;
+        ++src;
+        ++whiteSrc;
+    }
+    tmpBitmap.notifyPixelsChanged();
+    tmpBitmap.setImmutable();
+    sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(tmpBitmap.width(), tmpBitmap.height());
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->save();
+    // The data we got is upside-down.
+    SkMatrix matrix;
+    matrix.preTranslate(0, tmpBitmap.height());
+    matrix.setConcat(matrix, SkMatrix::MakeScale(1, -1));
+    canvas->concat(matrix);
+    canvas->drawBitmap(tmpBitmap, 0, 0, &paint);
+    canvas->restore();
+    return surface->makeImageSnapshot();
+}
+
 SkiaControlsCache::SkiaControlsCache()
     : cache(200)
 {


More information about the Libreoffice-commits mailing list