summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2022-09-15 15:50:03 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2022-09-19 09:56:19 +0200
commit7614859e5738cdf3789f3f99cc4379f2333e8870 (patch)
tree4841696113861d0fcdc94ef8740b31dcc23a1aab
parent4973de104da87e522fe1f485984c081bc0be1b36 (diff)
Make impBufferDevice faster again
When the Primitives for the Glow-Effects were added (modified ShadowPrimitive2D, SoftEdgePrimitive2D and GlowPrimitive2D) a modified version of impBufferDevice was created and used. That lowered the speed for drawing objects with transparence by about factor 2.5 and was unfortunately not only done for these Primitives, but for transprent objects in general. For the mentioned factor refer to: Patch to demonstrate former and now repaint differences https://gerrit.libreoffice.org/c/core/+/129301 After having reworked those Primitives to use another mechanism and being decomposed so they will work in all now and future renderers, it is possible to go back to that easier and faster method to render Transparency. For extended information, please take a look at the added comments, mainly in vclhelperbufferdevice.hxx Identified a still bad behaviour when objects use a TransparenceGradient. Corrected that and added (at this opportunity) a method 'createAlphaMask' along with 'convertToBitmapEx' which is now used in the GlowPrimitive2D & ShadowPrimitive2D which only need the AlphaMask to do their job anyways (I had commented there that thjis is possible before). That will be faster for visualizing those Primitives. Change-Id: Ieac880384de26960c2c4b8740a1dee1e15d7ac9e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140022 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx24
-rw-r--r--drawinglayer/source/primitive2d/shadowprimitive2d.cxx22
-rw-r--r--drawinglayer/source/processor2d/vclhelperbufferdevice.cxx218
-rw-r--r--drawinglayer/source/processor2d/vclhelperbufferdevice.hxx75
-rw-r--r--drawinglayer/source/tools/converters.cxx127
-rw-r--r--include/drawinglayer/converters.hxx12
-rw-r--r--solenv/clang-format/excludelist2
7 files changed, 395 insertions, 85 deletions
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index 44a97c536fb2..956a6f94eb82 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -155,24 +155,22 @@ void GlowPrimitive2D::create2DDecomposition(
// limitation to be safe and not go runtime/memory havoc. Use a pretty small
// limit due to this is glow functionality and will look good with bitmap scaling
// anyways. The value of 250.000 square pixels below maybe adapted as needed.
- // NOTE: This may be further optimized. Only the alpha channel is needed, so
- // convertToBitmapEx may be split in tooling to have a version that only
- // creates the alpha channel. Potential win is >50% for the alpha pixel
- // creation step ('>' because alpha painting uses a ColorStack and thus
- // often can used simplified rendering)
const basegfx::B2DVector aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation()
* aClippedRange.getRange());
const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX()));
const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY()));
const geometry::ViewInformation2D aViewInformation2D;
const sal_uInt32 nMaximumQuadraticPixels(250000);
- const BitmapEx aBitmapEx(::drawinglayer::convertToBitmapEx(
+
+ // I have now added a helper that just creates the mask without having
+ // to render the content, use it, it's faster
+ const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
nMaximumQuadraticPixels));
- if (!aBitmapEx.IsEmpty())
+ if (!aAlpha.IsEmpty())
{
- const Size& rBitmapExSizePixel(aBitmapEx.GetSizePixel());
+ const Size& rBitmapExSizePixel(aAlpha.GetSizePixel());
if (rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 0)
{
@@ -183,7 +181,7 @@ void GlowPrimitive2D::create2DDecomposition(
if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
|| static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
{
- // scale in X and Y should be the same (see fReduceFactor in convertToBitmapEx),
+ // scale in X and Y should be the same (see fReduceFactor in createAlphaMask),
// so adapt numerically to a single scale value, they are integer rounded values
const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
/ static_cast<double>(nDiscreteClippedWidth));
@@ -198,12 +196,12 @@ void GlowPrimitive2D::create2DDecomposition(
// When blurring a sharp boundary (our case), it gets 50% of original intensity, and
// fades to both sides by the blur radius; thus blur radius is half of glow radius.
// Consider glow transparency (initial transparency near the object edge)
- const AlphaMask mask(ProcessAndBlurAlphaMask(
- aBitmapEx.GetAlpha(), fDiscreteGlowRadius * fScale / 2.0,
- fDiscreteGlowRadius * fScale / 2.0, 255 - getGlowColor().GetAlpha()));
+ const AlphaMask mask(ProcessAndBlurAlphaMask(aAlpha, fDiscreteGlowRadius * fScale / 2.0,
+ fDiscreteGlowRadius * fScale / 2.0,
+ 255 - getGlowColor().GetAlpha()));
// The end result is the bitmap filled with glow color and blurred 8-bit alpha mask
- Bitmap bmp = aBitmapEx.GetBitmap();
+ Bitmap bmp(aAlpha.GetSizePixel(), vcl::PixelFormat::N24_BPP);
bmp.Erase(getGlowColor());
BitmapEx result(bmp, mask);
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index 299b6af625ab..67188d8358af 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -205,26 +205,24 @@ void ShadowPrimitive2D::create2DDecomposition(
// limitation to be safe and not go runtime/memory havoc. Use a pretty small
// limit due to this is Blurred Shadow functionality and will look good with bitmap
// scaling anyways. The value of 250.000 square pixels below maybe adapted as needed.
- // NOTE: This may be further optimized. Only the alpha channel is needed, so
- // convertToBitmapEx may be split in tooling to have a version that only
- // creates the alpha channel. Potential win is >50% for the alpha pixel
- // creation step ('>' because alpha painting uses a ColorStack and thus
- // often can used simplified rendering)
const basegfx::B2DVector aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation()
* aClippedRange.getRange());
const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX()));
const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY()));
const geometry::ViewInformation2D aViewInformation2D;
const sal_uInt32 nMaximumQuadraticPixels(250000);
- const BitmapEx aBitmapEx(::drawinglayer::convertToBitmapEx(
+
+ // I have now added a helper that just creates the mask without having
+ // to render the content, use it, it's faster
+ const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
nMaximumQuadraticPixels));
// if we have no shadow, we are done
- if (aBitmapEx.IsEmpty())
+ if (aAlpha.IsEmpty())
return;
- const Size& rBitmapExSizePixel(aBitmapEx.GetSizePixel());
+ const Size& rBitmapExSizePixel(aAlpha.GetSizePixel());
if (!(rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 0))
return;
@@ -235,7 +233,7 @@ void ShadowPrimitive2D::create2DDecomposition(
if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
|| static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
{
- // scale in X and Y should be the same (see fReduceFactor in convertToBitmapEx),
+ // scale in X and Y should be the same (see fReduceFactor in createAlphaMask),
// so adapt numerically to a single scale value, they are integer rounded values
const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
/ static_cast<double>(nDiscreteClippedWidth));
@@ -245,12 +243,12 @@ void ShadowPrimitive2D::create2DDecomposition(
fScale = (fScaleX + fScaleY) * 0.5;
}
- // Get the Alpha and use as base to blur and apply the effect
+ // Use the Alpha as base to blur and apply the effect
const AlphaMask mask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
- aBitmapEx.GetAlpha(), 0, fDiscreteBlurRadius * fScale, 0, false));
+ aAlpha, 0, fDiscreteBlurRadius * fScale, 0, false));
// The end result is the bitmap filled with blur color and blurred 8-bit alpha mask
- Bitmap bmp = aBitmapEx.GetBitmap();
+ Bitmap bmp(aAlpha.GetSizePixel(), vcl::PixelFormat::N24_BPP);
bmp.Erase(Color(getShadowColor()));
BitmapEx result(bmp, mask);
diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
index a0807e83f72b..fbb375417be3 100644
--- a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
@@ -28,15 +28,23 @@
#include <basegfx/range/b2drange.hxx>
#include <vcl/bitmapex.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
-#include <tools/stream.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <vcl/timer.hxx>
#include <vcl/lazydelete.hxx>
#include <vcl/dibtools.hxx>
#include <vcl/skia/SkiaHelper.hxx>
#include <mutex>
-// buffered VDev usage
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#endif
+
+// #define SPEED_COMPARE
+#ifdef SPEED_COMPARE
+#include <tools/time.hxx>
+#endif
+// buffered VDev usage
namespace
{
class VDevBuffer : public Timer
@@ -45,10 +53,8 @@ private:
struct Entry
{
VclPtr<VirtualDevice> buf;
- bool isTransparent = false;
- Entry(const VclPtr<VirtualDevice>& vdev, bool bTransparent)
+ Entry(const VclPtr<VirtualDevice>& vdev)
: buf(vdev)
- , isTransparent(bTransparent)
{
}
};
@@ -72,7 +78,7 @@ public:
VDevBuffer();
virtual ~VDevBuffer() override;
- VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bTransparent);
+ VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel);
void free(VirtualDevice& rDevice);
// Timer virtuals
@@ -131,8 +137,7 @@ bool VDevBuffer::isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size&
return false;
}
-VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel,
- bool bTransparent)
+VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel)
{
std::unique_lock aGuard(m_aMutex);
VclPtr<VirtualDevice> pRetval;
@@ -148,7 +153,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
{
assert(a->buf && "Empty pointer in VDevBuffer (!)");
- if (nBits == a->buf->GetBitCount() && bTransparent == a->isTransparent)
+ if (nBits == a->buf->GetBitCount())
{
// candidate is valid due to bit depth
if (aFound != maFreeBuffers.end())
@@ -229,9 +234,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
// no success yet, create new buffer
if (!pRetval)
{
- pRetval = VclPtr<VirtualDevice>::Create(rOutDev, DeviceFormat::DEFAULT,
- bTransparent ? DeviceFormat::DEFAULT
- : DeviceFormat::NONE);
+ pRetval = VclPtr<VirtualDevice>::Create(rOutDev, DeviceFormat::DEFAULT);
maDeviceTemplates[pRetval] = &rOutDev;
pRetval->SetOutputSizePixel(rSizePixel, true);
}
@@ -243,7 +246,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
}
// remember allocated buffer
- maUsedBuffers.emplace_back(pRetval, bTransparent);
+ maUsedBuffers.emplace_back(pRetval);
return pRetval;
}
@@ -278,13 +281,103 @@ void VDevBuffer::Invoke()
maFreeBuffers.pop_back();
}
}
+
+#ifdef SPEED_COMPARE
+void doSpeedCompare(double fTrans, const Bitmap& rContent, const tools::Rectangle& rDestPixel,
+ OutputDevice& rOutDev)
+{
+ const int nAvInd(500);
+ static double fFactors[nAvInd];
+ static int nIndex(nAvInd + 1);
+ static int nRepeat(5);
+ static int nWorseTotal(0);
+ static int nBetterTotal(0);
+ int a(0);
+
+ const Size aSizePixel(rDestPixel.GetSize());
+
+ // init statics
+ if (nIndex > nAvInd)
+ {
+ for (a = 0; a < nAvInd; a++)
+ fFactors[a] = 1.0;
+ nIndex = 0;
+ }
+
+ // get start time
+ const sal_uInt64 nTimeA(tools::Time::GetSystemTicks());
+
+ // loop nRepeat times to get somewhat better timings, else
+ // numbers are pretty small
+ for (a = 0; a < nRepeat; a++)
+ {
+ // "Former" method using a temporary AlphaMask & DrawBitmapEx
+ sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0)));
+ const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
+ rOutDev.DrawBitmapEx(rDestPixel.TopLeft(), BitmapEx(rContent, aAlphaMask));
+ }
+
+ // get intermediate time
+ const sal_uInt64 nTimeB(tools::Time::GetSystemTicks());
+
+ // loop nRepeat times
+ for (a = 0; a < nRepeat; a++)
+ {
+ // New method using DrawTransformedBitmapEx & fTrans directly
+ rOutDev.DrawTransformedBitmapEx(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aSizePixel.Width(), aSizePixel.Height(),
+ rDestPixel.TopLeft().X(), rDestPixel.TopLeft().Y()),
+ BitmapEx(rContent), 1 - fTrans);
+ }
+
+ // get end time
+ const sal_uInt64 nTimeC(tools::Time::GetSystemTicks());
+
+ // calculate deltas
+ const sal_uInt64 nTimeFormer(nTimeB - nTimeA);
+ const sal_uInt64 nTimeNew(nTimeC - nTimeB);
+
+ // compare & note down
+ if (nTimeFormer != nTimeNew && 0 != nTimeFormer && 0 != nTimeNew)
+ {
+ if ((nTimeFormer < 10 || nTimeNew < 10) && nRepeat < 500)
+ {
+ nRepeat += 1;
+ SAL_INFO("drawinglayer.processor2d", "Increment nRepeat to " << nRepeat);
+ return;
+ }
+
+ const double fNewFactor((double)nTimeFormer / nTimeNew);
+ fFactors[nIndex % nAvInd] = fNewFactor;
+ nIndex++;
+ double fAverage(0.0);
+ {
+ for (a = 0; a < nAvInd; a++)
+ fAverage += fFactors[a];
+ fAverage /= nAvInd;
+ }
+ if (fNewFactor < 1.0)
+ nWorseTotal++;
+ else
+ nBetterTotal++;
+
+ char buf[300];
+ sprintf(buf,
+ "Former: %ld New: %ld It got %s (factor %f) (av. last %d Former/New is %f, "
+ "WorseTotal: %d, BetterTotal: %d)",
+ nTimeFormer, nTimeNew, fNewFactor < 1.0 ? "WORSE" : "BETTER",
+ fNewFactor < 1.0 ? 1.0 / fNewFactor : fNewFactor, nAvInd, fAverage, nWorseTotal,
+ nBetterTotal);
+ SAL_INFO("drawinglayer.processor2d", buf);
+ }
+}
+#endif
}
// support for rendering Bitmap and BitmapEx contents
-
namespace drawinglayer
{
-// static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
+// static global VDev buffer for VclProcessor2D/VclPixelProcessor2D
VDevBuffer& getVDevBuffer()
{
// secure global instance with Vcl's safe destroyer of external (seen by
@@ -294,7 +387,7 @@ VDevBuffer& getVDevBuffer()
return *aVDevBuffer.get();
}
-impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange, bool bCrop)
+impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange)
: mrOutDev(rOutDev)
, mpContent(nullptr)
, mpAlpha(nullptr)
@@ -303,19 +396,26 @@ impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange&
aRangePixel.transform(mrOutDev.GetViewTransformation());
maDestPixel = tools::Rectangle(floor(aRangePixel.getMinX()), floor(aRangePixel.getMinY()),
ceil(aRangePixel.getMaxX()), ceil(aRangePixel.getMaxY()));
- if (bCrop)
- maDestPixel.Intersection({ {}, mrOutDev.GetOutputSizePixel() });
+ maDestPixel.Intersection({ {}, mrOutDev.GetOutputSizePixel() });
if (!isVisible())
return;
- mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true);
+ mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize());
// #i93485# assert when copying from window to VDev is used
SAL_WARN_IF(
mrOutDev.GetOutDevType() == OUTDEV_WINDOW, "drawinglayer",
"impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
+ // initialize buffer by blitting content of source to prepare for
+ // transparence/ copying back
+ const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
+ mrOutDev.EnableMapMode(false);
+ mpContent->DrawOutDev(Point(), maDestPixel.GetSize(), maDestPixel.TopLeft(),
+ maDestPixel.GetSize(), mrOutDev);
+ mrOutDev.EnableMapMode(bWasEnabledSrc);
+
MapMode aNewMapMode(mrOutDev.GetMapMode());
const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
@@ -351,15 +451,13 @@ void impBufferDevice::paint(double fTrans)
const Point aEmptyPoint;
const Size aSizePixel(maDestPixel.GetSize());
const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
-#ifdef DBG_UTIL
- static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
-#endif
mrOutDev.EnableMapMode(false);
mpContent->EnableMapMode(false);
#ifdef DBG_UTIL
// VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
static const OUString sDumpPath(OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
if (!sDumpPath.isEmpty() && bDoSaveForVisualControl)
@@ -388,17 +486,75 @@ void impBufferDevice::paint(double fTrans)
}
#endif
- BitmapEx aContent(mpContent->GetBitmapEx(aEmptyPoint, aSizePixel));
- aAlphaMask.BlendWith(aContent.GetAlpha());
- mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent.GetBitmap(), aAlphaMask));
+ Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
+ mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
}
else if (0.0 != fTrans)
{
- basegfx::B2DHomMatrix trans, scale;
- trans.translate(maDestPixel.TopLeft().X(), maDestPixel.TopLeft().Y());
- scale.scale(aSizePixel.Width(), aSizePixel.Height());
- mrOutDev.DrawTransformedBitmapEx(
- trans * scale, mpContent->GetBitmapEx(aEmptyPoint, aSizePixel), 1 - fTrans);
+ const Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
+
+#ifdef SPEED_COMPARE
+ static bool bCompareFormerAndNewTimings(true);
+
+ if (bCompareFormerAndNewTimings)
+ {
+ doSpeedCompare(fTrans, aContent, maDestPixel, mrOutDev);
+ }
+ else
+#endif
+ // Note: this extra scope is needed due to 'clang plugin indentation'. It complains
+ // that lines 494 and (now) 539 are 'statement mis-aligned compared to neighbours'.
+ // That is true if SPEED_COMPARE is not defined. Not nice, but have to fix this.
+ {
+ // For the case we have a unified transparency value there is a former
+ // and new method to paint that which can be used. To decide on measurements,
+ // I added 'doSpeedCompare' above which can be activated by defining
+ // SPEED_COMPARE at the top of this file.
+ // I added the used Testdoc: blurplay3.odg as
+ // https://bugs.documentfoundation.org/attachment.cgi?id=182463
+ // I did measure on
+ //
+ // Linux Dbg:
+ // Former: 21 New: 32 It got WORSE (factor 1.523810) (av. last 500 Former/New is 0.968533, WorseTotal: 515, BetterTotal: 934)
+ //
+ // Linux Pro:
+ // Former: 27 New: 44 It got WORSE (factor 1.629630) (av. last 500 Former/New is 0.923256, WorseTotal: 433, BetterTotal: 337)
+ //
+ // Win Dbg:
+ // Former: 21 New: 78 It got WORSE (factor 3.714286) (av. last 500 Former/New is 1.007176, WorseTotal: 85, BetterTotal: 1428)
+ //
+ // Win Pro:
+ // Former: 3 New: 4 It got WORSE (factor 1.333333) (av. last 500 Former/New is 1.054167, WorseTotal: 143, BetterTotal: 3909)
+ //
+ // Note: I am aware that the Dbg are of limited usefulness, but include them here
+ // for reference.
+ //
+ // The important part is "av. last 500 Former/New is %ld" which decribes the averaged factor from Former/New
+ // over the last 500 measurements. When < 1.0 Former is better (Linux), > 1.0 (Win) New is better. Since the
+ // factor on Win is still close to 1.0 what means we lose nearly nothing and Linux Former is better, I will
+ // use Former for now.
+ //
+ // To easily allow to change this (maybe system-dependent) I add a static switch here,
+ // also for evetually experimenting (hint: can be changed in the debugger).
+ static bool bUseNew(false);
+
+ if (bUseNew)
+ {
+ // New method using DrawTransformedBitmapEx & fTrans directly
+ mrOutDev.DrawTransformedBitmapEx(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aSizePixel.Width(), aSizePixel.Height(),
+ maDestPixel.TopLeft().X(),
+ maDestPixel.TopLeft().Y()),
+ BitmapEx(aContent), 1 - fTrans);
+ }
+ else
+ {
+ // "Former" method using a temporary AlphaMask & DrawBitmapEx
+ sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0)));
+ const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
+ mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
+ }
+ }
}
else
{
@@ -422,7 +578,7 @@ VirtualDevice& impBufferDevice::getTransparence()
"impBufferDevice: No content, check isVisible() before accessing (!)");
if (!mpAlpha)
{
- mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false);
+ mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize());
mpAlpha->SetMapMode(mpContent->GetMapMode());
// copy AA flag for new target; masking needs to be smooth
diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
index 99585b05b141..f893ec79bb93 100644
--- a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
@@ -21,12 +21,73 @@
#include <vcl/virdev.hxx>
-namespace basegfx
-{
-class B2DRange;
-}
-
-// support methods for vcl direct gradient rendering
+// Helper class *exclusively* for VclProcessor2D. It should only
+// be used internally, see current four usages. It is used to
+// render something with mask or transparence (see MaskPrimitive2D,
+// UnifiedTransparencePrimitive2D and TransparencePrimitive2D) or
+// as tooling for preparing pixelized output in the renderer
+// (see PatternFillPrimitive2D) if that is faster.
+//
+// To do so, initializing this instance takes over a lot of work
+// from you:
+// - It initializes a 'Content' VDev which is buffered (since it had
+// shown that re-allocating all the time is slower). It checks
+// visibility and all usages initializing this should check for
+// isVisible() after contruction.
+// - It pre-initializes the 'Content' VDev with blitting the content
+// of the target VDev.
+// - It offers to get a 'Transparence' VDev (also from the buffer) if
+// needed.
+//
+// If 'Transparence' is/was used, it combines as needed to paint
+// all buffered stuff to target VDev when calling paint().
+// Caution: It is designed to use *either* a fixed transparence in
+// the paint()-call *or* a fill TransparenceChannel using a
+// 'Transparence' VDev. It is *not* designed to use/combine
+// both - it's simply not needed for it's intended purpose/usage.
+//
+// Painting transparent works based on a simple principle: It first
+// blits the original content of the target VDev. Then the content
+// is painted on top of that, plus a Transparence/Mask prepared.
+// The combination always works since unchanged pixels will not
+// change, independent of the transparence value [0..255] it gets
+// mixed with. Or the other way around: Only pixels changed on the
+// Content *can* be changed by transparence values.
+//
+// This is 2.5 times faster than first painting to a
+// 'combined' VDev that supports transparency, as is used by the
+// presentation engine.
+// For the mentioned factor refer to:
+// Patch to demonstrate former and now repaint differences
+// https://gerrit.libreoffice.org/c/core/+/129301
+// git fetch https://git.libreoffice.org/core refs/changes/01/129301/3 && git cherry-pick FETCH_HEAD
+//
+// Note: This principle only works when the target is RGB, so
+// useful for EditViews like for PrimitiveRenderers where this is
+// the case. For usage with GBA tragets the starting conditions
+// would have to be modified to not blend against the initial color
+// of 'Content' (usually COL_WHITE).
+//
+// After having finished the rework of ShadowPrimitive2D,
+// SoftEdgePrimitive2D and GlowPrimitive2D (see commits:)
+// e735ad1c57cddaf17d6ffc0cf15b5e14fa63c4ad
+// 707b0c328a282d993fa33b618083d20b6c521de6
+// c2d1458723c66c2fd717a112f89f773226adc841
+// which used the impBufferDevice in such a mode combined with
+// mentioned 'combined' transparence VDev it is now possible
+// to return to this former, much faster method.
+//
+// Please do *not* hack/use this helper class, better create
+// a new one fitting your/the intended purpose. I do not want
+// to see losing performance by this getting modified again.
+//
+// Note: Using that 'combined' transparence VDev is not really
+// recommended, it may vanish anytime. That it works with
+// PrimitiveRenderers *at all* is neither designed nor tested
+// or recommended - it's pure coincidence.
+//
+// I hope that for the future all this will vanish by getting to
+// fully RGBA-capable devices - what is planned and makes sense.
namespace drawinglayer
{
@@ -38,7 +99,7 @@ class impBufferDevice
tools::Rectangle maDestPixel;
public:
- impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange, bool bCrop = true);
+ impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange);
~impBufferDevice();
void paint(double fTrans = 0.0);
diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx
index 3c4ad791a3c3..48a5b404b056 100644
--- a/drawinglayer/source/tools/converters.cxx
+++ b/drawinglayer/source/tools/converters.cxx
@@ -35,40 +35,114 @@
#include <vcl/filter/PngImageWriter.hxx>
#endif
-namespace drawinglayer
+namespace
{
-BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
- const geometry::ViewInformation2D& rViewInformation2D,
- sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
- sal_uInt32 nMaxSquarePixels)
+bool implPrepareConversion(drawinglayer::primitive2d::Primitive2DContainer& rSequence,
+ sal_uInt32& rnDiscreteWidth, sal_uInt32& rnDiscreteHeight,
+ const sal_uInt32 nMaxSquarePixels)
{
- BitmapEx aRetval;
-
- if (rSeq.empty())
- return aRetval;
+ if (rSequence.empty())
+ return false;
- if (nDiscreteWidth <= 0 || nDiscreteHeight <= 0)
- return aRetval;
+ if (rnDiscreteWidth <= 0 || rnDiscreteHeight <= 0)
+ return false;
- // get destination size in pixels
- const MapMode aMapModePixel(MapUnit::MapPixel);
- const sal_uInt32 nViewVisibleArea(nDiscreteWidth * nDiscreteHeight);
- drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
+ const sal_uInt32 nViewVisibleArea(rnDiscreteWidth * rnDiscreteHeight);
if (nViewVisibleArea > nMaxSquarePixels)
{
// reduce render size
double fReduceFactor
= sqrt(static_cast<double>(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
- nDiscreteWidth = basegfx::fround(static_cast<double>(nDiscreteWidth) * fReduceFactor);
- nDiscreteHeight = basegfx::fround(static_cast<double>(nDiscreteHeight) * fReduceFactor);
+ rnDiscreteWidth = basegfx::fround(static_cast<double>(rnDiscreteWidth) * fReduceFactor);
+ rnDiscreteHeight = basegfx::fround(static_cast<double>(rnDiscreteHeight) * fReduceFactor);
const drawinglayer::primitive2d::Primitive2DReference aEmbed(
new drawinglayer::primitive2d::TransformPrimitive2D(
basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor),
- std::move(aSequence)));
+ std::move(rSequence)));
- aSequence = drawinglayer::primitive2d::Primitive2DContainer{ aEmbed };
+ rSequence = drawinglayer::primitive2d::Primitive2DContainer{ aEmbed };
+ }
+
+ return true;
+}
+
+AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& rSequence,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D,
+ const Size& rSizePixel)
+{
+ ScopedVclPtrInstance<VirtualDevice> pContent;
+
+ // prepare vdev
+ if (!pContent->SetOutputSizePixel(rSizePixel, false))
+ {
+ SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << rSizePixel.Width() << "x"
+ << rSizePixel.Height());
+ return AlphaMask();
+ }
+
+ // create pixel processor, also already takes care of AAing and
+ // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If
+ // not wanted, change after this call as needed
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pContentProcessor
+ = drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(*pContent,
+ rViewInformation2D);
+
+ // prepare for mask creation
+ pContent->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // set alpha to all white (fully transparent)
+ pContent->Erase();
+
+ // embed primitives to paint them black
+ const drawinglayer::primitive2d::Primitive2DReference xRef(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ std::move(rSequence),
+ std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0, 0.0, 0.0))));
+ const drawinglayer::primitive2d::Primitive2DContainer xSeq{ xRef };
+
+ // render
+ pContentProcessor->process(xSeq);
+ pContentProcessor.reset();
+
+ // get alpha channel from vdev
+ pContent->EnableMapMode(false);
+ const Point aEmptyPoint;
+ return AlphaMask(pContent->GetBitmap(aEmptyPoint, rSizePixel));
+}
+}
+
+namespace drawinglayer
+{
+AlphaMask createAlphaMask(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D,
+ sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
+ sal_uInt32 nMaxSquarePixels)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
+
+ if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels))
+ {
+ return AlphaMask();
+ }
+
+ const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
+
+ return implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel);
+}
+
+BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D,
+ sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
+ sal_uInt32 nMaxSquarePixels)
+{
+ BitmapEx aRetval;
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
+
+ if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels))
+ {
+ return aRetval;
}
const Point aEmptyPoint;
@@ -99,7 +173,7 @@ BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSe
}
// We map to pixel, use that MapMode. Init by erasing.
- pContent->SetMapMode(aMapModePixel);
+ pContent->SetMapMode(MapMode(MapUnit::MapPixel));
pContent->Erase();
// create pixel processor, also already takes care of AAing and
@@ -131,6 +205,19 @@ BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSe
}
#endif
+ // Combined/mixed usage of VirtualDevice in Alpha-mode here and
+ // the faster method for divided content/alpha in the VclPixelProcessor2D
+ // *can* go wrong e.g. for objects filled with TransparenceGradients,
+ // so unfortunately for now we have to reliably re-create the
+ // AlphaMask using a method that does this always correct (also used
+ // now in GlowPrimitive2D and ShadowPrimitive2D which both only need the
+ // AlphaMask to do their job, so speeding that up, too).
+ // Note: This could be further approved by creating a small ::B2DProcessor
+ // that checks if rSeq contains a TransparenceGradient fill and only then use
+ // this correction.
+ const AlphaMask aAlpha(implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel));
+ aRetval = BitmapEx(aRetval.GetBitmap(), aAlpha);
+
return aRetval;
}
diff --git a/include/drawinglayer/converters.hxx b/include/drawinglayer/converters.hxx
index 1fda7a2f8350..f85bbfdae6f5 100644
--- a/include/drawinglayer/converters.hxx
+++ b/include/drawinglayer/converters.hxx
@@ -24,6 +24,18 @@
namespace drawinglayer
{
+// Helper that just creates the AlphaMask for a given Seq of Primitives.
+// If only the mask is needed this can be significantly faster then
+// creating content & mask in a BitmapEx (since the creation uses
+// e.g. a unified color for gradients instead of having to fully paint
+// these)
+AlphaMask DRAWINGLAYER_DLLPUBLIC
+createAlphaMask(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D, sal_uInt32 nDiscreteWidth,
+ sal_uInt32 nDiscreteHeight, sal_uInt32 nMaxSquarePixels);
+
+// Helper for convertPrimitive2DContainerToBitmapEx below, but can be also used
+// directly
BitmapEx DRAWINGLAYER_DLLPUBLIC
convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
const geometry::ViewInformation2D& rViewInformation2D, sal_uInt32 nDiscreteWidth,
diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist
index d0790424e6c0..c081d5184433 100644
--- a/solenv/clang-format/excludelist
+++ b/solenv/clang-format/excludelist
@@ -3254,7 +3254,6 @@ desktop/win32/source/applauncher/launcher.cxx
desktop/win32/source/loader.cxx
desktop/win32/source/loader.hxx
desktop/win32/source/unoinfo.cxx
-drawinglayer/inc/converters.hxx
drawinglayer/inc/emfplushelper.hxx
drawinglayer/inc/primitive2d/cropprimitive2d.hxx
drawinglayer/inc/primitive2d/graphicprimitivehelper2d.hxx
@@ -3328,7 +3327,6 @@ drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx
drawinglayer/source/primitive2d/primitivetools2d.cxx
drawinglayer/source/primitive2d/sceneprimitive2d.cxx
drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
-drawinglayer/source/primitive2d/shadowprimitive2d.cxx
drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
drawinglayer/source/primitive2d/textbreakuphelper.cxx