summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2018-08-24 13:01:08 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2018-08-30 19:48:46 +0200
commitb9fa01a8d1137a95af9865a3e47995734c40da6e (patch)
tree6d1e0a3e44b1a96fe5302d779c00fbee55cf8d24
parentf4a9ce33415a85d0b86ced3a0bf780f4ec61e25f (diff)
Support buffering SystemDependent GraphicData
This is a first step to allow buffering of system dependent data, especially (but not only) for the system-dependent implementations of graphic output. For example, for B2DPolygon and Win output, it allows buffering the Gdiplus::GraphicsPath instead of re- creating it all the time. To support that, the change includes forwarding the current transformation to the renderers in SalGraphics. The current state in VCL is to transform all and everything to device coordinates at every single paint. I have currently started to do this for ::drawPolyLine implementations. The fallbacks for all systems will at the start of that method just transform the data to device coordinates, so all works as before. This may also be done for FilledPolygon paint in a later step, but most urgent is FatLine painting. An arrangement of shared_ptr/weak_ptr is used so that either the instance buffering (in the example B2DPolygon) or the instance managing it can delete it. The instance managing it currently uses a 1s Timer and a cycle-lifetime management, but that can be extended in the future to e.g. include size hints, too. The mechanism it designed to support multiple Data per buffering element, e.g. for B2DPolygon at the same time system-dependent instances of Gdiplus and Cairo can be buffered, but also PDF-data. This is achieved semi-automatic by using typeid(class).hash_code() as key for organization. The mechanism will be used for now at B2DPolygon, but is not limited to. There is already a similar but less general buffer (see GdiPlusBuffer) that can and will be converted to use this new mechanism. Added vcl/headless Cairo renderer to support given ObjectToDevice transformation (not to transform given B2DPolygon) Added support for CairoPath buffered at B2DPolygon, seems to work well. Need to do more tests Moved usage to templates suggested by Noel Grandin (Noel Grandin <noelgrandin@gmail.com>), thanks for these suggestions. Adapted Win usage to that, too. Converted Win-specific GdiPlus BitmapBuffer to new mechanism, works well. Checked, the manager holds now a mix of bitmap and path data under Win Added a cleanup mechanism to flush all buffered data at DeInitVCL() using flushAll() at SystemDependentDataBuffer Adapted Linux-versions of ::drawPolyLine to support PixelSnapHairline, for now in a simplified version that still allows buffering. This will also be used (and use buffering) for the Cairo-fallback in X11SalGraphics Change-Id: I88d7e438a20b96ddab7707050893bdd590c098c7 Reviewed-on: https://gerrit.libreoffice.org/59555 Tested-by: Armin Le Grand <Armin.Le.Grand@cib.de> Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
-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--cui/source/dialogs/screenshotannotationdlg.cxx2
-rw-r--r--drawinglayer/source/primitive2d/polygonprimitive2d.cxx6
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx42
-rw-r--r--include/basegfx/polygon/b2dpolygon.hxx24
-rwxr-xr-xinclude/basegfx/utils/systemdependentdata.hxx139
-rw-r--r--include/vcl/outdev.hxx1
-rw-r--r--solenv/clang-format/blacklist2
-rw-r--r--vcl/headless/svpgdi.cxx289
-rw-r--r--vcl/inc/headless/svpgdi.hxx26
-rw-r--r--vcl/inc/openglgdiimpl.hxx4
-rw-r--r--vcl/inc/qt5/Qt5Graphics.hxx6
-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.cxx45
-rw-r--r--vcl/qt5/Qt5Graphics_GDI.cxx23
-rw-r--r--vcl/quartz/salgdicommon.cxx39
-rw-r--r--vcl/source/app/svmain.cxx5
-rw-r--r--vcl/source/gdi/salgdilayout.cxx183
-rw-r--r--vcl/source/outdev/line.cxx19
-rw-r--r--vcl/source/outdev/polygon.cxx82
-rw-r--r--vcl/source/outdev/polyline.cxx152
-rw-r--r--vcl/source/outdev/transparent.cxx20
-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.cxx44
-rw-r--r--vcl/unx/generic/print/genpspgraphics.cxx4
-rw-r--r--vcl/win/gdi/gdiimpl.cxx280
-rw-r--r--vcl/win/gdi/gdiimpl.hxx4
-rw-r--r--vcl/win/gdi/salbmp.cxx219
-rw-r--r--vcl/win/gdi/salgdi_gdiplus.cxx15
38 files changed, 1479 insertions, 504 deletions
diff --git a/basegfx/Library_basegfx.mk b/basegfx/Library_basegfx.mk
index 76d06b777668..0e428631056c 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/zoomtools \
diff --git a/basegfx/source/polygon/b2dpolygon.cxx b/basegfx/source/polygon/b2dpolygon.cxx
index 9372cb3d9038..c94f262d6600 100644
--- a/basegfx/source/polygon/b2dpolygon.cxx
+++ b/basegfx/source/polygon/b2dpolygon.cxx
@@ -24,6 +24,7 @@
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
#include <algorithm>
#include <memory>
#include <vector>
@@ -455,20 +456,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
{
@@ -1100,6 +1102,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
@@ -1470,6 +1492,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..45f2efba5012
--- /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/utils/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/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx
index 86e53c89e081..5805c1f1537e 100644
--- a/cui/source/dialogs/screenshotannotationdlg.cxx
+++ b/cui/source/dialogs/screenshotannotationdlg.cxx
@@ -40,6 +40,7 @@
#include <vcl/vclmedit.hxx>
#include <vcl/button.hxx>
#include <svtools/optionsdrawinglayer.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
using namespace com::sun::star;
@@ -454,6 +455,7 @@ void ScreenshotAnnotationDlg_Impl::PaintControlDataEntry(
// try to use transparency
if (!mpVirtualBufferDevice->DrawPolyLineDirect(
+ basegfx::B2DHomMatrix(),
aPolygon,
fLineWidth,
fTransparency,
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
index e296f397e01b..2350f28699fc 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -291,6 +291,9 @@ namespace drawinglayer
maLineAttribute(rLineAttribute),
maStrokeAttribute(rStrokeAttribute)
{
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
}
PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
@@ -301,6 +304,9 @@ namespace drawinglayer
maLineAttribute(rLineAttribute),
maStrokeAttribute()
{
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
}
bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 0845c3316643..3295a97129f3 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -127,9 +127,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;
@@ -139,10 +139,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
- return mpOutputDevice->DrawPolyLineDirect( aLocalPolygon, 0.0, fTransparency);
+ return mpOutputDevice->DrawPolyLineDirect(
+ maCurrentTransformation,
+ rLocalPolygon,
+ 0.0,
+ fTransparency);
}
bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency)
@@ -158,7 +162,8 @@ namespace drawinglayer
basegfx::B2DPolyPolygon aHairLinePolyPolygon;
// simplify curve segments
- aLocalPolygon = basegfx::utils::simplifyCurveSegments(aLocalPolygon);
+ // moved to PolygonStrokePrimitive2D::PolygonStrokePrimitive2D
+ // aLocalPolygon = basegfx::utils::simplifyCurveSegments(aLocalPolygon);
if(rSource.getStrokeAttribute().isDefault() || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen())
{
@@ -182,24 +187,24 @@ namespace drawinglayer
return true;
}
+ // 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(
@@ -208,7 +213,9 @@ namespace drawinglayer
mpOutputDevice->SetFillColor();
mpOutputDevice->SetLineColor(Color(aLineColor));
- aHairLinePolyPolygon.transform(maCurrentTransformation);
+
+ // do not transform self
+ // aHairLinePolyPolygon.transform(maCurrentTransformation);
bool bHasPoints(false);
bool bTryWorked(false);
@@ -222,6 +229,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 714d2a1fd10d..353e7130f5ed 100644
--- a/include/basegfx/polygon/b2dpolygon.hxx
+++ b/include/basegfx/polygon/b2dpolygon.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
@@ -218,6 +222,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/utils/systemdependentdata.hxx b/include/basegfx/utils/systemdependentdata.hxx
new file mode 100755
index 000000000000..17a0ce44f815
--- /dev/null
+++ b/include/basegfx/utils/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 54c22e6cc7fd..57bedf5b0635 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -800,6 +800,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/solenv/clang-format/blacklist b/solenv/clang-format/blacklist
index cd832249bf2d..810ca66431fc 100644
--- a/solenv/clang-format/blacklist
+++ b/solenv/clang-format/blacklist
@@ -369,6 +369,7 @@ basegfx/source/tools/gradienttools.cxx
basegfx/source/tools/keystoplerp.cxx
basegfx/source/tools/numbertools.cxx
basegfx/source/tools/stringconversiontools.cxx
+basegfx/source/tools/systemdependentdata.cxx
basegfx/source/tools/tools.cxx
basegfx/source/tools/unopolypolygon.cxx
basegfx/source/tools/unotools.cxx
@@ -5838,6 +5839,7 @@ include/basegfx/utils/gradienttools.hxx
include/basegfx/utils/keystoplerp.hxx
include/basegfx/utils/lerp.hxx
include/basegfx/utils/rectcliptools.hxx
+include/basegfx/utils/systemdependentdata.hxx
include/basegfx/utils/tools.hxx
include/basegfx/utils/unopolypolygon.hxx
include/basegfx/utils/zoomtools.hxx
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index f8ada21f8acd..fdf2295f367f 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -35,6 +35,8 @@
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
#if ENABLE_CAIRO_CANVAS
# if defined CAIRO_VERSION && CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 10, 0)
@@ -720,8 +722,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, basegfx::deg2rad(15.0) /*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)
@@ -879,18 +888,141 @@ void SvpSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
releaseCairoContext(cr, false, extents);
}
-basegfx::B2DRange SvpSalGraphics::drawPolyLine(
+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,
+ bool bPixelSnapHairline)
+{
+ // short circuit if there is nothing to do
+ if(0 == rPolyLine.count())
+ {
+ return true;
+ }
+
+ // 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)
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
{
- const bool bNoJoin = (basegfx::B2DLineJoin::NONE == eLineJoin && basegfx::fTools::more(rLineWidths.getX(), 0.0));
+ // 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;
@@ -933,76 +1065,131 @@ basegfx::B2DRange SvpSalGraphics::drawPolyLine(
}
}
- cairo_set_source_rgba(cr, rLineColor.GetRed()/255.0,
- rLineColor.GetGreen()/255.0,
- rLineColor.GetBlue()/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(), !bAntiAliasB2DDraw, true);
- extents = getClippedStrokeDamage(cr);
- cairo_stroke(cr);
+ // re-use data
+ cairo_append_path(cr, pSystemDependentData_CairoPath->getCairoPath());
}
else
{
- const int nPointCount = rPolyLine.count();
- // 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, !bAntiAliasB2DDraw, true);
+ aPolyLine = basegfx::utils::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));
- return extents;
-}
+ 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));
-bool SvpSalGraphics::drawPolyLine(
- const basegfx::B2DPolygon& rPolyLine,
- double fTransparency,
- const basegfx::B2DVector& rLineWidths,
- basegfx::B2DLineJoin eLineJoin,
- css::drawing::LineCap eLineCap,
- double fMiterMinimumAngle)
-{
- // short circuit if there is nothing to do
- if (rPolyLine.count() <= 0)
- return true;
+ AddPolygonToPath(cr, aEdge, false, !bAntiAliasB2DDraw, true);
- cairo_t* cr = getCairoContext(false);
- clipRegion(cr);
+ // prepare next step
+ aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+ }
+ }
- basegfx::B2DRange extents = drawPolyLine(cr, m_aLineColor, getAntiAliasB2DDraw(), rPolyLine,
- fTransparency, rLineWidths, eLineJoin, eLineCap, fMiterMinimumAngle);
+ // 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 8877b2ebf201..158c331eded6 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -99,16 +99,23 @@ public:
static cairo_user_data_key_t* getDamageKey();
static void clipRegion(cairo_t* cr, const vcl::Region& rClipRegion);
- static basegfx::B2DRange drawPolyLine(
+
+ // 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);
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline);
private:
void invert(const basegfx::B2DPolygon &rPoly, SalInvert nFlags);
@@ -194,12 +201,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 952a91e481e2..f2fd9b7819dd 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -252,12 +252,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/qt5/Qt5Graphics.hxx b/vcl/inc/qt5/Qt5Graphics.hxx
index fa9823b0e789..6bde41ffa27f 100644
--- a/vcl/inc/qt5/Qt5Graphics.hxx
+++ b/vcl/inc/qt5/Qt5Graphics.hxx
@@ -116,9 +116,11 @@ public:
virtual bool drawPolyPolygonBezier(sal_uInt32 nPoly, const sal_uInt32* pPoints,
const SalPoint* const* pPtAry,
const PolyFlags* const* pFlgAry) override;
- virtual bool drawPolyLine(const basegfx::B2DPolygon&, double fTransparency,
+ 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;
+ css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
virtual bool drawGradient(const tools::PolyPolygon&, const Gradient&) override;
virtual void copyArea(long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth,
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 31a8353a17ed..b03fa5b6b3b2 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -229,12 +229,14 @@ public:
virtual bool drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry ) override;
virtual bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const PolyFlags* 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 8744c8f77573..866a10b5beb4 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
@@ -76,6 +77,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;
@@ -242,12 +247,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(
@@ -458,12 +465,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 3fb3a0bfd7f9..8e545d093d20 100644
--- a/vcl/inc/salgdiimpl.hxx
+++ b/vcl/inc/salgdiimpl.hxx
@@ -103,12 +103,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 fd06248ff19f..3f6bae6c0744 100644
--- a/vcl/inc/unx/genpspgraphics.h
+++ b/vcl/inc/unx/genpspgraphics.h
@@ -128,12 +128,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 PolyFlags* pFlgAry ) override;
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index 408434f9062e..8b73c4583243 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -163,12 +163,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 f139a8608b30..7f172d21af82 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/utils/systemdependentdata.hxx>
#include <memory>
@@ -32,26 +33,13 @@ class BitmapPalette;
class SalGraphics;
namespace Gdiplus { class Bitmap; }
-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.
- std::shared_ptr< Gdiplus::Bitmap > maGdiPlusBitmap;
- const WinSalBitmap* mpAssociatedAlpha;
-
sal_uInt16 mnBitCount;
Gdiplus::Bitmap* ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource);
@@ -98,6 +86,22 @@ public:
virtual bool ScalingSupported() const override;
virtual bool Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag ) override;
virtual bool Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol ) override;
+
+ // 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 0540b66b34f7..51d14cb7ddda 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -237,12 +237,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 PolyFlags* pFlgAry ) override;
virtual bool drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry ) override;
virtual bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const PolyFlags* const* pFlgAry ) override;
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 5efecae3a34e..9fff1d50cc13 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1549,8 +1549,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, basegfx::deg2rad(15.0) /*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 )
@@ -1594,19 +1601,39 @@ bool OpenGLSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DPolyPolygon& rPoly
return true;
}
-bool OpenGLSalGraphicsImpl::drawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTransparency,
- const basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin,
- css::drawing::LineCap eLineCap, double fMiterMinimumAngle)
+bool OpenGLSalGraphicsImpl::drawPolyLine(
+ 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 " << rPolygon.getB2DRange());
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ basegfx::B2DPolygon aPolyLine(rPolygon);
+ aPolyLine.transform(rObjectToDevice);
+ if(bPixelSnapHairline) { aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+ const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth);
+
// addDrawPolyLine() assumes that there are no duplicate points in the
// polygon.
- basegfx::B2DPolygon aPolygon(rPolygon);
- aPolygon.removeDoublePoints();
+ // basegfx::B2DPolygon aPolygon(rPolygon);
+ aPolyLine.removeDoublePoints();
+
+ mpRenderList->addDrawPolyLine(
+ aPolyLine,
+ fTransparency,
+ aLineWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ mnLineColor,
+ mrParent.getAntiAliasB2DDraw());
- mpRenderList->addDrawPolyLine(aPolygon, fTransparency, rLineWidth, eLineJoin, eLineCap,
- fMiterMinimumAngle, mnLineColor, mrParent.getAntiAliasB2DDraw());
PostBatchDraw();
return true;
}
diff --git a/vcl/qt5/Qt5Graphics_GDI.cxx b/vcl/qt5/Qt5Graphics_GDI.cxx
index 1a61fe540e79..5470295cbf32 100644
--- a/vcl/qt5/Qt5Graphics_GDI.cxx
+++ b/vcl/qt5/Qt5Graphics_GDI.cxx
@@ -316,28 +316,39 @@ bool Qt5Graphics::drawPolyPolygonBezier(sal_uInt32 /*nPoly*/, const sal_uInt32*
return false;
}
-bool Qt5Graphics::drawPolyLine(const basegfx::B2DPolygon& rPolyLine, double fTransparency,
+bool Qt5Graphics::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)
{
if (SALCOLOR_NONE == m_aFillColor && SALCOLOR_NONE == m_aLineColor)
return true;
// short circuit if there is nothing to do
- const int nPointCount = rPolyLine.count();
- if (nPointCount <= 0)
+ if (0 == rPolyLine.count())
+ {
return true;
+ }
+
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ basegfx::B2DPolygon aPolyLine(rPolyLine);
+ aPolyLine.transform(rObjectToDevice);
+ if (bPixelSnapHairline)
+ {
+ aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine);
+ }
+ const basegfx::B2DVector aLineWidths(rObjectToDevice * rLineWidths);
// setup poly-polygon path
QPainterPath aPath;
- AddPolygonToPath(aPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true);
+ AddPolygonToPath(aPath, aPolyLine, aPolyLine.isClosed(), !getAntiAliasB2DDraw(), true);
Qt5Painter aPainter(*this, false, 255 * (1.0 - fTransparency));
// setup line attributes
QPen aPen = aPainter.pen();
- aPen.setWidth(rLineWidths.getX());
+ aPen.setWidth(aLineWidths.getX());
switch (eLineJoin)
{
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 77241c282ea4..09adf784d2ad 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -24,6 +24,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>
@@ -943,18 +944,20 @@ void AquaSalGraphics::drawPixel( long nX, long nY, Color nColor )
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;
@@ -968,16 +971,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::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+
// setup line attributes
CGLineJoin aCGLineJoin = kCGLineJoinMiter;
switch( eLineJoin )
@@ -1014,7 +1024,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 );
@@ -1034,7 +1049,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 a91b5c6686db..9e4e3ca1a04a 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -101,6 +101,8 @@
#include <opengl/zone.hxx>
#include <opengl/watchdog.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
+
#if OSL_DEBUG_LEVEL > 0
#include <typeinfo>
#include <rtl/strbuf.hxx>
@@ -426,6 +428,9 @@ 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 8a70deeb3a59..08fb2fae49f7 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -31,6 +31,10 @@
#include <salgdi.hxx>
#include <salframe.hxx>
#include <basegfx/numeric/ftools.hxx> //for F_PI180
+#include <basegfx/utils/systemdependentdata.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <o3tl/make_unique.hxx>
// The only common SalFrame method
@@ -75,6 +79,131 @@ namespace
}
}
+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);
+ SetStatic();
+ }
+
+ 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();
+}
+
rtl::Reference<OpenGLContext> SalGraphics::GetOpenGLContext() const
{
OpenGLSalGraphicsImpl *pImpl = dynamic_cast<OpenGLSalGraphicsImpl*>(GetImpl());
@@ -512,22 +641,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 8c856a83cd7c..fe16d676c29f 100644
--- a/vcl/source/outdev/line.cxx
+++ b/vcl/source/outdev/line.cxx
@@ -132,18 +132,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::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
- }
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
+ // }
if( mpGraphics->DrawPolyLine(
+ basegfx::B2DHomMatrix(),
aB2DPolyLine,
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))
{
return;
@@ -242,24 +245,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,
basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
this);
}
if(!bDone)
{
tools::Polygon aPolygon(aCandidate);
- mpGraphics->DrawPolyLine(aPolygon.GetSize(), reinterpret_cast<SalPoint*>(aPolygon.GetPointAry()), this);
+ mpGraphics->DrawPolyLine(
+ aPolygon.GetSize(),
+ reinterpret_cast<SalPoint*>(aPolygon.GetPointAry()),
+ this);
}
}
}
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx
index bef902a2d273..94ad52a79dbd 100644
--- a/vcl/source/outdev/polygon.cxx
+++ b/vcl/source/outdev/polygon.cxx
@@ -86,21 +86,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::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
- }
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyPolygon = basegfx::utils::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,
- basegfx::deg2rad(15.0), // 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);
}
}
@@ -199,19 +203,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::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon);
- }
-
- bSuccess = mpGraphics->DrawPolyLine( aB2DPolygon,
- 0.0,
- aB2DLineWidth,
- basegfx::B2DLineJoin::NONE,
- css::drawing::LineCap_BUTT,
- basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
- this);
+ const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
+
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolygon = basegfx::utils::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)
@@ -302,21 +310,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::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyPolygon);
- }
+ // if(mnAntialiasing & AntialiasingFlags::PixelSnapHairline)
+ // {
+ // aB2DPolyPolygon = basegfx::utils::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,
- basegfx::deg2rad(15.0), // 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 bd5ca14b57f5..b5c06b9aa411 100644
--- a/vcl/source/outdev/polyline.cxx
+++ b/vcl/source/outdev/polyline.cxx
@@ -59,31 +59,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::utils::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine);
- }
+ return;
+ }
- if(mpGraphics->DrawPolyLine(
- aB2DPolyLine,
- 0.0,
- aB2DLineWidth,
- basegfx::B2DLineJoin::NONE,
- css::drawing::LineCap_BUTT,
- basegfx::deg2rad(15.0) /*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::utils::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 );
@@ -175,8 +182,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
@@ -222,9 +238,15 @@ void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon,
// to avoid optical gaps
for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++)
{
- (void)DrawPolyLineDirect(aAreaPolyPolygon.getB2DPolygon(a), 0.0, 0.0,
- basegfx::B2DLineJoin::NONE, css::drawing::LineCap_BUTT,
- basegfx::deg2rad(15.0) /*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
@@ -287,13 +309,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());
@@ -322,37 +346,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::utils::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::utils::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 42874c7eba4e..ecd63b642b47 100644
--- a/vcl/source/outdev/transparent.cxx
+++ b/vcl/source/outdev/transparent.cxx
@@ -255,17 +255,22 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly,
if( bDrawnOk && IsLineColor() )
{
const basegfx::B2DVector aHairlineWidth(1,1);
- const sal_uInt32 nPolyCount = aB2DPolyPolygon.count();
+ 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,
basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
+ bPixelSnapHairline,
this );
}
}
@@ -358,17 +363,22 @@ bool OutputDevice::DrawTransparentNatively ( const tools::PolyPolygon& rPolyPoly
mpGraphics->SetFillColor();
// draw the border line
const basegfx::B2DVector aLineWidths( 1, 1 );
- const sal_uInt32 nPolyCount = aB2DPolyPolygon.count();
+ 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,
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 901e9fd3b0ce..c650f9dd4dad 100644
--- a/vcl/unx/generic/gdi/gdiimpl.cxx
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -1564,14 +1564,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) )
@@ -1585,18 +1589,23 @@ bool X11SalGraphicsImpl::drawPolyLine(
return false;
}
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ basegfx::B2DPolygon aPolyLine(rPolygon);
+ aPolyLine.transform(rObjectToDevice);
+ if(bPixelSnapHairline) { aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); }
+
// temporarily adjust brush color to pen color
// since the line is drawn as an area-polygon
const Color aKeepBrushColor = mnBrushColor;
mnBrushColor = mnPenColor;
// #i11575#desc5#b adjust B2D tessellation result to raster positions
- basegfx::B2DPolygon aPolygon = rPolygon;
- const double fHalfWidth = 0.5 * rLineWidth.getX();
+ // 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::utils::createTranslateB2DHomMatrix(0.5, 0.5) );
+ aPolyLine.transform( basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) );
// shortcut for hairline drawing to improve performance
bool bDrawnOk = true;
@@ -1605,7 +1614,7 @@ bool X11SalGraphicsImpl::drawPolyLine(
// hairlines can benefit from a simplified tessellation
// e.g. for hairlines the linejoin style can be ignored
basegfx::B2DTrapezoidVector aB2DTrapVector;
- basegfx::utils::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolygon, rLineWidth.getX() );
+ basegfx::utils::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolyLine, aLineWidth.getX() );
// draw tessellation result
const int nTrapCount = aB2DTrapVector.size();
@@ -1618,21 +1627,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::utils::createScaleB2DHomMatrix(1.0, rLineWidth.getX() / rLineWidth.getY()));
+ aPolyLine.transform( basegfx::utils::createScaleB2DHomMatrix(1.0, aLineWidth.getX() / aLineWidth.getY()));
}
// create the area-polygon for the line
- const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::utils::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin, eLineCap, fMiterMinimumAngle) );
-
- if( (rLineWidth.getX() != rLineWidth.getY())
- && !basegfx::fTools::equalZero( rLineWidth.getX() ) )
+ const basegfx::B2DPolyPolygon aAreaPolyPoly(
+ basegfx::utils::createAreaGeometry(
+ aPolyLine,
+ fHalfWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle));
+
+ if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getX() ) )
{
// postprocess createAreaGeometry() for anisotropic linewidth
- aPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(1.0, rLineWidth.getY() / rLineWidth.getX()));
+ aPolyLine.transform(basegfx::utils::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 156700fcac8a..f738e1e996ea 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 622dc223dcc6..1d6d5bf657ac 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -696,16 +696,16 @@ void X11SalGraphics::clipRegion(cairo_t* cr)
#endif // ENABLE_CAIRO_CANVAS
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)
{
- const int nPointCount(rPolygon.count());
-
- if(nPointCount <= 0)
+ if(0 == rPolygon.count())
{
return true;
}
@@ -723,17 +723,41 @@ bool X11SalGraphics::drawPolyLine(
cairo_t* cr = getCairoContext();
clipRegion(cr);
- SvpSalGraphics::drawPolyLine(cr, mnPenColor, getAntiAliasB2DDraw(),
- rPolygon, fTransparency, rLineWidth,
- eLineJoin, eLineCap, fMiterMinimumAngle);
+ // Use the now available static drawPolyLine from the Cairo-Headless-Fallback
+ // that will take care of all needed stuff
+ const bool bRetval(
+ SvpSalGraphics::drawPolyLine(
+ cr,
+ nullptr,
+ mnPenColor,
+ getAntiAliasB2DDraw(),
+ rObjectToDevice,
+ rPolygon,
+ fTransparency,
+ rLineWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ bPixelSnapHairline));
releaseCairoContext(cr);
- return true;
+
+ if(bRetval)
+ {
+ return true;
+ }
}
#endif // ENABLE_CAIRO_CANVAS
- return mxImpl->drawPolyLine( rPolygon, fTransparency, rLineWidth,
- eLineJoin, eLineCap, fMiterMinimumAngle );
+ 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 a9c9483890f0..4ee35c8b72eb 100644
--- a/vcl/unx/generic/print/genpspgraphics.cxx
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -411,12 +411,14 @@ bool GenPspGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon&, double /*f
}
bool GenPspGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& /* rObjectToDevice */,
const basegfx::B2DPolygon&,
double /*fTransparency*/,
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 a66f1f5f9579..bf2dcc9197e0 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/utils/systemdependentdata.hxx>
#include <outdata.hxx>
#include <win/salids.hrc>
@@ -1823,28 +1824,84 @@ 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));
if(nEdgeCount)
{
+ const bool bControls(rPolygon.areControlPointsUsed());
+ basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
+ basegfx::B2DHomMatrix aObjectToDeviceInv;
+
+ if(bPixelSnapHairline)
+ {
+ aCurr = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, 0);
+ }
+
for(sal_uInt32 a(0); a < nEdgeCount; a++)
{
const sal_uInt32 nNextIndex((a + 1) % nCount);
- const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
+ basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
+ if(bPixelSnapHairline)
+ {
+ aNext = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, nNextIndex);
+ }
+
if(b1stControlPointUsed || b2ndControlPointUsed)
{
basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
@@ -1914,7 +1971,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();
}
@@ -1954,100 +2016,172 @@ 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 = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
- const Gdiplus::Color aTestColor(aTrans, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
- 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, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
+ Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(rLineWidths.getX()));
+ bool bNoLineJoin(false);
+ Gdiplus::Matrix aMatrix;
- switch(eLineJoin)
+ // 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)
+ {
+ 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 3bd39663aa9c..83d4125207a2 100644
--- a/vcl/win/gdi/gdiimpl.hxx
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -109,12 +109,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 36bab849a316..fa8b011ab5aa 100644
--- a/vcl/win/gdi/salbmp.cxx
+++ b/vcl/win/gdi/salbmp.cxx
@@ -64,128 +64,12 @@ inline void ImplSetPixel4( sal_uInt8* pScanline, long nX, const BYTE cIndex )
}
}
-// Helper class to manage Gdiplus::Bitmap instances inside of
-// WinSalBitmap
-
-typedef ::std::map< WinSalBitmap*, sal_uInt32 > EntryMap;
-static const sal_uInt32 nDefaultCycles(60);
-
-class GdiPlusBuffer : protected cppu::BaseMutex, public Timer
-{
-private:
- EntryMap maEntries;
-
-public:
- GdiPlusBuffer( const sal_Char *pDebugName )
- : Timer( pDebugName ),
- maEntries()
- {
- SetTimeout(1000);
- SetStatic();
- }
-
- ~GdiPlusBuffer() override
- {
- 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() 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);
- 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 = nullptr;
- }
- }
- }
-
- if(!maEntries.empty())
- {
- Start();
- }
- }
-};
-
-// Global instance of GdiPlusBuffer which manages Gdiplus::Bitmap
-// instances
-
-static GdiPlusBuffer aGdiPlusBuffer( "vcl::win GdiPlusBuffer aGdiPlusBuffer" );
-
-
WinSalBitmap::WinSalBitmap()
-: maSize(),
+: SalBitmap(),
+ basegfx::SystemDependentDataHolder(),
+ maSize(),
mhDIB(nullptr),
mhDDB(nullptr),
- maGdiPlusBitmap(),
- mpAssociatedAlpha(nullptr),
mnBitCount(0)
{
}
@@ -197,11 +81,6 @@ WinSalBitmap::~WinSalBitmap()
void WinSalBitmap::Destroy()
{
- if(maGdiPlusBitmap.get())
- {
- aGdiPlusBuffer.remEntry(*this);
- }
-
if( mhDIB )
GlobalFree( mhDIB );
else if( mhDDB )
@@ -211,45 +90,85 @@ void WinSalBitmap::Destroy()
mnBitCount = 0;
}
+class SystemDependentData_GdiPlusBitmap : public basegfx::SystemDependentData
+{
+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; }
+};
+
+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
{
- WinSalBitmap* pThat = const_cast< WinSalBitmap* >(this);
+ std::shared_ptr< Gdiplus::Bitmap > aRetval;
+
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_GdiPlusBitmap> pSystemDependentData_GdiPlusBitmap(
+ getSystemDependentData<SystemDependentData_GdiPlusBitmap>());
- if(maGdiPlusBitmap.get() && pAlphaSource != mpAssociatedAlpha)
+ 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 = nullptr;
- }
+ // 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(