finally fix Skia Windows widget drawing (tdf#129416)

So much time wasted just because c6b66646870cb2bf couldn't be bothered
spending a minute or two explaining the weird black/white alpha hack
that it turns out is not even necessary as the resulting image
is incidentally in the premultiplied alpha format.

Change-Id: I810458a670b2c0c8047118f55f58bf588a37f9f1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/86569
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
This commit is contained in:
Luboš Luňák
2020-01-10 19:05:20 +01:00
parent 454e3eeaf3
commit 202146901b
2 changed files with 17 additions and 55 deletions

View File

@@ -31,9 +31,8 @@ public:
virtual bool wantsTextColorWhite() const override { return true; }
sk_sp<SkImage> getAsImage() const;
sk_sp<SkImage> getAsImage(bool fromPremultiplied = false) const;
sk_sp<SkImage> getAsMaskImage() const;
sk_sp<SkImage> getAsImageDiff(const SkiaCompatibleDC& other) const;
struct Texture;
};

View File

@@ -97,15 +97,21 @@ 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));
sk_sp<SkImage> image = static_cast<SkiaCompatibleDC&>(rWhite).getAsImageDiff(
static_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);
preDraw();
mSurface->getCanvas()->drawImage(image, nX, nY);
postDraw();
@@ -207,12 +213,13 @@ sk_sp<SkImage> SkiaCompatibleDC::getAsMaskImage() const
return surface->makeImageSnapshot();
}
sk_sp<SkImage> SkiaCompatibleDC::getAsImage() const
sk_sp<SkImage> SkiaCompatibleDC::getAsImage(bool fromPremultiplied) const
{
SkBitmap tmpBitmap;
if (!tmpBitmap.installPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight,
kBGRA_8888_SkColorType, kUnpremul_SkAlphaType),
mpData, maRects.mnSrcWidth * 4))
if (!tmpBitmap.installPixels(
SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight, kBGRA_8888_SkColorType,
fromPremultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType),
mpData, maRects.mnSrcWidth * 4))
abort();
tmpBitmap.setImmutable();
sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(tmpBitmap.width(), tmpBitmap.height());
@@ -232,50 +239,6 @@ sk_sp<SkImage> SkiaCompatibleDC::getAsImage() const
return surface->makeImageSnapshot();
}
sk_sp<SkImage> SkiaCompatibleDC::getAsImageDiff(const SkiaCompatibleDC& other) const
{
assert(maRects.mnSrcWidth == other.maRects.mnSrcWidth
|| maRects.mnSrcHeight == other.maRects.mnSrcHeight);
SkBitmap tmpBitmap;
if (!tmpBitmap.tryAllocPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight,
kBGRA_8888_SkColorType, kUnpremul_SkAlphaType),
maRects.mnSrcWidth * 4))
abort();
// Native widgets are drawn twice on black/white background to synthetize alpha
// (commit c6b66646870cb2bffaa73565affcf80bf74e0b5c).
// 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* otherSrc = other.mpData;
uint32_t* end = dest + tmpBitmap.width() * tmpBitmap.height();
while (dest < end)
{
uint32_t alpha = 255 - abs(int(*src >> 24) - int(*otherSrc >> 24));
*dest = (*src & 0x00ffffff) | (alpha << 24);
++dest;
++src;
++otherSrc;
}
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)
{