diff options
author | Armin Le Grand (Collabora) <Armin.Le.Grand@me.com> | 2024-07-23 13:26:34 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2024-07-23 18:35:26 +0200 |
commit | 462d85709ead9c7cec33ce58fc608997263cb6aa (patch) | |
tree | 3eef2f07a6bef84a3ba02d49c9536ab58d5abfbf | |
parent | 863b90e33c4b9964a697684887aeb42cc538b019 (diff) |
CairoSDPR: Support alpha for BitmapPrimitives
To more directly support an additional alpha channel
for BitmapPrimitive data I have done some deeper
changes to the primitive stack, in a compatible
way. Quite some more graphic types and primitives
now support an additional direct alpha value. All
that is decomposed/created/broken down in a way
that needs no changes for existing renderers, in
already described ways.
The CairoPixelProcessor2D already uses this in the
processFillGraphicPrimitive2D implementation and
thus in the FillGraphicPrimitive2D. This works since
primitive productions now all try to create that
FillGraphicPrimitive2D type including an additional
alpha value - if possible and necessary.
Change-Id: Ib1b16491a2b3aee16f14cc8196e28af30a7cf9be
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170900
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Tested-by: Jenkins
15 files changed, 280 insertions, 106 deletions
diff --git a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx index 31a7acb03d7c..7853709b9541 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx @@ -34,21 +34,36 @@ namespace drawinglayer::primitive2d Primitive2DReference PolyPolygonGraphicPrimitive2D::create2DDecomposition( const geometry::ViewInformation2D& /*rViewInformation*/) const { + if (basegfx::fTools::equal(getTransparency(), 1.0)) + { + // completely transparent, done + return nullptr; + } + if (getFillGraphic().isDefault()) + { + // no geometry data, done return nullptr; + } const Graphic& rGraphic = getFillGraphic().getGraphic(); const GraphicType aType(rGraphic.GetType()); // is there a bitmap or a metafile (do we have content)? if (GraphicType::Bitmap != aType && GraphicType::GdiMetafile != aType) + { + // no geometry data, done return nullptr; + } const Size aPrefSize(rGraphic.GetPrefSize()); // does content have a size? if (!(aPrefSize.Width() && aPrefSize.Height())) + { + // no geometry data with size, done return nullptr; + } // create SubSequence with FillGraphicPrimitive2D based on polygon range const basegfx::B2DRange aOutRange(getB2DPolyPolygon().getB2DRange()); @@ -85,11 +100,13 @@ Primitive2DReference PolyPolygonGraphicPrimitive2D::create2DDecomposition( getFillGraphic().getGraphic(), aAdaptedRange, getFillGraphic().getTiling(), getFillGraphic().getOffsetX(), getFillGraphic().getOffsetY()); - xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, aAdaptedFillGraphicAttribute); + xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, aAdaptedFillGraphicAttribute, + getTransparency()); } else { - xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, getFillGraphic()); + xSubRef + = new FillGraphicPrimitive2D(aNewObjectTransform, getFillGraphic(), getTransparency()); } // embed to mask primitive @@ -98,10 +115,11 @@ Primitive2DReference PolyPolygonGraphicPrimitive2D::create2DDecomposition( PolyPolygonGraphicPrimitive2D::PolyPolygonGraphicPrimitive2D( basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - const attribute::FillGraphicAttribute& rFillGraphic) + const attribute::FillGraphicAttribute& rFillGraphic, double fTransparency) : maPolyPolygon(std::move(aPolyPolygon)) , maDefinitionRange(rDefinitionRange) , maFillGraphic(rFillGraphic) + , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) { } @@ -114,7 +132,8 @@ bool PolyPolygonGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() && getDefinitionRange() == rCompare.getDefinitionRange() - && getFillGraphic() == rCompare.getFillGraphic()); + && getFillGraphic() == rCompare.getFillGraphic() + && basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())); } return false; diff --git a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx index 293f3738fa1b..5ef71c97aa33 100644 --- a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx @@ -69,7 +69,8 @@ namespace drawinglayer::primitive2d Primitive2DContainer xSeq; create2DDecompositionOfGraphic(xSeq, rGraphic, - basegfx::B2DHomMatrix()); + basegfx::B2DHomMatrix(), + getTransparency()); rtl::Reference<GroupPrimitive2D> xGroup = new GroupPrimitive2D(std::move(xSeq)); for(const auto &a : aMatrices) @@ -89,17 +90,21 @@ namespace drawinglayer::primitive2d create2DDecompositionOfGraphic(aContainer, rGraphic, - aObjectTransform); + aObjectTransform, + getTransparency()); } + return new GroupPrimitive2D(std::move(aContainer)); } FillGraphicPrimitive2D::FillGraphicPrimitive2D( basegfx::B2DHomMatrix aTransformation, - const attribute::FillGraphicAttribute& rFillGraphic) - : maTransformation(std::move(aTransformation)), - maFillGraphic(rFillGraphic), - maOffsetXYCreatedBitmap() + const attribute::FillGraphicAttribute& rFillGraphic, + double fTransparency) + : maTransformation(std::move(aTransformation)) + , maFillGraphic(rFillGraphic) + , maOffsetXYCreatedBitmap() + , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) { } @@ -110,7 +115,8 @@ namespace drawinglayer::primitive2d const FillGraphicPrimitive2D& rCompare = static_cast< const FillGraphicPrimitive2D& >(rPrimitive); return (getTransformation() == rCompare.getTransformation() - && getFillGraphic() == rCompare.getFillGraphic()); + && getFillGraphic() == rCompare.getFillGraphic() + && basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())); } return false; diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx index 7d9f4233160b..457bd64c95c6 100644 --- a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx @@ -113,9 +113,11 @@ GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D&) co } // create sub-content; helper takes care of correct handling of - // bitmap, svg or metafile content + // bitmap, svg or metafile content. also handle alpha there directly Primitive2DContainer aRetval; - create2DDecompositionOfGraphic(aRetval, aTransformedGraphic, aTransform); + const double fTransparency( + std::clamp((255 - getGraphicAttr().GetAlpha()) * (1.0 / 255.0), 0.0, 1.0)); + create2DDecompositionOfGraphic(aRetval, aTransformedGraphic, aTransform, fTransparency); if (aRetval.empty()) { @@ -144,19 +146,6 @@ GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D&) co } } - if (getGraphicAttr().IsTransparent()) - { - // check for transparency - const double fTransparency( - std::clamp((255 - getGraphicAttr().GetAlpha()) * (1.0 / 255.0), 0.0, 1.0)); - - if (!basegfx::fTools::equalZero(fTransparency)) - { - aRetval = Primitive2DContainer{ new UnifiedTransparencePrimitive2D(std::move(aRetval), - fTransparency) }; - } - } - if (getGraphicAttr().IsCropped()) { // check for cropping diff --git a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx index d4d660228565..d97d1bd36245 100644 --- a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx +++ b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx @@ -24,6 +24,8 @@ #include <drawinglayer/primitive2d/graphicprimitivehelper2d.hxx> #include <drawinglayer/animation/animationtiming.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/animatedprimitive2d.hxx> #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> @@ -65,6 +67,9 @@ namespace drawinglayer::primitive2d /// local animation processing data, excerpt from maGraphic ::Animation maAnimation; + /// the transparency in range [0.0 .. 1.0] + double mfTransparency; + /// the on-demand created VirtualDevices for frame creation ScopedVclPtrInstance< VirtualDevice > maVirtualDevice; ScopedVclPtrInstance< VirtualDevice > maVirtualDeviceMask; @@ -88,7 +93,8 @@ namespace drawinglayer::primitive2d { return (GraphicType::Bitmap == maGraphic.GetType() && maGraphic.IsAnimated() - && maAnimation.Count()); + && maAnimation.Count() + && !basegfx::fTools::equal(getTransparency(), 1.0)); } void ensureVirtualDeviceSizeAndState() @@ -192,7 +198,9 @@ namespace drawinglayer::primitive2d bitmap = BitmapEx(aMainBitmap, aMaskBitmap); } - return new BitmapPrimitive2D(bitmap, getTransform()); + if(basegfx::fTools::equal(getTransparency(), 0.0)) + return new BitmapPrimitive2D(bitmap, getTransform()); + return new BitmapAlphaPrimitive2D(bitmap, getTransform(), getTransparency()); } void checkSafeToBuffer(sal_uInt32 nIndex) @@ -355,11 +363,13 @@ namespace drawinglayer::primitive2d /// constructor AnimatedGraphicPrimitive2D( const Graphic& rGraphic, - basegfx::B2DHomMatrix aTransform); + basegfx::B2DHomMatrix aTransform, + double fTransparency = 0.0); virtual ~AnimatedGraphicPrimitive2D(); /// data read access const basegfx::B2DHomMatrix& getTransform() const { return maTransform; } + double getTransparency() const { return mfTransparency; } /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override { return PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D; } @@ -378,7 +388,8 @@ namespace drawinglayer::primitive2d AnimatedGraphicPrimitive2D::AnimatedGraphicPrimitive2D( const Graphic& rGraphic, - basegfx::B2DHomMatrix aTransform) + basegfx::B2DHomMatrix aTransform, + double fTransparency) : AnimatedSwitchPrimitive2D( animation::AnimationEntryList(), Primitive2DContainer(), @@ -386,6 +397,7 @@ namespace drawinglayer::primitive2d maTransform(std::move(aTransform)), maGraphic(rGraphic), maAnimation(rGraphic.GetAnimation()), + mfTransparency(std::max(0.0, std::min(1.0, fTransparency))), maVirtualDevice(*Application::GetDefaultDevice()), maVirtualDeviceMask(*Application::GetDefaultDevice()), mnNextFrameToPrepare(SAL_MAX_UINT32), @@ -529,9 +541,14 @@ namespace drawinglayer::primitive2d void create2DDecompositionOfGraphic( Primitive2DContainer& rContainer, const Graphic& rGraphic, - const basegfx::B2DHomMatrix& rTransform) + const basegfx::B2DHomMatrix& rTransform, + double fTransparency) { - Primitive2DContainer aRetval; + if (basegfx::fTools::equal(fTransparency, 1.0)) + { + // completely transparent, done + return; + } switch(rGraphic.GetType()) { @@ -539,10 +556,12 @@ namespace drawinglayer::primitive2d { if(rGraphic.IsAnimated()) { - // prepare specialized AnimatedGraphicPrimitive2D - aRetval = Primitive2DContainer { new AnimatedGraphicPrimitive2D( + // prepare specialized AnimatedGraphicPrimitive2D, now with + // support for alpha + rContainer.append(new AnimatedGraphicPrimitive2D( rGraphic, - rTransform) }; + rTransform, + fTransparency)); } else if(rGraphic.getVectorGraphicData()) { @@ -565,16 +584,37 @@ namespace drawinglayer::primitive2d aEmbedVectorGraphic = rTransform * aEmbedVectorGraphic; // add Vector Graphic Data primitives embedded - aRetval = Primitive2DContainer { new TransformPrimitive2D( - aEmbedVectorGraphic, - Primitive2DContainer(rGraphic.getVectorGraphicData()->getPrimitive2DSequence()))}; + rtl::Reference<BasePrimitive2D> aPrimitive( + new TransformPrimitive2D( + aEmbedVectorGraphic, + Primitive2DContainer(rGraphic.getVectorGraphicData()->getPrimitive2DSequence()))); + + // if needed embed to UnifiedTransparencePrimitive2D + if (!basegfx::fTools::equalZero(fTransparency, 0.0)) + aPrimitive = new UnifiedTransparencePrimitive2D( + Primitive2DContainer { aPrimitive }, fTransparency); + + rContainer.append(aPrimitive); } } else { - aRetval = Primitive2DContainer { new BitmapPrimitive2D( - rGraphic.GetBitmapEx(), - rTransform) }; + // dependent of transparency used create the needed bitmap primitive + if(basegfx::fTools::equal(fTransparency, 0.0)) + { + rContainer.append( + new BitmapPrimitive2D( + rGraphic.GetBitmapEx(), + rTransform)); + } + else + { + rContainer.append( + new BitmapAlphaPrimitive2D( + rGraphic.GetBitmapEx(), + rTransform, + fTransparency)); + } } break; @@ -585,9 +625,10 @@ namespace drawinglayer::primitive2d // create MetafilePrimitive2D const GDIMetaFile& rMetafile = rGraphic.GetGDIMetaFile(); - aRetval = Primitive2DContainer { new MetafilePrimitive2D( - rTransform, - rMetafile) }; + rtl::Reference<BasePrimitive2D> aPrimitive( + new MetafilePrimitive2D( + rTransform, + rMetafile)); // #i100357# find out if clipping is needed for this primitive. Unfortunately, // there exist Metafiles who's content is bigger than the proposed PrefSize set @@ -604,12 +645,17 @@ namespace drawinglayer::primitive2d basegfx::B2DPolygon aMaskPolygon(basegfx::utils::createUnitPolygon()); aMaskPolygon.transform(rTransform); - aRetval = Primitive2DContainer { - new MaskPrimitive2D( - basegfx::B2DPolyPolygon(aMaskPolygon), - std::move(aRetval)) - }; + aPrimitive = new MaskPrimitive2D( + basegfx::B2DPolyPolygon(aMaskPolygon), + Primitive2DContainer { aPrimitive }); } + + // if needed embed to UnifiedTransparencePrimitive2D + if (!basegfx::fTools::equalZero(fTransparency, 0.0)) + aPrimitive = new UnifiedTransparencePrimitive2D( + Primitive2DContainer { aPrimitive }, fTransparency); + + rContainer.append(aPrimitive); break; } @@ -619,8 +665,6 @@ namespace drawinglayer::primitive2d break; } } - - rContainer.append(std::move(aRetval)); } Primitive2DContainer create2DColorModifierEmbeddingsAsNeeded( diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index 2fc46f4dda21..2f5fad1e2c59 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -1828,6 +1828,13 @@ void CairoPixelProcessor2D::processSingleLinePrimitive2D( void CairoPixelProcessor2D::processFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D) { + if (rFillGraphicPrimitive2D.getTransparency() < 0.0 + || rFillGraphicPrimitive2D.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + BitmapEx aPreparedBitmap; basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange()); constexpr double fBigDiscreteArea(300.0 * 300.0); @@ -1870,12 +1877,24 @@ void CairoPixelProcessor2D::processFillGraphicPrimitive2D( // local primitive, that is not part of DisplayInfo yet aPolygon.transform(rFillGraphicPrimitive2D.getTransformation()); - rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> aTemp( - new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), - aModifiedColor)); + if (rFillGraphicPrimitive2D.hasTransparency()) + { + rtl::Reference<primitive2d::PolyPolygonRGBAPrimitive2D> aTemp( + new primitive2d::PolyPolygonRGBAPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), aModifiedColor, + rFillGraphicPrimitive2D.getTransparency())); + // draw as colored and transparent Polygon, done + processPolyPolygonRGBAPrimitive2D(*aTemp); + } + else + { + rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> aTemp( + new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), + aModifiedColor)); + // draw as colored Polygon, done + processPolyPolygonColorPrimitive2D(*aTemp); + } - // draw as colored Polygon, done - processPolyPolygonColorPrimitive2D(*aTemp); return; } } @@ -1941,7 +1960,10 @@ void CairoPixelProcessor2D::processFillGraphicPrimitive2D( cairo_pattern_set_extend(sourcepattern, CAIRO_EXTEND_REPEAT); // paint - cairo_paint(mpRT); + if (rFillGraphicPrimitive2D.hasTransparency()) + cairo_paint_with_alpha(mpRT, 1.0 - rFillGraphicPrimitive2D.getTransparency()); + else + cairo_paint(mpRT); static bool bRenderTransformationBounds(false); if (bRenderTransformationBounds) diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx index 7bc422a98f43..986515c6e339 100644 --- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx @@ -1803,6 +1803,20 @@ void D2DPixelProcessor2D::processSingleLinePrimitive2D( void D2DPixelProcessor2D::processFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D) { + if (rFillGraphicPrimitive2D.getTransparency() < 0.0 + || rFillGraphicPrimitive2D.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + + if (rFillGraphicPrimitive2D.hasTransparency()) + { + // cannot handle yet, use decomposition + process(rFillGraphicPrimitive2D); + return; + } + BitmapEx aPreparedBitmap; basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange()); constexpr double fBigDiscreteArea(300.0 * 300.0); diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx index a6fc3978b733..435bc7f804fe 100644 --- a/drawinglayer/source/processor2d/hittestprocessor2d.cxx +++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx @@ -24,6 +24,7 @@ #include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> @@ -143,6 +144,46 @@ namespace drawinglayer::processor2d return bRetval; } + void HitTestProcessor2D::checkBitmapHit(basegfx::B2DRange aRange, const BitmapEx& rBitmapEx, const basegfx::B2DHomMatrix& rTransform) + { + if(!getHitTextOnly()) + { + // The recently added BitmapEx::GetTransparency() makes it easy to extend + // the BitmapPrimitive2D HitTest to take the contained BitmapEx and it's + // transparency into account + if(!aRange.isEmpty()) + { + const Size& rSizePixel(rBitmapEx.GetSizePixel()); + + // When tiled rendering, don't bother with the pixel size of the candidate. + if(rSizePixel.Width() && rSizePixel.Height() && !comphelper::LibreOfficeKit::isActive()) + { + basegfx::B2DHomMatrix aBackTransform( + getViewInformation2D().getObjectToViewTransformation() * + rTransform); + aBackTransform.invert(); + + const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition()); + const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); + + if(aUnitRange.isInside(aRelativePoint)) + { + const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width())); + const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height())); + + mbHit = (0 != rBitmapEx.GetAlpha(nX, nY)); + } + } + else + { + // fallback to standard HitTest + const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange)); + mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); + } + } + } + } + void HitTestProcessor2D::check3DHit(const primitive2d::ScenePrimitive2D& rCandidate) { // calculate relative point in unified 2D scene @@ -416,49 +457,28 @@ namespace drawinglayer::processor2d break; } - case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + case PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D : { - if(!getHitTextOnly()) - { - // The recently added BitmapEx::GetTransparency() makes it easy to extend - // the BitmapPrimitive2D HitTest to take the contained BitmapEx and it's - // transparency into account - const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); - - if(!aRange.isEmpty()) - { - const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); - const BitmapEx aBitmapEx(rBitmapCandidate.getBitmap()); - const Size& rSizePixel(aBitmapEx.GetSizePixel()); - - // When tiled rendering, don't bother with the pixel size of the candidate. - if(rSizePixel.Width() && rSizePixel.Height() && !comphelper::LibreOfficeKit::isActive()) - { - basegfx::B2DHomMatrix aBackTransform( - getViewInformation2D().getObjectToViewTransformation() * - rBitmapCandidate.getTransform()); - aBackTransform.invert(); + // avoid decompose of this primitive by handling directly + const primitive2d::BitmapAlphaPrimitive2D& rBitmapAlphaCandidate(static_cast< const primitive2d::BitmapAlphaPrimitive2D& >(rCandidate)); - const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition()); - const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); - - if(aUnitRange.isInside(aRelativePoint)) - { - const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width())); - const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height())); - - mbHit = (0 != aBitmapEx.GetAlpha(nX, nY)); - } - } - else - { - // fallback to standard HitTest - const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange)); - mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); - } - } + if (!basegfx::fTools::equal(rBitmapAlphaCandidate.getTransparency(), 1.0)) + { + checkBitmapHit( + rCandidate.getB2DRange(getViewInformation2D()), + rBitmapAlphaCandidate.getBitmap(), + rBitmapAlphaCandidate.getTransform()); } - + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // use common tooling + const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + checkBitmapHit( + rCandidate.getB2DRange(getViewInformation2D()), + rBitmapCandidate.getBitmap(), + rBitmapCandidate.getTransform()); break; } case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index f12be451b03a..3ec3c4922260 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -1867,10 +1867,11 @@ void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D( aTransform.matrix[5] = aTransformPosition.getY(); pSvtGraphicFill.reset(new SvtGraphicFill( - getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd, - SvtGraphicFill::fillTexture, aTransform, rFillGraphicAttribute.getTiling(), - SvtGraphicFill::hatchSingle, Color(), SvtGraphicFill::GradientType::Linear, Color(), - Color(), 0, rFillGraphicAttribute.getGraphic())); + getFillPolyPolygon(aLocalPolyPolygon), Color(), rBitmapCandidate.getTransparency(), + SvtGraphicFill::fillEvenOdd, SvtGraphicFill::fillTexture, aTransform, + rFillGraphicAttribute.getTiling(), SvtGraphicFill::hatchSingle, Color(), + SvtGraphicFill::GradientType::Linear, Color(), Color(), 0, + rFillGraphicAttribute.getGraphic())); } // Do use decomposition; encapsulate with SvtGraphicFill diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index f7fd3fda7606..b783bdc69723 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -550,6 +550,20 @@ void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2 void VclProcessor2D::RenderFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) { + if (rFillBitmapCandidate.getTransparency() < 0.0 + || rFillBitmapCandidate.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + + if (rFillBitmapCandidate.hasTransparency()) + { + // cannot handle yet, use decomposition + process(rFillBitmapCandidate); + return; + } + bool bPrimitiveAccepted = RenderFillGraphicPrimitive2DImpl(rFillBitmapCandidate); if (!bPrimitiveAccepted) @@ -820,7 +834,8 @@ void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D( case GraphicType::Bitmap: { if (!rFillGraphicAttribute.getGraphic().IsTransparent() - && !rFillGraphicAttribute.getGraphic().IsAlpha()) + && !rFillGraphicAttribute.getGraphic().IsAlpha() + && !rPolygonCandidate.hasTransparency()) { // bitmap is not transparent and has no alpha const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count()); diff --git a/include/drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx b/include/drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx index f364ba7f19db..0b0e2ee4dbf2 100644 --- a/include/drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx +++ b/include/drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx @@ -32,6 +32,10 @@ namespace drawinglayer::primitive2d This primitive defines a tools::PolyPolygon filled with bitmap data (including transparence). The decomosition will create a MaskPrimitive2D containing a FillGraphicPrimitive2D. + + SDPR: support alpha now directly: the decompositon creates + FillGraphicPrimitive2D which also supports alpha directly + now. All direct usages are covered */ class DRAWINGLAYER_DLLPUBLIC PolyPolygonGraphicPrimitive2D final : public BufferedDecompositionPrimitive2D @@ -46,6 +50,9 @@ private: /// the bitmap fill definition (may include tiling) attribute::FillGraphicAttribute maFillGraphic; + /// the transparency in range [0.0 .. 1.0] + double mfTransparency; + /// local decomposition. virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; @@ -53,12 +60,15 @@ private: public: PolyPolygonGraphicPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - const attribute::FillGraphicAttribute& rFillGraphic); + const attribute::FillGraphicAttribute& rFillGraphic, + double fTransparency = 0.0); /// data read access const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; } const basegfx::B2DRange& getDefinitionRange() const { return maDefinitionRange; } const attribute::FillGraphicAttribute& getFillGraphic() const { return maFillGraphic; } + double getTransparency() const { return mfTransparency; } + bool hasTransparency() const { return !basegfx::fTools::equalZero(mfTransparency); } /// compare operator virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; diff --git a/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx b/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx index d43df3da84e4..784117ae880c 100644 --- a/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx +++ b/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx @@ -56,6 +56,12 @@ namespace drawinglayer::primitive2d Renderers should handle this primitive; it has a geometrically correct decomposition, but on pixel outputs the areas where the tiled pieces are aligned tend to show up (one overlapping or empty pixel) + + SDPR: support alpha directly now. If a primitive processor + cannot deal with it, use it's decomposition. The decompositon + uses create2DDecompositionOfGraphic, there all paths are now + capable of handling a given alpha, including metafile, SVG and + animated graphics */ class DRAWINGLAYER_DLLPUBLIC FillGraphicPrimitive2D final : public BufferedDecompositionPrimitive2D { @@ -69,6 +75,9 @@ namespace drawinglayer::primitive2d /// the evtl. buffered OffsetXYCreatedBitmap BitmapEx maOffsetXYCreatedBitmap; + /// the transparency in range [0.0 .. 1.0] + double mfTransparency; + /// local decomposition. virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; @@ -87,12 +96,15 @@ namespace drawinglayer::primitive2d /// constructor FillGraphicPrimitive2D( basegfx::B2DHomMatrix aTransformation, - const attribute::FillGraphicAttribute& rFillGraphic); + const attribute::FillGraphicAttribute& rFillGraphic, + double fTransparency = 0.0); /// data read access const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; } const attribute::FillGraphicAttribute& getFillGraphic() const { return maFillGraphic; } const BitmapEx& getOffsetXYCreatedBitmap() const { return maOffsetXYCreatedBitmap; } + double getTransparency() const { return mfTransparency; } + bool hasTransparency() const { return !basegfx::fTools::equalZero(mfTransparency); } /// compare operator virtual bool operator==( const BasePrimitive2D& rPrimitive ) const override; diff --git a/include/drawinglayer/primitive2d/graphicprimitivehelper2d.hxx b/include/drawinglayer/primitive2d/graphicprimitivehelper2d.hxx index 6aa8ef5191ce..024ad3fce28a 100644 --- a/include/drawinglayer/primitive2d/graphicprimitivehelper2d.hxx +++ b/include/drawinglayer/primitive2d/graphicprimitivehelper2d.hxx @@ -33,11 +33,16 @@ namespace drawinglayer::primitive2d and GraphicPrimitive2D at the same time. It is able to handle Bitmaps (with the sub-categories animated bitmap, and SVG), and Metafiles. + + SDPR: create2DDecompositionOfGraphic now supports a given + alpha directly: all paths are now capable of handling a + given alpha, including metafile, SVG and animated graphics */ void DRAWINGLAYER_DLLPUBLIC create2DDecompositionOfGraphic( Primitive2DContainer& rContainer, const Graphic& rGraphic, - const basegfx::B2DHomMatrix& rTransform); + const basegfx::B2DHomMatrix& rTransform, + double fTransparency = 0.0); /** Helper to embed given sequence of primitives to evtl. a stack of ModifiedColorPrimitive2D's to get all the needed modifications diff --git a/include/drawinglayer/processor2d/hittestprocessor2d.hxx b/include/drawinglayer/processor2d/hittestprocessor2d.hxx index 6d092efe983a..0f1f6d232d85 100644 --- a/include/drawinglayer/processor2d/hittestprocessor2d.hxx +++ b/include/drawinglayer/processor2d/hittestprocessor2d.hxx @@ -27,6 +27,7 @@ namespace basegfx { class B2DPolygon; } namespace basegfx { class B2DPolyPolygon; } namespace drawinglayer::primitive2d { class ScenePrimitive2D; } +class BitmapEx; namespace drawinglayer::processor2d { @@ -65,6 +66,7 @@ namespace drawinglayer::processor2d const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DVector& rDiscreteHitTolerancePerAxis) const; void check3DHit(const primitive2d::ScenePrimitive2D& rCandidate); + void checkBitmapHit(basegfx::B2DRange aRange, const BitmapEx& rBitmapEx, const basegfx::B2DHomMatrix& rTransform); public: HitTestProcessor2D( diff --git a/svx/source/sdr/contact/viewobjectcontact.cxx b/svx/source/sdr/contact/viewobjectcontact.cxx index 987f51b6d0cb..e7ee53f55f74 100644 --- a/svx/source/sdr/contact/viewobjectcontact.cxx +++ b/svx/source/sdr/contact/viewobjectcontact.cxx @@ -126,7 +126,11 @@ void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::p { // create temporary GraphicPrimitive to recursively extract evtl. animation drawinglayer::primitive2d::Primitive2DContainer aContainer; - drawinglayer::primitive2d::create2DDecompositionOfGraphic(aContainer, rGraphic, rFill.getTransformation()); + drawinglayer::primitive2d::create2DDecompositionOfGraphic( + aContainer, + rGraphic, + rFill.getTransformation(), + rFill.getTransparency()); process(aContainer); } diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index 7f315cb2486a..53b0ae33b711 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -434,6 +434,16 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const } else if(!rFill.getFillGraphic().isDefault()) { + // SDPR: check early if we have alpha and add directly + if(0.0 != rFill.getTransparence()) + { + return new PolyPolygonGraphicPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange), + rFill.getTransparence()); + } + pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D( rPolyPolygon, rDefinitionRange, @@ -448,6 +458,7 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const } else { + // SDPR: check early if we have alpha and add directly if(0.0 != rFill.getTransparence()) { return new PolyPolygonRGBAPrimitive2D( |