include/vcl/BitmapScreenBlendFilter.hxx       |   29 ++++++
 include/vcl/BitmapTools.hxx                   |    4 
 svgio/inc/svgfeblendnode.hxx                  |    7 +
 svgio/inc/svgtoken.hxx                        |    1 
 svgio/source/svgreader/svgfeblendnode.cxx     |   96 +++++++++++++++++++-
 svgio/source/svgreader/svgtoken.cxx           |    1 
 vcl/Library_vcl.mk                            |    1 
 vcl/qa/cppunit/BitmapFilterTest.cxx           |   57 ++++++++++++
 vcl/source/bitmap/BitmapScreenBlendFilter.cxx |   87 ++++++++++++++++++
 vcl/source/bitmap/BitmapTools.cxx             |  121 +++++++++++++++++++++++++-
 10 files changed, 396 insertions(+), 8 deletions(-)

New commits:
commit 732ca4879b7e49b171c5930166e4dba7be451841
Author:     Xisco Fauli <xiscofa...@libreoffice.org>
AuthorDate: Wed Apr 10 18:05:00 2024 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Fri Apr 12 10:47:56 2024 +0200

    tdf#159660: Add support for screen mode in feBlend
    
    Change-Id: Iefe655a370cca930319290baa2a25d791371f55c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165958
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/include/vcl/BitmapScreenBlendFilter.hxx 
b/include/vcl/BitmapScreenBlendFilter.hxx
new file mode 100644
index 000000000000..a8f816936f49
--- /dev/null
+++ b/include/vcl/BitmapScreenBlendFilter.hxx
@@ -0,0 +1,29 @@
+/* -*- 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/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_BITMAPSCREENBLENDFILTER_HXX
+#define INCLUDED_VCL_BITMAPSCREENBLENDFILTER_HXX
+
+#include <vcl/bitmapex.hxx>
+
+class VCL_DLLPUBLIC BitmapScreenBlendFilter
+{
+private:
+    BitmapEx maBitmapEx;
+    BitmapEx maBitmapEx2;
+
+public:
+    BitmapScreenBlendFilter(BitmapEx const& rBmpEx, BitmapEx const& rBmpEx2);
+
+    virtual ~BitmapScreenBlendFilter();
+    BitmapEx execute();
+};
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/BitmapTools.hxx b/include/vcl/BitmapTools.hxx
index d321d2be79a3..de0ad84ea3da 100644
--- a/include/vcl/BitmapTools.hxx
+++ b/include/vcl/BitmapTools.hxx
@@ -66,6 +66,10 @@ VCL_DLLPUBLIC BitmapEx CanvasTransformBitmap( const 
BitmapEx& rBitmap,
                                   ::basegfx::B2DRectangle const & rDestRect,
                                   ::basegfx::B2DHomMatrix const & 
rLocalTransform );
 
+VCL_DLLPUBLIC BitmapEx DrawBitmapInRect( const BitmapEx& rBitmap,
+                                ::basegfx::B2DRectangle const & rBitmapRect,
+                                ::basegfx::B2DRectangle const & rDestRect );
+
 VCL_DLLPUBLIC void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool 
bFixedTransparence, float fTransparence, AlphaMask & rNewMask);
 
 VCL_DLLPUBLIC void DrawAndClipBitmap(const Point& rPos, const Size& rSize, 
const BitmapEx& rBitmap, BitmapEx & aBmpEx, basegfx::B2DPolyPolygon const & 
rClipPath);
diff --git a/svgio/inc/svgfeblendnode.hxx b/svgio/inc/svgfeblendnode.hxx
index 8ff46adf15ff..5881959ad418 100644
--- a/svgio/inc/svgfeblendnode.hxx
+++ b/svgio/inc/svgfeblendnode.hxx
@@ -24,12 +24,19 @@
 
 namespace svgio::svgreader
 {
+enum class Mode
+{
+    Normal,
+    Screen
+};
+
 class SvgFeBlendNode : public SvgFilterNode
 {
 private:
     OUString maIn;
     OUString maIn2;
     OUString maResult;
+    Mode maMode;
 
 public:
     SvgFeBlendNode(SvgDocument& rDocument, SvgNode* pParent);
diff --git a/svgio/inc/svgtoken.hxx b/svgio/inc/svgtoken.hxx
index d988f4b2d993..9e6945859cd4 100644
--- a/svgio/inc/svgtoken.hxx
+++ b/svgio/inc/svgtoken.hxx
@@ -97,6 +97,7 @@ namespace svgio::svgreader
             FloodOpacity,
             Operator,
             Mask,
+            Mode,
             ClipPathUnits,
             MaskUnits,
             MaskContentUnits,
diff --git a/svgio/source/svgreader/svgfeblendnode.cxx 
b/svgio/source/svgreader/svgfeblendnode.cxx
index 6adc0e8e139e..918912fa54d8 100644
--- a/svgio/source/svgreader/svgfeblendnode.cxx
+++ b/svgio/source/svgreader/svgfeblendnode.cxx
@@ -19,10 +19,19 @@
 #include <svgfeblendnode.hxx>
 #include <o3tl/string_view.hxx>
 
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <vcl/bitmapex.hxx>
+#include <drawinglayer/converters.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/BitmapScreenBlendFilter.hxx>
+#include <vcl/BitmapTools.hxx>
+
 namespace svgio::svgreader
 {
 SvgFeBlendNode::SvgFeBlendNode(SvgDocument& rDocument, SvgNode* pParent)
     : SvgFilterNode(SVGToken::FeBlend, rDocument, pParent)
+    , maMode(Mode::Normal)
 {
 }
 
@@ -53,6 +62,21 @@ void SvgFeBlendNode::parseAttribute(SVGToken aSVGToken, 
const OUString& aContent
             maResult = aContent.trim();
             break;
         }
+        case SVGToken::Mode:
+        {
+            if (!aContent.isEmpty())
+            {
+                if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), 
u"normal"))
+                {
+                    maMode = Mode::Normal;
+                }
+                else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), 
u"screen"))
+                {
+                    maMode = Mode::Screen;
+                }
+            }
+            break;
+        }
         default:
         {
             break;
@@ -63,16 +87,74 @@ void SvgFeBlendNode::parseAttribute(SVGToken aSVGToken, 
const OUString& aContent
 void SvgFeBlendNode::apply(drawinglayer::primitive2d::Primitive2DContainer& 
rTarget,
                            const SvgFilterNode* pParent) const
 {
-    if (const drawinglayer::primitive2d::Primitive2DContainer* rSource2
-        = pParent->findGraphicSource(maIn2))
+    if (maMode == Mode::Normal)
     {
-        rTarget = *rSource2;
-    }
+        if (const drawinglayer::primitive2d::Primitive2DContainer* rSource2
+            = pParent->findGraphicSource(maIn2))
+        {
+            rTarget = *rSource2;
+        }
 
-    if (const drawinglayer::primitive2d::Primitive2DContainer* rSource
-        = pParent->findGraphicSource(maIn))
+        if (const drawinglayer::primitive2d::Primitive2DContainer* rSource
+            = pParent->findGraphicSource(maIn))
+        {
+            rTarget.append(*rSource);
+        }
+    }
+    else if (maMode == Mode::Screen)
     {
-        rTarget.append(*rSource);
+        basegfx::B2DRange aRange, aRange2;
+        BitmapEx aBmpEx, aBmpEx2;
+
+        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource
+            = pParent->findGraphicSource(maIn))
+        {
+            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+            aRange = pSource->getB2DRange(aViewInformation2D);
+            basegfx::B2DHomMatrix aEmbedding(
+                basegfx::utils::createTranslateB2DHomMatrix(-aRange.getMinX(), 
-aRange.getMinY()));
+            aEmbedding.scale(aRange.getWidth(), aRange.getHeight());
+            const drawinglayer::primitive2d::Primitive2DReference xEmbedRef(
+                new drawinglayer::primitive2d::TransformPrimitive2D(
+                    aEmbedding, 
drawinglayer::primitive2d::Primitive2DContainer(*pSource)));
+            drawinglayer::primitive2d::Primitive2DContainer xEmbedSeq{ 
xEmbedRef };
+            aBmpEx = drawinglayer::convertToBitmapEx(std::move(xEmbedSeq), 
aViewInformation2D,
+                                                     aRange.getWidth(), 
aRange.getHeight(), 500000);
+        }
+
+        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource2
+            = pParent->findGraphicSource(maIn2))
+        {
+            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+            aRange2 = pSource2->getB2DRange(aViewInformation2D);
+            basegfx::B2DHomMatrix 
aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+                -aRange2.getMinX(), -aRange2.getMinY()));
+            aEmbedding.scale(aRange2.getWidth(), aRange2.getHeight());
+            const drawinglayer::primitive2d::Primitive2DReference xEmbedRef(
+                new drawinglayer::primitive2d::TransformPrimitive2D(
+                    aEmbedding, 
drawinglayer::primitive2d::Primitive2DContainer(*pSource2)));
+            drawinglayer::primitive2d::Primitive2DContainer xEmbedSeq{ 
xEmbedRef };
+            aBmpEx2
+                = drawinglayer::convertToBitmapEx(std::move(xEmbedSeq), 
aViewInformation2D,
+                                                  aRange2.getWidth(), 
aRange2.getHeight(), 500000);
+        }
+
+        basegfx::B2DRectangle aBaseRect(std::min(aRange.getMinX(), 
aRange2.getMinX()),
+                                        std::min(aRange.getMinY(), 
aRange2.getMinY()),
+                                        std::max(aRange.getMaxX(), 
aRange2.getMaxX()),
+                                        std::max(aRange.getMaxY(), 
aRange2.getMaxY()));
+
+        aBmpEx = vcl::bitmap::DrawBitmapInRect(aBmpEx, aRange, aBaseRect);
+        aBmpEx2 = vcl::bitmap::DrawBitmapInRect(aBmpEx2, aRange2, aBaseRect);
+
+        BitmapScreenBlendFilter* pScreenBlendFilter = new 
BitmapScreenBlendFilter(aBmpEx, aBmpEx2);
+        BitmapEx aResBmpEx = pScreenBlendFilter->execute();
+
+        const drawinglayer::primitive2d::Primitive2DReference xRef(
+            new drawinglayer::primitive2d::BitmapPrimitive2D(
+                aResBmpEx, basegfx::utils::createScaleTranslateB2DHomMatrix(
+                               aBaseRect.getRange(), aBaseRect.getMinimum())));
+        rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
     }
 
     pParent->addGraphicSourceToMapper(maResult, rTarget);
diff --git a/svgio/source/svgreader/svgtoken.cxx 
b/svgio/source/svgreader/svgtoken.cxx
index 3f5ac5c7a306..b019f71c4835 100644
--- a/svgio/source/svgreader/svgtoken.cxx
+++ b/svgio/source/svgreader/svgtoken.cxx
@@ -95,6 +95,7 @@ constexpr auto aSVGTokenMap = 
frozen::make_unordered_map<std::u16string_view, SV
     { u"flood-opacity", SVGToken::FloodOpacity },
     { u"operator", SVGToken::Operator },
     { u"mask", SVGToken::Mask },
+    { u"mode", SVGToken::Mode },
     { u"clipPathUnits", SVGToken::ClipPathUnits },
     { u"maskUnits", SVGToken::MaskUnits },
     { u"maskContentUnits", SVGToken::MaskContentUnits },
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index a3c2581ae6b7..7b54123dbed2 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -346,6 +346,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/bitmap/BitmapBasicMorphologyFilter \
     vcl/source/bitmap/BitmapMaskToAlphaFilter \
     vcl/source/bitmap/BitmapMonochromeFilter \
+    vcl/source/bitmap/BitmapScreenBlendFilter \
     vcl/source/bitmap/BitmapSmoothenFilter \
     vcl/source/bitmap/BitmapLightenFilter \
     vcl/source/bitmap/BitmapDisabledImageFilter \
diff --git a/vcl/qa/cppunit/BitmapFilterTest.cxx 
b/vcl/qa/cppunit/BitmapFilterTest.cxx
index 0432041cac7c..00ba12b4a925 100644
--- a/vcl/qa/cppunit/BitmapFilterTest.cxx
+++ b/vcl/qa/cppunit/BitmapFilterTest.cxx
@@ -15,6 +15,7 @@
 #include <tools/stream.hxx>
 #include <vcl/graphicfilter.hxx>
 
+#include <vcl/BitmapScreenBlendFilter.hxx>
 #include <vcl/BitmapBasicMorphologyFilter.hxx>
 #include <vcl/BitmapFilterStackBlur.hxx>
 #include <BitmapSymmetryCheck.hxx>
@@ -39,12 +40,14 @@ public:
     void testBasicMorphology();
     void testPerformance();
     void testGenerateStripRanges();
+    void testScreenBlendFilter();
 
     CPPUNIT_TEST_SUITE(BitmapFilterTest);
     CPPUNIT_TEST(testBlurCorrectness);
     CPPUNIT_TEST(testBasicMorphology);
     CPPUNIT_TEST(testPerformance);
     CPPUNIT_TEST(testGenerateStripRanges);
+    CPPUNIT_TEST(testScreenBlendFilter);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -276,6 +279,60 @@ void BitmapFilterTest::testGenerateStripRanges()
     }
 }
 
+void BitmapFilterTest::testScreenBlendFilter()
+{
+    Bitmap aRedBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
+    CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, 
aRedBitmap.getPixelFormat());
+    {
+        BitmapScopedWriteAccess aWriteAccess(aRedBitmap);
+        aWriteAccess->Erase(COL_LIGHTRED);
+    }
+
+    Bitmap aGreenBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
+    CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, 
aGreenBitmap.getPixelFormat());
+    {
+        BitmapScopedWriteAccess aWriteAccess(aGreenBitmap);
+        aWriteAccess->Erase(COL_GREEN);
+    }
+
+    Bitmap aTransparentBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
+    CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, 
aTransparentBitmap.getPixelFormat());
+    {
+        BitmapScopedWriteAccess aWriteAccess(aTransparentBitmap);
+        aWriteAccess->Erase(COL_AUTO);
+    }
+
+    BitmapEx aRedBitmapEx(aRedBitmap);
+    BitmapEx aGreenBitmapEx(aGreenBitmap);
+    BitmapEx aTransparentBitmapEx(aTransparentBitmap);
+
+    // same color
+    {
+        BitmapScreenBlendFilter* pArithmeticFilter
+            = new BitmapScreenBlendFilter(aRedBitmapEx, aRedBitmapEx);
+        BitmapEx aResBitmapEx = pArithmeticFilter->execute();
+        CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aResBitmapEx.GetPixelColor(2, 2));
+    }
+
+    // different color
+    {
+        BitmapScreenBlendFilter* pArithmeticFilter
+            = new BitmapScreenBlendFilter(aRedBitmapEx, aGreenBitmapEx);
+        BitmapEx aResBitmapEx = pArithmeticFilter->execute();
+        CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xFF, 0xFF, 0x80, 0x00),
+                             aResBitmapEx.GetPixelColor(2, 2));
+    }
+
+    // transparent
+    {
+        BitmapScreenBlendFilter* pArithmeticFilter
+            = new BitmapScreenBlendFilter(aRedBitmapEx, aTransparentBitmapEx);
+        BitmapEx aResBitmapEx = pArithmeticFilter->execute();
+        CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xFF, 0xFF, 0xFF, 0xFF),
+                             aResBitmapEx.GetPixelColor(2, 2));
+    }
+}
+
 } // namespace
 
 CPPUNIT_TEST_SUITE_REGISTRATION(BitmapFilterTest);
diff --git a/vcl/source/bitmap/BitmapScreenBlendFilter.cxx 
b/vcl/source/bitmap/BitmapScreenBlendFilter.cxx
new file mode 100644
index 000000000000..67525a24cdf9
--- /dev/null
+++ b/vcl/source/bitmap/BitmapScreenBlendFilter.cxx
@@ -0,0 +1,87 @@
+/* -*- 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 <comphelper/diagnose_ex.hxx>
+#include <vcl/BitmapScreenBlendFilter.hxx>
+#include <vcl/BitmapWriteAccess.hxx>
+#include <vcl/BitmapTools.hxx>
+
+BitmapScreenBlendFilter::BitmapScreenBlendFilter(BitmapEx const& rBitmapEx,
+                                                 BitmapEx const& rBitmapEx2)
+    : maBitmapEx(rBitmapEx)
+    , maBitmapEx2(rBitmapEx2)
+{
+}
+
+BitmapScreenBlendFilter::~BitmapScreenBlendFilter() {}
+
+static sal_uInt8 lcl_calculate(const sal_uInt8 aColor, const sal_uInt8 aColor2)
+{
+    const double c1 = aColor / 255.0;
+    const double c2 = aColor2 / 255.0;
+    const double result = c2 + c1 - c1 * c2;
+    return result * 255.0;
+}
+
+BitmapEx BitmapScreenBlendFilter::execute()
+{
+    if (maBitmapEx.IsEmpty() || maBitmapEx2.IsEmpty())
+        return BitmapEx();
+
+    Size aSize = maBitmapEx.GetBitmap().GetSizePixel();
+    Size aSize2 = maBitmapEx2.GetBitmap().GetSizePixel();
+    sal_Int32 nHeight = std::min(aSize.getHeight(), aSize2.getHeight());
+    sal_Int32 nWidth = std::min(aSize.getWidth(), aSize2.getWidth());
+
+    BitmapScopedReadAccess pReadAccess(maBitmapEx.GetBitmap());
+    Bitmap aDstBitmap(Size(nWidth, nHeight), 
maBitmapEx.GetBitmap().getPixelFormat(),
+                      &pReadAccess->GetPalette());
+    Bitmap aDstAlpha(AlphaMask(Size(nWidth, nHeight)).GetBitmap());
+
+    {
+        // just to be on the safe side: let the
+        // ScopedAccessors get destructed before
+        // copy-constructing the resulting bitmap. This will
+        // rule out the possibility that cached accessor data
+        // is not yet written back.
+
+        BitmapScopedWriteAccess pWriteAccess(aDstBitmap);
+        BitmapScopedWriteAccess pAlphaWriteAccess(aDstAlpha);
+
+        if (pWriteAccess.get() != nullptr && pAlphaWriteAccess.get() != 
nullptr)
+        {
+            for (tools::Long y(0); y < nHeight; ++y)
+            {
+                Scanline pScanline = pWriteAccess->GetScanline(y);
+                Scanline pScanAlpha = pAlphaWriteAccess->GetScanline(y);
+                for (tools::Long x(0); x < nWidth; ++x)
+                {
+                    BitmapColor i1 = maBitmapEx.GetPixelColor(x, y);
+                    BitmapColor i2 = maBitmapEx2.GetPixelColor(x, y);
+                    sal_uInt8 r(lcl_calculate(i1.GetRed(), i2.GetRed()));
+                    sal_uInt8 g(lcl_calculate(i1.GetGreen(), i2.GetGreen()));
+                    sal_uInt8 b(lcl_calculate(i1.GetBlue(), i2.GetBlue()));
+                    sal_uInt8 a(lcl_calculate(i1.GetAlpha(), i2.GetAlpha()));
+
+                    pWriteAccess->SetPixelOnData(pScanline, x, BitmapColor(r, 
g, b));
+                    pAlphaWriteAccess->SetPixelOnData(pScanAlpha, x, 
BitmapColor(a));
+                }
+            }
+        }
+        else
+        {
+            // TODO(E2): Error handling!
+            ENSURE_OR_THROW(false, "BitmapScreenBlendFilter: could not access 
bitmap");
+        }
+    }
+
+    return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapTools.cxx 
b/vcl/source/bitmap/BitmapTools.cxx
index 2330483bb98b..41f8ff38992c 100644
--- a/vcl/source/bitmap/BitmapTools.cxx
+++ b/vcl/source/bitmap/BitmapTools.cxx
@@ -512,6 +512,126 @@ BitmapEx CanvasTransformBitmap( const BitmapEx&           
      rBitmap,
     return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
 }
 
+BitmapEx DrawBitmapInRect( const BitmapEx& rBitmap,
+                                ::basegfx::B2DRectangle const & rBitmapRect,
+                                ::basegfx::B2DRectangle const & rDestRect )
+{
+    if( rBitmapRect.isEmpty() || rDestRect.isEmpty() )
+        return BitmapEx();
+
+    const Size aDestBmpSize( ::basegfx::fround( rDestRect.getWidth() ),
+                             ::basegfx::fround( rDestRect.getHeight() ) );
+
+    Bitmap aSrcBitmap( rBitmap.GetBitmap() );
+    Bitmap aSrcAlpha;
+
+    // differentiate mask and alpha channel (on-off
+    // vs. multi-level transparency)
+    if( rBitmap.IsAlpha() )
+    {
+        aSrcAlpha = rBitmap.GetAlphaMask().GetBitmap();
+    }
+
+    BitmapScopedReadAccess pReadAccess( aSrcBitmap );
+    BitmapScopedReadAccess pAlphaReadAccess;
+    if (rBitmap.IsAlpha())
+        pAlphaReadAccess = aSrcAlpha;
+
+    if( !pReadAccess || (!pAlphaReadAccess && rBitmap.IsAlpha()) )
+    {
+        // TODO(E2): Error handling!
+        ENSURE_OR_THROW( false,
+                          "DrawBitmapInRect(): could not access source bitmap" 
);
+    }
+
+    // mapping table, to translate pAlphaReadAccess' pixel
+    // values into destination alpha values (needed e.g. for
+    // paletted 1-bit masks).
+    sal_uInt8 aAlphaMap[256];
+
+    if( rBitmap.IsAlpha() )
+    {
+        // source already has alpha channel - 1:1 mapping,
+        // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
+        sal_uInt8  val=0;
+        sal_uInt8* pCur=aAlphaMap;
+        sal_uInt8* const pEnd=&aAlphaMap[256];
+        while(pCur != pEnd)
+            *pCur++ = val++;
+    }
+    // else: mapping table is not used
+
+    Bitmap aDstBitmap(aDestBmpSize, aSrcBitmap.getPixelFormat(), 
&pReadAccess->GetPalette());
+    Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
+
+    {
+        // just to be on the safe side: let the
+        // ScopedAccessors get destructed before
+        // copy-constructing the resulting bitmap. This will
+        // rule out the possibility that cached accessor data
+        // is not yet written back.
+        BitmapScopedWriteAccess pWriteAccess( aDstBitmap );
+        BitmapScopedWriteAccess pAlphaWriteAccess( aDstAlpha );
+
+
+        if( pWriteAccess.get() != nullptr &&
+            pAlphaWriteAccess.get() != nullptr)
+        {
+            // for the time being, always read as ARGB
+            for (tools::Long y(rDestRect.getMinY()); y < rDestRect.getMaxY(); 
y++)
+            {
+                // differentiate mask and alpha channel (on-off
+                // vs. multi-level transparency)
+                if( rBitmap.IsAlpha() )
+                {
+                    Scanline pScan = pWriteAccess->GetScanline( y  - 
rDestRect.getMinY() );
+                    Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y  - 
rDestRect.getMinY() );
+                    // Handling alpha and mask just the same...
+                    for (tools::Long x(rDestRect.getMinX()); x < 
rDestRect.getMaxX(); x++)
+                    {
+                        if (rBitmapRect.getMinX() <= x && 
rBitmapRect.getMaxX() > x && rBitmapRect.getMinY() <= y
+                          && rBitmapRect.getMaxY() > y)
+                        {
+                            const sal_uInt8 cAlphaIdx = 
pAlphaReadAccess->GetPixelIndex( x - rBitmapRect.getMinX(), y - 
rBitmapRect.getMinY() );
+                            pAlphaWriteAccess->SetPixelOnData( pScanAlpha,  x 
- rDestRect.getMinX(), BitmapColor(aAlphaMap[ cAlphaIdx ]) );
+                            pWriteAccess->SetPixelOnData( pScan,  x - 
rDestRect.getMinX(), pReadAccess->GetPixel( x - rBitmapRect.getMinX(), y - 
rBitmapRect.getMinY() ) );
+                        }
+                        else
+                        {
+                            pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x - 
rDestRect.getMinX(), BitmapColor(0) );
+                        }
+                    }
+                }
+                else
+                {
+                    Scanline pScan = pWriteAccess->GetScanline( y  - 
rDestRect.getMinY() );
+                    Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y  - 
rDestRect.getMinY() );
+                    for (tools::Long x(rDestRect.getMinX()); x < 
rDestRect.getMaxX(); x++)
+                    {
+                        if (rBitmapRect.getMinX() <= x && 
rBitmapRect.getMaxX() > x && rBitmapRect.getMinY() <= y
+                          && rBitmapRect.getMaxY() > y)
+                        {
+                            pAlphaWriteAccess->SetPixelOnData( pScanAlpha,  x 
- rDestRect.getMinX(), BitmapColor(255) );
+                            pWriteAccess->SetPixelOnData( pScan,  x - 
rDestRect.getMinX(), pReadAccess->GetPixel( x - rBitmapRect.getMinX(), y - 
rBitmapRect.getMinY() ) );
+                        }
+                        else
+                        {
+                            pAlphaWriteAccess->SetPixelOnData( pScanAlpha,  x 
- rDestRect.getMinX(), BitmapColor(0) );
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            // TODO(E2): Error handling!
+            ENSURE_OR_THROW( false,
+                              "DrawBitmapInRect(): could not access bitmap" );
+        }
+    }
+
+    return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
+}
 
 void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool 
bFixedTransparence, float fTransparence, AlphaMask & rNewMask)
 {
@@ -644,7 +764,6 @@ void DrawAndClipBitmap(const Point& rPos, const Size& 
rSize, const BitmapEx& rBi
     }
 }
 
-
 css::uno::Sequence< sal_Int8 > GetMaskDIB(BitmapEx const & aBmpEx)
 {
     if ( aBmpEx.IsAlpha() )

Reply via email to