summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <armin.le.grand@me.com>2020-02-21 16:58:17 +0100
committerArmin Le Grand <Armin.Le.Grand@me.com>2020-02-21 20:16:59 +0100
commit828504974d70111e4a35b31d579cf42fe660a660 (patch)
tree4c2f7720b3efaecf55b8fa7b9b3eeccb278160e6 /vcl
parent813cde918338bccc4f711230616340cad2c1d4a0 (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.cxx7
-rw-r--r--vcl/headless/svpgdi.cxx219
-rw-r--r--vcl/inc/headless/svpbmp.hxx26
-rw-r--r--vcl/source/outdev/bitmap.cxx285
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);
}
}