/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 nAlpha(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 += rPixel.getRed(); nGreen += rPixel.getGreen(); nBlue += rPixel.getBlue(); nAlpha += rPixel.getAlpha(); } nIndex += rRaster.getWidth() - mnAntiAlialize; } nAlpha /= nDivisor; if(nAlpha) { aContent.SetPixel(y, x, Color(ColorAlpha, static_cast(nAlpha), static_cast(nRed / nDivisor), static_cast(nGreen / nDivisor), static_cast(nBlue / nDivisor) )); } else aContent.SetPixel(y, x, Color(ColorAlpha, 0, 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.getAlpha()) { aContent.SetPixel(y, x, Color(ColorAlpha, rPixel.getAlpha(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue())); } else aContent.SetPixel(y, x, Color(ColorAlpha, 0, 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 { // 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.append(maShadowPrimitives); } } // 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 double fMaximumVisibleArea(officecfg::Office::Common::Drawinglayer::Quadratic3DRenderLimit::get()); 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); sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::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 double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength()); double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength()); // generate RasterWidth and RasterHeight for visible part sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1); sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1); if(!rViewInformation.getReducedDisplayQuality() && comphelper::LibreOfficeKit::isActive()) { // for this purpose allow reduced 3D quality and make a compromise // between quality and speed. This is balanced between those two // targets, fine-tuning/experimenting can be done with the values // below. // define some values which allow fine-tuning this feature static const double fMin(80.0); static const double fSqareMin(fMin * fMin); static const double fMax(800.0); static const double fSqareMax(fMax * fMax); static const double fMaxReduction(0.65); // get the square pixels (work on pixel numbers to get same // behaviour independent of width/height relations) const double fSquarePixels(nRasterWidth * nRasterHeight); if (fSquarePixels > fSqareMin) { // only reduce at all when more than fSqareMin pixels needed double fReduction(fMaxReduction); if (fSquarePixels < fSqareMax) { // range between fSqareMin and fSqareMax, calculate a // linear interpolated reduction based on square root fReduction = sqrt(fSquarePixels); // [fMin .. fMax] fReduction = fReduction - fMin; // [0 .. (fMax - fMin)] fReduction = fReduction / (fMax - fMin); // [0 .. 1] fReduction = 1.0 - (fReduction * (1.0 - fMaxReduction)); // [1 .. fMaxReduction] // reduce oversampling for this range if(nOversampleValue > 2) nOversampleValue--; } else { // more than fSqareMax pixels, disable oversampling nOversampleValue = 0; } // adapt needed values to reduction nRasterWidth = basegfx::fround(fReduction * nRasterWidth); nRasterHeight = basegfx::fround(fReduction * nRasterHeight); fFullViewSizeX *= fReduction; fFullViewSizeY *= fReduction; } } 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 mpZBufferProcessor3D; const primitive3d::Primitive3DContainer& mrChildren3D; public: explicit Executor( std::shared_ptr const & rTag, std::unique_ptr 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 aTag = comphelper::ThreadPool::createThreadTaskTag(); for(sal_Int32 a(0); a < nThreadCount; a++) { std::unique_ptr 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 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( 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(std::move(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()) return false; basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation()); aInverseSceneTransform.invert(); const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint); if(!maOldUnitVisiblePart.isInside(aRelativePoint)) return false; // 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 = (0 != maOldRenderedBitmap.GetAlpha(nX, nY)); return true; } ScenePrimitive2D::ScenePrimitive2D( primitive3d::Primitive3DContainer aChildren3D, attribute::SdrSceneAttribute aSdrSceneAttribute, attribute::SdrLightingAttribute aSdrLightingAttribute, basegfx::B2DHomMatrix aObjectTransformation, geometry::ViewInformation3D aViewInformation3D) : BufferedDecompositionPrimitive2D(), mxChildren3D(std::move(aChildren3D)), maSdrSceneAttribute(std::move(aSdrSceneAttribute)), maSdrLightingAttribute(std::move(aSdrLightingAttribute)), maObjectTransformation(std::move(aObjectTransformation)), maViewInformation3D(std::move(aViewInformation3D)), mbShadow3DChecked(false), mfOldDiscreteSizeX(0.0), mfOldDiscreteSizeY(0.0) { // activate callback to flush buffered decomposition content setCallbackSeconds(45); } bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) { const ScenePrimitive2D& rCompare = static_cast(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 { // 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 sal_uInt32 ScenePrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_SCENEPRIMITIVE2D; } } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */