summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2020-01-30 13:04:23 +0100
committerLuboš Luňák <l.lunak@collabora.com>2020-01-30 15:14:01 +0100
commit7f56bbe3a09265a62792dc0624fdb44f8c176172 (patch)
tree7855dd0b57be35df23800564cb9a7bd5728373d2
parent4ce8120f1e53f7b81e653b01d141643013bc69ab (diff)
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@collabora.com>
-rw-r--r--vcl/inc/skia/win/gdiimpl.hxx3
-rw-r--r--vcl/skia/win/gdiimpl.cxx73
2 files changed, 59 insertions, 17 deletions
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)
{