summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basegfx/Library_basegfx.mk1
-rw-r--r--basegfx/source/polygon/b2dpolygon.cxx47
-rwxr-xr-xbasegfx/source/tools/systemdependentdata.cxx141
-rw-r--r--drawinglayer/source/primitive2d/polygonprimitive2d.cxx6
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx62
-rw-r--r--include/basegfx/polygon/b2dpolygon.hxx26
-rwxr-xr-xinclude/basegfx/tools/systemdependentdata.hxx139
-rw-r--r--include/vcl/outdev.hxx1
-rw-r--r--vcl/headless/svpgdi.cxx270
-rw-r--r--vcl/inc/headless/svpgdi.hxx34
-rw-r--r--vcl/inc/openglgdiimpl.hxx4
-rw-r--r--vcl/inc/quartz/salgdi.h4
-rw-r--r--vcl/inc/salgdi.hxx11
-rw-r--r--vcl/inc/salgdiimpl.hxx4
-rw-r--r--vcl/inc/unx/genpspgraphics.h15
-rw-r--r--vcl/inc/unx/salgdi.h4
-rw-r--r--vcl/inc/win/salbmp.h32
-rw-r--r--vcl/inc/win/salgdi.h4
-rw-r--r--vcl/opengl/gdiimpl.cxx25
-rw-r--r--vcl/quartz/salgdicommon.cxx39
-rw-r--r--vcl/source/app/svmain.cxx6
-rw-r--r--vcl/source/gdi/salgdilayout.cxx183
-rw-r--r--vcl/source/outdev/line.cxx25
-rw-r--r--vcl/source/outdev/polygon.cxx82
-rw-r--r--vcl/source/outdev/polyline.cxx150
-rw-r--r--vcl/source/outdev/transparent.cxx28
-rw-r--r--vcl/unx/generic/gdi/gdiimpl.cxx41
-rw-r--r--vcl/unx/generic/gdi/gdiimpl.hxx4
-rw-r--r--vcl/unx/generic/gdi/salgdi.cxx25
-rw-r--r--vcl/unx/generic/print/genpspgraphics.cxx4
-rw-r--r--vcl/win/gdi/gdiimpl.cxx362
-rw-r--r--vcl/win/gdi/gdiimpl.hxx4
-rw-r--r--vcl/win/gdi/salbmp.cxx233
-rw-r--r--vcl/win/gdi/salgdi_gdiplus.cxx15
34 files changed, 1494 insertions, 537 deletions
diff --git a/basegfx/Library_basegfx.mk b/basegfx/Library_basegfx.mk
index 68cfe1cfae05..d2d28107d989 100644
--- a/basegfx/Library_basegfx.mk
+++ b/basegfx/Library_basegfx.mk
@@ -72,6 +72,7 @@ $(eval $(call gb_Library_add_exception_objects,basegfx,\
basegfx/source/tools/keystoplerp \
basegfx/source/tools/numbertools \
basegfx/source/tools/stringconversiontools \
+ basegfx/source/tools/systemdependentdata \
basegfx/source/tools/tools \
basegfx/source/tools/unopolypolygon \
basegfx/source/tools/unotools \
diff --git a/basegfx/source/polygon/b2dpolygon.cxx b/basegfx/source/polygon/b2dpolygon.cxx
index 5ad06eaedb57..d07354952358 100644
--- a/basegfx/source/polygon/b2dpolygon.cxx
+++ b/basegfx/source/polygon/b2dpolygon.cxx
@@ -25,6 +25,7 @@
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <rtl/instance.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/tools/systemdependentdata.hxx>
#include <algorithm>
#include <memory>
#include <vector>
@@ -464,20 +465,21 @@ public:
}
};
-class ImplBufferedData
+class ImplBufferedData : public basegfx::SystemDependentDataHolder
{
private:
// Possibility to hold the last subdivision
- std::unique_ptr< basegfx::B2DPolygon > mpDefaultSubdivision;
+ std::unique_ptr< basegfx::B2DPolygon > mpDefaultSubdivision;
// Possibility to hold the last B2DRange calculation
- std::unique_ptr< basegfx::B2DRange > mpB2DRange;
+ std::unique_ptr< basegfx::B2DRange > mpB2DRange;
public:
ImplBufferedData()
: mpDefaultSubdivision(),
mpB2DRange()
- {}
+ {
+ }
const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
{
@@ -1092,6 +1094,26 @@ public:
maPoints.transform(rMatrix);
}
}
+
+ void addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
+ {
+ if(!mpBufferedData)
+ {
+ mpBufferedData.reset(new ImplBufferedData);
+ }
+
+ mpBufferedData->addOrReplaceSystemDependentData(rData);
+ }
+
+ basegfx::SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const
+ {
+ if(mpBufferedData)
+ {
+ return mpBufferedData->getSystemDependentData(hash_code);
+ }
+
+ return basegfx::SystemDependentData_SharedPtr();
+ }
};
namespace basegfx
@@ -1464,6 +1486,23 @@ namespace basegfx
}
}
+ void B2DPolygon::addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const
+ {
+ // Need to get ImplB2DPolygon* from cow_wrapper *without*
+ // calling make_unique() here - we do not want to
+ // 'modify' the ImplB2DPolygon, but add buffered data that
+ // is valid for all referencing instances
+ const B2DPolygon* pMe(this);
+ const ImplB2DPolygon* pMyImpl(pMe->mpPolygon.get());
+
+ const_cast<ImplB2DPolygon*>(pMyImpl)->addOrReplaceSystemDependentData(rData);
+ }
+
+ SystemDependentData_SharedPtr B2DPolygon::getSystemDependantDataInternal(size_t hash_code) const
+ {
+ return mpPolygon->getSystemDependentData(hash_code);
+ }
+
} // end of namespace basegfx
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basegfx/source/tools/systemdependentdata.cxx b/basegfx/source/tools/systemdependentdata.cxx
new file mode 100755
index 000000000000..ddb99b06e912
--- /dev/null
+++ b/basegfx/source/tools/systemdependentdata.cxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <basegfx/tools/systemdependentdata.hxx>
+
+namespace basegfx
+{
+ SystemDependentDataManager::SystemDependentDataManager()
+ {
+ }
+
+ SystemDependentDataManager::~SystemDependentDataManager()
+ {
+ }
+} // namespace basegfx
+
+namespace basegfx
+{
+ MinimalSystemDependentDataManager::MinimalSystemDependentDataManager()
+ : SystemDependentDataManager(),
+ maSystemDependentDataReferences()
+ {
+ }
+
+ MinimalSystemDependentDataManager::~MinimalSystemDependentDataManager()
+ {
+ }
+
+ void MinimalSystemDependentDataManager::startUsage(basegfx::SystemDependentData_SharedPtr& rData)
+ {
+ if(rData)
+ {
+ maSystemDependentDataReferences.insert(rData);
+ }
+ }
+
+ void MinimalSystemDependentDataManager::endUsage(basegfx::SystemDependentData_SharedPtr& rData)
+ {
+ if(rData)
+ {
+ maSystemDependentDataReferences.erase(rData);
+ }
+ }
+
+ void MinimalSystemDependentDataManager::touchUsage(basegfx::SystemDependentData_SharedPtr& /* rData */)
+ {
+ }
+
+ void MinimalSystemDependentDataManager::flushAll()
+ {
+ maSystemDependentDataReferences.clear();
+ }
+} // namespace basegfx
+
+namespace basegfx
+{
+ SystemDependentData::SystemDependentData(
+ SystemDependentDataManager& rSystemDependentDataManager,
+ sal_uInt32 nHoldCycles)
+ : mrSystemDependentDataManager(rSystemDependentDataManager),
+ mnHoldCycles(nHoldCycles)
+ {
+ }
+
+ SystemDependentData::~SystemDependentData()
+ {
+ }
+} // namespace basegfx
+
+namespace basegfx
+{
+ SystemDependentDataHolder::SystemDependentDataHolder()
+ : maSystemDependentReferences()
+ {
+ }
+
+ SystemDependentDataHolder::~SystemDependentDataHolder()
+ {
+ for(auto& candidate : maSystemDependentReferences)
+ {
+ basegfx::SystemDependentData_SharedPtr aData(candidate.second.lock());
+
+ if(aData)
+ {
+ aData->getSystemDependentDataManager().endUsage(aData);
+ }
+ }
+ }
+
+ void SystemDependentDataHolder::addOrReplaceSystemDependentData(basegfx::SystemDependentData_SharedPtr& rData)
+ {
+ const size_t hash_code(typeid(*rData.get()).hash_code());
+ auto result(maSystemDependentReferences.find(hash_code));
+
+ if(result != maSystemDependentReferences.end())
+ {
+ basegfx::SystemDependentData_SharedPtr aData(result->second.lock());
+
+ if(aData)
+ {
+ aData->getSystemDependentDataManager().endUsage(aData);
+ }
+
+ maSystemDependentReferences.erase(result);
+ result = maSystemDependentReferences.end();
+ }
+
+ maSystemDependentReferences[hash_code] = rData;
+ rData->getSystemDependentDataManager().startUsage(rData);
+ }
+
+ SystemDependentData_SharedPtr SystemDependentDataHolder::getSystemDependentData(size_t hash_code) const
+ {
+ basegfx::SystemDependentData_SharedPtr aRetval;
+ auto result(maSystemDependentReferences.find(hash_code));
+
+ if(result != maSystemDependentReferences.end())
+ {
+ aRetval = result->second.lock();
+
+ if(aRetval)
+ {
+ aRetval->getSystemDependentDataManager().touchUsage(aRetval);
+ }
+ else
+ {
+ const_cast< SystemDependentDataHolder* >(this)->maSystemDependentReferences.erase(result);
+ }
+ }
+
+ return aRetval;
+ }
+} // namespace basegfx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
index 76fc498d34d5..3ef630cc7b5c 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -313,6 +313,9 @@ namespace drawinglayer
maLineAttribute(rLineAttribute),
maStrokeAttribute(rStrokeAttribute)
{
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::tools::simplifyCurveSegments(maPolygon);
}
PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
@@ -323,6 +326,9 @@ namespace drawinglayer
maLineAttribute(rLineAttribute),
maStrokeAttribute()
{
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::tools::simplifyCurveSegments(maPolygon);
}
bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 80f4ab6e0618..91214fbd030c 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -129,9 +129,9 @@ namespace drawinglayer
bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect(const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency)
{
- basegfx::B2DPolygon aLocalPolygon(rSource.getB2DPolygon());
+ const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon());
- if(!aLocalPolygon.count())
+ if(!rLocalPolygon.count())
{
// no geometry, done
return true;
@@ -141,15 +141,14 @@ namespace drawinglayer
mpOutputDevice->SetFillColor();
mpOutputDevice->SetLineColor(Color(aLineColor));
- aLocalPolygon.transform(maCurrentTransformation);
+ //aLocalPolygon.transform(maCurrentTransformation);
// try drawing; if it did not work, use standard fallback
- if(mpOutputDevice->DrawPolyLineDirect( aLocalPolygon, 0.0, fTransparency))
- {
- return true;
- }
-
- return false;
+ return mpOutputDevice->DrawPolyLineDirect(
+ maCurrentTransformation,
+ rLocalPolygon,
+ 0.0,
+ fTransparency);
}
bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency)
@@ -165,6 +164,10 @@ namespace drawinglayer
aLocalPolygon = basegfx::tools::simplifyCurveSegments(aLocalPolygon);
basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+ // simplify curve segments
+ // moved to PolygonStrokePrimitive2D::PolygonStrokePrimitive2D
+ // aLocalPolygon = basegfx::tools::simplifyCurveSegments(aLocalPolygon);
+
if(rSource.getStrokeAttribute().isDefault() || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen())
{
// no line dashing, just copy
@@ -187,34 +190,36 @@ namespace drawinglayer
return true;
}
- const basegfx::BColor aLineColor(
- maBColorModifierStack.getModifiedColor(
- rSource.getLineAttribute().getColor()));
-
- mpOutputDevice->SetFillColor();
- mpOutputDevice->SetLineColor(Color(aLineColor));
- aHairLinePolyPolygon.transform(maCurrentTransformation);
-
+ // check if LineWidth can be simplified in world coordinates
double fLineWidth(rSource.getLineAttribute().getWidth());
if(basegfx::fTools::more(fLineWidth, 0.0))
{
basegfx::B2DVector aLineWidth(fLineWidth, 0.0);
-
aLineWidth = maCurrentTransformation * aLineWidth;
- fLineWidth = aLineWidth.getLength();
- }
+ const double fWorldLineWidth(aLineWidth.getLength());
- // draw simple hairline for small line widths
- // see also RenderPolygonStrokePrimitive2D which is used if this try fails
- bool bIsAntiAliasing = getOptionsDrawinglayer().IsAntiAliasing();
- if ( (basegfx::fTools::lessOrEqual(fLineWidth, 1.0) && bIsAntiAliasing)
- || (basegfx::fTools::lessOrEqual(fLineWidth, 1.5) && !bIsAntiAliasing))
- {
- // draw simple hairline
- fLineWidth = 0.0;
+ // draw simple hairline for small line widths
+ // see also RenderPolygonStrokePrimitive2D which is used if this try fails
+ bool bIsAntiAliasing = getOptionsDrawinglayer().IsAntiAliasing();
+ if ( (basegfx::fTools::lessOrEqual(fWorldLineWidth, 1.0) && bIsAntiAliasing)
+ || (basegfx::fTools::lessOrEqual(fWorldLineWidth, 1.5) && !bIsAntiAliasing))
+ {
+ // draw simple hairline
+ fLineWidth = 0.0;
+ }
}
+ const basegfx::BColor aLineColor(
+ maBColorModifierStack.getModifiedColor(
+ rSource.getLineAttribute().getColor()));
+
+ mpOutputDevice->SetFillColor();
+ mpOutputDevice->SetLineColor(Color(aLineColor));
+
+ // do not transform self
+ // aHairLinePolyPolygon.transform(maCurrentTransformation);
+
bool bHasPoints(false);
bool bTryWorked(false);
@@ -227,6 +232,7 @@ namespace drawinglayer
bHasPoints = true;
if(mpOutputDevice->DrawPolyLineDirect(
+ maCurrentTransformation,
aSingle,
fLineWidth,
fTransparency,
diff --git a/include/basegfx/polygon/b2dpolygon.hxx b/include/basegfx/polygon/b2dpolygon.hxx
index 6ef76933eebe..a1766bd09ffb 100644
--- a/include/basegfx/polygon/b2dpolygon.hxx
+++ b/include/basegfx/polygon/b2dpolygon.hxx
@@ -21,7 +21,7 @@
#define INCLUDED_BASEGFX_POLYGON_B2DPOLYGON_HXX
#include <ostream>
-
+#include <memory>
#include <sal/types.h>
#include <o3tl/cow_wrapper.hxx>
#include <basegfx/vector/b2enums.hxx>
@@ -29,6 +29,7 @@
#include <basegfx/basegfxdllapi.h>
class ImplB2DPolygon;
+class SalGraphicsImpl;
namespace basegfx
{
@@ -37,6 +38,9 @@ namespace basegfx
class B2DVector;
class B2DHomMatrix;
class B2DCubicBezier;
+ class SystemDependentData;
+ class SystemDependentDataManager;
+ typedef std::shared_ptr<SystemDependentData> SystemDependentData_SharedPtr;
}
namespace basegfx
@@ -214,6 +218,26 @@ namespace basegfx
/// apply transformation given in matrix form
void transform(const basegfx::B2DHomMatrix& rMatrix);
+
+ // exclusive management op's for SystemDependentData at B2DPolygon
+ template<class T>
+ std::shared_ptr<T> getSystemDependentData() const
+ {
+ return std::static_pointer_cast<T>(getSystemDependantDataInternal(typeid(T).hash_code()));
+ }
+
+ template<class T, class... Args>
+ std::shared_ptr<T> addOrReplaceSystemDependentData(SystemDependentDataManager& manager, Args&&... args) const
+ {
+ std::shared_ptr<T> r = std::make_shared<T>(manager, std::forward<Args>(args)...);
+ basegfx::SystemDependentData_SharedPtr r2(r);
+ addOrReplaceSystemDependentDataInternal(r2);
+ return r;
+ }
+
+ private:
+ void addOrReplaceSystemDependentDataInternal(SystemDependentData_SharedPtr& rData) const;
+ SystemDependentData_SharedPtr getSystemDependantDataInternal(size_t hash_code) const;
};
// typedef for a vector of B2DPolygons
diff --git a/include/basegfx/tools/systemdependentdata.hxx b/include/basegfx/tools/systemdependentdata.hxx
new file mode 100755
index 000000000000..17a0ce44f815
--- /dev/null
+++ b/include/basegfx/tools/systemdependentdata.hxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_BASEGFX_SYSTEMDEPENDENTDATA_HXX
+#define INCLUDED_BASEGFX_SYSTEMDEPENDENTDATA_HXX
+
+#include <sal/types.h>
+#include <basegfx/basegfxdllapi.h>
+#include <memory>
+#include <map>
+#include <set>
+
+namespace basegfx
+{
+ class SystemDependentData;
+ typedef std::shared_ptr<SystemDependentData> SystemDependentData_SharedPtr;
+ typedef std::weak_ptr<SystemDependentData> SystemDependentData_WeakPtr;
+} // end of namespace basegfx
+
+namespace basegfx
+{
+ class BASEGFX_DLLPUBLIC SystemDependentDataManager
+ {
+ private:
+ // noncopyable
+ SystemDependentDataManager(const SystemDependentDataManager&) = delete;
+ SystemDependentDataManager& operator=(const SystemDependentDataManager&) = delete;
+
+ public:
+ SystemDependentDataManager();
+ virtual ~SystemDependentDataManager();
+
+ // call from (and with) SystemDependentData objects when start/end/touch
+ // usage is needed
+ virtual void startUsage(basegfx::SystemDependentData_SharedPtr& rData) = 0;
+ virtual void endUsage(basegfx::SystemDependentData_SharedPtr& rData) = 0;
+ virtual void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) = 0;
+
+ // flush all buffred data (e.g. cleanup/shutdown)
+ virtual void flushAll() = 0;
+ };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+ class BASEGFX_DLLPUBLIC MinimalSystemDependentDataManager : public SystemDependentDataManager
+ {
+ private:
+ // example of a minimal SystemDependentDataManager. It *needs to hold*
+ // a SystemDependentData_SharedPtr while SystemDependentDataHolder's will
+ // use a SystemDependentData_WeakPtr. When the held SystemDependentData_SharedPtr
+ // is deleted, the corresponding SystemDependentData_WeakPtr will get void.
+ // To make this work, a minimal SystemDependentDataManager *has* to hold at
+ // least that one SystemDependentData_SharedPtr.
+ // That SystemDependentData_SharedPtr may be (e.g. Timer-based or ressource-based)
+ // be freed then. This minimal implementation does never free it, so all stay valid.
+ // The instances may still be removed by endUsage calls, but there is no
+ // caching/buffering mechanism involved here at all. It's an example, but
+ // not used - better use an advanced derivation of SystemDependentDataManager
+ std::set< SystemDependentData_SharedPtr > maSystemDependentDataReferences;
+
+ public:
+ MinimalSystemDependentDataManager();
+ virtual ~MinimalSystemDependentDataManager() override;
+
+ virtual void startUsage(basegfx::SystemDependentData_SharedPtr& rData) override;
+ virtual void endUsage(basegfx::SystemDependentData_SharedPtr& rData) override;
+ virtual void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) override;
+ virtual void flushAll() override;
+ };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+ class BASEGFX_DLLPUBLIC SystemDependentData
+ {
+ private:
+ // noncopyable
+ SystemDependentData(const SystemDependentData&) = delete;
+ SystemDependentData& operator=(const SystemDependentData&) = delete;
+
+ // reference to a SystemDependentDataManager, probably
+ // a single, globally used one, but not necessarily
+ SystemDependentDataManager& mrSystemDependentDataManager;
+
+ // number of cycles a SystemDependentDataManager should/might
+ // hold this instance - does not have to be used, but should be
+ sal_uInt32 mnHoldCycles;
+
+ public:
+ SystemDependentData(
+ SystemDependentDataManager& rSystemDependentDataManager,
+ sal_uInt32 nHoldCycles = 60);
+
+ // CAUTION! It is VERY important to keep this base class
+ // virtual, else typeid(class).hash_code() from derived classes
+ // will NOT work what is ESSENTIAL for the SystemDependentData
+ // mechanism to work properly. So DO NOT REMOVE virtual here, please.
+ virtual ~SystemDependentData();
+
+ // allow access to call startUsage/endUsage/touchUsage
+ // using getSystemDependentDataManager()
+ SystemDependentDataManager& getSystemDependentDataManager() { return mrSystemDependentDataManager; }
+
+ // number of cycles to hold data
+ sal_uInt32 getHoldCycles() const { return mnHoldCycles; }
+ };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+ class BASEGFX_DLLPUBLIC SystemDependentDataHolder
+ {
+ private:
+ // Possibility to hold System-Dependent B2DPolygon-Representations
+ std::map< size_t, SystemDependentData_WeakPtr > maSystemDependentReferences;
+
+ // noncopyable
+ SystemDependentDataHolder(const SystemDependentDataHolder&) = delete;
+ SystemDependentDataHolder& operator=(const SystemDependentDataHolder&) = delete;
+
+ public:
+ SystemDependentDataHolder();
+ virtual ~SystemDependentDataHolder();
+
+ void addOrReplaceSystemDependentData(SystemDependentData_SharedPtr& rData);
+ SystemDependentData_SharedPtr getSystemDependentData(size_t hash_code) const;
+ };
+} // end of namespace basegfx
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index f6f53548bae4..fd5e5565d7b8 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -816,6 +816,7 @@ public:
// #i101491#
// Helper who tries to use SalGDI's DrawPolyLine direct and returns it's bool.
bool DrawPolyLineDirect(
+ const basegfx::B2DHomMatrix& rObjectTransform,
const basegfx::B2DPolygon& rB2DPolygon,
double fLineWidth = 0.0,
double fTransparency = 0.0,
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index 11f408aaf773..825d5b1ebf21 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -34,6 +34,8 @@
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/tools/systemdependentdata.hxx>
#include <cairo.h>
@@ -542,8 +544,15 @@ void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry)
aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
aPoly.setClosed(false);
- drawPolyLine(aPoly, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter,
- css::drawing::LineCap_BUTT, 15.0 * F_PI180 /*default*/);
+ drawPolyLine(
+ basegfx::B2DHomMatrix(),
+ aPoly,
+ 0.0,
+ basegfx::B2DVector(1.0, 1.0),
+ basegfx::B2DLineJoin::Miter,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0) /*default*/,
+ false);
}
void SvpSalGraphics::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
@@ -699,26 +708,142 @@ void SvpSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
releaseCairoContext(cr, false, extents);
}
+class SystemDependentData_CairoPath : public basegfx::SystemDependentData
+{
+private:
+ cairo_path_t* mpCairoPath;
+
+public:
+ SystemDependentData_CairoPath(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ cairo_path_t* pCairoPath);
+ virtual ~SystemDependentData_CairoPath() override;
+
+ cairo_path_t* getCairoPath() { return mpCairoPath; }
+};
+
+SystemDependentData_CairoPath::SystemDependentData_CairoPath(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ cairo_path_t* pCairoPath)
+: basegfx::SystemDependentData(rSystemDependentDataManager),
+ mpCairoPath(pCairoPath)
+{
+}
+
+SystemDependentData_CairoPath::~SystemDependentData_CairoPath()
+{
+ if(nullptr != mpCairoPath)
+ {
+ cairo_path_destroy(mpCairoPath);
+ mpCairoPath = nullptr;
+ }
+}
+
bool SvpSalGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& rPolyLine,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin eLineJoin,
css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
// short circuit if there is nothing to do
- const int nPointCount = rPolyLine.count();
- if (nPointCount <= 0)
+ if(0 == rPolyLine.count())
{
return true;
}
- const bool bNoJoin = (basegfx::B2DLineJoin::NONE == eLineJoin && basegfx::fTools::more(rLineWidths.getX(), 0.0));
-
+ // Wrap call to static verion of ::drawPolyLine by
+ // preparing/getting some local data and parameters
+ // due to usage in vcl/unx/generic/gdi/salgdi.cxx.
+ // This is mainly about extended handling of extents
+ // and the way destruction of CairoContext is handled
+ // due to current XOR stuff
cairo_t* cr = getCairoContext(false);
+ basegfx::B2DRange aExtents;
clipRegion(cr);
+ bool bRetval(
+ drawPolyLine(
+ cr,
+ &aExtents,
+ m_aLineColor,
+ getAntiAliasB2DDraw(),
+ rObjectToDevice,
+ rPolyLine,
+ fTransparency,
+ rLineWidths,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ bPixelSnapHairline));
+
+ releaseCairoContext(cr, false, aExtents);
+
+ return bRetval;
+}
+
+bool SvpSalGraphics::drawPolyLine(
+ cairo_t* cr,
+ basegfx::B2DRange* pExtents,
+ const Color& rLineColor,
+ bool bAntiAliasB2DDraw,
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolyLine,
+ double fTransparency,
+ const basegfx::B2DVector& rLineWidths,
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
+{
+ // short circuit if there is nothing to do
+ if(0 == rPolyLine.count())
+ {
+ return true;
+ }
+
+ // need to check/handle LineWidth when ObjectToDevice transformation is used
+ basegfx::B2DVector aLineWidths(rLineWidths);
+ const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
+ const basegfx::B2DVector aDeviceLineWidths(bObjectToDeviceIsIdentity ? rLineWidths : rObjectToDevice * rLineWidths);
+ const bool bCorrectLineWidth(!bObjectToDeviceIsIdentity && aDeviceLineWidths.getX() < 1.0 && aLineWidths.getX() >= 1.0);
+
+ // on-demand inverse of ObjectToDevice transformation
+ basegfx::B2DHomMatrix aObjectToDeviceInv;
+
+ if(bCorrectLineWidth)
+ {
+ if(aObjectToDeviceInv.isIdentity())
+ {
+ aObjectToDeviceInv = rObjectToDevice;
+ aObjectToDeviceInv.invert();
+ }
+
+ // calculate-back logical LineWidth for a hairline
+ aLineWidths = aObjectToDeviceInv * basegfx::B2DVector(1.0, 1.0);
+ }
+
+ if(!bObjectToDeviceIsIdentity)
+ {
+ // set ObjectToDevice transformation
+ cairo_matrix_t aMatrix;
+
+ cairo_matrix_init(
+ &aMatrix,
+ rObjectToDevice.get( 0, 0 ),
+ rObjectToDevice.get( 1, 0 ),
+ rObjectToDevice.get( 0, 1 ),
+ rObjectToDevice.get( 1, 1 ),
+ rObjectToDevice.get( 0, 2 ),
+ rObjectToDevice.get( 1, 2 ));
+ cairo_set_matrix(cr, &aMatrix);
+ }
+
+ const bool bNoJoin((basegfx::B2DLineJoin::NONE == eLineJoin && basegfx::fTools::more(aLineWidths.getX(), 0.0)));
+
// setup line attributes
cairo_line_join_t eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
switch (eLineJoin)
@@ -760,54 +885,131 @@ bool SvpSalGraphics::drawPolyLine(
}
}
- cairo_set_source_rgba(cr, SALCOLOR_RED(m_aLineColor)/255.0,
- SALCOLOR_GREEN(m_aLineColor)/255.0,
- SALCOLOR_BLUE(m_aLineColor)/255.0,
- 1.0-fTransparency);
+ cairo_set_source_rgba(
+ cr,
+ rLineColor.GetRed()/255.0,
+ rLineColor.GetGreen()/255.0,
+ rLineColor.GetBlue()/255.0,
+ 1.0-fTransparency);
cairo_set_line_join(cr, eCairoLineJoin);
cairo_set_line_cap(cr, eCairoLineCap);
- cairo_set_line_width(cr, rLineWidths.getX());
+ cairo_set_line_width(cr, aLineWidths.getX());
cairo_set_miter_limit(cr, fMiterLimit);
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_CairoPath> pSystemDependentData_CairoPath(
+ rPolyLine.getSystemDependentData<SystemDependentData_CairoPath>());
- basegfx::B2DRange extents(0, 0, 0, 0);
+ if(pSystemDependentData_CairoPath)
+ {
+ // check data validity
+ if(nullptr == pSystemDependentData_CairoPath->getCairoPath())
+ {
+ // data invalid, forget
+ pSystemDependentData_CairoPath.reset();
+ }
+ }
- if (!bNoJoin)
+ if(pSystemDependentData_CairoPath)
{
- AddPolygonToPath(cr, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true);
- extents = getClippedStrokeDamage(cr);
- cairo_stroke(cr);
+ // re-use data
+ cairo_append_path(cr, pSystemDependentData_CairoPath->getCairoPath());
}
else
{
- // emulate rendering::PathJoinType::NONE by painting single edges
- const sal_uInt32 nEdgeCount(rPolyLine.isClosed() ? nPointCount : nPointCount - 1);
- basegfx::B2DPolygon aEdge;
- aEdge.append(rPolyLine.getB2DPoint(0));
- aEdge.append(basegfx::B2DPoint(0.0, 0.0));
+ // create data
+ basegfx::B2DPolygon aPolyLine(rPolyLine);
- for (sal_uInt32 i = 0; i < nEdgeCount; ++i)
+ if(bPixelSnapHairline)
{
- const sal_uInt32 nNextIndex((i + 1) % nPointCount);
- aEdge.setB2DPoint(1, rPolyLine.getB2DPoint(nNextIndex));
- aEdge.setNextControlPoint(0, rPolyLine.getNextControlPoint(i % nPointCount));
- aEdge.setPrevControlPoint(1, rPolyLine.getPrevControlPoint(nNextIndex));
+ // Need to take care of PixelSnapHairline now. The 'short' version
+ // will manipulate the Polygon by using the known tooling at
+ // basegfx. To do this correct, this needs to be done in device
+ // coordinates, so when the transformation is used, transform
+ // to device first, execute, transform back using the inverse.
+ // The important part for buffering the result and not need to
+ // do this at each repaint (for now) is to change a copy of the
+ // Polygon to create the CairoData, but to buffer it at the original
+ // unmodified Polygon.
+ // The 'long' version would be to add this to AddPolygonToPath
+ // equal as done in Win version (see impPixelSnap), should be done
+ // later
+ if(!bObjectToDeviceIsIdentity)
+ {
+ aPolyLine.transform(rObjectToDevice);
+ }
- AddPolygonToPath(cr, aEdge, false, !getAntiAliasB2DDraw(), true);
+ aPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
- extents.expand(getStrokeDamage(cr));
+ if(!bObjectToDeviceIsIdentity)
+ {
+ if(aObjectToDeviceInv.isIdentity())
+ {
+ aObjectToDeviceInv = rObjectToDevice;
+ aObjectToDeviceInv.invert();
+ }
- cairo_stroke(cr);
+ aPolyLine.transform(aObjectToDeviceInv);
+ }
+ }
- // prepare next step
- aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+ if (!bNoJoin)
+ {
+ AddPolygonToPath(cr, rPolyLine, rPolyLine.isClosed(), !bAntiAliasB2DDraw, true);
}
+ else
+ {
+ const sal_uInt32 nPointCount(rPolyLine.count());
+ const sal_uInt32 nEdgeCount(rPolyLine.isClosed() ? nPointCount : nPointCount - 1);
+ basegfx::B2DPolygon aEdge;
- extents.intersect(getClipBox(cr));
+ aEdge.append(rPolyLine.getB2DPoint(0));
+ aEdge.append(basegfx::B2DPoint(0.0, 0.0));
+
+ for (sal_uInt32 i(0); i < nEdgeCount; i++)
+ {
+ const sal_uInt32 nNextIndex((i + 1) % nPointCount);
+ aEdge.setB2DPoint(1, rPolyLine.getB2DPoint(nNextIndex));
+ aEdge.setNextControlPoint(0, rPolyLine.getNextControlPoint(i));
+ aEdge.setPrevControlPoint(1, rPolyLine.getPrevControlPoint(nNextIndex));
+
+ AddPolygonToPath(cr, aEdge, false, !bAntiAliasB2DDraw, true);
+
+ // prepare next step
+ aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+ }
+ }
+
+ // copy and add to buffering mechanism
+ rPolyLine.addOrReplaceSystemDependentData<SystemDependentData_CairoPath>(
+ SalGraphics::getSystemDependentDataManager(),
+ cairo_copy_path(cr));
}
- releaseCairoContext(cr, false, extents);
+ // extract extents
+ if(nullptr != pExtents)
+ {
+ *pExtents = getClippedStrokeDamage(cr);
+ }
+
+ // draw and consume
+ cairo_stroke(cr);
+
+ if(!bObjectToDeviceIsIdentity)
+ {
+ // reset ObjectToDevice transformation if was set (safe, may
+ // be better suited at ::getCairoContext)
+ cairo_identity_matrix(cr);
+ }
+
+ if(nullptr != pExtents && !pExtents->isEmpty() && !bObjectToDeviceIsIdentity)
+ {
+ // transform extents to DeviceCoordiinates if used. These
+ // were calculated with ObjectToDevice transformation actively set,
+ // but use DeviceCoordinates locally
+ pExtents->transform(rObjectToDevice);
+ }
return true;
}
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index 060c9f967312..dcf022bcd528 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -90,6 +90,25 @@ public:
void setSurface(cairo_surface_t* pSurface);
static cairo_user_data_key_t* getDamageKey();
+ static void clipRegion(cairo_t* cr, const vcl::Region& rClipRegion);
+
+ // need this static version of ::drawPolyLine for usage from
+ // vcl/unx/generic/gdi/salgdi.cxx. It gets wrapped by
+ // ::drawPolyLine with some added parameters (see there)
+ static bool drawPolyLine(
+ cairo_t* cr,
+ basegfx::B2DRange* pExtents,
+ const Color& rLineColor,
+ bool bAntiAliasB2DDraw,
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolyLine,
+ double fTransparency,
+ const basegfx::B2DVector& rLineWidths,
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline);
+
private:
void invert(const basegfx::B2DPolygon &rPoly, SalInvert nFlags);
void copySource(const SalTwoRect& rTR, cairo_surface_t* source);
@@ -175,12 +194,15 @@ public:
virtual void drawLine( long nX1, long nY1, long nX2, long nY2 ) override;
virtual void drawRect( long nX, long nY, long nWidth, long nHeight ) override;
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
- virtual bool drawPolyLine( const basegfx::B2DPolygon&,
- double fTransparency,
- const basegfx::B2DVector& rLineWidths,
- basegfx::B2DLineJoin,
- css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon&,
+ double fTransparency,
+ const basegfx::B2DVector& rLineWidths,
+ basegfx::B2DLineJoin,
+ css::drawing::LineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual void drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
virtual void drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
virtual void drawPolyPolygon( sal_uInt32 nPoly,
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index 265d21603dd5..3fdc86740478 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -250,12 +250,14 @@ public:
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawPolyLineBezier(
sal_uInt32 nPoints,
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 09dc6193148b..997115c193c2 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -242,12 +242,14 @@ public:
virtual bool drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) override;
virtual bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const sal_uInt8* const* pFlgAry ) override;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin,
css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle) override;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawGradient( const tools::PolyPolygon&, const Gradient& ) override { return false; };
// CopyArea --> No RasterOp, but ClipRegion
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 56fe42dbfe38..c421b0b86b27 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -54,6 +54,7 @@ namespace basegfx {
class B2DVector;
class B2DPolygon;
class B2DPolyPolygon;
+ class SystemDependentDataManager;
}
typedef sal_Unicode sal_Ucs; // TODO: use sal_UCS4 instead of sal_Unicode
@@ -79,6 +80,10 @@ public:
virtual SalGraphicsImpl* GetImpl() const = 0;
+ // access to single global managing instance of a basegfx::SystemDependentDataManager,
+ // used to handle graphic data in system-dependent form
+ static basegfx::SystemDependentDataManager& getSystemDependentDataManager();
+
/// Check that our mpImpl is OpenGL and return the context, otherwise NULL.
rtl::Reference<OpenGLContext> GetOpenGLContext() const;
@@ -263,12 +268,14 @@ public:
const OutputDevice *i_pOutDev);
bool DrawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& i_rPolygon,
double i_fTransparency,
const basegfx::B2DVector& i_rLineWidth,
basegfx::B2DLineJoin i_eLineJoin,
css::drawing::LineCap i_eLineCap,
double i_fMiterMinimumAngle,
+ bool bPixelSnapHairline,
const OutputDevice* i_pOutDev);
bool DrawPolyLineBezier(
@@ -462,12 +469,14 @@ protected:
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) = 0;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) = 0;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) = 0;
virtual bool drawPolyLineBezier(
sal_uInt32 nPoints,
diff --git a/vcl/inc/salgdiimpl.hxx b/vcl/inc/salgdiimpl.hxx
index c2c008e68462..1bdc245b8799 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -101,12 +101,14 @@ public:
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) = 0;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) = 0;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) = 0;
virtual bool drawPolyLineBezier(
sal_uInt32 nPoints,
diff --git a/vcl/inc/unx/genpspgraphics.h b/vcl/inc/unx/genpspgraphics.h
index b7658b93f3dd..94a99bbf9386 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -144,12 +144,15 @@ public:
PCONSTSALPOINT* pPtAry ) override;
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&,
double fTransparency ) override;
- virtual bool drawPolyLine( const basegfx::B2DPolygon&,
- double fTransparency,
- const basegfx::B2DVector& rLineWidths,
- basegfx::B2DLineJoin,
- css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon&,
+ double fTransparency,
+ const basegfx::B2DVector& rLineWidths,
+ basegfx::B2DLineJoin,
+ css::drawing::LineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawPolyLineBezier( sal_uInt32 nPoints,
const SalPoint* pPtAry,
const sal_uInt8* pFlgAry ) override;
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index 40aa91b6ac70..120071ca5e3b 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -172,12 +172,14 @@ public:
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidth,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawGradient( const tools::PolyPolygon&, const Gradient& ) override;
diff --git a/vcl/inc/win/salbmp.h b/vcl/inc/win/salbmp.h
index 0a698e5eae46..55108f2ef679 100644
--- a/vcl/inc/win/salbmp.h
+++ b/vcl/inc/win/salbmp.h
@@ -23,6 +23,7 @@
#include <tools/gen.hxx>
#include <win/wincomp.hxx>
#include <salbmp.hxx>
+#include <basegfx/tools/systemdependentdata.hxx>
#include <memory>
@@ -33,26 +34,13 @@ class SalGraphics;
namespace Gdiplus { class Bitmap; }
typedef std::shared_ptr< Gdiplus::Bitmap > GdiPlusBmpPtr;
-class WinSalBitmap : public SalBitmap
+class WinSalBitmap : public SalBitmap, public basegfx::SystemDependentDataHolder
{
private:
- friend class GdiPlusBuffer; // allow buffer to remove maGdiPlusBitmap and mpAssociatedAlpha eventually
-
Size maSize;
HGLOBAL mhDIB;
HBITMAP mhDDB;
- // the buffered evtl. used Gdiplus::Bitmap instance. It is managed by
- // GdiPlusBuffer. To make this safe, it is only handed out as shared
- // pointer; the GdiPlusBuffer may delete the local instance.
-
- // mpAssociatedAlpha holds the last WinSalBitmap used to construct an
- // evtl. buffered GdiPlusBmp. This is needed since the GdiPlusBmp is a single
- // instance and remembered only on the content-WinSalBitmap, not on the
- // alpha-WinSalBitmap.
- GdiPlusBmpPtr maGdiPlusBitmap;
- const WinSalBitmap* mpAssociatedAlpha;
-
sal_uInt16 mnBitCount;
Gdiplus::Bitmap* ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource);
@@ -98,6 +86,22 @@ public:
virtual bool Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag ) override;
virtual bool Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol ) override;
+
+ // 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)...);
+ basegfx::SystemDependentData_SharedPtr r2(r);
+ const_cast< WinSalBitmap* >(this)->basegfx::SystemDependentDataHolder::addOrReplaceSystemDependentData(r2);
+ return r;
+ }
};
#endif // INCLUDED_VCL_INC_WIN_SALBMP_H
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index d2a81624cb4a..9091f0700817 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -282,12 +282,14 @@ protected:
virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidth,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) override;
virtual bool drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) override;
virtual bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const BYTE* const* pFlgAry ) override;
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index ab719f3c2bd5..851163e2b3c6 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1842,8 +1842,15 @@ void OpenGLSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pP
aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
aPoly.setClosed(false);
- drawPolyLine(aPoly, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter,
- css::drawing::LineCap_BUTT, 15.0 * F_PI180 /*default*/);
+ drawPolyLine(
+ basegfx::B2DHomMatrix(),
+ aPoly,
+ 0.0,
+ basegfx::B2DVector(1.0, 1.0),
+ basegfx::B2DLineJoin::Miter,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0) /*default*/,
+ false);
}
void OpenGLSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
@@ -1916,12 +1923,14 @@ bool OpenGLSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPol
}
bool OpenGLSalGraphicsImpl::drawPolyLine(
- const basegfx::B2DPolygon& rPolygon,
- double fTransparency,
- const basegfx::B2DVector& rLineWidth,
- basegfx::B2DLineJoin eLineJoin,
- css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+ const basegfx::B2DHomMatrix& /*rObjectToDevice*/,
+ const basegfx::B2DPolygon& rPolygon,
+ double fTransparency,
+ const basegfx::B2DVector& rLineWidth,
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool /*bPixelSnapHairline*/)
{
VCL_GL_INFO( "::drawPolyLine trans " << fTransparency );
if( mnLineColor == SALCOLOR_NONE )
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 93c22b3c98c8..8b9d2752bf2d 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -23,6 +23,7 @@
#include <cstring>
#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
#include <osl/endian.h>
#include <osl/file.hxx>
#include <sal/types.h>
@@ -957,18 +958,20 @@ void AquaSalGraphics::drawPixel( long nX, long nY, SalColor nSalColor )
ImplDrawPixel( nX, nY, aPixelColor );
}
-bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
- double fTransparency,
- const basegfx::B2DVector& rLineWidths,
- basegfx::B2DLineJoin eLineJoin,
- css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+bool AquaSalGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolyLine,
+ double fTransparency,
+ const basegfx::B2DVector& rLineWidths,
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
DBG_DRAW_OPERATION("drawPolyLine", true);
// short circuit if there is nothing to do
- const int nPointCount = rPolyLine.count();
- if( nPointCount <= 0 )
+ if(0 == rPolyLine.count())
{
DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
return true;
@@ -982,16 +985,23 @@ bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
}
#endif
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ const basegfx::B2DVector aLineWidths(rObjectToDevice * rLineWidths);
+
// #i101491# Aqua does not support B2DLineJoin::NONE; return false to use
// the fallback (own geometry preparation)
// #i104886# linejoin-mode and thus the above only applies to "fat" lines
- if( (basegfx::B2DLineJoin::NONE == eLineJoin) &&
- (rLineWidths.getX() > 1.3) )
+ if( (basegfx::B2DLineJoin::NONE == eLineJoin) && (aLineWidths.getX() > 1.3) )
{
DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
return false;
}
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ basegfx::B2DPolygon aPolyLine(rPolyLine);
+ aPolyLine.transform(rObjectToDevice);
+ if(bPixelSnapHairline) { aPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+
// setup line attributes
CGLineJoin aCGLineJoin = kCGLineJoinMiter;
switch( eLineJoin )
@@ -1028,7 +1038,12 @@ bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
// setup poly-polygon path
CGMutablePathRef xPath = CGPathCreateMutable();
SAL_INFO( "vcl.cg", "CGPathCreateMutable() = " << xPath );
- AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true );
+ AddPolygonToPath(
+ xPath,
+ aPolyLine,
+ aPolyLine.isClosed(),
+ !getAntiAliasB2DDraw(),
+ true);
const CGRect aRefreshRect = CGPathGetBoundingBox( xPath );
SAL_INFO( "vcl.cg", "CGPathGetBoundingBox(" << xPath << ") = " << aRefreshRect );
@@ -1048,7 +1063,7 @@ bool AquaSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolyLine,
CGContextSetAlpha( mrContext, 1.0 - fTransparency );
CGContextSetLineJoin( mrContext, aCGLineJoin );
CGContextSetLineCap( mrContext, aCGLineCap );
- CGContextSetLineWidth( mrContext, rLineWidths.getX() );
+ CGContextSetLineWidth( mrContext, aLineWidths.getX() );
CGContextSetMiterLimit(mrContext, fCGMiterLimit);
SAL_INFO( "vcl.cg", "CGContextDrawPath(" << mrContext << ",kCGPathStroke)" );
CGContextDrawPath( mrContext, kCGPathStroke );
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index 042ec278075a..b05b7e324394 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -93,6 +93,8 @@
#include <opengl/zone.hxx>
#include <opengl/watchdog.hxx>
+#include <basegfx/tools/systemdependentdata.hxx>
+
#if OSL_DEBUG_LEVEL > 0
#include <typeinfo>
#include <rtl/strbuf.hxx>
@@ -370,6 +372,10 @@ VCLUnoWrapperDeleter::disposing(lang::EventObject const& /* rSource */)
void DeInitVCL()
{
ImplSVData* pSVData = ImplGetSVData();
+
+ // cleanup SystemDependentData
+ SalGraphics::getSystemDependentDataManager().flushAll();
+
// lp#1560328: clear cache before disposing rest of VCL
if(pSVData->mpBlendFrameCache)
pSVData->mpBlendFrameCache->m_aLastResult.Clear();
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index 7c13637fcbe3..495918360d9f 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -24,6 +24,10 @@
#include "salgdi.hxx"
#include "salframe.hxx"
#include <basegfx/numeric/ftools.hxx> //for F_PI180
+#include <basegfx/tools/systemdependentdata.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <o3tl/make_unique.hxx>
// The only common SalFrame method
@@ -65,6 +69,131 @@ rtl::Reference<OpenGLContext> SalGraphics::GetOpenGLContext() const
}
#endif
+basegfx::SystemDependentDataManager& SalGraphics::getSystemDependentDataManager()
+{
+ typedef ::std::map< basegfx::SystemDependentData_SharedPtr, sal_uInt32 > EntryMap;
+
+ class SystemDependentDataBuffer : public basegfx::SystemDependentDataManager, protected cppu::BaseMutex, public Timer
+ {
+ private:
+ EntryMap maEntries;
+
+ public:
+ SystemDependentDataBuffer( const sal_Char *pDebugName )
+ : basegfx::SystemDependentDataManager(),
+ Timer( pDebugName ),
+ maEntries()
+ {
+ SetTimeout(1000);
+ Stop();
+ }
+
+ virtual ~SystemDependentDataBuffer() override
+ {
+ Stop();
+ }
+
+ void startUsage(basegfx::SystemDependentData_SharedPtr& rData) override
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ EntryMap::iterator aFound(maEntries.find(rData));
+
+ if(aFound == maEntries.end())
+ {
+ if(maEntries.empty())
+ {
+ Start();
+ }
+
+ maEntries[rData] = rData->getHoldCycles();
+ }
+ }
+
+ void endUsage(basegfx::SystemDependentData_SharedPtr& rData) override
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ EntryMap::iterator aFound(maEntries.find(rData));
+
+ if(aFound != maEntries.end())
+ {
+ maEntries.erase(aFound);
+
+ if(maEntries.empty())
+ {
+ Stop();
+ }
+ }
+ }
+
+ void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) override
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ EntryMap::iterator aFound(maEntries.find(rData));
+
+ if(aFound != maEntries.end())
+ {
+ aFound->second = rData->getHoldCycles();
+ }
+ }
+
+ void flushAll() override
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ EntryMap::iterator aIter(maEntries.begin());
+
+ Stop();
+
+ while(aIter != maEntries.end())
+ {
+ EntryMap::iterator aDelete(aIter);
+ ++aIter;
+ maEntries.erase(aDelete);
+ }
+ }
+
+ // from parent Timer
+ virtual void Invoke() override
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ EntryMap::iterator aIter(maEntries.begin());
+
+ while(aIter != maEntries.end())
+ {
+ if(aIter->second)
+ {
+ aIter->second--;
+ ++aIter;
+ }
+ else
+ {
+ EntryMap::iterator aDelete(aIter);
+ ++aIter;
+ maEntries.erase(aDelete);
+
+ if(maEntries.empty())
+ {
+ Stop();
+ }
+ }
+ }
+
+ if(!maEntries.empty())
+ {
+ Start();
+ }
+ }
+ };
+
+ static std::unique_ptr<SystemDependentDataBuffer> aSystemDependentDataBuffer;
+
+ if(!aSystemDependentDataBuffer)
+ {
+ aSystemDependentDataBuffer = o3tl::make_unique<SystemDependentDataBuffer>(nullptr);
+ }
+
+ return *aSystemDependentDataBuffer.get();
+}
+
bool SalGraphics::drawTransformedBitmap(
const basegfx::B2DPoint& /* rNull */,
const basegfx::B2DPoint& /* rX */,
@@ -477,22 +606,56 @@ bool SalGraphics::DrawPolyPolygonBezier( sal_uInt32 i_nPoly, const sal_uInt32* i
return bRet;
}
-bool SalGraphics::DrawPolyLine( const basegfx::B2DPolygon& i_rPolygon,
- double i_fTransparency,
- const basegfx::B2DVector& i_rLineWidth,
- basegfx::B2DLineJoin i_eLineJoin,
- css::drawing::LineCap i_eLineCap,
- double i_fMiterMinimumAngle,
- const OutputDevice* i_pOutDev )
+bool SalGraphics::DrawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& i_rPolygon,
+ double i_fTransparency,
+ const basegfx::B2DVector& i_rLineWidth,
+ basegfx::B2DLineJoin i_eLineJoin,
+ css::drawing::LineCap i_eLineCap,
+ double i_fMiterMinimumAngle,
+ bool bPixelSnapHairline,
+ const OutputDevice* i_pOutDev)
{
bool bRet = false;
+
if( (m_nLayout & SalLayoutFlags::BiDiRtl) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) )
{
- basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) );
- bRet = drawPolyLine( aMirror, i_fTransparency, i_rLineWidth, i_eLineJoin, i_eLineCap, i_fMiterMinimumAngle );
+ // if mirrored, we need to apply transformation since it is
+ // not clear what 'mirror' does - might be changed when this
+ // happens often
+ basegfx::B2DPolygon aMirror(i_rPolygon);
+
+ aMirror.transform(rObjectToDevice);
+ aMirror = mirror(aMirror, i_pOutDev);
+ // basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) );
+
+ // also need to transform LineWidth
+ const basegfx::B2DVector aLineWidth(rObjectToDevice * i_rLineWidth);
+
+ bRet = drawPolyLine(
+ basegfx::B2DHomMatrix(), // now empty transformation, already used
+ aMirror,
+ i_fTransparency,
+ aLineWidth,
+ i_eLineJoin,
+ i_eLineCap,
+ i_fMiterMinimumAngle,
+ bPixelSnapHairline);
}
else
- bRet = drawPolyLine( i_rPolygon, i_fTransparency, i_rLineWidth, i_eLineJoin, i_eLineCap, i_fMiterMinimumAngle );
+ {
+ bRet = drawPolyLine(
+ rObjectToDevice,
+ i_rPolygon,
+ i_fTransparency,
+ i_rLineWidth,
+ i_eLineJoin,
+ i_eLineCap,
+ i_fMiterMinimumAngle,
+ bPixelSnapHairline);
+ }
+
return bRet;
}
diff --git a/vcl/source/outdev/line.cxx b/vcl/source/outdev/line.cxx
index a04dd802ee96..49ba56f28e61 100644
--- a/vcl/source/outdev/line.cxx
+++ b/vcl/source/outdev/line.cxx
@@ -129,18 +129,21 @@ void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
aB2DPolyLine.append(basegfx::B2DPoint(rEndPt.X(), rEndPt.Y()));
aB2DPolyLine.transform( aTransform );
- if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
- {
- aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
- }
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
+ // }
if( mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
aB2DPolyLine,
0.0,
aB2DLineWidth,
basegfx::B2DLineJoin::NONE,
css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
this))
{
return;
@@ -239,24 +242,30 @@ void OutputDevice::drawLine( basegfx::B2DPolyPolygon aLinePolyPolygon, const Lin
for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
{
const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
bool bDone(false);
if(bTryAA)
{
bDone = mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
aCandidate,
0.0,
basegfx::B2DVector(1.0,1.0),
basegfx::B2DLineJoin::NONE,
css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
this);
}
if(!bDone)
{
- const tools::Polygon aPolygon(aCandidate);
- mpGraphics->DrawPolyLine(aPolygon.GetSize(), reinterpret_cast<const SalPoint*>(aPolygon.GetConstPointAry()), this);
+ tools::Polygon aPolygon(aCandidate);
+ mpGraphics->DrawPolyLine(
+ aPolygon.GetSize(),
+ reinterpret_cast<const SalPoint*>(aPolygon.GetConstPointAry()),
+ this);
}
}
}
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx
index 612e93716bb1..3b9333131133 100644
--- a/vcl/source/outdev/polygon.cxx
+++ b/vcl/source/outdev/polygon.cxx
@@ -84,21 +84,25 @@ void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
if(bSuccess && IsLineColor())
{
const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
- if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
- {
- aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
- }
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
+ // }
for(sal_uInt32 a(0); bSuccess && a < aB2DPolyPolygon.count(); a++)
{
- bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a),
- 0.0,
- aB2DLineWidth,
- basegfx::B2DLineJoin::NONE,
- css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
- this);
+ bSuccess = mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
+ aB2DPolyPolygon.getB2DPolygon(a),
+ 0.0,
+ aB2DLineWidth,
+ basegfx::B2DLineJoin::NONE,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
+ this);
}
}
@@ -197,19 +201,23 @@ void OutputDevice::DrawPolygon( const tools::Polygon& rPoly )
if(bSuccess && IsLineColor())
{
const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
-
- if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
- {
- aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
- }
-
- bSuccess = mpGraphics->DrawPolyLine( aB2DPolygon,
- 0.0,
- aB2DLineWidth,
- basegfx::B2DLineJoin::NONE,
- css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
- this);
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
+ // }
+
+ bSuccess = mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
+ aB2DPolygon,
+ 0.0,
+ aB2DLineWidth,
+ basegfx::B2DLineJoin::NONE,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
+ this);
}
if(bSuccess)
@@ -300,21 +308,25 @@ void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyP
if(bSuccess && IsLineColor())
{
const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
- if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
- {
- aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
- }
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
+ // }
for(sal_uInt32 a(0);bSuccess && a < aB2DPolyPolygon.count(); a++)
{
- bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a),
- 0.0,
- aB2DLineWidth,
- basegfx::B2DLineJoin::NONE,
- css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
- this);
+ bSuccess = mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
+ aB2DPolyPolygon.getB2DPolygon(a),
+ 0.0,
+ aB2DLineWidth,
+ basegfx::B2DLineJoin::NONE,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
+ this);
}
}
diff --git a/vcl/source/outdev/polyline.cxx b/vcl/source/outdev/polyline.cxx
index ff89d7b8d3ec..d4131c343b9a 100644
--- a/vcl/source/outdev/polyline.cxx
+++ b/vcl/source/outdev/polyline.cxx
@@ -58,31 +58,38 @@ void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly )
InitLineColor();
// use b2dpolygon drawing if possible
- if ( DrawPolyLineDirect( rPoly.getB2DPolygon() ) )
+ if(DrawPolyLineDirect(
+ basegfx::B2DHomMatrix(),
+ rPoly.getB2DPolygon()))
{
- basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon());
- const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
- const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
-
- // transform the polygon
- aB2DPolyLine.transform( aTransform );
-
- if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
- {
- aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
- }
+ return;
+ }
- if(mpGraphics->DrawPolyLine(
- aB2DPolyLine,
- 0.0,
- aB2DLineWidth,
- basegfx::B2DLineJoin::NONE,
- css::drawing::LineCap_BUTT,
- 15.0 * F_PI180 /*default fMiterMinimumAngle, not used*/,
- this))
- {
- return;
- }
+ const basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon());
+ const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation());
+ const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
+ // transform the polygon - do not (!)
+ // aB2DPolyLine.transform( aTransform );
+
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
+ // }
+
+ if(mpGraphics->DrawPolyLine(
+ aTransform,
+ aB2DPolyLine,
+ 0.0,
+ aB2DLineWidth,
+ basegfx::B2DLineJoin::NONE,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0) /*default fMiterMinimumAngle, not used*/,
+ bPixelSnapHairline,
+ this))
+ {
+ return;
}
tools::Polygon aPoly = ImplLogicToDevicePixel( rPoly );
@@ -174,8 +181,17 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
InitLineColor();
// use b2dpolygon drawing if possible
- if ( DrawPolyLineDirect(rB2DPolygon, fLineWidth, 0.0, eLineJoin, eLineCap, fMiterMinimumAngle) )
+ if(DrawPolyLineDirect(
+ basegfx::B2DHomMatrix(),
+ rB2DPolygon,
+ fLineWidth,
+ 0.0,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle))
+ {
return;
+ }
// #i101491#
// no output yet; fallback to geometry decomposition and use filled polygon paint
@@ -221,7 +237,15 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
// to avoid optical gaps
for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++)
{
- DrawPolyLineDirect( aAreaPolyPolygon.getB2DPolygon(a), 0.0, 0.0, basegfx::B2DLineJoin::NONE, css::drawing::LineCap_BUTT, 15.0 * F_PI180 /*default, not used*/, bTryAA);
+ (void)DrawPolyLineDirect(
+ basegfx::B2DHomMatrix(),
+ aAreaPolyPolygon.getB2DPolygon(a),
+ 0.0,
+ 0.0,
+ basegfx::B2DLineJoin::NONE,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0) /*default, not used*/,
+ bTryAA);
}
}
else
@@ -284,13 +308,15 @@ void OutputDevice::drawPolyLine(const tools::Polygon& rPoly, const LineInfo& rLi
mpAlphaVDev->DrawPolyLine( rPoly, rLineInfo );
}
-bool OutputDevice::DrawPolyLineDirect( const basegfx::B2DPolygon& rB2DPolygon,
- double fLineWidth,
- double fTransparency,
- basegfx::B2DLineJoin eLineJoin,
- css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle,
- bool bBypassAACheck)
+bool OutputDevice::DrawPolyLineDirect(
+ const basegfx::B2DHomMatrix& rObjectTransform,
+ const basegfx::B2DPolygon& rB2DPolygon,
+ double fLineWidth,
+ double fTransparency,
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bBypassAACheck)
{
assert(!is_double_buffered_window());
@@ -319,37 +345,43 @@ bool OutputDevice::DrawPolyLineDirect( const basegfx::B2DPolygon& rB2DPolygon,
if(bTryAA)
{
- const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation();
- basegfx::B2DVector aB2DLineWidth(1.0, 1.0);
+ // combine rObjectTransform with WorldToDevice
+ const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation() * rObjectTransform);
+ const bool bLineWidthZero(basegfx::fTools::equalZero(fLineWidth));
+ const basegfx::B2DVector aB2DLineWidth(bLineWidthZero ? 1.0 : fLineWidth, bLineWidthZero ? 1.0 : fLineWidth);
// transform the line width if used
- if( fLineWidth != 0.0 )
- {
- aB2DLineWidth = aTransform * basegfx::B2DVector( fLineWidth, fLineWidth );
- }
-
- // transform the polygon
- basegfx::B2DPolygon aB2DPolygon(rB2DPolygon);
- aB2DPolygon.transform(aTransform);
-
- if((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) &&
- aB2DPolygon.count() < 1000)
- {
- // #i98289#, #i101491#
- // better to remove doubles on device coordinates. Also assume from a given amount
- // of points that the single edges are not long enough to smooth
- aB2DPolygon.removeDoublePoints();
- aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
- }
+ // if( fLineWidth != 0.0 )
+ // {
+ // aB2DLineWidth = aTransform * basegfx::B2DVector( fLineWidth, fLineWidth );
+ // }
+
+ // transform the polygon - no!
+ // basegfx::B2DPolygon aB2DPolygon(rB2DPolygon);
+ // aB2DPolygon.transform(aTransform);
+
+ const bool bPixelSnapHairline((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) && rB2DPolygon.count() < 1000);
+ // if((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) &&
+ // aB2DPolygon.count() < 1000)
+ // {
+ // // #i98289#, #i101491#
+ // // better to remove doubles on device coordinates. Also assume from a given amount
+ // // of points that the single edges are not long enough to smooth
+ // aB2DPolygon.removeDoublePoints();
+ // aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
+ // }
// draw the polyline
- bool bDrawSuccess = mpGraphics->DrawPolyLine( aB2DPolygon,
- fTransparency,
- aB2DLineWidth,
- eLineJoin,
- eLineCap,
- fMiterMinimumAngle,
- this );
+ bool bDrawSuccess = mpGraphics->DrawPolyLine(
+ aTransform,
+ rB2DPolygon,
+ fTransparency,
+ aB2DLineWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ bPixelSnapHairline,
+ this);
if( bDrawSuccess )
{
diff --git a/vcl/source/outdev/transparent.cxx b/vcl/source/outdev/transparent.cxx
index 0f06d065e33c..5070821f1e58 100644
--- a/vcl/source/outdev/transparent.cxx
+++ b/vcl/source/outdev/transparent.cxx
@@ -252,17 +252,22 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly,
if( bDrawnOk && IsLineColor() )
{
const basegfx::B2DVector aHairlineWidth(1,1);
- const int nPolyCount = aB2DPolyPolygon.count();
- for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
+ const sal_uInt32 nPolyCount(aB2DPolyPolygon.count());
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
+ for( sal_uInt32 nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
{
- const basegfx::B2DPolygon aOnePoly = aB2DPolyPolygon.getB2DPolygon( nPolyIdx );
+ const basegfx::B2DPolygon aOnePoly(aB2DPolyPolygon.getB2DPolygon(nPolyIdx));
+
mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
aOnePoly,
fTransparency,
aHairlineWidth,
basegfx::B2DLineJoin::NONE,
css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
this );
}
}
@@ -355,17 +360,22 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
mpGraphics->SetFillColor();
// draw the border line
const basegfx::B2DVector aLineWidths( 1, 1 );
- const int nPolyCount = aB2DPolyPolygon.count();
- for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
+ const sal_uInt32 nPolyCount(aB2DPolyPolygon.count());
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
+ for( sal_uInt32 nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
{
- const basegfx::B2DPolygon& rPolygon = aB2DPolyPolygon.getB2DPolygon( nPolyIdx );
+ const basegfx::B2DPolygon aPolygon(aB2DPolyPolygon.getB2DPolygon(nPolyIdx));
+
bDrawn = mpGraphics->DrawPolyLine(
- rPolygon,
+ basegfx::B2DHomMatrix(),
+ aPolygon,
fTransparency,
aLineWidths,
basegfx::B2DLineJoin::NONE,
css::drawing::LineCap_BUTT,
- 15.0 * F_PI180, // not used with B2DLineJoin::NONE, but the correct default
+ basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
this );
}
// prepare to restore the fill color
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx
index 7845d49f9df3..0d12d15f3a8e 100644
--- a/vcl/unx/generic/gdi/gdiimpl.cxx
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -1593,14 +1593,18 @@ bool X11SalGraphicsImpl::drawFilledTrapezoids( const basegfx::B2DTrapezoid* pB2D
}
bool X11SalGraphicsImpl::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& rPolygon,
double fTransparency,
const basegfx::B2DVector& rLineWidth,
basegfx::B2DLineJoin eLineJoin,
css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
- const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2);
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth);
+ const bool bIsHairline((aLineWidth.getX() == aLineWidth.getY()) && (aLineWidth.getX() <= 1.2));
// #i101491#
if( !bIsHairline && (rPolygon.count() > 1000) )
@@ -1614,18 +1618,23 @@ bool X11SalGraphicsImpl::drawPolyLine(
return false;
}
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ basegfx::B2DPolygon aPolyLine(rPolygon);
+ aPolyLine.transform(rObjectToDevice);
+ if(bPixelSnapHairline) { aPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+
// temporarily adjust brush color to pen color
// since the line is drawn as an area-polygon
const SalColor aKeepBrushColor = mnBrushColor;
mnBrushColor = mnPenColor;
- // #i11575#desc5#b adjust B2D tesselation result to raster positions
- basegfx::B2DPolygon aPolygon = rPolygon;
- const double fHalfWidth = 0.5 * rLineWidth.getX();
+ // #i11575#desc5#b adjust B2D tessellation result to raster positions
+ // basegfx::B2DPolygon aPolygon = rPolygon;
+ const double fHalfWidth = 0.5 * aLineWidth.getX();
// #i122456# This is probably thought to happen to align hairlines to pixel positions, so
// it should be a 0.5 translation, not more. It will definitely go wrong with fat lines
- aPolygon.transform( basegfx::tools::createTranslateB2DHomMatrix(0.5, 0.5) );
+ aPolyLine.transform( basegfx::tools::createTranslateB2DHomMatrix(0.5, 0.5) );
// shortcut for hairline drawing to improve performance
bool bDrawnOk = true;
@@ -1634,7 +1643,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
// hairlines can benefit from a simplified tesselation
// e.g. for hairlines the linejoin style can be ignored
basegfx::B2DTrapezoidVector aB2DTrapVector;
- basegfx::tools::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolygon, rLineWidth.getX() );
+ basegfx::tools::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolyLine, aLineWidth.getX() );
// draw tesselation result
const int nTrapCount = aB2DTrapVector.size();
@@ -1647,21 +1656,25 @@ bool X11SalGraphicsImpl::drawPolyLine(
}
// get the area polygon for the line polygon
- if( (rLineWidth.getX() != rLineWidth.getY())
- && !basegfx::fTools::equalZero( rLineWidth.getY() ) )
+ if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getY() ) )
{
// prepare for createAreaGeometry() with anisotropic linewidth
- aPolygon.transform( basegfx::tools::createScaleB2DHomMatrix(1.0, rLineWidth.getX() / rLineWidth.getY()));
+ aPolyLine.transform( basegfx::tools::createScaleB2DHomMatrix(1.0, aLineWidth.getX() / aLineWidth.getY()));
}
// create the area-polygon for the line
- const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::tools::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin, eLineCap, fMiterMinimumAngle) );
+ const basegfx::B2DPolyPolygon aAreaPolyPoly(
+ basegfx::tools::createAreaGeometry(
+ aPolyLine,
+ fHalfWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle));
- if( (rLineWidth.getX() != rLineWidth.getY())
- && !basegfx::fTools::equalZero( rLineWidth.getX() ) )
+ if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getX() ) )
{
// postprocess createAreaGeometry() for anisotropic linewidth
- aPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(1.0, rLineWidth.getY() / rLineWidth.getX()));
+ aPolyLine.transform(basegfx::tools::createScaleB2DHomMatrix(1.0, aLineWidth.getY() / aLineWidth.getX()));
}
// draw each area polypolygon component individually
diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx
index 5b04ba67be0c..c8d0d79596fe 100644
--- a/vcl/unx/generic/gdi/gdiimpl.hxx
+++ b/vcl/unx/generic/gdi/gdiimpl.hxx
@@ -160,12 +160,14 @@ public:
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawPolyLineBezier(
sal_uInt32 nPoints,
diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
index 814173fbcbe7..f837c19e8ffc 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -559,15 +559,34 @@ bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPo
}
bool X11SalGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& rPolygon,
double fTransparency,
const basegfx::B2DVector& rLineWidth,
basegfx::B2DLineJoin eLineJoin,
css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
- return mxImpl->drawPolyLine( rPolygon, fTransparency, rLineWidth,
- eLineJoin, eLineCap, fMiterMinimumAngle );
+ if(0 == rPolygon.count())
+ {
+ return true;
+ }
+
+ if(fTransparency >= 1.0)
+ {
+ return true;
+ }
+
+ return mxImpl->drawPolyLine(
+ rObjectToDevice,
+ rPolygon,
+ fTransparency,
+ rLineWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ bPixelSnapHairline);
}
bool X11SalGraphics::drawGradient(const tools::PolyPolygon& rPoly, const Gradient& rGradient)
diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx
index 78eee8212a43..d67a73534e27 100644
--- a/vcl/unx/generic/print/genpspgraphics.cxx
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -441,12 +441,14 @@ bool GenPspGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon&, double /*f
}
bool GenPspGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& /* rObjectToDevice */,
const basegfx::B2DPolygon&,
double /*fTranspareny*/,
const basegfx::B2DVector& /*rLineWidths*/,
basegfx::B2DLineJoin /*eJoin*/,
css::drawing::LineCap /*eLineCap*/,
- double /*fMiterMinimumAngle*/)
+ double /*fMiterMinimumAngle*/,
+ bool /* bPixelSnapHairline */)
{
// TODO: a PS printer can draw B2DPolyLines almost directly
return false;
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
index 915cd9351521..16fd8128a8e8 100644
--- a/vcl/win/gdi/gdiimpl.cxx
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -35,6 +35,7 @@
#include <vcl/salbtype.hxx>
#include <win/salframe.h>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/tools/systemdependentdata.hxx>
#include "outdata.hxx"
#include "win/salids.hrc"
@@ -1895,69 +1896,127 @@ bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt
return bRet;
}
+basegfx::B2DPoint impPixelSnap(
+ const basegfx::B2DPolygon& rPolygon,
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ basegfx::B2DHomMatrix& rObjectToDeviceInv,
+ sal_uInt32 nIndex)
+{
+ const sal_uInt32 nCount(rPolygon.count());
+
+ // get the data
+ const basegfx::B2ITuple aPrevTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
+ const basegfx::B2DPoint aCurrPoint(rObjectToDevice * rPolygon.getB2DPoint(nIndex));
+ const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+ const basegfx::B2ITuple aNextTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
+
+ // get the states
+ const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+ const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+ const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+ const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+ const bool bSnapX(bPrevVertical || bNextVertical);
+ const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+ if(bSnapX || bSnapY)
+ {
+ basegfx::B2DPoint aSnappedPoint(
+ bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
+ bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
+
+ if(rObjectToDeviceInv.isIdentity())
+ {
+ rObjectToDeviceInv = rObjectToDevice;
+ rObjectToDeviceInv.invert();
+ }
+
+ aSnappedPoint *= rObjectToDeviceInv;
+
+ return aSnappedPoint;
+ }
+
+ return rPolygon.getB2DPoint(nIndex);
+}
+
void impAddB2DPolygonToGDIPlusGraphicsPathReal(
Gdiplus::GraphicsPath& rGraphicsPath,
const basegfx::B2DPolygon& rPolygon,
- bool bNoLineJoin)
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ bool bNoLineJoin,
+ bool bPixelSnapHairline)
{
sal_uInt32 nCount(rPolygon.count());
if(nCount)
{
const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
- const bool bControls(rPolygon.areControlPointsUsed());
- basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
- for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ if(nEdgeCount)
{
- const sal_uInt32 nNextIndex((a + 1) % nCount);
- const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
- const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
- const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
+ const bool bControls(rPolygon.areControlPointsUsed());
+ basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
+ basegfx::B2DHomMatrix aObjectToDeviceInv;
- if(b1stControlPointUsed || b2ndControlPointUsed)
+ if(bPixelSnapHairline)
{
- basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
- basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
-
- // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
- // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
- // no 1st or 2nd control point, despite that these are mathematicaly correct definitions
- // (basegfx can handle that). To solve, create replacement vectors to thre resp. next
- // control point with 1/3rd of length (the default control vector for these cases).
- // Only one of this can happen here, else the is(Next|Prev)ControlPointUsed wopuld have
- // both been false.
- // Caution: This error (and it's correction) might be necessary for other graphical
- // sub-systems in a similar way
- if(!b1stControlPointUsed)
- {
- aCa = aCurr + ((aCb - aCurr) * 0.3);
- }
- else if(!b2ndControlPointUsed)
+ aCurr = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, 0);
+ }
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nCount);
+ basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
+ const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
+ const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
+
+ if(bPixelSnapHairline)
{
- aCb = aNext + ((aCa - aNext) * 0.3);
+ aNext = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, nNextIndex);
}
- rGraphicsPath.AddBezier(
- static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
- static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
- static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
- static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
- }
- else
- {
- rGraphicsPath.AddLine(
- static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
- static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
- }
+ if(b1stControlPointUsed || b2ndControlPointUsed)
+ {
+ basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
+ basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
+
+ // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
+ // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
+ // no 1st or 2nd control point, despite that these are mathematicaly correct definitions
+ // (basegfx can handle that).
+ // Caution: This error (and it's correction) might be necessary for other graphical
+ // sub-systems in a similar way.
+ // tdf#101026 The 1st attempt to create a mathematically correct replacement control
+ // vector was wrong. Best alternative is one as close as possible which means short.
+ if(!b1stControlPointUsed)
+ {
+ aCa = aCurr + ((aCb - aCurr) * 0.0005);
+ }
+ else if(!b2ndControlPointUsed)
+ {
+ aCb = aNext + ((aCa - aNext) * 0.0005);
+ }
- if(a + 1 < nEdgeCount)
- {
- aCurr = aNext;
+ rGraphicsPath.AddBezier(
+ static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
+ static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
+ static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
+ static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
+ }
+ else
+ {
+ rGraphicsPath.AddLine(
+ static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
+ static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
+ }
- if(bNoLineJoin)
+ if(a + 1 < nEdgeCount)
{
- rGraphicsPath.StartFigure();
+ aCurr = aNext;
+
+ if(bNoLineJoin)
+ {
+ rGraphicsPath.StartFigure();
+ }
}
}
}
@@ -1984,7 +2043,12 @@ bool WinSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPo
aGraphicsPath.StartFigure();
}
- impAddB2DPolygonToGDIPlusGraphicsPathReal(aGraphicsPath, rPolyPolygon.getB2DPolygon(a), false);
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(
+ aGraphicsPath,
+ rPolyPolygon.getB2DPolygon(a),
+ basegfx::B2DHomMatrix(),
+ false,
+ false);
aGraphicsPath.CloseFigure();
}
@@ -2024,99 +2088,171 @@ bool WinSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPo
return true;
}
+class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
+{
+private:
+ Gdiplus::GraphicsPath maGraphicsPath;
+ bool mbPixelSnapHairline;
+
+public:
+ SystemDependentData_GraphicsPath(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager);
+ virtual ~SystemDependentData_GraphicsPath() override;
+
+ Gdiplus::GraphicsPath& getGraphicsPath() { return maGraphicsPath; }
+
+ bool getPixelSnapHairline() const { return mbPixelSnapHairline; }
+ void setPixelSnapHairline(bool bNew) { mbPixelSnapHairline = bNew; }
+};
+
+SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager)
+: basegfx::SystemDependentData(rSystemDependentDataManager),
+ maGraphicsPath(),
+ mbPixelSnapHairline(false)
+{
+}
+
+SystemDependentData_GraphicsPath::~SystemDependentData_GraphicsPath()
+{
+}
+
bool WinSalGraphicsImpl::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& rPolygon,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin eLineJoin,
css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
- const sal_uInt32 nCount(rPolygon.count());
-
- if(mbPen && nCount)
+ if(!mbPen || 0 == rPolygon.count())
{
- Gdiplus::Graphics aGraphics(mrParent.getHDC());
- const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
- const Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
- Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(rLineWidths.getX()));
- Gdiplus::GraphicsPath aGraphicsPath(Gdiplus::FillModeAlternate);
- bool bNoLineJoin(false);
+ return true;
+ }
+
+ Gdiplus::Graphics aGraphics(mrParent.getHDC());
+ const sal_uInt8 aTrans = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
+ const Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
+ Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(rLineWidths.getX()));
+ bool bNoLineJoin(false);
+ Gdiplus::Matrix aMatrix;
+
+ // Set full (Object-to-Device) transformation
+ aMatrix.SetElements(
+ rObjectToDevice.get(0, 0),
+ rObjectToDevice.get(1, 0),
+ rObjectToDevice.get(0, 1),
+ rObjectToDevice.get(1, 1),
+ rObjectToDevice.get(0, 2),
+ rObjectToDevice.get(1, 2));
+ aGraphics.SetTransform(&aMatrix);
- switch(eLineJoin)
+ switch(eLineJoin)
+ {
+ case basegfx::B2DLineJoin::NONE :
{
- case basegfx::B2DLineJoin::NONE :
- {
- if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
- {
- bNoLineJoin = true;
- }
- break;
- }
- case basegfx::B2DLineJoin::Bevel :
+ if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
{
- aPen.SetLineJoin(Gdiplus::LineJoinBevel);
- break;
- }
- case basegfx::B2DLineJoin::Miter :
- {
- const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
-
- aPen.SetMiterLimit(aMiterLimit);
- // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
- // graphics, somewhere clipped in some distance from the edge point, dependent
- // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
- // that instead
- aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
- break;
- }
- case basegfx::B2DLineJoin::Round :
- {
- aPen.SetLineJoin(Gdiplus::LineJoinRound);
- break;
+ bNoLineJoin = true;
}
+ break;
}
-
- switch(eLineCap)
+ case basegfx::B2DLineJoin::Bevel :
{
- default: /*css::drawing::LineCap_BUTT*/
- {
- // nothing to do
- break;
- }
- case css::drawing::LineCap_ROUND:
- {
- aPen.SetStartCap(Gdiplus::LineCapRound);
- aPen.SetEndCap(Gdiplus::LineCapRound);
- break;
- }
- case css::drawing::LineCap_SQUARE:
- {
- aPen.SetStartCap(Gdiplus::LineCapSquare);
- aPen.SetEndCap(Gdiplus::LineCapSquare);
- break;
- }
+ aPen.SetLineJoin(Gdiplus::LineJoinBevel);
+ break;
}
+ case basegfx::B2DLineJoin::Miter :
+ {
+ const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
+
+ aPen.SetMiterLimit(aMiterLimit);
+ // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
+ // graphics, somewhere clipped in some distance from the edge point, dependent
+ // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
+ // that instead
+ aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
+ break;
+ }
+ case basegfx::B2DLineJoin::Round :
+ {
+ aPen.SetLineJoin(Gdiplus::LineJoinRound);
+ break;
+ }
+ }
- impAddB2DPolygonToGDIPlusGraphicsPathReal(aGraphicsPath, rPolygon, bNoLineJoin);
-
- if(rPolygon.isClosed() && !bNoLineJoin)
+ switch(eLineCap)
+ {
+ default: /*css::drawing::LineCap_BUTT*/
{
- // #i101491# needed to create the correct line joins
- aGraphicsPath.CloseFigure();
+ // nothing to do
+ break;
+ }
+ case css::drawing::LineCap_ROUND:
+ {
+ aPen.SetStartCap(Gdiplus::LineCapRound);
+ aPen.SetEndCap(Gdiplus::LineCapRound);
+ break;
+ }
+ case css::drawing::LineCap_SQUARE:
+ {
+ aPen.SetStartCap(Gdiplus::LineCapSquare);
+ aPen.SetEndCap(Gdiplus::LineCapSquare);
+ break;
}
+ }
- if(mrParent.getAntiAliasB2DDraw())
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
+ rPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
+
+ if(pSystemDependentData_GraphicsPath)
+ {
+ // check data validity
+ if(pSystemDependentData_GraphicsPath->getPixelSnapHairline() != bPixelSnapHairline)
{
- aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
+ // data invalid, forget
+ pSystemDependentData_GraphicsPath.reset();
}
- else
+ }
+
+ if(!pSystemDependentData_GraphicsPath)
+ {
+ // add to buffering mechanism
+ pSystemDependentData_GraphicsPath = rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
+ SalGraphics::getSystemDependentDataManager());
+
+ // fill data of buffered data
+ pSystemDependentData_GraphicsPath->setPixelSnapHairline(bPixelSnapHairline);
+
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(
+ pSystemDependentData_GraphicsPath->getGraphicsPath(),
+ rPolygon,
+ rObjectToDevice,
+ bNoLineJoin,
+ bPixelSnapHairline);
+
+ if(rPolygon.isClosed() && !bNoLineJoin)
{
- aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
+ // #i101491# needed to create the correct line joins
+ pSystemDependentData_GraphicsPath->getGraphicsPath().CloseFigure();
}
+ }
- aGraphics.DrawPath(&aPen, &aGraphicsPath);
+ if(mrParent.getAntiAliasB2DDraw())
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
}
+ else
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
+ }
+
+ aGraphics.DrawPath(
+ &aPen,
+ &pSystemDependentData_GraphicsPath->getGraphicsPath());
return true;
}
diff --git a/vcl/win/gdi/gdiimpl.hxx b/vcl/win/gdi/gdiimpl.hxx
index 9ba7dd8bf7fe..1abb824de842 100644
--- a/vcl/win/gdi/gdiimpl.hxx
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -107,12 +107,14 @@ public:
virtual bool drawPolyPolygon( const basegfx::B2DPolyPolygon&, double fTransparency ) override;
virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin,
css::drawing::LineCap,
- double fMiterMinimumAngle) override;
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawPolyLineBezier(
sal_uInt32 nPoints,
diff --git a/vcl/win/gdi/salbmp.cxx b/vcl/win/gdi/salbmp.cxx
index cc3a051c0dd5..8f378e23c15c 100644
--- a/vcl/win/gdi/salbmp.cxx
+++ b/vcl/win/gdi/salbmp.cxx
@@ -59,136 +59,12 @@ inline void ImplSetPixel4( sal_uInt8* pScanline, long nX, const BYTE cIndex )
}
}
-// Helper class to manage Gdiplus::Bitmap instances inside of
-// WinSalBitmap
-
-struct Comparator
-{
- bool operator()(WinSalBitmap* pA, WinSalBitmap* pB) const
- {
- return pA < pB;
- }
-};
-
-typedef ::std::map< WinSalBitmap*, sal_uInt32, Comparator > EntryMap;
-static const sal_uInt32 nDefaultCycles(60);
-
-class GdiPlusBuffer : protected comphelper::OBaseMutex, public Timer
-{
-private:
- EntryMap maEntries;
-
-public:
- GdiPlusBuffer( const sal_Char *pDebugName )
- : Timer( pDebugName ),
- maEntries()
- {
- SetTimeout(1000);
- Stop();
- }
-
- ~GdiPlusBuffer()
- {
- Stop();
- }
-
- void addEntry(WinSalBitmap& rEntry)
- {
- ::osl::MutexGuard aGuard(m_aMutex);
- EntryMap::iterator aFound = maEntries.find(&rEntry);
-
- if(aFound == maEntries.end())
- {
- if(maEntries.empty())
- {
- Start();
- }
-
- maEntries[&rEntry] = nDefaultCycles;
- }
- }
-
- void remEntry(WinSalBitmap& rEntry)
- {
- ::osl::MutexGuard aGuard(m_aMutex);
- EntryMap::iterator aFound = maEntries.find(&rEntry);
-
- if(aFound != maEntries.end())
- {
- maEntries.erase(aFound);
-
- if(maEntries.empty())
- {
- Stop();
- }
- }
- }
-
- void touchEntry(WinSalBitmap& rEntry)
- {
- ::osl::MutexGuard aGuard(m_aMutex);
- EntryMap::iterator aFound = maEntries.find(&rEntry);
-
- if(aFound != maEntries.end())
- {
- aFound->second = nDefaultCycles;
- }
- }
-
- // from parent Timer
- virtual void Invoke()
- {
- ::osl::MutexGuard aGuard(m_aMutex);
- EntryMap::iterator aIter(maEntries.begin());
-
- while(aIter != maEntries.end())
- {
- if(aIter->second)
- {
- aIter->second--;
- ++aIter;
- }
- else
- {
- EntryMap::iterator aDelete(aIter);
- WinSalBitmap* pSource = aDelete->first;
- ++aIter;
- maEntries.erase(aDelete);
-
- if(maEntries.empty())
- {
- Stop();
- }
-
- // delete at WinSalBitmap after entry is removed; this
- // way it would not hurt to call remEntry from there, too
- if(pSource->maGdiPlusBitmap.get())
- {
- pSource->maGdiPlusBitmap.reset();
- pSource->mpAssociatedAlpha = 0;
- }
- }
- }
-
- if(!maEntries.empty())
- {
- Start();
- }
- }
-};
-
-// Global instance of GdiPlusBuffer which manages Gdiplus::Bitmap
-// instances
-
-static GdiPlusBuffer aGdiPlusBuffer( "vcl::win GdiPlusBuffer aGdiPlusBuffer" );
-
-
WinSalBitmap::WinSalBitmap()
-: maSize(),
- mhDIB(0),
- mhDDB(0),
- maGdiPlusBitmap(),
- mpAssociatedAlpha(0),
+: SalBitmap(),
+ basegfx::SystemDependentDataHolder(),
+ maSize(),
+ mhDIB(nullptr),
+ mhDDB(nullptr),
mnBitCount(0)
{
}
@@ -200,11 +76,6 @@ WinSalBitmap::~WinSalBitmap()
void WinSalBitmap::Destroy()
{
- if(maGdiPlusBitmap.get())
- {
- aGdiPlusBuffer.remEntry(*this);
- }
-
if( mhDIB )
GlobalFree( mhDIB );
else if( mhDDB )
@@ -214,45 +85,85 @@ void WinSalBitmap::Destroy()
mnBitCount = 0;
}
-GdiPlusBmpPtr WinSalBitmap::ImplGetGdiPlusBitmap(const WinSalBitmap* pAlphaSource) const
+class SystemDependentData_GdiPlusBitmap : public basegfx::SystemDependentData
{
- WinSalBitmap* pThat = const_cast< WinSalBitmap* >(this);
+private:
+ std::shared_ptr<Gdiplus::Bitmap> mpGdiPlusBitmap;
+ const WinSalBitmap* mpAssociatedAlpha;
+
+public:
+ SystemDependentData_GdiPlusBitmap(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager);
+ virtual ~SystemDependentData_GdiPlusBitmap() override;
+
+ const WinSalBitmap* getAssociatedAlpha() const { return mpAssociatedAlpha; }
+ void setAssociatedAlpha(const WinSalBitmap* pNew) { mpAssociatedAlpha = pNew; }
+
+ const std::shared_ptr<Gdiplus::Bitmap>& getGdiPlusBitmap() const { return mpGdiPlusBitmap; }
+ void setGdiPlusBitmap(const std::shared_ptr<Gdiplus::Bitmap>& rNew) { mpGdiPlusBitmap = rNew; }
+};
- if(maGdiPlusBitmap.get() && pAlphaSource != mpAssociatedAlpha)
+SystemDependentData_GdiPlusBitmap::SystemDependentData_GdiPlusBitmap(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager)
+: basegfx::SystemDependentData(rSystemDependentDataManager),
+ mpGdiPlusBitmap(),
+ mpAssociatedAlpha(nullptr)
+{
+}
+
+SystemDependentData_GdiPlusBitmap::~SystemDependentData_GdiPlusBitmap()
+{
+}
+
+std::shared_ptr< Gdiplus::Bitmap > WinSalBitmap::ImplGetGdiPlusBitmap(const WinSalBitmap* pAlphaSource) const
+{
+ std::shared_ptr< Gdiplus::Bitmap > aRetval;
+
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_GdiPlusBitmap> pSystemDependentData_GdiPlusBitmap(
+ getSystemDependentData<SystemDependentData_GdiPlusBitmap>());
+
+ if(pSystemDependentData_GdiPlusBitmap)
{
- // #122350# if associated alpha with which the GDIPlus was constructed has changed
- // it is necessary to remove it from buffer, reset reference to it and reconstruct
- pThat->maGdiPlusBitmap.reset();
- aGdiPlusBuffer.remEntry(const_cast< WinSalBitmap& >(*this));
+ // check data validity
+ if(pSystemDependentData_GdiPlusBitmap->getAssociatedAlpha() != pAlphaSource
+ || 0 == maSize.Width()
+ || 0 == maSize.Height())
+ {
+ // #122350# if associated alpha with which the GDIPlus was constructed has changed
+ // it is necessary to remove it from buffer, reset reference to it and reconstruct
+ // data invalid, forget
+ pSystemDependentData_GdiPlusBitmap.reset();
+ }
}
- if(maGdiPlusBitmap.get())
+ if(pSystemDependentData_GdiPlusBitmap)
{
- aGdiPlusBuffer.touchEntry(const_cast< WinSalBitmap& >(*this));
+ // use from buffer
+ aRetval = pSystemDependentData_GdiPlusBitmap->getGdiPlusBitmap();
}
- else
+ else if(maSize.Width() > 0 && maSize.Height() > 0)
{
- if(maSize.Width() > 0 && maSize.Height() > 0)
- {
- if(pAlphaSource)
- {
- pThat->maGdiPlusBitmap.reset(pThat->ImplCreateGdiPlusBitmap(*pAlphaSource));
- pThat->mpAssociatedAlpha = pAlphaSource;
- }
- else
- {
- pThat->maGdiPlusBitmap.reset(pThat->ImplCreateGdiPlusBitmap());
- pThat->mpAssociatedAlpha = 0;
- }
+ // add to buffering mechanism
+ pSystemDependentData_GdiPlusBitmap = addOrReplaceSystemDependentData<SystemDependentData_GdiPlusBitmap>(
+ SalGraphics::getSystemDependentDataManager());
- if(maGdiPlusBitmap.get())
- {
- aGdiPlusBuffer.addEntry(*pThat);
- }
+ // create and set data
+ if(pAlphaSource)
+ {
+ aRetval.reset(const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap(*pAlphaSource));
+ pSystemDependentData_GdiPlusBitmap->setGdiPlusBitmap(aRetval);
+ pSystemDependentData_GdiPlusBitmap->setAssociatedAlpha(pAlphaSource);
+ }
+ else
+ {
+ aRetval.reset(const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap());
+ pSystemDependentData_GdiPlusBitmap->setGdiPlusBitmap(aRetval);
+ pSystemDependentData_GdiPlusBitmap->setAssociatedAlpha(nullptr);
}
}
- return maGdiPlusBitmap;
+ return aRetval;
}
Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap()
diff --git a/vcl/win/gdi/salgdi_gdiplus.cxx b/vcl/win/gdi/salgdi_gdiplus.cxx
index 45e760ae871b..1f536e1843e6 100644
--- a/vcl/win/gdi/salgdi_gdiplus.cxx
+++ b/vcl/win/gdi/salgdi_gdiplus.cxx
@@ -32,15 +32,24 @@ bool WinSalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygo
}
bool WinSalGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& rPolygon,
double fTransparency,
const basegfx::B2DVector& rLineWidths,
basegfx::B2DLineJoin eLineJoin,
css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
- return mpImpl->drawPolyLine(rPolygon, fTransparency, rLineWidths,
- eLineJoin, eLineCap, fMiterMinimumAngle);
+ return mpImpl->drawPolyLine(
+ rObjectToDevice,
+ rPolygon,
+ fTransparency,
+ rLineWidths,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ bPixelSnapHairline);
}
bool WinSalGraphics::blendBitmap(