tdf#129342 tdf#170985 EMF+ Implement BrushTypeTextureFill for bitmaps
This patch implements the missing TextureFill brush support for EMF+ records. Previously, texture fills were ignored, resulting in missing backgrounds or shapes. Change-Id: I61f80a9851f3929651f54b68d6d4d21e65720aa6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200547 Tested-by: Jenkins Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
This commit is contained in:
@@ -60,7 +60,7 @@ namespace emfplushelper
|
||||
return u""_ustr;
|
||||
}
|
||||
|
||||
void EMFPBrush::Read(SvStream& s, EmfPlusHelperData const & rR)
|
||||
void EMFPBrush::Read(SvMemoryStream& s, EmfPlusHelperData const & rR, sal_uInt32 dataSize, bool bUseWholeStream)
|
||||
{
|
||||
sal_uInt32 header;
|
||||
|
||||
@@ -99,7 +99,21 @@ namespace emfplushelper
|
||||
}
|
||||
case BrushTypeTextureFill:
|
||||
{
|
||||
SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
|
||||
s.ReadUInt32(additionalFlags).ReadInt32(wrapMode);
|
||||
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tTextureFill, flags: 0x"
|
||||
<< std::hex << additionalFlags << std::dec
|
||||
<< ", wrapMode: " << wrapMode);
|
||||
|
||||
if (additionalFlags & 0x02) // BrushDataTransform
|
||||
{
|
||||
EmfPlusHelperData::readXForm(s, brush_transformation);
|
||||
hasTransformation = true;
|
||||
SAL_INFO("drawinglayer.emf",
|
||||
"EMF+\t\t\t\tUse brush transformation: " << brush_transformation);
|
||||
}
|
||||
|
||||
textureImage = std::make_unique<EMFPImage>();
|
||||
textureImage->Read(s, dataSize, bUseWholeStream);
|
||||
break;
|
||||
}
|
||||
case BrushTypePathGradient:
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "emfphelperdata.hxx"
|
||||
#include "emfpimage.hxx"
|
||||
#include <tools/color.hxx>
|
||||
|
||||
namespace emfplushelper
|
||||
@@ -114,6 +115,7 @@ namespace emfplushelper
|
||||
std::unique_ptr<::Color[]> surroundColors;
|
||||
std::unique_ptr<EMFPPath> path;
|
||||
EmfPlusHatchStyle hatchStyle;
|
||||
std::unique_ptr<EMFPImage> textureImage;
|
||||
|
||||
EMFPBrush();
|
||||
virtual ~EMFPBrush() override;
|
||||
@@ -121,7 +123,7 @@ namespace emfplushelper
|
||||
sal_uInt32 GetType() const { return type; }
|
||||
const ::Color& GetColor() const { return solidColor; }
|
||||
|
||||
void Read(SvStream& s, EmfPlusHelperData const & rR);
|
||||
void Read(SvMemoryStream& s, EmfPlusHelperData const & rR, sal_uInt32 dataSize, bool bUseWholeStream);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,11 +28,14 @@
|
||||
#include "emfpfont.hxx"
|
||||
#include "emfpstringformat.hxx"
|
||||
#include <wmfemfhelper.hxx>
|
||||
#include <drawinglayer/attribute/fillgraphicattribute.hxx>
|
||||
#include <drawinglayer/attribute/fontattribute.hxx>
|
||||
#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
|
||||
@@ -40,7 +43,6 @@
|
||||
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
|
||||
#include <drawinglayer/attribute/fontattribute.hxx>
|
||||
#include <basegfx/color/bcolor.hxx>
|
||||
#include <basegfx/color/bcolormodifier.hxx>
|
||||
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
||||
@@ -273,14 +275,14 @@ namespace emfplushelper
|
||||
{
|
||||
EMFPBrush *brush = new EMFPBrush();
|
||||
maEMFPObjects[index].reset(brush);
|
||||
brush->Read(rObjectStream, *this);
|
||||
brush->Read(rObjectStream, *this, dataSize, bUseWholeStream);
|
||||
break;
|
||||
}
|
||||
case EmfPlusObjectTypePen:
|
||||
{
|
||||
EMFPPen *pen = new EMFPPen();
|
||||
maEMFPObjects[index].reset(pen);
|
||||
pen->Read(rObjectStream, *this);
|
||||
pen->Read(rObjectStream, *this, dataSize, bUseWholeStream);
|
||||
pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal);
|
||||
break;
|
||||
}
|
||||
@@ -771,12 +773,65 @@ namespace emfplushelper
|
||||
polygon,
|
||||
fillColor.getBColor()));
|
||||
}
|
||||
else if (brush->type == BrushTypeTextureFill)
|
||||
else if (brush->type == BrushTypeTextureFill && brush->textureImage)
|
||||
{
|
||||
SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
|
||||
if (brush->textureImage->type != ImageDataTypeBitmap)
|
||||
{
|
||||
SAL_WARN("drawinglayer.emf", "EMF+\tTextureFill: Metafiles type is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
const Size aSizePx = brush->textureImage->graphic.GetSizePixel();
|
||||
if (aSizePx.Width() <= 0 || aSizePx.Height() <= 0)
|
||||
return;
|
||||
|
||||
basegfx::B2DHomMatrix aBrushMatrix;
|
||||
if (brush->additionalFlags & 0x02) // 0x02 = BrushDataTransform flag
|
||||
aBrushMatrix = brush->brush_transformation;
|
||||
|
||||
// Create a scaling matrix to map the normalized 1x1 range
|
||||
// to the actual pixel dimensions of the texture.
|
||||
|
||||
const basegfx::B2DHomMatrix aTextureScale(
|
||||
/* Row 0, Column 0 */ aSizePx.Width(),
|
||||
/* Row 0, Column 1 */ 0.0,
|
||||
/* Row 0, Column 2 */ 0.0,
|
||||
/* Row 1, Column 0 */ 0.0,
|
||||
/* Row 1, Column 1 */ aSizePx.Height(),
|
||||
/* Row 1, Column 2 */ 0.0);
|
||||
|
||||
const basegfx::B2DHomMatrix aTotalMatrix = maMapTransform * aBrushMatrix * aTextureScale;
|
||||
|
||||
// Invert the matrix to transform the fill polygon from document space
|
||||
// back into the local normalized texture space (0.0 to 1.0).
|
||||
basegfx::B2DHomMatrix aInvTotalMatrix = aTotalMatrix;
|
||||
if (!aInvTotalMatrix.invert())
|
||||
{
|
||||
SAL_WARN("drawinglayer.emf", "EMF+\tTextureFill: Failed to invert transformation matrix");
|
||||
return;
|
||||
}
|
||||
basegfx::B2DPolyPolygon aLocalPolygon = polygon;
|
||||
aLocalPolygon.transform(aInvTotalMatrix);
|
||||
|
||||
const bool bIsTiled = (brush->wrapMode != WrapModeClamp);
|
||||
|
||||
const drawinglayer::attribute::FillGraphicAttribute aFillAttribute(
|
||||
brush->textureImage->graphic,
|
||||
basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
|
||||
bIsTiled);
|
||||
|
||||
drawinglayer::primitive2d::Primitive2DReference xFillPrimitive =
|
||||
new drawinglayer::primitive2d::PolyPolygonGraphicPrimitive2D(
|
||||
aLocalPolygon,
|
||||
basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
|
||||
aFillAttribute);
|
||||
|
||||
mrTargetHolders.Current().append(
|
||||
new drawinglayer::primitive2d::TransformPrimitive2D(
|
||||
aTotalMatrix,
|
||||
drawinglayer::primitive2d::Primitive2DContainer({ xFillPrimitive })));
|
||||
}
|
||||
else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
|
||||
|
||||
{
|
||||
if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
|
||||
{
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace emfplushelper
|
||||
return drawinglayer::attribute::StrokeAttribute();
|
||||
}
|
||||
|
||||
void EMFPPen::Read(SvStream& s, EmfPlusHelperData const & rR)
|
||||
void EMFPPen::Read(SvMemoryStream& s, EmfPlusHelperData const & rR, sal_uInt32 dataSize, bool bUseWholeStream)
|
||||
{
|
||||
sal_Int32 lineJoin = EmfPlusLineJoinTypeMiter;
|
||||
sal_uInt32 graphicsVersion, penType;
|
||||
@@ -373,7 +373,7 @@ namespace emfplushelper
|
||||
customEndCapLen = 0;
|
||||
}
|
||||
|
||||
EMFPBrush::Read(s, rR);
|
||||
EMFPBrush::Read(s, rR, dataSize, bUseWholeStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace emfplushelper
|
||||
|
||||
virtual ~EMFPPen() override;
|
||||
|
||||
void Read(SvStream& s, EmfPlusHelperData const & rR);
|
||||
void Read(SvMemoryStream& s, EmfPlusHelperData const & rR, sal_uInt32 dataSize, bool bUseWholeStream);
|
||||
|
||||
drawinglayer::attribute::StrokeAttribute GetStrokeAttribute(const double aTransformation) const;
|
||||
};
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
|
||||
#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
|
||||
@@ -68,6 +69,7 @@
|
||||
#include <drawinglayer/attribute/sdrfillattribute.hxx>
|
||||
#include <drawinglayer/attribute/fillhatchattribute.hxx>
|
||||
#include <drawinglayer/attribute/fillgradientattribute.hxx>
|
||||
#include <drawinglayer/attribute/fillgraphicattribute.hxx>
|
||||
#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
|
||||
#include <drawinglayer/attribute/materialattribute3d.hxx>
|
||||
#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
|
||||
@@ -733,6 +735,15 @@ void Primitive2dXmlDump::decomposeAndWrite(
|
||||
rWriter.endElement();
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
|
||||
{
|
||||
rWriter.startElement("fillgraphic");
|
||||
runDecomposeAndRecurse(pBasePrimitive, rWriter);
|
||||
rWriter.endElement();
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D:
|
||||
{
|
||||
const auto& rHiddenGeometryPrimitive2D
|
||||
@@ -852,6 +863,20 @@ void Primitive2dXmlDump::decomposeAndWrite(
|
||||
rWriter.endElement();
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
|
||||
{
|
||||
const auto& rPolyPolygonGraphicPrimitive2D
|
||||
= static_cast<const PolyPolygonGraphicPrimitive2D&>(*pBasePrimitive);
|
||||
rWriter.startElement("polypolygongraphic");
|
||||
rWriter.attribute(
|
||||
"transparency",
|
||||
std::lround(100 * rPolyPolygonGraphicPrimitive2D.getTransparency()));
|
||||
runDecomposeAndRecurse(pBasePrimitive, rWriter);
|
||||
rWriter.endElement();
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
|
||||
{
|
||||
const auto& rPolyPolygonStrokePrimitive2D
|
||||
|
||||
@@ -117,7 +117,7 @@ CPPUNIT_TEST_FIXTURE(Test, testPolyPolygon)
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testDrawImagePointsTypeBitmap)
|
||||
{
|
||||
// tdf#142941 EMF+ file with ObjectTypeImage, FillRects, DrawImagePoints ,records
|
||||
// tdf#142941 EMF+ file with ObjectTypeImage, FillRects, DrawImagePoints
|
||||
// The test is checking the position of displaying bitmap with too large SrcRect
|
||||
|
||||
Primitive2DSequence aSequence
|
||||
@@ -147,6 +147,50 @@ CPPUNIT_TEST_FIXTURE(Test, testDrawImagePointsTypeBitmap)
|
||||
"b63539,b53335,ba3236,a2393e,1c0000");
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testFillRectsWithTextureBrush)
|
||||
{
|
||||
// tdf#129342 tdf#170985 EMF+ file with ObjectTypeBrush, ObjectTypepen, FillRects
|
||||
// The test is checking the position of displaying bitmap with too large SrcRect
|
||||
|
||||
Primitive2DSequence aSequence
|
||||
= parseEmf(u"/emfio/qa/cppunit/emf/data/TestEmfPlusFillRectsWithTextureBrush.emf");
|
||||
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
|
||||
drawinglayer::Primitive2dXmlDump dumper;
|
||||
xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence));
|
||||
CPPUNIT_ASSERT(pDocument);
|
||||
|
||||
assertXPath(pDocument, aXPathPrefix + "transform/polypolygongraphic", "transparency", u"0");
|
||||
assertXPath(pDocument, aXPathPrefix + "transform/polypolygongraphic/mask/polypolygon", 1);
|
||||
|
||||
assertXPath(pDocument,
|
||||
aXPathPrefix + "transform/polypolygongraphic/mask/fillgraphic/group/transform", 17);
|
||||
assertXPath(pDocument,
|
||||
aXPathPrefix
|
||||
+ "transform/polypolygongraphic/mask/fillgraphic/group/transform/bitmap/data",
|
||||
697);
|
||||
|
||||
assertXPath(
|
||||
pDocument,
|
||||
aXPathPrefix
|
||||
+ "transform/polypolygongraphic/mask/fillgraphic/group/transform[1]/bitmap/data",
|
||||
41);
|
||||
assertXPath(
|
||||
pDocument,
|
||||
aXPathPrefix
|
||||
+ "transform/polypolygongraphic/mask/fillgraphic/group/transform[1]/bitmap/data[30]",
|
||||
"row",
|
||||
u"fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,"
|
||||
u"fefefe,fefefe,fefefe,fefefe,ffedfe,edf6f5,e6fffd,edfff8,e8f3e3,ffffef,fffbed,ffe8e1,"
|
||||
u"f5f6e6,fff7f3,fff0f6,ffeff4,f6ffff,e9ffff,f2ffff,f4ebfe,fefefe,fcfcfc,fefefe,ffffff,"
|
||||
u"ffffff,fdfdfd,fbfbfb,fdfdfd,fff6fd,fff9fc,fdfcfa,f6fdf6,f7fffb,f7fffa,f9fffa,f2f9f1,"
|
||||
u"fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,"
|
||||
u"fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe,fefefe");
|
||||
|
||||
assertXPath(pDocument, aXPathPrefix + "polypolygonstroke/line", "width", u"26");
|
||||
assertXPath(pDocument, aXPathPrefix + "polypolygonstroke/polypolygon", "minx", u"397");
|
||||
assertXPath(pDocument, aXPathPrefix + "polypolygonstroke/polypolygon", "miny", u"397");
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testDrawString)
|
||||
{
|
||||
#if HAVE_MORE_FONTS
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user