summaryrefslogtreecommitdiffstats
path: root/drawinglayer
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-01 15:40:00 +0200
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2016-07-07 22:32:39 +0200
commit4609380bb0bde0d4437b72b752c1c24ee2950361 (patch)
treed5083eb62aa8f5717997274cecf4011e8f5b8126 /drawinglayer
parenttdf#99165 avoid passing empty control points for beziers (diff)
downloadcore-4609380bb0bde0d4437b72b752c1c24ee2950361.tar.gz
core-4609380bb0bde0d4437b72b752c1c24ee2950361.zip
tdf#82214 optimize performance for primitives
See svg bug doc, which is processed quite slowly. Beyond needing faster renderers, there is also demand to improve the handling of primitives created by SVG import. Conflicts: drawinglayer/source/primitive2d/patternfillprimitive2d.cxx vcl/win/gdi/gdiimpl.cxx Change-Id: I10992a5746b8b2d6b50e3ee3fe415a035685c9ba
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/source/primitive2d/patternfillprimitive2d.cxx205
-rw-r--r--drawinglayer/source/processor2d/objectinfoextractor2d.cxx20
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx10
-rw-r--r--drawinglayer/source/texture/texture.cxx53
4 files changed, 253 insertions, 35 deletions
diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
index ff3e4452886b..39b695c4be7a 100644
--- a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
@@ -21,19 +21,144 @@
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/texture/texture.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
-
+#include <drawinglayer/tools/converters.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
using namespace com::sun::star;
+#define MAXIMUM_SQUARE_LENGTH (164.0)
+#define MINIMUM_SQUARE_LENGTH (32.0)
+#define MINIMUM_TILES_LENGTH (3)
namespace drawinglayer
{
namespace primitive2d
{
+ void PatternFillPrimitive2D::calculateNeededDiscreteBufferSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // reset parameters
+ rWidth = rHeight = 0;
+
+ // check if resolution is in the range which may be buffered
+ const basegfx::B2DPolyPolygon& rMaskPolygon = getMask();
+ const basegfx::B2DRange aMaskRange(rMaskPolygon.getB2DRange());
+
+ // get discrete rounded up square size of a single tile
+ const basegfx::B2DHomMatrix aMaskRangeTransformation(
+ basegfx::tools::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+ const basegfx::B2DHomMatrix aTransform(
+ rViewInformation.getObjectToViewTransformation() * aMaskRangeTransformation);
+ const basegfx::B2DPoint aTopLeft(aTransform * getReferenceRange().getMinimum());
+ const basegfx::B2DPoint aX(aTransform * basegfx::B2DPoint(getReferenceRange().getMaxX(), getReferenceRange().getMinY()));
+ const basegfx::B2DPoint aY(aTransform * basegfx::B2DPoint(getReferenceRange().getMinX(), getReferenceRange().getMaxY()));
+ const double fW(basegfx::B2DVector(aX - aTopLeft).getLength());
+ const double fH(basegfx::B2DVector(aY - aTopLeft).getLength());
+ const double fSquare(fW * fH);
+
+ if(fSquare > 0.0)
+ {
+ // check if less than a maximum square pixels is used
+ static sal_uInt32 fMaximumSquare(MAXIMUM_SQUARE_LENGTH * MAXIMUM_SQUARE_LENGTH);
+
+ if(fSquare < fMaximumSquare)
+ {
+ // calculate needed number of tiles and check if used more than a minimum count
+ const texture::GeoTexSvxTiled aTiling(getReferenceRange());
+ const sal_uInt32 nTiles(aTiling.getNumberOfTiles());
+ static sal_uInt32 nMinimumTiles(MINIMUM_TILES_LENGTH * MINIMUM_TILES_LENGTH);
+
+ if(nTiles >= nMinimumTiles)
+ {
+ rWidth = basegfx::fround(ceil(fW));
+ rHeight = basegfx::fround(ceil(fH));
+ static sal_uInt32 fMinimumSquare(MINIMUM_SQUARE_LENGTH * MINIMUM_SQUARE_LENGTH);
+
+ if(fSquare < fMinimumSquare)
+ {
+ const double fRel(fW/fH);
+ rWidth = basegfx::fround(sqrt(fMinimumSquare * fRel));
+ rHeight = basegfx::fround(sqrt(fMinimumSquare / fRel));
+ }
+ }
+ }
+ }
+ }
+
+ Primitive2DContainer PatternFillPrimitive2D::createContent(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aContent;
+
+ // see if buffering is wanted. it is wanted, create buffered content in given resolution
+ if(0 != mnDiscreteWidth && 0 != mnDiscreteHeight)
+ {
+ const geometry::ViewInformation2D aViewInformation2D;
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ basegfx::tools::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight),
+ getChildren()));
+ const primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ const BitmapEx aBitmapEx(
+ tools::convertToBitmapEx(
+ xEmbedSeq,
+ aViewInformation2D,
+ mnDiscreteWidth,
+ mnDiscreteHeight,
+ mnDiscreteWidth * mnDiscreteHeight));
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ const Size& rBmpPix = aBitmapEx.GetSizePixel();
+
+ if(rBmpPix.Width() > 0 && rBmpPix.Height() > 0)
+ {
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new primitive2d::BitmapPrimitive2D(
+ aBitmapEx,
+ basegfx::B2DHomMatrix()));
+ aContent = primitive2d::Primitive2DContainer { xEmbedRefBitmap };
+ }
+ }
+ }
+
+ if(aContent.empty())
+ {
+ // buffering was not tried or did fail - reset remembered buffered size
+ // in any case
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = pThat->mnDiscreteHeight = 0;
+
+ // use children as default context
+ aContent = getChildren();
+
+ // check if content needs to be clipped
+ const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ const basegfx::B2DRange aContentRange(getChildren().getB2DRange(rViewInformation));
+
+ if(!aUnitRange.isInside(aContentRange))
+ {
+ const Primitive2DReference xRef(
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aUnitRange)),
+ aContent));
+
+ aContent = Primitive2DContainer { xRef };
+ }
+ }
+
+ return aContent;
+ }
+
Primitive2DContainer PatternFillPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
Primitive2DContainer aRetval;
@@ -52,20 +177,8 @@ namespace drawinglayer
aTiling.appendTransformations(aMatrices);
- // check if content needs to be clipped
- const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
- const basegfx::B2DRange aContentRange(getChildren().getB2DRange(rViewInformation));
- Primitive2DContainer aContent(getChildren());
-
- if(!aUnitRange.isInside(aContentRange))
- {
- const Primitive2DReference xRef(
- new MaskPrimitive2D(
- basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aUnitRange)),
- aContent));
-
- aContent = Primitive2DContainer { xRef };
- }
+ // create content
+ const Primitive2DContainer aContent(createContent(rViewInformation));
// resize result
aRetval.resize(aMatrices.size());
@@ -117,7 +230,9 @@ namespace drawinglayer
: BufferedDecompositionPrimitive2D(),
maMask(rMask),
maChildren(rChildren),
- maReferenceRange(rReferenceRange)
+ maReferenceRange(rReferenceRange),
+ mnDiscreteWidth(0),
+ mnDiscreteHeight(0)
{
}
@@ -140,6 +255,64 @@ namespace drawinglayer
return getMask().getB2DRange();
}
+ Primitive2DContainer PatternFillPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(0 == mnDiscreteWidth || 0 == mnDiscreteHeight)
+ {
+ // Currently no buffering is used. Check if the resulting discrete sizes
+ // in the current situation would be good for buffering from now on
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+
+ calculateNeededDiscreteBufferSize(pThat->mnDiscreteWidth, pThat->mnDiscreteHeight, rViewInformation);
+ }
+ else
+ {
+ // The existing bufferd decomposition uses a buffer in the remembered
+ // size. Get new needed sizes which depend on the given ViewInformation
+ sal_uInt32 nW(0);
+ sal_uInt32 nH(0);
+ calculateNeededDiscreteBufferSize(nW, nH, rViewInformation);
+
+ if(0 != nW && 0 != nH)
+ {
+ // buffering is possible - check if reset is needed
+ bool bResetBuffering = false;
+
+ if(nW > mnDiscreteWidth || nH > mnDiscreteHeight)
+ {
+ // Higher resolution is needed than used in the existing buffered
+ // decomposition
+ bResetBuffering = true;
+ }
+ else if(double(nW * nH) / double(mnDiscreteWidth * mnDiscreteHeight) <= 0.5)
+ {
+ // Size has shrunk for 50% or more - it's worth to refresh the buffering
+ bResetBuffering = true;
+ }
+
+ if(bResetBuffering)
+ {
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = nW;
+ pThat->mnDiscreteHeight = nH;
+ pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+ else
+ {
+ // no buffering wanted or possible - clear decomposition to create a
+ // new, unbuffered one
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = 0;
+ pThat->mnDiscreteHeight = 0;
+ pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ // call parent
+ return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
+ }
+
// provide unique ID
ImplPrimitive2DIDBlock(PatternFillPrimitive2D, PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D)
diff --git a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
index ad26620a1630..7ae806f2890b 100644
--- a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
+++ b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
@@ -40,8 +40,24 @@ namespace drawinglayer
}
default :
{
- // process recursively
- process(rCandidate.get2DDecomposition(getViewInformation2D()));
+ // we look for an encapsulated primitive, so do not decompose primitives
+ // based on GroupPrimitive2D, just visit their children. It may be that more
+ // group-like primitives need to be added here, but all primitives with
+ // grouping functionality should be implemented based on the GroupPrimitive2D
+ // class and have their main content accessible as children
+ const primitive2d::GroupPrimitive2D* pGroupPrimitive2D = dynamic_cast< const primitive2d::GroupPrimitive2D* >(&rCandidate);
+
+ if(pGroupPrimitive2D)
+ {
+ // process group children recursively
+ process(pGroupPrimitive2D->getChildren());
+ }
+ else
+ {
+ // do not process recursively, we *only* want to find existing
+ // ObjectInfoPrimitive2D entries
+ }
+
break;
}
}
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 2e6bc257b337..1424d8a1f182 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -182,17 +182,19 @@ namespace drawinglayer
bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency)
{
- basegfx::B2DPolygon aLocalPolygon(rSource.getB2DPolygon());
-
- if(!aLocalPolygon.count())
+ if(!rSource.getB2DPolygon().count())
{
// no geometry, done
return true;
}
- aLocalPolygon = basegfx::tools::simplifyCurveSegments(aLocalPolygon);
+ // get geometry data, prepare hairline data
+ basegfx::B2DPolygon aLocalPolygon(rSource.getB2DPolygon());
basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+ // simplify curve segments
+ aLocalPolygon = basegfx::tools::simplifyCurveSegments(aLocalPolygon);
+
if(rSource.getStrokeAttribute().isDefault() || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen())
{
// no line dashing, just copy
diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx
index e205d89c53da..7d53a46e4d29 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -720,9 +720,20 @@ namespace drawinglayer
&& mfOffsetY == pCompare->mfOffsetY);
}
- void GeoTexSvxTiled::appendTransformations(::std::vector< basegfx::B2DHomMatrix >& rMatrices)
+ sal_uInt32 GeoTexSvxTiled::getNumberOfTiles() const
+ {
+ return iterateTiles(nullptr);
+ }
+
+ void GeoTexSvxTiled::appendTransformations(::std::vector< basegfx::B2DHomMatrix >& rMatrices) const
+ {
+ iterateTiles(&rMatrices);
+ }
+
+ sal_Int32 GeoTexSvxTiled::iterateTiles(::std::vector< basegfx::B2DHomMatrix >* pMatrices) const
{
const double fWidth(maRange.getWidth());
+ sal_Int32 nTiles = 0;
if(!basegfx::fTools::equalZero(fWidth))
{
@@ -774,12 +785,19 @@ namespace drawinglayer
for(double fPosY((nPosX % 2) ? fStartY - fHeight + (mfOffsetY * fHeight) : fStartY);
basegfx::fTools::less(fPosY, 1.0); fPosY += fHeight)
{
- rMatrices.push_back(
- basegfx::tools::createScaleTranslateB2DHomMatrix(
- fWidth,
- fHeight,
- fPosX,
- fPosY));
+ if(pMatrices)
+ {
+ pMatrices->push_back(
+ basegfx::tools::createScaleTranslateB2DHomMatrix(
+ fWidth,
+ fHeight,
+ fPosX,
+ fPosY));
+ }
+ else
+ {
+ nTiles++;
+ }
}
}
}
@@ -790,17 +808,26 @@ namespace drawinglayer
for(double fPosX((nPosY % 2) ? fStartX - fWidth + (mfOffsetX * fWidth) : fStartX);
basegfx::fTools::less(fPosX, 1.0); fPosX += fWidth)
{
- rMatrices.push_back(
- basegfx::tools::createScaleTranslateB2DHomMatrix(
- fWidth,
- fHeight,
- fPosX,
- fPosY));
+ if(pMatrices)
+ {
+ pMatrices->push_back(
+ basegfx::tools::createScaleTranslateB2DHomMatrix(
+ fWidth,
+ fHeight,
+ fPosX,
+ fPosY));
+ }
+ else
+ {
+ nTiles++;
+ }
}
}
}
}
}
+
+ return nTiles;
}
} // end of namespace texture
} // end of namespace drawinglayer