bin/find-can-be-private-symbols.functions.results | 1 canvas/inc/pch/precompiled_vclcanvas.hxx | 1 canvas/source/vcl/canvascustomsprite.cxx | 16 + canvas/source/vcl/canvashelper.cxx | 155 ++++++++++++++ canvas/source/vcl/canvashelper.hxx | 10 canvas/source/vcl/canvashelper_texturefill.cxx | 66 +++++ canvas/source/vcl/spritehelper.cxx | 34 ++- canvas/source/vcl/spritehelper.hxx | 2 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx | 100 +++++++-- include/vcl/bitmap/BitmapAlphaClampFilter.hxx | 31 ++ include/vcl/rendercontext/DrawModeFlags.hxx | 6 vcl/Library_vcl.mk | 1 vcl/qa/cppunit/BitmapFilterTest.cxx | 18 + vcl/qa/cppunit/drawmode.cxx | 16 + vcl/qa/cppunit/outdev.cxx | 89 ++++++++ vcl/source/bitmap/BitmapAlphaClampFilter.cxx | 43 +++ vcl/source/outdev/bitmap.cxx | 17 + vcl/source/outdev/bitmapex.cxx | 2 vcl/source/rendercontext/drawmode.cxx | 30 ++ 19 files changed, 610 insertions(+), 28 deletions(-)
New commits: commit 074bdd6962b61198cf82fe8394de1b3a6f6a66be Author: Noel Grandin <[email protected]> AuthorDate: Wed Nov 12 15:57:20 2025 +0200 Commit: Noel Grandin <[email protected]> CommitDate: Thu Nov 13 06:57:13 2025 +0100 tdf#168721 Fade in animation on grouped shapes not shown There is definitely a better fix for this, we should not need two different bitmaps and masking and stuff, but this will fix the problem for now. Mostly revert the following commits: commit 7278030b92ff27c4937a80e01442597aec2d847c Author: Noel Grandin <[email protected]> Date: Mon Sep 15 16:26:25 2025 +0200 remove unused DrawModeFlags commit 581c52dad2694df1e1d9d74fc687ae505747127d Author: Noel Grandin <[email protected]> Date: Wed Sep 10 15:08:04 2025 +0200 BitmapAlphaClampFilter is dead since commit e7b460f9197a57360dd017481dc747170e6a729f Author: Noel Grandin <[email protected]> Date: Sat Aug 30 15:23:11 2025 +0200 BitmapEx->Bitmap in canvas Change-Id: Ic5747ce6143a8320f977111c7635bf736a9996d9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193873 Reviewed-by: Noel Grandin <[email protected]> Tested-by: Jenkins diff --git a/bin/find-can-be-private-symbols.functions.results b/bin/find-can-be-private-symbols.functions.results index 6235f5df43fa..732761e70a60 100644 --- a/bin/find-can-be-private-symbols.functions.results +++ b/bin/find-can-be-private-symbols.functions.results @@ -32,6 +32,7 @@ BasicManager::SetLibraryContainerInfo(LibraryContainerInfo const&) BasicManager::~BasicManager() BigInt::BigInt(unsigned int) BigInt::operator%=(BigInt const&) +BitmapAlphaClampFilter::execute(BitmapEx const&) const BitmapBasicMorphologyFilter::filter(Bitmap const&) const BitmapColorQuantizationFilter::execute(BitmapEx const&) const BitmapConvolutionMatrixFilter::execute(BitmapEx const&) const diff --git a/canvas/inc/pch/precompiled_vclcanvas.hxx b/canvas/inc/pch/precompiled_vclcanvas.hxx index dee8538d638d..d8a0e367643f 100644 --- a/canvas/inc/pch/precompiled_vclcanvas.hxx +++ b/canvas/inc/pch/precompiled_vclcanvas.hxx @@ -35,6 +35,7 @@ #include <vcl/BitmapReadAccess.hxx> #include <vcl/BitmapTools.hxx> #include <vcl/alpha.hxx> +#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> #include <vcl/canvastools.hxx> #include <vcl/dibtools.hxx> #include <vcl/gradient.hxx> diff --git a/canvas/source/vcl/canvascustomsprite.cxx b/canvas/source/vcl/canvascustomsprite.cxx index a19dd18c4781..316176f6ac24 100644 --- a/canvas/source/vcl/canvascustomsprite.cxx +++ b/canvas/source/vcl/canvascustomsprite.cxx @@ -58,27 +58,43 @@ namespace vclcanvas BackBufferSharedPtr pBackBuffer = std::make_shared<BackBuffer>( rOutDevProvider->getOutDev() ); pBackBuffer->setSize( aSize ); + // create mask backbuffer + BackBufferSharedPtr pBackBufferMask = std::make_shared<BackBuffer>( rOutDevProvider->getOutDev() ); + pBackBufferMask->setSize( aSize ); + // TODO(F1): Implement alpha vdev (could prolly enable // antialiasing again, then) // disable font antialiasing (causes ugly shadows otherwise) pBackBuffer->getOutDev().SetAntialiasing( AntialiasingFlags::DisableText ); + pBackBufferMask->getOutDev().SetAntialiasing( AntialiasingFlags::DisableText ); + + // set mask vdev drawmode, such that everything is painted + // black. That leaves us with a binary image, white for + // background, black for painted content + pBackBufferMask->getOutDev().SetDrawMode( DrawModeFlags::BlackLine | DrawModeFlags::BlackFill | DrawModeFlags::BlackText | + DrawModeFlags::BlackGradient | DrawModeFlags::BlackBitmap ); + // setup canvas helper + // always render into back buffer, don't preserve state (it's // our private VDev, after all), have notion of alpha maCanvasHelper.init( rDevice, pBackBuffer, false, true ); + maCanvasHelper.setBackgroundOutDev( pBackBufferMask ); // setup sprite helper + maSpriteHelper.init( rSpriteSize, rOwningSpriteCanvas, pBackBuffer, + pBackBufferMask, bShowSpriteBounds ); // clear sprite to 100% transparent diff --git a/canvas/source/vcl/canvashelper.cxx b/canvas/source/vcl/canvashelper.cxx index 4866bcdd6f1e..68d7fda9d9b8 100644 --- a/canvas/source/vcl/canvashelper.cxx +++ b/canvas/source/vcl/canvashelper.cxx @@ -37,9 +37,12 @@ #include <rtl/math.hxx> #include <comphelper/diagnose_ex.hxx> #include <tools/poly.hxx> +#include <vcl/alpha.hxx> #include <vcl/bitmap.hxx> +#include <vcl/bitmap/BitmapFilter.hxx> #include <vcl/BitmapReadAccess.hxx> #include <vcl/canvastools.hxx> +#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> #include <vcl/skia/SkiaHelper.hxx> #include <canvas/canvastools.hxx> @@ -113,6 +116,7 @@ namespace vclcanvas mpDevice = nullptr; mpProtectedOutDevProvider.reset(); mpOutDevProvider.reset(); + mp2ndOutDevProvider.reset(); } void CanvasHelper::init( rendering::XGraphicDevice& rDevice, @@ -139,6 +143,13 @@ namespace vclcanvas mpOutDevProvider = rOutDev; } + void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev ) + { + mp2ndOutDevProvider = rOutDev; + mp2ndOutDevProvider->getOutDev().EnableMapMode( false ); + mp2ndOutDevProvider->getOutDev().SetAntialiasing( AntialiasingFlags::Enable ); + } + void CanvasHelper::clear() { // are we disposed? @@ -155,6 +166,22 @@ namespace vclcanvas rOutDev.SetClipRegion(); rOutDev.DrawRect( ::tools::Rectangle( Point(), rOutDev.GetOutputSizePixel()) ); + + if( !mp2ndOutDevProvider ) + return; + + OutputDevice& rOutDev2( mp2ndOutDevProvider->getOutDev() ); + + rOutDev2.SetDrawMode( DrawModeFlags::Default ); + rOutDev2.EnableMapMode( false ); + rOutDev2.SetAntialiasing( AntialiasingFlags::Enable ); + rOutDev2.SetLineColor( COL_WHITE ); + rOutDev2.SetFillColor( COL_WHITE ); + rOutDev2.SetClipRegion(); + rOutDev2.DrawRect( ::tools::Rectangle( Point(), + rOutDev2.GetOutputSizePixel()) ); + rOutDev2.SetDrawMode( DrawModeFlags::BlackLine | DrawModeFlags::BlackFill | DrawModeFlags::BlackText | + DrawModeFlags::BlackGradient | DrawModeFlags::BlackBitmap ); } void CanvasHelper::drawLine( const rendering::XCanvas* , @@ -177,6 +204,9 @@ namespace vclcanvas viewState, renderState ) ); // TODO(F2): alpha mpOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint ); + + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint ); } void CanvasHelper::drawBezier( const rendering::XCanvas* , @@ -215,6 +245,8 @@ namespace vclcanvas // TODO(F2): alpha mpOutDevProvider->getOutDev().DrawPolygon( aPoly ); + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawPolygon( aPoly ); } uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* , @@ -237,6 +269,9 @@ namespace vclcanvas if( aBasegfxPolyPoly.isClosed() ) { mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); + + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); } else { @@ -252,6 +287,9 @@ namespace vclcanvas for( sal_uInt16 i=0; i<nSize; ++i ) { mpOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] ); + + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] ); } } } @@ -366,8 +404,12 @@ namespace vclcanvas const basegfx::B2DPolygon& polygon = aStrokedPolyPoly.getB2DPolygon( i ); if( polygon.isClosed()) { mpOutDevProvider->getOutDev().DrawPolygon( polygon ); + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawPolygon( polygon ); } else { mpOutDevProvider->getOutDev().DrawPolyLine( polygon ); + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawPolyLine( polygon ); } } } @@ -435,6 +477,19 @@ namespace vclcanvas const int nTransPercent( ((255 - nAlpha) * 100 + 128) / 255 ); // normal rounding, no truncation here mpOutDevProvider->getOutDev().DrawTransparent( aPolyPoly, static_cast<sal_uInt16>(nTransPercent) ); } + + if( mp2ndOutDevProvider ) + { + // HACK. Normally, CanvasHelper does not care about + // actually what mp2ndOutDev is... well, here we do & + // assume a 1bpp target - everything beyond 97% + // transparency is fully transparent + if( nAlpha > 2 ) + { + mp2ndOutDevProvider->getOutDev().SetFillColor( COL_BLACK ); + mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); + } + } } // TODO(P1): Provide caching here. @@ -518,6 +573,15 @@ namespace vclcanvas text.Text, ::canvastools::numeric_cast<sal_uInt16>(text.StartPosition), ::canvastools::numeric_cast<sal_uInt16>(text.Length) ); + + if( mp2ndOutDevProvider ) + { + mp2ndOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode ); + mp2ndOutDevProvider->getOutDev().DrawText( aOutpos, + text.Text, + ::canvastools::numeric_cast<sal_uInt16>(text.StartPosition), + ::canvastools::numeric_cast<sal_uInt16>(text.Length) ); + } } return uno::Reference< rendering::XCachedPrimitive >(nullptr); @@ -551,6 +615,9 @@ namespace vclcanvas // TODO(F2): What about the offset scalings? // TODO(F2): alpha pTextLayout->draw( mpOutDevProvider->getOutDev(), aOutpos, viewState, renderState ); + + if( mp2ndOutDevProvider ) + pTextLayout->draw( mp2ndOutDevProvider->getOutDev(), aOutpos, viewState, renderState ); } } else @@ -618,6 +685,21 @@ namespace vclcanvas mpOutDevProvider->getOutDev().DrawBitmap( vcl::unotools::pointFromB2DPoint( aOutputPos ), aBmp ); + if( mp2ndOutDevProvider ) + { + // HACK. Normally, CanvasHelper does not care about + // actually what mp2ndOutDev is... well, here we do & + // assume a 1bpp target - everything beyond 97% + // transparency is fully transparent + if( aBmp.HasAlpha() && !SkiaHelper::isVCLSkiaEnabled()) + { + BitmapFilter::Filter(aBmp, BitmapAlphaClampFilter(253)); + } + + mp2ndOutDevProvider->getOutDev().DrawBitmap( vcl::unotools::pointFromB2DPoint( aOutputPos ), + aBmp ); + } + // Returning a cache object is not useful, the XBitmap // itself serves this purpose return uno::Reference< rendering::XCachedPrimitive >(nullptr); @@ -630,6 +712,31 @@ namespace vclcanvas const double fAlpha = bModulateColors ? renderState.DeviceColor[3] : 1.0; mpOutDevProvider->getOutDev().DrawTransformedBitmapEx( aMatrix, aBmp, fAlpha ); + if( mp2ndOutDevProvider ) + { + if( aBmp.HasAlpha() ) + { + // tdf#157790 invert alpha mask + // Due to commit 81994cb2b8b32453a92bcb011830fcb884f22ff3, + // the alpha mask needs to be inverted. Note: when + // testing tdf#157790, this code only gets executed + // when Skia is enabled. + AlphaMask aAlpha( aBmp.CreateAlphaMask() ); + aAlpha.Invert(); + aBmp = Bitmap( aBmp.CreateColorBitmap(), aAlpha ); + + // HACK. Normally, CanvasHelper does not care about + // actually what mp2ndOutDev is... well, here we do & + // assume a 1bpp target - everything beyond 97% + // transparency is fully transparent + if( !SkiaHelper::isVCLSkiaEnabled()) + { + BitmapFilter::Filter(aBmp, BitmapAlphaClampFilter(253)); + } + } + + mp2ndOutDevProvider->getOutDev().DrawTransformedBitmapEx( aMatrix, aBmp ); + } return uno::Reference< rendering::XCachedPrimitive >(nullptr); } else @@ -724,6 +831,28 @@ namespace vclcanvas aSz, &aGrfAttr); + if( mp2ndOutDevProvider ) + { + GraphicObjectSharedPtr p2ndGrfObj = pGrfObj; + if( aBmp.HasAlpha() ) + { + // tdf#157790 invert alpha mask + // Due to commit 81994cb2b8b32453a92bcb011830fcb884f22ff3, + // the alpha mask needs to be inverted. Note: when + // testing tdf#157790, this code only gets executed + // when Skia is disabled. + AlphaMask aAlpha( aBmp.CreateAlphaMask() ); + aAlpha.Invert(); + Bitmap a2ndBmp( aBmp.CreateColorBitmap(), aAlpha ); + p2ndGrfObj = std::make_shared<GraphicObject>( a2ndBmp ); + } + + p2ndGrfObj->Draw(mp2ndOutDevProvider->getOutDev(), + aPt, + aSz, + &aGrfAttr); + } + // created GraphicObject, which possibly cached // display bitmap - return cache object, to retain // that information. @@ -912,6 +1041,10 @@ namespace vclcanvas rOutDev.EnableMapMode( false ); rOutDev.SetAntialiasing( AntialiasingFlags::Enable ); + OutputDevice* p2ndOutDev = nullptr; + + if( mp2ndOutDevProvider ) + p2ndOutDev = &mp2ndOutDevProvider->getOutDev(); int nAlpha(255); @@ -939,15 +1072,27 @@ namespace vclcanvas case LINE_COLOR: rOutDev.SetLineColor( aColor ); rOutDev.SetFillColor(); + if( p2ndOutDev ) + { + p2ndOutDev->SetLineColor( aColor ); + p2ndOutDev->SetFillColor(); + } break; case FILL_COLOR: rOutDev.SetFillColor( aColor ); rOutDev.SetLineColor(); + if( p2ndOutDev ) + { + p2ndOutDev->SetFillColor( aColor ); + p2ndOutDev->SetLineColor(); + } break; case TEXT_COLOR: rOutDev.SetTextColor( aColor ); + if( p2ndOutDev ) + p2ndOutDev->SetTextColor( aColor ); break; default: @@ -996,6 +1141,9 @@ namespace vclcanvas rOutDev.SetFont( aVCLFont ); + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().SetFont( aVCLFont ); + return true; } @@ -1019,6 +1167,10 @@ namespace vclcanvas if (!rGrf->Draw(mpOutDevProvider->getOutDev(), rPt, rSz, &rAttr)) return false; + // #i80779# Redraw also into mask outdev + if (mp2ndOutDevProvider) + return rGrf->Draw(mp2ndOutDevProvider->getOutDev(), rPt, rSz, &rAttr); + return true; } } @@ -1027,6 +1179,9 @@ namespace vclcanvas { if (mpOutDevProvider) mpOutDevProvider->getOutDev().Flush(); + + if (mp2ndOutDevProvider) + mp2ndOutDevProvider->getOutDev().Flush(); } } diff --git a/canvas/source/vcl/canvashelper.hxx b/canvas/source/vcl/canvashelper.hxx index 86472c790eb1..cf406d287351 100644 --- a/canvas/source/vcl/canvashelper.hxx +++ b/canvas/source/vcl/canvashelper.hxx @@ -86,6 +86,13 @@ namespace vclcanvas void setOutDev( const OutDevProviderSharedPtr& rOutDev, bool bProtect); + /** Set secondary output device + + Used for sprites, to generate mask bitmap. + */ + void setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev ); + + // CanvasHelper functionality // ========================== @@ -275,6 +282,9 @@ namespace vclcanvas /// Rendering to this outdev does not preserve its state OutDevProviderSharedPtr mpOutDevProvider; + /// Rendering to this outdev does not preserve its state + OutDevProviderSharedPtr mp2ndOutDevProvider; + /// When true, content is able to represent alpha bool mbHaveAlpha; diff --git a/canvas/source/vcl/canvashelper_texturefill.cxx b/canvas/source/vcl/canvashelper_texturefill.cxx index fde866405934..49c9e56438e7 100644 --- a/canvas/source/vcl/canvashelper_texturefill.cxx +++ b/canvas/source/vcl/canvashelper_texturefill.cxx @@ -478,12 +478,14 @@ namespace vclcanvas } bool gradientFill( OutputDevice& rOutDev, + OutputDevice* p2ndOutDev, const ::canvas::ParametricPolyPolygon::Values& rValues, const std::vector< ::Color >& rColors, const ::tools::PolyPolygon& rPoly, const rendering::ViewState& viewState, const rendering::RenderState& renderState, - const rendering::Texture& texture ) + const rendering::Texture& texture, + int nTransparency ) { // TODO(T2): It is maybe necessary to lock here, should // maGradientPoly someday cease to be const. But then, beware of @@ -532,6 +534,16 @@ namespace vclcanvas aPolygonDeviceRectOrig, nStepCount ); rOutDev.Pop(); + + if( p2ndOutDev && nTransparency < 253 ) + { + // HACK. Normally, CanvasHelper does not care about + // actually what mp2ndOutDev is... well, here we do & + // assume a 1bpp target - everything beyond 97% + // transparency is fully transparent + p2ndOutDev->SetFillColor( COL_BLACK ); + p2ndOutDev->DrawRect( aPolygonDeviceRectOrig ); + } } else { @@ -547,6 +559,16 @@ namespace vclcanvas aPolygonDeviceRectOrig, nStepCount ); rOutDev.Pop(); + + if( p2ndOutDev && nTransparency < 253 ) + { + // HACK. Normally, CanvasHelper does not care about + // actually what mp2ndOutDev is... well, here we do & + // assume a 1bpp target - everything beyond 97% + // transparency is fully transparent + p2ndOutDev->SetFillColor( COL_BLACK ); + p2ndOutDev->DrawPolyPolygon( rPoly ); + } } #ifdef DEBUG_CANVAS_CANVASHELPER_TEXTUREFILL @@ -590,7 +612,7 @@ namespace vclcanvas { vclcanvastools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); - setupOutDevState( viewState, renderState, IGNORE_COLOR ); + const int nTransparency( setupOutDevState( viewState, renderState, IGNORE_COLOR ) ); ::tools::PolyPolygon aPolyPoly( vclcanvastools::mapPolyPolygon( ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon), viewState, renderState ) ); @@ -629,12 +651,14 @@ namespace vclcanvas // TODO(E1): Return value // TODO(F1): FillRule gradientFill( mpOutDevProvider->getOutDev(), + mp2ndOutDevProvider ? &mp2ndOutDevProvider->getOutDev() : nullptr, aValues, aColors, aPolyPoly, viewState, renderState, - textures[0] ); + textures[0], + nTransparency ); } } else @@ -925,6 +949,21 @@ namespace vclcanvas nTilesY, aSz, aGrfAttr ); + + if( mp2ndOutDevProvider ) + { + OutputDevice& r2ndOutDev( mp2ndOutDevProvider->getOutDev() ); + r2ndOutDev.IntersectClipRegion( aPolygonDeviceRect ); + textureFill( r2ndOutDev, + *pGrfObj, + aPt, + aIntegerNextTileX, + aIntegerNextTileY, + nTilesX, + nTilesY, + aSz, + aGrfAttr ); + } } else { @@ -975,6 +1014,10 @@ namespace vclcanvas Bitmap aOutputBmp( aContentBmp.CreateColorBitmap(), aAlpha ); rOutDev.DrawBitmap( aPolygonDeviceRect.TopLeft(), aOutputBmp ); + + if( mp2ndOutDevProvider ) + mp2ndOutDevProvider->getOutDev().DrawBitmap( aPolygonDeviceRect.TopLeft(), + aOutputBmp ); } else { @@ -993,6 +1036,23 @@ namespace vclcanvas aSz, aGrfAttr ); rOutDev.Pop(); + + if( mp2ndOutDevProvider ) + { + OutputDevice& r2ndOutDev( mp2ndOutDevProvider->getOutDev() ); + auto popIt = r2ndOutDev.ScopedPush(vcl::PushFlags::CLIPREGION); + + r2ndOutDev.IntersectClipRegion( aPolyClipRegion ); + textureFill( r2ndOutDev, + *pGrfObj, + aPt, + aIntegerNextTileX, + aIntegerNextTileY, + nTilesX, + nTilesY, + aSz, + aGrfAttr ); + } } } } diff --git a/canvas/source/vcl/spritehelper.cxx b/canvas/source/vcl/spritehelper.cxx index 6588af07971e..1bdab567bd60 100644 --- a/canvas/source/vcl/spritehelper.cxx +++ b/canvas/source/vcl/spritehelper.cxx @@ -49,12 +49,14 @@ namespace vclcanvas void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize, const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas, const BackBufferSharedPtr& rBackBuffer, + const BackBufferSharedPtr& rBackBufferMask, bool bShowSpriteBounds ) { - ENSURE_OR_THROW( rOwningSpriteCanvas && rBackBuffer, + ENSURE_OR_THROW( rOwningSpriteCanvas && rBackBuffer && rBackBufferMask, "SpriteHelper::init(): Invalid sprite canvas or back buffer" ); mpBackBuffer = rBackBuffer; + mpBackBufferMask = rBackBufferMask; mbShowSpriteBounds = bShowSpriteBounds; init( rSpriteSize, rOwningSpriteCanvas ); @@ -63,6 +65,7 @@ namespace vclcanvas void SpriteHelper::disposing() { mpBackBuffer.reset(); + mpBackBufferMask.reset(); // forward to parent CanvasCustomSpriteHelper::disposing(); @@ -73,7 +76,8 @@ namespace vclcanvas bool& io_bSurfacesDirty, bool /*bBufferedUpdate*/ ) const { - if( !mpBackBuffer ) + if( !mpBackBuffer || + !mpBackBufferMask ) { return; // we're disposed } @@ -117,10 +121,28 @@ namespace vclcanvas Bitmap aBmp( mpBackBuffer->getOutDev().GetBitmap( aEmptyPoint, aOutputSize ) ); - // Note: since we retrieved aBmp directly - // from an OutDev, it's already a 'display bitmap' - // on windows. - maContent = aBmp; + if( isContentFullyOpaque() ) + { + // optimized case: content canvas is fully + // opaque. Note: since we retrieved aBmp directly + // from an OutDev, it's already a 'display bitmap' + // on windows. + maContent = aBmp; + } + else + { + // sprite content might contain alpha, create + // BmpEx, then. + Bitmap aMask( mpBackBufferMask->getOutDev().GetBitmap( aEmptyPoint, + aOutputSize ) ); + AlphaMask aAlpha( aMask ); + aAlpha.Invert(); + + // Note: since we retrieved aBmp and aMask + // directly from an OutDev, it's already a + // 'display bitmap' on windows. + maContent = Bitmap( aBmp, aAlpha ); + } } ::basegfx::B2DHomMatrix aTransform( getTransformation() ); diff --git a/canvas/source/vcl/spritehelper.hxx b/canvas/source/vcl/spritehelper.hxx index 328acb748c3b..8cfa418c2115 100644 --- a/canvas/source/vcl/spritehelper.hxx +++ b/canvas/source/vcl/spritehelper.hxx @@ -64,6 +64,7 @@ namespace vclcanvas void init( const css::geometry::RealSize2D& rSpriteSize, const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas, const BackBufferSharedPtr& rBackBuffer, + const BackBufferSharedPtr& rBackBufferMask, bool bShowSpriteBounds ); void disposing(); @@ -93,6 +94,7 @@ namespace vclcanvas // for the redraw BackBufferSharedPtr mpBackBuffer; + BackBufferSharedPtr mpBackBufferMask; /// Cached bitmap for the current sprite content mutable ::canvas::vcltools::VCLObject<Bitmap> maContent; diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index 9fb4991ebb36..24e29f2b4ae7 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -1251,16 +1251,33 @@ Bitmap CairoPixelProcessor2D::extractBitmap() const void CairoPixelProcessor2D::processBitmapPrimitive2D( const primitive2d::BitmapPrimitive2D& rBitmapCandidate) { + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); - const bool bDrawModeFlagsUsed(aDrawModeFlags & DrawModeFlags::GrayBitmap); + const bool bDrawModeFlagsUsed(aDrawModeFlags & BITMAP); if (bDrawModeFlagsUsed) { // if DrawModeFlags for Bitmap are used, encapsulate with // corresponding BColorModifier - const basegfx::BColorModifierSharedPtr aBColorModifier( - std::make_shared<basegfx::BColorModifier_gray>()); - maBColorModifierStack.push(aBColorModifier); + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0, 0, 0))); + maBColorModifierStack.push(aBColorModifier); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1, 1, 1))); + maBColorModifierStack.push(aBColorModifier); + } + else // DrawModeFlags::GrayBitmap + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } } paintBitmapAlpha(rBitmapCandidate.getBitmap(), rBitmapCandidate.getTransform()); @@ -2083,12 +2100,29 @@ void CairoPixelProcessor2D::processMarkerArrayPrimitive2D( // prepare Marker's Bitmap Bitmap aBitmap(rMarkerArrayCandidate.getMarker()); + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); - if (aDrawModeFlags & DrawModeFlags::GrayBitmap) + if (aDrawModeFlags & BITMAP) { - const basegfx::BColorModifierSharedPtr aBColorModifier( - std::make_shared<basegfx::BColorModifier_gray>()); - maBColorModifierStack.push(aBColorModifier); + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0, 0, 0))); + maBColorModifierStack.push(aBColorModifier); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1, 1, 1))); + maBColorModifierStack.push(aBColorModifier); + } + else // DrawModeFlags::GrayBitmap + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } // need to apply ColorModifier to Bitmap data aBitmap = aBitmap.Modify(maBColorModifierStack); @@ -2541,15 +2575,30 @@ void CairoPixelProcessor2D::processFillGraphicPrimitive2D( return; } + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); basegfx::BColor aReplacementColor(0, 0, 0); bool bTemporaryGrayColorModifier(false); const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); - if (aDrawModeFlags & DrawModeFlags::GrayBitmap) + if (aDrawModeFlags & BITMAP) { - bTemporaryGrayColorModifier = true; - const basegfx::BColorModifierSharedPtr aBColorModifier( - std::make_shared<basegfx::BColorModifier_gray>()); - maBColorModifierStack.push(aBColorModifier); + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + // aReplacementColor already set + aPreparedBitmap.SetEmpty(); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + aReplacementColor = basegfx::BColor(1, 1, 1); + aPreparedBitmap.SetEmpty(); + } + else // DrawModeFlags::GrayBitmap + { + bTemporaryGrayColorModifier = true; + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } } if (!aPreparedBitmap.IsEmpty() && maBColorModifierStack.count()) @@ -3630,14 +3679,31 @@ void CairoPixelProcessor2D::processPolyPolygonAlphaGradientPrimitive2D( void CairoPixelProcessor2D::processBitmapAlphaPrimitive2D( const primitive2d::BitmapAlphaPrimitive2D& rBitmapAlphaPrimitive2D) { + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); - const bool bDrawModeFlagsUsed(aDrawModeFlags & DrawModeFlags::GrayBitmap); + const bool bDrawModeFlagsUsed(aDrawModeFlags & BITMAP); if (bDrawModeFlagsUsed) { - const basegfx::BColorModifierSharedPtr aBColorModifier( - std::make_shared<basegfx::BColorModifier_gray>()); - maBColorModifierStack.push(aBColorModifier); + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0, 0, 0))); + maBColorModifierStack.push(aBColorModifier); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1, 1, 1))); + maBColorModifierStack.push(aBColorModifier); + } + else // DrawModeFlags::GrayBitmap + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } } if (!rBitmapAlphaPrimitive2D.hasTransparency()) diff --git a/include/vcl/bitmap/BitmapAlphaClampFilter.hxx b/include/vcl/bitmap/BitmapAlphaClampFilter.hxx new file mode 100644 index 000000000000..4a036263e0d9 --- /dev/null +++ b/include/vcl/bitmap/BitmapAlphaClampFilter.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#pragma once + +#include <vcl/bitmap/BitmapFilter.hxx> + +/** If the alpha is beyond a certain threshold, make it fully transparent + */ +class VCL_DLLPUBLIC BitmapAlphaClampFilter final : public BitmapFilter +{ +public: + BitmapAlphaClampFilter(sal_uInt8 cThreshold) + : mcThreshold(cThreshold) + { + } + + virtual Bitmap execute(Bitmap const& rBitmap) const override; + +private: + sal_uInt8 mcThreshold; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/vcl/rendercontext/DrawModeFlags.hxx b/include/vcl/rendercontext/DrawModeFlags.hxx index e3e42703a2d6..b51b1798e877 100644 --- a/include/vcl/rendercontext/DrawModeFlags.hxx +++ b/include/vcl/rendercontext/DrawModeFlags.hxx @@ -28,6 +28,8 @@ enum class DrawModeFlags : sal_uInt32 BlackLine = 0x00000001, BlackFill = 0x00000002, BlackText = 0x00000004, + BlackBitmap = 0x00000008, + BlackGradient = 0x00000010, GrayLine = 0x00000020, GrayFill = 0x00000040, GrayText = 0x00000080, @@ -36,6 +38,8 @@ enum class DrawModeFlags : sal_uInt32 NoFill = 0x00000400, WhiteLine = 0x00000800, WhiteFill = 0x00001000, + WhiteText = 0x00002000, + WhiteBitmap = 0x00004000, WhiteGradient = 0x00008000, SettingsLine = 0x00010000, SettingsFill = 0x00020000, @@ -46,7 +50,7 @@ enum class DrawModeFlags : sal_uInt32 }; namespace o3tl { -template <> struct typed_flags<DrawModeFlags> : is_typed_flags<DrawModeFlags, 0x3f9fe7> +template <> struct typed_flags<DrawModeFlags> : is_typed_flags<DrawModeFlags, 0x3fffff> { }; } diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 677937c97c6e..14088046ee62 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -387,6 +387,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/bitmap/bitmappaint \ vcl/source/bitmap/BitmapArithmeticBlendFilter \ vcl/source/bitmap/BitmapShadowFilter \ + vcl/source/bitmap/BitmapAlphaClampFilter \ vcl/source/bitmap/BitmapBasicMorphologyFilter \ vcl/source/bitmap/BitmapDarkenBlendFilter \ vcl/source/bitmap/BitmapLightenBlendFilter \ diff --git a/vcl/qa/cppunit/BitmapFilterTest.cxx b/vcl/qa/cppunit/BitmapFilterTest.cxx index fb27ab59bd99..4c48d25fd69f 100644 --- a/vcl/qa/cppunit/BitmapFilterTest.cxx +++ b/vcl/qa/cppunit/BitmapFilterTest.cxx @@ -12,6 +12,7 @@ #include <tools/stream.hxx> #include <vcl/BitmapWriteAccess.hxx> +#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> #include <vcl/bitmap/BitmapArithmeticBlendFilter.hxx> #include <vcl/bitmap/BitmapDarkenBlendFilter.hxx> #include <vcl/bitmap/BitmapLightenBlendFilter.hxx> @@ -40,6 +41,7 @@ public: { } + void testClampAlpha(); void testBlurCorrectness(); void testBasicMorphology(); void testPerformance(); @@ -52,6 +54,7 @@ public: void testArithmeticBlendFilter(); CPPUNIT_TEST_SUITE(BitmapFilterTest); + CPPUNIT_TEST(testClampAlpha); CPPUNIT_TEST(testBlurCorrectness); CPPUNIT_TEST(testBasicMorphology); CPPUNIT_TEST(testPerformance); @@ -89,6 +92,21 @@ private: } }; +void BitmapFilterTest::testClampAlpha() +{ + // Setup test bitmap + Size aSize(1, 1); + Bitmap aBitmap24Bit(aSize, vcl::PixelFormat::N24_BPP); + + { + BitmapScopedWriteAccess aWriteAccess(aBitmap24Bit); + aWriteAccess->Erase(COL_RED); + } + + BitmapFilter::Filter(aBitmap24Bit, BitmapAlphaClampFilter(0x7F)); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(0xFF), aBitmap24Bit.GetPixelColor(0, 0).GetAlpha()); +} + void BitmapFilterTest::testBlurCorrectness() { // Setup test bitmap diff --git a/vcl/qa/cppunit/drawmode.cxx b/vcl/qa/cppunit/drawmode.cxx index 14369db5c776..5a2c5b8caf77 100644 --- a/vcl/qa/cppunit/drawmode.cxx +++ b/vcl/qa/cppunit/drawmode.cxx @@ -95,6 +95,10 @@ void VclDrawModeTest::testDrawModeLineColor() aColor, vcl::drawmode::GetLineColor(aColor, DrawModeFlags::GrayText, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetLineColor(aColor, DrawModeFlags::SettingsText, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetLineColor(aColor, DrawModeFlags::BlackBitmap, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetLineColor(aColor, DrawModeFlags::WhiteBitmap, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetLineColor(aColor, DrawModeFlags::GrayBitmap, aStyleSettings)); } @@ -149,6 +153,10 @@ void VclDrawModeTest::testDrawModeFillColor() aColor, vcl::drawmode::GetFillColor(aColor, DrawModeFlags::GrayText, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetFillColor(aColor, DrawModeFlags::SettingsText, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetFillColor(aColor, DrawModeFlags::BlackBitmap, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetFillColor(aColor, DrawModeFlags::WhiteBitmap, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetFillColor(aColor, DrawModeFlags::GrayBitmap, aStyleSettings)); } @@ -187,6 +195,10 @@ void VclDrawModeTest::testDrawModeHatchColor() aColor, vcl::drawmode::GetHatchColor(aColor, DrawModeFlags::GrayText, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetHatchColor(aColor, DrawModeFlags::SettingsText, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetHatchColor(aColor, DrawModeFlags::BlackBitmap, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetHatchColor(aColor, DrawModeFlags::WhiteBitmap, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetHatchColor(aColor, DrawModeFlags::GrayBitmap, aStyleSettings)); } @@ -225,6 +237,10 @@ void VclDrawModeTest::testDrawModeTextColor() aColor, vcl::drawmode::GetTextColor(aColor, DrawModeFlags::GrayFill, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetTextColor(aColor, DrawModeFlags::SettingsFill, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetTextColor(aColor, DrawModeFlags::BlackBitmap, aStyleSettings)); + CPPUNIT_ASSERT_EQUAL( + aColor, vcl::drawmode::GetTextColor(aColor, DrawModeFlags::WhiteBitmap, aStyleSettings)); CPPUNIT_ASSERT_EQUAL( aColor, vcl::drawmode::GetTextColor(aColor, DrawModeFlags::GrayBitmap, aStyleSettings)); } diff --git a/vcl/qa/cppunit/outdev.cxx b/vcl/qa/cppunit/outdev.cxx index 5f3d1d3be325..f64c20d255b9 100644 --- a/vcl/qa/cppunit/outdev.cxx +++ b/vcl/qa/cppunit/outdev.cxx @@ -165,6 +165,95 @@ CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawInvertedBitmap) CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(0, 0), Size(10, 10)), pRectAction->GetRect()); } +CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawBlackBitmap) +{ + ScopedVclPtrInstance<VirtualDevice> pVDev; + Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP); + aBitmap.Erase(COL_RED); + + GDIMetaFile aMtf; + aMtf.Record(pVDev.get()); + + pVDev->SetDrawMode(DrawModeFlags::BlackBitmap); + pVDev->DrawBitmap(Point(0, 0), Size(10, 10), Point(0, 0), Size(10, 10), aBitmap, + MetaActionType::BMP); + + MetaAction* pAction = aMtf.GetAction(0); + CPPUNIT_ASSERT_EQUAL(MetaActionType::PUSH, pAction->GetType()); + auto pPushAction = static_cast<MetaPushAction*>(pAction); + bool bLineFillFlag + = ((vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR) == pPushAction->GetFlags()); + CPPUNIT_ASSERT_MESSAGE("Push flags not LINECOLOR | FILLCOLOR", bLineFillFlag); + + pAction = aMtf.GetAction(1); + CPPUNIT_ASSERT_EQUAL(MetaActionType::LINECOLOR, pAction->GetType()); + auto pLineColorAction = static_cast<MetaLineColorAction*>(pAction); + CPPUNIT_ASSERT_EQUAL(COL_BLACK, pLineColorAction->GetColor()); + + pAction = aMtf.GetAction(2); + CPPUNIT_ASSERT_EQUAL(MetaActionType::FILLCOLOR, pAction->GetType()); + auto pFillColorAction = static_cast<MetaFillColorAction*>(pAction); + CPPUNIT_ASSERT_EQUAL(COL_BLACK, pFillColorAction->GetColor()); + + pAction = aMtf.GetAction(3); + CPPUNIT_ASSERT_EQUAL(MetaActionType::RECT, pAction->GetType()); + auto pRectAction = static_cast<MetaRectAction*>(pAction); + CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(0, 0), Size(10, 10)), pRectAction->GetRect()); + + pAction = aMtf.GetAction(4); + CPPUNIT_ASSERT_EQUAL(MetaActionType::POP, pAction->GetType()); + + // test to see if the color is black + Bitmap aBlackBmp(pVDev->GetBitmap(Point(0, 0), Size(10, 10))); + BitmapScopedReadAccess pReadAccess(aBlackBmp); + const BitmapColor aColor = pReadAccess->GetColor(0, 0); + CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLACK), aColor); +} + +CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawWhiteBitmap) +{ + ScopedVclPtrInstance<VirtualDevice> pVDev; + Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP); + + GDIMetaFile aMtf; + aMtf.Record(pVDev.get()); + + pVDev->SetDrawMode(DrawModeFlags::WhiteBitmap); + pVDev->DrawBitmap(Point(0, 0), Size(10, 10), Point(0, 0), Size(10, 10), aBitmap, + MetaActionType::BMP); + + MetaAction* pAction = aMtf.GetAction(0); + CPPUNIT_ASSERT_EQUAL(MetaActionType::PUSH, pAction->GetType()); + auto pPushAction = static_cast<MetaPushAction*>(pAction); + bool bLineFillFlag + = ((vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR) == pPushAction->GetFlags()); + CPPUNIT_ASSERT_MESSAGE("Push flags not LINECOLOR | FILLCOLOR", bLineFillFlag); + + pAction = aMtf.GetAction(1); + CPPUNIT_ASSERT_EQUAL(MetaActionType::LINECOLOR, pAction->GetType()); + auto pLineColorAction = static_cast<MetaLineColorAction*>(pAction); + CPPUNIT_ASSERT_EQUAL(COL_WHITE, pLineColorAction->GetColor()); + + pAction = aMtf.GetAction(2); + CPPUNIT_ASSERT_EQUAL(MetaActionType::FILLCOLOR, pAction->GetType()); + auto pFillColorAction = static_cast<MetaFillColorAction*>(pAction); + CPPUNIT_ASSERT_EQUAL(COL_WHITE, pFillColorAction->GetColor()); + + pAction = aMtf.GetAction(3); + CPPUNIT_ASSERT_EQUAL(MetaActionType::RECT, pAction->GetType()); + auto pRectAction = static_cast<MetaRectAction*>(pAction); + CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(0, 0), Size(10, 10)), pRectAction->GetRect()); + + pAction = aMtf.GetAction(4); + CPPUNIT_ASSERT_EQUAL(MetaActionType::POP, pAction->GetType()); + + // test to see if the color is white + Bitmap aWhiteBmp(pVDev->GetBitmap(Point(0, 0), Size(10, 10))); + BitmapScopedReadAccess pReadAccess(aWhiteBmp); + const BitmapColor aColor = pReadAccess->GetColor(0, 0); + CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_WHITE), aColor); +} + CPPUNIT_TEST_FIXTURE(VclOutdevTest, testDrawBitmap) { ScopedVclPtrInstance<VirtualDevice> pVDev; diff --git a/vcl/source/bitmap/BitmapAlphaClampFilter.cxx b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx new file mode 100644 index 000000000000..68061d4e37d6 --- /dev/null +++ b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> +#include <vcl/BitmapWriteAccess.hxx> + +Bitmap BitmapAlphaClampFilter::execute(Bitmap const& rBitmap) const +{ + if (!rBitmap.HasAlpha()) + return rBitmap; + + Bitmap aNewBitmap(rBitmap); + { + BitmapScopedWriteAccess pWriteAcc(aNewBitmap); + const Size aSize(aNewBitmap.GetSizePixel()); + + for (sal_Int32 nY = 0; nY < sal_Int32(aSize.Height()); ++nY) + { + Scanline pScan = pWriteAcc->GetScanline(nY); + + for (sal_Int32 nX = 0; nX < sal_Int32(aSize.Width()); ++nX) + { + BitmapColor aBitmapValue(pWriteAcc->GetPixelFromData(pScan, nX)); + if ((255 - aBitmapValue.GetAlpha()) > mcThreshold) + { + aBitmapValue.SetAlpha(0); + pWriteAcc->SetPixelOnData(pScan, nX, aBitmapValue); + } + } + } + } + + return aNewBitmap; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/outdev/bitmap.cxx b/vcl/source/outdev/bitmap.cxx index b163babde789..b434ca2d8d46 100644 --- a/vcl/source/outdev/bitmap.cxx +++ b/vcl/source/outdev/bitmap.cxx @@ -92,6 +92,23 @@ void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, return; } + if (mnDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap)) + { + sal_uInt8 cCmpVal; + + if (mnDrawMode & DrawModeFlags::BlackBitmap) + cCmpVal = 0; + else + cCmpVal = 255; + + Color aCol(cCmpVal, cCmpVal, cCmpVal); + auto popIt = ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); + SetLineColor(aCol); + SetFillColor(aCol); + DrawRect(tools::Rectangle(rDestPt, rDestSize)); + return; + } + Bitmap aBmp(rBitmap); if (mnDrawMode & DrawModeFlags::GrayBitmap && !aBmp.IsEmpty()) diff --git a/vcl/source/outdev/bitmapex.cxx b/vcl/source/outdev/bitmapex.cxx index 30089db85a46..9fc4e50ed42f 100644 --- a/vcl/source/outdev/bitmapex.cxx +++ b/vcl/source/outdev/bitmapex.cxx @@ -249,7 +249,7 @@ void OutputDevice::DrawTransformedBitmapEx( Bitmap bitmap = rBitmap; const bool bInvert(RasterOp::Invert == meRasterOp); - const bool bBitmapChangedColor(mnDrawMode & DrawModeFlags::GrayBitmap); + const bool bBitmapChangedColor(mnDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap | DrawModeFlags::GrayBitmap )); const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile); // tdf#130768 CAUTION(!) using GetViewTransformation() is *not* enough here, it may // be that mnOutOffX/mnOutOffY is used - see AOO bug 75163, mentioned at diff --git a/vcl/source/rendercontext/drawmode.cxx b/vcl/source/rendercontext/drawmode.cxx index 459b86d479d4..c071829079df 100644 --- a/vcl/source/rendercontext/drawmode.cxx +++ b/vcl/source/rendercontext/drawmode.cxx @@ -235,6 +235,36 @@ Bitmap GetBitmap(Bitmap const& rBitmap, DrawModeFlags nDrawMode) { Bitmap aBmp(rBitmap); + if (nDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap)) + { + Bitmap aColorBmp(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP, + &Bitmap::GetGreyPalette(256)); + sal_uInt8 cCmpVal; + + if (nDrawMode & DrawModeFlags::BlackBitmap) + cCmpVal = 0; + else + cCmpVal = 255; + + aColorBmp.Erase(Color(cCmpVal, cCmpVal, cCmpVal)); + + if (aBmp.HasAlpha()) + { + // Create one-bit mask out of alpha channel, by thresholding it at alpha=0.5. As + // DRAWMODE_BLACK/WHITEBITMAP requires monochrome output, having alpha-induced + // grey levels is not acceptable + Bitmap aMask(aBmp.CreateAlphaMask().GetBitmap()); + aMask.Invert(); // convert to transparency + BitmapFilter::Filter(aMask, BitmapMonochromeFilter(129)); + aMask.Invert(); // convert to alpha + aBmp = Bitmap(aColorBmp, aMask); + } + else + { + aBmp = std::move(aColorBmp); + } + } + if (nDrawMode & DrawModeFlags::GrayBitmap && !aBmp.IsEmpty()) aBmp.Convert(BmpConversion::N8BitGreys);
