vcl/backendtest/outputdevice/bitmap.cxx | 12 +++++++----- vcl/skia/gdiimpl.cxx | 29 +++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 11 deletions(-)
New commits: commit 3aa151e4de4c02b094acb6fa8a919cc86051a6fd Author: Luboš Luňák <l.lu...@collabora.com> AuthorDate: Thu Jan 9 13:21:04 2020 +0100 Commit: Luboš Luňák <l.lu...@collabora.com> CommitDate: Thu Jan 9 15:25:25 2020 +0100 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.lu...@collabora.com> diff --git a/vcl/backendtest/outputdevice/bitmap.cxx b/vcl/backendtest/outputdevice/bitmap.cxx index 66a0a64adf49..2e7e0713294a 100644 --- a/vcl/backendtest/outputdevice/bitmap.cxx +++ b/vcl/backendtest/outputdevice/bitmap.cxx @@ -133,10 +133,13 @@ BitmapEx OutputDeviceTestBitmap::setupDrawBlend() 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 @@ TestResult OutputDeviceTestBitmap::checkBlend(BitmapEx& rBitmapEx) 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 25980de0cc60..a4e716122c3a 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -738,6 +738,15 @@ bool SkiaSalGraphicsImpl::blendBitmap(const SalTwoRect& rPosAry, const SalBitmap 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 @@ bool SkiaSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect& rPosAry, 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; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits