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