summaryrefslogtreecommitdiff
path: root/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-01 15:40:00 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-07 15:08:59 +0200
commit3665e5b65d74d4ae22b637502efdac65d4b6c96c (patch)
tree6860d5553a8dd0ed374780b2690252d51c9a0bb9 /drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
parent96b3f84a2b1e3745326d54bc1ebe5eb69d536be5 (diff)
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/source/primitive2d/patternfillprimitive2d.cxx')
-rw-r--r--drawinglayer/source/primitive2d/patternfillprimitive2d.cxx205
1 files changed, 189 insertions, 16 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)