dc8632a8b5
just use the wrapped officecfg methods instead of: IsSolidDragCreate IsRenderDecoratedTextDirect IsRenderSimpleTextDirect GetQuadratic3DRenderLimit GetQuadraticFormControlRenderLimit IsTransparentSelection Change-Id: Ie0f3ec0f8fdbbf08facfff1a372c666c8a0c8979 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160654 Tested-by: Jenkins Reviewed-by: Thorsten Behrens <thorsten.behrens@allotropia.de>
745 lines
34 KiB
C++
745 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
#include <basegfx/polygon/b2dpolygon.hxx>
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
|
|
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
|
|
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
|
|
#include <processor3d/zbufferprocessor3d.hxx>
|
|
#include <processor3d/shadow3dextractor.hxx>
|
|
#include <drawinglayer/geometry/viewinformation2d.hxx>
|
|
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
|
|
#include <svtools/optionsdrawinglayer.hxx>
|
|
#include <processor3d/geometry2dextractor.hxx>
|
|
#include <basegfx/raster/bzpixelraster.hxx>
|
|
#include <utility>
|
|
#include <vcl/BitmapTools.hxx>
|
|
#include <comphelper/threadpool.hxx>
|
|
#include <comphelper/lok.hxx>
|
|
#include <toolkit/helper/vclunohelper.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
namespace
|
|
{
|
|
BitmapEx BPixelRasterToBitmapEx(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize)
|
|
{
|
|
BitmapEx aRetval;
|
|
const sal_uInt32 nWidth(mnAntiAlialize ? rRaster.getWidth()/mnAntiAlialize : rRaster.getWidth());
|
|
const sal_uInt32 nHeight(mnAntiAlialize ? rRaster.getHeight()/mnAntiAlialize : rRaster.getHeight());
|
|
|
|
if(nWidth && nHeight)
|
|
{
|
|
const Size aDestSize(nWidth, nHeight);
|
|
vcl::bitmap::RawBitmap aContent(aDestSize, 32);
|
|
|
|
if(mnAntiAlialize)
|
|
{
|
|
const sal_uInt16 nDivisor(mnAntiAlialize * mnAntiAlialize);
|
|
|
|
for(sal_uInt32 y(0); y < nHeight; y++)
|
|
{
|
|
for(sal_uInt32 x(0); x < nWidth; x++)
|
|
{
|
|
sal_uInt16 nRed(0);
|
|
sal_uInt16 nGreen(0);
|
|
sal_uInt16 nBlue(0);
|
|
sal_uInt16 nAlpha(0);
|
|
sal_uInt32 nIndex(rRaster.getIndexFromXY(x * mnAntiAlialize, y * mnAntiAlialize));
|
|
|
|
for(sal_uInt32 c(0); c < mnAntiAlialize; c++)
|
|
{
|
|
for(sal_uInt32 d(0); d < mnAntiAlialize; d++)
|
|
{
|
|
const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
|
|
nRed += rPixel.getRed();
|
|
nGreen += rPixel.getGreen();
|
|
nBlue += rPixel.getBlue();
|
|
nAlpha += rPixel.getAlpha();
|
|
}
|
|
|
|
nIndex += rRaster.getWidth() - mnAntiAlialize;
|
|
}
|
|
|
|
nAlpha /= nDivisor;
|
|
|
|
if(nAlpha)
|
|
{
|
|
aContent.SetPixel(y, x, Color(ColorAlpha,
|
|
static_cast<sal_uInt8>(nAlpha),
|
|
static_cast<sal_uInt8>(nRed / nDivisor),
|
|
static_cast<sal_uInt8>(nGreen / nDivisor),
|
|
static_cast<sal_uInt8>(nBlue / nDivisor) ));
|
|
}
|
|
else
|
|
aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sal_uInt32 nIndex(0);
|
|
|
|
for(sal_uInt32 y(0); y < nHeight; y++)
|
|
{
|
|
for(sal_uInt32 x(0); x < nWidth; x++)
|
|
{
|
|
const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
|
|
|
|
if(rPixel.getAlpha())
|
|
{
|
|
aContent.SetPixel(y, x, Color(ColorAlpha, rPixel.getAlpha(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue()));
|
|
}
|
|
else
|
|
aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
aRetval = vcl::bitmap::CreateFromData(std::move(aContent));
|
|
|
|
// #i101811# set PrefMapMode and PrefSize at newly created Bitmap
|
|
aRetval.SetPrefMapMode(MapMode(MapUnit::MapPixel));
|
|
aRetval.SetPrefSize(Size(nWidth, nHeight));
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
} // end of anonymous namespace
|
|
|
|
namespace drawinglayer::primitive2d
|
|
{
|
|
bool ScenePrimitive2D::impGetShadow3D() const
|
|
{
|
|
// create on demand
|
|
if(!mbShadow3DChecked && !getChildren3D().empty())
|
|
{
|
|
basegfx::B3DVector aLightNormal;
|
|
const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
|
|
const basegfx::B3DRange aScene3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
|
|
|
|
if(!maSdrLightingAttribute.getLightVector().empty())
|
|
{
|
|
// get light normal from first light and normalize
|
|
aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
|
|
aLightNormal.normalize();
|
|
}
|
|
|
|
// create shadow extraction processor
|
|
processor3d::Shadow3DExtractingProcessor aShadowProcessor(
|
|
getViewInformation3D(),
|
|
getObjectTransformation(),
|
|
aLightNormal,
|
|
fShadowSlant,
|
|
aScene3DRange);
|
|
|
|
// process local primitives
|
|
aShadowProcessor.process(getChildren3D());
|
|
|
|
// fetch result and set checked flag
|
|
const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
|
|
const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
|
|
}
|
|
|
|
// return if there are shadow primitives
|
|
return !maShadowPrimitives.empty();
|
|
}
|
|
|
|
void ScenePrimitive2D::calculateDiscreteSizes(
|
|
const geometry::ViewInformation2D& rViewInformation,
|
|
basegfx::B2DRange& rDiscreteRange,
|
|
basegfx::B2DRange& rVisibleDiscreteRange,
|
|
basegfx::B2DRange& rUnitVisibleRange) const
|
|
{
|
|
// use unit range and transform to discrete coordinates
|
|
rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
|
|
rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
|
|
|
|
// clip it against discrete Viewport (if set)
|
|
rVisibleDiscreteRange = rDiscreteRange;
|
|
|
|
if(!rViewInformation.getViewport().isEmpty())
|
|
{
|
|
rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
|
|
}
|
|
|
|
if(rVisibleDiscreteRange.isEmpty())
|
|
{
|
|
rUnitVisibleRange = rVisibleDiscreteRange;
|
|
}
|
|
else
|
|
{
|
|
// create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
|
|
// the relative position of rVisibleDiscreteRange inside rDiscreteRange
|
|
const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
|
|
const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
|
|
|
|
const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
|
|
? 0.0
|
|
: (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
|
|
const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
|
|
? 0.0
|
|
: (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
|
|
|
|
const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
|
|
? 1.0
|
|
: (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
|
|
const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
|
|
? 1.0
|
|
: (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
|
|
|
|
rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
|
|
}
|
|
}
|
|
|
|
void ScenePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
|
|
{
|
|
// create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
|
|
// there are some or not. Do this at start, the shadow might still be visible even when the scene is not
|
|
if(impGetShadow3D())
|
|
{
|
|
// test visibility
|
|
const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
|
|
const basegfx::B2DRange aViewRange(
|
|
rViewInformation.getViewport());
|
|
|
|
if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
|
|
{
|
|
// add extracted 2d shadows (before 3d scene creations itself)
|
|
rContainer.append(maShadowPrimitives);
|
|
}
|
|
}
|
|
|
|
// get the involved ranges (see helper method calculateDiscreteSizes for details)
|
|
basegfx::B2DRange aDiscreteRange;
|
|
basegfx::B2DRange aVisibleDiscreteRange;
|
|
basegfx::B2DRange aUnitVisibleRange;
|
|
|
|
calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
|
|
|
|
if(aVisibleDiscreteRange.isEmpty())
|
|
return;
|
|
|
|
// test if discrete view size (pixel) maybe too big and limit it
|
|
double fViewSizeX(aVisibleDiscreteRange.getWidth());
|
|
double fViewSizeY(aVisibleDiscreteRange.getHeight());
|
|
const double fViewVisibleArea(fViewSizeX * fViewSizeY);
|
|
const double fMaximumVisibleArea(officecfg::Office::Common::Drawinglayer::Quadratic3DRenderLimit::get());
|
|
double fReduceFactor(1.0);
|
|
|
|
if(fViewVisibleArea > fMaximumVisibleArea)
|
|
{
|
|
fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
|
|
fViewSizeX *= fReduceFactor;
|
|
fViewSizeY *= fReduceFactor;
|
|
}
|
|
|
|
if(rViewInformation.getReducedDisplayQuality())
|
|
{
|
|
// when reducing the visualisation is allowed (e.g. an OverlayObject
|
|
// only needed for dragging), reduce resolution extra
|
|
// to speed up dragging interactions
|
|
const double fArea(fViewSizeX * fViewSizeY);
|
|
if (fArea != 0.0)
|
|
{
|
|
double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
|
|
|
|
if(fReducedVisualisationFactor > 1.0)
|
|
{
|
|
fReducedVisualisationFactor = 1.0;
|
|
}
|
|
else if(fReducedVisualisationFactor < 0.20)
|
|
{
|
|
fReducedVisualisationFactor = 0.20;
|
|
}
|
|
|
|
if(fReducedVisualisationFactor != 1.0)
|
|
{
|
|
fReduceFactor *= fReducedVisualisationFactor;
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine the oversample value
|
|
static const sal_uInt16 nDefaultOversampleValue(3);
|
|
sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0);
|
|
|
|
geometry::ViewInformation3D aViewInformation3D(getViewInformation3D());
|
|
{
|
|
// calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
|
|
// Start with full transformation from object to discrete units
|
|
basegfx::B2DHomMatrix aObjToUnit(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
|
|
|
|
// bring to unit coordinates by applying inverse DiscreteRange
|
|
aObjToUnit.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY());
|
|
if (aDiscreteRange.getWidth() != 0.0 && aDiscreteRange.getHeight() != 0.0)
|
|
{
|
|
aObjToUnit.scale(1.0 / aDiscreteRange.getWidth(), 1.0 / aDiscreteRange.getHeight());
|
|
}
|
|
|
|
// calculate transformed user coordinate system
|
|
const basegfx::B2DPoint aStandardNull(0.0, 0.0);
|
|
const basegfx::B2DPoint aUnitRangeTopLeft(aObjToUnit * aStandardNull);
|
|
const basegfx::B2DVector aStandardXAxis(1.0, 0.0);
|
|
const basegfx::B2DVector aUnitRangeXAxis(aObjToUnit * aStandardXAxis);
|
|
const basegfx::B2DVector aStandardYAxis(0.0, 1.0);
|
|
const basegfx::B2DVector aUnitRangeYAxis(aObjToUnit * aStandardYAxis);
|
|
|
|
if(!aUnitRangeTopLeft.equal(aStandardNull) || !aUnitRangeXAxis.equal(aStandardXAxis) || !aUnitRangeYAxis.equal(aStandardYAxis))
|
|
{
|
|
// build transformation from unit range to user coordinate system; the unit range
|
|
// X and Y axes are the column vectors, the null point is the offset
|
|
basegfx::B2DHomMatrix aUnitRangeToUser;
|
|
|
|
aUnitRangeToUser.set3x2(
|
|
aUnitRangeXAxis.getX(), aUnitRangeYAxis.getX(), aUnitRangeTopLeft.getX(),
|
|
aUnitRangeXAxis.getY(), aUnitRangeYAxis.getY(), aUnitRangeTopLeft.getY());
|
|
|
|
// decompose to allow to apply this to the 3D transformation
|
|
basegfx::B2DVector aScale, aTranslate;
|
|
double fRotate, fShearX;
|
|
aUnitRangeToUser.decompose(aScale, aTranslate, fRotate, fShearX);
|
|
|
|
// apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
|
|
// and not yet flipped in Y
|
|
basegfx::B3DHomMatrix aExtendedProjection(aViewInformation3D.getProjection());
|
|
|
|
// bring to unit coordinates, flip Y, leave Z unchanged
|
|
aExtendedProjection.scale(0.5, -0.5, 1.0);
|
|
aExtendedProjection.translate(0.5, 0.5, 0.0);
|
|
|
|
// apply extra; Y is flipped now, go with positive shear and rotate values
|
|
aExtendedProjection.scale(aScale.getX(), aScale.getY(), 1.0);
|
|
aExtendedProjection.shearXZ(fShearX, 0.0);
|
|
aExtendedProjection.rotate(0.0, 0.0, fRotate);
|
|
aExtendedProjection.translate(aTranslate.getX(), aTranslate.getY(), 0.0);
|
|
|
|
// back to state after projection
|
|
aExtendedProjection.translate(-0.5, -0.5, 0.0);
|
|
aExtendedProjection.scale(2.0, -2.0, 1.0);
|
|
|
|
aViewInformation3D = geometry::ViewInformation3D(
|
|
aViewInformation3D.getObjectTransformation(),
|
|
aViewInformation3D.getOrientation(),
|
|
aExtendedProjection,
|
|
aViewInformation3D.getDeviceToView(),
|
|
aViewInformation3D.getViewTime(),
|
|
aViewInformation3D.getExtendedInformationSequence());
|
|
}
|
|
}
|
|
|
|
// calculate logic render size in world coordinates for usage in renderer
|
|
const basegfx::B2DHomMatrix& aInverseOToV(rViewInformation.getInverseObjectToViewTransformation());
|
|
const double fLogicX((aInverseOToV * basegfx::B2DVector(aDiscreteRange.getWidth() * fReduceFactor, 0.0)).getLength());
|
|
const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength());
|
|
|
|
// generate ViewSizes
|
|
double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength());
|
|
double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength());
|
|
|
|
// generate RasterWidth and RasterHeight for visible part
|
|
sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1);
|
|
sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1);
|
|
|
|
if(!rViewInformation.getReducedDisplayQuality() && comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
// for this purpose allow reduced 3D quality and make a compromise
|
|
// between quality and speed. This is balanced between those two
|
|
// targets, fine-tuning/experimenting can be done with the values
|
|
// below.
|
|
|
|
// define some values which allow fine-tuning this feature
|
|
static const double fMin(80.0);
|
|
static const double fSqareMin(fMin * fMin);
|
|
static const double fMax(800.0);
|
|
static const double fSqareMax(fMax * fMax);
|
|
static const double fMaxReduction(0.65);
|
|
|
|
// get the square pixels (work on pixel numbers to get same
|
|
// behaviour independent of width/height relations)
|
|
const double fSquarePixels(nRasterWidth * nRasterHeight);
|
|
|
|
if (fSquarePixels > fSqareMin)
|
|
{
|
|
// only reduce at all when more than fSqareMin pixels needed
|
|
double fReduction(fMaxReduction);
|
|
|
|
if (fSquarePixels < fSqareMax)
|
|
{
|
|
// range between fSqareMin and fSqareMax, calculate a
|
|
// linear interpolated reduction based on square root
|
|
fReduction = sqrt(fSquarePixels); // [fMin .. fMax]
|
|
fReduction = fReduction - fMin; // [0 .. (fMax - fMin)]
|
|
fReduction = fReduction / (fMax - fMin); // [0 .. 1]
|
|
fReduction = 1.0 - (fReduction * (1.0 - fMaxReduction)); // [1 .. fMaxReduction]
|
|
|
|
// reduce oversampling for this range
|
|
if(nOversampleValue > 2)
|
|
nOversampleValue--;
|
|
}
|
|
else
|
|
{
|
|
// more than fSqareMax pixels, disable oversampling
|
|
nOversampleValue = 0;
|
|
}
|
|
|
|
// adapt needed values to reduction
|
|
nRasterWidth = basegfx::fround(fReduction * nRasterWidth);
|
|
nRasterHeight = basegfx::fround(fReduction * nRasterHeight);
|
|
fFullViewSizeX *= fReduction;
|
|
fFullViewSizeY *= fReduction;
|
|
}
|
|
}
|
|
|
|
if(!(nRasterWidth && nRasterHeight))
|
|
return;
|
|
|
|
// create view unit buffer
|
|
basegfx::BZPixelRaster aBZPixelRaster(
|
|
nOversampleValue ? nRasterWidth * nOversampleValue : nRasterWidth,
|
|
nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight);
|
|
|
|
// check for parallel execution possibilities
|
|
static bool bMultithreadAllowed = false; // loplugin:constvars:ignore
|
|
sal_Int32 nThreadCount(0);
|
|
comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
|
|
|
|
if(bMultithreadAllowed)
|
|
{
|
|
nThreadCount = rThreadPool.getWorkerCount();
|
|
|
|
if(nThreadCount > 1)
|
|
{
|
|
// at least use 10px per processor, so limit number of processors to
|
|
// target pixel size divided by 10 (which might be zero what is okay)
|
|
nThreadCount = std::min(nThreadCount, nRasterHeight / 10);
|
|
}
|
|
}
|
|
|
|
if(nThreadCount > 1)
|
|
{
|
|
class Executor : public comphelper::ThreadTask
|
|
{
|
|
private:
|
|
std::unique_ptr<processor3d::ZBufferProcessor3D> mpZBufferProcessor3D;
|
|
const primitive3d::Primitive3DContainer& mrChildren3D;
|
|
|
|
public:
|
|
explicit Executor(
|
|
std::shared_ptr<comphelper::ThreadTaskTag> const & rTag,
|
|
std::unique_ptr<processor3d::ZBufferProcessor3D> pZBufferProcessor3D,
|
|
const primitive3d::Primitive3DContainer& rChildren3D)
|
|
: comphelper::ThreadTask(rTag),
|
|
mpZBufferProcessor3D(std::move(pZBufferProcessor3D)),
|
|
mrChildren3D(rChildren3D)
|
|
{
|
|
}
|
|
|
|
virtual void doWork() override
|
|
{
|
|
mpZBufferProcessor3D->process(mrChildren3D);
|
|
mpZBufferProcessor3D->finish();
|
|
mpZBufferProcessor3D.reset();
|
|
}
|
|
};
|
|
|
|
const sal_uInt32 nLinesPerThread(aBZPixelRaster.getHeight() / nThreadCount);
|
|
std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
|
|
|
|
for(sal_Int32 a(0); a < nThreadCount; a++)
|
|
{
|
|
std::unique_ptr<processor3d::ZBufferProcessor3D> pNewZBufferProcessor3D(new processor3d::ZBufferProcessor3D(
|
|
aViewInformation3D,
|
|
getSdrSceneAttribute(),
|
|
getSdrLightingAttribute(),
|
|
aUnitVisibleRange,
|
|
nOversampleValue,
|
|
fFullViewSizeX,
|
|
fFullViewSizeY,
|
|
aBZPixelRaster,
|
|
nLinesPerThread * a,
|
|
a + 1 == nThreadCount ? aBZPixelRaster.getHeight() : nLinesPerThread * (a + 1)));
|
|
std::unique_ptr<Executor> pExecutor(new Executor(aTag, std::move(pNewZBufferProcessor3D), getChildren3D()));
|
|
rThreadPool.pushTask(std::move(pExecutor));
|
|
}
|
|
|
|
rThreadPool.waitUntilDone(aTag);
|
|
}
|
|
else
|
|
{
|
|
// use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
|
|
processor3d::ZBufferProcessor3D aZBufferProcessor3D(
|
|
aViewInformation3D,
|
|
getSdrSceneAttribute(),
|
|
getSdrLightingAttribute(),
|
|
aUnitVisibleRange,
|
|
nOversampleValue,
|
|
fFullViewSizeX,
|
|
fFullViewSizeY,
|
|
aBZPixelRaster,
|
|
0,
|
|
aBZPixelRaster.getHeight());
|
|
|
|
aZBufferProcessor3D.process(getChildren3D());
|
|
aZBufferProcessor3D.finish();
|
|
}
|
|
|
|
const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmapEx(aBZPixelRaster, nOversampleValue);
|
|
const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
|
|
|
|
if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight()))
|
|
return;
|
|
|
|
// create transform for the created bitmap in discrete coordinates first.
|
|
basegfx::B2DHomMatrix aNew2DTransform;
|
|
|
|
aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
|
|
aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
|
|
aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
|
|
aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
|
|
|
|
// transform back to world coordinates for usage in primitive creation
|
|
aNew2DTransform *= aInverseOToV;
|
|
|
|
// create bitmap primitive and add
|
|
rContainer.push_back(
|
|
new BitmapPrimitive2D(
|
|
maOldRenderedBitmap,
|
|
aNew2DTransform));
|
|
|
|
// test: Allow to add an outline in the debugger when tests are needed
|
|
static bool bAddOutlineToCreated3DSceneRepresentation(false); // loplugin:constvars:ignore
|
|
|
|
if(bAddOutlineToCreated3DSceneRepresentation)
|
|
{
|
|
basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
|
|
aOutline.transform(aNew2DTransform);
|
|
rContainer.push_back(new PolygonHairlinePrimitive2D(std::move(aOutline), basegfx::BColor(1.0, 0.0, 0.0)));
|
|
}
|
|
}
|
|
|
|
Primitive2DContainer ScenePrimitive2D::getGeometry2D() const
|
|
{
|
|
Primitive2DContainer aRetval;
|
|
|
|
// create 2D projected geometry from 3D geometry
|
|
if(!getChildren3D().empty())
|
|
{
|
|
// create 2D geometry extraction processor
|
|
processor3d::Geometry2DExtractingProcessor aGeometryProcessor(
|
|
getViewInformation3D(),
|
|
getObjectTransformation());
|
|
|
|
// process local primitives
|
|
aGeometryProcessor.process(getChildren3D());
|
|
|
|
// fetch result
|
|
aRetval = aGeometryProcessor.getPrimitive2DSequence();
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
Primitive2DContainer ScenePrimitive2D::getShadow2D() const
|
|
{
|
|
Primitive2DContainer aRetval;
|
|
|
|
// create 2D shadows from contained 3D primitives
|
|
if(impGetShadow3D())
|
|
{
|
|
// add extracted 2d shadows (before 3d scene creations itself)
|
|
aRetval = maShadowPrimitives;
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
|
|
{
|
|
if(maOldRenderedBitmap.IsEmpty() || maOldUnitVisiblePart.isEmpty())
|
|
return false;
|
|
|
|
basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
|
|
aInverseSceneTransform.invert();
|
|
const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
|
|
|
|
if(!maOldUnitVisiblePart.isInside(aRelativePoint))
|
|
return false;
|
|
|
|
// calculate coordinates relative to visualized part
|
|
double fDivisorX(maOldUnitVisiblePart.getWidth());
|
|
double fDivisorY(maOldUnitVisiblePart.getHeight());
|
|
|
|
if(basegfx::fTools::equalZero(fDivisorX))
|
|
{
|
|
fDivisorX = 1.0;
|
|
}
|
|
|
|
if(basegfx::fTools::equalZero(fDivisorY))
|
|
{
|
|
fDivisorY = 1.0;
|
|
}
|
|
|
|
const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
|
|
const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
|
|
|
|
// combine with real BitmapSizePixel to get bitmap coordinates
|
|
const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
|
|
const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
|
|
const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
|
|
|
|
// try to get a statement about transparency in that pixel
|
|
o_rResult = (0 != maOldRenderedBitmap.GetAlpha(nX, nY));
|
|
return true;
|
|
}
|
|
|
|
ScenePrimitive2D::ScenePrimitive2D(
|
|
primitive3d::Primitive3DContainer aChildren3D,
|
|
attribute::SdrSceneAttribute aSdrSceneAttribute,
|
|
attribute::SdrLightingAttribute aSdrLightingAttribute,
|
|
basegfx::B2DHomMatrix aObjectTransformation,
|
|
geometry::ViewInformation3D aViewInformation3D)
|
|
: BufferedDecompositionPrimitive2D(),
|
|
mxChildren3D(std::move(aChildren3D)),
|
|
maSdrSceneAttribute(std::move(aSdrSceneAttribute)),
|
|
maSdrLightingAttribute(std::move(aSdrLightingAttribute)),
|
|
maObjectTransformation(std::move(aObjectTransformation)),
|
|
maViewInformation3D(std::move(aViewInformation3D)),
|
|
mbShadow3DChecked(false),
|
|
mfOldDiscreteSizeX(0.0),
|
|
mfOldDiscreteSizeY(0.0)
|
|
{
|
|
// activate callback to flush buffered decomposition content
|
|
setCallbackSeconds(45);
|
|
}
|
|
|
|
bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
|
|
{
|
|
if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
|
|
{
|
|
const ScenePrimitive2D& rCompare = static_cast<const ScenePrimitive2D&>(rPrimitive);
|
|
|
|
return (getChildren3D() == rCompare.getChildren3D()
|
|
&& getSdrSceneAttribute() == rCompare.getSdrSceneAttribute()
|
|
&& getSdrLightingAttribute() == rCompare.getSdrLightingAttribute()
|
|
&& getObjectTransformation() == rCompare.getObjectTransformation()
|
|
&& getViewInformation3D() == rCompare.getViewInformation3D());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
basegfx::B2DRange ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
|
|
{
|
|
// transform unit range to discrete coordinate range
|
|
basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
|
|
aRetval.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
|
|
|
|
// force to discrete expanded bounds (it grows, so expanding works perfectly well)
|
|
aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
|
|
aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
|
|
|
|
// transform back from discrete (view) to world coordinates
|
|
aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
|
|
|
|
// expand by evtl. existing shadow primitives
|
|
if(impGetShadow3D())
|
|
{
|
|
const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
|
|
|
|
if(!aShadow2DRange.isEmpty())
|
|
{
|
|
aRetval.expand(aShadow2DRange);
|
|
}
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
|
|
{
|
|
// get the involved ranges (see helper method calculateDiscreteSizes for details)
|
|
basegfx::B2DRange aDiscreteRange;
|
|
basegfx::B2DRange aUnitVisibleRange;
|
|
bool bNeedNewDecomposition(false);
|
|
bool bDiscreteSizesAreCalculated(false);
|
|
|
|
if(!getBuffered2DDecomposition().empty())
|
|
{
|
|
basegfx::B2DRange aVisibleDiscreteRange;
|
|
calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
|
|
bDiscreteSizesAreCalculated = true;
|
|
|
|
// needs to be painted when the new part is not part of the last
|
|
// decomposition
|
|
if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
|
|
{
|
|
bNeedNewDecomposition = true;
|
|
}
|
|
|
|
// display has changed and cannot be reused when resolution got bigger. It
|
|
// can be reused when resolution got smaller, though.
|
|
if(!bNeedNewDecomposition)
|
|
{
|
|
if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
|
|
basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
|
|
{
|
|
bNeedNewDecomposition = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bNeedNewDecomposition)
|
|
{
|
|
// conditions of last local decomposition have changed, delete
|
|
const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
|
|
}
|
|
|
|
if(getBuffered2DDecomposition().empty())
|
|
{
|
|
if(!bDiscreteSizesAreCalculated)
|
|
{
|
|
basegfx::B2DRange aVisibleDiscreteRange;
|
|
calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
|
|
}
|
|
|
|
// remember last used NewDiscreteSize and NewUnitVisiblePart
|
|
ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
|
|
pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
|
|
pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
|
|
pThat->maOldUnitVisiblePart = aUnitVisibleRange;
|
|
}
|
|
|
|
// use parent implementation
|
|
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
|
|
}
|
|
|
|
// provide unique ID
|
|
sal_uInt32 ScenePrimitive2D::getPrimitive2DID() const
|
|
{
|
|
return PRIMITIVE2D_ID_SCENEPRIMITIVE2D;
|
|
}
|
|
|
|
} // end of namespace
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|