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>
diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx
index dabd56a..daf41e8 100644
--- a/vcl/inc/skia/win/gdiimpl.hxx
+++ b/vcl/inc/skia/win/gdiimpl.hxx
@@ -31,9 +31,8 @@

    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;
};
diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index b3f538b..437e7c7 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -97,15 +97,21 @@
    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 @@
    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 @@
    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)
{