fix Skia virtual device alpha blending (tdf#129865)

The blendBitmap()/blendAlphaBitmap() stuff coming from the OpenGL code
is some undocumented crazy stuff (probably because the VirtualDevice
alpha handling itself is rather crazy). Hopefully I've finally figured
it out to work properly for Skia too. This separate alpha handling
all over the place in VCL should be just nuked.

Change-Id: I82615a9be7064e9ade00ec4970a131a80a543c14
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/86488
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/vcl/backendtest/outputdevice/bitmap.cxx b/vcl/backendtest/outputdevice/bitmap.cxx
index 66a0a64..2e7e071 100644
--- a/vcl/backendtest/outputdevice/bitmap.cxx
+++ b/vcl/backendtest/outputdevice/bitmap.cxx
@@ -133,10 +133,13 @@
        aWriteAccess->DrawRect(tools::Rectangle(3, 3, 5, 5));
    }

    initialSetup(13, 13, constBackgroundColor, false, true);
    initialSetup(13, 13, COL_TRANSPARENT, false, true);
    mpVirtualDevice->SetFillColor(constBackgroundColor);
    mpVirtualDevice->SetLineColor(constBackgroundColor);
    mpVirtualDevice->DrawRect(maVDRectangle);
    // Leave the outer part of the device transparent, the inner part set to the background color.
    // This will test blending of VirtualDevice's "alpha" device (outer yellow rectangle
    // will be blended with transparent background, inner with the grey one).
    mpVirtualDevice->DrawRect( tools::Rectangle( Point( 3, 3 ), Size( 7, 7 )));

    Point aPoint(alignToCenter(maVDRectangle, tools::Rectangle(Point(), aBitmapSize)).TopLeft());

@@ -179,9 +182,8 @@

    std::vector<Color> aExpected
    {
        constBackgroundColor, constBackgroundColor,
        aBlendedColor, constBackgroundColor, constBackgroundColor,
        aBlendedColor, constBackgroundColor
        COL_WHITE, COL_WHITE, COL_YELLOW, constBackgroundColor,
        constBackgroundColor, aBlendedColor, constBackgroundColor
    };
    Bitmap aBitmap(rBitmapEx.GetBitmap());
    return checkRectangles(aBitmap, aExpected);
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 25980de..a4e7161 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -738,6 +738,15 @@

    assert(dynamic_cast<const SkiaSalBitmap*>(&rBitmap));
    const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rBitmap);
    // This is used by VirtualDevice in the alpha mode for the "alpha" layer which
    // is actually one-minus-alpha (opacity). Therefore white=0xff=transparent,
    // black=0x00=opaque. So the result is transparent only if both the inputs
    // are transparent. Since for blending operations white=1.0 and black=0.0,
    // kMultiply should handle exactly that (transparent*transparent=transparent,
    // opaque*transparent=opaque). And guessing from the "floor" in TYPE_BLEND in opengl's
    // combinedTextureFragmentShader.glsl, the layer is not even alpha values but
    // simply yes-or-no mask.
    // See also blendAlphaBitmap().
    drawImage(rPosAry, rSkiaBitmap.GetSkImage(), SkBlendMode::kMultiply);
    return true;
}
@@ -762,17 +771,25 @@
    const SkiaSalBitmap& rSkiaMaskBitmap = static_cast<const SkiaSalBitmap&>(rMaskBitmap);
    const SkiaSalBitmap& rSkiaAlphaBitmap = static_cast<const SkiaSalBitmap&>(rAlphaBitmap);

    // This was originally implemented for the OpenGL drawing method and it is poorly documented.
    // The source and mask bitmaps are the usual data and alpha bitmaps, and 'alpha'
    // is the "alpha" layer of the VirtualDevice (the alpha in VirtualDevice is also stored
    // as a separate bitmap). Now I understand it correctly these two alpha masks first need
    // to be combined into the actual alpha mask to be used. The formula for TYPE_BLEND
    // in opengl's combinedTextureFragmentShader.glsl is
    // "result_alpha = 1.0 - (1.0 - floor(alpha)) * mask".
    // See also blendBitmap().
    SkCanvas* aCanvas = tmpSurface->getCanvas();
    SkPaint aPaint;

    // First copy the mask as is.
    aPaint.setBlendMode(SkBlendMode::kSrc);
    aCanvas->drawImage(rSkiaSourceBitmap.GetSkImage(), 0, 0, &aPaint);

    // Apply cumulatively both the bitmap alpha and the device alpha.
    aPaint.setBlendMode(SkBlendMode::kDstOut); // VCL alpha is one-minus-alpha
    aCanvas->drawImage(rSkiaMaskBitmap.GetAlphaSkImage(), 0, 0, &aPaint);
    aPaint.setBlendMode(SkBlendMode::kDstOut); // VCL alpha is one-minus-alpha
    // Do the "1 - alpha" (no idea how to do "floor", but hopefully not needed in practice).
    aPaint.setBlendMode(SkBlendMode::kDstOut);
    aCanvas->drawImage(rSkiaAlphaBitmap.GetAlphaSkImage(), 0, 0, &aPaint);
    // And now draw the bitmap with "1 - x", where x is the "( 1 - alpha ) * mask".
    aPaint.setBlendMode(SkBlendMode::kSrcOut);
    aCanvas->drawImage(rSkiaSourceBitmap.GetSkImage(), 0, 0, &aPaint);

    drawImage(rPosAry, tmpSurface->makeImageSnapshot());
    return true;