summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <armin.le.grand@me.com>2020-02-21 16:58:17 +0100
committerMiklos Vajna <vmiklos@collabora.com>2020-02-24 13:10:29 +0100
commitef6f94e93bbe55a26b67b55b2aecda67d406ab85 (patch)
tree271d344f997e520ecf406a5b102edaf059542d1d
parent59edfb81f05da304b49609a239961607db6f6900 (diff)
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@me.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89252 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
-rw-r--r--sw/source/core/doc/notxtfrm.cxx198
-rw-r--r--sw/source/core/inc/frmtool.hxx6
-rw-r--r--sw/source/core/inc/notxtfrm.hxx6
-rw-r--r--vcl/headless/svpbmp.cxx7
-rw-r--r--vcl/headless/svpgdi.cxx219
-rw-r--r--vcl/inc/headless/svpbmp.hxx26
-rw-r--r--vcl/source/outdev/bitmap.cxx247
7 files changed, 602 insertions, 107 deletions
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index 04e556277738..f2b148361994 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -82,6 +82,14 @@
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.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 )
@@ -148,7 +156,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;
}
@@ -925,6 +935,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,
@@ -936,12 +947,24 @@ 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);
+}
+
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ drawinglayer::primitive2d::Primitive2DContainer& rContent,
+ const basegfx::B2DHomMatrix& rGraphicTransform)
+{
// 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
@@ -1008,9 +1031,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;
}
}
@@ -1019,11 +1045,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
@@ -1149,13 +1275,61 @@ void SwNoTextFrame::PaintPicture( vcl::RenderContext* pOut, const SwRect &rGrfAr
}
else
{
- const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+ // 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);
- paintGraphicUsingPrimitivesHelper(
- *pOut,
- rGrfObj,
- aGrfAttr,
- aGraphicTransform);
+ 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);
+ }
+ }
+ 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);
+ }
}
}
else
diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx
index 5ffcd20083f7..54c1c9231e28 100644
--- a/sw/source/core/inc/frmtool.hxx
+++ b/sw/source/core/inc/frmtool.hxx
@@ -97,6 +97,12 @@ void paintGraphicUsingPrimitivesHelper(
GraphicAttr const& rGraphicAttr,
const basegfx::B2DHomMatrix& rGraphicTransform);
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ drawinglayer::primitive2d::Primitive2DContainer& rContent,
+ const basegfx::B2DHomMatrix& rGraphicTransform);
+
// 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 fad416c27d8f..6c168041d244 100644
--- a/sw/source/core/inc/notxtfrm.hxx
+++ b/sw/source/core/inc/notxtfrm.hxx
@@ -20,6 +20,8 @@
#define INCLUDED_SW_SOURCE_CORE_INC_NOTXTFRM_HXX
#include "cntfrm.hxx"
+// MM02
+#include <svx/sdr/contact/viewcontact.hxx>
class SwNoTextNode;
class OutputDevice;
@@ -49,6 +51,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 824fb847a732..a7e5bdb55ddb 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 bcfde817b604..ad99b21ba6ef 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -334,6 +334,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:
@@ -399,6 +437,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 )
@@ -409,16 +564,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");
@@ -479,29 +640,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);
@@ -1737,8 +1903,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;
+ }
+
copySource(rTR, source);
}
@@ -1806,6 +1981,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 d97c0fee128e..a1e30d8b3903 100644
--- a/vcl/inc/headless/svpbmp.hxx
+++ b/vcl/inc/headless/svpbmp.hxx
@@ -24,11 +24,13 @@
#include <tools/solar.h>
#include <salbmp.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
-class VCL_DLLPUBLIC SvpSalBitmap : public SalBitmap
+class VCL_DLLPUBLIC SvpSalBitmap : public SalBitmap, public basegfx::SystemDependentDataHolder // MM02
{
std::unique_ptr<BitmapBuffer> mpDIB;
public:
+ SvpSalBitmap();
virtual ~SvpSalBitmap() override;
// SalBitmap
@@ -59,6 +61,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 42aa0d78c486..7fe75ec21a14 100644
--- a/vcl/source/outdev/bitmap.cxx
+++ b/vcl/source/outdev/bitmap.cxx
@@ -1163,6 +1163,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)
@@ -1178,6 +1204,41 @@ void OutputDevice::DrawTransformedBitmapEx(
if ( mnDrawMode & DrawModeFlags::NoBitmap )
return;
+ // 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;
@@ -1187,10 +1248,7 @@ void OutputDevice::DrawTransformedBitmapEx(
const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
- static bool bForceToOwnTransformer(false);
- const bool bMetafile = mpMetaFile != nullptr;
-
- if(!bForceToOwnTransformer && !bRotated && !bSheared && !bMirroredX && !bMirroredY)
+ if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
{
// with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
// do *not* execute the mirroring here, it's done in the fallback
@@ -1215,99 +1273,140 @@ 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 | DrawModeFlags::GhostedBitmap));
- bool bDone(false);
- const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation);
- const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile );
-
- if(!bForceToOwnTransformer && bTryDirectPaint)
+ // 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(!bForceToOwnTransformer && !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;
}
+ }
- // fallback; create transformed bitmap the hard way (back-transform
- // the pixels) and paint
- basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
+ // 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());
- // 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(bSheared || bRotated ? fOrigArea * 1.44 : fOrigArea);
- double fMaximumArea(std::min(4500000.0, std::max(1000000.0, fOrigAreaScaled)));
+ DrawBitmapEx(aDestPt, aDestSize, rBitmapEx);
+ return;
+ }
- if(!bMetafile)
- {
- if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
- return;
- }
+ // 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);
- if(!aVisibleRange.isEmpty())
+ // 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);
+
+ if(!bMetafile)
+ {
+ if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
+ return;
+ }
+
+ if(!aVisibleRange.isEmpty())
+ {
+ static bool bDoSmoothAtAll(true);
+ BitmapEx aTransformed(rBitmapEx);
+
+ // #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))
{
- static bool bDoSmoothAtAll(true);
- 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;
+ }
+ 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,
bDoSmoothAtAll);
- basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
+ }
+ else
+ {
+ // 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);
+ }
+ 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);
}
}