1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
32 #include <basegfx/tools/canvastools.hxx>
33 #include <basegfx/polygon/b2dpolygontools.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygonclipper.hxx>
36 #include <basegfx/polygon/b2dpolypolygontools.hxx>
37 #include <basegfx/matrix/b2dhommatrix.hxx>
38 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
39 #include <drawinglayer/processor3d/zbufferprocessor3d.hxx>
40 #include <drawinglayer/processor3d/shadow3dextractor.hxx>
41 #include <drawinglayer/geometry/viewinformation2d.hxx>
42 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
43 #include <svtools/optionsdrawinglayer.hxx>
44 #include <drawinglayer/processor3d/geometry2dextractor.hxx>
45 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
46 
47 //////////////////////////////////////////////////////////////////////////////
48 
49 using namespace com::sun::star;
50 
51 //////////////////////////////////////////////////////////////////////////////
52 
53 namespace drawinglayer
54 {
55 	namespace primitive2d
56 	{
57 		bool ScenePrimitive2D::impGetShadow3D(const geometry::ViewInformation2D& /*rViewInformation*/) const
58 		{
59             ::osl::MutexGuard aGuard( m_aMutex );
60 
61 			// create on demand
62 			if(!mbShadow3DChecked && getChildren3D().hasElements())
63 			{
64 				basegfx::B3DVector aLightNormal;
65                 const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
66 				const basegfx::B3DRange aScene3DRange(primitive3d::getB3DRangeFromPrimitive3DSequence(getChildren3D(), getViewInformation3D()));
67 
68 				if(maSdrLightingAttribute.getLightVector().size())
69 				{
70 					// get light normal from first light and normalize
71 					aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
72 					aLightNormal.normalize();
73 				}
74 
75 				// create shadow extraction processor
76 				processor3d::Shadow3DExtractingProcessor aShadowProcessor(
77 					getViewInformation3D(),
78 					getObjectTransformation(),
79 					aLightNormal,
80 					fShadowSlant,
81                     aScene3DRange);
82 
83 				// process local primitives
84 				aShadowProcessor.process(getChildren3D());
85 
86 				// fetch result and set checked flag
87 				const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
88 				const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
89 			}
90 
91 			// return if there are shadow primitives
92 			return maShadowPrimitives.hasElements();
93 		}
94 
95         void ScenePrimitive2D::calculateDiscreteSizes(
96 			const geometry::ViewInformation2D& rViewInformation,
97 			basegfx::B2DRange& rDiscreteRange,
98 			basegfx::B2DRange& rVisibleDiscreteRange,
99 			basegfx::B2DRange& rUnitVisibleRange) const
100 		{
101 			// use unit range and transform to discrete coordinates
102 			rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
103 			rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
104 
105 			// clip it against discrete Viewport (if set)
106 			rVisibleDiscreteRange = rDiscreteRange;
107 
108 			if(!rViewInformation.getViewport().isEmpty())
109 			{
110 				rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
111 			}
112 
113 			if(rVisibleDiscreteRange.isEmpty())
114 			{
115 				rUnitVisibleRange = rVisibleDiscreteRange;
116 			}
117 			else
118 			{
119 				// create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
120 				// the relative position of rVisibleDiscreteRange inside rDiscreteRange
121 				const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
122 				const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
123 
124 				const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
125 					? 0.0
126 					: (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
127 				const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
128 					? 0.0
129 					: (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
130 
131 				const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
132 					? 1.0
133 					: (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
134 				const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
135 					? 1.0
136 					: (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
137 
138 				rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
139 			}
140 		}
141 
142 		Primitive2DSequence ScenePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
143 		{
144 			Primitive2DSequence aRetval;
145 
146 			// create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
147 			// there are some or not. Do this at start, the shadow might still be visible even when the scene is not
148 			if(impGetShadow3D(rViewInformation))
149 			{
150 				// test visibility
151 				const basegfx::B2DRange aShadow2DRange(
152                     getB2DRangeFromPrimitive2DSequence(maShadowPrimitives, rViewInformation));
153 				const basegfx::B2DRange aViewRange(
154                     rViewInformation.getViewport());
155 
156 				if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
157 				{
158 					// add extracted 2d shadows (before 3d scene creations itself)
159 					aRetval = maShadowPrimitives;
160 				}
161 			}
162 
163 			// get the involved ranges (see helper method calculateDiscreteSizes for details)
164 			basegfx::B2DRange aDiscreteRange;
165 			basegfx::B2DRange aVisibleDiscreteRange;
166 			basegfx::B2DRange aUnitVisibleRange;
167 
168             calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
169 
170 			if(!aVisibleDiscreteRange.isEmpty())
171 			{
172 				// test if discrete view size (pixel) maybe too big and limit it
173 				double fViewSizeX(aVisibleDiscreteRange.getWidth());
174 				double fViewSizeY(aVisibleDiscreteRange.getHeight());
175 				const double fViewVisibleArea(fViewSizeX * fViewSizeY);
176                 const SvtOptionsDrawinglayer aDrawinglayerOpt;
177 				const double fMaximumVisibleArea(aDrawinglayerOpt.GetQuadratic3DRenderLimit());
178 				double fReduceFactor(1.0);
179 
180 				if(fViewVisibleArea > fMaximumVisibleArea)
181 				{
182 					fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
183 					fViewSizeX *= fReduceFactor;
184 					fViewSizeY *= fReduceFactor;
185 				}
186 
187 				if(rViewInformation.getReducedDisplayQuality())
188 				{
189 					// when reducing the visualisation is allowed (e.g. an OverlayObject
190 					// only needed for dragging), reduce resolution extra
191 					// to speed up dragging interactions
192 					const double fArea(fViewSizeX * fViewSizeY);
193 					double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
194 
195 					if(fReducedVisualisationFactor > 1.0)
196 					{
197 						fReducedVisualisationFactor = 1.0;
198 					}
199 					else if(fReducedVisualisationFactor < 0.20)
200 					{
201 						fReducedVisualisationFactor = 0.20;
202 					}
203 
204 					if(fReducedVisualisationFactor != 1.0)
205 					{
206 						fReduceFactor *= fReducedVisualisationFactor;
207 						fViewSizeX *= fReducedVisualisationFactor;
208 						fViewSizeY *= fReducedVisualisationFactor;
209 					}
210 				}
211 
212 				// calculate logic render size in world coordinates for usage in renderer
213 				basegfx::B2DVector aLogicRenderSize(
214 					aDiscreteRange.getWidth() * fReduceFactor,
215 					aDiscreteRange.getHeight() * fReduceFactor);
216 				aLogicRenderSize *= rViewInformation.getInverseObjectToViewTransformation();
217 
218                 // determine the oversample value
219                 static sal_uInt16 nDefaultOversampleValue(3);
220                 const sal_uInt16 nOversampleValue(aDrawinglayerOpt.IsAntiAliasing() ? nDefaultOversampleValue : 0);
221 
222 			    // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
223 			    processor3d::ZBufferProcessor3D aZBufferProcessor3D(
224 				    getViewInformation3D(),
225 					rViewInformation,
226 				    getSdrSceneAttribute(),
227 				    getSdrLightingAttribute(),
228 				    aLogicRenderSize.getX(),
229 				    aLogicRenderSize.getY(),
230 				    aUnitVisibleRange,
231                     nOversampleValue);
232 
233 			    aZBufferProcessor3D.process(getChildren3D());
234 			    aZBufferProcessor3D.finish();
235 
236                 const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = aZBufferProcessor3D.getBitmapEx();
237 				const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
238 
239 				if(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight())
240 				{
241 					// create transform for the created bitmap in discrete coordinates first.
242 					basegfx::B2DHomMatrix aNew2DTransform;
243 
244                     aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
245 					aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
246 					aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
247 					aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
248 
249 					// transform back to world coordinates for usage in primitive creation
250 					aNew2DTransform *= rViewInformation.getInverseObjectToViewTransformation();
251 
252 					// create bitmap primitive and add
253 					const Primitive2DReference xRef(new BitmapPrimitive2D(maOldRenderedBitmap, aNew2DTransform));
254 					appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRef);
255 
256 					// test: Allow to add an outline in the debugger when tests are needed
257 					static bool bAddOutlineToCreated3DSceneRepresentation(false);
258 
259 					if(bAddOutlineToCreated3DSceneRepresentation)
260 					{
261 						basegfx::B2DPolygon aOutline(basegfx::tools::createUnitPolygon());
262 						aOutline.transform(aNew2DTransform);
263 						const Primitive2DReference xRef2(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0)));
264 						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRef2);
265 					}
266 				}
267 			}
268 
269 			return aRetval;
270 		}
271 
272 		Primitive2DSequence ScenePrimitive2D::getGeometry2D() const
273 		{
274 			Primitive2DSequence aRetval;
275 
276             // create 2D projected geometry from 3D geometry
277 			if(getChildren3D().hasElements())
278 			{
279 				// create 2D geometry extraction processor
280 				processor3d::Geometry2DExtractingProcessor aGeometryProcessor(
281 					getViewInformation3D(),
282 					getObjectTransformation());
283 
284 				// process local primitives
285 				aGeometryProcessor.process(getChildren3D());
286 
287 				// fetch result
288 				aRetval = aGeometryProcessor.getPrimitive2DSequence();
289 			}
290 
291 			return aRetval;
292 		}
293 
294 		Primitive2DSequence ScenePrimitive2D::getShadow2D(const geometry::ViewInformation2D& rViewInformation) const
295 		{
296 			Primitive2DSequence aRetval;
297 
298 			// create 2D shadows from contained 3D primitives
299 			if(impGetShadow3D(rViewInformation))
300 			{
301 				// add extracted 2d shadows (before 3d scene creations itself)
302 				aRetval = maShadowPrimitives;
303 			}
304 
305 			return aRetval;
306 		}
307 
308         bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
309         {
310             if(!maOldRenderedBitmap.IsEmpty() && !maOldUnitVisiblePart.isEmpty())
311             {
312                 basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
313                 aInverseSceneTransform.invert();
314                 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
315 
316                 if(maOldUnitVisiblePart.isInside(aRelativePoint))
317                 {
318                     // calculate coordinates relative to visualized part
319                     double fDivisorX(maOldUnitVisiblePart.getWidth());
320                     double fDivisorY(maOldUnitVisiblePart.getHeight());
321 
322                     if(basegfx::fTools::equalZero(fDivisorX))
323                     {
324                         fDivisorX = 1.0;
325                     }
326 
327                     if(basegfx::fTools::equalZero(fDivisorY))
328                     {
329                         fDivisorY = 1.0;
330                     }
331 
332                     const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
333                     const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
334 
335                     // combine with real BitmapSizePixel to get bitmap coordinates
336     				const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
337                     const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
338                     const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
339 
340                     // try to get a statement about transparency in that pixel
341                     o_rResult = (0xff != maOldRenderedBitmap.GetTransparency(nX, nY));
342                     return true;
343                 }
344             }
345 
346             return false;
347         }
348 
349 		ScenePrimitive2D::ScenePrimitive2D(
350 			const primitive3d::Primitive3DSequence& rxChildren3D,
351 			const attribute::SdrSceneAttribute& rSdrSceneAttribute,
352 			const attribute::SdrLightingAttribute& rSdrLightingAttribute,
353 			const basegfx::B2DHomMatrix& rObjectTransformation,
354 			const geometry::ViewInformation3D& rViewInformation3D)
355 		:	BufferedDecompositionPrimitive2D(),
356 			mxChildren3D(rxChildren3D),
357 			maSdrSceneAttribute(rSdrSceneAttribute),
358 			maSdrLightingAttribute(rSdrLightingAttribute),
359 			maObjectTransformation(rObjectTransformation),
360 			maViewInformation3D(rViewInformation3D),
361             maShadowPrimitives(),
362 			mbShadow3DChecked(false),
363 			mfOldDiscreteSizeX(0.0),
364 			mfOldDiscreteSizeY(0.0),
365 			maOldUnitVisiblePart(),
366             maOldRenderedBitmap()
367 		{
368 		}
369 
370 		bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
371 		{
372 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
373 			{
374 				const ScenePrimitive2D& rCompare = (ScenePrimitive2D&)rPrimitive;
375 
376 				return (primitive3d::arePrimitive3DSequencesEqual(getChildren3D(), rCompare.getChildren3D())
377 					&& getSdrSceneAttribute() == rCompare.getSdrSceneAttribute()
378 					&& getSdrLightingAttribute() == rCompare.getSdrLightingAttribute()
379 					&& getObjectTransformation() == rCompare.getObjectTransformation()
380 					&& getViewInformation3D() == rCompare.getViewInformation3D());
381 			}
382 
383 			return false;
384 		}
385 
386 		basegfx::B2DRange ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
387 		{
388 			// transform unit range to discrete coordinate range
389 			basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
390 			aRetval.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
391 
392 			// force to discrete expanded bounds (it grows, so expanding works perfectly well)
393 			aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
394 			aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
395 
396 			// transform back from discrete (view) to world coordinates
397 			aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
398 
399 			// expand by evtl. existing shadow primitives
400 			if(impGetShadow3D(rViewInformation))
401 			{
402 				const basegfx::B2DRange aShadow2DRange(getB2DRangeFromPrimitive2DSequence(maShadowPrimitives, rViewInformation));
403 
404 				if(!aShadow2DRange.isEmpty())
405 				{
406 					aRetval.expand(aShadow2DRange);
407 				}
408 			}
409 
410 			return aRetval;
411 		}
412 
413 		Primitive2DSequence ScenePrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
414 		{
415 			::osl::MutexGuard aGuard( m_aMutex );
416 
417 			// get the involved ranges (see helper method calculateDiscreteSizes for details)
418 		    basegfx::B2DRange aDiscreteRange;
419 		    basegfx::B2DRange aUnitVisibleRange;
420 			bool bNeedNewDecomposition(false);
421 			bool bDiscreteSizesAreCalculated(false);
422 
423 			if(getBuffered2DDecomposition().hasElements())
424 			{
425 			    basegfx::B2DRange aVisibleDiscreteRange;
426 			    calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
427 				bDiscreteSizesAreCalculated = true;
428 
429 				// needs to be painted when the new part is not part of the last
430                 // decomposition
431 				if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
432                 {
433 					bNeedNewDecomposition = true;
434                 }
435 
436                 // display has changed and cannot be reused when resolution got bigger. It
437                 // can be reused when resolution got smaller, though.
438 				if(!bNeedNewDecomposition)
439 				{
440 				    if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
441 					    basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
442 				    {
443 					    bNeedNewDecomposition = true;
444 				    }
445 				}
446 			}
447 
448 			if(bNeedNewDecomposition)
449 			{
450 				// conditions of last local decomposition have changed, delete
451 				const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
452 			}
453 
454 			if(!getBuffered2DDecomposition().hasElements())
455 			{
456 				if(!bDiscreteSizesAreCalculated)
457 				{
458 					basegfx::B2DRange aVisibleDiscreteRange;
459 					calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
460 				}
461 
462 				// remember last used NewDiscreteSize and NewUnitVisiblePart
463 				ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
464 				pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
465 				pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
466 				pThat->maOldUnitVisiblePart = aUnitVisibleRange;
467 			}
468 
469 			// use parent implementation
470 			return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
471 		}
472 
473 		// provide unique ID
474 		ImplPrimitrive2DIDBlock(ScenePrimitive2D, PRIMITIVE2D_ID_SCENEPRIMITIVE2D)
475 
476 	} // end of namespace primitive2d
477 } // end of namespace drawinglayer
478 
479 //////////////////////////////////////////////////////////////////////////////
480 // eof
481