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: */