include/vcl/BitmapNormalBlendFilter.hxx       |   29 ++++++
 svgio/qa/cppunit/SvgImportTest.cxx            |   60 ++++++++----
 svgio/source/svgreader/svgfeblendnode.cxx     |  124 ++++++++++++--------------
 vcl/Library_vcl.mk                            |    1 
 vcl/qa/cppunit/BitmapFilterTest.cxx           |   57 +++++++++++
 vcl/source/bitmap/BitmapNormalBlendFilter.cxx |  104 +++++++++++++++++++++
 6 files changed, 294 insertions(+), 81 deletions(-)

New commits:
commit 851f60697d32849454528e5f14ed80446b330e0c
Author:     Xisco Fauli <xiscofa...@libreoffice.org>
AuthorDate: Mon Apr 22 16:38:35 2024 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Mon Apr 22 18:43:08 2024 +0200

    tdf#159660: also add normal blend filter
    
    Change-Id: I3edc7495975618357f002536857a11dcc72cc0b9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166460
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/include/vcl/BitmapNormalBlendFilter.hxx 
b/include/vcl/BitmapNormalBlendFilter.hxx
new file mode 100644
index 000000000000..f89ea848d5cc
--- /dev/null
+++ b/include/vcl/BitmapNormalBlendFilter.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_BITMAPNORMALBLENDFILTER_HXX
+#define INCLUDED_VCL_BITMAPNORMALBLENDFILTER_HXX
+
+#include <vcl/bitmapex.hxx>
+
+class VCL_DLLPUBLIC BitmapNormalBlendFilter
+{
+private:
+    BitmapEx maBitmapEx;
+    BitmapEx maBitmapEx2;
+
+public:
+    BitmapNormalBlendFilter(BitmapEx const& rBmpEx, BitmapEx const& rBmpEx2);
+
+    virtual ~BitmapNormalBlendFilter();
+    BitmapEx execute();
+};
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index 31eb63c6075b..817980d00421 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -176,24 +176,48 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf155819)
     assertXPath(pDocument, "/primitive2D/transform/transform"_ostr, 4);
 }
 
-CPPUNIT_TEST_FIXTURE(Test, testFilterFeBlend)
-{
-    xmlDocUniquePtr pDocument = 
dumpAndParseSvg(u"/svgio/qa/cppunit/data/filterFeBlend.svg");
-
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]"_ostr, "color"_ostr, 
"#8a2be2");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"height"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"width"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"minx"_ostr, "70");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"miny"_ostr, "70");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"maxx"_ostr, "170");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[1]/polypolygon"_ostr, 
"maxy"_ostr, "170");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]"_ostr, "color"_ostr, 
"#ffd700");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"height"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"width"_ostr, "100");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"minx"_ostr, "30");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"miny"_ostr, "30");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"maxx"_ostr, "130");
-    assertXPath(pDocument, 
"/primitive2D/transform/transform/polypolygoncolor[2]/polypolygon"_ostr, 
"maxy"_ostr, "130");
+CPPUNIT_TEST_FIXTURE(Test, testNormalBlend)
+{
+    xmlDocUniquePtr pDocument = 
dumpAndParseSvg(u"/svgio/qa/cppunit/data/normalBlend.svg");
+
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "height"_ostr, 
"170");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "width"_ostr, 
"170");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap/data"_ostr, 170);
+
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy11"_ostr, 
"170");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy12"_ostr, "0");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy13"_ostr, "0");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy21"_ostr, "0");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy22"_ostr, 
"170");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy23"_ostr, "0");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy31"_ostr, "0");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy32"_ostr, "0");
+    assertXPath(pDocument,
+            "/primitive2D/transform/transform/bitmap"_ostr, "xy33"_ostr, "1");
+
+    // Check the colors in the diagonal
+    OUString sDataRow = getXPath(pDocument, 
"/primitive2D/transform/transform/bitmap/data[40]"_ostr, "row"_ostr);
+    std::vector<OUString> aPixels = comphelper::string::split(sDataRow, ',');
+    CPPUNIT_ASSERT_EQUAL(OUString("ffd700"), aPixels[40]);
+
+    sDataRow = getXPath(pDocument, 
"/primitive2D/transform/transform/bitmap/data[85]"_ostr, "row"_ostr);
+    aPixels = comphelper::string::split(sDataRow, ',');
+    CPPUNIT_ASSERT_EQUAL(OUString("ffd700"), aPixels[85]);
+
+    sDataRow = getXPath(pDocument, 
"/primitive2D/transform/transform/bitmap/data[130]"_ostr, "row"_ostr);
+    aPixels = comphelper::string::split(sDataRow, ',');
+    CPPUNIT_ASSERT_EQUAL(OUString("8a2be2"), aPixels[130]);
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testFeColorMatrix)
diff --git a/svgio/qa/cppunit/data/filterFeBlend.svg 
b/svgio/qa/cppunit/data/normalBlend.svg
similarity index 100%
rename from svgio/qa/cppunit/data/filterFeBlend.svg
rename to svgio/qa/cppunit/data/normalBlend.svg
diff --git a/svgio/source/svgreader/svgfeblendnode.cxx 
b/svgio/source/svgreader/svgfeblendnode.cxx
index a5109aa24fe6..fd6bd10ef442 100644
--- a/svgio/source/svgreader/svgfeblendnode.cxx
+++ b/svgio/source/svgreader/svgfeblendnode.cxx
@@ -27,6 +27,7 @@
 #include <vcl/BitmapDarkenBlendFilter.hxx>
 #include <vcl/BitmapLightenBlendFilter.hxx>
 #include <vcl/BitmapMultiplyBlendFilter.hxx>
+#include <vcl/BitmapNormalBlendFilter.hxx>
 #include <vcl/BitmapScreenBlendFilter.hxx>
 #include <vcl/BitmapTools.hxx>
 
@@ -107,95 +108,92 @@ void 
SvgFeBlendNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTar
     const drawinglayer::primitive2d::Primitive2DContainer* pSource2
         = pParent->findGraphicSource(maIn2);
 
-    if (maMode == Mode::Normal)
+    basegfx::B2DRange aRange, aRange2;
+    const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+    if (pSource)
     {
-        // Process maIn2 first
-        if (pSource2)
-        {
-            rTarget = *pSource2;
-        }
-
-        if (pSource)
-        {
-            rTarget.append(*pSource);
-        }
+        aRange = pSource->getB2DRange(aViewInformation2D);
     }
-    else
-    {
-        basegfx::B2DRange aRange, aRange2;
-        const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
-        if (pSource)
-        {
-            aRange = pSource->getB2DRange(aViewInformation2D);
-        }
 
-        if (pSource2)
-        {
-            aRange2 = pSource2->getB2DRange(aViewInformation2D);
-        }
+    if (pSource2)
+    {
+        aRange2 = pSource2->getB2DRange(aViewInformation2D);
+    }
 
-        const basegfx::B2DRange aBaseRange(0, 0, std::max(aRange.getMaxX(), 
aRange2.getMaxX()),
-                                           std::max(aRange.getMaxY(), 
aRange2.getMaxY()));
+    const basegfx::B2DRange aBaseRange(0, 0, std::max(aRange.getMaxX(), 
aRange2.getMaxX()),
+                                       std::max(aRange.getMaxY(), 
aRange2.getMaxY()));
 
-        BitmapEx aBmpEx, aBmpEx2;
+    BitmapEx aBmpEx, aBmpEx2;
 
-        if (pSource)
-        {
-            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource);
-            aBmpEx = drawinglayer::convertToBitmapEx(
-                std::move(aSource), aViewInformation2D, aBaseRange.getWidth(),
-                aBaseRange.getHeight(), aBaseRange.getWidth() * 
aBaseRange.getHeight());
-        }
-        else
-        {
-            aBmpEx = drawinglayer::convertToBitmapEx(
-                std::move(rTarget), aViewInformation2D, aBaseRange.getWidth(),
-                aBaseRange.getHeight(), aBaseRange.getWidth() * 
aBaseRange.getHeight());
-        }
+    if (pSource)
+    {
+        drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource);
+        aBmpEx = drawinglayer::convertToBitmapEx(std::move(aSource), 
aViewInformation2D,
+                                                 aBaseRange.getWidth(), 
aBaseRange.getHeight(),
+                                                 aBaseRange.getWidth() * 
aBaseRange.getHeight());
+    }
+    else
+    {
+        aBmpEx = drawinglayer::convertToBitmapEx(std::move(rTarget), 
aViewInformation2D,
+                                                 aBaseRange.getWidth(), 
aBaseRange.getHeight(),
+                                                 aBaseRange.getWidth() * 
aBaseRange.getHeight());
+    }
 
-        if (pSource2)
-        {
-            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource2);
-            aBmpEx2 = drawinglayer::convertToBitmapEx(
-                std::move(aSource), aViewInformation2D, aBaseRange.getWidth(),
-                aBaseRange.getHeight(), aBaseRange.getWidth() * 
aBaseRange.getHeight());
-        }
-        else
-        {
-            aBmpEx2 = drawinglayer::convertToBitmapEx(
-                std::move(rTarget), aViewInformation2D, aBaseRange.getWidth(),
-                aBaseRange.getHeight(), aBaseRange.getWidth() * 
aBaseRange.getHeight());
-        }
+    if (pSource2)
+    {
+        drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource2);
+        aBmpEx2 = drawinglayer::convertToBitmapEx(std::move(aSource), 
aViewInformation2D,
+                                                  aBaseRange.getWidth(), 
aBaseRange.getHeight(),
+                                                  aBaseRange.getWidth() * 
aBaseRange.getHeight());
+    }
+    else
+    {
+        aBmpEx2 = drawinglayer::convertToBitmapEx(std::move(rTarget), 
aViewInformation2D,
+                                                  aBaseRange.getWidth(), 
aBaseRange.getHeight(),
+                                                  aBaseRange.getWidth() * 
aBaseRange.getHeight());
+    }
 
-        BitmapEx aResBmpEx;
-        if (maMode == Mode::Screen)
+    BitmapEx aResBmpEx;
+    switch (maMode)
+    {
+        case Mode::Screen:
         {
             BitmapScreenBlendFilter aScreenBlendFilter(aBmpEx, aBmpEx2);
             aResBmpEx = aScreenBlendFilter.execute();
+            break;
         }
-        else if (maMode == Mode::Multiply)
+        case Mode::Multiply:
         {
             BitmapMultiplyBlendFilter aMultiplyBlendFilter(aBmpEx, aBmpEx2);
             aResBmpEx = aMultiplyBlendFilter.execute();
+            break;
         }
-        else if (maMode == Mode::Darken)
+        case Mode::Darken:
         {
             BitmapDarkenBlendFilter aDarkenBlendFilter(aBmpEx, aBmpEx2);
             aResBmpEx = aDarkenBlendFilter.execute();
+            break;
         }
-        else if (maMode == Mode::Lighten)
+        case Mode::Lighten:
         {
             BitmapLightenBlendFilter aLightenBlendFilter(aBmpEx, aBmpEx2);
             aResBmpEx = aLightenBlendFilter.execute();
+            break;
+        }
+        case Mode::Normal:
+        {
+            BitmapNormalBlendFilter aNormalBlendFilter(aBmpEx, aBmpEx2);
+            aResBmpEx = aNormalBlendFilter.execute();
+            break;
         }
-
-        const drawinglayer::primitive2d::Primitive2DReference xRef(
-            new drawinglayer::primitive2d::BitmapPrimitive2D(
-                aResBmpEx, basegfx::utils::createScaleTranslateB2DHomMatrix(
-                               aBaseRange.getRange(), 
aBaseRange.getMinimum())));
-        rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
     }
 
+    const drawinglayer::primitive2d::Primitive2DReference xRef(
+        new drawinglayer::primitive2d::BitmapPrimitive2D(
+            aResBmpEx, 
basegfx::utils::createScaleTranslateB2DHomMatrix(aBaseRange.getRange(),
+                                                                        
aBaseRange.getMinimum())));
+    rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
+
     pParent->addGraphicSourceToMapper(maResult, rTarget);
 }
 
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index de651cfa1aed..c832e712e22f 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -350,6 +350,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/bitmap/BitmapMaskToAlphaFilter \
     vcl/source/bitmap/BitmapMonochromeFilter \
     vcl/source/bitmap/BitmapMultiplyBlendFilter \
+    vcl/source/bitmap/BitmapNormalBlendFilter \
     vcl/source/bitmap/BitmapScreenBlendFilter \
     vcl/source/bitmap/BitmapSmoothenFilter \
     vcl/source/bitmap/BitmapLightenFilter \
diff --git a/vcl/qa/cppunit/BitmapFilterTest.cxx 
b/vcl/qa/cppunit/BitmapFilterTest.cxx
index ca04956312a4..d59cd331d467 100644
--- a/vcl/qa/cppunit/BitmapFilterTest.cxx
+++ b/vcl/qa/cppunit/BitmapFilterTest.cxx
@@ -19,6 +19,7 @@
 #include <vcl/BitmapDarkenBlendFilter.hxx>
 #include <vcl/BitmapLightenBlendFilter.hxx>
 #include <vcl/BitmapMultiplyBlendFilter.hxx>
+#include <vcl/BitmapNormalBlendFilter.hxx>
 #include <vcl/BitmapScreenBlendFilter.hxx>
 #include <vcl/BitmapBasicMorphologyFilter.hxx>
 #include <vcl/BitmapFilterStackBlur.hxx>
@@ -45,6 +46,7 @@ public:
     void testPerformance();
     void testGenerateStripRanges();
     void testMultiplyBlendFilter();
+    void testNormalBlendFilter();
     void testDarkenBlendFilter();
     void testLightenBlendFilter();
     void testScreenBlendFilter();
@@ -56,6 +58,7 @@ public:
     CPPUNIT_TEST(testPerformance);
     CPPUNIT_TEST(testGenerateStripRanges);
     CPPUNIT_TEST(testMultiplyBlendFilter);
+    CPPUNIT_TEST(testNormalBlendFilter);
     CPPUNIT_TEST(testDarkenBlendFilter);
     CPPUNIT_TEST(testLightenBlendFilter);
     CPPUNIT_TEST(testScreenBlendFilter);
@@ -345,6 +348,60 @@ void BitmapFilterTest::testMultiplyBlendFilter()
     }
 }
 
+void BitmapFilterTest::testNormalBlendFilter()
+{
+    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
+    {
+        BitmapNormalBlendFilter* pArithmeticFilter
+            = new BitmapNormalBlendFilter(aRedBitmapEx, aRedBitmapEx);
+        BitmapEx aResBitmapEx = pArithmeticFilter->execute();
+        CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aResBitmapEx.GetPixelColor(2, 2));
+    }
+
+    // different color
+    {
+        BitmapNormalBlendFilter* pArithmeticFilter
+            = new BitmapNormalBlendFilter(aRedBitmapEx, aGreenBitmapEx);
+        BitmapEx aResBitmapEx = pArithmeticFilter->execute();
+        CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xFF, 0xFF, 0x00, 0x00),
+                             aResBitmapEx.GetPixelColor(2, 2));
+    }
+
+    // transparent
+    {
+        BitmapNormalBlendFilter* pArithmeticFilter
+            = new BitmapNormalBlendFilter(aRedBitmapEx, aTransparentBitmapEx);
+        BitmapEx aResBitmapEx = pArithmeticFilter->execute();
+        CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xFF, 0xFF, 0x00, 0x00),
+                             aResBitmapEx.GetPixelColor(2, 2));
+    }
+}
+
 void BitmapFilterTest::testDarkenBlendFilter()
 {
     Bitmap aRedBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
diff --git a/vcl/source/bitmap/BitmapNormalBlendFilter.cxx 
b/vcl/source/bitmap/BitmapNormalBlendFilter.cxx
new file mode 100644
index 000000000000..5f207d37a756
--- /dev/null
+++ b/vcl/source/bitmap/BitmapNormalBlendFilter.cxx
@@ -0,0 +1,104 @@
+/* -*- 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/BitmapNormalBlendFilter.hxx>
+#include <vcl/BitmapWriteAccess.hxx>
+#include <vcl/BitmapTools.hxx>
+
+BitmapNormalBlendFilter::BitmapNormalBlendFilter(BitmapEx const& rBitmapEx,
+                                                 BitmapEx const& rBitmapEx2)
+    : maBitmapEx(rBitmapEx)
+    , maBitmapEx2(rBitmapEx2)
+{
+}
+
+BitmapNormalBlendFilter::~BitmapNormalBlendFilter() {}
+
+static sal_uInt8 lcl_calculate(const sal_uInt8 aColor, const sal_uInt8 aAlpha,
+                               const sal_uInt8 aColor2)
+{
+    const double c1 = aColor / 255.0;
+    const double c2 = aColor2 / 255.0;
+    const double a1 = aAlpha / 255.0;
+    const double result = (1.0 - a1) * c2 + c1;
+    return result * 255.0;
+}
+
+static BitmapColor premultiply(const BitmapColor c)
+{
+    return BitmapColor(ColorAlpha, vcl::bitmap::premultiply(c.GetRed(), 
c.GetAlpha()),
+                       vcl::bitmap::premultiply(c.GetGreen(), c.GetAlpha()),
+                       vcl::bitmap::premultiply(c.GetBlue(), c.GetAlpha()), 
c.GetAlpha());
+}
+
+static BitmapColor unpremultiply(const BitmapColor c)
+{
+    return BitmapColor(ColorAlpha, vcl::bitmap::unpremultiply(c.GetRed(), 
c.GetAlpha()),
+                       vcl::bitmap::unpremultiply(c.GetGreen(), c.GetAlpha()),
+                       vcl::bitmap::unpremultiply(c.GetBlue(), c.GetAlpha()), 
c.GetAlpha());
+}
+
+BitmapEx BitmapNormalBlendFilter::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 = premultiply(maBitmapEx.GetPixelColor(x, 
y));
+                    BitmapColor i2 = premultiply(maBitmapEx2.GetPixelColor(x, 
y));
+                    sal_uInt8 r(lcl_calculate(i1.GetRed(), i1.GetAlpha(), 
i2.GetRed()));
+                    sal_uInt8 g(lcl_calculate(i1.GetGreen(), i1.GetAlpha(), 
i2.GetGreen()));
+                    sal_uInt8 b(lcl_calculate(i1.GetBlue(), i1.GetAlpha(), 
i2.GetBlue()));
+                    sal_uInt8 a(lcl_calculate(i1.GetAlpha(), i1.GetAlpha(), 
i2.GetAlpha()));
+
+                    pWriteAccess->SetPixelOnData(
+                        pScanline, x, unpremultiply(BitmapColor(ColorAlpha, r, 
g, b, a)));
+                    pAlphaWriteAccess->SetPixelOnData(pScanAlpha, x, 
BitmapColor(a));
+                }
+            }
+        }
+        else
+        {
+            // TODO(E2): Error handling!
+            ENSURE_OR_THROW(false, "BitmapNormalBlendFilter: could not access 
bitmap");
+        }
+    }
+
+    return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to