diff options
author | Armin Le Grand (Collabora) <armin.le.grand@me.com> | 2020-02-21 16:58:17 +0100 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2020-02-21 20:16:59 +0100 |
commit | 828504974d70111e4a35b31d579cf42fe660a660 (patch) | |
tree | 4c2f7720b3efaecf55b8fa7b9b3eeccb278160e6 /vcl | |
parent | 813cde918338bccc4f711230616340cad2c1d4a0 (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>
Diffstat (limited to 'vcl')
-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 |
4 files changed, 403 insertions, 134 deletions
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); } } |