canvas/source/directx/dx_canvasbitmap.cxx | 2 canvas/source/directx/dx_vcltools.cxx | 18 canvas/source/vcl/canvasbitmaphelper.cxx | 2 cppcanvas/source/mtfrenderer/transparencygroupaction.cxx | 2 cui/source/dialogs/about.cxx | 26 - drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx | 6 drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx | 1 drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx | 2 drawinglayer/source/processor2d/vclhelperbufferdevice.cxx | 1 drawinglayer/source/tools/converters.cxx | 27 - forms/source/component/imgprod.cxx | 4 include/tools/color.hxx | 4 include/vcl/bitmap.hxx | 31 + include/vcl/virdev.hxx | 4 sd/qa/unit/PNGExportTests.cxx | 2 sfx2/source/dialog/infobar.cxx | 2 slideshow/source/engine/shapes/gdimtftools.cxx | 6 svx/source/svdraw/svdfmtf.cxx | 5 toolkit/source/helper/vclunohelper.cxx | 1 vcl/backendtest/outputdevice/bitmap.cxx | 8 vcl/headless/BitmapHelper.cxx | 14 vcl/headless/CairoCommon.cxx | 15 vcl/inc/bitmap/ScanlineTools.hxx | 4 vcl/inc/headless/BitmapHelper.hxx | 3 vcl/inc/headless/CairoCommon.hxx | 2 vcl/qa/cppunit/BackendTest.cxx | 2 vcl/qa/cppunit/BitmapExTest.cxx | 3 vcl/qa/cppunit/ScanlineToolsTest.cxx | 10 vcl/qa/cppunit/canvasbitmaptest.cxx | 28 - vcl/qa/cppunit/png/PngFilterTest.cxx | 32 - vcl/qa/cppunit/skia/skia.cxx | 6 vcl/qa/cppunit/svm/svmtest.cxx | 21 vcl/qt5/QtGraphics_GDI.cxx | 1 vcl/quartz/cgutils.mm | 47 +- vcl/skia/gdiimpl.cxx | 31 - vcl/skia/salbmp.cxx | 26 - vcl/source/bitmap/BitmapAlphaClampFilter.cxx | 4 vcl/source/bitmap/BitmapEx.cxx | 48 +- vcl/source/bitmap/BitmapMaskToAlphaFilter.cxx | 8 vcl/source/bitmap/BitmapTools.cxx | 43 + vcl/source/bitmap/alpha.cxx | 33 + vcl/source/bitmap/bitmappaint.cxx | 235 ++++++++-- vcl/source/bitmap/dibtools.cxx | 11 vcl/source/filter/eps/eps.cxx | 8 vcl/source/filter/igif/gifread.cxx | 4 vcl/source/filter/ipdf/pdfread.cxx | 3 vcl/source/filter/ipsd/ipsd.cxx | 4 vcl/source/filter/itiff/itiff.cxx | 2 vcl/source/filter/jpeg/JpegReader.cxx | 6 vcl/source/filter/jpeg/JpegReader.hxx | 2 vcl/source/filter/png/PngImageReader.cxx | 5 vcl/source/filter/png/PngImageWriter.cxx | 8 vcl/source/filter/wmf/emfwr.cxx | 3 vcl/source/filter/wmf/wmfwr.cxx | 3 vcl/source/gdi/gdimtf.cxx | 2 vcl/source/gdi/pdfwriter_impl2.cxx | 89 ++- vcl/source/gdi/virdev.cxx | 18 vcl/source/graphic/GraphicObject2.cxx | 4 vcl/source/graphic/UnoGraphic.cxx | 2 vcl/source/helper/canvasbitmap.cxx | 16 vcl/source/helper/canvastools.cxx | 4 vcl/source/image/ImplImage.cxx | 11 vcl/source/opengl/OpenGLHelper.cxx | 2 vcl/source/outdev/background.cxx | 8 vcl/source/outdev/bitmap.cxx | 15 vcl/source/outdev/bitmapex.cxx | 6 vcl/source/outdev/fill.cxx | 2 vcl/source/outdev/font.cxx | 2 vcl/source/outdev/gradient.cxx | 2 vcl/source/outdev/line.cxx | 2 vcl/source/outdev/pixel.cxx | 4 vcl/source/outdev/text.cxx | 4 vcl/source/outdev/textline.cxx | 4 vcl/source/outdev/transparent.cxx | 43 + vcl/source/rendercontext/drawmode.cxx | 2 vcl/win/gdi/salbmp.cxx | 2 76 files changed, 703 insertions(+), 370 deletions(-)
New commits: commit 81994cb2b8b32453a92bcb011830fcb884f22ff3 Author: Noel Grandin <noelgran...@gmail.com> AuthorDate: Fri Apr 16 20:33:10 2021 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Tue Jul 25 08:38:12 2023 +0200 Convert internal vcl bitmap formats transparency->alpha (II) (Second attempt at landing this) Image formats and graphics APIs use alpha, not transparency, so change our internal formats and data structures to work directly with alpha, so we don't need to modify data before we push it to graphics APIs. Add a couple of new Color constants to make the intention of the vcl code clearer. Notes (*) On macOS, tweaking the logic in CreateWithSalBitmapAndMask to more accurately reflect the requirements of the CGImageCreateWithMask function seems to fix some tests. (*) The vcl code does not properly support gradients with transparency. So the previous code was wrong, and this change is going to result in slightly different wrongness. Change-Id: I9e21c2e98d88ecfdc5f75db13bd1ffff7c38db98 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114168 Tested-by: Jenkins Reviewed-by: Patrick Luby <plub...@neooffice.org> Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/canvas/source/directx/dx_canvasbitmap.cxx b/canvas/source/directx/dx_canvasbitmap.cxx index 33dc7859fadb..fb06288ada86 100644 --- a/canvas/source/directx/dx_canvasbitmap.cxx +++ b/canvas/source/directx/dx_canvasbitmap.cxx @@ -214,7 +214,7 @@ namespace dxcanvas sal_uInt8* pOutBits=pAlphaBits.get()+y*nScanWidth; for( sal_Int32 x=0; x<aSize.getWidth(); ++x ) { - *pOutBits++ = 255-*pInBits; + *pOutBits++ = *pInBits; pInBits += 4; } } diff --git a/canvas/source/directx/dx_vcltools.cxx b/canvas/source/directx/dx_vcltools.cxx index 456f22386dd1..fb5e5c5e93db 100644 --- a/canvas/source/directx/dx_vcltools.cxx +++ b/canvas/source/directx/dx_vcltools.cxx @@ -211,11 +211,7 @@ namespace dxcanvas::tools *pCurrOutput++ = aCol.GetBlue(); *pCurrOutput++ = aCol.GetGreen(); *pCurrOutput++ = aCol.GetRed(); - - // our notion of alpha is - // different from the rest - // of the world's - *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++); + *pCurrOutput++ = static_cast<BYTE>(*pAScan++); } } break; @@ -231,11 +227,7 @@ namespace dxcanvas::tools *pCurrOutput++ = *pScan++; *pCurrOutput++ = *pScan++; *pCurrOutput++ = *pScan++; - - // our notion of alpha is - // different from the rest - // of the world's - *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++); + *pCurrOutput++ = static_cast<BYTE>(*pAScan++); } } break; @@ -258,11 +250,7 @@ namespace dxcanvas::tools *pCurrOutput++ = aCol.GetBlue(); *pCurrOutput++ = aCol.GetGreen(); *pCurrOutput++ = aCol.GetRed(); - - // our notion of alpha is - // different from the rest - // of the world's - *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++); + *pCurrOutput++ = static_cast<BYTE>(*pAScan++); } } break; diff --git a/canvas/source/vcl/canvasbitmaphelper.cxx b/canvas/source/vcl/canvasbitmaphelper.cxx index cd33b266346d..99b9831cabf6 100644 --- a/canvas/source/vcl/canvasbitmaphelper.cxx +++ b/canvas/source/vcl/canvasbitmaphelper.cxx @@ -146,7 +146,7 @@ namespace vclcanvas pRes[ 0 ] = aColor.GetRed(); pRes[ 1 ] = aColor.GetGreen(); pRes[ 2 ] = aColor.GetBlue(); - pRes[ 3 ] = 255 - aColor.GetAlpha(); + pRes[ 3 ] = aColor.GetAlpha(); return aRes; } diff --git a/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx b/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx index 35a2b45fd7bd..b8350a8f3944 100644 --- a/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx +++ b/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx @@ -229,7 +229,7 @@ namespace cppcanvas::internal // VirtualDevice with alpha channel ScopedVclPtrInstance<VirtualDevice> aVDev( *::Application::GetDefaultDevice(), DeviceFormat::WITH_ALPHA ); - aVDev->SetOutputSizePixel( aBitmapSizePixel ); + aVDev->SetOutputSizePixel( aBitmapSizePixel, true, true ); aVDev->SetMapMode(); if( rSubset.mnSubsetBegin != 0 || diff --git a/cui/source/dialogs/about.cxx b/cui/source/dialogs/about.cxx index 41d17b2a06d5..ce82e418cf9e 100644 --- a/cui/source/dialogs/about.cxx +++ b/cui/source/dialogs/about.cxx @@ -26,9 +26,9 @@ #include <osl/process.h> //osl_getProcessLocale #include <rtl/bootstrap.hxx> #include <sal/log.hxx> //SAL_WARN +#include <vcl/graph.hxx> //Graphic #include <vcl/settings.hxx> //GetSettings #include <vcl/svapp.hxx> //Application:: -#include <vcl/virdev.hxx> //VirtualDevice #include <vcl/weld.hxx> #include <unotools/resmgr.hxx> //Translate @@ -105,20 +105,20 @@ AboutDialog::AboutDialog(weld::Window *pParent) ? "shell/logo_inverted" : "shell/logo", aBackgroundBitmap, nWidth * 0.8)) { - ScopedVclPtr<VirtualDevice> m_pVirDev = - m_pBrandImage->create_virtual_device(); - m_pVirDev->SetOutputSizePixel(aBackgroundBitmap.GetSizePixel()); - m_pVirDev->DrawBitmapEx(Point(0, 0), aBackgroundBitmap); - m_pBrandImage->set_image(m_pVirDev.get()); - m_pVirDev.disposeAndClear(); + // Eliminate white background when Skia is disabled by not drawing the + // background bitmap to a VirtualDevice. On most platforms, non-Skia + // VirtualDevices will be filled with a solid color when drawing + // the bitmap. + Graphic aGraphic(aBackgroundBitmap); + m_pBrandImage->set_image(aGraphic.GetXGraphic()); } if (SfxApplication::loadBrandSvg("shell/about", aBackgroundBitmap, nWidth * 0.9)) { - ScopedVclPtr<VirtualDevice> m_pVirDev = - m_pAboutImage->create_virtual_device(); - m_pVirDev->SetOutputSizePixel(aBackgroundBitmap.GetSizePixel()); - m_pVirDev->DrawBitmapEx(Point(0, 0), aBackgroundBitmap); - m_pAboutImage->set_image(m_pVirDev.get()); - m_pVirDev.disposeAndClear(); + // Eliminate white background when Skia is disabled by not drawing the + // background bitmap to a VirtualDevice. On most platforms, non-Skia + // VirtualDevices will be filled with a solid color when drawing + // the bitmap. + Graphic aGraphic(aBackgroundBitmap); + m_pAboutImage->set_image(aGraphic.GetXGraphic()); } // Links diff --git a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx index 3c45fdd030f4..9bbdf7176935 100644 --- a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx +++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx @@ -58,10 +58,10 @@ AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius else if (fErodeDilateRadius < 0) BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF)); - if (nTransparency) + if (nTransparency != 255) { const Color aTransparency(nTransparency, nTransparency, nTransparency); - mask.Replace(COL_BLACK, aTransparency); + mask.Replace(COL_WHITE, aTransparency); } // We need 8-bit grey mask for blurring @@ -72,6 +72,8 @@ AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius mask.Scale(rMask.GetSizePixel()); + mask.Invert(); // convert transparency to alpha + return AlphaMask(mask.GetBitmap()); } diff --git a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx index 4a64da368a2f..89c4335bb0b3 100644 --- a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx @@ -32,6 +32,7 @@ namespace drawinglayer::primitive2d DiscreteShadow::DiscreteShadow(const BitmapEx& rBitmapEx) : maBitmapEx(rBitmapEx) { + maBitmapEx.Invert(); // convert transparency to alpha const Size& rBitmapSize = getBitmapEx().GetSizePixel(); if(rBitmapSize.Width() != rBitmapSize.Height() || rBitmapSize.Width() < 7) diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx index 783060c2be4c..7671e0c29a05 100644 --- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx @@ -410,7 +410,7 @@ sal::systools::COMReference<ID2D1Bitmap> createB2DBitmap(const BitmapEx& rBitmap { const BitmapColor aColor(pReadAccess->GetColor(y, x)); const BitmapColor aAlpha(pAlphaReadAccess->GetColor(y, x)); - const sal_uInt16 nAlpha(255 - aAlpha.GetRed()); + const sal_uInt16 nAlpha(aAlpha.GetRed()); *pTarget++ = sal_uInt32(BitmapColor( ColorAlpha, sal_uInt8((sal_uInt16(aColor.GetRed()) * nAlpha) >> 8), diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx index c76225194dd7..f90049bf8773 100644 --- a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx +++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx @@ -476,6 +476,7 @@ void impBufferDevice::paint(double fTrans) { mpAlpha->EnableMapMode(false); AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel)); + aAlphaMask.Invert(); // convert transparency to alpha #ifdef DBG_UTIL if (!sDumpPath.isEmpty() && bDoSaveForVisualControl) diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx index 001019cd9623..80ae15a3b6a9 100644 --- a/drawinglayer/source/tools/converters.cxx +++ b/drawinglayer/source/tools/converters.cxx @@ -95,7 +95,7 @@ AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& r // prepare for mask creation pContent->SetMapMode(MapMode(MapUnit::MapPixel)); - // set alpha to all white (fully transparent) + // set transparency to all white (fully transparent) pContent->Erase(); basegfx::BColorModifierSharedPtr aBColorModifier; @@ -107,7 +107,7 @@ AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& r } else { - // Embed primitives to paint them black + // Embed primitives to paint them black (fully opaque) aBColorModifier = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0, 0.0, 0.0)); } @@ -123,7 +123,14 @@ AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& r // get alpha channel from vdev pContent->EnableMapMode(false); const Point aEmptyPoint; - return AlphaMask(pContent->GetBitmap(aEmptyPoint, rSizePixel)); + + // Convert from transparency->alpha. + // FIXME in theory I should be able to directly construct alpha by using black as background + // and white as foreground, but that doesn't work for some reason. + Bitmap aContentBitmap = pContent->GetBitmap(aEmptyPoint, rSizePixel); + aContentBitmap.Invert(); + + return AlphaMask(aContentBitmap); } } @@ -257,12 +264,16 @@ BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSe if (aAlpha.hasAlpha()) { // Need to correct content using known alpha to get to background-free - // RGBA result, usable e.g. in PNG export(s) or convert-to-bitmap - aRetval.RemoveBlendedStartColor(COL_WHITE, aAlpha); + // RGBA result, usable e.g. in PNG export(s) or convert-to-bitmap. + // Now that vcl supports bitmaps with an alpha channel, only apply + // this correction to bitmaps without an alpha channel. + if (pContent->GetBitCount() < 32) + aRetval.RemoveBlendedStartColor(COL_WHITE, aAlpha); + // return combined result + return BitmapEx(aRetval, aAlpha); } - - // return combined result - return BitmapEx(aRetval, aAlpha); + else + return BitmapEx(aRetval); } BitmapEx convertPrimitive2DContainerToBitmapEx(primitive2d::Primitive2DContainer&& rSequence, diff --git a/forms/source/component/imgprod.cxx b/forms/source/component/imgprod.cxx index 46439682519f..3f2e6bfdee5b 100644 --- a/forms/source/component/imgprod.cxx +++ b/forms/source/component/imgprod.cxx @@ -379,7 +379,7 @@ void ImageProducer::ImplUpdateConsumer( const Graphic& rGraphic ) if( !pMskAcc ) { - aMask = Bitmap(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256)); + aMask = AlphaMask(aBmp.GetSizePixel()); aMask.Erase( 0 ); pMskAcc = aMask.AcquireReadAccess(); } @@ -389,7 +389,7 @@ void ImageProducer::ImplUpdateConsumer( const Graphic& rGraphic ) if( pBmpAcc->HasPalette() ) { - const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( COL_WHITE ) ); + const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( COL_ALPHA_TRANSPARENT ) ); if( mnTransIndex < 256 ) { diff --git a/include/tools/color.hxx b/include/tools/color.hxx index 41826ec2335f..c3bbb5bf0e2b 100644 --- a/include/tools/color.hxx +++ b/include/tools/color.hxx @@ -449,6 +449,10 @@ static_assert (sal_uInt32(Color(0x12, 0x34, 0x56)) == 0x00123456); inline constexpr ::Color COL_TRANSPARENT ( ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF ); inline constexpr ::Color COL_AUTO ( ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF ); +// These are used when drawing to the separate alpha channel we use in vcl +inline constexpr ::Color COL_ALPHA_TRANSPARENT ( 0x00, 0x00, 0x00 ); +inline constexpr ::Color COL_ALPHA_OPAQUE ( 0xff, 0xff, 0xff ); + inline constexpr ::Color COL_BLACK ( 0x00, 0x00, 0x00 ); inline constexpr ::Color COL_BLUE ( 0x00, 0x00, 0x80 ); inline constexpr ::Color COL_GREEN ( 0x00, 0x80, 0x00 ); diff --git a/include/vcl/bitmap.hxx b/include/vcl/bitmap.hxx index 0c794d496342..bc0d628179ca 100644 --- a/include/vcl/bitmap.hxx +++ b/include/vcl/bitmap.hxx @@ -374,6 +374,37 @@ public: */ Bitmap CreateMask( const Color& rTransColor, sal_uInt8 nTol ) const; + /** Create on-off alpha mask from bitmap + + This method creates a bitmask from the bitmap, where every + pixel that equals rTransColor is set transparent, the rest + opaque. + + @param rTransColor + Color value where the bitmask should be transparent + + @return the resulting bitmask. + */ + AlphaMask CreateAlphaMask( const Color& rTransColor ) const; + + /** Create on-off alpha mask from bitmap + + This method creates a bitmask from the bitmap, where every + pixel that equals rTransColor is set transparent, the rest + opaque. + + @param rTransColor + Color value where the bitmask should be transparent + + @param nTol + Tolerance value. Specifies the maximal difference between + rTransColor and the individual pixel values, such that the + corresponding pixel is still regarded as transparent. + + @return the resulting bitmask. + */ + AlphaMask CreateAlphaMask( const Color& rTransColor, sal_uInt8 nTol ) const; + /** Create region of similar colors in a given rectangle @param rColor diff --git a/include/vcl/virdev.hxx b/include/vcl/virdev.hxx index 0f6ec8ee1857..40cf445f0390 100644 --- a/include/vcl/virdev.hxx +++ b/include/vcl/virdev.hxx @@ -58,7 +58,7 @@ private: SAL_DLLPRIVATE bool InnerImplSetOutputSizePixel( const Size& rNewSize, bool bErase, sal_uInt8* pBuffer ); SAL_DLLPRIVATE bool ImplSetOutputSizePixel( const Size& rNewSize, bool bErase, - sal_uInt8* pBuffer ); + sal_uInt8* pBuffer, bool bAlphaMaskTransparent = false ); VirtualDevice (const VirtualDevice &) = delete; VirtualDevice & operator= (const VirtualDevice &) = delete; @@ -124,7 +124,7 @@ public: virtual void EnableRTL( bool bEnable = true ) override; - bool SetOutputSizePixel( const Size& rNewSize, bool bErase = true ); + bool SetOutputSizePixel( const Size& rNewSize, bool bErase = true, bool bAlphaMaskTransparent = false ); bool SetOutputSizePixelScaleOffsetAndLOKBuffer( const Size& rNewSize, const Fraction& rScale, const Point& rNewOffset, diff --git a/sd/qa/unit/PNGExportTests.cxx b/sd/qa/unit/PNGExportTests.cxx index 5ca65f545dc2..58ec8d6077cc 100644 --- a/sd/qa/unit/PNGExportTests.cxx +++ b/sd/qa/unit/PNGExportTests.cxx @@ -295,7 +295,7 @@ CPPUNIT_TEST_FIXTURE(SdPNGExportTest, testTdf147119) // - Expected: Color: R:255 G:255 B:255 A:0 // - Actual : Color: R:0 G:0 B:0 A:0 const Color aColor = pReadAccess->GetColor(nY, nX); - CPPUNIT_ASSERT_EQUAL(COL_WHITE, aColor); + CPPUNIT_ASSERT_EQUAL(COL_ALPHA_TRANSPARENT, aColor); } } } diff --git a/sfx2/source/dialog/infobar.cxx b/sfx2/source/dialog/infobar.cxx index 81e8ffe9d864..778b5e9b140e 100644 --- a/sfx2/source/dialog/infobar.cxx +++ b/sfx2/source/dialog/infobar.cxx @@ -115,7 +115,7 @@ void SfxInfoBarWindow::SetCloseButtonImage() drawinglayer::primitive2d::Primitive2DContainer aSeq(2); // Draw backround. The right and bottom need to be extended by 1 or - // there will be a white line on both edges when Skia is enabled. + // there will be a white line on both edges. B2DPolygon aPolygon; aPolygon.append(B2DPoint(aRect.Left(), aRect.Top())); aPolygon.append(B2DPoint(aRect.Right() + 1, aRect.Top())); diff --git a/slideshow/source/engine/shapes/gdimtftools.cxx b/slideshow/source/engine/shapes/gdimtftools.cxx index 990d9d26caac..c5a215eefbc4 100644 --- a/slideshow/source/engine/shapes/gdimtftools.cxx +++ b/slideshow/source/engine/shapes/gdimtftools.cxx @@ -338,15 +338,15 @@ bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames, // extract current aVDev content into a new animation // frame GDIMetaFileSharedPtr pMtf = std::make_shared<GDIMetaFile>(); + Bitmap aAlphaMask = pVDevMask->GetBitmap(aEmptyPoint, aAnimSize); + aAlphaMask.Invert(); // convert from transparency to alpha pMtf->AddAction( new MetaBmpExAction( aEmptyPoint, BitmapEx( pVDev->GetBitmap( aEmptyPoint, aAnimSize ), - pVDevMask->GetBitmap( - aEmptyPoint, - aAnimSize )))); + aAlphaMask))); // setup mtf dimensions and pref map mode (for // simplicity, keep it all in pixel. the metafile diff --git a/svx/source/svdraw/svdfmtf.cxx b/svx/source/svdraw/svdfmtf.cxx index 839fff3bdb15..923c40a550fa 100644 --- a/svx/source/svdraw/svdfmtf.cxx +++ b/svx/source/svdraw/svdfmtf.cxx @@ -1564,6 +1564,7 @@ void ImpSdrGDIMetaFileImport::DoAction(MetaFloatTransparentAction const & rAct) pVDev->DrawGradient(tools::Rectangle(Point(0, 0), pVDev->GetOutputSizePixel()), rGradient); aNewMask = AlphaMask(pVDev->GetBitmap(Point(0, 0), pVDev->GetOutputSizePixel())); + aNewMask.Invert(); // convert transparency to alpha bHasNewMask = true; } @@ -1577,9 +1578,9 @@ void ImpSdrGDIMetaFileImport::DoAction(MetaFloatTransparentAction const & rAct) // no transparence yet, apply new one if(bFixedTransparence) { - sal_uInt8 aAlpha(basegfx::fround(fTransparence * 255.0)); + sal_uInt8 nTransparence(basegfx::fround(fTransparence * 255.0)); - aNewMask = AlphaMask(aBitmapEx.GetBitmap().GetSizePixel(), &aAlpha); + aNewMask = AlphaMask(aBitmapEx.GetBitmap().GetSizePixel(), &nTransparence); } aBitmapEx = BitmapEx(aBitmapEx.GetBitmap(), aNewMask); diff --git a/toolkit/source/helper/vclunohelper.cxx b/toolkit/source/helper/vclunohelper.cxx index 45580c37cac8..9f05ae707065 100644 --- a/toolkit/source/helper/vclunohelper.cxx +++ b/toolkit/source/helper/vclunohelper.cxx @@ -89,6 +89,7 @@ BitmapEx VCLUnoHelper::GetBitmap( const css::uno::Reference< css::awt::XBitmap>& SvMemoryStream aMem( aBytes.getArray(), aBytes.getLength(), StreamMode::READ ); ReadDIB(aMask, aMem, true); } + aMask.Invert(); // Convert from transparency to alpha aBmp = BitmapEx( aDIB, aMask ); } } diff --git a/vcl/backendtest/outputdevice/bitmap.cxx b/vcl/backendtest/outputdevice/bitmap.cxx index 5b491badf587..9ea1a6f08d84 100644 --- a/vcl/backendtest/outputdevice/bitmap.cxx +++ b/vcl/backendtest/outputdevice/bitmap.cxx @@ -105,8 +105,8 @@ Bitmap OutputDeviceTestBitmap::setupDrawBitmapExWithAlpha(vcl::PixelFormat aBitm AlphaMask aAlpha(aBitmapSize); { AlphaScopedWriteAccess aWriteAccess(aAlpha); - aWriteAccess->Erase(COL_WHITE); - aWriteAccess->SetLineColor(Color(0x44, 0x44, 0x44)); + aWriteAccess->Erase(COL_ALPHA_TRANSPARENT); + aWriteAccess->SetLineColor(Color(0xBB, 0xBB, 0xBB)); aWriteAccess->DrawRect(tools::Rectangle(0, 0, 8, 8)); aWriteAccess->DrawRect(tools::Rectangle(3, 3, 5, 5)); } @@ -154,8 +154,8 @@ BitmapEx OutputDeviceTestBitmap::setupDrawBlend(vcl::PixelFormat aBitmapFormat) AlphaMask aAlpha(aBitmapSize); { AlphaScopedWriteAccess aWriteAccess(aAlpha); - aWriteAccess->Erase(COL_WHITE); - aWriteAccess->SetLineColor(Color(0x44, 0x44, 0x44)); + aWriteAccess->Erase(COL_ALPHA_TRANSPARENT); + aWriteAccess->SetLineColor(Color(0xBB, 0xBB, 0xBB)); aWriteAccess->DrawRect(tools::Rectangle(0, 0, 8, 8)); aWriteAccess->DrawRect(tools::Rectangle(3, 3, 5, 5)); } diff --git a/vcl/headless/BitmapHelper.cxx b/vcl/headless/BitmapHelper.cxx index 3eb29aa76ad3..8ed0cac09fcb 100644 --- a/vcl/headless/BitmapHelper.cxx +++ b/vcl/headless/BitmapHelper.cxx @@ -84,19 +84,7 @@ MaskHelper::MaskHelper(const SalBitmap& rAlphaBitmap) const BitmapBuffer* pMaskBuf = rMask.GetBuffer(); assert(rAlphaBitmap.GetBitCount() == 8 && "we only support 8-bit masks now"); - // the alpha values need to be inverted for Cairo - // so big stupid copy and invert here - const int nImageSize = pMaskBuf->mnHeight * pMaskBuf->mnScanlineSize; - pAlphaBits.reset(new unsigned char[nImageSize]); - memcpy(pAlphaBits.get(), pMaskBuf->mpBits, nImageSize); - - // TODO: make upper layers use standard alpha - sal_uInt32* pLDst = reinterpret_cast<sal_uInt32*>(pAlphaBits.get()); - for (int i = nImageSize / sizeof(sal_uInt32); --i >= 0; ++pLDst) - *pLDst = ~*pLDst; - assert(reinterpret_cast<unsigned char*>(pLDst) == pAlphaBits.get() + nImageSize); - - implSetSurface(cairo_image_surface_create_for_data(pAlphaBits.get(), CAIRO_FORMAT_A8, + implSetSurface(cairo_image_surface_create_for_data(pMaskBuf->mpBits, CAIRO_FORMAT_A8, pMaskBuf->mnWidth, pMaskBuf->mnHeight, pMaskBuf->mnScanlineSize)); } diff --git a/vcl/headless/CairoCommon.cxx b/vcl/headless/CairoCommon.cxx index 15b8cebfb261..d378d1477bd4 100644 --- a/vcl/headless/CairoCommon.cxx +++ b/vcl/headless/CairoCommon.cxx @@ -1912,8 +1912,6 @@ std::shared_ptr<SalBitmap> CairoCommon::getBitmap(tools::Long nX, tools::Long nY cairo_destroy(cr); cairo_surface_destroy(target); - Toggle1BitTransparency(*pBitmap->GetBuffer()); - return pBitmap; } @@ -2080,19 +2078,6 @@ std::unique_ptr<BitmapBuffer> FastConvert24BitRgbTo32BitCairo(const BitmapBuffer return pDst; } -void Toggle1BitTransparency(const BitmapBuffer& rBuf) -{ - assert(rBuf.maPalette.GetBestIndex(BitmapColor(COL_BLACK)) == 0); - // TODO: make upper layers use standard alpha - if (getCairoFormat(rBuf) == CAIRO_FORMAT_A1) - { - const int nImageSize = rBuf.mnHeight * rBuf.mnScanlineSize; - unsigned char* pDst = rBuf.mpBits; - for (int i = nImageSize; --i >= 0; ++pDst) - *pDst = ~*pDst; - } -} - namespace { // check for env var that decides for using downscale pattern diff --git a/vcl/inc/bitmap/ScanlineTools.hxx b/vcl/inc/bitmap/ScanlineTools.hxx index c343cf34f61e..99ce5dc33aae 100644 --- a/vcl/inc/bitmap/ScanlineTools.hxx +++ b/vcl/inc/bitmap/ScanlineTools.hxx @@ -39,14 +39,14 @@ public: virtual Color readPixel() override { - const Color aColor(ColorTransparency, pData[4], pData[1], pData[2], pData[3]); + const Color aColor(ColorAlpha, pData[4], pData[1], pData[2], pData[3]); pData += 4; return aColor; } virtual void writePixel(Color nColor) override { - *pData++ = 255 - nColor.GetAlpha(); + *pData++ = nColor.GetAlpha(); *pData++ = nColor.GetRed(); *pData++ = nColor.GetGreen(); *pData++ = nColor.GetBlue(); diff --git a/vcl/inc/headless/BitmapHelper.hxx b/vcl/inc/headless/BitmapHelper.hxx index dbd7e86675e3..2b6598464703 100644 --- a/vcl/inc/headless/BitmapHelper.hxx +++ b/vcl/inc/headless/BitmapHelper.hxx @@ -39,9 +39,6 @@ public: class VCL_DLLPUBLIC MaskHelper : public SurfaceHelper { -private: - std::unique_ptr<unsigned char[]> pAlphaBits; - public: explicit MaskHelper(const SalBitmap& rAlphaBitmap); }; diff --git a/vcl/inc/headless/CairoCommon.hxx b/vcl/inc/headless/CairoCommon.hxx index ea07e798b74b..8402e38b1d59 100644 --- a/vcl/inc/headless/CairoCommon.hxx +++ b/vcl/inc/headless/CairoCommon.hxx @@ -111,8 +111,6 @@ VCL_DLLPUBLIC cairo_format_t getCairoFormat(const BitmapBuffer& rBuffer); VCL_DLLPUBLIC std::unique_ptr<BitmapBuffer> FastConvert24BitRgbTo32BitCairo(const BitmapBuffer* pSrc); -VCL_DLLPUBLIC void Toggle1BitTransparency(const BitmapBuffer& rBuf); - enum class PaintMode { Over, diff --git a/vcl/qa/cppunit/BackendTest.cxx b/vcl/qa/cppunit/BackendTest.cxx index 2c85be06e66d..e5013d244335 100644 --- a/vcl/qa/cppunit/BackendTest.cxx +++ b/vcl/qa/cppunit/BackendTest.cxx @@ -1181,7 +1181,7 @@ public: device->Erase(); alpha.Erase(255); // transparent BitmapWriteAccess* alphaWrite = alpha.AcquireAlphaWriteAccess(); - alphaWrite->SetPixelIndex(0, 0, 0); // opaque + alphaWrite->SetPixelIndex(0, 0, 255); // opaque alpha.ReleaseAccess(alphaWrite); device->DrawBitmapEx(Point(2, 2), BitmapEx(bitmap, alpha)); exportDevice("blend_extended_04.png", device); diff --git a/vcl/qa/cppunit/BitmapExTest.cxx b/vcl/qa/cppunit/BitmapExTest.cxx index a0a6b3096f53..a8f1e5a9cda5 100644 --- a/vcl/qa/cppunit/BitmapExTest.cxx +++ b/vcl/qa/cppunit/BitmapExTest.cxx @@ -47,8 +47,7 @@ void BitmapExTest::testGetPixelColor24_8() BitmapEx aBitmapEx(aBitmap, aMask); - CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xAA, 0x00, 0xFF, 0x00), - aBitmapEx.GetPixelColor(0, 0)); + CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xAA, 0x00, 0xFF, 0x00), aBitmapEx.GetPixelColor(0, 0)); } void BitmapExTest::testGetPixelColor32() diff --git a/vcl/qa/cppunit/ScanlineToolsTest.cxx b/vcl/qa/cppunit/ScanlineToolsTest.cxx index 8b8a9088a1ea..97233f2692af 100644 --- a/vcl/qa/cppunit/ScanlineToolsTest.cxx +++ b/vcl/qa/cppunit/ScanlineToolsTest.cxx @@ -42,9 +42,9 @@ void ScanlineToolsTest::ScanlineTransformer_32_ARGB() pScanlineTransformer->startLine(aScanLine.data()); std::vector<Color> aColors{ - Color(ColorTransparency, 0, 10, 250, 120), Color(ColorTransparency, 50, 30, 230, 110), - Color(ColorTransparency, 100, 50, 210, 100), Color(ColorTransparency, 150, 70, 190, 90), - Color(ColorTransparency, 200, 90, 170, 80), + Color(ColorAlpha, 255, 10, 250, 120), Color(ColorAlpha, 205, 30, 230, 110), + Color(ColorAlpha, 155, 50, 210, 100), Color(ColorAlpha, 105, 70, 190, 90), + Color(ColorAlpha, 55, 90, 170, 80), }; for (Color const& aColor : aColors) @@ -52,8 +52,8 @@ void ScanlineToolsTest::ScanlineTransformer_32_ARGB() pScanlineTransformer->writePixel(aColor); } - std::vector<sal_uInt8> aExpectedBytes{ 0, 10, 250, 120, 50, 30, 230, 110, 100, 50, - 210, 100, 150, 70, 190, 90, 200, 90, 170, 80 }; + std::vector<sal_uInt8> aExpectedBytes{ 255, 10, 250, 120, 205, 30, 230, 110, 155, 50, + 210, 100, 105, 70, 190, 90, 55, 90, 170, 80 }; for (size_t i = 0; i < aScanLine.size(); ++i) { diff --git a/vcl/qa/cppunit/canvasbitmaptest.cxx b/vcl/qa/cppunit/canvasbitmaptest.cxx index 27ac06d0d9de..78eb033f90b0 100644 --- a/vcl/qa/cppunit/canvasbitmaptest.cxx +++ b/vcl/qa/cppunit/canvasbitmaptest.cxx @@ -672,17 +672,17 @@ void CanvasBitmapTest::runTest() checkCanvasBitmap( xBmp, "single bitmap", nDepth ); - Bitmap aMask(Size(200,200), vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256)); - aMask.Erase(COL_WHITE); + AlphaMask aMask(Size(200,200)); + aMask.Erase(255); { BitmapScopedWriteAccess pAcc(aMask); if( pAcc.get() ) { - pAcc->SetFillColor(COL_BLACK); + pAcc->SetFillColor(COL_ALPHA_OPAQUE); pAcc->FillRect(tools::Rectangle(0,0,100,100)); - pAcc->SetPixel(0,0,BitmapColor(255)); - pAcc->SetPixel(0,1,BitmapColor(0)); - pAcc->SetPixel(0,2,BitmapColor(255)); + pAcc->SetPixel(0,0,BitmapColor(0)); + pAcc->SetPixel(0,1,BitmapColor(255)); + pAcc->SetPixel(0,2,BitmapColor(0)); } } @@ -691,16 +691,16 @@ void CanvasBitmapTest::runTest() checkCanvasBitmap( xBmp, "masked bitmap", nDepth ); AlphaMask aAlpha(Size(200,200)); - aAlpha.Erase(255); + aAlpha.Erase(0); { BitmapWriteAccess* pAcc = aAlpha.AcquireWriteAccess(); if( pAcc ) { - pAcc->SetFillColor(COL_BLACK); + pAcc->SetFillColor(COL_ALPHA_OPAQUE); pAcc->FillRect(tools::Rectangle(0,0,100,100)); - pAcc->SetPixel(0,0,BitmapColor(255)); - pAcc->SetPixel(0,1,BitmapColor(0)); - pAcc->SetPixel(0,2,BitmapColor(255)); + pAcc->SetPixel(0,0,BitmapColor(0)); + pAcc->SetPixel(0,1,BitmapColor(255)); + pAcc->SetPixel(0,2,BitmapColor(0)); aAlpha.ReleaseAccess(pAcc); } } @@ -762,15 +762,15 @@ void CanvasBitmapTest::runTest() CPPUNIT_ASSERT_EQUAL_MESSAGE("(0,0) incorrect content", BitmapColor(0,1,0), pBmpAcc->GetPixel(0,0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("(0,0) incorrect alpha content", - BitmapColor(255), pAlphaAcc->GetPixel(0,0)); + BitmapColor(0), pAlphaAcc->GetPixel(0,0)); CPPUNIT_ASSERT_EQUAL_MESSAGE("(2,2) incorrect content", BitmapColor(0,3,2), pBmpAcc->GetPixel(2,2)); CPPUNIT_ASSERT_EQUAL_MESSAGE("(2,2) incorrect alpha content", - BitmapColor(253), pAlphaAcc->GetPixel(2,2)); + BitmapColor(2), pAlphaAcc->GetPixel(2,2)); CPPUNIT_ASSERT_EQUAL_MESSAGE("(9,2) incorrect content", BitmapColor(0,3,9), pBmpAcc->GetPixel(2,9)); CPPUNIT_ASSERT_EQUAL_MESSAGE("(9,2) correct alpha content", - BitmapColor(253), pAlphaAcc->GetPixel(2,9)); + BitmapColor(2), pAlphaAcc->GetPixel(2,9)); aBitmapAlpha.ReleaseAccess(pAlphaAcc); Bitmap::ReleaseAccess(pBmpAcc); diff --git a/vcl/qa/cppunit/png/PngFilterTest.cxx b/vcl/qa/cppunit/png/PngFilterTest.cxx index 760e12f8b3fc..8e9c15e6dd49 100644 --- a/vcl/qa/cppunit/png/PngFilterTest.cxx +++ b/vcl/qa/cppunit/png/PngFilterTest.cxx @@ -325,22 +325,22 @@ void PngFilterTest::testPng() CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAlphaAccess->Width()); CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAlphaAccess->Height()); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x80, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00), pAlphaAccess->GetPixel(0, 0)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x80, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00), pAlphaAccess->GetPixel(3, 3)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x80, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00), pAlphaAccess->GetPixel(3, 0)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x80, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00), pAlphaAccess->GetPixel(0, 3)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x40, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xBF, 0x00), pAlphaAccess->GetPixel(1, 1)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xC0, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x3F, 0x00), pAlphaAccess->GetPixel(1, 2)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xC0, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x3F, 0x00), pAlphaAccess->GetPixel(2, 1)); - CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x40, 0x00), + CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xBF, 0x00), pAlphaAccess->GetPixel(2, 2)); } } @@ -1887,15 +1887,14 @@ void PngFilterTest::testPngRoundtrip24_8() { BitmapScopedWriteAccess pWriteAccessBitmap(aBitmap); AlphaScopedWriteAccess pWriteAccessAlpha(aAlpha); - pWriteAccessAlpha->Erase(Color(ColorTransparency, 0x00, 0xAA, 0xAA, 0xAA)); + pWriteAccessAlpha->Erase(Color(0xAA, 0xAA, 0xAA)); pWriteAccessBitmap->Erase(COL_BLACK); for (int i = 0; i < 8; ++i) { for (int j = 0; j < 8; ++j) { pWriteAccessBitmap->SetPixel(i, j, COL_LIGHTRED); - pWriteAccessAlpha->SetPixel(i, j, - Color(ColorTransparency, 0x00, 0xBB, 0xBB, 0xBB)); + pWriteAccessAlpha->SetPixel(i, j, Color(0xBB, 0xBB, 0xBB)); } } for (int i = 8; i < 16; ++i) @@ -1903,8 +1902,7 @@ void PngFilterTest::testPngRoundtrip24_8() for (int j = 8; j < 16; ++j) { pWriteAccessBitmap->SetPixel(i, j, COL_LIGHTBLUE); - pWriteAccessAlpha->SetPixel(i, j, - Color(ColorTransparency, 0x00, 0xCC, 0xCC, 0xCC)); + pWriteAccessAlpha->SetPixel(i, j, Color(0xCC, 0xCC, 0xCC)); } } } @@ -1922,13 +1920,13 @@ void PngFilterTest::testPngRoundtrip24_8() CPPUNIT_ASSERT_EQUAL(Size(16, 16), aBitmapEx.GetSizePixel()); - CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xBB, 0xFF, 0x00, 0x00), + CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xBB, 0xFF, 0x00, 0x00), aBitmapEx.GetPixelColor(0, 0)); - CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xCC, 0x00, 0x00, 0xFF), + CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xCC, 0x00, 0x00, 0xFF), aBitmapEx.GetPixelColor(15, 15)); - CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xAA, 0x00, 0x00, 0x00), + CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xAA, 0x00, 0x00, 0x00), aBitmapEx.GetPixelColor(15, 0)); - CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xAA, 0x00, 0x00, 0x00), + CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xAA, 0x00, 0x00, 0x00), aBitmapEx.GetPixelColor(0, 15)); } } diff --git a/vcl/qa/cppunit/skia/skia.cxx b/vcl/qa/cppunit/skia/skia.cxx index 5d81e39f587a..b90b915e17ea 100644 --- a/vcl/qa/cppunit/skia/skia.cxx +++ b/vcl/qa/cppunit/skia/skia.cxx @@ -244,7 +244,7 @@ void SkiaTest::testAlphaBlendWith() skiaAlpha = dynamic_cast<SkiaSalBitmap*>(alpha.ImplGetSalBitmap().get()); CPPUNIT_ASSERT(skiaAlpha->unittestHasEraseColor()); CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, alpha.getPixelFormat()); - CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(112), + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(208), AlphaMask::ScopedReadAccess(alpha)->GetPixelIndex(0, 0)); // Test with images set. @@ -264,7 +264,7 @@ void SkiaTest::testAlphaBlendWith() skiaAlpha = dynamic_cast<SkiaSalBitmap*>(alpha.ImplGetSalBitmap().get()); CPPUNIT_ASSERT(skiaAlpha->unittestHasImage()); CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, alpha.getPixelFormat()); - CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(112), + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(207), AlphaMask::ScopedReadAccess(alpha)->GetPixelIndex(0, 0)); // Test with erase color for alpha and image for other bitmap. @@ -281,7 +281,7 @@ void SkiaTest::testAlphaBlendWith() skiaAlpha = dynamic_cast<SkiaSalBitmap*>(alpha.ImplGetSalBitmap().get()); CPPUNIT_ASSERT(skiaAlpha->unittestHasImage()); CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, alpha.getPixelFormat()); - CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(112), + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(207), AlphaMask::ScopedReadAccess(alpha)->GetPixelIndex(0, 0)); } diff --git a/vcl/qa/cppunit/svm/svmtest.cxx b/vcl/qa/cppunit/svm/svmtest.cxx index fcf863e7664a..5161f4cce960 100644 --- a/vcl/qa/cppunit/svm/svmtest.cxx +++ b/vcl/qa/cppunit/svm/svmtest.cxx @@ -26,6 +26,7 @@ #include <vcl/filter/SvmReader.hxx> #include <vcl/filter/SvmWriter.hxx> #include <salhelper/simplereferenceobject.hxx> +#include <sal/log.hxx> #include <bitmap/BitmapWriteAccess.hxx> @@ -46,7 +47,7 @@ class SvmTest : public test::BootstrapFixture, public XmlTestTools return m_directories.getURLFromSrc(maDataUrl) + sFileName; } - void checkRendering(ScopedVclPtrInstance<VirtualDevice> const & pVirtualDev, const GDIMetaFile& rMetaFile); + void checkRendering(ScopedVclPtrInstance<VirtualDevice> const & pVirtualDev, const GDIMetaFile& rMetaFile, const char * where); // write GDI Metafile to a file in data directory // only use this for new tests to create the svm file @@ -268,7 +269,7 @@ static void setupBaseVirtualDevice(VirtualDevice& rDevice, GDIMetaFile& rMeta) rDevice.Erase(); } -void SvmTest::checkRendering(ScopedVclPtrInstance<VirtualDevice> const & pVirtualDev, const GDIMetaFile& rMetaFile) +void SvmTest::checkRendering(ScopedVclPtrInstance<VirtualDevice> const & pVirtualDev, const GDIMetaFile& rMetaFile, const char * where) { BitmapEx aSourceBitmapEx = pVirtualDev->GetBitmapEx(Point(), Size(10, 10)); ScopedVclPtrInstance<VirtualDevice> pVirtualDevResult; @@ -294,7 +295,7 @@ void SvmTest::checkRendering(ScopedVclPtrInstance<VirtualDevice> const & pVirtua aPNGWriter.write(aResultBitmapEx); } } - CPPUNIT_ASSERT_EQUAL(aSourceBitmapEx.GetChecksum(), aResultBitmapEx.GetChecksum()); + CPPUNIT_ASSERT_EQUAL_MESSAGE(where, aSourceBitmapEx.GetChecksum(), aResultBitmapEx.GetChecksum()); } static GDIMetaFile readMetafile(const OUString& rUrl) @@ -989,12 +990,12 @@ void SvmTest::testBitmaps() { GDIMetaFile aReloadedGDIMetaFile = writeAndReadStream(aGDIMetaFile); checkBitmaps(aReloadedGDIMetaFile); - checkRendering(pVirtualDev, aReloadedGDIMetaFile); + checkRendering(pVirtualDev, aReloadedGDIMetaFile, SAL_WHERE); } { GDIMetaFile aFileGDIMetaFile = readFile(u"bitmaps.svm"); checkBitmaps(aFileGDIMetaFile); - checkRendering(pVirtualDev, aFileGDIMetaFile); + checkRendering(pVirtualDev, aFileGDIMetaFile, SAL_WHERE); } } @@ -1005,7 +1006,7 @@ void SvmTest::checkBitmapExs(const GDIMetaFile& rMetaFile, bool bIsSvmFile) if (SkiaHelper::isVCLSkiaEnabled()) return; // TODO SKIA using CRCs is broken (the idea of it) - std::array<OUString, 8> aExpectedCRC + static const std::vector<OUString> aExpectedCRC { #if defined OSL_BIGENDIAN "08feb5d3", @@ -1028,7 +1029,7 @@ void SvmTest::checkBitmapExs(const GDIMetaFile& rMetaFile, bool bIsSvmFile) #endif }; - std::array<OUString, 8> aExpectedContentChecksum + static const std::array<OUString, 8> aExpectedContentChecksum { "26bdebd04e5b18d685cea04982179e273ee3b659", "f4f52df6ef965a2f0fbccbe6aca35ba3457cf9d5", @@ -1126,7 +1127,7 @@ void SvmTest::testBitmapExs() pAccess->Erase(COL_MAGENTA); AlphaScopedWriteAccess pAlphaAccess(aAlpha); - pAlphaAccess->Erase(Color(128, 128, 128)); + pAlphaAccess->Erase(Color(127, 127, 127)); } pVirtualDev->DrawBitmapEx(Point(6, 6), BitmapEx(aBitmap, aAlpha)); } @@ -1178,12 +1179,12 @@ void SvmTest::testBitmapExs() { GDIMetaFile aReloadedGDIMetaFile = writeAndReadStream(aGDIMetaFile); checkBitmapExs(aReloadedGDIMetaFile, /*bIsSvmFile*/false); - checkRendering(pVirtualDev, aReloadedGDIMetaFile); + checkRendering(pVirtualDev, aReloadedGDIMetaFile, SAL_WHERE); } { GDIMetaFile aFileGDIMetaFile = readFile(u"bitmapexs.svm"); checkBitmapExs(aFileGDIMetaFile, /*bIsSvmFile*/true); - checkRendering(pVirtualDev, aFileGDIMetaFile); + checkRendering(pVirtualDev, aFileGDIMetaFile, SAL_WHERE); } } diff --git a/vcl/qt5/QtGraphics_GDI.cxx b/vcl/qt5/QtGraphics_GDI.cxx index 28bab34a7cca..70c598c8bcd9 100644 --- a/vcl/qt5/QtGraphics_GDI.cxx +++ b/vcl/qt5/QtGraphics_GDI.cxx @@ -597,7 +597,6 @@ static QImage getAlphaImage(const SalBitmap& rSourceBitmap, const SalBitmap& rAl assert(rAlphaBitmap.GetBitCount() == 8 || rAlphaBitmap.GetBitCount() == 1); QImage aAlphaMask = *static_cast<const QtBitmap*>(&rAlphaBitmap)->GetQImage(); - aAlphaMask.invertPixels(); const QImage* pBitmap = static_cast<const QtBitmap*>(&rSourceBitmap)->GetQImage(); QImage aImage = pBitmap->convertToFormat(Qt_DefaultFormat32); diff --git a/vcl/quartz/cgutils.mm b/vcl/quartz/cgutils.mm index c28391c48395..c6a490d44885 100644 --- a/vcl/quartz/cgutils.mm +++ b/vcl/quartz/cgutils.mm @@ -41,27 +41,38 @@ CGImageRef CreateWithSalBitmapAndMask( const SalBitmap& rBitmap, const SalBitmap if( !xMask ) return xImage; - // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed - // TODO: isolate in an extra method? - if( !CGImageIsMask(xMask) || rMask.GetBitCount() != 8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) ) + // If xMask is an image (i.e. not a mask), it must be greyscale - a requirement of the + // CGImageCreateWithMask() function. + if( !CGImageIsMask(xMask) && CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace ) { - const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset + CGImageRef xGrayMask = CGImageCreateCopyWithColorSpace(xMask, GetSalData()->mxGraySpace); + if (xGrayMask) + { + CFRelease(xMask); + xMask = xGrayMask; + } + else + { + // Many gallery images will fail to be converted to a grayscale + // colorspace so fall back to old mask creation code + const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset - // create the alpha mask image fitting our image - // TODO: is caching the full mask or the subimage mask worth it? - int nMaskBytesPerRow = ((nWidth + 3) & ~3); - void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight ); - CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem, - nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone ); - CGContextDrawImage( xMaskContext, xImageRect, xMask ); - CFRelease( xMask ); - CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr, - pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) ); + // create the alpha mask image fitting our image + // TODO: is caching the full mask or the subimage mask worth it? + int nMaskBytesPerRow = ((nWidth + 3) & ~3); + void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight ); + CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem, + nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone ); + CGContextDrawImage( xMaskContext, xImageRect, xMask ); + CFRelease( xMask ); + CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr, + pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) ); - static const CGFloat* pDecode = nullptr; - xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false ); - CFRelease( xDataProvider ); - CFRelease( xMaskContext ); + static const CGFloat* pDecode = nullptr; + xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false ); + CFRelease( xDataProvider ); + CFRelease( xMaskContext ); + } } if( !xMask ) diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx index 40341bba85f7..e6c373c2f86b 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -1272,23 +1272,19 @@ 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. + // This is used by VirtualDevice in the alpha mode for the "alpha" layer + // So the result is transparent only if both the inputs + // are transparent. Which seems to be what SkBlendMode::kModulate does, + // so use that. // See also blendAlphaBitmap(). if (rSkiaBitmap.IsFullyOpaqueAsAlpha()) { - // Optimization. If the bitmap means fully opaque, it's all zero's. In CPU + // Optimization. If the bitmap means fully opaque, it's all one's. In CPU // mode it should be faster to just copy instead of SkBlendMode::kMultiply. drawBitmap(rPosAry, rSkiaBitmap); } else - drawBitmap(rPosAry, rSkiaBitmap, SkBlendMode::kMultiply); + drawBitmap(rPosAry, rSkiaBitmap, SkBlendMode::kModulate); return true; } @@ -1337,10 +1333,10 @@ bool SkiaSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect& rPosAry, // First do the "( 1 - alpha ) * mask" // (no idea how to do "floor", but hopefully not needed in practice). sk_sp<SkShader> shaderAlpha - = SkShaders::Blend(SkBlendMode::kDstOut, rSkiaMaskBitmap.GetAlphaSkShader(samplingOptions), + = SkShaders::Blend(SkBlendMode::kDstIn, rSkiaMaskBitmap.GetAlphaSkShader(samplingOptions), rSkiaAlphaBitmap.GetAlphaSkShader(samplingOptions)); // And now draw the bitmap with "1 - x", where x is the "( 1 - alpha ) * mask". - sk_sp<SkShader> shader = SkShaders::Blend(SkBlendMode::kSrcOut, shaderAlpha, + sk_sp<SkShader> shader = SkShaders::Blend(SkBlendMode::kSrcIn, shaderAlpha, rSkiaSourceBitmap.GetSkShader(samplingOptions)); drawShader(rPosAry, shader); return true; @@ -1368,9 +1364,12 @@ void SkiaSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const SalBitmap& r { assert(dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap)); const SkiaSalBitmap& skiaBitmap = static_cast<const SkiaSalBitmap&>(rSalBitmap); + // SkBlendMode::kDstOut must be used instead of SkBlendMode::kDstIn because + // the alpha channel of what is drawn appears to get inverted at some point + // after it is drawn drawShader( rPosAry, - SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha. + SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is alpha. SkShaders::Color(toSkColor(nMaskColor)), skiaBitmap.GetAlphaSkShader(makeSamplingOptions(rPosAry, mScaling)))); } @@ -1680,7 +1679,7 @@ sk_sp<SkImage> SkiaSalGraphicsImpl::mergeCacheBitmaps(const SkiaSalBitmap& bitma { canvas->clear(SK_ColorTRANSPARENT); paint.setShader( - SkShaders::Blend(SkBlendMode::kDstOut, bitmap.GetSkShader(samplingOptions, bitmapType), + SkShaders::Blend(SkBlendMode::kDstIn, bitmap.GetSkShader(samplingOptions, bitmapType), alphaBitmap->GetAlphaSkShader(samplingOptions, alphaBitmapType))); canvas->drawPaint(paint); } @@ -1742,7 +1741,7 @@ bool SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBi else drawShader(rPosAry, SkShaders::Blend( - SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha. + SkBlendMode::kDstIn, rSkiaSourceBitmap.GetSkShader(makeSamplingOptions(rPosAry, mScaling)), rSkiaAlphaBitmap.GetAlphaSkShader(makeSamplingOptions(rPosAry, mScaling)))); return true; @@ -1965,7 +1964,7 @@ bool SkiaSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull, if (pSkiaAlphaBitmap) { SkPaint paint = makeBitmapPaint(); - paint.setShader(SkShaders::Blend(SkBlendMode::kDstOut, // VCL alpha is one-minus-alpha. + paint.setShader(SkShaders::Blend(SkBlendMode::kDstIn, rSkiaBitmap.GetSkShader(samplingOptions), pSkiaAlphaBitmap->GetAlphaSkShader(samplingOptions))); if (fAlpha != 1.0) diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx index 778917b6db0e..5a514aebb05d 100644 --- a/vcl/skia/salbmp.cxx +++ b/vcl/skia/salbmp.cxx @@ -617,7 +617,12 @@ bool SkiaSalBitmap::AlphaBlendWith(const SalBitmap& rSalBmp) SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); // set as is surface->getCanvas()->drawImage(GetSkImage(), 0, 0, SkSamplingOptions(), &paint); - paint.setBlendMode(SkBlendMode::kScreen); // src+dest - src*dest/255 (in 0..1) + // in the 0..1 range that skia uses, the equation we want is: + // r = 1 - ((1 - src) + (1 - dest) - (1 - src) * (1 - dest)) + // which simplifies to: + // r = src * dest + // which is SkBlendMode::kModulate + paint.setBlendMode(SkBlendMode::kModulate); surface->getCanvas()->drawImage(otherBitmap->GetSkImage(), 0, 0, SkSamplingOptions(), &paint); ResetToSkImage(makeCheckedImageSnapshot(surface)); DataChanged(); @@ -640,8 +645,10 @@ bool SkiaSalBitmap::Invert() surface->getCanvas()->clear(SK_ColorWHITE); SkPaint paint; paint.setBlendMode(SkBlendMode::kDifference); - surface->getCanvas()->drawImage( - mImage, 0, 0, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), &paint); + // Drawing the image does not work so create a shader from the image + paint.setShader(GetSkShader(SkSamplingOptions(SkSamplingOptions()))); + surface->getCanvas()->drawRect(SkRect::MakeXYWH(0, 0, mSize.Width(), mSize.Height()), + paint); ResetToSkImage(makeCheckedImageSnapshot(surface)); DataChanged(); SAL_INFO("vcl.skia.trace", "invert(" << this << ")"); @@ -746,11 +753,8 @@ SkBitmap SkiaSalBitmap::GetAsSkBitmap() const } // If mEraseColor is set, this is the color to use when the bitmap is used as alpha bitmap. -// E.g. COL_BLACK actually means fully opaque and COL_WHITE means fully transparent. +// E.g. COL_BLACK actually means fully transparent and COL_WHITE means fully opaque. // This is because the alpha value is set as the color itself, not the alpha of the color. -// Additionally VCL actually uses transparency and not opacity, so we should use "255 - value", -// but we account for this by doing SkBlendMode::kDstOut when using alpha images (which -// basically does another "255 - alpha"), so do not do it here. static SkColor fromEraseColorToAlphaImageColor(Color color) { return SkColorSetARGB(color.GetBlue(), 0, 0, 0); @@ -1053,9 +1057,7 @@ bool SkiaSalBitmap::IsFullyOpaqueAsAlpha() const return false; // If the erase color is set so that this bitmap used as alpha would // mean a fully opaque alpha mask (= noop), we can skip using it. - // Note that for alpha bitmaps we use the VCL "transparency" convention, - // i.e. alpha 0 is opaque. - return SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)) == 0; + return SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)) == 255; } SkAlphaType SkiaSalBitmap::alphaType() const @@ -1080,7 +1082,7 @@ void SkiaSalBitmap::PerformErase() abort(); Color fastColor = mEraseColor; if (!!mPalette) - fastColor = Color(ColorTransparency, mPalette.GetBestIndex(fastColor)); + fastColor = Color(ColorAlpha, mPalette.GetBestIndex(fastColor)); if (!ImplFastEraseBitmap(*bitmapBuffer, fastColor)) { FncSetPixel setPixel = BitmapReadAccess::SetPixelFunction(bitmapBuffer->mnFormat); @@ -1406,7 +1408,7 @@ OString SkiaSalBitmap::GetAlphaImageKey(DirectImage direct) const { std::stringstream ss; ss << std::hex << std::setfill('0') << std::setw(2) - << static_cast<int>(255 - SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor))); + << static_cast<int>(SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor))); return OString::Concat("E") + ss.str().c_str(); } assert(direct == DirectImage::No || mAlphaImage); diff --git a/vcl/source/bitmap/BitmapAlphaClampFilter.cxx b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx index eb3245e2347c..820bdbdde34b 100644 --- a/vcl/source/bitmap/BitmapAlphaClampFilter.cxx +++ b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx @@ -30,9 +30,9 @@ BitmapEx BitmapAlphaClampFilter::execute(BitmapEx const& rBitmapEx) const for (sal_Int32 nX = 0; nX < sal_Int32(aSize.Width()); ++nX) { BitmapColor aBitmapAlphaValue(pWriteAlpha->GetPixelFromData(pScanAlpha, nX)); - if (aBitmapAlphaValue.GetIndex() > mcThreshold) + if ((255 - aBitmapAlphaValue.GetIndex()) > mcThreshold) { - aBitmapAlphaValue.SetIndex(255); + aBitmapAlphaValue.SetIndex(0); pWriteAlpha->SetPixelOnData(pScanAlpha, nX, aBitmapAlphaValue); } } diff --git a/vcl/source/bitmap/BitmapEx.cxx b/vcl/source/bitmap/BitmapEx.cxx index ad4adca6319e..c9be55521f16 100644 --- a/vcl/source/bitmap/BitmapEx.cxx +++ b/vcl/source/bitmap/BitmapEx.cxx @@ -113,12 +113,20 @@ BitmapEx::BitmapEx( const Bitmap& rBmp, const Bitmap& rMask ) : if (rMask.IsEmpty()) return; + assert(typeid(rMask) != typeid(AlphaMask) + && "If this mask is actually an AlphaMask, then it will be inverted unnecessarily " + "and the alpha channel will be wrong"); + if( rMask.getPixelFormat() == vcl::PixelFormat::N8_BPP && rMask.HasGreyPalette8Bit() ) + { maAlphaMask = rMask; + maAlphaMask.Invert(); + } else if( rMask.getPixelFormat() == vcl::PixelFormat::N8_BPP ) { BitmapEx aMaskEx(rMask); BitmapFilter::Filter(aMaskEx, BitmapMonochromeFilter(255)); + aMaskEx.Invert(); maAlphaMask = aMaskEx.GetBitmap(); } else @@ -127,6 +135,7 @@ BitmapEx::BitmapEx( const Bitmap& rBmp, const Bitmap& rMask ) : SAL_WARN( "vcl", "BitmapEx: forced mask to monochrome"); BitmapEx aMaskEx(rMask); BitmapFilter::Filter(aMaskEx, BitmapMonochromeFilter(255)); + aMaskEx.Invert(); maAlphaMask = aMaskEx.GetBitmap(); } @@ -154,7 +163,7 @@ BitmapEx::BitmapEx( const Bitmap& rBmp, const Color& rTransparentColor ) : maBitmap ( rBmp ), maBitmapSize ( maBitmap.GetSizePixel() ) { - maAlphaMask = maBitmap.CreateMask( rTransparentColor ); + maAlphaMask = maBitmap.CreateAlphaMask( rTransparentColor ); SAL_WARN_IF(rBmp.GetSizePixel() != maAlphaMask.GetSizePixel(), "vcl", "BitmapEx::BitmapEx(): size mismatch for bitmap and alpha mask."); @@ -332,14 +341,14 @@ bool BitmapEx::Rotate( Degree10 nAngle10, const Color& rFillColor ) } if( bRet && !maAlphaMask.IsEmpty() ) - maAlphaMask.Rotate( nAngle10, COL_WHITE ); + maAlphaMask.Rotate( nAngle10, COL_ALPHA_TRANSPARENT ); } else { bRet = maBitmap.Rotate( nAngle10, rFillColor ); if( bRet && !maAlphaMask.IsEmpty() ) - maAlphaMask.Rotate( nAngle10, COL_WHITE ); + maAlphaMask.Rotate( nAngle10, COL_ALPHA_TRANSPARENT ); } SetSizePixel(maBitmap.GetSizePixel()); @@ -387,7 +396,7 @@ void BitmapEx::Expand( sal_Int32 nDX, sal_Int32 nDY, bool bExpandTransparent ) if( bRet && !maAlphaMask.IsEmpty() ) { - Color aColor( bExpandTransparent ? COL_WHITE : COL_BLACK ); + Color aColor( bExpandTransparent ? COL_ALPHA_TRANSPARENT : COL_ALPHA_OPAQUE ); maAlphaMask.Expand( nDX, nDY, &aColor ); } @@ -427,8 +436,8 @@ bool BitmapEx::CopyPixel( const tools::Rectangle& rRectDst, const tools::Rectang maAlphaMask.CopyPixel_AlphaOptimized( rRectDst, rRectSrc, &pBmpExSrc->maAlphaMask ); else { - sal_uInt8 cBlack = 0; - std::optional<AlphaMask> pAlpha(std::in_place, GetSizePixel(), &cBlack); + sal_uInt8 nTransparencyOpaque = 0; + std::optional<AlphaMask> pAlpha(std::in_place, GetSizePixel(), &nTransparencyOpaque); maAlphaMask = pAlpha->ImplGetBitmap(); pAlpha.reset(); @@ -437,8 +446,8 @@ bool BitmapEx::CopyPixel( const tools::Rectangle& rRectDst, const tools::Rectang } else if (IsAlpha()) { - sal_uInt8 cBlack = 0; - const AlphaMask aAlphaSrc(pBmpExSrc->GetSizePixel(), &cBlack); + sal_uInt8 nTransparencyOpaque = 0; + const AlphaMask aAlphaSrc(pBmpExSrc->GetSizePixel(), &nTransparencyOpaque); maAlphaMask.CopyPixel( rRectDst, rRectSrc, &aAlphaSrc.ImplGetBitmap() ); } @@ -581,7 +590,7 @@ sal_uInt8 BitmapEx::GetAlpha(sal_Int32 nX, sal_Int32 nY) const if(pRead) { const BitmapColor aBitmapColor(pRead->GetPixel(nY, nX)); - nAlpha = 255 - aBitmapColor.GetIndex(); + nAlpha = aBitmapColor.GetIndex(); } } return nAlpha; @@ -599,7 +608,7 @@ Color BitmapEx::GetPixelColor(sal_Int32 nX, sal_Int32 nY) const { AlphaMask aAlpha = GetAlphaMask(); AlphaMask::ScopedReadAccess pAlphaReadAccess(aAlpha); - aColor.SetAlpha(255 - pAlphaReadAccess->GetPixel(nY, nX).GetIndex()); + aColor.SetAlpha(pAlphaReadAccess->GetPixel(nY, nX).GetIndex()); } else if (maBitmap.getPixelFormat() != vcl::PixelFormat::N32_BPP) { @@ -1023,6 +1032,8 @@ BitmapEx createBlendFrame( Color aColorBottomRight, Color aColorBottomLeft) { + // FIXME the call sites are actually passing in transparency + nAlpha = 255 - nAlpha; BlendFrameCache* pBlendFrameCache = ImplGetBlendFrameCache(); if(pBlendFrameCache->m_aLastSize == rSize @@ -1368,7 +1379,7 @@ void BitmapEx::ChangeColorAlpha( sal_uInt8 cIndexFrom, sal_Int8 nAlphaTo ) { const sal_uInt8 cIndex = pReadAccess->GetPixelFromData( pScanlineRead, nX ).GetIndex(); if ( cIndex == cIndexFrom ) - pAlphaWriteAccess->SetPixelOnData( pScanline, nX, BitmapColor(255 - nAlphaTo) ); + pAlphaWriteAccess->SetPixelOnData( pScanline, nX, BitmapColor(nAlphaTo) ); } } *this = BitmapEx( GetBitmap(), aAlphaMask ); @@ -1391,7 +1402,7 @@ void BitmapEx::AdjustTransparency(sal_uInt8 cTrans) if( !pA ) return; - sal_uLong nTrans = cTrans, nNewTrans; + sal_uLong nTrans = cTrans; const tools::Long nWidth = pA->Width(), nHeight = pA->Height(); if( pA->GetScanlineFormat() == ScanlineFormat::N8BitPal ) @@ -1402,8 +1413,10 @@ void BitmapEx::AdjustTransparency(sal_uInt8 cTrans) for( tools::Long nX = 0; nX < nWidth; nX++ ) { - nNewTrans = nTrans + *pAScan; - *pAScan++ = static_cast<sal_uInt8>( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ); + sal_uLong nNewTrans = nTrans + (255 - *pAScan); + // clamp to 255 + nNewTrans = ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans; + *pAScan++ = static_cast<sal_uInt8>( 255 - nNewTrans ); } } } @@ -1416,8 +1429,11 @@ void BitmapEx::AdjustTransparency(sal_uInt8 cTrans) Scanline pScanline = pA->GetScanline( nY ); for( tools::Long nX = 0; nX < nWidth; nX++ ) { - nNewTrans = nTrans + pA->GetIndexFromData( pScanline, nX ); - aAlphaValue.SetIndex( static_cast<sal_uInt8>( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ) ); + sal_uLong nNewTrans = nTrans + (255 - pA->GetIndexFromData( pScanline, nX )); + // clamp to 255 + nNewTrans = ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans; + // convert back to alpha + aAlphaValue.SetIndex( static_cast<sal_uInt8>(255 - nNewTrans) ); pA->SetPixelOnData( pScanline, nX, aAlphaValue ); } } diff --git a/vcl/source/bitmap/BitmapMaskToAlphaFilter.cxx b/vcl/source/bitmap/BitmapMaskToAlphaFilter.cxx index c9ec102d933a..c5f1cda1d9cb 100644 --- a/vcl/source/bitmap/BitmapMaskToAlphaFilter.cxx +++ b/vcl/source/bitmap/BitmapMaskToAlphaFilter.cxx @@ -14,7 +14,7 @@ #include <bitmap/BitmapMaskToAlphaFilter.hxx> /** - * Convert a 1-bit mask to an alpha layer + * Convert a 1-bit mask to an alpha bitmap */ BitmapEx BitmapMaskToAlphaFilter::execute(BitmapEx const& rBitmapEx) const { @@ -38,11 +38,11 @@ BitmapEx BitmapMaskToAlphaFilter::execute(BitmapEx const& rBitmapEx) const { BitmapColor aBmpColor = pRead->GetPixelFromData(pScanlineRead, nX); if (aBmpColor == COL_BLACK) - aBmpColor = COL_BLACK; + aBmpColor = COL_ALPHA_OPAQUE; else if (aBmpColor == COL_WHITE) - aBmpColor = COL_WHITE; + aBmpColor = COL_ALPHA_TRANSPARENT; else if (aBmpColor == Color(0, 0, 1)) - aBmpColor = COL_WHITE; + aBmpColor = COL_ALPHA_TRANSPARENT; else assert(false); pWrite->SetPixelOnData(pScanline, nX, aBmpColor); diff --git a/vcl/source/bitmap/BitmapTools.cxx b/vcl/source/bitmap/BitmapTools.cxx index beec528b2f0e..7caf1f12f328 100644 --- a/vcl/source/bitmap/BitmapTools.cxx +++ b/vcl/source/bitmap/BitmapTools.cxx @@ -205,7 +205,8 @@ BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHei Scanline pMaskScanLine = xMaskAcc->GetScanline(y); for (tools::Long x = 0; x < nWidth; ++x) { - const sal_uInt8 nValue = bReverseAlpha ? 0xff - *p : *p; + // FIXME this parameter is badly named + const sal_uInt8 nValue = bReverseAlpha ? *p : 0xff - *p; xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(nValue)); p += 4; } @@ -271,11 +272,15 @@ BitmapEx CreateFromData( RawBitmap&& rawBitmap ) Scanline pMaskScanLine = xMaskAcc->GetScanline(y); for (tools::Long x = 0; x < nWidth; ++x) { - xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(255 - *p)); + xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(*p)); p += 4; } } } + + xMaskAcc.reset(); + pWrite.reset(); + if (nBitCount == 32) return BitmapEx(aBmp, *pAlphaMask); else @@ -350,7 +355,7 @@ BitmapEx* CreateFromCairoSurface(Size aSize, cairo_surface_t * pSurface) #endif } pRGBWrite->SetPixel( y, x, BitmapColor( nR, nG, nB ) ); - pMaskWrite->SetPixelIndex( y, x, 255 - nAlpha ); + pMaskWrite->SetPixelIndex( y, x, nAlpha ); pPix++; } } @@ -462,7 +467,7 @@ BitmapEx CanvasTransformBitmap( const BitmapEx& rBitmap, if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || nSrcY < 0 || nSrcY >= aBmpSize.Height() ) { - pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) ); + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) ); } else { @@ -486,11 +491,11 @@ BitmapEx CanvasTransformBitmap( const BitmapEx& rBitmap, if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || nSrcY < 0 || nSrcY >= aBmpSize.Height() ) { - pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) ); + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) ); } else { - pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) ); + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) ); pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY, nSrcX ) ); } @@ -521,6 +526,7 @@ void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool bFixedTransparen } { + AlphaScopedWriteAccess pOld(aOldMask); assert(pOld && "Got no access to old alpha mask (!)"); @@ -536,8 +542,8 @@ void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool bFixedTransparen Scanline pScanline = pOld->GetScanline( y ); for(tools::Long x(0); x < pOld->Width(); x++) { - const double fOpOld(1.0 - (pOld->GetIndexFromData(pScanline, x) * fFactor)); - const sal_uInt8 aCol(basegfx::fround((1.0 - (fOpOld * fOpNew)) * 255.0)); + const double fOpOld(pOld->GetIndexFromData(pScanline, x) * fFactor); + const sal_uInt8 aCol(basegfx::fround((fOpOld * fOpNew) * 255.0)); pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol)); } @@ -557,9 +563,9 @@ void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool bFixedTransparen Scanline pScanline = pOld->GetScanline( y ); for(tools::Long x(0); x < pOld->Width(); x++) { - const double fOpOld(1.0 - (pOld->GetIndexFromData(pScanline, x) * fFactor)); - const double fOpNew(1.0 - (pNew->GetIndexFromData(pScanline, x) * fFactor)); - const sal_uInt8 aCol(basegfx::fround((1.0 - (fOpOld * fOpNew)) * 255.0)); + const double fOpOld(pOld->GetIndexFromData(pScanline, x) * fFactor); + const double fOpNew(pNew->GetIndexFromData(pScanline, x) * fFactor); + const sal_uInt8 aCol(basegfx::fround((fOpOld * fOpNew) * 255.0)); pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol)); } @@ -619,10 +625,9 @@ void DrawAndClipBitmap(const Point& rPos, const Size& rSize, const BitmapEx& rBi const sal_uInt8 nIndR(pR->GetIndexFromData(pScanlineR, nX)); const sal_uInt8 nIndW(pW->GetIndexFromData(pScanlineW, nX)); - // these values represent transparency (0 == no, 255 == fully transparent), - // so to blend these we have to multiply the inverse (opacity) - // and re-invert the result to transparence - const sal_uInt8 nCombined(0x00ff - (((0x00ff - nIndR) * (0x00ff - nIndW)) >> 8)); + // these values represent alpha (255 == no, 0 == fully transparent), + // so to blend these we have to multiply + const sal_uInt8 nCombined((nIndR * nIndW) >> 8); pW->SetPixelOnData(pScanlineW, nX, BitmapColor(nCombined)); } @@ -672,7 +677,7 @@ static bool readAlpha( BitmapReadAccess const * pAlphaReadAcc, tools::Long nY, c BitmapColor const& rColor( pAlphaReadAcc->GetPaletteColor(*pReadScan)); pReadScan++; - nAlpha = data[ nOff ] = 255 - rColor.GetIndex(); + nAlpha = data[ nOff ] = rColor.GetIndex(); if( nAlpha != 255 ) bIsAlpha = true; nOff += 4; @@ -682,7 +687,7 @@ static bool readAlpha( BitmapReadAccess const * pAlphaReadAcc, tools::Long nY, c SAL_INFO( "canvas.cairo", "fallback to GetColor for alpha - slow, format: " << static_cast<int>(pAlphaReadAcc->GetScanlineFormat()) ); for( nX = 0; nX < nWidth; nX++ ) { - nAlpha = data[ nOff ] = 255 - pAlphaReadAcc->GetColor( nY, nX ).GetIndex(); + nAlpha = data[ nOff ] = pAlphaReadAcc->GetColor( nY, nX ).GetIndex(); if( nAlpha != 255 ) bIsAlpha = true; nOff += 4; @@ -1027,7 +1032,7 @@ void CanvasCairoExtractBitmapData( BitmapEx const & aBmpEx, Bitmap & aBitmap, un pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); - pRes[ nCurrPos++ ] = pAlphaReadAccess->GetIndexFromData( pScanlineReadAlpha, x ); + pRes[ nCurrPos++ ] = 255 - pAlphaReadAccess->GetIndexFromData( pScanlineReadAlpha, x ); } } else @@ -1217,7 +1222,7 @@ bool convertBitmap32To24Plus8(BitmapEx const & rInput, BitmapEx & rResult) { const BitmapColor aColor = pReadAccess->GetPixelFromData(aReadScan, nX); BitmapColor aResultColor(aColor.GetRed(), aColor.GetGreen(), aColor.GetBlue()); - BitmapColor aResultColorAlpha(255 - aColor.GetAlpha(), 255 - aColor.GetAlpha(), 255 - aColor.GetAlpha()); + BitmapColor aResultColorAlpha(aColor.GetAlpha(), aColor.GetAlpha(), aColor.GetAlpha()); pResultBitmapAccess->SetPixelOnData(aResultScan, nX, aResultColor); pResultAlphaAccess->SetPixelOnData(aResultScanAlpha, nX, aResultColorAlpha); diff --git a/vcl/source/bitmap/alpha.cxx b/vcl/source/bitmap/alpha.cxx index f307dda63a28..8e082c695137 100644 --- a/vcl/source/bitmap/alpha.cxx +++ b/vcl/source/bitmap/alpha.cxx @@ -31,8 +31,11 @@ AlphaMask::AlphaMask() = default; AlphaMask::AlphaMask( const Bitmap& rBitmap ) : Bitmap( rBitmap ) { - if( !rBitmap.IsEmpty() ) + // no need to do any conversion if it is already an AlphaMask + if ( typeid(rBitmap) != typeid(AlphaMask) && !rBitmap.IsEmpty() ) Convert( BmpConversion::N8BitNoConversion ); + assert( (IsEmpty() || getPixelFormat() == vcl::PixelFormat::N8_BPP) && "alpha bitmap should be 8bpp" ); + assert( (IsEmpty() || HasGreyPalette8Bit()) && "alpha bitmap should have greyscale palette" ); } AlphaMask::AlphaMask( const AlphaMask& ) = default; @@ -43,7 +46,12 @@ AlphaMask::AlphaMask( const Size& rSizePixel, const sal_uInt8* pEraseTransparenc : Bitmap(rSizePixel, vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256)) { if( pEraseTransparency ) - Bitmap::Erase( Color( *pEraseTransparency, *pEraseTransparency, *pEraseTransparency ) ); + { + sal_uInt8 nAlpha = 255 - *pEraseTransparency; + Bitmap::Erase( Color( nAlpha, nAlpha, nAlpha ) ); + } + else + Bitmap::Erase( COL_ALPHA_OPAQUE ); } AlphaMask::~AlphaMask() = default; @@ -55,6 +63,9 @@ AlphaMask& AlphaMask::operator=( const Bitmap& rBitmap ) if( !rBitmap.IsEmpty() ) Convert( BmpConversion::N8BitNoConversion ); + assert( getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); + assert( HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); + return *this; } @@ -70,7 +81,8 @@ Bitmap const & AlphaMask::GetBitmap() const void AlphaMask::Erase( sal_uInt8 cTransparency ) { - Bitmap::Erase( Color( cTransparency, cTransparency, cTransparency ) ); + sal_uInt8 nAlpha = 255 - cTransparency; + Bitmap::Erase( Color( nAlpha, nAlpha, nAlpha ) ); } void AlphaMask::BlendWith(const AlphaMask& rOther) @@ -79,6 +91,8 @@ void AlphaMask::BlendWith(const AlphaMask& rOther) if (xImpBmp->Create(*ImplGetSalBitmap()) && xImpBmp->AlphaBlendWith(*rOther.ImplGetSalBitmap())) { ImplSetSalBitmap(xImpBmp); + assert( getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); + assert( HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); return; } Bitmap::ScopedReadAccess pOtherAcc(const_cast<AlphaMask&>(rOther)); @@ -101,11 +115,18 @@ void AlphaMask::BlendWith(const AlphaMask& rOther) // Use sal_uInt16 for following multiplication const sal_uInt16 nGrey1 = *scanline; const sal_uInt16 nGrey2 = *otherScanline; - *scanline = static_cast<sal_uInt8>(nGrey1 + nGrey2 - nGrey1 * nGrey2 / 255); + // Awkward calculation because the original used transparency, and to replicate + // the logic we need to translate into transparency, perform the original logic, + // then translate back to alpha. + auto tmp = 255 - ((255 - nGrey1) + (255 - nGrey2) - (255 - nGrey1) * (255 - nGrey2)); + *scanline = static_cast<sal_uInt8>(tmp / 255); ++scanline; ++otherScanline; } } + pAcc.reset(); + assert( getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); + assert( HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); } bool AlphaMask::hasAlpha() const @@ -126,7 +147,7 @@ bool AlphaMask::hasAlpha() const { for (tools::Long x = 0; x < nWidth; ++x) { - if (0 != pAcc->GetColor(y, x).GetRed()) + if (255 != pAcc->GetColor(y, x).GetRed()) { return true; } @@ -143,6 +164,8 @@ void AlphaMask::ReleaseAccess( BitmapReadAccess* pAccess ) Bitmap::ReleaseAccess( pAccess ); Convert( BmpConversion::N8BitNoConversion ); } + assert( getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); + assert( HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/bitmap/bitmappaint.cxx b/vcl/source/bitmap/bitmappaint.cxx index 0207bb813fe8..487e6d1a6ad6 100644 --- a/vcl/source/bitmap/bitmappaint.cxx +++ b/vcl/source/bitmap/bitmappaint.cxx @@ -60,38 +60,54 @@ bool Bitmap::Erase(const Color& rFillColor) bool Bitmap::Invert() { - ScopedReadAccess pReadAcc(*this); - if (!pReadAcc) + if (!mxSalBmp) return false; - if (pReadAcc->HasPalette()) + // For alpha masks, we need to actually invert the underlying data + // or the optimisations elsewhere do not work right. + if (typeid(*this) != typeid(AlphaMask)) { - BitmapScopedWriteAccess pWriteAcc(*this); - BitmapPalette aBmpPal(pWriteAcc->GetPalette()); - const sal_uInt16 nCount = aBmpPal.GetEntryCount(); - - for (sal_uInt16 i = 0; i < nCount; i++) + // We want to avoid using ScopedReadAccess until we really need + // it, because on Skia it triggers a GPU->RAM copy, which is very slow. + ScopedReadAccess pReadAcc(*this); + if (!pReadAcc) + return false; + if (pReadAcc->HasPalette()) { - aBmpPal[i].Invert(); - } + BitmapScopedWriteAccess pWriteAcc(*this); + BitmapPalette aBmpPal(pWriteAcc->GetPalette()); + const sal_uInt16 nCount = aBmpPal.GetEntryCount(); - pWriteAcc->SetPalette(aBmpPal); + for (sal_uInt16 i = 0; i < nCount; i++) + { + aBmpPal[i].Invert(); + } + + pWriteAcc->SetPalette(aBmpPal); + mxSalBmp->InvalidateChecksum(); + return true; + } } - else if (!mxSalBmp->Invert()) // try optimised call first + + // try optimised call, much faster on Skia + if (mxSalBmp->Invert()) { - BitmapScopedWriteAccess pWriteAcc(*this); - const tools::Long nWidth = pWriteAcc->Width(); - const tools::Long nHeight = pWriteAcc->Height(); + mxSalBmp->InvalidateChecksum(); + return true; + } - for (tools::Long nY = 0; nY < nHeight; nY++) + BitmapScopedWriteAccess pWriteAcc(*this); + const tools::Long nWidth = pWriteAcc->Width(); + const tools::Long nHeight = pWriteAcc->Height(); + + for (tools::Long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + for (tools::Long nX = 0; nX < nWidth; nX++) { - Scanline pScanline = pWriteAcc->GetScanline(nY); - for (tools::Long nX = 0; nX < nWidth; nX++) - { - BitmapColor aBmpColor = pWriteAcc->GetPixelFromData(pScanline, nX); - aBmpColor.Invert(); - pWriteAcc->SetPixelOnData(pScanline, nX, aBmpColor); - } + BitmapColor aBmpColor = pWriteAcc->GetPixelFromData(pScanline, nX); + aBmpColor.Invert(); + pWriteAcc->SetPixelOnData(pScanline, nX, aBmpColor); } } @@ -607,6 +623,174 @@ Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const return aNewBmp; } +AlphaMask Bitmap::CreateAlphaMask(const Color& rTransColor) const +{ + ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this)); + if (!pReadAcc) + return AlphaMask(); + + // Historically LO used 1bpp masks, but 8bpp masks are much faster, + // better supported by hardware, and the memory savings are not worth + // it anymore. + + if ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal) + && pReadAcc->GetBestMatchingColor(COL_ALPHA_TRANSPARENT) + == pReadAcc->GetBestMatchingColor(rTransColor)) + { + // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it + // already, then just return a copy + return AlphaMask(*this); + } + + AlphaMask aNewBmp(GetSizePixel()); + BitmapScopedWriteAccess pWriteAcc(aNewBmp); + if (!pWriteAcc) + return AlphaMask(); + + const tools::Long nWidth = pReadAcc->Width(); + const tools::Long nHeight = pReadAcc->Height(); + const BitmapColor aOpaqueColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_OPAQUE)); + const BitmapColor aTransparentColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_TRANSPARENT)); + + const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor)); + + if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal) + { + // optimized for 8Bit source palette + const sal_uInt8 cTest = aTest.GetIndex(); + + for (tools::Long nY = 0; nY < nHeight; ++nY) + { + Scanline pSrc = pReadAcc->GetScanline(nY); + Scanline pDst = pWriteAcc->GetScanline(nY); + for (tools::Long nX = 0; nX < nWidth; ++nX) + { + if (cTest == pSrc[nX]) + pDst[nX] = aTransparentColor.GetIndex(); + else + pDst[nX] = aOpaqueColor.GetIndex(); + } + } + } + else + { + // not optimized + for (tools::Long nY = 0; nY < nHeight; ++nY) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + for (tools::Long nX = 0; nX < nWidth; ++nX) + { + if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX)) + pWriteAcc->SetPixelOnData(pScanline, nX, aTransparentColor); + else + pWriteAcc->SetPixelOnData(pScanline, nX, aOpaqueColor); + } + } + } + + pWriteAcc.reset(); + pReadAcc.reset(); + + aNewBmp.maPrefSize = maPrefSize; + aNewBmp.maPrefMapMode = maPrefMapMode; + + return aNewBmp; +} + +AlphaMask Bitmap::CreateAlphaMask(const Color& rTransColor, sal_uInt8 nTol) const +{ + if (nTol == 0) + return CreateAlphaMask(rTransColor); + + ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this)); + if (!pReadAcc) + return AlphaMask(); + + // Historically LO used 1bpp masks, but 8bpp masks are much faster, + // better supported by hardware, and the memory savings are not worth + // it anymore. + // TODO: Possibly remove the 1bpp code later. + + AlphaMask aNewBmp(GetSizePixel()); + BitmapScopedWriteAccess pWriteAcc(aNewBmp); + if (!pWriteAcc) + return AlphaMask(); + + const tools::Long nWidth = pReadAcc->Width(); + const tools::Long nHeight = pReadAcc->Height(); + const BitmapColor aOpaqueColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_OPAQUE)); + const BitmapColor aTransparentColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_TRANSPARENT)); + + BitmapColor aCol; + tools::Long nR, nG, nB; + const tools::Long nMinR = MinMax<tools::Long>(rTransColor.GetRed() - nTol, 0, 255); + const tools::Long nMaxR = MinMax<tools::Long>(rTransColor.GetRed() + nTol, 0, 255); + const tools::Long nMinG = MinMax<tools::Long>(rTransColor.GetGreen() - nTol, 0, 255); + const tools::Long nMaxG = MinMax<tools::Long>(rTransColor.GetGreen() + nTol, 0, 255); + const tools::Long nMinB = MinMax<tools::Long>(rTransColor.GetBlue() - nTol, 0, 255); + const tools::Long nMaxB = MinMax<tools::Long>(rTransColor.GetBlue() + nTol, 0, 255); + + if (pReadAcc->HasPalette()) + { + for (tools::Long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + for (tools::Long nX = 0; nX < nWidth; nX++) + { + aCol = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, nX)); + nR = aCol.GetRed(); + nG = aCol.GetGreen(); + nB = aCol.GetBlue(); + + if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB + && nMaxB >= nB) + { + pWriteAcc->SetPixelOnData(pScanline, nX, aTransparentColor); + } + else + { + pWriteAcc->SetPixelOnData(pScanline, nX, aOpaqueColor); + } + } + } + } + else + { + for (tools::Long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + for (tools::Long nX = 0; nX < nWidth; nX++) + { + aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX); + nR = aCol.GetRed(); + nG = aCol.GetGreen(); + nB = aCol.GetBlue(); + + if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB + && nMaxB >= nB) + { + pWriteAcc->SetPixelOnData(pScanline, nX, aTransparentColor); + } + else + { + pWriteAcc->SetPixelOnData(pScanline, nX, aOpaqueColor); + } + } + } + } + + pWriteAcc.reset(); + pReadAcc.reset(); + + aNewBmp.maPrefSize = maPrefSize; + aNewBmp.maPrefMapMode = maPrefMapMode; + + return aNewBmp; +} + vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const { tools::Rectangle aRect(rRect); @@ -721,7 +905,7 @@ bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor) for (tools::Long nX = 0; nX < nWidth; nX++) { aCol = pAcc->GetColor(nY, nX); - aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX)); + aCol.Merge(rMergeColor, pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX)); pNewAcc->SetPixelOnData(pScanline, nX, aCol); } } @@ -965,8 +1149,7 @@ bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor) for (tools::Long nX = 0; nX < nWidth; ++nX) { BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX); - aBmpColor.Merge(rBackgroundColor, - 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX)); + aBmpColor.Merge(rBackgroundColor, pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX)); pAcc->SetPixelOnData(pScanline, nX, aBmpColor); } } diff --git a/vcl/source/bitmap/dibtools.cxx b/vcl/source/bitmap/dibtools.cxx index d8fa61362635..d6ac43a403da 100644 --- a/vcl/source/bitmap/dibtools.cxx +++ b/vcl/source/bitmap/dibtools.cxx @@ -1699,7 +1699,11 @@ bool ReadDIBV5( AlphaMask& rTargetAlpha, SvStream& rIStm) { - return ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true); + bool rv = ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true); + // convert transparency->alpha + if (rv) + rTargetAlpha.Invert(); + return rv; } bool ReadRawDIB( @@ -1747,7 +1751,10 @@ bool WriteDIBBitmapEx( if(rSource.IsAlpha()) { - return ImplWriteDIB(rSource.maAlphaMask, rOStm, true, true); + // invert the alpha because the other routines actually want transparency + AlphaMask tmpAlpha = rSource.maAlphaMask; + tmpAlpha.Invert(); + return ImplWriteDIB(tmpAlpha, rOStm, true, true); } } diff --git a/vcl/source/filter/eps/eps.cxx b/vcl/source/filter/eps/eps.cxx index 455e89cc543a..a107a5d400c6 100644 --- a/vcl/source/filter/eps/eps.cxx +++ b/vcl/source/filter/eps/eps.cxx @@ -200,7 +200,7 @@ private: void ImplPolyLine( const tools::Polygon & rPolygon ); void ImplSetClipRegion( vcl::Region const & rRegion ); - void ImplBmp( Bitmap const *, Bitmap const *, const Point &, double nWidth, double nHeight ); + void ImplBmp( Bitmap const *, AlphaMask const *, const Point &, double nWidth, double nHeight ); void ImplText( const OUString& rUniString, const Point& rPos, KernArraySpan pDXArry, o3tl::span<const sal_Bool> pKashidaArry, sal_Int32 nWidth, VirtualDevice const & rVDev ); void ImplSetAttrForText( const Point & rPoint ); void ImplWriteCharacter( char ); @@ -1641,7 +1641,7 @@ void PSWriter::ImplSetClipRegion( vcl::Region const & rClipRegion ) // color 1(pal), 4(pal), 8(pal), 24 Bit // -void PSWriter::ImplBmp( Bitmap const * pBitmap, Bitmap const * pMaskBitmap, const Point & rPoint, double nXWidth, double nYHeightOrg ) +void PSWriter::ImplBmp( Bitmap const * pBitmap, AlphaMask const * pAlphaMaskBitmap, const Point & rPoint, double nXWidth, double nYHeightOrg ) { if ( !pBitmap ) return; @@ -1662,7 +1662,7 @@ void PSWriter::ImplBmp( Bitmap const * pBitmap, Bitmap const * pMaskBitmap, cons tools::Rectangle aRect; vcl::Region aRegion; - if ( pMaskBitmap ) + if ( pAlphaMaskBitmap ) { bDoTrans = true; while (true) @@ -1670,7 +1670,7 @@ void PSWriter::ImplBmp( Bitmap const * pBitmap, Bitmap const * pMaskBitmap, cons if ( mnLevel == 1 && nHeight > 10 ) nHeight = 8; aRect = tools::Rectangle( Point( 0, nHeightOrg - nHeightLeft ), Size( nWidth, nHeight ) ); - aRegion = pMaskBitmap->CreateRegion( COL_BLACK, aRect ); + aRegion = pAlphaMaskBitmap->CreateRegion( COL_ALPHA_OPAQUE, aRect ); if( mnLevel == 1 ) { diff --git a/vcl/source/filter/igif/gifread.cxx b/vcl/source/filter/igif/gifread.cxx index 7cdc37710f51..818317f8baa8 100644 --- a/vcl/source/filter/igif/gifread.cxx +++ b/vcl/source/filter/igif/gifread.cxx @@ -661,7 +661,9 @@ void GIFReader::CreateNewBitmaps() if( bGCTransparent ) { pAcc1.reset(); - aAnimationFrame.maBitmapEx = BitmapEx( aBmp8, aBmp1 ); + AlphaMask aAlphaMask(aBmp1); + aAlphaMask.Invert(); // convert from transparency to alpha + aAnimationFrame.maBitmapEx = BitmapEx( aBmp8, aAlphaMask ); } else aAnimationFrame.maBitmapEx = BitmapEx( aBmp8 ); diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx index e3d70f7c0ade..18f8c4dee95e 100644 --- a/vcl/source/filter/ipdf/pdfread.cxx +++ b/vcl/source/filter/ipdf/pdfread.cxx @@ -102,8 +102,7 @@ size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<BitmapEx>& r pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride); for (int nCol = 0; nCol < nPageWidth; ++nCol) { - // Invert alpha (source is alpha, target is opacity). - aScanlineAlpha[nCol] = ~pPdfLine[3]; + aScanlineAlpha[nCol] = pPdfLine[3]; pPdfLine += 4; } pMaskAccess->CopyScanline(nRow, aScanlineAlpha.data(), ScanlineFormat::N8BitPal, diff --git a/vcl/source/filter/ipsd/ipsd.cxx b/vcl/source/filter/ipsd/ipsd.cxx index 0ded0938efef..79ccda943dca 100644 --- a/vcl/source/filter/ipsd/ipsd.cxx +++ b/vcl/source/filter/ipsd/ipsd.cxx @@ -727,7 +727,7 @@ bool PSDReader::ImplReadBody() m_rPSD.ReadUChar( nDummy ); for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ ) { - mpBitmap->SetAlpha(nY, nX, nDat ? 0 : 255); + mpBitmap->SetAlpha(nY, nX, nDat ? 255 : 0); if ( ++nX == mpFileHeader->nColumns ) { nX = 0; @@ -748,7 +748,7 @@ bool PSDReader::ImplReadBody() nDat = 1; if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped m_rPSD.ReadUChar( nDummy ); - mpBitmap->SetAlpha(nY, nX, nDat ? 0 : 255); + mpBitmap->SetAlpha(nY, nX, nDat ? 255 : 0); if ( ++nX == mpFileHeader->nColumns ) { nX = 0; diff --git a/vcl/source/filter/itiff/itiff.cxx b/vcl/source/filter/itiff/itiff.cxx index 439cbd856182..bdbf0309e296 100644 --- a/vcl/source/filter/itiff/itiff.cxx +++ b/vcl/source/filter/itiff/itiff.cxx @@ -277,7 +277,7 @@ bool ImportTiffGraphicImport(SvStream& rTIFF, Graphic& rGraphic) } access->SetPixel(y, dest, Color(r, g, b)); - accessAlpha->SetPixelIndex(y, dest, 255 - a); + accessAlpha->SetPixelIndex(y, dest, a); ++src; } } diff --git a/vcl/source/filter/jpeg/JpegReader.cxx b/vcl/source/filter/jpeg/JpegReader.cxx index 8d190200face..186105603904 100644 --- a/vcl/source/filter/jpeg/JpegReader.cxx +++ b/vcl/source/filter/jpeg/JpegReader.cxx @@ -249,8 +249,8 @@ Graphic JPEGReader::CreateIntermediateGraphic(tools::Long nLines) if (!mnLastLines) { - mpIncompleteAlpha.emplace(aSizePixel, vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256)); - mpIncompleteAlpha->Erase(COL_WHITE); + mpIncompleteAlpha.emplace(aSizePixel); + mpIncompleteAlpha->Erase(255); } if (nLines && (nLines < aSizePixel.Height())) @@ -261,7 +261,7 @@ Graphic JPEGReader::CreateIntermediateGraphic(tools::Long nLines) { { BitmapScopedWriteAccess pAccess(*mpIncompleteAlpha); - pAccess->SetFillColor(COL_BLACK); + pAccess->SetFillColor(COL_ALPHA_OPAQUE); pAccess->FillRect(tools::Rectangle(Point(0, mnLastLines), Size(pAccess->Width(), nNewLines))); } diff --git a/vcl/source/filter/jpeg/JpegReader.hxx b/vcl/source/filter/jpeg/JpegReader.hxx index a4df7e212119..576027237ab8 100644 --- a/vcl/source/filter/jpeg/JpegReader.hxx +++ b/vcl/source/filter/jpeg/JpegReader.hxx @@ -49,7 +49,7 @@ class JPEGReader : public GraphicReader { SvStream& mrStream; std::optional<Bitmap> mpBitmap; - std::optional<Bitmap> mpIncompleteAlpha; + std::optional<AlphaMask> mpIncompleteAlpha; tools::Long mnLastPos; tools::Long mnLastLines; diff --git a/vcl/source/filter/png/PngImageReader.cxx b/vcl/source/filter/png/PngImageReader.cxx index e7b5c414762b..fcdcd210a157 100644 --- a/vcl/source/filter/png/PngImageReader.cxx +++ b/vcl/source/filter/png/PngImageReader.cxx @@ -465,6 +465,7 @@ bool reader(SvStream& rStream, Graphic& rGraphic, { aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP); aBitmapAlpha = AlphaMask(Size(width, height), nullptr); + aBitmapAlpha.Erase(0); // opaque } break; case PNG_COLOR_TYPE_GRAY: @@ -605,7 +606,7 @@ bool reader(SvStream& rStream, Graphic& rGraphic, pScanline[iColor++] = pRow[i + 0]; pScanline[iColor++] = pRow[i + 1]; pScanline[iColor++] = pRow[i + 2]; - pScanAlpha[iAlpha++] = 0xFF - pRow[i + 3]; + pScanAlpha[iAlpha++] = pRow[i + 3]; } } } @@ -629,7 +630,7 @@ bool reader(SvStream& rStream, Graphic& rGraphic, pScanline[iColor++] = pRow[i + 0]; pScanline[iColor++] = pRow[i + 1]; pScanline[iColor++] = pRow[i + 2]; - pScanAlpha[iAlpha++] = 0xFF - pRow[i + 3]; + pScanAlpha[iAlpha++] = pRow[i + 3]; } } } ... etc. - the rest is truncated