[Libreoffice-commits] core.git: sw/source vcl/headless vcl/inc vcl/source

Armin Le Grand (Collabora) (via logerrit) logerrit at kemper.freedesktop.org
Fri Feb 21 19:17:30 UTC 2020


 sw/source/core/doc/notxtfrm.cxx |  228 +++++++++++++++++++++++++++++---
 sw/source/core/inc/frmtool.hxx  |    9 +
 sw/source/core/inc/notxtfrm.hxx |    6 
 vcl/headless/svpbmp.cxx         |    7 
 vcl/headless/svpgdi.cxx         |  219 +++++++++++++++++++++++++++---
 vcl/inc/headless/svpbmp.hxx     |   26 +++
 vcl/source/outdev/bitmap.cxx    |  285 ++++++++++++++++++++++++----------------
 7 files changed, 625 insertions(+), 155 deletions(-)

New commits:
commit 828504974d70111e4a35b31d579cf42fe660a660
Author:     Armin Le Grand (Collabora) <armin.le.grand at me.com>
AuthorDate: Fri Feb 21 16:58:17 2020 +0100
Commit:     Armin Le Grand <Armin.Le.Grand at me.com>
CommitDate: Fri Feb 21 20:16:59 2020 +0100

    tdf#130768 speedup huge pixel graphics Cairo
    
    For more information/documentation please
    refer to the bugzilla task
    
    Fixed a crash in CppunitTest_desktop_lib which
    led to a missing test of mpGraphics in
    OutputDevice::DrawTransformedBitmapEx. Other
    public methods test that and one of the goals
    of the cange was to use that method more often,
    so this may have never been detected before
    
    Change-Id: I10e57bd05db0c8cf868ff98d63f5af3d116a3015
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89230
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <Armin.Le.Grand at me.com>

diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index 9858cc3eebfa..86a74dc66fc2 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -74,6 +74,14 @@
 #include <basegfx/polygon/b2dpolygontools.hxx>
 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
 
+// MM02 needed for VOC mechanism and getting the OC - may be moved to an own file
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+
 using namespace com::sun::star;
 
 static bool GetRealURL( const SwGrfNode& rNd, OUString& rText )
@@ -140,7 +148,9 @@ static void lcl_PaintReplacement( const SwRect &rRect, const OUString &rText,
 SwNoTextFrame::SwNoTextFrame(SwNoTextNode * const pNode, SwFrame* pSib )
 :   SwContentFrame( pNode, pSib ),
     // RotateFlyFrame3
-    mpTransformableSwFrame()
+    mpTransformableSwFrame(),
+    // MM02
+    mpViewContact()
 {
     mnFrameType = SwFrameType::NoTxt;
 }
@@ -917,6 +927,7 @@ static bool paintUsingPrimitivesHelper(
     return false;
 }
 
+// MM02 original using falölback to VOC and primitive-based version
 void paintGraphicUsingPrimitivesHelper(
     vcl::RenderContext & rOutputDevice,
     GraphicObject const& rGrfObj,
@@ -931,12 +942,30 @@ void paintGraphicUsingPrimitivesHelper(
     // -> the primitive renderer will create the needed pdf export data
     // -> if bitmap content, it will be cached system-dependent
     drawinglayer::primitive2d::Primitive2DContainer aContent(1);
-
     aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D(
         rGraphicTransform,
         rGrfObj,
         rGraphicAttr);
 
+    // MM02 use primitive-based version for visualization
+    paintGraphicUsingPrimitivesHelper(
+        rOutputDevice,
+        aContent,
+        rGraphicTransform,
+        rName,
+        rTitle,
+        rDescription);
+}
+
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+    vcl::RenderContext & rOutputDevice,
+    drawinglayer::primitive2d::Primitive2DContainer& rContent,
+    const basegfx::B2DHomMatrix& rGraphicTransform,
+    const OUString& rName,
+    const OUString& rTitle,
+    const OUString& rDescription)
+{
     // RotateFlyFrame3: If ClipRegion is set at OutputDevice, we
     // need to use that. Usually the renderer would be a VCL-based
     // PrimitiveRenderer, but there are system-specific shortcuts that
@@ -1003,9 +1032,12 @@ void paintGraphicUsingPrimitivesHelper(
                 aTarget.append(aClip);
             }
 
-            aContent[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
-                aTarget,
-                aContent);
+            drawinglayer::primitive2d::MaskPrimitive2D* pNew(
+                new drawinglayer::primitive2d::MaskPrimitive2D(
+                    aTarget,
+                    rContent));
+            rContent.resize(1);
+            rContent[0] = pNew;
         }
     }
 
@@ -1013,11 +1045,14 @@ void paintGraphicUsingPrimitivesHelper(
     {
         // Embed to ObjectInfoPrimitive2D when we have Name/Title/Description
         // information available
-        aContent[0] = new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
-            aContent,
-            rName,
-            rTitle,
-            rDescription);
+        drawinglayer::primitive2d::ObjectInfoPrimitive2D* pNew(
+            new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
+                rContent,
+                rName,
+                rTitle,
+                rDescription));
+        rContent.resize(1);
+        rContent[0] = pNew;
     }
 
     basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
@@ -1025,11 +1060,111 @@ void paintGraphicUsingPrimitivesHelper(
 
     paintUsingPrimitivesHelper(
         rOutputDevice,
-        aContent,
+        rContent,
         aTargetRange,
         aTargetRange);
 }
 
+// DrawContact section
+namespace { // anonymous namespace
+class ViewObjectContactOfSwNoTextFrame : public sdr::contact::ViewObjectContact
+{
+protected:
+    virtual drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequence(
+        const sdr::contact::DisplayInfo& rDisplayInfo) const override;
+
+public:
+    ViewObjectContactOfSwNoTextFrame(
+        sdr::contact::ObjectContact& rObjectContact,
+        sdr::contact::ViewContact& rViewContact);
+};
+
+class ViewContactOfSwNoTextFrame : public sdr::contact::ViewContact
+{
+private:
+    // owner
+    const SwNoTextFrame&        mrSwNoTextFrame;
+
+protected:
+    // Create an Object-Specific ViewObjectContact, set ViewContact and
+    // ObjectContact. Always needs to return something.
+    virtual sdr::contact::ViewObjectContact& CreateObjectSpecificViewObjectContact(
+        sdr::contact::ObjectContact& rObjectContact) override;
+
+public:
+    // read-access to owner
+    const SwNoTextFrame& getSwNoTextFrame() const { return mrSwNoTextFrame; }
+
+    // basic constructor, used from SwNoTextFrame.
+    explicit ViewContactOfSwNoTextFrame(const SwNoTextFrame& rSwNoTextFrame);
+};
+
+drawinglayer::primitive2d::Primitive2DContainer ViewObjectContactOfSwNoTextFrame::createPrimitive2DSequence(
+    const sdr::contact::DisplayInfo& /*rDisplayInfo*/) const
+{
+    // MM02 get all the parameters formally used in paintGraphicUsingPrimitivesHelper
+    ViewContactOfSwNoTextFrame& rVCOfNTF(static_cast<ViewContactOfSwNoTextFrame&>(GetViewContact()));
+    const SwNoTextFrame& rSwNoTextFrame(rVCOfNTF.getSwNoTextFrame());
+    SwNoTextNode& rNoTNd(const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(rSwNoTextFrame.GetNode())));
+    SwGrfNode* pGrfNd(rNoTNd.GetGrfNode());
+
+    if(nullptr != pGrfNd)
+    {
+        const bool bPrn(GetObjectContact().isOutputToPrinter() || GetObjectContact().isOutputToRecordingMetaFile());
+        const GraphicObject& rGrfObj(pGrfNd->GetGrfObj(bPrn));
+        GraphicAttr aGraphicAttr;
+        pGrfNd->GetGraphicAttr(aGraphicAttr, &rSwNoTextFrame);
+        const basegfx::B2DHomMatrix aGraphicTransform(rSwNoTextFrame.getFrameAreaTransformation());
+
+        // MM02 this is the right place in the VOC-Mechanism to create
+        // the primitives for visualization - these will be automatically
+        // buffered and reused
+        drawinglayer::primitive2d::Primitive2DContainer aContent(1);
+        aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D(
+            aGraphicTransform,
+            rGrfObj,
+            aGraphicAttr);
+
+        return aContent;
+    }
+
+    return drawinglayer::primitive2d::Primitive2DContainer();
+}
+
+ViewObjectContactOfSwNoTextFrame::ViewObjectContactOfSwNoTextFrame(
+    sdr::contact::ObjectContact& rObjectContact,
+    sdr::contact::ViewContact& rViewContact)
+:   sdr::contact::ViewObjectContact(rObjectContact, rViewContact)
+{
+}
+
+sdr::contact::ViewObjectContact& ViewContactOfSwNoTextFrame::CreateObjectSpecificViewObjectContact(
+    sdr::contact::ObjectContact& rObjectContact)
+{
+    sdr::contact::ViewObjectContact* pRetval = new ViewObjectContactOfSwNoTextFrame(rObjectContact, *this);
+    return *pRetval;
+}
+
+ViewContactOfSwNoTextFrame::ViewContactOfSwNoTextFrame(
+    const SwNoTextFrame& rSwNoTextFrame
+)
+:   sdr::contact::ViewContact(),
+    mrSwNoTextFrame(rSwNoTextFrame)
+{
+}
+} // end of anonymous namespace
+
+sdr::contact::ViewContact& SwNoTextFrame::GetViewContact() const
+{
+    if(!mpViewContact)
+    {
+        const_cast< SwNoTextFrame* >(this)->mpViewContact =
+            std::make_unique<ViewContactOfSwNoTextFrame>(*this);
+    }
+
+    return *mpViewContact;
+}
+
 /** Paint the graphic.
 
     We require either a QuickDraw-Bitmap or a graphic here. If we do not have
@@ -1155,16 +1290,67 @@ void SwNoTextFrame::PaintPicture( vcl::RenderContext* pOut, const SwRect &rGrfAr
                 }
                 else
                 {
-                    const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
-
-                    paintGraphicUsingPrimitivesHelper(
-                        *pOut,
-                        rGrfObj,
-                        aGrfAttr,
-                        aGraphicTransform,
-                        nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
-                        rNoTNd.GetTitle(),
-                        rNoTNd.GetDescription());
+                    // MM02 To allow system-dependent buffering of the involved
+                    // bitmaps it is necessary to re-use the involved primitives
+                    // and their already executed decomposition (also for
+                    // performance reasons). This is usually done in DrawingLayer
+                    // by using the VOC-Mechanism (see descriptions elsewhere).
+                    // To get that here, make the involved SwNoTextFrame (this)
+                    // a sdr::contact::ViewContact supplier by supporing
+                    // a GetViewContact() - call. For ObjectContact we can use
+                    // the already exising ObjectContact from the involved
+                    // DrawingLayer. For tis, the helper classes
+                    //     ViewObjectContactOfSwNoTextFrame
+                    //     ViewContactOfSwNoTextFrame
+                    // are created which support the VOC-mechanism in it's minimal
+                    // form. This allows automatic and view-dependent (multiple edit
+                    // windows, print, etc.) re-use of the created primitives.
+                    // Also: Will be very useful when completely changing the Writer
+                    // repaint to VOC and Primitives, too.
+                    static const char* pDisableMM02Goodies(getenv("SAL_DISABLE_MM02_GOODIES"));
+                    static bool bUseViewObjectContactMechanism(nullptr == pDisableMM02Goodies);
+
+                    if(bUseViewObjectContactMechanism)
+                    {
+                        // MM02 use VOC-mechanism and buffer primitives
+                        SwViewShellImp* pImp(pShell->Imp());
+                        SdrPageView* pPageView(nullptr != pImp ? pImp->GetPageView() : nullptr);
+                        SdrPageWindow* pPageWindow(nullptr != pPageView ? pPageView->FindPageWindow(*pShell->GetOut()) : nullptr);
+
+                        if(nullptr != pPageWindow)
+                        {
+                            sdr::contact::ObjectContact& rOC(pPageWindow->GetObjectContact());
+                            sdr::contact::ViewContact& rVC(GetViewContact());
+                            sdr::contact::ViewObjectContact& rVOC(rVC.GetViewObjectContact(rOC));
+                            sdr::contact::DisplayInfo aDisplayInfo;
+
+                            drawinglayer::primitive2d::Primitive2DContainer aPrimitives(rVOC.getPrimitive2DSequence(aDisplayInfo));
+                            const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+
+                            paintGraphicUsingPrimitivesHelper(
+                                *pOut,
+                                aPrimitives,
+                                aGraphicTransform,
+                                nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
+                                rNoTNd.GetTitle(),
+                                rNoTNd.GetDescription());
+                        }
+                    }
+                    else
+                    {
+                        // MM02 fallback to direct paint with primitive-recreation
+                        // which will block reusage of system-dependent bitmap data
+                        const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+
+                        paintGraphicUsingPrimitivesHelper(
+                            *pOut,
+                            rGrfObj,
+                            aGrfAttr,
+                            aGraphicTransform,
+                            nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
+                            rNoTNd.GetTitle(),
+                            rNoTNd.GetDescription());
+                    }
                 }
             }
             else
diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx
index c8447d8dd0f1..c070ed2bf28c 100644
--- a/sw/source/core/inc/frmtool.hxx
+++ b/sw/source/core/inc/frmtool.hxx
@@ -100,6 +100,15 @@ void paintGraphicUsingPrimitivesHelper(
     const OUString& rTitle,
     const OUString& rDescription);
 
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+    vcl::RenderContext & rOutputDevice,
+    drawinglayer::primitive2d::Primitive2DContainer& rContent,
+    const basegfx::B2DHomMatrix& rGraphicTransform,
+    const OUString& rName,
+    const OUString& rTitle,
+    const OUString& rDescription);
+
 // method to align rectangle.
 // Created declaration here to avoid <extern> declarations
 void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext );
diff --git a/sw/source/core/inc/notxtfrm.hxx b/sw/source/core/inc/notxtfrm.hxx
index e726ec6402ed..c958da594e27 100644
--- a/sw/source/core/inc/notxtfrm.hxx
+++ b/sw/source/core/inc/notxtfrm.hxx
@@ -21,6 +21,8 @@
 
 #include "cntfrm.hxx"
 #include <node.hxx>
+// MM02
+#include <svx/sdr/contact/viewcontact.hxx>
 
 class SwNoTextNode;
 class OutputDevice;
@@ -50,6 +52,10 @@ private:
 
     void ClearCache();
 
+    // MM02
+    std::unique_ptr<sdr::contact::ViewContact> mpViewContact;
+    sdr::contact::ViewContact& GetViewContact() const;
+
 protected:
     virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
     virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override;
diff --git a/vcl/headless/svpbmp.cxx b/vcl/headless/svpbmp.cxx
index 6dd5aeb64bfb..4d881f025d0b 100644
--- a/vcl/headless/svpbmp.cxx
+++ b/vcl/headless/svpbmp.cxx
@@ -34,6 +34,13 @@
 
 using namespace basegfx;
 
+SvpSalBitmap::SvpSalBitmap()
+:   SalBitmap(),
+    basegfx::SystemDependentDataHolder(), // MM02
+    mpDIB()
+{
+}
+
 SvpSalBitmap::~SvpSalBitmap()
 {
     Destroy();
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index cd59eee1af16..9a2fe936e782 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -323,6 +323,44 @@ namespace
         SourceHelper& operator=(const SourceHelper&) = delete;
     };
 
+    class SystemDependentData_SourceHelper : public basegfx::SystemDependentData
+    {
+    private:
+        std::shared_ptr<SourceHelper>       maSourceHelper;
+
+    public:
+        SystemDependentData_SourceHelper(
+            basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+            const std::shared_ptr<SourceHelper>& rSourceHelper)
+        :   basegfx::SystemDependentData(rSystemDependentDataManager),
+            maSourceHelper(rSourceHelper)
+        {
+        }
+
+        const std::shared_ptr<SourceHelper>& getSourceHelper() const { return maSourceHelper; };
+        virtual sal_Int64 estimateUsageInBytes() const override;
+    };
+
+    // MM02 class to allow buffering of SourceHelper
+    sal_Int64 SystemDependentData_SourceHelper::estimateUsageInBytes() const
+    {
+        sal_Int64 nRetval(0);
+        cairo_surface_t* source(maSourceHelper ? maSourceHelper->getSurface() : nullptr);
+
+        if(source)
+        {
+            const long nStride(cairo_image_surface_get_stride(source));
+            const long nHeight(cairo_image_surface_get_height(source));
+
+            if(0 != nStride && 0 != nHeight)
+            {
+                nRetval = nStride * nHeight;
+            }
+        }
+
+        return nRetval;
+    }
+
     class MaskHelper
     {
     public:
@@ -388,6 +426,123 @@ namespace
         MaskHelper(const MaskHelper&) = delete;
         MaskHelper& operator=(const MaskHelper&) = delete;
     };
+
+    class SystemDependentData_MaskHelper : public basegfx::SystemDependentData
+    {
+    private:
+        std::shared_ptr<MaskHelper>       maMaskHelper;
+
+    public:
+        SystemDependentData_MaskHelper(
+            basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+            const std::shared_ptr<MaskHelper>& rMaskHelper)
+        :   basegfx::SystemDependentData(rSystemDependentDataManager),
+            maMaskHelper(rMaskHelper)
+        {
+        }
+
+        const std::shared_ptr<MaskHelper>& getMaskHelper() const { return maMaskHelper; };
+        virtual sal_Int64 estimateUsageInBytes() const override;
+    };
+
+    // MM02 class to allow buffering of MaskHelper
+    sal_Int64 SystemDependentData_MaskHelper::estimateUsageInBytes() const
+    {
+        sal_Int64 nRetval(0);
+        cairo_surface_t* mask(maMaskHelper ? maMaskHelper->getMask() : nullptr);
+
+        if(mask)
+        {
+            const long nStride(cairo_image_surface_get_stride(mask));
+            const long nHeight(cairo_image_surface_get_height(mask));
+
+            if(0 != nStride && 0 != nHeight)
+            {
+                nRetval = nStride * nHeight;
+            }
+        }
+
+        return nRetval;
+    }
+
+    // MM02 decide to use buffers or not
+    static const char* pDisableMM02Goodies(getenv("SAL_DISABLE_MM02_GOODIES"));
+    static bool bUseBuffer(nullptr == pDisableMM02Goodies);
+    static long nMinimalSquareSizeToBuffer(64*64);
+
+    void tryToUseSourceBuffer(
+        const SalBitmap& rSourceBitmap,
+        std::shared_ptr<SourceHelper>& rSurface)
+    {
+        // MM02 try to access buffered SourceHelper
+        std::shared_ptr<SystemDependentData_SourceHelper> pSystemDependentData_SourceHelper;
+        const bool bBufferSource(bUseBuffer
+            && rSourceBitmap.GetSize().Width() * rSourceBitmap.GetSize().Height() > nMinimalSquareSizeToBuffer);
+
+        if(bBufferSource)
+        {
+            const SvpSalBitmap& rSrcBmp(static_cast<const SvpSalBitmap&>(rSourceBitmap));
+            pSystemDependentData_SourceHelper = rSrcBmp.getSystemDependentData<SystemDependentData_SourceHelper>();
+
+            if(pSystemDependentData_SourceHelper)
+            {
+                // reuse buffered data
+                rSurface = pSystemDependentData_SourceHelper->getSourceHelper();
+            }
+        }
+
+        if(!rSurface)
+        {
+            // create data on-demand
+            rSurface = std::make_shared<SourceHelper>(rSourceBitmap);
+
+            if(bBufferSource)
+            {
+                // add to buffering mechanism to potentially reuse next time
+                const SvpSalBitmap& rSrcBmp(static_cast<const SvpSalBitmap&>(rSourceBitmap));
+                rSrcBmp.addOrReplaceSystemDependentData<SystemDependentData_SourceHelper>(
+                    ImplGetSystemDependentDataManager(),
+                    rSurface);
+            }
+        }
+    }
+
+    void tryToUseMaskBuffer(
+        const SalBitmap& rMaskBitmap,
+        std::shared_ptr<MaskHelper>& rMask)
+    {
+        // MM02 try to access buffered MaskHelper
+        std::shared_ptr<SystemDependentData_MaskHelper> pSystemDependentData_MaskHelper;
+        const bool bBufferMask(bUseBuffer
+            && rMaskBitmap.GetSize().Width() * rMaskBitmap.GetSize().Height() > nMinimalSquareSizeToBuffer);
+
+        if(bBufferMask)
+        {
+            const SvpSalBitmap& rSrcBmp(static_cast<const SvpSalBitmap&>(rMaskBitmap));
+            pSystemDependentData_MaskHelper = rSrcBmp.getSystemDependentData<SystemDependentData_MaskHelper>();
+
+            if(pSystemDependentData_MaskHelper)
+            {
+                // reuse buffered data
+                rMask = pSystemDependentData_MaskHelper->getMaskHelper();
+            }
+        }
+
+        if(!rMask)
+        {
+            // create data on-demand
+            rMask = std::make_shared<MaskHelper>(rMaskBitmap);
+
+            if(bBufferMask)
+            {
+                // add to buffering mechanism to potentially reuse next time
+                const SvpSalBitmap& rSrcBmp(static_cast<const SvpSalBitmap&>(rMaskBitmap));
+                rSrcBmp.addOrReplaceSystemDependentData<SystemDependentData_MaskHelper>(
+                    ImplGetSystemDependentDataManager(),
+                    rMask);
+            }
+        }
+    }
 }
 
 bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, const SalBitmap& rSourceBitmap, const SalBitmap& rAlphaBitmap )
@@ -398,16 +553,22 @@ bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, const SalBitmap& rS
         return false;
     }
 
-    SourceHelper aSurface(rSourceBitmap);
-    cairo_surface_t* source = aSurface.getSurface();
+    // MM02 try to access buffered SourceHelper
+    std::shared_ptr<SourceHelper> aSurface;
+    tryToUseSourceBuffer(rSourceBitmap, aSurface);
+    cairo_surface_t* source = aSurface->getSurface();
+
     if (!source)
     {
         SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
         return false;
     }
 
-    MaskHelper aMask(rAlphaBitmap);
-    cairo_surface_t *mask = aMask.getMask();
+    // MM02 try to access buffered MaskHelper
+    std::shared_ptr<MaskHelper> aMask;
+    tryToUseMaskBuffer(rAlphaBitmap, aMask);
+    cairo_surface_t *mask = aMask->getMask();
+
     if (!mask)
     {
         SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
@@ -468,29 +629,34 @@ bool SvpSalGraphics::drawTransformedBitmap(
         return false;
     }
 
-    SourceHelper aSurface(rSourceBitmap);
-    cairo_surface_t* source = aSurface.getSurface();
-    if (!source)
+    // MM02 try to access buffered SourceHelper
+    std::shared_ptr<SourceHelper> aSurface;
+    tryToUseSourceBuffer(rSourceBitmap, aSurface);
+    cairo_surface_t* source(aSurface->getSurface());
+
+    if(!source)
     {
         SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
         return false;
     }
 
-    std::unique_ptr<MaskHelper> xMask;
-    cairo_surface_t *mask = nullptr;
-    if (pAlphaBitmap)
+    // MM02 try to access buffered MaskHelper
+    std::shared_ptr<MaskHelper> aMask;
+
+    if(nullptr != pAlphaBitmap)
     {
-        xMask.reset(new MaskHelper(*pAlphaBitmap));
-        mask = xMask->getMask();
-        if (!mask)
-        {
-            SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
-            return false;
-        }
+        tryToUseMaskBuffer(*pAlphaBitmap, aMask);
     }
 
-    const Size aSize = rSourceBitmap.GetSize();
+    // access cairo_surface_t from MaskHelper
+    cairo_surface_t* mask(aMask ? aMask->getMask() : nullptr);
+    if(nullptr != pAlphaBitmap && nullptr == mask)
+    {
+        SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
+        return false;
+    }
 
+    const Size aSize = rSourceBitmap.GetSize();
     cairo_t* cr = getCairoContext(false);
     clipRegion(cr);
 
@@ -1761,8 +1927,17 @@ void SvpSalGraphics::copyBits( const SalTwoRect& rTR,
 
 void SvpSalGraphics::drawBitmap(const SalTwoRect& rTR, const SalBitmap& rSourceBitmap)
 {
-    SourceHelper aSurface(rSourceBitmap);
-    cairo_surface_t* source = aSurface.getSurface();
+    // MM02 try to access buffered SourceHelper
+    std::shared_ptr<SourceHelper> aSurface;
+    tryToUseSourceBuffer(rSourceBitmap, aSurface);
+    cairo_surface_t* source = aSurface->getSurface();
+
+    if (!source)
+    {
+        SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
+        return;
+    }
+
     copyWithOperator(rTR, source, CAIRO_OPERATOR_OVER);
 }
 
@@ -1786,6 +1961,10 @@ void SvpSalGraphics::drawMask( const SalTwoRect& rTR,
 {
     /** creates an image from the given rectangle, replacing all black pixels
      *  with nMaskColor and make all other full transparent */
+    // MM02 here decided *against* using buffered SourceHelper
+    // because the data gets somehow 'unmuliplied'. This may also be
+    // done just once, but I am not sure if this is safe to do.
+    // So for now dispense re-using data here.
     SourceHelper aSurface(rSalBitmap, true); // The mask is argb32
     if (!aSurface.getSurface())
     {
diff --git a/vcl/inc/headless/svpbmp.hxx b/vcl/inc/headless/svpbmp.hxx
index 7f79dda8e9bc..1551fc844a82 100644
--- a/vcl/inc/headless/svpbmp.hxx
+++ b/vcl/inc/headless/svpbmp.hxx
@@ -23,11 +23,13 @@
 #include <sal/config.h>
 
 #include <salbmp.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
 
-class VCL_DLLPUBLIC SvpSalBitmap final : public SalBitmap
+class VCL_DLLPUBLIC SvpSalBitmap final : public SalBitmap, public basegfx::SystemDependentDataHolder // MM02
 {
     std::unique_ptr<BitmapBuffer> mpDIB;
 public:
+             SvpSalBitmap();
     virtual ~SvpSalBitmap() override;
 
     // SalBitmap
@@ -58,6 +60,28 @@ public:
     virtual bool            ScalingSupported() const override;
     virtual bool            Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag ) override;
     virtual bool            Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol ) override;
+
+    // MM02 exclusive management op's for SystemDependentData at WinSalBitmap
+    template<class T>
+    std::shared_ptr<T> getSystemDependentData() const
+    {
+        return std::static_pointer_cast<T>(basegfx::SystemDependentDataHolder::getSystemDependentData(typeid(T).hash_code()));
+    }
+
+    template<class T, class... Args>
+    std::shared_ptr<T> addOrReplaceSystemDependentData(basegfx::SystemDependentDataManager& manager, Args&&... args) const
+    {
+        std::shared_ptr<T> r = std::make_shared<T>(manager, std::forward<Args>(args)...);
+
+        // tdf#129845 only add to buffer if a relevant buffer time is estimated
+        if(r->calculateCombinedHoldCyclesInSeconds() > 0)
+        {
+            basegfx::SystemDependentData_SharedPtr r2(r);
+            const_cast< SvpSalBitmap* >(this)->basegfx::SystemDependentDataHolder::addOrReplaceSystemDependentData(r2);
+        }
+
+        return r;
+    }
 };
 
 #endif // INCLUDED_VCL_INC_HEADLESS_SVPBMP_HXX
diff --git a/vcl/source/outdev/bitmap.cxx b/vcl/source/outdev/bitmap.cxx
index 7ab069c2f443..9821fa0aade5 100644
--- a/vcl/source/outdev/bitmap.cxx
+++ b/vcl/source/outdev/bitmap.cxx
@@ -1157,6 +1157,32 @@ bool OutputDevice::TransformAndReduceBitmapExToTargetRange(
     return true;
 }
 
+// MM02 add som etest class to get a simple timer-based output to be able
+// to check if it gets faster - and how much. Uncomment next line or set
+// DO_TIME_TEST for compile tiome if you want to use it
+// #define DO_TIME_TEST
+#ifdef DO_TIME_TEST
+#include <tools/time.hxx>
+struct LocalTimeTest
+{
+    const sal_uInt64 nStartTime;
+    LocalTimeTest() : nStartTime(tools::Time::GetSystemTicks()) {}
+    ~LocalTimeTest()
+    {
+        const sal_uInt64 nEndTime(tools::Time::GetSystemTicks());
+        const sal_uInt64 nDiffTime(nEndTime - nStartTime);
+
+        if(nDiffTime > 0)
+        {
+            OStringBuffer aOutput("Time: ");
+            OString aNumber(OString::number(nDiffTime));
+            aOutput.append(aNumber);
+            OSL_FAIL(aOutput.getStr());
+        }
+    }
+};
+#endif
+
 void OutputDevice::DrawTransformedBitmapEx(
     const basegfx::B2DHomMatrix& rTransformation,
     const BitmapEx& rBitmapEx)
@@ -1169,6 +1195,42 @@ void OutputDevice::DrawTransformedBitmapEx(
     if(rBitmapEx.IsEmpty())
         return;
 
+    // MM02 compared to other public methods of OutputDevice
+    // this test was missing and led to zero-ptr-accesses
+    if ( !mpGraphics && !AcquireGraphics() )
+        return;
+
+#ifdef DO_TIME_TEST
+    // MM02 start time test when some data (not for trivial stuff). Will
+    // trigger and show data when leaving this method by destructing helper
+    static const char* pEnableBitmapDrawTimerTimer(getenv("SAL_ENABLE_TIMER_BITMAPDRAW"));
+    static bool bUseTimer(nullptr != pEnableBitmapDrawTimerTimer);
+    std::unique_ptr<LocalTimeTest> aTimeTest(
+        bUseTimer && rBitmapEx.GetSizeBytes() > 10000
+        ? new LocalTimeTest()
+        : nullptr);
+#endif
+
+    // MM02 reorganize order: Prefer DrawTransformBitmapExDirect due
+    // to this having evolved and is improved on quite some systems.
+    // Check for exclusion parameters that may prevent using it
+    static bool bAllowPreferDirectPaint(true);
+    const bool bInvert(RasterOp::Invert == meRasterOp);
+    const bool bBitmapChangedColor(mnDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap | DrawModeFlags::GrayBitmap ));
+    const bool bMetafile(nullptr != mpMetaFile);
+    const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile);
+
+    if(bAllowPreferDirectPaint && bTryDirectPaint)
+    {
+        const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation);
+
+        if(DrawTransformBitmapExDirect(aFullTransform, rBitmapEx))
+        {
+            // we are done
+            return;
+        }
+    }
+
     // decompose matrix to check rotation and shear
     basegfx::B2DVector aScale, aTranslate;
     double fRotate, fShearX;
@@ -1178,8 +1240,6 @@ void OutputDevice::DrawTransformedBitmapEx(
     const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
     const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
 
-    const bool bMetafile = mpMetaFile != nullptr;
-
     if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
     {
         // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
@@ -1205,139 +1265,138 @@ void OutputDevice::DrawTransformedBitmapEx(
         return;
     }
 
-    // we have rotation,shear or mirror, check if some crazy mode needs the
-    // created transformed bitmap
-    const bool bInvert(RasterOp::Invert == meRasterOp);
-    const bool bBitmapChangedColor(mnDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap | DrawModeFlags::GrayBitmap ));
-    bool bDone(false);
-    basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation);
-    const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile );
-
+    // MM02 bAllowPreferDirectPaint may have been false to allow
+    // to specify order of executions, so give bTryDirectPaint a call
     if(bTryDirectPaint)
     {
-        bDone = DrawTransformBitmapExDirect(aFullTransform, rBitmapEx);
-    }
+        const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation);
 
-    if(!bDone)
-    {
-        // take the fallback when no rotate and shear, but mirror (else we would have done this above)
-        if(!bRotated && !bSheared)
+        if(DrawTransformBitmapExDirect(aFullTransform, rBitmapEx))
         {
-            // with no rotation or shear it can be mapped to DrawBitmapEx
-            // do *not* execute the mirroring here, it's done in the fallback
-            // #i124580# the correct DestSize needs to be calculated based on MaxXY values
-            const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
-            const Size aDestSize(
-                basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
-                basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
-
-            DrawBitmapEx(aDestPt, aDestSize, rBitmapEx);
+            // we are done
             return;
         }
+    }
+
+    // take the fallback when no rotate and shear, but mirror (else we would have done this above)
+    if(!bRotated && !bSheared)
+    {
+        // with no rotation or shear it can be mapped to DrawBitmapEx
+        // do *not* execute the mirroring here, it's done in the fallback
+        // #i124580# the correct DestSize needs to be calculated based on MaxXY values
+        const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
+        const Size aDestSize(
+            basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
+            basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
 
-        assert(bSheared || bRotated); // at this point we are either sheared or rotated or both
+        DrawBitmapEx(aDestPt, aDestSize, rBitmapEx);
+        return;
+    }
 
-        // fallback; create transformed bitmap the hard way (back-transform
-        // the pixels) and paint
-        basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
+    // at this point we are either sheared or rotated or both
+    assert(bSheared || bRotated);
+
+    // fallback; create transformed bitmap the hard way (back-transform
+    // the pixels) and paint
+    basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
+
+    // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
+    // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
+    // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
+    // to avoid crashes/resource problems (ca. 1500x3000 here)
+    const Size& rOriginalSizePixel(rBitmapEx.GetSizePixel());
+    const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
+    const double fOrigAreaScaled(fOrigArea * 1.44);
+    double fMaximumArea(std::min(4500000.0, std::max(1000000.0, fOrigAreaScaled)));
+    basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation);
 
-        // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
-        // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
-        // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
-        // to avoid crashes/resource problems (ca. 1500x3000 here)
-        const Size& rOriginalSizePixel(rBitmapEx.GetSizePixel());
-        const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
-        const double fOrigAreaScaled(fOrigArea * 1.44);
-        double fMaximumArea(std::min(4500000.0, std::max(1000000.0, fOrigAreaScaled)));
+    if(!bMetafile)
+    {
+        if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
+            return;
+    }
 
-        if(!bMetafile)
-        {
-            if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
-                return;
-        }
+    if(!aVisibleRange.isEmpty())
+    {
+        BitmapEx aTransformed(rBitmapEx);
 
-        if(!aVisibleRange.isEmpty())
+        // #122923# when the result needs an alpha channel due to being rotated or sheared
+        // and thus uncovering areas, add these channels so that the own transformer (used
+        // in getTransformed) also creates a transformed alpha channel
+        if(!aTransformed.IsTransparent() && (bSheared || bRotated))
         {
-            BitmapEx aTransformed(rBitmapEx);
+            // parts will be uncovered, extend aTransformed with a mask bitmap
+            const Bitmap aContent(aTransformed.GetBitmap());
 
-            // #122923# when the result needs an alpha channel due to being rotated or sheared
-            // and thus uncovering areas, add these channels so that the own transformer (used
-            // in getTransformed) also creates a transformed alpha channel
-            if(!aTransformed.IsTransparent() && (bSheared || bRotated))
-            {
-                // parts will be uncovered, extend aTransformed with a mask bitmap
-                const Bitmap aContent(aTransformed.GetBitmap());
-
-                AlphaMask aMaskBmp(aContent.GetSizePixel());
-                aMaskBmp.Erase(0);
+            AlphaMask aMaskBmp(aContent.GetSizePixel());
+            aMaskBmp.Erase(0);
 
-                aTransformed = BitmapEx(aContent, aMaskBmp);
-            }
+            aTransformed = BitmapEx(aContent, aMaskBmp);
+        }
 
-            // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling
-            // will happen according to aDestSize.
-            basegfx::B2DVector aFullScale, aFullTranslate;
-            double fFullRotate, fFullShearX;
-            aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
-            // Require positive scaling, negative scaling would loose horizontal or vertical flip.
-            if (aFullScale.getX() > 0 && aFullScale.getY() > 0)
-            {
-                basegfx::B2DHomMatrix aTransform = basegfx::utils::createScaleB2DHomMatrix(
-                    rOriginalSizePixel.getWidth() / aFullScale.getX(),
-                    rOriginalSizePixel.getHeight() / aFullScale.getY());
-                aFullTransform *= aTransform;
-            }
+        // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling
+        // will happen according to aDestSize.
+        basegfx::B2DVector aFullScale, aFullTranslate;
+        double fFullRotate, fFullShearX;
+        aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
+        // Require positive scaling, negative scaling would loose horizontal or vertical flip.
+        if (aFullScale.getX() > 0 && aFullScale.getY() > 0)
+        {
+            basegfx::B2DHomMatrix aTransform = basegfx::utils::createScaleB2DHomMatrix(
+                rOriginalSizePixel.getWidth() / aFullScale.getX(),
+                rOriginalSizePixel.getHeight() / aFullScale.getY());
+            aFullTransform *= aTransform;
+        }
 
-            double fSourceRatio = 1.0;
-            if (rOriginalSizePixel.getHeight() != 0)
-            {
-                fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
-            }
-            double fTargetRatio = 1.0;
-            if (aFullScale.getY() != 0)
-            {
-                fTargetRatio = aFullScale.getX() / aFullScale.getY();
-            }
-            bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
-            if (bSheared || !bAspectRatioKept)
-            {
-                // Not only rotation, or scaling does not keep aspect ratio.
-                aTransformed = aTransformed.getTransformed(
-                    aFullTransform,
-                    aVisibleRange,
-                    fMaximumArea);
-            }
-            else
+        double fSourceRatio = 1.0;
+        if (rOriginalSizePixel.getHeight() != 0)
+        {
+            fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
+        }
+        double fTargetRatio = 1.0;
+        if (aFullScale.getY() != 0)
+        {
+            fTargetRatio = aFullScale.getX() / aFullScale.getY();
+        }
+        bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
+        if (bSheared || !bAspectRatioKept)
+        {
+            // Not only rotation, or scaling does not keep aspect ratio.
+            aTransformed = aTransformed.getTransformed(
+                aFullTransform,
+                aVisibleRange,
+                fMaximumArea);
+        }
+        else
+        {
+            // Just rotation, can do that directly.
+            fFullRotate = fmod(fFullRotate * -1, F_2PI);
+            if (fFullRotate < 0)
             {
-                // Just rotation, can do that directly.
-                fFullRotate = fmod(fFullRotate * -1, F_2PI);
-                if (fFullRotate < 0)
-                {
-                    fFullRotate += F_2PI;
-                }
-                long nAngle10 = basegfx::fround(basegfx::rad2deg(fFullRotate) * 10);
-                aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
+                fFullRotate += F_2PI;
             }
-            basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
+            long nAngle10 = basegfx::fround(basegfx::rad2deg(fFullRotate) * 10);
+            aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
+        }
+        basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
 
-            // get logic object target range
-            aTargetRange.transform(rTransformation);
+        // get logic object target range
+        aTargetRange.transform(rTransformation);
 
-            // get from unified/relative VisibleRange to logoc one
-            aVisibleRange.transform(
-                basegfx::utils::createScaleTranslateB2DHomMatrix(
-                    aTargetRange.getRange(),
-                    aTargetRange.getMinimum()));
+        // get from unified/relative VisibleRange to logoc one
+        aVisibleRange.transform(
+            basegfx::utils::createScaleTranslateB2DHomMatrix(
+                aTargetRange.getRange(),
+                aTargetRange.getMinimum()));
 
-            // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
-            // #i124580# the correct DestSize needs to be calculated based on MaxXY values
-            const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
-            const Size aDestSize(
-                basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
-                basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
+        // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
+        // #i124580# the correct DestSize needs to be calculated based on MaxXY values
+        const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
+        const Size aDestSize(
+            basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
+            basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
 
-            DrawBitmapEx(aDestPt, aDestSize, aTransformed);
-        }
+        DrawBitmapEx(aDestPt, aDestSize, aTransformed);
     }
 }
 


More information about the Libreoffice-commits mailing list