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