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