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>
This commit is contained in:
committed by
Armin Le Grand
parent
813cde9183
commit
828504974d
+206
-20
@@ -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());
|
||||
// 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);
|
||||
|
||||
paintGraphicUsingPrimitivesHelper(
|
||||
*pOut,
|
||||
rGrfObj,
|
||||
aGrfAttr,
|
||||
aGraphicTransform,
|
||||
nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
|
||||
rNoTNd.GetTitle(),
|
||||
rNoTNd.GetDescription());
|
||||
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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
|
||||
using namespace basegfx;
|
||||
|
||||
SvpSalBitmap::SvpSalBitmap()
|
||||
: SalBitmap(),
|
||||
basegfx::SystemDependentDataHolder(), // MM02
|
||||
mpDIB()
|
||||
{
|
||||
}
|
||||
|
||||
SvpSalBitmap::~SvpSalBitmap()
|
||||
{
|
||||
Destroy();
|
||||
|
||||
+199
-20
@@ -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);
|
||||
}
|
||||
|
||||
// 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())
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
+179
-120
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
assert(bSheared || bRotated); // at this point we are either sheared or rotated or both
|
||||
// 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());
|
||||
|
||||
// fallback; create transformed bitmap the hard way (back-transform
|
||||
// the pixels) and paint
|
||||
basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
|
||||
DrawBitmapEx(aDestPt, aDestSize, rBitmapEx);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)));
|
||||
// at this point we are either sheared or rotated or both
|
||||
assert(bSheared || bRotated);
|
||||
|
||||
if(!bMetafile)
|
||||
// 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);
|
||||
|
||||
if(!bMetafile)
|
||||
{
|
||||
if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
|
||||
return;
|
||||
}
|
||||
|
||||
if(!aVisibleRange.isEmpty())
|
||||
{
|
||||
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))
|
||||
{
|
||||
if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
|
||||
return;
|
||||
// parts will be uncovered, extend aTransformed with a mask bitmap
|
||||
const Bitmap aContent(aTransformed.GetBitmap());
|
||||
|
||||
AlphaMask aMaskBmp(aContent.GetSizePixel());
|
||||
aMaskBmp.Erase(0);
|
||||
|
||||
aTransformed = BitmapEx(aContent, aMaskBmp);
|
||||
}
|
||||
|
||||
if(!aVisibleRange.isEmpty())
|
||||
// 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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
// parts will be uncovered, extend aTransformed with a mask bitmap
|
||||
const Bitmap aContent(aTransformed.GetBitmap());
|
||||
|
||||
AlphaMask aMaskBmp(aContent.GetSizePixel());
|
||||
aMaskBmp.Erase(0);
|
||||
|
||||
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);
|
||||
}
|
||||
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 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());
|
||||
|
||||
DrawBitmapEx(aDestPt, aDestSize, aTransformed);
|
||||
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
|
||||
{
|
||||
// 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 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());
|
||||
|
||||
DrawBitmapEx(aDestPt, aDestSize, aTransformed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user