summaryrefslogtreecommitdiff
path: root/drawinglayer
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@Sun.COM>2009-10-07 14:25:40 +0200
committerArmin Le Grand <Armin.Le.Grand@Sun.COM>2009-10-07 14:25:40 +0200
commit407f7ae14844d0e55b9f0f87a19a7208762b05ed (patch)
tree2a24b06f3a318077961b66210bd097fe9aa66262 /drawinglayer
parent78fb1283ea0f5a059740790e3933fe60b520c7cf (diff)
#i105323# added FastPath for 3D scene HitTest for 3d CustomShapes by re-using the buffered last render result from the ScenePrimitive2D
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/inc/drawinglayer/primitive2d/sceneprimitive2d.hxx17
-rw-r--r--drawinglayer/source/primitive2d/sceneprimitive2d.cxx116
-rw-r--r--drawinglayer/source/processor2d/hittestprocessor2d.cxx169
3 files changed, 186 insertions, 116 deletions
diff --git a/drawinglayer/inc/drawinglayer/primitive2d/sceneprimitive2d.hxx b/drawinglayer/inc/drawinglayer/primitive2d/sceneprimitive2d.hxx
index 1cb433e6bff1..a86e32adca26 100644
--- a/drawinglayer/inc/drawinglayer/primitive2d/sceneprimitive2d.hxx
+++ b/drawinglayer/inc/drawinglayer/primitive2d/sceneprimitive2d.hxx
@@ -41,6 +41,7 @@
#include <drawinglayer/attribute/sdrattribute3d.hxx>
#include <drawinglayer/geometry/viewinformation3d.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vcl/bitmapex.hxx>
//////////////////////////////////////////////////////////////////////////////
@@ -70,9 +71,13 @@ namespace drawinglayer
double mfOldDiscreteSizeY;
basegfx::B2DRange maOldUnitVisiblePart;
+ // the last created BitmapEx, e.g. for fast HitTest. This does not really need
+ // memory since BitmapEx is internally RefCounted
+ BitmapEx maOldRenderedBitmap;
+
// private helpers
bool impGetShadow3D(const geometry::ViewInformation2D& rViewInformation) const;
- void calculateDsicreteSizes(
+ void calculateDiscreteSizes(
const geometry::ViewInformation2D& rViewInformation,
basegfx::B2DRange& rDiscreteRange,
basegfx::B2DRange& rVisibleDiscreteRange,
@@ -90,6 +95,16 @@ namespace drawinglayer
Primitive2DSequence getGeometry2D() const;
Primitive2DSequence getShadow2D(const geometry::ViewInformation2D& rViewInformation) const;
+ // Fast HitTest which uses the last buffered BitmapEx from the last
+ // rendered area if available. The return value describes if the check
+ // could be done with the current information, so do NOT use o_rResult
+ // when it returns false. o_rResult will be changed on return true and
+ // then contains a definitive answer if content of this scene is hit or
+ // not. On return false, it is normally necessary to use the geometric
+ // HitTest (see CutFindProcessor usages). The given HitPoint
+ // has to be in logic coordinates in scene's ObjectCoordinateSystem.
+ bool tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const;
+
// constructor/destructor
ScenePrimitive2D(
const primitive3d::Primitive3DSequence& rxChildren3D,
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
index 7fa9fdec336c..6b346d5abdc0 100644
--- a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -100,7 +100,7 @@ namespace drawinglayer
return maShadowPrimitives.hasElements();
}
- void ScenePrimitive2D::calculateDsicreteSizes(
+ void ScenePrimitive2D::calculateDiscreteSizes(
const geometry::ViewInformation2D& rViewInformation,
basegfx::B2DRange& rDiscreteRange,
basegfx::B2DRange& rVisibleDiscreteRange,
@@ -110,23 +110,12 @@ namespace drawinglayer
rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
- // force to discrete expanded bounds (it grows, so expanding works perfectly well)
- rDiscreteRange.expand(basegfx::B2DTuple(floor(rDiscreteRange.getMinX()), floor(rDiscreteRange.getMinY())));
- rDiscreteRange.expand(basegfx::B2DTuple(ceil(rDiscreteRange.getMaxX()), ceil(rDiscreteRange.getMaxY())));
-
// clip it against discrete Viewport (if set)
rVisibleDiscreteRange = rDiscreteRange;
if(!rViewInformation.getViewport().isEmpty())
{
rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
-
- if(!rVisibleDiscreteRange.isEmpty())
- {
- // force to discrete expanded bounds, too
- rVisibleDiscreteRange.expand(basegfx::B2DTuple(floor(rVisibleDiscreteRange.getMinX()), floor(rVisibleDiscreteRange.getMinY())));
- rVisibleDiscreteRange.expand(basegfx::B2DTuple(ceil(rVisibleDiscreteRange.getMaxX()), ceil(rVisibleDiscreteRange.getMaxY())));
- }
}
if(rVisibleDiscreteRange.isEmpty())
@@ -177,11 +166,11 @@ namespace drawinglayer
}
}
- // get the involved ranges (see helper method calculateDsicreteSizes for details)
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
basegfx::B2DRange aDiscreteRange;
basegfx::B2DRange aVisibleDiscreteRange;
basegfx::B2DRange aUnitVisibleRange;
- calculateDsicreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
if(!aVisibleDiscreteRange.isEmpty())
{
@@ -204,11 +193,25 @@ namespace drawinglayer
{
// when reducing the visualisation is allowed (e.g. an OverlayObject
// only needed for dragging), reduce resolution extra
- // to speed up dragging interactions (1/4th currently)
- const double fReducedVisualisationFactor(0.25);
- fReduceFactor *= fReducedVisualisationFactor;
- fViewSizeX *= fReducedVisualisationFactor;
- fViewSizeY *= fReducedVisualisationFactor;
+ // to speed up dragging interactions
+ const double fArea(fViewSizeX * fViewSizeY);
+ double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
+
+ if(fReducedVisualisationFactor > 1.0)
+ {
+ fReducedVisualisationFactor = 1.0;
+ }
+ else if(fReducedVisualisationFactor < 0.20)
+ {
+ fReducedVisualisationFactor = 0.20;
+ }
+
+ if(fReducedVisualisationFactor != 1.0)
+ {
+ fReduceFactor *= fReducedVisualisationFactor;
+ fViewSizeX *= fReducedVisualisationFactor;
+ fViewSizeY *= fReducedVisualisationFactor;
+ }
}
// calculate logic render size in world coordinates for usage in renderer
@@ -234,18 +237,16 @@ namespace drawinglayer
aZBufferProcessor3D.processNonTransparent(getChildren3D());
aZBufferProcessor3D.processTransparent(getChildren3D());
- const BitmapEx aNewBitmap(aZBufferProcessor3D.getBitmapEx());
- const Size aBitmapSizePixel(aNewBitmap.GetSizePixel());
+ const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = aZBufferProcessor3D.getBitmapEx();
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
if(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight())
{
// create transform for the created bitmap in discrete coordinates first.
- // #i97772# Do not forget to apply evtl. render size reduction to scaling
basegfx::B2DHomMatrix aNew2DTransform;
- const double fSizeReductionFactor(1.0 / fReduceFactor);
- aNew2DTransform.set(0, 0, (double)(aBitmapSizePixel.getWidth() - 1) * fSizeReductionFactor);
- aNew2DTransform.set(1, 1, (double)(aBitmapSizePixel.getHeight() - 1) * fSizeReductionFactor);
+ aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
+ aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
@@ -253,7 +254,7 @@ namespace drawinglayer
aNew2DTransform *= rViewInformation.getInverseObjectToViewTransformation();
// create bitmap primitive and add
- const Primitive2DReference xRef(new BitmapPrimitive2D(aNewBitmap, aNew2DTransform));
+ const Primitive2DReference xRef(new BitmapPrimitive2D(maOldRenderedBitmap, aNew2DTransform));
appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRef);
// test: Allow to add an outline in the debugger when tests are needed
@@ -308,6 +309,47 @@ namespace drawinglayer
return aRetval;
}
+ bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
+ {
+ if(!maOldRenderedBitmap.IsEmpty() && !maOldUnitVisiblePart.isEmpty())
+ {
+ basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
+
+ if(maOldUnitVisiblePart.isInside(aRelativePoint))
+ {
+ // calculate coordinates relative to visualized part
+ double fDivisorX(maOldUnitVisiblePart.getWidth());
+ double fDivisorY(maOldUnitVisiblePart.getHeight());
+
+ if(basegfx::fTools::equalZero(fDivisorX))
+ {
+ fDivisorX = 1.0;
+ }
+
+ if(basegfx::fTools::equalZero(fDivisorY))
+ {
+ fDivisorY = 1.0;
+ }
+
+ const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
+ const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
+
+ // combine with real BitmapSizePixel to get bitmap coordinates
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
+ const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
+ const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
+
+ // try to get a statement about transparency in that pixel
+ o_rResult = (0xff != maOldRenderedBitmap.GetTransparency(nX, nY));
+ return true;
+ }
+ }
+
+ return false;
+ }
+
ScenePrimitive2D::ScenePrimitive2D(
const primitive3d::Primitive3DSequence& rxChildren3D,
const attribute::SdrSceneAttribute& rSdrSceneAttribute,
@@ -324,7 +366,8 @@ namespace drawinglayer
mbShadow3DChecked(false),
mfOldDiscreteSizeX(0.0),
mfOldDiscreteSizeY(0.0),
- maOldUnitVisiblePart()
+ maOldUnitVisiblePart(),
+ maOldRenderedBitmap()
{
}
@@ -375,7 +418,7 @@ namespace drawinglayer
{
::osl::MutexGuard aGuard( m_aMutex );
- // get the involved ranges (see helper method calculateDsicreteSizes for details)
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
basegfx::B2DRange aDiscreteRange;
basegfx::B2DRange aUnitVisibleRange;
bool bNeedNewDecomposition(false);
@@ -384,21 +427,22 @@ namespace drawinglayer
if(getBuffered2DDecomposition().hasElements())
{
basegfx::B2DRange aVisibleDiscreteRange;
- calculateDsicreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
bDiscreteSizesAreCalculated = true;
- // display has changed and cannot be reused when resolution did change
- if(!basegfx::fTools::equal(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
- !basegfx::fTools::equal(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
+ // needs to be painted when the new part is not part of the last
+ // decomposition
+ if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
{
bNeedNewDecomposition = true;
}
+ // display has changed and cannot be reused when resolution got bigger. It
+ // can be reused when resolution got smaller, though.
if(!bNeedNewDecomposition)
{
- // needs to be painted when the new part is not part of the last
- // decomposition
- if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
+ if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
+ basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
{
bNeedNewDecomposition = true;
}
@@ -416,7 +460,7 @@ namespace drawinglayer
if(!bDiscreteSizesAreCalculated)
{
basegfx::B2DRange aVisibleDiscreteRange;
- calculateDsicreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
}
// remember last used NewDiscreteSize and NewUnitVisiblePart
diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
index 98bbe2a82cb8..09eb6e3e71f6 100644
--- a/drawinglayer/source/processor2d/hittestprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
@@ -170,102 +170,113 @@ namespace drawinglayer
{
// calculate relative point in unified 2D scene
const basegfx::B2DPoint aLogicHitPosition(getViewInformation2D().getInverseObjectToViewTransformation() * getDiscreteHitPosition());
- basegfx::B2DHomMatrix aInverseSceneTransform(rCandidate.getObjectTransformation());
- aInverseSceneTransform.invert();
- const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * aLogicHitPosition);
- // check if test point is inside scene's unified area at all
- if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0
- && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
- {
- // get 3D view information
- const geometry::ViewInformation3D& rObjectViewInformation3D = rCandidate.getViewInformation3D();
+ // use bitmap check in ScenePrimitive2D
+ bool bTryFastResult(false);
- // create HitPoint Front and Back, transform to object coordinates
- basegfx::B3DHomMatrix aViewToObject(rObjectViewInformation3D.getObjectToView());
- aViewToObject.invert();
- const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
- const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
+ if(rCandidate.tryToCheckLastVisualisationDirectHit(aLogicHitPosition, bTryFastResult))
+ {
+ mbHit = bTryFastResult;
+ }
+ else
+ {
+ basegfx::B2DHomMatrix aInverseSceneTransform(rCandidate.getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * aLogicHitPosition);
- if(!aFront.equal(aBack))
+ // check if test point is inside scene's unified area at all
+ if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0
+ && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
{
- const primitive3d::Primitive3DSequence& rPrimitives = rCandidate.getChildren3D();
+ // get 3D view information
+ const geometry::ViewInformation3D& rObjectViewInformation3D = rCandidate.getViewInformation3D();
+
+ // create HitPoint Front and Back, transform to object coordinates
+ basegfx::B3DHomMatrix aViewToObject(rObjectViewInformation3D.getObjectToView());
+ aViewToObject.invert();
+ const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
+ const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
- if(rPrimitives.hasElements())
+ if(!aFront.equal(aBack))
{
- // make BoundVolume empty and overlapping test for speedup
- const basegfx::B3DRange aObjectRange(
- drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(
- rPrimitives, rObjectViewInformation3D));
+ const primitive3d::Primitive3DSequence& rPrimitives = rCandidate.getChildren3D();
- if(!aObjectRange.isEmpty())
+ if(rPrimitives.hasElements())
{
- const basegfx::B3DRange aFrontBackRange(aFront, aBack);
+ // make BoundVolume empty and overlapping test for speedup
+ const basegfx::B3DRange aObjectRange(
+ drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(
+ rPrimitives, rObjectViewInformation3D));
- if(aObjectRange.overlaps(aFrontBackRange))
+ if(!aObjectRange.isEmpty())
{
- // bound volumes hit, geometric cut tests needed
- drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(
- rObjectViewInformation3D,
- aFront,
- aBack,
- true);
- aCutFindProcessor.process(rPrimitives);
-
- mbHit = (0 != aCutFindProcessor.getCutPoints().size());
+ const basegfx::B3DRange aFrontBackRange(aFront, aBack);
+
+ if(aObjectRange.overlaps(aFrontBackRange))
+ {
+ // bound volumes hit, geometric cut tests needed
+ drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(
+ rObjectViewInformation3D,
+ aFront,
+ aBack,
+ true);
+ aCutFindProcessor.process(rPrimitives);
+
+ mbHit = (0 != aCutFindProcessor.getCutPoints().size());
+ }
}
}
}
}
- }
- // This is needed to check hit with 3D shadows, too. HitTest is without shadow
- // to keep compatible with previous versions. Keeping here as reference
- //
- // if(!getHit())
- // {
- // // if scene has shadow, check hit with shadow, too
- // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rCandidate.getShadow2D(getViewInformation2D()));
- //
- // if(xExtracted2DSceneShadow.hasElements())
- // {
- // // proccess extracted 2D content
- // process(xExtracted2DSceneShadow);
- // }
- // }
-
- if(!getHit())
- {
- // empty 3D scene; Check for border hit
- basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(basegfx::B2DRange(0.0, 0.0, 1.0, 1.0)));
- aOutline.transform(rCandidate.getObjectTransformation());
+ // This is needed to check hit with 3D shadows, too. HitTest is without shadow
+ // to keep compatible with previous versions. Keeping here as reference
+ //
+ // if(!getHit())
+ // {
+ // // if scene has shadow, check hit with shadow, too
+ // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rCandidate.getShadow2D(getViewInformation2D()));
+ //
+ // if(xExtracted2DSceneShadow.hasElements())
+ // {
+ // // proccess extracted 2D content
+ // process(xExtracted2DSceneShadow);
+ // }
+ // }
+
+ if(!getHit())
+ {
+ // empty 3D scene; Check for border hit
+ basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(basegfx::B2DRange(0.0, 0.0, 1.0, 1.0)));
+ aOutline.transform(rCandidate.getObjectTransformation());
- mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
- }
+ mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
+ }
- // This is what the previous version did. Keeping it here for reference
- //
- // // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates
- // // This may be refined later to an own 3D HitTest renderer which processes the 3D
- // // geometry directly
- // const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
- // const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D());
- // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D(getViewInformation2D()));
- //
- // if(xExtracted2DSceneGeometry.hasElements() || xExtracted2DSceneShadow.hasElements())
- // {
- // // proccess extracted 2D content
- // process(xExtracted2DSceneGeometry);
- // process(xExtracted2DSceneShadow);
- // }
- // else
- // {
- // // empty 3D scene; Check for border hit
- // const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
- // basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
- //
- // mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
- // }
+ // This is what the previous version did. Keeping it here for reference
+ //
+ // // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates
+ // // This may be refined later to an own 3D HitTest renderer which processes the 3D
+ // // geometry directly
+ // const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
+ // const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D());
+ // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D(getViewInformation2D()));
+ //
+ // if(xExtracted2DSceneGeometry.hasElements() || xExtracted2DSceneShadow.hasElements())
+ // {
+ // // proccess extracted 2D content
+ // process(xExtracted2DSceneGeometry);
+ // process(xExtracted2DSceneShadow);
+ // }
+ // else
+ // {
+ // // empty 3D scene; Check for border hit
+ // const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
+ // basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
+ //
+ // mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
+ // }
+ }
}
void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)