summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/primitive2d/sceneprimitive2d.cxx')
-rw-r--r--drawinglayer/source/primitive2d/sceneprimitive2d.cxx697
1 files changed, 697 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
new file mode 100644
index 000000000..e58c05e40
--- /dev/null
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -0,0 +1,697 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <processor3d/zbufferprocessor3d.hxx>
+#include <processor3d/shadow3dextractor.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <processor3d/geometry2dextractor.hxx>
+#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <basegfx/raster/bzpixelraster.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <comphelper/threadpool.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+ BitmapEx BPixelRasterToBitmapEx(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize)
+ {
+ BitmapEx aRetval;
+ const sal_uInt32 nWidth(mnAntiAlialize ? rRaster.getWidth()/mnAntiAlialize : rRaster.getWidth());
+ const sal_uInt32 nHeight(mnAntiAlialize ? rRaster.getHeight()/mnAntiAlialize : rRaster.getHeight());
+
+ if(nWidth && nHeight)
+ {
+ const Size aDestSize(nWidth, nHeight);
+ vcl::bitmap::RawBitmap aContent(aDestSize, 32);
+
+ if(mnAntiAlialize)
+ {
+ const sal_uInt16 nDivisor(mnAntiAlialize * mnAntiAlialize);
+
+ for(sal_uInt32 y(0); y < nHeight; y++)
+ {
+ for(sal_uInt32 x(0); x < nWidth; x++)
+ {
+ sal_uInt16 nRed(0);
+ sal_uInt16 nGreen(0);
+ sal_uInt16 nBlue(0);
+ sal_uInt16 nOpacity(0);
+ sal_uInt32 nIndex(rRaster.getIndexFromXY(x * mnAntiAlialize, y * mnAntiAlialize));
+
+ for(sal_uInt32 c(0); c < mnAntiAlialize; c++)
+ {
+ for(sal_uInt32 d(0); d < mnAntiAlialize; d++)
+ {
+ const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
+ nRed = nRed + rPixel.getRed();
+ nGreen = nGreen + rPixel.getGreen();
+ nBlue = nBlue + rPixel.getBlue();
+ nOpacity = nOpacity + rPixel.getOpacity();
+ }
+
+ nIndex += rRaster.getWidth() - mnAntiAlialize;
+ }
+
+ nOpacity = nOpacity / nDivisor;
+
+ if(nOpacity)
+ {
+ aContent.SetPixel(y, x, Color(
+ 255 - static_cast<sal_uInt8>(nOpacity),
+ static_cast<sal_uInt8>(nRed / nDivisor),
+ static_cast<sal_uInt8>(nGreen / nDivisor),
+ static_cast<sal_uInt8>(nBlue / nDivisor) ));
+ }
+ else
+ aContent.SetPixel(y, x, Color(255, 0, 0, 0));
+ }
+ }
+ }
+ else
+ {
+ sal_uInt32 nIndex(0);
+
+ for(sal_uInt32 y(0); y < nHeight; y++)
+ {
+ for(sal_uInt32 x(0); x < nWidth; x++)
+ {
+ const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
+
+ if(rPixel.getOpacity())
+ {
+ aContent.SetPixel(y, x, Color(255 - rPixel.getOpacity(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue()));
+ }
+ else
+ aContent.SetPixel(y, x, Color(255, 0, 0, 0));
+ }
+ }
+ }
+
+ aRetval = vcl::bitmap::CreateFromData(std::move(aContent));
+
+ // #i101811# set PrefMapMode and PrefSize at newly created Bitmap
+ aRetval.SetPrefMapMode(MapMode(MapUnit::MapPixel));
+ aRetval.SetPrefSize(Size(nWidth, nHeight));
+ }
+
+ return aRetval;
+ }
+} // end of anonymous namespace
+
+namespace drawinglayer::primitive2d
+{
+ bool ScenePrimitive2D::impGetShadow3D() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // create on demand
+ if(!mbShadow3DChecked && !getChildren3D().empty())
+ {
+ basegfx::B3DVector aLightNormal;
+ const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
+ const basegfx::B3DRange aScene3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
+
+ if(!maSdrLightingAttribute.getLightVector().empty())
+ {
+ // get light normal from first light and normalize
+ aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
+ aLightNormal.normalize();
+ }
+
+ // create shadow extraction processor
+ processor3d::Shadow3DExtractingProcessor aShadowProcessor(
+ getViewInformation3D(),
+ getObjectTransformation(),
+ aLightNormal,
+ fShadowSlant,
+ aScene3DRange);
+
+ // process local primitives
+ aShadowProcessor.process(getChildren3D());
+
+ // fetch result and set checked flag
+ const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
+ const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
+ }
+
+ // return if there are shadow primitives
+ return !maShadowPrimitives.empty();
+ }
+
+ void ScenePrimitive2D::calculateDiscreteSizes(
+ const geometry::ViewInformation2D& rViewInformation,
+ basegfx::B2DRange& rDiscreteRange,
+ basegfx::B2DRange& rVisibleDiscreteRange,
+ basegfx::B2DRange& rUnitVisibleRange) const
+ {
+ // use unit range and transform to discrete coordinates
+ rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
+ rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // clip it against discrete Viewport (if set)
+ rVisibleDiscreteRange = rDiscreteRange;
+
+ if(!rViewInformation.getViewport().isEmpty())
+ {
+ rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
+ }
+
+ if(rVisibleDiscreteRange.isEmpty())
+ {
+ rUnitVisibleRange = rVisibleDiscreteRange;
+ }
+ else
+ {
+ // create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
+ // the relative position of rVisibleDiscreteRange inside rDiscreteRange
+ const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
+ const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
+
+ const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
+ ? 0.0
+ : (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
+ const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
+ ? 0.0
+ : (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
+
+ const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
+ ? 1.0
+ : (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
+ const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
+ ? 1.0
+ : (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
+
+ rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
+ }
+ }
+
+ void ScenePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
+ // there are some or not. Do this at start, the shadow might still be visible even when the scene is not
+ if(impGetShadow3D())
+ {
+ // test visibility
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+ const basegfx::B2DRange aViewRange(
+ rViewInformation.getViewport());
+
+ if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
+ {
+ // add extracted 2d shadows (before 3d scene creations itself)
+ rContainer.insert(rContainer.end(), maShadowPrimitives.begin(), maShadowPrimitives.end());
+ }
+ }
+
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
+ basegfx::B2DRange aDiscreteRange;
+ basegfx::B2DRange aVisibleDiscreteRange;
+ basegfx::B2DRange aUnitVisibleRange;
+
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+
+ if(aVisibleDiscreteRange.isEmpty())
+ return;
+
+ // test if discrete view size (pixel) maybe too big and limit it
+ double fViewSizeX(aVisibleDiscreteRange.getWidth());
+ double fViewSizeY(aVisibleDiscreteRange.getHeight());
+ const double fViewVisibleArea(fViewSizeX * fViewSizeY);
+ const SvtOptionsDrawinglayer aDrawinglayerOpt;
+ const double fMaximumVisibleArea(aDrawinglayerOpt.GetQuadratic3DRenderLimit());
+ double fReduceFactor(1.0);
+
+ if(fViewVisibleArea > fMaximumVisibleArea)
+ {
+ fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
+ fViewSizeX *= fReduceFactor;
+ fViewSizeY *= fReduceFactor;
+ }
+
+ if(rViewInformation.getReducedDisplayQuality())
+ {
+ // when reducing the visualisation is allowed (e.g. an OverlayObject
+ // only needed for dragging), reduce resolution extra
+ // to speed up dragging interactions
+ const double fArea(fViewSizeX * fViewSizeY);
+ if (fArea != 0.0)
+ {
+ 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;
+ }
+ }
+ }
+
+ // determine the oversample value
+ static const sal_uInt16 nDefaultOversampleValue(3);
+ const sal_uInt16 nOversampleValue(aDrawinglayerOpt.IsAntiAliasing() ? nDefaultOversampleValue : 0);
+
+ geometry::ViewInformation3D aViewInformation3D(getViewInformation3D());
+ {
+ // calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
+ // Start with full transformation from object to discrete units
+ basegfx::B2DHomMatrix aObjToUnit(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // bring to unit coordinates by applying inverse DiscreteRange
+ aObjToUnit.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY());
+ if (aDiscreteRange.getWidth() != 0.0 && aDiscreteRange.getHeight() != 0.0)
+ {
+ aObjToUnit.scale(1.0 / aDiscreteRange.getWidth(), 1.0 / aDiscreteRange.getHeight());
+ }
+
+ // calculate transformed user coordinate system
+ const basegfx::B2DPoint aStandardNull(0.0, 0.0);
+ const basegfx::B2DPoint aUnitRangeTopLeft(aObjToUnit * aStandardNull);
+ const basegfx::B2DVector aStandardXAxis(1.0, 0.0);
+ const basegfx::B2DVector aUnitRangeXAxis(aObjToUnit * aStandardXAxis);
+ const basegfx::B2DVector aStandardYAxis(0.0, 1.0);
+ const basegfx::B2DVector aUnitRangeYAxis(aObjToUnit * aStandardYAxis);
+
+ if(!aUnitRangeTopLeft.equal(aStandardNull) || !aUnitRangeXAxis.equal(aStandardXAxis) || !aUnitRangeYAxis.equal(aStandardYAxis))
+ {
+ // build transformation from unit range to user coordinate system; the unit range
+ // X and Y axes are the column vectors, the null point is the offset
+ basegfx::B2DHomMatrix aUnitRangeToUser;
+
+ aUnitRangeToUser.set3x2(
+ aUnitRangeXAxis.getX(), aUnitRangeYAxis.getX(), aUnitRangeTopLeft.getX(),
+ aUnitRangeXAxis.getY(), aUnitRangeYAxis.getY(), aUnitRangeTopLeft.getY());
+
+ // decompose to allow to apply this to the 3D transformation
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aUnitRangeToUser.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
+ // and not yet flipped in Y
+ basegfx::B3DHomMatrix aExtendedProjection(aViewInformation3D.getProjection());
+
+ // bring to unit coordinates, flip Y, leave Z unchanged
+ aExtendedProjection.scale(0.5, -0.5, 1.0);
+ aExtendedProjection.translate(0.5, 0.5, 0.0);
+
+ // apply extra; Y is flipped now, go with positive shear and rotate values
+ aExtendedProjection.scale(aScale.getX(), aScale.getY(), 1.0);
+ aExtendedProjection.shearXZ(fShearX, 0.0);
+ aExtendedProjection.rotate(0.0, 0.0, fRotate);
+ aExtendedProjection.translate(aTranslate.getX(), aTranslate.getY(), 0.0);
+
+ // back to state after projection
+ aExtendedProjection.translate(-0.5, -0.5, 0.0);
+ aExtendedProjection.scale(2.0, -2.0, 1.0);
+
+ aViewInformation3D = geometry::ViewInformation3D(
+ aViewInformation3D.getObjectTransformation(),
+ aViewInformation3D.getOrientation(),
+ aExtendedProjection,
+ aViewInformation3D.getDeviceToView(),
+ aViewInformation3D.getViewTime(),
+ aViewInformation3D.getExtendedInformationSequence());
+ }
+ }
+
+ // calculate logic render size in world coordinates for usage in renderer
+ const basegfx::B2DHomMatrix& aInverseOToV(rViewInformation.getInverseObjectToViewTransformation());
+ const double fLogicX((aInverseOToV * basegfx::B2DVector(aDiscreteRange.getWidth() * fReduceFactor, 0.0)).getLength());
+ const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength());
+
+ // generate ViewSizes
+ const double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength());
+ const double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength());
+
+ // generate RasterWidth and RasterHeight for visible part
+ const sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1);
+ const sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1);
+
+ if(!(nRasterWidth && nRasterHeight))
+ return;
+
+ // create view unit buffer
+ basegfx::BZPixelRaster aBZPixelRaster(
+ nOversampleValue ? nRasterWidth * nOversampleValue : nRasterWidth,
+ nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight);
+
+ // check for parallel execution possibilities
+ static bool bMultithreadAllowed = false; // loplugin:constvars:ignore
+ sal_Int32 nThreadCount(0);
+ comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
+
+ if(bMultithreadAllowed)
+ {
+ nThreadCount = rThreadPool.getWorkerCount();
+
+ if(nThreadCount > 1)
+ {
+ // at least use 10px per processor, so limit number of processors to
+ // target pixel size divided by 10 (which might be zero what is okay)
+ nThreadCount = std::min(nThreadCount, nRasterHeight / 10);
+ }
+ }
+
+ if(nThreadCount > 1)
+ {
+ class Executor : public comphelper::ThreadTask
+ {
+ private:
+ std::unique_ptr<processor3d::ZBufferProcessor3D> mpZBufferProcessor3D;
+ const primitive3d::Primitive3DContainer& mrChildren3D;
+
+ public:
+ explicit Executor(
+ std::shared_ptr<comphelper::ThreadTaskTag> const & rTag,
+ std::unique_ptr<processor3d::ZBufferProcessor3D> pZBufferProcessor3D,
+ const primitive3d::Primitive3DContainer& rChildren3D)
+ : comphelper::ThreadTask(rTag),
+ mpZBufferProcessor3D(std::move(pZBufferProcessor3D)),
+ mrChildren3D(rChildren3D)
+ {
+ }
+
+ virtual void doWork() override
+ {
+ mpZBufferProcessor3D->process(mrChildren3D);
+ mpZBufferProcessor3D->finish();
+ mpZBufferProcessor3D.reset();
+ }
+ };
+
+ const sal_uInt32 nLinesPerThread(aBZPixelRaster.getHeight() / nThreadCount);
+ std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ for(sal_Int32 a(0); a < nThreadCount; a++)
+ {
+ std::unique_ptr<processor3d::ZBufferProcessor3D> pNewZBufferProcessor3D(new processor3d::ZBufferProcessor3D(
+ aViewInformation3D,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ aUnitVisibleRange,
+ nOversampleValue,
+ fFullViewSizeX,
+ fFullViewSizeY,
+ aBZPixelRaster,
+ nLinesPerThread * a,
+ a + 1 == nThreadCount ? aBZPixelRaster.getHeight() : nLinesPerThread * (a + 1)));
+ std::unique_ptr<Executor> pExecutor(new Executor(aTag, std::move(pNewZBufferProcessor3D), getChildren3D()));
+ rThreadPool.pushTask(std::move(pExecutor));
+ }
+
+ rThreadPool.waitUntilDone(aTag);
+ }
+ else
+ {
+ // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
+ processor3d::ZBufferProcessor3D aZBufferProcessor3D(
+ aViewInformation3D,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ aUnitVisibleRange,
+ nOversampleValue,
+ fFullViewSizeX,
+ fFullViewSizeY,
+ aBZPixelRaster,
+ 0,
+ aBZPixelRaster.getHeight());
+
+ aZBufferProcessor3D.process(getChildren3D());
+ aZBufferProcessor3D.finish();
+ }
+
+ const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmapEx(aBZPixelRaster, nOversampleValue);
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
+
+ if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight()))
+ return;
+
+ // create transform for the created bitmap in discrete coordinates first.
+ basegfx::B2DHomMatrix aNew2DTransform;
+
+ aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
+ aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
+ aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
+ aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
+
+ // transform back to world coordinates for usage in primitive creation
+ aNew2DTransform *= aInverseOToV;
+
+ // create bitmap primitive and add
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(maOldRenderedBitmap),
+ aNew2DTransform));
+
+ // test: Allow to add an outline in the debugger when tests are needed
+ static bool bAddOutlineToCreated3DSceneRepresentation(false); // loplugin:constvars:ignore
+
+ if(bAddOutlineToCreated3DSceneRepresentation)
+ {
+ basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
+ aOutline.transform(aNew2DTransform);
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0)));
+ }
+ }
+
+ Primitive2DContainer ScenePrimitive2D::getGeometry2D() const
+ {
+ Primitive2DContainer aRetval;
+
+ // create 2D projected geometry from 3D geometry
+ if(!getChildren3D().empty())
+ {
+ // create 2D geometry extraction processor
+ processor3d::Geometry2DExtractingProcessor aGeometryProcessor(
+ getViewInformation3D(),
+ getObjectTransformation());
+
+ // process local primitives
+ aGeometryProcessor.process(getChildren3D());
+
+ // fetch result
+ aRetval = aGeometryProcessor.getPrimitive2DSequence();
+ }
+
+ return aRetval;
+ }
+
+ Primitive2DContainer ScenePrimitive2D::getShadow2D() const
+ {
+ Primitive2DContainer aRetval;
+
+ // create 2D shadows from contained 3D primitives
+ if(impGetShadow3D())
+ {
+ // add extracted 2d shadows (before 3d scene creations itself)
+ aRetval = maShadowPrimitives;
+ }
+
+ 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::Primitive3DContainer& rxChildren3D,
+ const attribute::SdrSceneAttribute& rSdrSceneAttribute,
+ const attribute::SdrLightingAttribute& rSdrLightingAttribute,
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ const geometry::ViewInformation3D& rViewInformation3D)
+ : BufferedDecompositionPrimitive2D(),
+ mxChildren3D(rxChildren3D),
+ maSdrSceneAttribute(rSdrSceneAttribute),
+ maSdrLightingAttribute(rSdrLightingAttribute),
+ maObjectTransformation(rObjectTransformation),
+ maViewInformation3D(rViewInformation3D),
+ maShadowPrimitives(),
+ mbShadow3DChecked(false),
+ mfOldDiscreteSizeX(0.0),
+ mfOldDiscreteSizeY(0.0),
+ maOldUnitVisiblePart(),
+ maOldRenderedBitmap()
+ {
+ }
+
+ bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const ScenePrimitive2D& rCompare = static_cast<const ScenePrimitive2D&>(rPrimitive);
+
+ return (getChildren3D() == rCompare.getChildren3D()
+ && getSdrSceneAttribute() == rCompare.getSdrSceneAttribute()
+ && getSdrLightingAttribute() == rCompare.getSdrLightingAttribute()
+ && getObjectTransformation() == rCompare.getObjectTransformation()
+ && getViewInformation3D() == rCompare.getViewInformation3D());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // transform unit range to discrete coordinate range
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // force to discrete expanded bounds (it grows, so expanding works perfectly well)
+ aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
+ aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
+
+ // transform back from discrete (view) to world coordinates
+ aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
+
+ // expand by evtl. existing shadow primitives
+ if(impGetShadow3D())
+ {
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+
+ if(!aShadow2DRange.isEmpty())
+ {
+ aRetval.expand(aShadow2DRange);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
+ basegfx::B2DRange aDiscreteRange;
+ basegfx::B2DRange aUnitVisibleRange;
+ bool bNeedNewDecomposition(false);
+ bool bDiscreteSizesAreCalculated(false);
+
+ if(!getBuffered2DDecomposition().empty())
+ {
+ basegfx::B2DRange aVisibleDiscreteRange;
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ bDiscreteSizesAreCalculated = true;
+
+ // 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)
+ {
+ if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
+ basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
+ {
+ bNeedNewDecomposition = true;
+ }
+ }
+ }
+
+ if(bNeedNewDecomposition)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ if(!bDiscreteSizesAreCalculated)
+ {
+ basegfx::B2DRange aVisibleDiscreteRange;
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ }
+
+ // remember last used NewDiscreteSize and NewUnitVisiblePart
+ ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
+ pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
+ pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
+ pThat->maOldUnitVisiblePart = aUnitVisibleRange;
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ ImplPrimitive2DIDBlock(ScenePrimitive2D, PRIMITIVE2D_ID_SCENEPRIMITIVE2D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */