LibreOffice Module drawinglayer (master) 1
sceneprimitive2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
34#include <utility>
35#include <vcl/BitmapTools.hxx>
38
39using namespace com::sun::star;
40
41namespace
42{
43 BitmapEx BPixelRasterToBitmapEx(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize)
44 {
45 BitmapEx aRetval;
46 const sal_uInt32 nWidth(mnAntiAlialize ? rRaster.getWidth()/mnAntiAlialize : rRaster.getWidth());
47 const sal_uInt32 nHeight(mnAntiAlialize ? rRaster.getHeight()/mnAntiAlialize : rRaster.getHeight());
48
49 if(nWidth && nHeight)
50 {
51 const Size aDestSize(nWidth, nHeight);
52 vcl::bitmap::RawBitmap aContent(aDestSize, 32);
53
54 if(mnAntiAlialize)
55 {
56 const sal_uInt16 nDivisor(mnAntiAlialize * mnAntiAlialize);
57
58 for(sal_uInt32 y(0); y < nHeight; y++)
59 {
60 for(sal_uInt32 x(0); x < nWidth; x++)
61 {
62 sal_uInt16 nRed(0);
63 sal_uInt16 nGreen(0);
64 sal_uInt16 nBlue(0);
65 sal_uInt16 nAlpha(0);
66 sal_uInt32 nIndex(rRaster.getIndexFromXY(x * mnAntiAlialize, y * mnAntiAlialize));
67
68 for(sal_uInt32 c(0); c < mnAntiAlialize; c++)
69 {
70 for(sal_uInt32 d(0); d < mnAntiAlialize; d++)
71 {
72 const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
73 nRed += rPixel.getRed();
74 nGreen += rPixel.getGreen();
75 nBlue += rPixel.getBlue();
76 nAlpha += rPixel.getAlpha();
77 }
78
79 nIndex += rRaster.getWidth() - mnAntiAlialize;
80 }
81
82 nAlpha /= nDivisor;
83
84 if(nAlpha)
85 {
86 aContent.SetPixel(y, x, Color(ColorAlpha,
87 static_cast<sal_uInt8>(nAlpha),
88 static_cast<sal_uInt8>(nRed / nDivisor),
89 static_cast<sal_uInt8>(nGreen / nDivisor),
90 static_cast<sal_uInt8>(nBlue / nDivisor) ));
91 }
92 else
93 aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
94 }
95 }
96 }
97 else
98 {
99 sal_uInt32 nIndex(0);
100
101 for(sal_uInt32 y(0); y < nHeight; y++)
102 {
103 for(sal_uInt32 x(0); x < nWidth; x++)
104 {
105 const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
106
107 if(rPixel.getAlpha())
108 {
109 aContent.SetPixel(y, x, Color(ColorAlpha, rPixel.getAlpha(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue()));
110 }
111 else
112 aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
113 }
114 }
115 }
116
117 aRetval = vcl::bitmap::CreateFromData(std::move(aContent));
118
119 // #i101811# set PrefMapMode and PrefSize at newly created Bitmap
120 aRetval.SetPrefMapMode(MapMode(MapUnit::MapPixel));
121 aRetval.SetPrefSize(Size(nWidth, nHeight));
122 }
123
124 return aRetval;
125 }
126} // end of anonymous namespace
127
129{
131 {
132 // create on demand
133 if(!mbShadow3DChecked && !getChildren3D().empty())
134 {
135 basegfx::B3DVector aLightNormal;
136 const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
137 const basegfx::B3DRange aScene3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
138
140 {
141 // get light normal from first light and normalize
142 aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
143 aLightNormal.normalize();
144 }
145
146 // create shadow extraction processor
150 aLightNormal,
151 fShadowSlant,
152 aScene3DRange);
153
154 // process local primitives
155 aShadowProcessor.process(getChildren3D());
156
157 // fetch result and set checked flag
158 const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
159 const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
160 }
161
162 // return if there are shadow primitives
163 return !maShadowPrimitives.empty();
164 }
165
167 const geometry::ViewInformation2D& rViewInformation,
168 basegfx::B2DRange& rDiscreteRange,
169 basegfx::B2DRange& rVisibleDiscreteRange,
170 basegfx::B2DRange& rUnitVisibleRange) const
171 {
172 // use unit range and transform to discrete coordinates
173 rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
174 rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
175
176 // clip it against discrete Viewport (if set)
177 rVisibleDiscreteRange = rDiscreteRange;
178
179 if(!rViewInformation.getViewport().isEmpty())
180 {
181 rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
182 }
183
184 if(rVisibleDiscreteRange.isEmpty())
185 {
186 rUnitVisibleRange = rVisibleDiscreteRange;
187 }
188 else
189 {
190 // create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
191 // the relative position of rVisibleDiscreteRange inside rDiscreteRange
192 const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
193 const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
194
195 const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
196 ? 0.0
197 : (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
198 const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
199 ? 0.0
200 : (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
201
202 const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
203 ? 1.0
204 : (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
205 const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
206 ? 1.0
207 : (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
208
209 rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
210 }
211 }
212
214 {
215 // create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
216 // there are some or not. Do this at start, the shadow might still be visible even when the scene is not
217 if(impGetShadow3D())
218 {
219 // test visibility
220 const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
221 const basegfx::B2DRange aViewRange(
222 rViewInformation.getViewport());
223
224 if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
225 {
226 // add extracted 2d shadows (before 3d scene creations itself)
227 rContainer.append(maShadowPrimitives);
228 }
229 }
230
231 // get the involved ranges (see helper method calculateDiscreteSizes for details)
232 basegfx::B2DRange aDiscreteRange;
233 basegfx::B2DRange aVisibleDiscreteRange;
234 basegfx::B2DRange aUnitVisibleRange;
235
236 calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
237
238 if(aVisibleDiscreteRange.isEmpty())
239 return;
240
241 // test if discrete view size (pixel) maybe too big and limit it
242 double fViewSizeX(aVisibleDiscreteRange.getWidth());
243 double fViewSizeY(aVisibleDiscreteRange.getHeight());
244 const double fViewVisibleArea(fViewSizeX * fViewSizeY);
245 const double fMaximumVisibleArea(SvtOptionsDrawinglayer::GetQuadratic3DRenderLimit());
246 double fReduceFactor(1.0);
247
248 if(fViewVisibleArea > fMaximumVisibleArea)
249 {
250 fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
251 fViewSizeX *= fReduceFactor;
252 fViewSizeY *= fReduceFactor;
253 }
254
255 if(rViewInformation.getReducedDisplayQuality())
256 {
257 // when reducing the visualisation is allowed (e.g. an OverlayObject
258 // only needed for dragging), reduce resolution extra
259 // to speed up dragging interactions
260 const double fArea(fViewSizeX * fViewSizeY);
261 if (fArea != 0.0)
262 {
263 double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
264
265 if(fReducedVisualisationFactor > 1.0)
266 {
267 fReducedVisualisationFactor = 1.0;
268 }
269 else if(fReducedVisualisationFactor < 0.20)
270 {
271 fReducedVisualisationFactor = 0.20;
272 }
273
274 if(fReducedVisualisationFactor != 1.0)
275 {
276 fReduceFactor *= fReducedVisualisationFactor;
277 }
278 }
279 }
280
281 // determine the oversample value
282 static const sal_uInt16 nDefaultOversampleValue(3);
283 const sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0);
284
286 {
287 // calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
288 // Start with full transformation from object to discrete units
290
291 // bring to unit coordinates by applying inverse DiscreteRange
292 aObjToUnit.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY());
293 if (aDiscreteRange.getWidth() != 0.0 && aDiscreteRange.getHeight() != 0.0)
294 {
295 aObjToUnit.scale(1.0 / aDiscreteRange.getWidth(), 1.0 / aDiscreteRange.getHeight());
296 }
297
298 // calculate transformed user coordinate system
299 const basegfx::B2DPoint aStandardNull(0.0, 0.0);
300 const basegfx::B2DPoint aUnitRangeTopLeft(aObjToUnit * aStandardNull);
301 const basegfx::B2DVector aStandardXAxis(1.0, 0.0);
302 const basegfx::B2DVector aUnitRangeXAxis(aObjToUnit * aStandardXAxis);
303 const basegfx::B2DVector aStandardYAxis(0.0, 1.0);
304 const basegfx::B2DVector aUnitRangeYAxis(aObjToUnit * aStandardYAxis);
305
306 if(!aUnitRangeTopLeft.equal(aStandardNull) || !aUnitRangeXAxis.equal(aStandardXAxis) || !aUnitRangeYAxis.equal(aStandardYAxis))
307 {
308 // build transformation from unit range to user coordinate system; the unit range
309 // X and Y axes are the column vectors, the null point is the offset
310 basegfx::B2DHomMatrix aUnitRangeToUser;
311
312 aUnitRangeToUser.set3x2(
313 aUnitRangeXAxis.getX(), aUnitRangeYAxis.getX(), aUnitRangeTopLeft.getX(),
314 aUnitRangeXAxis.getY(), aUnitRangeYAxis.getY(), aUnitRangeTopLeft.getY());
315
316 // decompose to allow to apply this to the 3D transformation
317 basegfx::B2DVector aScale, aTranslate;
318 double fRotate, fShearX;
319 aUnitRangeToUser.decompose(aScale, aTranslate, fRotate, fShearX);
320
321 // apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
322 // and not yet flipped in Y
323 basegfx::B3DHomMatrix aExtendedProjection(aViewInformation3D.getProjection());
324
325 // bring to unit coordinates, flip Y, leave Z unchanged
326 aExtendedProjection.scale(0.5, -0.5, 1.0);
327 aExtendedProjection.translate(0.5, 0.5, 0.0);
328
329 // apply extra; Y is flipped now, go with positive shear and rotate values
330 aExtendedProjection.scale(aScale.getX(), aScale.getY(), 1.0);
331 aExtendedProjection.shearXZ(fShearX, 0.0);
332 aExtendedProjection.rotate(0.0, 0.0, fRotate);
333 aExtendedProjection.translate(aTranslate.getX(), aTranslate.getY(), 0.0);
334
335 // back to state after projection
336 aExtendedProjection.translate(-0.5, -0.5, 0.0);
337 aExtendedProjection.scale(2.0, -2.0, 1.0);
338
339 aViewInformation3D = geometry::ViewInformation3D(
340 aViewInformation3D.getObjectTransformation(),
341 aViewInformation3D.getOrientation(),
342 aExtendedProjection,
343 aViewInformation3D.getDeviceToView(),
344 aViewInformation3D.getViewTime(),
345 aViewInformation3D.getExtendedInformationSequence());
346 }
347 }
348
349 // calculate logic render size in world coordinates for usage in renderer
350 const basegfx::B2DHomMatrix& aInverseOToV(rViewInformation.getInverseObjectToViewTransformation());
351 const double fLogicX((aInverseOToV * basegfx::B2DVector(aDiscreteRange.getWidth() * fReduceFactor, 0.0)).getLength());
352 const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength());
353
354 // generate ViewSizes
355 const double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength());
356 const double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength());
357
358 // generate RasterWidth and RasterHeight for visible part
359 const sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1);
360 const sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1);
361
362 if(!(nRasterWidth && nRasterHeight))
363 return;
364
365 // create view unit buffer
366 basegfx::BZPixelRaster aBZPixelRaster(
367 nOversampleValue ? nRasterWidth * nOversampleValue : nRasterWidth,
368 nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight);
369
370 // check for parallel execution possibilities
371 static bool bMultithreadAllowed = false; // loplugin:constvars:ignore
372 sal_Int32 nThreadCount(0);
374
375 if(bMultithreadAllowed)
376 {
377 nThreadCount = rThreadPool.getWorkerCount();
378
379 if(nThreadCount > 1)
380 {
381 // at least use 10px per processor, so limit number of processors to
382 // target pixel size divided by 10 (which might be zero what is okay)
383 nThreadCount = std::min(nThreadCount, nRasterHeight / 10);
384 }
385 }
386
387 if(nThreadCount > 1)
388 {
389 class Executor : public comphelper::ThreadTask
390 {
391 private:
392 std::unique_ptr<processor3d::ZBufferProcessor3D> mpZBufferProcessor3D;
393 const primitive3d::Primitive3DContainer& mrChildren3D;
394
395 public:
396 explicit Executor(
397 std::shared_ptr<comphelper::ThreadTaskTag> const & rTag,
398 std::unique_ptr<processor3d::ZBufferProcessor3D> pZBufferProcessor3D,
399 const primitive3d::Primitive3DContainer& rChildren3D)
401 mpZBufferProcessor3D(std::move(pZBufferProcessor3D)),
402 mrChildren3D(rChildren3D)
403 {
404 }
405
406 virtual void doWork() override
407 {
408 mpZBufferProcessor3D->process(mrChildren3D);
409 mpZBufferProcessor3D->finish();
410 mpZBufferProcessor3D.reset();
411 }
412 };
413
414 const sal_uInt32 nLinesPerThread(aBZPixelRaster.getHeight() / nThreadCount);
415 std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
416
417 for(sal_Int32 a(0); a < nThreadCount; a++)
418 {
419 std::unique_ptr<processor3d::ZBufferProcessor3D> pNewZBufferProcessor3D(new processor3d::ZBufferProcessor3D(
420 aViewInformation3D,
423 aUnitVisibleRange,
424 nOversampleValue,
425 fFullViewSizeX,
426 fFullViewSizeY,
427 aBZPixelRaster,
428 nLinesPerThread * a,
429 a + 1 == nThreadCount ? aBZPixelRaster.getHeight() : nLinesPerThread * (a + 1)));
430 std::unique_ptr<Executor> pExecutor(new Executor(aTag, std::move(pNewZBufferProcessor3D), getChildren3D()));
431 rThreadPool.pushTask(std::move(pExecutor));
432 }
433
434 rThreadPool.waitUntilDone(aTag);
435 }
436 else
437 {
438 // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
439 processor3d::ZBufferProcessor3D aZBufferProcessor3D(
440 aViewInformation3D,
443 aUnitVisibleRange,
444 nOversampleValue,
445 fFullViewSizeX,
446 fFullViewSizeY,
447 aBZPixelRaster,
448 0,
449 aBZPixelRaster.getHeight());
450
451 aZBufferProcessor3D.process(getChildren3D());
452 aZBufferProcessor3D.finish();
453 }
454
455 const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmapEx(aBZPixelRaster, nOversampleValue);
456 const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
457
458 if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight()))
459 return;
460
461 // create transform for the created bitmap in discrete coordinates first.
462 basegfx::B2DHomMatrix aNew2DTransform;
463
464 aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
465 aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
466 aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
467 aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
468
469 // transform back to world coordinates for usage in primitive creation
470 aNew2DTransform *= aInverseOToV;
471
472 // create bitmap primitive and add
473 rContainer.push_back(
476 aNew2DTransform));
477
478 // test: Allow to add an outline in the debugger when tests are needed
479 static bool bAddOutlineToCreated3DSceneRepresentation(false); // loplugin:constvars:ignore
480
481 if(bAddOutlineToCreated3DSceneRepresentation)
482 {
484 aOutline.transform(aNew2DTransform);
485 rContainer.push_back(new PolygonHairlinePrimitive2D(std::move(aOutline), basegfx::BColor(1.0, 0.0, 0.0)));
486 }
487 }
488
490 {
491 Primitive2DContainer aRetval;
492
493 // create 2D projected geometry from 3D geometry
494 if(!getChildren3D().empty())
495 {
496 // create 2D geometry extraction processor
500
501 // process local primitives
502 aGeometryProcessor.process(getChildren3D());
503
504 // fetch result
505 aRetval = aGeometryProcessor.getPrimitive2DSequence();
506 }
507
508 return aRetval;
509 }
510
512 {
513 Primitive2DContainer aRetval;
514
515 // create 2D shadows from contained 3D primitives
516 if(impGetShadow3D())
517 {
518 // add extracted 2d shadows (before 3d scene creations itself)
519 aRetval = maShadowPrimitives;
520 }
521
522 return aRetval;
523 }
524
525 bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
526 {
528 return false;
529
530 basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
531 aInverseSceneTransform.invert();
532 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
533
534 if(!maOldUnitVisiblePart.isInside(aRelativePoint))
535 return false;
536
537 // calculate coordinates relative to visualized part
538 double fDivisorX(maOldUnitVisiblePart.getWidth());
539 double fDivisorY(maOldUnitVisiblePart.getHeight());
540
541 if(basegfx::fTools::equalZero(fDivisorX))
542 {
543 fDivisorX = 1.0;
544 }
545
546 if(basegfx::fTools::equalZero(fDivisorY))
547 {
548 fDivisorY = 1.0;
549 }
550
551 const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
552 const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
553
554 // combine with real BitmapSizePixel to get bitmap coordinates
555 const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
556 const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
557 const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
558
559 // try to get a statement about transparency in that pixel
560 o_rResult = (0 != maOldRenderedBitmap.GetAlpha(nX, nY));
561 return true;
562 }
563
566 attribute::SdrSceneAttribute aSdrSceneAttribute,
567 attribute::SdrLightingAttribute aSdrLightingAttribute,
568 basegfx::B2DHomMatrix aObjectTransformation,
569 geometry::ViewInformation3D aViewInformation3D)
570 : mxChildren3D(std::move(aChildren3D)),
571 maSdrSceneAttribute(std::move(aSdrSceneAttribute)),
572 maSdrLightingAttribute(std::move(aSdrLightingAttribute)),
573 maObjectTransformation(std::move(aObjectTransformation)),
574 maViewInformation3D(std::move(aViewInformation3D)),
575 mbShadow3DChecked(false),
576 mfOldDiscreteSizeX(0.0),
577 mfOldDiscreteSizeY(0.0)
578 {
579 }
580
581 bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
582 {
583 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
584 {
585 const ScenePrimitive2D& rCompare = static_cast<const ScenePrimitive2D&>(rPrimitive);
586
587 return (getChildren3D() == rCompare.getChildren3D()
591 && getViewInformation3D() == rCompare.getViewInformation3D());
592 }
593
594 return false;
595 }
596
598 {
599 // transform unit range to discrete coordinate range
600 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
602
603 // force to discrete expanded bounds (it grows, so expanding works perfectly well)
604 aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
605 aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
606
607 // transform back from discrete (view) to world coordinates
608 aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
609
610 // expand by evtl. existing shadow primitives
611 if(impGetShadow3D())
612 {
613 const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
614
615 if(!aShadow2DRange.isEmpty())
616 {
617 aRetval.expand(aShadow2DRange);
618 }
619 }
620
621 return aRetval;
622 }
623
625 {
626 // get the involved ranges (see helper method calculateDiscreteSizes for details)
627 basegfx::B2DRange aDiscreteRange;
628 basegfx::B2DRange aUnitVisibleRange;
629 bool bNeedNewDecomposition(false);
630 bool bDiscreteSizesAreCalculated(false);
631
632 if(!getBuffered2DDecomposition().empty())
633 {
634 basegfx::B2DRange aVisibleDiscreteRange;
635 calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
636 bDiscreteSizesAreCalculated = true;
637
638 // needs to be painted when the new part is not part of the last
639 // decomposition
640 if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
641 {
642 bNeedNewDecomposition = true;
643 }
644
645 // display has changed and cannot be reused when resolution got bigger. It
646 // can be reused when resolution got smaller, though.
647 if(!bNeedNewDecomposition)
648 {
649 if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
651 {
652 bNeedNewDecomposition = true;
653 }
654 }
655 }
656
657 if(bNeedNewDecomposition)
658 {
659 // conditions of last local decomposition have changed, delete
661 }
662
663 if(getBuffered2DDecomposition().empty())
664 {
665 if(!bDiscreteSizesAreCalculated)
666 {
667 basegfx::B2DRange aVisibleDiscreteRange;
668 calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
669 }
670
671 // remember last used NewDiscreteSize and NewUnitVisiblePart
672 ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
673 pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
674 pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
675 pThat->maOldUnitVisiblePart = aUnitVisibleRange;
676 }
677
678 // use parent implementation
680 }
681
682 // provide unique ID
684 {
686 }
687
688} // end of namespace
689
690/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double d
void SetPrefMapMode(const MapMode &rPrefMapMode)
void SetPrefSize(const Size &rPrefSize)
bool IsEmpty() const
sal_uInt8 GetAlpha(sal_Int32 nX, sal_Int32 nY) const
const Size & GetSizePixel() const
constexpr tools::Long getHeight() const
constexpr tools::Long Height() const
constexpr tools::Long getWidth() const
constexpr tools::Long Width() const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void set3x2(double f_0x0, double f_0x1, double f_0x2, double f_1x0, double f_1x1, double f_1x2)
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void translate(double fX, double fY)
void scale(double fX, double fY)
void transform(const basegfx::B2DHomMatrix &rMatrix)
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
void rotate(double fAngleX, double fAngleY, double fAngleZ)
void translate(double fX, double fY, double fZ)
void scale(double fX, double fY, double fZ)
void shearXZ(double fSx, double fSz)
B3DVector & normalize()
sal_uInt32 getIndexFromXY(sal_uInt32 nX, sal_uInt32 nY) const
sal_uInt32 getWidth() const
sal_uInt32 getHeight() const
const BPixel & getBPixel(sal_uInt32 nIndex) const
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
void expand(const Tuple2D< TYPE > &rTuple)
TYPE getMaxY() const
void intersect(const Range2D &rRange)
bool isInside(const Tuple2D< TYPE > &rTuple) const
bool isEmpty() const
TYPE getHeight() const
bool overlaps(const Range2D &rRange) const
bool equal(const Tuple2D< TYPE > &rTup) const
TYPE getX() const
TYPE getY() const
static ThreadPool & getSharedOptimalPool()
void waitUntilDone(const std::shared_ptr< ThreadTaskTag > &, bool bJoin=true)
static std::shared_ptr< ThreadTaskTag > createThreadTaskTag()
void pushTask(std::unique_ptr< ThreadTask > pTask)
sal_Int32 getWorkerCount() const
const ::std::vector< Sdr3DLightAttribute > & getLightVector() const
const basegfx::B2DRange & getViewport() const
Empty viewport means everything is visible.
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
const basegfx::B2DRange & getDiscreteViewport() const
On-demand prepared Viewport in discrete units for convenience Empty viewport means everything is visi...
const basegfx::B2DHomMatrix & getObjectToViewTransformation() const
On-demand prepared Object to View transformation and its inverse for convenience.
bool getReducedDisplayQuality() const
Support reduced DisplayQuality, PropertyName is 'ReducedDisplayQuality'.
const css::uno::Sequence< css::beans::PropertyValue > & getExtendedInformationSequence() const
Get the uno::Sequence< beans::PropertyValue > which contains only ViewInformation not offered directl...
const basegfx::B3DHomMatrix & getDeviceToView() const
const basegfx::B3DHomMatrix & getObjectTransformation() const
data access
const basegfx::B3DHomMatrix & getProjection() const
const basegfx::B3DHomMatrix & getOrientation() const
const Primitive2DContainer & getBuffered2DDecomposition() const
access methods to maBuffered2DDecomposition.
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
The getDecomposition default implementation will on demand use create2DDecomposition() if maBuffered2...
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
const attribute::SdrSceneAttribute & getSdrSceneAttribute() const
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
const geometry::ViewInformation3D & getViewInformation3D() const
attribute::SdrLightingAttribute maSdrLightingAttribute
lighting attribute set
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
const primitive3d::Primitive3DContainer & getChildren3D() const
data read access
BitmapEx maOldRenderedBitmap
the last created BitmapEx, e.g.
bool mbShadow3DChecked
flag if given 3D geometry is already checked for shadow definitions and 2d shadows are created in maS...
Primitive2DContainer getGeometry2D() const
public helpers
Primitive2DContainer maShadowPrimitives
the primitiveSequence for on-demand created shadow primitives (see mbShadow3DChecked)
bool tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint &rLogicHitPoint, bool &o_rResult) const
Fast HitTest which uses the last buffered BitmapEx from the last rendered area if available.
ScenePrimitive2D(primitive3d::Primitive3DContainer xChildren3D, attribute::SdrSceneAttribute aSdrSceneAttribute, attribute::SdrLightingAttribute aSdrLightingAttribute, basegfx::B2DHomMatrix aObjectTransformation, geometry::ViewInformation3D aViewInformation3D)
constructor
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
const basegfx::B2DHomMatrix & getObjectTransformation() const
double mfOldDiscreteSizeX
the last used NewDiscreteSize and NewUnitVisiblePart definitions for decomposition
const attribute::SdrLightingAttribute & getSdrLightingAttribute() const
void calculateDiscreteSizes(const geometry::ViewInformation2D &rViewInformation, basegfx::B2DRange &rDiscreteRange, basegfx::B2DRange &rVisibleDiscreteRange, basegfx::B2DRange &rUnitVisibleRange) const
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
get local decomposition. Override since this decomposition is view-dependent
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
void process(const primitive3d::Primitive3DContainer &rSource)
const primitive2d::Primitive2DContainer & getPrimitive2DSequence() const
const primitive2d::Primitive2DContainer & getPrimitive2DSequence() const
data read access
This 3D renderer derived from DefaultProcessor3D renders all fed primitives to a 2D raster bitmap usi...
#define PRIMITIVE2D_ID_SCENEPRIMITIVE2D
float y
float x
sal_Int32 nIndex
uno_Any a
sal_uInt32 GetQuadratic3DRenderLimit()
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
B2DPolygon const & createUnitPolygon()
B2IRange fround(const B2DRange &rRange)
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, sal_Int8 nBitCount, bool bReversColors, bool bReverseAlpha)
unsigned char sal_uInt8