diff options
-rw-r--r-- | sw/source/core/doc/notxtfrm.cxx | 228 | ||||
-rw-r--r-- | sw/source/core/inc/frmtool.hxx | 9 | ||||
-rw-r--r-- | sw/source/core/inc/notxtfrm.hxx | 6 | ||||
-rw-r--r-- | vcl/headless/svpbmp.cxx | 7 | ||||
-rw-r--r-- | vcl/headless/svpgdi.cxx | 219 | ||||
-rw-r--r-- | vcl/inc/headless/svpbmp.hxx | 26 | ||||
-rw-r--r-- | vcl/source/outdev/bitmap.cxx | 285 |
7 files changed, 625 insertions, 155 deletions
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); } } |