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);
 

Reply via email to