drawinglayer/source/primitive2d/softedgeprimitive2d.cxx   |   20 ++--
 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx |   17 +--
 drawinglayer/source/processor2d/vclprocessor2d.cxx        |    8 -
 drawinglayer/source/processor3d/defaultprocessor3d.cxx    |    6 -
 include/vcl/bitmap.hxx                                    |    5 +
 vcl/source/bitmap/bitmap.cxx                              |   61 ++++++++++++--
 6 files changed, 83 insertions(+), 34 deletions(-)

New commits:
commit 2ed3f9e32c3d4efe0e38a70aca197fb2d9a40c7a
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Tue Aug 19 21:22:20 2025 +0200
Commit:     Noel Grandin <noelgran...@gmail.com>
CommitDate: Wed Aug 20 08:59:15 2025 +0200

    BitmapEx->Bitmap in VclProcessor2D
    
    now that Bitmap can handle transparency.
    This points out a bug in Bitmap::Modify
    
    Change-Id: I4774727d235493fdaad054cf36f2d2a36b1db32d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189936
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins

diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index fd491119029c..175093bb9e4c 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -515,15 +515,15 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D(
 // direct draw of transformed BitmapEx primitive
 void VclProcessor2D::RenderBitmapPrimitive2D(const 
primitive2d::BitmapPrimitive2D& rBitmapCandidate)
 {
-    BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
+    Bitmap aBitmap(rBitmapCandidate.getBitmap());
     const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
                                                 * 
rBitmapCandidate.getTransform());
 
     if (maBColorModifierStack.count())
     {
-        aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+        aBitmap = aBitmap.Modify(maBColorModifierStack);
 
-        if (aBitmapEx.IsEmpty())
+        if (aBitmap.IsEmpty())
         {
             // color gets completely replaced, get it
             const basegfx::BColor aModifiedColor(
@@ -543,7 +543,7 @@ void VclProcessor2D::RenderBitmapPrimitive2D(const 
primitive2d::BitmapPrimitive2
     // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
 
     // draw using OutputDevice'sDrawTransformedBitmapEx
-    mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx);
+    mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmap);
 }
 
 void VclProcessor2D::RenderFillGraphicPrimitive2D(
diff --git a/vcl/source/bitmap/bitmap.cxx b/vcl/source/bitmap/bitmap.cxx
index d976a4cf5ebf..44e39909a80d 100644
--- a/vcl/source/bitmap/bitmap.cxx
+++ b/vcl/source/bitmap/bitmap.cxx
@@ -1979,20 +1979,21 @@ Bitmap Bitmap::Modify(const 
basegfx::BColorModifierStack& rBColorModifierStack)
         }
     }
 
-    // have to create modified Bitmap
-    Bitmap aChangedBitmap(*this);
+    // have to create modified Bitmap, but we want to preserve the alpha 
information
+    Bitmap aChangedBitmap;
 
     if (nullptr != pLastModifierReplace)
     {
         // special case -> we have BColorModifier_replace but Alpha channel
-        if (vcl::isPalettePixelFormat(aChangedBitmap.getPixelFormat()))
+        if (vcl::isPalettePixelFormat(getPixelFormat()))
         {
+            assert(!HasAlpha());
+            aChangedBitmap = *this;
             // For e.g. 8bit Bitmaps, the nearest color to the given erase 
color is
             // determined and used -> this may be different from what is 
wanted here.
             // Better create a new bitmap with the needed color explicitly.
             BitmapScopedReadAccess xReadAccess(aChangedBitmap);
-            SAL_WARN_IF(!xReadAccess, "vcl", "Got no Bitmap ReadAccess ?!?");
-
+            assert(xReadAccess);
             if(xReadAccess)
             {
                 BitmapPalette aNewPalette(xReadAccess->GetPalette());
@@ -2003,16 +2004,26 @@ Bitmap Bitmap::Modify(const 
basegfx::BColorModifierStack& rBColorModifierStack)
                     &aNewPalette);
             }
         }
+        else if (HasAlpha())
+        {
+            // clear bitmap with dest color
+            AlphaMask aAlphaMask(CreateAlphaMask());
+            Bitmap aTmpBitmap(CreateColorBitmap());
+            aTmpBitmap.Erase(Color(pLastModifierReplace->getBColor()));
+            aChangedBitmap = Bitmap(BitmapEx(aTmpBitmap, aAlphaMask));
+        }
         else
         {
             // clear bitmap with dest color
+            aChangedBitmap = *this;
             aChangedBitmap.Erase(Color(pLastModifierReplace->getBColor()));
         }
     }
     else
     {
+        aChangedBitmap = *this;
         BitmapScopedWriteAccess xContent(aChangedBitmap);
-
+        assert(xContent);
         if(xContent)
         {
             const double fConvertColor(1.0 / 255.0);
@@ -2083,8 +2094,10 @@ Bitmap Bitmap::Modify(const 
basegfx::BColorModifierStack& rBColorModifierStack)
                             static_cast<double>(aBMCol.GetGreen()) * 
fConvertColor,
                             static_cast<double>(aBMCol.GetBlue()) * 
fConvertColor);
                         const basegfx::BColor 
aBDest(rBColorModifierStack.getModifiedColor(aBSource));
-
-                        xContent->SetPixelOnData(pScanline, x, 
BitmapColor(Color(aBDest)));
+                        // preserve alpha
+                        BitmapColor aDestCol((Color(aBDest)));
+                        aDestCol.SetAlpha(aBMCol.GetAlpha());
+                        xContent->SetPixelOnData(pScanline, x, aDestCol);
                     }
                 }
             }
commit 15abc0aa75495fb26d19fe004a108ef5c5820807
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Tue Aug 19 09:18:39 2025 +0200
Commit:     Noel Grandin <noelgran...@gmail.com>
CommitDate: Wed Aug 20 08:59:03 2025 +0200

    BitmapEx->Bitmap in drawinglayer
    
    now that Bitmap supports transparency
    
    Change-Id: I37d358db5b68cd500349f1084eb3a65cfd83aa4d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189898
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins

diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx 
b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index 7985f604c221..6f21715168d9 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -174,37 +174,37 @@ void SoftEdgePrimitive2D::create2DDecomposition(
         // drawinglayer::primitive2d::ProcessAndBlurAlphaMask() can be called.
         // Otherwise, blurring of edges will fail in cases like running in a
         // slideshow or exporting to PDF.
-        const BitmapEx aBitmapEx(::drawinglayer::convertToBitmap(
+        const Bitmap aBitmap(::drawinglayer::convertToBitmap(
             std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, 
nDiscreteClippedHeight,
             nMaximumQuadraticPixels, true));
 
-        if (aBitmapEx.IsEmpty())
+        if (aBitmap.IsEmpty())
             break;
 
         // Get BitmapEx and check size. If no content, we are done
-        const Size& rBitmapExSizePixel(aBitmapEx.GetSizePixel());
-        if (!(rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 
0))
+        const Size aBitmapSizePixel(aBitmap.GetSizePixel());
+        if (!(aBitmapSizePixel.Width() > 0 && aBitmapSizePixel.Height() > 0))
             break;
 
         // We may have to take a corrective scaling into account when the
         // MaximumQuadraticPixel limit was used/triggered
         double fScale(1.0);
 
-        if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != 
nDiscreteClippedWidth
-            || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != 
nDiscreteClippedHeight)
+        if (static_cast<sal_uInt32>(aBitmapSizePixel.Width()) != 
nDiscreteClippedWidth
+            || static_cast<sal_uInt32>(aBitmapSizePixel.Height()) != 
nDiscreteClippedHeight)
         {
             // scale in X and Y should be the same (see fReduceFactor in 
convertToBitmapEx),
             // so adapt numerically to a single scale value, they are integer 
rounded values
-            const double 
fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
+            const double fScaleX(static_cast<double>(aBitmapSizePixel.Width())
                                  / static_cast<double>(nDiscreteClippedWidth));
-            const double 
fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+            const double fScaleY(static_cast<double>(aBitmapSizePixel.Height())
                                  / 
static_cast<double>(nDiscreteClippedHeight));
 
             fScale = (fScaleX + fScaleY) * 0.5;
         }
 
         // Get the Alpha and use as base to blur and apply the effect
-        AlphaMask aMask(aBitmapEx.GetAlphaMask());
+        AlphaMask aMask(aBitmap.CreateAlphaMask());
         if (aMask.IsEmpty()) // There is no mask, fully opaque
             break;
         AlphaMask blurMask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
@@ -212,7 +212,7 @@ void SoftEdgePrimitive2D::create2DDecomposition(
         aMask.BlendWith(blurMask);
 
         // The end result is the original bitmap with blurred 8-bit alpha mask
-        BitmapEx result(aBitmapEx.GetBitmap(), aMask);
+        BitmapEx result(aBitmap.CreateColorBitmap(), aMask);
 
 #ifdef DBG_UTIL
         static bool bDoSaveForVisualControl(false); // 
loplugin:constvars:ignore
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index e73250819007..5a2fe99b363a 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -2078,7 +2078,7 @@ void CairoPixelProcessor2D::processMarkerArrayPrimitive2D(
     }
 
     // prepare Marker's Bitmap
-    BitmapEx aBitmapEx(rMarkerArrayCandidate.getMarker());
+    Bitmap aBitmap(rMarkerArrayCandidate.getMarker());
 
     constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | 
DrawModeFlags::WhiteBitmap
                                    | DrawModeFlags::GrayBitmap);
@@ -2105,28 +2105,27 @@ void 
CairoPixelProcessor2D::processMarkerArrayPrimitive2D(
         }
 
         // need to apply ColorModifier to Bitmap data
-        aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+        aBitmap = aBitmap.Modify(maBColorModifierStack);
 
-        if (aBitmapEx.IsEmpty())
+        if (aBitmap.IsEmpty())
         {
             // color gets completely replaced, get it
             const basegfx::BColor aReplacementColor(
                 maBColorModifierStack.getModifiedColor(basegfx::BColor()));
-            Bitmap aBitmap(rMarker.GetSizePixel(), vcl::PixelFormat::N24_BPP);
-            aBitmap.Erase(Color(aReplacementColor));
+            Bitmap aBitmap2(rMarker.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+            aBitmap2.Erase(Color(aReplacementColor));
 
             if (rMarker.HasAlpha())
-                aBitmapEx = BitmapEx(aBitmap, rMarker.CreateAlphaMask());
+                aBitmap = Bitmap(BitmapEx(aBitmap2, 
rMarker.CreateAlphaMask()));
             else
-                aBitmapEx = BitmapEx(aBitmap);
+                aBitmap = aBitmap2;
         }
 
         maBColorModifierStack.pop();
     }
 
     // access or create cairo bitmap data
-    std::shared_ptr<CairoSurfaceHelper> aCairoSurfaceHelper(
-        getOrCreateCairoSurfaceHelper(Bitmap(aBitmapEx)));
+    std::shared_ptr<CairoSurfaceHelper> 
aCairoSurfaceHelper(getOrCreateCairoSurfaceHelper(aBitmap));
     if (!aCairoSurfaceHelper)
     {
         SAL_WARN("drawinglayer", "SDPRCairo: No SurfaceHelper from BitmapEx 
(!)");
diff --git a/drawinglayer/source/processor3d/defaultprocessor3d.cxx 
b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
index 79c40e5614d4..feab494fdb73 100644
--- a/drawinglayer/source/processor3d/defaultprocessor3d.cxx
+++ b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
@@ -233,7 +233,7 @@ namespace drawinglayer::processor3d
             const attribute::FillGraphicAttribute& rFillGraphicAttribute = 
rPrimitive.getFillGraphicAttribute();
 
             // #121194# For 3D texture we will use the BitmapRex representation
-            const BitmapEx 
aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
+            const Bitmap 
aBitmap(rFillGraphicAttribute.getGraphic().GetBitmap());
 
             // create range scaled by texture size
             basegfx::B2DRange 
aGraphicRange(rFillGraphicAttribute.getGraphicRange());
@@ -246,7 +246,7 @@ namespace drawinglayer::processor3d
             {
                 mpGeoTexSvx =
                     std::make_shared<texture::GeoTexSvxBitmapExTiled>(
-                        Bitmap(aBitmapEx),
+                        aBitmap,
                         aGraphicRange,
                         rFillGraphicAttribute.getOffsetX(),
                         rFillGraphicAttribute.getOffsetY());
@@ -255,7 +255,7 @@ namespace drawinglayer::processor3d
             {
                 mpGeoTexSvx =
                     std::make_shared<texture::GeoTexSvxBitmapEx>(
-                        Bitmap(aBitmapEx),
+                        aBitmap,
                         aGraphicRange);
             }
 
diff --git a/include/vcl/bitmap.hxx b/include/vcl/bitmap.hxx
index 32622e1ed3fd..1f2c43113c6a 100644
--- a/include/vcl/bitmap.hxx
+++ b/include/vcl/bitmap.hxx
@@ -587,6 +587,11 @@ public:
      */
     SAL_DLLPRIVATE void AdjustTransparency( sal_uInt8 cTrans );
 
+    /**
+     * Blends, i.e. multiplies the alpha layer with the new alpha value.
+     */
+    void BlendAlpha( sal_uInt8 nAlpha );
+
     /** Remove existing blending against COL_WHITE based on given AlphaMask
 
         Inside convertToBitmapEx the content gets rendered to RGB target (no 
'A'),
diff --git a/vcl/source/bitmap/bitmap.cxx b/vcl/source/bitmap/bitmap.cxx
index eec3a1ffd81a..d976a4cf5ebf 100644
--- a/vcl/source/bitmap/bitmap.cxx
+++ b/vcl/source/bitmap/bitmap.cxx
@@ -2227,4 +2227,36 @@ bool Bitmap::Create( const css::uno::Reference< 
css::rendering::XBitmapCanvas >
     return false;
 }
 
+void Bitmap::BlendAlpha(sal_uInt8 nAlpha)
+{
+    if (!HasAlpha())
+    {
+        sal_uInt8 cTrans = 255 - nAlpha;
+        *this = Bitmap(BitmapEx( *this, AlphaMask(GetSizePixel(), &cTrans) ));
+    }
+    else
+    {
+        BitmapScopedWriteAccess pAccess(*this);
+        assert(pAccess);
+        if( !pAccess )
+            return;
+
+        const tools::Long  nWidth = pAccess->Width(), nHeight = 
pAccess->Height();
+
+        BitmapColor aAlphaValue( 0 );
+
+        for( tools::Long nY = 0; nY < nHeight; nY++ )
+        {
+            Scanline pScanline = pAccess->GetScanline( nY );
+            for( tools::Long nX = 0; nX < nWidth; nX++ )
+            {
+                BitmapColor aCol = pAccess->GetPixelFromData( pScanline, nX );
+                sal_uInt8 nNewAlpha = static_cast<sal_Int32>(nAlpha) * 
aCol.GetAlpha() / 255;
+                aCol.SetAlpha( nNewAlpha );
+                pAccess->SetPixelOnData( pScanline, nX, aCol );
+            }
+        }
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to