tdf#160726, tdf#48062: Simplify how BitmapExs are created

In my initial approach, I tranformed the primitive2DContainers
before converting them to BitmapEx. This caused circles like
https://bugs.documentfoundation.org/attachment.cgi?id=193790
not to be displayed.
Simplify how BitmapExs are created by just using the range both
primitive2DContainers have in common. This way, DrawBitmapInRect
can be dropped now

Change-Id: I2401dc87b98e04b9cf9f5ebade2b5622d884fc3a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166391
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
diff --git a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
index 22a20f0..e50e59a1 100644
--- a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
+++ b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
@@ -147,8 +147,7 @@ namespace drawinglayer::unorenderer
                        convertToBitmapEx(
                            std::move(xEmbedSeq),
                            aViewInformation2D,
                            nDiscreteWidth,
                            nDiscreteHeight,
                            basegfx::B2DRange(0, 0, nDiscreteWidth, nDiscreteHeight),
                            MaximumQuadraticPixels));

                    if(!aBitmapEx.IsEmpty())
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index 6bf9dea..5cec7a4 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -176,7 +176,8 @@ void GlowPrimitive2D::create2DDecomposition(
    // I have now added a helper that just creates the mask without having
    // to render the content, use it, it's faster
    const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
        std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
        std::move(xEmbedSeq), aViewInformation2D,
        basegfx::B2DRange(0, 0, nDiscreteClippedWidth, nDiscreteClippedHeight),
        nMaximumQuadraticPixels));

    if (aAlpha.IsEmpty())
diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
index 516b004..8068a386 100644
--- a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
@@ -136,8 +136,7 @@ namespace drawinglayer::primitive2d
                    convertToBitmapEx(
                        std::move(xEmbedSeq),
                        aViewInformation2D,
                        mnDiscreteWidth,
                        mnDiscreteHeight,
                        basegfx::B2DRange(0, 0, mnDiscreteWidth, mnDiscreteHeight),
                        mnDiscreteWidth * mnDiscreteHeight));

                if(!aBitmapEx.IsEmpty())
@@ -197,8 +196,7 @@ namespace drawinglayer::primitive2d
            return convertToBitmapEx(
                        std::move(xEmbedSeq),
                        aViewInformation2D,
                        nWidth,
                        nHeight,
                        basegfx::B2DRange(0, 0, nWidth, nHeight),
                        nWidth * nHeight);
        }

diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index 5de34c5..c32f37b 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -217,7 +217,8 @@ void ShadowPrimitive2D::create2DDecomposition(
    // I have now added a helper that just creates the mask without having
    // to render the content, use it, it's faster
    const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
        std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
        std::move(xEmbedSeq), aViewInformation2D,
        basegfx::B2DRange(0, 0, nDiscreteClippedWidth, nDiscreteClippedHeight),
        nMaximumQuadraticPixels));

    // if we have no shadow, we are done
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index e6f92f3..fb01242 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -174,7 +174,8 @@ void SoftEdgePrimitive2D::create2DDecomposition(
        // Otherwise, blurring of edges will fail in cases like running in a
        // slideshow or exporting to PDF.
        const BitmapEx aBitmapEx(::drawinglayer::convertToBitmapEx(
            std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
            std::move(xEmbedSeq), aViewInformation2D,
            basegfx::B2DRange(0, 0, nDiscreteClippedWidth, nDiscreteClippedHeight),
            nMaximumQuadraticPixels, true));

        if (aBitmapEx.IsEmpty())
diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
index 6bfc958..5347378 100644
--- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
@@ -1060,8 +1060,9 @@ sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_B2

    // use new mode to create AlphaChannel (not just AlphaMask) for transparency channel
    const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
        std::move(xEmbedSeq), aEmptyViewInformation2D, nDiscreteClippedWidth,
        nDiscreteClippedHeight, nMaximumQuadraticPixels, true));
        std::move(xEmbedSeq), aEmptyViewInformation2D,
        basegfx::B2DRange(0, 0, nDiscreteClippedWidth, nDiscreteClippedHeight),
        nMaximumQuadraticPixels, true));
    sal::systools::COMReference<ID2D1Bitmap> pRetval;

    if (aAlpha.IsEmpty())
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index d93d98f..e3c7703 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -2524,9 +2524,11 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
    // limitation to paint the content
    const auto aViewInformation2D(geometry::createViewInformation2D({}));
    const sal_uInt32 nMaximumQuadraticPixels(500000);
    const BitmapEx aBitmapEx(convertToBitmapEx(
        std::move(xEmbedSeq), aViewInformation2D, basegfx::fround(aDiscreteRange.getWidth()),
        basegfx::fround(aDiscreteRange.getHeight()), nMaximumQuadraticPixels));
    const BitmapEx aBitmapEx(
        convertToBitmapEx(std::move(xEmbedSeq), aViewInformation2D,
                          basegfx::B2DRange(0, 0, basegfx::fround(aDiscreteRange.getWidth()),
                                            basegfx::fround(aDiscreteRange.getHeight())),
                          nMaximumQuadraticPixels));

    // add to target metafile (will create MetaFloatTransparentAction)
    mpOutputDevice->DrawBitmapEx(Point(basegfx::fround<tools::Long>(aLogicRange.getMinX()),
diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx
index cf339f8..a040a7c 100644
--- a/drawinglayer/source/tools/converters.cxx
+++ b/drawinglayer/source/tools/converters.cxx
@@ -73,7 +73,7 @@ bool implPrepareConversion(drawinglayer::primitive2d::Primitive2DContainer& rSeq

AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& rSequence,
                              const drawinglayer::geometry::ViewInformation2D& rViewInformation2D,
                              const Size& rSizePixel, bool bUseLuminance)
                              const Size& rSizePixel, const Point& rPoint, bool bUseLuminance)
{
    ScopedVclPtrInstance<VirtualDevice> pContent;

@@ -122,12 +122,11 @@ AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& r

    // get alpha channel from vdev
    pContent->EnableMapMode(false);
    const Point aEmptyPoint;

    // Convert from transparency->alpha.
    // FIXME in theory I should be able to directly construct alpha by using black as background
    // and white as foreground, but that doesn't work for some reason.
    Bitmap aContentBitmap = pContent->GetBitmap(aEmptyPoint, rSizePixel);
    Bitmap aContentBitmap = pContent->GetBitmap(rPoint, rSizePixel);
    aContentBitmap.Invert();

    return AlphaMask(aContentBitmap);
@@ -138,34 +137,39 @@ namespace drawinglayer
{
AlphaMask createAlphaMask(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
                          const geometry::ViewInformation2D& rViewInformation2D,
                          sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
                           const basegfx::B2DRange& rTargetRange,
                          sal_uInt32 nMaxSquarePixels, bool bUseLuminance)
{
    drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
    sal_uInt32 nDiscreteWidth = rTargetRange.getWidth();
    sal_uInt32 nDiscreteHeight = rTargetRange.getHeight();

    if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels))
    {
        return AlphaMask();
    }

    const Point aPoint(rTargetRange.getMinX(), rTargetRange.getMinY());
    const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);

    return implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, bUseLuminance);
    return implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, aPoint, bUseLuminance);
}

BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
                           const geometry::ViewInformation2D& rViewInformation2D,
                           sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
                           const basegfx::B2DRange& rTargetRange,
                           sal_uInt32 nMaxSquarePixels, bool bForceAlphaMaskCreation)
{
    drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
    sal_uInt32 nDiscreteWidth = rTargetRange.getWidth();
    sal_uInt32 nDiscreteHeight = rTargetRange.getHeight();

    if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels))
    {
        return BitmapEx();
    }

    const Point aEmptyPoint;
    const Point aPoint(rTargetRange.getMinX(), rTargetRange.getMinY());
    const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);

    // Create target VirtualDevice. Go back to using a simple RGB
@@ -224,7 +228,7 @@ BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSe
    pContentProcessor->process(aSequence);

    // create final BitmapEx result (content)
    Bitmap aRetval(pContent->GetBitmap(aEmptyPoint, aSizePixel));
    Bitmap aRetval(pContent->GetBitmap(aPoint, aSizePixel));

#ifdef DBG_UTIL
    static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
@@ -245,7 +249,7 @@ BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSe
    // Create the AlphaMask using a method that does this always correct (also used
    // now in GlowPrimitive2D and ShadowPrimitive2D which both only need the
    // AlphaMask to do their job, so speeding that up, too).
    AlphaMask aAlpha(implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, false));
    AlphaMask aAlpha(implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, aPoint, false));

#ifdef DBG_UTIL
    if (bDoSaveForVisualControl)
@@ -356,7 +360,7 @@ BitmapEx convertPrimitive2DContainerToBitmapEx(primitive2d::Primitive2DContainer
        primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };

        BitmapEx aBitmapEx(convertToBitmapEx(std::move(xEmbedSeq), aViewInformation2D,
                                             nDiscreteWidth, nDiscreteHeight,
                                             basegfx::B2DRange(0, 0, nDiscreteWidth, nDiscreteHeight),
                                             nMaximumQuadraticPixels));

        if (aBitmapEx.IsEmpty())
diff --git a/include/drawinglayer/converters.hxx b/include/drawinglayer/converters.hxx
index d090b1e..4e4d621 100644
--- a/include/drawinglayer/converters.hxx
+++ b/include/drawinglayer/converters.hxx
@@ -33,15 +33,15 @@ namespace drawinglayer
//           for any content (e.g. gradients)
AlphaMask DRAWINGLAYER_DLLPUBLIC createAlphaMask(
    drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
    const geometry::ViewInformation2D& rViewInformation2D, sal_uInt32 nDiscreteWidth,
    sal_uInt32 nDiscreteHeight, sal_uInt32 nMaxSquarePixels, bool bUseLuminance = false);
    const geometry::ViewInformation2D& rViewInformation2D, const basegfx::B2DRange& rTargetRange,
    sal_uInt32 nMaxSquarePixels, bool bUseLuminance = false);

// Helper for convertPrimitive2DContainerToBitmapEx below, but can be also used
// directly
BitmapEx DRAWINGLAYER_DLLPUBLIC convertToBitmapEx(
    drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
    const geometry::ViewInformation2D& rViewInformation2D, sal_uInt32 nDiscreteWidth,
    sal_uInt32 nDiscreteHeight, sal_uInt32 nMaxSquarePixels, bool bForceAlphaMaskCreation = false);
    const geometry::ViewInformation2D& rViewInformation2D, const basegfx::B2DRange& rTargetRange,
    sal_uInt32 nMaxSquarePixels, bool bForceAlphaMaskCreation = false);

// helper to convert any Primitive2DSequence to a good quality BitmapEx,
// using default parameters
diff --git a/include/vcl/BitmapTools.hxx b/include/vcl/BitmapTools.hxx
index de0ad84..d321d2be 100644
--- a/include/vcl/BitmapTools.hxx
+++ b/include/vcl/BitmapTools.hxx
@@ -66,10 +66,6 @@ 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/svgfilternode.hxx b/svgio/inc/svgfilternode.hxx
index ec42d3c..7aa5221 100644
--- a/svgio/inc/svgfilternode.hxx
+++ b/svgio/inc/svgfilternode.hxx
@@ -23,7 +23,6 @@
#include "svgnode.hxx"
#include "svgstyleattributes.hxx"
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <vcl/bitmapex.hxx>

typedef std::unordered_map<OUString, drawinglayer::primitive2d::Primitive2DContainer>
    IdGraphicSourceMapper;
@@ -47,8 +46,6 @@ public:
                             drawinglayer::primitive2d::Primitive2DContainer pGraphicSource) const;
    const drawinglayer::primitive2d::Primitive2DContainer*
    findGraphicSource(const OUString& rStr) const;

    static BitmapEx convertToBitmapEx(const drawinglayer::primitive2d::Primitive2DContainer* pSeq);
};

} // end of namespace svgio::svgreader
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx
index e05fe53..93d43fa 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -1614,16 +1614,167 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf160517)
    xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf160517.svg");

    assertXPath(pDocument,
            "/primitive2D/transform/bitmap"_ostr, "height"_ostr, "100");
            "/primitive2D/transform/bitmap"_ostr, "height"_ostr, "110");
    assertXPath(pDocument,
            "/primitive2D/transform/bitmap"_ostr, "width"_ostr, "100");
            "/primitive2D/transform/bitmap"_ostr, "width"_ostr, "110");
    assertXPath(pDocument,
            "/primitive2D/transform/bitmap/data"_ostr, 100);
            "/primitive2D/transform/bitmap/data"_ostr, 110);

    assertXPath(pDocument,
            "/primitive2D/transform/bitmap"_ostr, "xy11"_ostr, "110");
    assertXPath(pDocument,
            "/primitive2D/transform/bitmap"_ostr, "xy12"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy13"_ostr, "10");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy21"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy22"_ostr, "110");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy23"_ostr, "10");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy31"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy32"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D//transform/bitmap"_ostr, "xy33"_ostr, "1");

    // Check the color of a pixel in the middle
    const OUString sDataRow = getXPath(pDocument, "/primitive2D/transform/bitmap/data[50]"_ostr, "row"_ostr);
    const OUString sDataRow = getXPath(pDocument, "/primitive2D/transform/bitmap/data[55]"_ostr, "row"_ostr);
    std::vector<OUString> aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("008100"), aPixels[50]);
    CPPUNIT_ASSERT_EQUAL(OUString("008100"), aPixels[55]);
}

CPPUNIT_TEST_FIXTURE(Test, testArithmeticComposite)
{
    xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/arithmetic.svg");

    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "height"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "width"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap/data"_ostr, 150);

    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy11"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy12"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy13"_ostr, "50");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy21"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy22"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy23"_ostr, "50");
    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[25]"_ostr, "row"_ostr);
    std::vector<OUString> aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ff8000"), aPixels[25]);

    sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[75]"_ostr, "row"_ostr);
    aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ff8000"), aPixels[75]);

    sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[125]"_ostr, "row"_ostr);
    aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("000000"), aPixels[125]);
}

CPPUNIT_TEST_FIXTURE(Test, testArithmeticComposite2)
{
    xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/arithmetic2.svg");

    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "height"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "width"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap/data"_ostr, 150);

    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy11"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy12"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy13"_ostr, "20");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy21"_ostr, "0");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy22"_ostr, "150");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy23"_ostr, "20");
    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[25]"_ostr, "row"_ostr);
    std::vector<OUString> aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ff0000"), aPixels[25]);

    sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[75]"_ostr, "row"_ostr);
    aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ff8000"), aPixels[75]);

    sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[125]"_ostr, "row"_ostr);
    aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("008000"), aPixels[125]);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf160726)
{
    xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf160726.svg");

    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "height"_ostr, "250");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "width"_ostr, "250");
    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap/data"_ostr, 250);

    assertXPath(pDocument,
            "/primitive2D/transform/transform/bitmap"_ostr, "xy11"_ostr, "250");
    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, "250");
    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[50]"_ostr, "row"_ostr);
    std::vector<OUString> aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ff0000"), aPixels[50]);

    sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[125]"_ostr, "row"_ostr);
    aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ffff00"), aPixels[125]);

    sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[200]"_ostr, "row"_ostr);
    aPixels = comphelper::string::split(sDataRow, ',');
    CPPUNIT_ASSERT_EQUAL(OUString("ffff00"), aPixels[200]);
}

CPPUNIT_TEST_FIXTURE(Test, testTdf149880)
diff --git a/svgio/qa/cppunit/data/arithmetic.svg b/svgio/qa/cppunit/data/arithmetic.svg
new file mode 100644
index 0000000..cc22eed
--- /dev/null
+++ b/svgio/qa/cppunit/data/arithmetic.svg
@@ -0,0 +1,8 @@
<svg width="100" height="140" viewBox="0 0 200 280" xmlns="http://www.w3.org/2000/svg">
  <filter id="filter" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
    <feFlood x="50" y="50" width="100" height="100" flood-color="red" flood-opacity="1" result="img1"></feFlood>
    <feFlood x="50" y="50" width="100" height="100" flood-color="green" flood-opacity="1" result="img2"></feFlood>
    <feComposite in="img1" in2="img2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
  </filter>
  <use style="filter: url(#filter)"></use>
</svg>
diff --git a/svgio/qa/cppunit/data/arithmetic2.svg b/svgio/qa/cppunit/data/arithmetic2.svg
new file mode 100644
index 0000000..ba58e30
--- /dev/null
+++ b/svgio/qa/cppunit/data/arithmetic2.svg
@@ -0,0 +1,8 @@
<svg width="100" height="140" viewBox="0 0 200 280" xmlns="http://www.w3.org/2000/svg">
  <filter id="filter" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
    <feFlood x="20" y="20" width="100" height="100" flood-color="red" flood-opacity="1" result="img1"></feFlood>
    <feFlood x="50" y="50" width="100" height="100" flood-color="green" flood-opacity="1" result="img2"></feFlood>
    <feComposite in="img1" in2="img2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
  </filter>
  <use style="filter: url(#filter)"></use>
</svg>
diff --git a/svgio/qa/cppunit/data/tdf160726.svg b/svgio/qa/cppunit/data/tdf160726.svg
new file mode 100644
index 0000000..7a9ac33
--- /dev/null
+++ b/svgio/qa/cppunit/data/tdf160726.svg
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   width="500"
   height="500"
   viewBox="0 0 500 500"
   version="1.1">
  <defs
     id="defs2">
    <filter
       height="2"
       width="2"
       id="filter2103">
      <feOffset
         result="result1"
         id="feOffset2097"
         dy="50"
         dx="50" />
      <feColorMatrix
         result="result2"
         values="1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 "
         id="feColorMatrix2099" />
      <feComposite
         operator="arithmetic"
         in="result2"
         id="feComposite2101"
         in2="SourceGraphic" 
         k1="0"
         k2="1"
         k3="1"
         k4="0"/>
    </filter>
  </defs>
  <g>
    <circle
       r="100"
       cy="100"
       cx="100"
       id="circle2113"
       style="fill:#ff0000;filter:url(#filter2103)" />
  </g>
</svg>
diff --git a/svgio/source/svgreader/svgfeblendnode.cxx b/svgio/source/svgreader/svgfeblendnode.cxx
index e144018..eace3a5 100644
--- a/svgio/source/svgreader/svgfeblendnode.cxx
+++ b/svgio/source/svgreader/svgfeblendnode.cxx
@@ -87,48 +87,74 @@ void SvgFeBlendNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent
void SvgFeBlendNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTarget,
                           const SvgFilterNode* pParent) const
{
    const drawinglayer::primitive2d::Primitive2DContainer* pSource
        = pParent->findGraphicSource(maIn);
    const drawinglayer::primitive2d::Primitive2DContainer* pSource2
        = pParent->findGraphicSource(maIn2);

    if (maMode == Mode::Normal)
    {
        if (const drawinglayer::primitive2d::Primitive2DContainer* rSource2
            = pParent->findGraphicSource(maIn2))
        // Process maIn2 first
        if (pSource2)
        {
            rTarget = *rSource2;
            rTarget = *pSource2;
        }

        if (const drawinglayer::primitive2d::Primitive2DContainer* rSource
            = pParent->findGraphicSource(maIn))
        if (pSource)
        {
            rTarget.append(*rSource);
            rTarget.append(*pSource);
        }
    }
    else if (maMode == Mode::Screen)
    {
        basegfx::B2DRange aRange, aRange2;
        const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
        if (pSource)
        {
            aRange = pSource->getB2DRange(aViewInformation2D);
        }

        if (pSource2)
        {
            aRange2 = pSource2->getB2DRange(aViewInformation2D);
        }

        const sal_Int32 nX1 = std::min(aRange.getMinX(), aRange2.getMinX());
        const sal_Int32 nY1 = std::min(aRange.getMinY(), aRange2.getMinY());
        const sal_Int32 nX2 = std::max(aRange.getMaxX(), aRange2.getMaxX());
        const sal_Int32 nY2 = std::max(aRange.getMaxY(), aRange2.getMaxY());

        const basegfx::B2DRange aBaseRange(nX1, nY1, nX1 + nX2, nY1 + nY2);

        BitmapEx aBmpEx, aBmpEx2;

        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource
            = pParent->findGraphicSource(maIn))
        if (pSource)
        {
            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
            aRange = pSource->getB2DRange(aViewInformation2D);
            aBmpEx = convertToBitmapEx(pSource);
            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource);
            aBmpEx = drawinglayer::convertToBitmapEx(
                std::move(aSource), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }
        else
        {
            aBmpEx = drawinglayer::convertToBitmapEx(
                std::move(rTarget), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }

        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource2
            = pParent->findGraphicSource(maIn2))
        if (pSource2)
        {
            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
            aRange2 = pSource2->getB2DRange(aViewInformation2D);
            aBmpEx2 = convertToBitmapEx(pSource2);
            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource2);
            aBmpEx2 = drawinglayer::convertToBitmapEx(
                std::move(aSource), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }

        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);
        else
        {
            aBmpEx2 = drawinglayer::convertToBitmapEx(
                std::move(rTarget), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }

        BitmapScreenBlendFilter aScreenBlendFilter(aBmpEx, aBmpEx2);
        BitmapEx aResBmpEx = aScreenBlendFilter.execute();
@@ -136,7 +162,7 @@ void SvgFeBlendNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTar
        const drawinglayer::primitive2d::Primitive2DReference xRef(
            new drawinglayer::primitive2d::BitmapPrimitive2D(
                aResBmpEx, basegfx::utils::createScaleTranslateB2DHomMatrix(
                               aBaseRect.getRange(), aBaseRect.getMinimum())));
                               aBaseRange.getRange(), aBaseRange.getMinimum())));
        rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
    }

diff --git a/svgio/source/svgreader/svgfecompositenode.cxx b/svgio/source/svgreader/svgfecompositenode.cxx
index 3eba9f3..9136436 100644
--- a/svgio/source/svgreader/svgfecompositenode.cxx
+++ b/svgio/source/svgreader/svgfecompositenode.cxx
@@ -148,13 +148,17 @@ void SvgFeCompositeNode::parseAttribute(SVGToken aSVGToken, const OUString& aCon
void SvgFeCompositeNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTarget,
                               const SvgFilterNode* pParent) const
{
    const drawinglayer::primitive2d::Primitive2DContainer* pSource
        = pParent->findGraphicSource(maIn);
    const drawinglayer::primitive2d::Primitive2DContainer* pSource2
        = pParent->findGraphicSource(maIn2);

    if (maOperator != Operator::Arithmetic)
    {
        basegfx::B2DPolyPolygon aPolyPolygon, aPolyPolygon2;

        // Process maIn2 first
        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource2
            = pParent->findGraphicSource(maIn2))
        if (pSource2)
        {
            rTarget.append(*pSource2);
            drawinglayer::processor2d::ContourExtractor2D aExtractor(
@@ -164,8 +168,7 @@ void SvgFeCompositeNode::apply(drawinglayer::primitive2d::Primitive2DContainer& 
            aPolyPolygon2 = basegfx::utils::mergeToSinglePolyPolygon(rResult);
        }

        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource
            = pParent->findGraphicSource(maIn))
        if (pSource)
        {
            rTarget.append(*pSource);
            drawinglayer::processor2d::ContourExtractor2D aExtractor(
@@ -209,31 +212,53 @@ void SvgFeCompositeNode::apply(drawinglayer::primitive2d::Primitive2DContainer& 
    else
    {
        basegfx::B2DRange aRange, aRange2;
        const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
        if (pSource)
        {
            aRange = pSource->getB2DRange(aViewInformation2D);
        }

        if (pSource2)
        {
            aRange2 = pSource2->getB2DRange(aViewInformation2D);
        }

        const sal_uInt32 nX1 = std::min(aRange.getMinX(), aRange2.getMinX());
        const sal_uInt32 nY1 = std::min(aRange.getMinY(), aRange2.getMinY());
        const sal_uInt32 nX2 = std::max(aRange.getMaxX(), aRange2.getMaxX());
        const sal_uInt32 nY2 = std::max(aRange.getMaxY(), aRange2.getMaxY());

        const basegfx::B2DRange aBaseRange(nX1, nY1, nX1 + nX2, nY1 + nY2);

        BitmapEx aBmpEx, aBmpEx2;

        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource
            = pParent->findGraphicSource(maIn))
        if (pSource)
        {
            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
            aRange = pSource->getB2DRange(aViewInformation2D);
            aBmpEx = convertToBitmapEx(pSource);
            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource);
            aBmpEx = drawinglayer::convertToBitmapEx(
                std::move(aSource), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }
        else
        {
            aBmpEx = drawinglayer::convertToBitmapEx(
                std::move(rTarget), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }

        if (const drawinglayer::primitive2d::Primitive2DContainer* pSource2
            = pParent->findGraphicSource(maIn2))
        if (pSource2)
        {
            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
            aRange2 = pSource2->getB2DRange(aViewInformation2D);
            aBmpEx2 = convertToBitmapEx(pSource2);
            drawinglayer::primitive2d::Primitive2DContainer aSource(*pSource2);
            aBmpEx2 = drawinglayer::convertToBitmapEx(
                std::move(aSource), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }

        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);
        else
        {
            aBmpEx2 = drawinglayer::convertToBitmapEx(
                std::move(rTarget), aViewInformation2D, aBaseRange,
                aBaseRange.getWidth() * aBaseRange.getHeight());
        }

        BitmapArithmeticBlendFilter aArithmeticFilter(aBmpEx, aBmpEx2);
        BitmapEx aResBmpEx = aArithmeticFilter.execute(maK1.getNumber(), maK2.getNumber(),
@@ -242,7 +267,7 @@ void SvgFeCompositeNode::apply(drawinglayer::primitive2d::Primitive2DContainer& 
        const drawinglayer::primitive2d::Primitive2DReference xRef(
            new drawinglayer::primitive2d::BitmapPrimitive2D(
                aResBmpEx, basegfx::utils::createScaleTranslateB2DHomMatrix(
                               aBaseRect.getRange(), aBaseRect.getMinimum())));
                               aBaseRange.getRange(), aBaseRange.getMinimum())));
        rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
    }
}
diff --git a/svgio/source/svgreader/svgfilternode.cxx b/svgio/source/svgreader/svgfilternode.cxx
index 4b97563..5b2f7e8 100644
--- a/svgio/source/svgreader/svgfilternode.cxx
+++ b/svgio/source/svgreader/svgfilternode.cxx
@@ -25,10 +25,6 @@
#include <svgfegaussianblurnode.hxx>
#include <svgfeoffsetnode.hxx>

#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/converters.hxx>

namespace svgio::svgreader
{
SvgFilterNode::SvgFilterNode(SVGToken aType, SvgDocument& rDocument, SvgNode* pParent)
@@ -95,23 +91,6 @@ SvgFilterNode::findGraphicSource(const OUString& rStr) const
    }
}

BitmapEx
SvgFilterNode::convertToBitmapEx(const drawinglayer::primitive2d::Primitive2DContainer* pSeq)
{
    drawinglayer::primitive2d::Primitive2DContainer aSequence(*pSeq);

    const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
    basegfx::B2DRange aRange = aSequence.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, std::move(aSequence)));
    drawinglayer::primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
    return drawinglayer::convertToBitmapEx(std::move(xEmbedSeq), aViewInformation2D,
                                           aRange.getWidth(), aRange.getHeight(), 500000);
}

} // end of namespace svgio::svgreader

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapTools.cxx b/vcl/source/bitmap/BitmapTools.cxx
index b518661..cb094c0 100644
--- a/vcl/source/bitmap/BitmapTools.cxx
+++ b/vcl/source/bitmap/BitmapTools.cxx
@@ -512,120 +512,6 @@ BitmapEx CanvasTransformBitmap( const BitmapEx&                 rBitmap,
    return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
}

BitmapEx DrawBitmapInRect( const BitmapEx& rBitmap,
                                ::basegfx::B2DRectangle const & rBitmapRect,
                                ::basegfx::B2DRectangle const & rDestRect )
{
    if( rDestRect.isEmpty() )
        return BitmapEx();

    const Size aDestBmpSize( ::basegfx::fround<tools::Long>( rDestRect.getWidth() ),
                             ::basegfx::fround<tools::Long>( 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;

    // 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, vcl::PixelFormat::N24_BPP);
    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)
{
    // mix existing and new alpha mask