summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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