1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
21 #include <basegfx/polygon/b2dpolygontools.hxx>
22 #include <basegfx/polygon/b2dpolygon.hxx>
23 #include <basegfx/matrix/b2dhommatrix.hxx>
24 #include <drawinglayer/attribute/sdrlightattribute3d.hxx>
25 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
27 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
28 #include <processor3d/zbufferprocessor3d.hxx>
29 #include <processor3d/shadow3dextractor.hxx>
30 #include <drawinglayer/geometry/viewinformation2d.hxx>
31 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
32 #include <svtools/optionsdrawinglayer.hxx>
33 #include <processor3d/geometry2dextractor.hxx>
34 #include <basegfx/raster/bzpixelraster.hxx>
36 #include <vcl/BitmapTools.hxx>
37 #include <comphelper/threadpool.hxx>
38 #include <comphelper/lok.hxx>
39 #include <toolkit/helper/vclunohelper.hxx>
40 #include <officecfg/Office/Common.hxx>
42 using namespace com::sun::star
;
46 BitmapEx
BPixelRasterToBitmapEx(const basegfx::BZPixelRaster
& rRaster
, sal_uInt16 mnAntiAlialize
)
49 const sal_uInt32
nWidth(mnAntiAlialize
? rRaster
.getWidth()/mnAntiAlialize
: rRaster
.getWidth());
50 const sal_uInt32
nHeight(mnAntiAlialize
? rRaster
.getHeight()/mnAntiAlialize
: rRaster
.getHeight());
54 const Size
aDestSize(nWidth
, nHeight
);
55 vcl::bitmap::RawBitmap
aContent(aDestSize
, 32);
59 const sal_uInt16
nDivisor(mnAntiAlialize
* mnAntiAlialize
);
61 for(sal_uInt32
y(0); y
< nHeight
; y
++)
63 for(sal_uInt32
x(0); x
< nWidth
; x
++)
69 sal_uInt32
nIndex(rRaster
.getIndexFromXY(x
* mnAntiAlialize
, y
* mnAntiAlialize
));
71 for(sal_uInt32
c(0); c
< mnAntiAlialize
; c
++)
73 for(sal_uInt32
d(0); d
< mnAntiAlialize
; d
++)
75 const basegfx::BPixel
& rPixel(rRaster
.getBPixel(nIndex
++));
76 nRed
+= rPixel
.getRed();
77 nGreen
+= rPixel
.getGreen();
78 nBlue
+= rPixel
.getBlue();
79 nAlpha
+= rPixel
.getAlpha();
82 nIndex
+= rRaster
.getWidth() - mnAntiAlialize
;
89 aContent
.SetPixel(y
, x
, Color(ColorAlpha
,
90 static_cast<sal_uInt8
>(nAlpha
),
91 static_cast<sal_uInt8
>(nRed
/ nDivisor
),
92 static_cast<sal_uInt8
>(nGreen
/ nDivisor
),
93 static_cast<sal_uInt8
>(nBlue
/ nDivisor
) ));
96 aContent
.SetPixel(y
, x
, Color(ColorAlpha
, 0, 0, 0, 0));
102 sal_uInt32
nIndex(0);
104 for(sal_uInt32
y(0); y
< nHeight
; y
++)
106 for(sal_uInt32
x(0); x
< nWidth
; x
++)
108 const basegfx::BPixel
& rPixel(rRaster
.getBPixel(nIndex
++));
110 if(rPixel
.getAlpha())
112 aContent
.SetPixel(y
, x
, Color(ColorAlpha
, rPixel
.getAlpha(), rPixel
.getRed(), rPixel
.getGreen(), rPixel
.getBlue()));
115 aContent
.SetPixel(y
, x
, Color(ColorAlpha
, 0, 0, 0, 0));
120 aRetval
= vcl::bitmap::CreateFromData(std::move(aContent
));
122 // #i101811# set PrefMapMode and PrefSize at newly created Bitmap
123 aRetval
.SetPrefMapMode(MapMode(MapUnit::MapPixel
));
124 aRetval
.SetPrefSize(Size(nWidth
, nHeight
));
129 } // end of anonymous namespace
131 namespace drawinglayer::primitive2d
133 bool ScenePrimitive2D::impGetShadow3D() const
136 if(!mbShadow3DChecked
&& !getChildren3D().empty())
138 basegfx::B3DVector aLightNormal
;
139 const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
140 const basegfx::B3DRange
aScene3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
142 if(!maSdrLightingAttribute
.getLightVector().empty())
144 // get light normal from first light and normalize
145 aLightNormal
= maSdrLightingAttribute
.getLightVector()[0].getDirection();
146 aLightNormal
.normalize();
149 // create shadow extraction processor
150 processor3d::Shadow3DExtractingProcessor
aShadowProcessor(
151 getViewInformation3D(),
152 getObjectTransformation(),
157 // process local primitives
158 aShadowProcessor
.process(getChildren3D());
160 // fetch result and set checked flag
161 const_cast< ScenePrimitive2D
* >(this)->maShadowPrimitives
= aShadowProcessor
.getPrimitive2DSequence();
162 const_cast< ScenePrimitive2D
* >(this)->mbShadow3DChecked
= true;
165 // return if there are shadow primitives
166 return !maShadowPrimitives
.empty();
169 void ScenePrimitive2D::calculateDiscreteSizes(
170 const geometry::ViewInformation2D
& rViewInformation
,
171 basegfx::B2DRange
& rDiscreteRange
,
172 basegfx::B2DRange
& rVisibleDiscreteRange
,
173 basegfx::B2DRange
& rUnitVisibleRange
) const
175 // use unit range and transform to discrete coordinates
176 rDiscreteRange
= basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
177 rDiscreteRange
.transform(rViewInformation
.getObjectToViewTransformation() * getObjectTransformation());
179 // clip it against discrete Viewport (if set)
180 rVisibleDiscreteRange
= rDiscreteRange
;
182 if(!rViewInformation
.getViewport().isEmpty())
184 rVisibleDiscreteRange
.intersect(rViewInformation
.getDiscreteViewport());
187 if(rVisibleDiscreteRange
.isEmpty())
189 rUnitVisibleRange
= rVisibleDiscreteRange
;
193 // create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
194 // the relative position of rVisibleDiscreteRange inside rDiscreteRange
195 const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange
.getWidth()) ? 1.0 : 1.0 / rDiscreteRange
.getWidth());
196 const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange
.getHeight()) ? 1.0 : 1.0 / rDiscreteRange
.getHeight());
198 const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange
.getMinX(), rDiscreteRange
.getMinX())
200 : (rVisibleDiscreteRange
.getMinX() - rDiscreteRange
.getMinX()) * fDiscreteScaleFactorX
);
201 const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange
.getMinY(), rDiscreteRange
.getMinY())
203 : (rVisibleDiscreteRange
.getMinY() - rDiscreteRange
.getMinY()) * fDiscreteScaleFactorY
);
205 const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange
.getMaxX(), rDiscreteRange
.getMaxX())
207 : (rVisibleDiscreteRange
.getMaxX() - rDiscreteRange
.getMinX()) * fDiscreteScaleFactorX
);
208 const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange
.getMaxY(), rDiscreteRange
.getMaxY())
210 : (rVisibleDiscreteRange
.getMaxY() - rDiscreteRange
.getMinY()) * fDiscreteScaleFactorY
);
212 rUnitVisibleRange
= basegfx::B2DRange(fMinX
, fMinY
, fMaxX
, fMaxY
);
216 Primitive2DReference
ScenePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& rViewInformation
) const
218 Primitive2DContainer aContainer
;
219 // create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
220 // there are some or not. Do this at start, the shadow might still be visible even when the scene is not
224 const basegfx::B2DRange
aShadow2DRange(maShadowPrimitives
.getB2DRange(rViewInformation
));
225 const basegfx::B2DRange
aViewRange(
226 rViewInformation
.getViewport());
228 if(aViewRange
.isEmpty() || aShadow2DRange
.overlaps(aViewRange
))
230 // add extracted 2d shadows (before 3d scene creations itself)
231 aContainer
.append(maShadowPrimitives
);
235 // get the involved ranges (see helper method calculateDiscreteSizes for details)
236 basegfx::B2DRange aDiscreteRange
;
237 basegfx::B2DRange aVisibleDiscreteRange
;
238 basegfx::B2DRange aUnitVisibleRange
;
240 calculateDiscreteSizes(rViewInformation
, aDiscreteRange
, aVisibleDiscreteRange
, aUnitVisibleRange
);
242 if(aVisibleDiscreteRange
.isEmpty())
243 return new GroupPrimitive2D(std::move(aContainer
));
245 // test if discrete view size (pixel) maybe too big and limit it
246 double fViewSizeX(aVisibleDiscreteRange
.getWidth());
247 double fViewSizeY(aVisibleDiscreteRange
.getHeight());
248 const double fViewVisibleArea(fViewSizeX
* fViewSizeY
);
249 const double fMaximumVisibleArea(officecfg::Office::Common::Drawinglayer::Quadratic3DRenderLimit::get());
250 double fReduceFactor(1.0);
252 if(fViewVisibleArea
> fMaximumVisibleArea
)
254 fReduceFactor
= sqrt(fMaximumVisibleArea
/ fViewVisibleArea
);
255 fViewSizeX
*= fReduceFactor
;
256 fViewSizeY
*= fReduceFactor
;
259 if(rViewInformation
.getReducedDisplayQuality())
261 // when reducing the visualisation is allowed (e.g. an OverlayObject
262 // only needed for dragging), reduce resolution extra
263 // to speed up dragging interactions
264 const double fArea(fViewSizeX
* fViewSizeY
);
267 double fReducedVisualisationFactor(1.0 / (sqrt(fArea
) * (1.0 / 170.0)));
269 if(fReducedVisualisationFactor
> 1.0)
271 fReducedVisualisationFactor
= 1.0;
273 else if(fReducedVisualisationFactor
< 0.20)
275 fReducedVisualisationFactor
= 0.20;
278 if(fReducedVisualisationFactor
!= 1.0)
280 fReduceFactor
*= fReducedVisualisationFactor
;
285 // determine the oversample value
286 static const sal_uInt16
nDefaultOversampleValue(3);
287 sal_uInt16
nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue
: 0);
289 geometry::ViewInformation3D
aViewInformation3D(getViewInformation3D());
291 // calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
292 // Start with full transformation from object to discrete units
293 basegfx::B2DHomMatrix
aObjToUnit(rViewInformation
.getObjectToViewTransformation() * getObjectTransformation());
295 // bring to unit coordinates by applying inverse DiscreteRange
296 aObjToUnit
.translate(-aDiscreteRange
.getMinX(), -aDiscreteRange
.getMinY());
297 if (aDiscreteRange
.getWidth() != 0.0 && aDiscreteRange
.getHeight() != 0.0)
299 aObjToUnit
.scale(1.0 / aDiscreteRange
.getWidth(), 1.0 / aDiscreteRange
.getHeight());
302 // calculate transformed user coordinate system
303 const basegfx::B2DPoint
aStandardNull(0.0, 0.0);
304 const basegfx::B2DPoint
aUnitRangeTopLeft(aObjToUnit
* aStandardNull
);
305 const basegfx::B2DVector
aStandardXAxis(1.0, 0.0);
306 const basegfx::B2DVector
aUnitRangeXAxis(aObjToUnit
* aStandardXAxis
);
307 const basegfx::B2DVector
aStandardYAxis(0.0, 1.0);
308 const basegfx::B2DVector
aUnitRangeYAxis(aObjToUnit
* aStandardYAxis
);
310 if(!aUnitRangeTopLeft
.equal(aStandardNull
) || !aUnitRangeXAxis
.equal(aStandardXAxis
) || !aUnitRangeYAxis
.equal(aStandardYAxis
))
312 // build transformation from unit range to user coordinate system; the unit range
313 // X and Y axes are the column vectors, the null point is the offset
314 basegfx::B2DHomMatrix aUnitRangeToUser
;
316 aUnitRangeToUser
.set3x2(
317 aUnitRangeXAxis
.getX(), aUnitRangeYAxis
.getX(), aUnitRangeTopLeft
.getX(),
318 aUnitRangeXAxis
.getY(), aUnitRangeYAxis
.getY(), aUnitRangeTopLeft
.getY());
320 // decompose to allow to apply this to the 3D transformation
321 basegfx::B2DVector aScale
, aTranslate
;
322 double fRotate
, fShearX
;
323 aUnitRangeToUser
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
325 // apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
326 // and not yet flipped in Y
327 basegfx::B3DHomMatrix
aExtendedProjection(aViewInformation3D
.getProjection());
329 // bring to unit coordinates, flip Y, leave Z unchanged
330 aExtendedProjection
.scale(0.5, -0.5, 1.0);
331 aExtendedProjection
.translate(0.5, 0.5, 0.0);
333 // apply extra; Y is flipped now, go with positive shear and rotate values
334 aExtendedProjection
.scale(aScale
.getX(), aScale
.getY(), 1.0);
335 aExtendedProjection
.shearXZ(fShearX
, 0.0);
336 aExtendedProjection
.rotate(0.0, 0.0, fRotate
);
337 aExtendedProjection
.translate(aTranslate
.getX(), aTranslate
.getY(), 0.0);
339 // back to state after projection
340 aExtendedProjection
.translate(-0.5, -0.5, 0.0);
341 aExtendedProjection
.scale(2.0, -2.0, 1.0);
343 aViewInformation3D
= geometry::ViewInformation3D(
344 aViewInformation3D
.getObjectTransformation(),
345 aViewInformation3D
.getOrientation(),
347 aViewInformation3D
.getDeviceToView(),
348 aViewInformation3D
.getViewTime(),
349 aViewInformation3D
.getExtendedInformationSequence());
353 // calculate logic render size in world coordinates for usage in renderer
354 const basegfx::B2DHomMatrix
& aInverseOToV(rViewInformation
.getInverseObjectToViewTransformation());
355 const double fLogicX((aInverseOToV
* basegfx::B2DVector(aDiscreteRange
.getWidth() * fReduceFactor
, 0.0)).getLength());
356 const double fLogicY((aInverseOToV
* basegfx::B2DVector(0.0, aDiscreteRange
.getHeight() * fReduceFactor
)).getLength());
358 // generate ViewSizes
359 double fFullViewSizeX((rViewInformation
.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX
, 0.0)).getLength());
360 double fFullViewSizeY((rViewInformation
.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY
)).getLength());
362 // generate RasterWidth and RasterHeight for visible part
363 sal_Int32
nRasterWidth(basegfx::fround(fFullViewSizeX
* aUnitVisibleRange
.getWidth()) + 1);
364 sal_Int32
nRasterHeight(basegfx::fround(fFullViewSizeY
* aUnitVisibleRange
.getHeight()) + 1);
366 if(!rViewInformation
.getReducedDisplayQuality() && comphelper::LibreOfficeKit::isActive())
368 // for this purpose allow reduced 3D quality and make a compromise
369 // between quality and speed. This is balanced between those two
370 // targets, fine-tuning/experimenting can be done with the values
373 // define some values which allow fine-tuning this feature
374 static const double fMin(80.0);
375 static const double fSqareMin(fMin
* fMin
);
376 static const double fMax(800.0);
377 static const double fSqareMax(fMax
* fMax
);
378 static const double fMaxReduction(0.65);
380 // get the square pixels (work on pixel numbers to get same
381 // behaviour independent of width/height relations)
382 const double fSquarePixels(nRasterWidth
* nRasterHeight
);
384 if (fSquarePixels
> fSqareMin
)
386 // only reduce at all when more than fSqareMin pixels needed
387 double fReduction(fMaxReduction
);
389 if (fSquarePixels
< fSqareMax
)
391 // range between fSqareMin and fSqareMax, calculate a
392 // linear interpolated reduction based on square root
393 fReduction
= sqrt(fSquarePixels
); // [fMin .. fMax]
394 fReduction
= fReduction
- fMin
; // [0 .. (fMax - fMin)]
395 fReduction
= fReduction
/ (fMax
- fMin
); // [0 .. 1]
396 fReduction
= 1.0 - (fReduction
* (1.0 - fMaxReduction
)); // [1 .. fMaxReduction]
398 // reduce oversampling for this range
399 if(nOversampleValue
> 2)
404 // more than fSqareMax pixels, disable oversampling
405 nOversampleValue
= 0;
408 // adapt needed values to reduction
409 nRasterWidth
= basegfx::fround(fReduction
* nRasterWidth
);
410 nRasterHeight
= basegfx::fround(fReduction
* nRasterHeight
);
411 fFullViewSizeX
*= fReduction
;
412 fFullViewSizeY
*= fReduction
;
416 if(!(nRasterWidth
&& nRasterHeight
))
417 return new GroupPrimitive2D(std::move(aContainer
));
419 // create view unit buffer
420 basegfx::BZPixelRaster
aBZPixelRaster(
421 nOversampleValue
? nRasterWidth
* nOversampleValue
: nRasterWidth
,
422 nOversampleValue
? nRasterHeight
* nOversampleValue
: nRasterHeight
);
424 // check for parallel execution possibilities
425 static bool bMultithreadAllowed
= true; // loplugin:constvars:ignore
426 sal_Int32
nThreadCount(0);
427 comphelper::ThreadPool
& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
429 if(bMultithreadAllowed
)
431 nThreadCount
= rThreadPool
.getWorkerCount();
435 // at least use 10px per processor, so limit number of processors to
436 // target pixel size divided by 10 (which might be zero what is okay)
437 nThreadCount
= std::min(nThreadCount
, nRasterHeight
/ 10);
443 class Executor
: public comphelper::ThreadTask
446 std::unique_ptr
<processor3d::ZBufferProcessor3D
> mpZBufferProcessor3D
;
447 const primitive3d::Primitive3DContainer
& mrChildren3D
;
451 std::shared_ptr
<comphelper::ThreadTaskTag
> const & rTag
,
452 std::unique_ptr
<processor3d::ZBufferProcessor3D
> pZBufferProcessor3D
,
453 const primitive3d::Primitive3DContainer
& rChildren3D
)
454 : comphelper::ThreadTask(rTag
),
455 mpZBufferProcessor3D(std::move(pZBufferProcessor3D
)),
456 mrChildren3D(rChildren3D
)
460 virtual void doWork() override
462 mpZBufferProcessor3D
->process(mrChildren3D
);
463 mpZBufferProcessor3D
->finish();
464 mpZBufferProcessor3D
.reset();
468 const sal_uInt32
nLinesPerThread(aBZPixelRaster
.getHeight() / nThreadCount
);
469 std::shared_ptr
<comphelper::ThreadTaskTag
> aTag
= comphelper::ThreadPool::createThreadTaskTag();
471 for(sal_Int32
a(0); a
< nThreadCount
; a
++)
473 std::unique_ptr
<processor3d::ZBufferProcessor3D
> pNewZBufferProcessor3D(new processor3d::ZBufferProcessor3D(
475 getSdrSceneAttribute(),
476 getSdrLightingAttribute(),
483 a
+ 1 == nThreadCount
? aBZPixelRaster
.getHeight() : nLinesPerThread
* (a
+ 1)));
484 std::unique_ptr
<Executor
> pExecutor(new Executor(aTag
, std::move(pNewZBufferProcessor3D
), getChildren3D()));
485 rThreadPool
.pushTask(std::move(pExecutor
));
488 rThreadPool
.waitUntilDone(aTag
);
492 // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
493 processor3d::ZBufferProcessor3D
aZBufferProcessor3D(
495 getSdrSceneAttribute(),
496 getSdrLightingAttribute(),
503 aBZPixelRaster
.getHeight());
505 aZBufferProcessor3D
.process(getChildren3D());
506 aZBufferProcessor3D
.finish();
509 const_cast< ScenePrimitive2D
* >(this)->maOldRenderedBitmap
= BPixelRasterToBitmapEx(aBZPixelRaster
, nOversampleValue
);
510 const Size
aBitmapSizePixel(maOldRenderedBitmap
.GetSizePixel());
512 if(!(aBitmapSizePixel
.getWidth() && aBitmapSizePixel
.getHeight()))
513 return new GroupPrimitive2D(std::move(aContainer
));
515 // create transform for the created bitmap in discrete coordinates first.
516 basegfx::B2DHomMatrix aNew2DTransform
;
518 aNew2DTransform
.set(0, 0, aVisibleDiscreteRange
.getWidth());
519 aNew2DTransform
.set(1, 1, aVisibleDiscreteRange
.getHeight());
520 aNew2DTransform
.set(0, 2, aVisibleDiscreteRange
.getMinX());
521 aNew2DTransform
.set(1, 2, aVisibleDiscreteRange
.getMinY());
523 // transform back to world coordinates for usage in primitive creation
524 aNew2DTransform
*= aInverseOToV
;
526 // create bitmap primitive and add
527 aContainer
.push_back(
528 new BitmapPrimitive2D(
532 // test: Allow to add an outline in the debugger when tests are needed
533 static bool bAddOutlineToCreated3DSceneRepresentation(false); // loplugin:constvars:ignore
535 if(bAddOutlineToCreated3DSceneRepresentation
)
537 basegfx::B2DPolygon
aOutline(basegfx::utils::createUnitPolygon());
538 aOutline
.transform(aNew2DTransform
);
539 aContainer
.push_back(new PolygonHairlinePrimitive2D(std::move(aOutline
), basegfx::BColor(1.0, 0.0, 0.0)));
541 return new GroupPrimitive2D(std::move(aContainer
));
544 Primitive2DContainer
ScenePrimitive2D::getGeometry2D() const
546 Primitive2DContainer aRetval
;
548 // create 2D projected geometry from 3D geometry
549 if(!getChildren3D().empty())
551 // create 2D geometry extraction processor
552 processor3d::Geometry2DExtractingProcessor
aGeometryProcessor(
553 getViewInformation3D(),
554 getObjectTransformation());
556 // process local primitives
557 aGeometryProcessor
.process(getChildren3D());
560 aRetval
= aGeometryProcessor
.getPrimitive2DSequence();
566 Primitive2DContainer
ScenePrimitive2D::getShadow2D() const
568 Primitive2DContainer aRetval
;
570 // create 2D shadows from contained 3D primitives
573 // add extracted 2d shadows (before 3d scene creations itself)
574 aRetval
= maShadowPrimitives
;
580 bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint
& rLogicHitPoint
, bool& o_rResult
) const
582 if(maOldRenderedBitmap
.IsEmpty() || maOldUnitVisiblePart
.isEmpty())
585 basegfx::B2DHomMatrix
aInverseSceneTransform(getObjectTransformation());
586 aInverseSceneTransform
.invert();
587 const basegfx::B2DPoint
aRelativePoint(aInverseSceneTransform
* rLogicHitPoint
);
589 if(!maOldUnitVisiblePart
.isInside(aRelativePoint
))
592 // calculate coordinates relative to visualized part
593 double fDivisorX(maOldUnitVisiblePart
.getWidth());
594 double fDivisorY(maOldUnitVisiblePart
.getHeight());
596 if(basegfx::fTools::equalZero(fDivisorX
))
601 if(basegfx::fTools::equalZero(fDivisorY
))
606 const double fRelativeX((aRelativePoint
.getX() - maOldUnitVisiblePart
.getMinX()) / fDivisorX
);
607 const double fRelativeY((aRelativePoint
.getY() - maOldUnitVisiblePart
.getMinY()) / fDivisorY
);
609 // combine with real BitmapSizePixel to get bitmap coordinates
610 const Size
aBitmapSizePixel(maOldRenderedBitmap
.GetSizePixel());
611 const sal_Int32
nX(basegfx::fround(fRelativeX
* aBitmapSizePixel
.Width()));
612 const sal_Int32
nY(basegfx::fround(fRelativeY
* aBitmapSizePixel
.Height()));
614 // try to get a statement about transparency in that pixel
615 o_rResult
= (0 != maOldRenderedBitmap
.GetAlpha(nX
, nY
));
619 ScenePrimitive2D::ScenePrimitive2D(
620 primitive3d::Primitive3DContainer aChildren3D
,
621 attribute::SdrSceneAttribute aSdrSceneAttribute
,
622 attribute::SdrLightingAttribute aSdrLightingAttribute
,
623 basegfx::B2DHomMatrix aObjectTransformation
,
624 geometry::ViewInformation3D aViewInformation3D
)
625 : BufferedDecompositionPrimitive2D(),
626 mxChildren3D(std::move(aChildren3D
)),
627 maSdrSceneAttribute(std::move(aSdrSceneAttribute
)),
628 maSdrLightingAttribute(std::move(aSdrLightingAttribute
)),
629 maObjectTransformation(std::move(aObjectTransformation
)),
630 maViewInformation3D(std::move(aViewInformation3D
)),
631 mbShadow3DChecked(false),
632 mfOldDiscreteSizeX(0.0),
633 mfOldDiscreteSizeY(0.0)
635 // activate callback to flush buffered decomposition content
636 setCallbackSeconds(45);
639 bool ScenePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
641 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
643 const ScenePrimitive2D
& rCompare
= static_cast<const ScenePrimitive2D
&>(rPrimitive
);
645 return (getChildren3D() == rCompare
.getChildren3D()
646 && getSdrSceneAttribute() == rCompare
.getSdrSceneAttribute()
647 && getSdrLightingAttribute() == rCompare
.getSdrLightingAttribute()
648 && getObjectTransformation() == rCompare
.getObjectTransformation()
649 && getViewInformation3D() == rCompare
.getViewInformation3D());
655 basegfx::B2DRange
ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
657 // transform unit range to discrete coordinate range
658 basegfx::B2DRange
aRetval(0.0, 0.0, 1.0, 1.0);
659 aRetval
.transform(rViewInformation
.getObjectToViewTransformation() * getObjectTransformation());
661 // force to discrete expanded bounds (it grows, so expanding works perfectly well)
662 aRetval
.expand(basegfx::B2DTuple(floor(aRetval
.getMinX()), floor(aRetval
.getMinY())));
663 aRetval
.expand(basegfx::B2DTuple(ceil(aRetval
.getMaxX()), ceil(aRetval
.getMaxY())));
665 // transform back from discrete (view) to world coordinates
666 aRetval
.transform(rViewInformation
.getInverseObjectToViewTransformation());
668 // expand by evtl. existing shadow primitives
671 const basegfx::B2DRange
aShadow2DRange(maShadowPrimitives
.getB2DRange(rViewInformation
));
673 if(!aShadow2DRange
.isEmpty())
675 aRetval
.expand(aShadow2DRange
);
682 void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor
& rVisitor
, const geometry::ViewInformation2D
& rViewInformation
) const
684 // get the involved ranges (see helper method calculateDiscreteSizes for details)
685 basegfx::B2DRange aDiscreteRange
;
686 basegfx::B2DRange aUnitVisibleRange
;
687 bool bNeedNewDecomposition(false);
688 bool bDiscreteSizesAreCalculated(false);
690 if(getBuffered2DDecomposition())
692 basegfx::B2DRange aVisibleDiscreteRange
;
693 calculateDiscreteSizes(rViewInformation
, aDiscreteRange
, aVisibleDiscreteRange
, aUnitVisibleRange
);
694 bDiscreteSizesAreCalculated
= true;
696 // needs to be painted when the new part is not part of the last
698 if(!maOldUnitVisiblePart
.isInside(aUnitVisibleRange
))
700 bNeedNewDecomposition
= true;
703 // display has changed and cannot be reused when resolution got bigger. It
704 // can be reused when resolution got smaller, though.
705 if(!bNeedNewDecomposition
)
707 if(basegfx::fTools::more(aDiscreteRange
.getWidth(), mfOldDiscreteSizeX
) ||
708 basegfx::fTools::more(aDiscreteRange
.getHeight(), mfOldDiscreteSizeY
))
710 bNeedNewDecomposition
= true;
715 if(bNeedNewDecomposition
)
717 // conditions of last local decomposition have changed, delete
718 const_cast< ScenePrimitive2D
* >(this)->setBuffered2DDecomposition(nullptr);
721 if(!getBuffered2DDecomposition())
723 if(!bDiscreteSizesAreCalculated
)
725 basegfx::B2DRange aVisibleDiscreteRange
;
726 calculateDiscreteSizes(rViewInformation
, aDiscreteRange
, aVisibleDiscreteRange
, aUnitVisibleRange
);
729 // remember last used NewDiscreteSize and NewUnitVisiblePart
730 ScenePrimitive2D
* pThat
= const_cast< ScenePrimitive2D
* >(this);
731 pThat
->mfOldDiscreteSizeX
= aDiscreteRange
.getWidth();
732 pThat
->mfOldDiscreteSizeY
= aDiscreteRange
.getHeight();
733 pThat
->maOldUnitVisiblePart
= aUnitVisibleRange
;
736 // use parent implementation
737 BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor
, rViewInformation
);
741 sal_uInt32
ScenePrimitive2D::getPrimitive2DID() const
743 return PRIMITIVE2D_ID_SCENEPRIMITIVE2D
;
746 } // end of namespace
748 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */