summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2022-09-07 13:20:56 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2022-09-08 13:33:58 +0200
commit707b0c328a282d993fa33b618083d20b6c521de6 (patch)
treef73397fe8c30bb9e21997dcd7232da1e210cecba
parent7d9c03fb354af184f35e257f319a70ef3481703a (diff)
Rework of SoftEdgePrimitive2D
This is pretty much the same for SoftEdgePrimitive2D as the change for GlowPrimitive2D, so for more comments please refer to commit c2d1458723c66c2fd717a112f89f773226adc841 Added suggested change of DoSaveForVisualControl mechanism Change-Id: I28901e7a0b6e1823000d2aa6a335ce2fd80e6ce3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139585 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx53
-rw-r--r--drawinglayer/source/primitive2d/softedgeprimitive2d.cxx298
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx1
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx68
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.hxx1
-rw-r--r--drawinglayer/source/tools/converters.cxx316
-rw-r--r--emfio/source/reader/emfreader.cxx16
-rw-r--r--include/drawinglayer/primitive2d/softedgeprimitive2d.hxx34
8 files changed, 508 insertions, 279 deletions
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index d3c8539eddf8..f8c503759e7d 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -178,19 +178,19 @@ void GlowPrimitive2D::create2DDecomposition(
{
// We may have to take a corrective scaling into account when the
// MaximumQuadraticPixel limit was used/triggered
- double fScaleX(1.0);
- double fScaleY(1.0);
+ double fScale(1.0);
- if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth)
+ if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
+ || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
{
- fScaleX = static_cast<double>(rBitmapExSizePixel.Width())
- / static_cast<double>(nDiscreteClippedWidth);
- }
-
- if (static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
- {
- fScaleY = static_cast<double>(rBitmapExSizePixel.Height())
- / static_cast<double>(nDiscreteClippedHeight);
+ // scale in X and Y should be the same (see fReduceFactor in convertToBitmapEx),
+ // 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));
+ const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+ / static_cast<double>(nDiscreteClippedHeight));
+
+ fScale = (fScaleX + fScaleY) * 0.5;
}
// fDiscreteGlowRadius is the size of the halo from each side of the object. The halo is the
@@ -199,8 +199,8 @@ void GlowPrimitive2D::create2DDecomposition(
// 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 * fScaleX / 2.0,
- fDiscreteGlowRadius * fScaleY / 2.0, 255 - getGlowColor().GetAlpha()));
+ aBitmapEx.GetAlpha(), 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();
@@ -211,16 +211,16 @@ void GlowPrimitive2D::create2DDecomposition(
static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
if (bDoSaveForVisualControl)
{
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\test_glow.png"
-#else
- "~/test_glow.png"
-#endif
- ,
- StreamMode::WRITE | StreamMode::TRUNC);
- vcl::PngImageWriter aPNGWriter(aNew);
- aPNGWriter.write(result);
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_glow.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(result);
+ }
}
#endif
@@ -317,6 +317,13 @@ void GlowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisit
}
}
+ if (getBuffered2DDecomposition().empty())
+ {
+ // refresh last used DiscreteGlowRadius and ClippedRange to new remembered values
+ const_cast<GlowPrimitive2D*>(this)->mfLastDiscreteGlowRadius = fDiscreteGlowRadius;
+ const_cast<GlowPrimitive2D*>(this)->maLastClippedRange = aClippedRange;
+ }
+
// call parent, that will check for empty, call create2DDecomposition and
// set as decomposition
BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index 59cf59cd7679..55cddb919aa6 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -18,20 +18,32 @@
*/
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <drawinglayer/converters.hxx>
+#include <drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx>
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
namespace drawinglayer::primitive2d
{
SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& aChildren)
- : GroupPrimitive2D(std::move(aChildren))
+ : BufferedDecompositionGroupPrimitive2D(std::move(aChildren))
, mfRadius(fRadius)
+ , mfLastDiscreteSoftRadius(0.0)
+ , maLastClippedRange()
{
}
bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
- if (GroupPrimitive2D::operator==(rPrimitive))
+ if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive))
{
auto& rCompare = static_cast<const SoftEdgePrimitive2D&>(rPrimitive);
return getRadius() == rCompare.getRadius();
@@ -40,27 +52,285 @@ bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
return false;
}
-void SoftEdgePrimitive2D::get2DDecomposition(
- Primitive2DDecompositionVisitor& rVisitor,
+bool SoftEdgePrimitive2D::prepareValuesAndcheckValidity(
+ basegfx::B2DRange& rSoftRange, basegfx::B2DRange& rClippedRange,
+ basegfx::B2DVector& rDiscreteSoftSize, double& rfDiscreteSoftRadius,
const geometry::ViewInformation2D& rViewInformation) const
{
+ // no SoftRadius defined, done
+ if (getRadius() <= 0.0)
+ return false;
+
+ // no geometry, done
if (getChildren().empty())
+ return false;
+
+ // no pixel target, done
+ if (rViewInformation.getObjectToViewTransformation().isIdentity())
+ return false;
+
+ // get geometry range that defines area that needs to be pixelated
+ rSoftRange = getChildren().getB2DRange(rViewInformation);
+
+ // no range of geometry, done
+ if (rSoftRange.isEmpty())
+ return false;
+
+ // initialize ClippedRange to full SoftRange -> all is visible
+ rClippedRange = rSoftRange;
+
+ // get Viewport and check if used. If empty, all is visible (see
+ // ViewInformation2D definition in viewinformation2d.hxx)
+ if (!rViewInformation.getViewport().isEmpty())
+ {
+ // if used, extend by SoftRadius to ensure needed parts are included
+ // that are not visible, but influence the visible parts
+ basegfx::B2DRange aVisibleArea(rViewInformation.getViewport());
+ aVisibleArea.grow(getRadius() * 2);
+
+ // calculate ClippedRange
+ rClippedRange.intersect(aVisibleArea);
+
+ // if SoftRange is completely outside of VisibleArea, ClippedRange
+ // will be empty and we are done
+ if (rClippedRange.isEmpty())
+ return false;
+ }
+
+ // calculate discrete pixel size of SoftRange. If it's too small to visualize, we are done
+ rDiscreteSoftSize = rViewInformation.getObjectToViewTransformation() * rSoftRange.getRange();
+ if (ceil(rDiscreteSoftSize.getX()) < 2.0 || ceil(rDiscreteSoftSize.getY()) < 2.0)
+ return false;
+
+ // calculate discrete pixel size of SoftRadius. If it's too small to visualize, we are done
+ rfDiscreteSoftRadius = ceil(
+ (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getRadius(), 0))
+ .getLength());
+ if (rfDiscreteSoftRadius < 1.0)
+ return false;
+
+ return true;
+}
+
+void SoftEdgePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Use endless while-loop-and-break mechanism due to having multiple
+ // exit scenarios that all have to do the same thing when exiting
+ while (true)
+ {
+ basegfx::B2DRange aSoftRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteSoftSize;
+ double fDiscreteSoftRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aSoftRange, aClippedRange, aDiscreteSoftSize,
+ fDiscreteSoftRadius, rViewInformation))
+ break;
+
+ // Create embedding transformation from object to top-left zero-aligned
+ // target pixel geometry (discrete form of ClippedRange)
+ // First, move to top-left of SoftRange
+ const sal_uInt32 nDiscreteSoftWidth(ceil(aDiscreteSoftSize.getX()));
+ const sal_uInt32 nDiscreteSoftHeight(ceil(aDiscreteSoftSize.getY()));
+ basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+ -aClippedRange.getMinX(), -aClippedRange.getMinY()));
+ // Second, scale to discrete bitmap size
+ // Even when using the offset from ClippedRange, we need to use the
+ // scaling from the full representation, thus from SoftRange
+ aEmbedding.scale(nDiscreteSoftWidth / aSoftRange.getWidth(),
+ nDiscreteSoftHeight / aSoftRange.getHeight());
+
+ // Embed content graphics to TransformPrimitive2D
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(aEmbedding, Primitive2DContainer(getChildren())));
+ primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+ // Create BitmapEx using drawinglayer tooling, including a MaximumQuadraticPixel
+ // limitation to be safe and not go runtime/memory havoc. Use a pretty small
+ // limit due to this is softEdge functionality and will look good with bitmap scaling
+ // anyways. The value of 250.000 square pixels below maybe adapted as needed.
+ 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(
+ std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
+ nMaximumQuadraticPixels));
+
+ if (aBitmapEx.IsEmpty())
+ break;
+
+ // Get BitmapEx and check size. If no content, we are done
+ const Size& rBitmapExSizePixel(aBitmapEx.GetSizePixel());
+ if (!(rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 0))
+ break;
+
+ // We may have to take a corrective scaling into account when the
+ // MaximumQuadraticPixel limit was used/triggered
+ double fScale(1.0);
+
+ 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),
+ // 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));
+ const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+ / static_cast<double>(nDiscreteClippedHeight));
+
+ fScale = (fScaleX + fScaleY) * 0.5;
+ }
+
+ // Get the Alpha and use as base to blur and apply the effect
+ AlphaMask aMask(aBitmapEx.GetAlpha());
+ const AlphaMask blurMask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
+ aMask, -fDiscreteSoftRadius * fScale, fDiscreteSoftRadius * fScale, 0));
+ aMask.BlendWith(blurMask);
+
+ // The end result is the original bitmap with blurred 8-bit alpha mask
+ BitmapEx result(aBitmapEx.GetBitmap(), aMask);
+
+#ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(true); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_softedge.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(result);
+ }
+ }
+#endif
+
+ // Independent from discrete sizes of soft alpha creation, always
+ // map and project soft result to geometry range extended by soft
+ // radius, but to the eventually clipped instance (ClippedRange)
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new BitmapPrimitive2D(VCLUnoHelper::CreateVCLXBitmap(result),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aClippedRange.getWidth(), aClippedRange.getHeight(),
+ aClippedRange.getMinX(), aClippedRange.getMinY())));
+
+ rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap };
+
+ // we made it, return
return;
+ }
+
+ // creation failed for some of many possible reasons, use original
+ // content, so the unmodified original geometry will be the result,
+ // just without any softEdge effect
+ rContainer = getChildren();
+}
- if (!mbInMaskGeneration)
+void SoftEdgePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Use endless while-loop-and-break mechanism due to having multiple
+ // exit scenarios that all have to do the same thing when exiting
+ while (true)
{
- GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ basegfx::B2DRange aSoftRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteSoftSize;
+ double fDiscreteSoftRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aSoftRange, aClippedRange, aDiscreteSoftSize,
+ fDiscreteSoftRadius, rViewInformation))
+ break;
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // First check is to detect if the last created decompose is capable
+ // to represent the now requested visualization (see similar
+ // implementation at GlowPrimitive2D).
+ if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange))
+ {
+ basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange);
+
+ if (!rViewInformation.getObjectToViewTransformation().isIdentity())
+ {
+ // Grow by view-dependent size of 1/2 pixel
+ const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(0.5, 0))
+ .getLength());
+ aLastClippedRangeAndHairline.grow(fHalfPixel);
+ }
+
+ if (!aLastClippedRangeAndHairline.isInside(aClippedRange))
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<SoftEdgePrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+ }
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // Second check is to react on changes of the DiscreteSoftRadius when
+ // zooming in/out (see similar implementation at GlowPrimitive2D).
+ bool bFree(mfLastDiscreteSoftRadius <= 0.0 || fDiscreteSoftRadius <= 0.0);
+
+ if (!bFree)
+ {
+ const double fDiff(fabs(mfLastDiscreteSoftRadius - fDiscreteSoftRadius));
+ const double fLen(fabs(mfLastDiscreteSoftRadius) + fabs(fDiscreteSoftRadius));
+ const double fRelativeChange(fDiff / fLen);
+
+ // Use a lower value here, soft edge keeps it's content so avoid that it gets too
+ // unsharp in the pixel visualization
+ // Value is in the range of ]0.0 .. 1.0]
+ bFree = fRelativeChange >= 0.075;
+ }
+
+ if (bFree)
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<SoftEdgePrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // refresh last used DiscreteSoftRadius and ClippedRange to new remembered values
+ const_cast<SoftEdgePrimitive2D*>(this)->mfLastDiscreteSoftRadius = fDiscreteSoftRadius;
+ const_cast<SoftEdgePrimitive2D*>(this)->maLastClippedRange = aClippedRange;
+ }
+
+ // call parent, that will check for empty, call create2DDecomposition and
+ // set as decomposition
+ BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+
+ // we made it, return
return;
}
- // create a modifiedColorPrimitive containing the *black* color and the content. Using black
- // on white allows creating useful mask in VclPixelProcessor2D::processSoftEdgePrimitive2D.
- basegfx::BColorModifierSharedPtr aBColorModifier
- = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor());
+ // No soft edge needed for some of many possible reasons, use original content
+ rVisitor.visit(getChildren());
+}
- const Primitive2DReference xRef(
- new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), aBColorModifier));
- rVisitor.visit(xRef);
+basegfx::B2DRange
+SoftEdgePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily)
+ // use the decompose - what works, but is not needed here.
+ // We know the to-be-visualized geometry and the radius it needs to be extended,
+ // so simply calculate the exact needed range.
+ return getChildren().getB2DRange(rViewInformation);
}
sal_uInt32 SoftEdgePrimitive2D::getPrimitive2DID() const
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 96e6daa66ab5..926835788383 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -929,7 +929,6 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
break;
}
case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
- case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
{
processPrimitive2DOnPixelProcessor(rCandidate);
break;
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index f00a38d49374..976e5cb1dccb 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -56,7 +56,6 @@
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
-#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
#include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
#include <drawinglayer/primitive2d/GlowSoftEgdeShadowTools.hxx>
@@ -406,12 +405,6 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate));
break;
}
- case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
- {
- processSoftEdgePrimitive2D(
- static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate));
- break;
- }
case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
{
processShadowPrimitive2D(
@@ -966,67 +959,6 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrim
}
}
-void VclPixelProcessor2D::processSoftEdgePrimitive2D(
- const primitive2d::SoftEdgePrimitive2D& rCandidate)
-{
- const double nRadius(rCandidate.getRadius());
- // Avoid wrong effect on the cut-off side; so expand by diameter
- const auto aExpandedViewInfo(::drawinglayer::primitive2d::expandB2DRangeAtViewInformation2D(
- getViewInformation2D(), nRadius * 2));
-
- basegfx::B2DRange aRange(rCandidate.getB2DRange(aExpandedViewInfo));
- aRange.transform(maCurrentTransformation);
- basegfx::B2DVector aRadiusVector(nRadius, 0);
- // Calculate the pixel size of soft edge radius in current transformation
- aRadiusVector *= maCurrentTransformation;
- // Blur radius is equal to soft edge radius
- const double fBlurRadius = aRadiusVector.getLength();
-
- impBufferDevice aBufferDevice(*mpOutputDevice, aRange, false);
- if (aBufferDevice.isVisible())
- {
- // remember last OutDev and set to content
- OutputDevice* pLastOutputDevice = mpOutputDevice;
- mpOutputDevice = &aBufferDevice.getContent();
- // Since the effect converts all children to bitmap, we can't disable antialiasing here,
- // because it would result in poor quality in areas not affected by the effect
- process(rCandidate);
-
- // Limit the bitmap size to the visible area.
- basegfx::B2DRange bitmapRange(aRange);
- if (!aExpandedViewInfo.getDiscreteViewport().isEmpty())
- bitmapRange.intersect(aExpandedViewInfo.getDiscreteViewport());
- if (!bitmapRange.isEmpty())
- {
- const tools::Rectangle aRect(
- static_cast<tools::Long>(std::floor(bitmapRange.getMinX())),
- static_cast<tools::Long>(std::floor(bitmapRange.getMinY())),
- static_cast<tools::Long>(std::ceil(bitmapRange.getMaxX())),
- static_cast<tools::Long>(std::ceil(bitmapRange.getMaxY())));
- BitmapEx bitmap = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
-
- AlphaMask aMask = bitmap.GetAlpha();
- AlphaMask blurMask = drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
- aMask, -fBlurRadius, fBlurRadius, 0);
-
- aMask.BlendWith(blurMask);
-
- // The end result is the original bitmap with blurred 8-bit alpha mask
- BitmapEx result(bitmap.GetBitmap(), aMask);
-
- // back to old OutDev
- mpOutputDevice = pLastOutputDevice;
- mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
- }
- else
- {
- mpOutputDevice = pLastOutputDevice;
- }
- }
- else
- SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
-}
-
void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate)
{
if (rCandidate.getShadowBlur() == 0)
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
index e083e951afbf..d7494ccbbfff 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
@@ -98,7 +98,6 @@ class VclPixelProcessor2D final : public VclProcessor2D
processBorderLinePrimitive2D(const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder);
void processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
void processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
- void processSoftEdgePrimitive2D(const primitive2d::SoftEdgePrimitive2D& rCandidate);
void processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate);
void processFillGradientPrimitive2D(const primitive2d::FillGradientPrimitive2D& rPrimitive);
void processPatternFillPrimitive2D(const primitive2d::PatternFillPrimitive2D& rPrimitive);
diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx
index 84294b24af26..3c4ad791a3c3 100644
--- a/drawinglayer/source/tools/converters.cxx
+++ b/drawinglayer/source/tools/converters.cxx
@@ -37,197 +37,189 @@
namespace drawinglayer
{
- BitmapEx convertToBitmapEx(
- drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
- const geometry::ViewInformation2D& rViewInformation2D,
- sal_uInt32 nDiscreteWidth,
- sal_uInt32 nDiscreteHeight,
- sal_uInt32 nMaxSquarePixels)
- {
- BitmapEx aRetval;
-
- if(rSeq.empty())
- return aRetval;
-
- if(nDiscreteWidth <= 0 || nDiscreteHeight <= 0)
- return aRetval;
+BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D,
+ sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
+ sal_uInt32 nMaxSquarePixels)
+{
+ BitmapEx aRetval;
- // get destination size in pixels
- const MapMode aMapModePixel(MapUnit::MapPixel);
- const sal_uInt32 nViewVisibleArea(nDiscreteWidth * nDiscreteHeight);
- drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
+ if (rSeq.empty())
+ return aRetval;
- 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);
+ if (nDiscreteWidth <= 0 || nDiscreteHeight <= 0)
+ return aRetval;
- const drawinglayer::primitive2d::Primitive2DReference aEmbed(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor),
- std::move(aSequence)));
+ // get destination size in pixels
+ const MapMode aMapModePixel(MapUnit::MapPixel);
+ const sal_uInt32 nViewVisibleArea(nDiscreteWidth * nDiscreteHeight);
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
- aSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed };
- }
+ 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);
+
+ const drawinglayer::primitive2d::Primitive2DReference aEmbed(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor),
+ std::move(aSequence)));
+
+ aSequence = drawinglayer::primitive2d::Primitive2DContainer{ aEmbed };
+ }
- const Point aEmptyPoint;
- const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
-
- // Create target VirtualDevice. Use a VirtualDevice in the Alpha-mode.
- // This creates the needed alpha channel 'in parallel'. It is not
- // cheaper though since the VDev in that mode internally uses two VDevs,
- // so resource-wise it's more expensive, speed-wise pretty much the same
- // (the former two-path rendering created content & alpha separately in
- // two runs). The former method always created the correct Alpha, but
- // when transparent geometry was involved, the created content was
- // blended against white (COL_WHITE) due to the starting conditions of
- // creation.
- // There are more ways than this to do this correctly, but this is the
- // most simple for now. Due to hoping to be able to render to RGBA in the
- // future anyways there is no need to experiment trying to do the correct
- // thing using an expanded version of the former method.
- ScopedVclPtrInstance<VirtualDevice> pContent(
- *Application::GetDefaultDevice(), DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
-
- // prepare vdev
- if (!pContent->SetOutputSizePixel(aSizePixel, false))
- {
- SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << aSizePixel.Width() << "x" << aSizePixel.Height());
- return aRetval;
- }
+ const Point aEmptyPoint;
+ const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
+
+ // Create target VirtualDevice. Use a VirtualDevice in the Alpha-mode.
+ // This creates the needed alpha channel 'in parallel'. It is not
+ // cheaper though since the VDev in that mode internally uses two VDevs,
+ // so resource-wise it's more expensive, speed-wise pretty much the same
+ // (the former two-path rendering created content & alpha separately in
+ // two runs). The former method always created the correct Alpha, but
+ // when transparent geometry was involved, the created content was
+ // blended against white (COL_WHITE) due to the starting conditions of
+ // creation.
+ // There are more ways than this to do this correctly, but this is the
+ // most simple for now. Due to hoping to be able to render to RGBA in the
+ // future anyways there is no need to experiment trying to do the correct
+ // thing using an expanded version of the former method.
+ ScopedVclPtrInstance<VirtualDevice> pContent(*Application::GetDefaultDevice(),
+ DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
+
+ // prepare vdev
+ if (!pContent->SetOutputSizePixel(aSizePixel, false))
+ {
+ SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << aSizePixel.Width() << "x"
+ << aSizePixel.Height());
+ return aRetval;
+ }
- // We map to pixel, use that MapMode. Init by erasing.
- pContent->SetMapMode(aMapModePixel);
- pContent->Erase();
+ // We map to pixel, use that MapMode. Init by erasing.
+ pContent->SetMapMode(aMapModePixel);
+ pContent->Erase();
- // 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<processor2d::BaseProcessor2D> pContentProcessor = processor2d::createPixelProcessor2DFromOutputDevice(
- *pContent,
- rViewInformation2D);
+ // 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<processor2d::BaseProcessor2D> pContentProcessor
+ = processor2d::createPixelProcessor2DFromOutputDevice(*pContent, rViewInformation2D);
- // render content
- pContentProcessor->process(aSequence);
+ // render content
+ pContentProcessor->process(aSequence);
- // create final BitmapEx result
- aRetval = pContent->GetBitmapEx(aEmptyPoint, aSizePixel);
+ // create final BitmapEx result
+ aRetval = pContent->GetBitmapEx(aEmptyPoint, aSizePixel);
#ifdef DBG_UTIL
- static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
- if(bDoSaveForVisualControl)
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
{
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\test_combined.png"
-#else
- "~/test_combined.png"
-#endif
- , StreamMode::WRITE|StreamMode::TRUNC);
+ SvFileStream aNew(sDumpPath + "test_combined.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
vcl::PngImageWriter aPNGWriter(aNew);
aPNGWriter.write(aRetval);
}
+ }
#endif
- return aRetval;
- }
+ return aRetval;
+}
+
+BitmapEx convertPrimitive2DContainerToBitmapEx(primitive2d::Primitive2DContainer&& rSequence,
+ const basegfx::B2DRange& rTargetRange,
+ sal_uInt32 nMaximumQuadraticPixels,
+ const o3tl::Length eTargetUnit,
+ const std::optional<Size>& rTargetDPI)
+{
+ if (rSequence.empty())
+ return BitmapEx();
- BitmapEx convertPrimitive2DContainerToBitmapEx(
- primitive2d::Primitive2DContainer&& rSequence,
- const basegfx::B2DRange& rTargetRange,
- sal_uInt32 nMaximumQuadraticPixels,
- const o3tl::Length eTargetUnit,
- const std::optional<Size>& rTargetDPI)
+ try
{
- if(rSequence.empty())
+ css::geometry::RealRectangle2D aRealRect;
+ aRealRect.X1 = rTargetRange.getMinX();
+ aRealRect.Y1 = rTargetRange.getMinY();
+ aRealRect.X2 = rTargetRange.getMaxX();
+ aRealRect.Y2 = rTargetRange.getMaxY();
+
+ // get system DPI
+ Size aDPI(
+ Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ if (rTargetDPI.has_value())
+ {
+ aDPI = *rTargetDPI;
+ }
+
+ ::sal_uInt32 DPI_X = aDPI.getWidth();
+ ::sal_uInt32 DPI_Y = aDPI.getHeight();
+ const basegfx::B2DRange aRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2);
+ const double fWidth(aRange.getWidth());
+ const double fHeight(aRange.getHeight());
+
+ if (!(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0)))
return BitmapEx();
- try
+ if (0 == DPI_X)
{
- css::geometry::RealRectangle2D aRealRect;
- aRealRect.X1 = rTargetRange.getMinX();
- aRealRect.Y1 = rTargetRange.getMinY();
- aRealRect.X2 = rTargetRange.getMaxX();
- aRealRect.Y2 = rTargetRange.getMaxY();
-
- // get system DPI
- Size aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- if (rTargetDPI.has_value())
- {
- aDPI = *rTargetDPI;
- }
-
- ::sal_uInt32 DPI_X = aDPI.getWidth();
- ::sal_uInt32 DPI_Y = aDPI.getHeight();
- const basegfx::B2DRange aRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2);
- const double fWidth(aRange.getWidth());
- const double fHeight(aRange.getHeight());
-
- if(!(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0)))
- return BitmapEx();
-
- if(0 == DPI_X)
- {
- DPI_X = 75;
- }
-
- if(0 == DPI_Y)
- {
- DPI_Y = 75;
- }
-
- if(0 == nMaximumQuadraticPixels)
- {
- nMaximumQuadraticPixels = 500000;
- }
-
- const auto aViewInformation2D = geometry::createViewInformation2D({});
- const sal_uInt32 nDiscreteWidth(basegfx::fround(o3tl::convert(fWidth, eTargetUnit, o3tl::Length::in) * DPI_X));
- const sal_uInt32 nDiscreteHeight(basegfx::fround(o3tl::convert(fHeight, eTargetUnit, o3tl::Length::in) * DPI_Y));
-
- basegfx::B2DHomMatrix aEmbedding(
- basegfx::utils::createTranslateB2DHomMatrix(
- -aRange.getMinX(),
- -aRange.getMinY()));
-
- aEmbedding.scale(
- nDiscreteWidth / fWidth,
- nDiscreteHeight / fHeight);
-
- const primitive2d::Primitive2DReference xEmbedRef(
- new primitive2d::TransformPrimitive2D(
- aEmbedding,
- std::move(rSequence)));
- primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
-
- BitmapEx aBitmapEx(
- convertToBitmapEx(
- std::move(xEmbedSeq),
- aViewInformation2D,
- nDiscreteWidth,
- nDiscreteHeight,
- nMaximumQuadraticPixels));
-
- if(aBitmapEx.IsEmpty())
- return BitmapEx();
- aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
- aBitmapEx.SetPrefSize(Size(basegfx::fround(fWidth), basegfx::fround(fHeight)));
-
- return aBitmapEx;
+ DPI_X = 75;
}
- catch (const css::uno::Exception&)
+
+ if (0 == DPI_Y)
{
- TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
+ DPI_Y = 75;
}
- catch (const std::exception& e)
+
+ if (0 == nMaximumQuadraticPixels)
{
- SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what());
+ nMaximumQuadraticPixels = 500000;
}
- return BitmapEx();
+ const auto aViewInformation2D = geometry::createViewInformation2D({});
+ const sal_uInt32 nDiscreteWidth(
+ basegfx::fround(o3tl::convert(fWidth, eTargetUnit, o3tl::Length::in) * DPI_X));
+ const sal_uInt32 nDiscreteHeight(
+ basegfx::fround(o3tl::convert(fHeight, eTargetUnit, o3tl::Length::in) * DPI_Y));
+
+ basegfx::B2DHomMatrix aEmbedding(
+ basegfx::utils::createTranslateB2DHomMatrix(-aRange.getMinX(), -aRange.getMinY()));
+
+ aEmbedding.scale(nDiscreteWidth / fWidth, nDiscreteHeight / fHeight);
+
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(aEmbedding, std::move(rSequence)));
+ primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+ BitmapEx aBitmapEx(convertToBitmapEx(std::move(xEmbedSeq), aViewInformation2D,
+ nDiscreteWidth, nDiscreteHeight,
+ nMaximumQuadraticPixels));
+
+ if (aBitmapEx.IsEmpty())
+ return BitmapEx();
+ aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aBitmapEx.SetPrefSize(Size(basegfx::fround(fWidth), basegfx::fround(fHeight)));
+
+ return aBitmapEx;
}
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what());
+ }
+
+ return BitmapEx();
+}
} // end of namespace drawinglayer
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx
index 2d50a9a057bb..37978478bdbe 100644
--- a/emfio/source/reader/emfreader.cxx
+++ b/emfio/source/reader/emfreader.cxx
@@ -1496,16 +1496,22 @@ namespace emfio
}
}
- #ifdef DBG_UTIL
+#ifdef DBG_UTIL
static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
if(bDoSaveForVisualControl)
{
- SvFileStream aNew("c:\\metafile_content.png", StreamMode::WRITE|StreamMode::TRUNC);
- vcl::PngImageWriter aPNGWriter(aNew);
- aPNGWriter.write(aBitmapEx);
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if(!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "metafile_content.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(aBitmapEx);
+ }
}
- #endif
+#endif
maBmpSaveList.emplace_back(aBitmapEx, aRect, SRCAND|SRCINVERT);
}
}
diff --git a/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx b/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx
index 90ada61f7b2e..4a49444560c5 100644
--- a/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/softedgeprimitive2d.hxx
@@ -20,30 +20,54 @@
#pragma once
#include <drawinglayer/drawinglayerdllapi.h>
-#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
namespace drawinglayer::primitive2d
{
-class DRAWINGLAYER_DLLPUBLIC SoftEdgePrimitive2D final : public GroupPrimitive2D
+class DRAWINGLAYER_DLLPUBLIC SoftEdgePrimitive2D final
+ : public BufferedDecompositionGroupPrimitive2D
{
private:
/// Soft edge size, in logical units (100ths of mm)
double mfRadius;
- mutable bool mbInMaskGeneration = false;
+
+ /// last used DiscreteSoftRadius and ClippedRange
+ double mfLastDiscreteSoftRadius;
+ basegfx::B2DRange maLastClippedRange;
+
+ /// helpers
+ bool prepareValuesAndcheckValidity(basegfx::B2DRange& rSoftRange,
+ basegfx::B2DRange& rClippedRange,
+ basegfx::B2DVector& rDiscreteSoftSize,
+ double& rfDiscreteSoftRadius,
+ const geometry::ViewInformation2D& rViewInformation) const;
+
+protected:
+ /** method which is to be used to implement the local decomposition of a 2D primitive. */
+ virtual void
+ create2DDecomposition(Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const override;
public:
+ /// constructor
SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& aChildren);
+ /// data read access
double getRadius() const { return mfRadius; }
- void setMaskGeneration(bool bVal = true) const { mbInMaskGeneration = bVal; }
-
+ /// compare operator
virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+ /// get range
+ virtual basegfx::B2DRange
+ getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
+
+ /// The default implementation will return an empty sequence
virtual void
get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor,
const geometry::ViewInformation2D& rViewInformation) const override;
+ /// provide unique ID
virtual sal_uInt32 getPrimitive2DID() const override;
};
} // end of namespace drawinglayer::primitive2d