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