summaryrefslogtreecommitdiffstats
path: root/vcl/unx/generic/gdi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/unx/generic/gdi
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/unx/generic/gdi')
-rw-r--r--vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.cxx243
-rw-r--r--vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.hxx188
-rw-r--r--vcl/unx/generic/gdi/cairo_xlib_cairo.cxx277
-rw-r--r--vcl/unx/generic/gdi/cairo_xlib_cairo.hxx93
-rw-r--r--vcl/unx/generic/gdi/cairotextrender.cxx526
-rw-r--r--vcl/unx/generic/gdi/font.cxx84
-rw-r--r--vcl/unx/generic/gdi/freetypetextrender.cxx185
-rw-r--r--vcl/unx/generic/gdi/salgdi.cxx298
-rw-r--r--vcl/unx/generic/gdi/salvd.cxx241
9 files changed, 2135 insertions, 0 deletions
diff --git a/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.cxx b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.cxx
new file mode 100644
index 0000000000..5a751f9ea5
--- /dev/null
+++ b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.cxx
@@ -0,0 +1,243 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "X11CairoSalGraphicsImpl.hxx"
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <salframe.hxx>
+
+X11CairoSalGraphicsImpl::X11CairoSalGraphicsImpl(X11SalGraphics& rParent, CairoCommon& rCairoCommon)
+ : mrParent(rParent)
+ , mrCairoCommon(rCairoCommon)
+{
+}
+
+tools::Long X11CairoSalGraphicsImpl::GetGraphicsWidth() const
+{
+ if (mrParent.m_pFrame)
+ return mrParent.m_pFrame->maGeometry.width();
+ return mrCairoCommon.m_pSurface ? mrCairoCommon.m_aFrameSize.getX() : 0;
+}
+
+void X11CairoSalGraphicsImpl::drawRect(tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::Long nHeight)
+{
+ mrCairoCommon.drawRect(nX, nY, nWidth, nHeight, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawPolygon(sal_uInt32 nPoints, const Point* pPtAry)
+{
+ mrCairoCommon.drawPolygon(nPoints, pPtAry, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPointCounts,
+ const Point** pPtAry)
+{
+ mrCairoCommon.drawPolyPolygon(nPoly, pPointCounts, pPtAry, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ mrCairoCommon.drawPolyPolygon(rObjectToDevice, rPolyPolygon, fTransparency, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawPixel(tools::Long nX, tools::Long nY)
+{
+ mrCairoCommon.drawPixel(mrCairoCommon.m_oLineColor, nX, nY, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawPixel(tools::Long nX, tools::Long nY, Color aColor)
+{
+ mrCairoCommon.drawPixel(aColor, nX, nY, getAntiAlias());
+}
+
+Color X11CairoSalGraphicsImpl::getPixel(tools::Long nX, tools::Long nY)
+{
+ return CairoCommon::getPixel(mrCairoCommon.m_pSurface, nX, nY);
+}
+
+void X11CairoSalGraphicsImpl::drawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2,
+ tools::Long nY2)
+{
+ mrCairoCommon.drawLine(nX1, nY1, nX2, nY2, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const Point* pPtAry)
+{
+ mrCairoCommon.drawPolyLine(nPoints, pPtAry, getAntiAlias());
+}
+
+bool X11CairoSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolyLine,
+ double fTransparency, double fLineWidth,
+ const std::vector<double>* pStroke,
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle, bool bPixelSnapHairline)
+{
+ return mrCairoCommon.drawPolyLine(rObjectToDevice, rPolyLine, fTransparency, fLineWidth,
+ pStroke, eLineJoin, eLineCap, fMiterMinimumAngle,
+ bPixelSnapHairline, getAntiAlias());
+}
+
+bool X11CairoSalGraphicsImpl::drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::Long nHeight, sal_uInt8 nTransparency)
+{
+ return mrCairoCommon.drawAlphaRect(nX, nY, nWidth, nHeight, nTransparency, getAntiAlias());
+}
+
+bool X11CairoSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPolygon,
+ const Gradient& rGradient)
+{
+ return mrCairoCommon.drawGradient(rPolyPolygon, rGradient, getAntiAlias());
+}
+
+bool X11CairoSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon const& rPolyPolygon,
+ SalGradient const& rGradient)
+{
+ return mrCairoCommon.implDrawGradient(rPolyPolygon, rGradient, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::invert(tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::Long nHeight, SalInvert nFlags)
+{
+ mrCairoCommon.invert(nX, nY, nWidth, nHeight, nFlags, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::invert(sal_uInt32 nPoints, const Point* pPtAry, SalInvert nFlags)
+{
+ mrCairoCommon.invert(nPoints, pPtAry, nFlags, getAntiAlias());
+}
+
+bool X11CairoSalGraphicsImpl::hasFastDrawTransformedBitmap() const
+{
+ return CairoCommon::hasFastDrawTransformedBitmap();
+}
+
+bool X11CairoSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
+{
+ return CairoCommon::supportsOperation(eType);
+}
+
+void X11CairoSalGraphicsImpl::copyArea(tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX,
+ tools::Long nSrcY, tools::Long nSrcWidth,
+ tools::Long nSrcHeight, bool /*bWindowInvalidate*/)
+{
+ SalTwoRect aTR(nSrcX, nSrcY, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight);
+
+ cairo_surface_t* source = mrCairoCommon.m_pSurface;
+ mrCairoCommon.copyBitsCairo(aTR, source, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::copyBits(const SalTwoRect& rTR, SalGraphics* pSrcGraphics)
+{
+ cairo_surface_t* source = nullptr;
+
+ if (pSrcGraphics)
+ {
+ X11CairoSalGraphicsImpl* pSrc
+ = static_cast<X11CairoSalGraphicsImpl*>(pSrcGraphics->GetImpl());
+ source = pSrc->mrCairoCommon.m_pSurface;
+ }
+ else
+ {
+ source = mrCairoCommon.m_pSurface;
+ }
+
+ mrCairoCommon.copyBitsCairo(rTR, source, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
+{
+ mrCairoCommon.drawBitmap(rPosAry, rSalBitmap, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
+ const SalBitmap& rTransparentBitmap)
+{
+ drawAlphaBitmap(rPosAry, rSalBitmap, rTransparentBitmap);
+}
+
+bool X11CairoSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap,
+ const SalBitmap& rAlphaBmp)
+{
+ return mrCairoCommon.drawAlphaBitmap(rTR, rSrcBitmap, rAlphaBmp, getAntiAlias());
+}
+
+void X11CairoSalGraphicsImpl::drawMask(const SalTwoRect& rTR, const SalBitmap& rSalBitmap,
+ Color nMaskColor)
+{
+ mrCairoCommon.drawMask(rTR, rSalBitmap, nMaskColor, getAntiAlias());
+}
+
+std::shared_ptr<SalBitmap> X11CairoSalGraphicsImpl::getBitmap(tools::Long nX, tools::Long nY,
+ tools::Long nWidth,
+ tools::Long nHeight)
+{
+ return mrCairoCommon.getBitmap(nX, nY, nWidth, nHeight);
+}
+
+void X11CairoSalGraphicsImpl::Init() {}
+
+void X11CairoSalGraphicsImpl::freeResources() {}
+
+bool X11CairoSalGraphicsImpl::drawPolyLineBezier(sal_uInt32, const Point*, const PolyFlags*)
+{
+ return false;
+}
+
+bool X11CairoSalGraphicsImpl::drawPolygonBezier(sal_uInt32, const Point*, const PolyFlags*)
+{
+ return false;
+}
+
+bool X11CairoSalGraphicsImpl::drawPolyPolygonBezier(sal_uInt32, const sal_uInt32*,
+ const Point* const*, const PolyFlags* const*)
+{
+ return false;
+}
+
+bool X11CairoSalGraphicsImpl::drawEPS(tools::Long, tools::Long, tools::Long, tools::Long, void*,
+ sal_uInt32)
+{
+ return false;
+}
+
+bool X11CairoSalGraphicsImpl::blendBitmap(const SalTwoRect&, const SalBitmap&) { return false; }
+
+bool X11CairoSalGraphicsImpl::blendAlphaBitmap(const SalTwoRect&, const SalBitmap&,
+ const SalBitmap&, const SalBitmap&)
+{
+ return false;
+}
+
+bool X11CairoSalGraphicsImpl::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap* pAlphaBitmap, double fAlpha)
+{
+ return mrCairoCommon.drawTransformedBitmap(rNull, rX, rY, rSourceBitmap, pAlphaBitmap, fAlpha,
+ getAntiAlias());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.hxx b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.hxx
new file mode 100644
index 0000000000..23547daa05
--- /dev/null
+++ b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.hxx
@@ -0,0 +1,188 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cairo-xlib.h>
+#include <unx/salgdi.h>
+#include <unx/x11/x11gdiimpl.h>
+#include "cairo_xlib_cairo.hxx"
+
+#include <headless/CairoCommon.hxx>
+
+class X11CairoSalGraphicsImpl : public SalGraphicsImpl, public X11GraphicsImpl
+{
+private:
+ X11SalGraphics& mrParent;
+ CairoCommon& mrCairoCommon;
+
+public:
+ X11CairoSalGraphicsImpl(X11SalGraphics& rParent, CairoCommon& rCairoCommon);
+
+ void Init() override;
+
+ OUString getRenderBackendName() const override { return "gen"; }
+
+ // get the depth of the device
+ sal_uInt16 GetBitCount() const override { return mrParent.GetVisual().GetDepth(); }
+
+ // get the width of the device
+ tools::Long GetGraphicsWidth() const override;
+
+ void ResetClipRegion() override { mrCairoCommon.m_aClipRegion.SetNull(); }
+
+ void setClipRegion(const vcl::Region& i_rClip) override
+ {
+ mrCairoCommon.m_aClipRegion = i_rClip;
+ }
+
+ void SetLineColor() override { mrCairoCommon.m_oLineColor = std::nullopt; }
+
+ void SetLineColor(Color nColor) override { mrCairoCommon.m_oLineColor = nColor; }
+
+ void SetFillColor() override { mrCairoCommon.m_oFillColor = std::nullopt; }
+
+ void SetFillColor(Color nColor) override { mrCairoCommon.m_oFillColor = nColor; }
+
+ void SetXORMode(bool bSet, bool bInvertOnly) override
+ {
+ mrCairoCommon.SetXORMode(bSet, bInvertOnly);
+ }
+
+ void SetROPLineColor(SalROPColor nROPColor) override
+ {
+ mrCairoCommon.SetROPLineColor(nROPColor);
+ }
+
+ void SetROPFillColor(SalROPColor nROPColor) override
+ {
+ mrCairoCommon.SetROPFillColor(nROPColor);
+ }
+
+ void clipRegion(cairo_t* cr) { CairoCommon::clipRegion(cr, mrCairoCommon.m_aClipRegion); }
+
+ void drawPixel(tools::Long nX, tools::Long nY) override;
+ void drawPixel(tools::Long nX, tools::Long nY, Color nColor) override;
+ Color getPixel(tools::Long nX, tools::Long nY) override;
+
+ void drawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2) override;
+
+ void drawRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight) override;
+
+ void drawPolygon(sal_uInt32 nPoints, const Point* pPtAry) override;
+
+ void drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const Point** pPtAry) override;
+
+ void drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency) override;
+
+ void drawPolyLine(sal_uInt32 nPoints, const Point* pPtAry) override;
+
+ bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolygon, double fTransparency, double fLineWidth,
+ const std::vector<double>* pStroke, basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
+
+ /** Render solid rectangle with given transparency
+
+ @param nTransparency
+ Transparency value (0-255) to use. 0 blits and opaque, 255 a
+ fully transparent rectangle
+ */
+ bool drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
+ sal_uInt8 nTransparency) override;
+
+ bool drawGradient(const tools::PolyPolygon& rPolygon, const Gradient& rGradient) override;
+
+ bool implDrawGradient(basegfx::B2DPolyPolygon const& rPolyPolygon,
+ SalGradient const& rGradient) override;
+
+ void invert(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
+ SalInvert nFlags) override;
+
+ void invert(sal_uInt32 nPoints, const Point* pPtAry, SalInvert nFlags) override;
+
+ // CopyArea --> No RasterOp, but ClipRegion
+ void copyArea(tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX, tools::Long nSrcY,
+ tools::Long nSrcWidth, tools::Long nSrcHeight, bool bWindowInvalidate) override;
+
+ // CopyBits and DrawBitmap --> RasterOp and ClipRegion
+ // CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics
+ void copyBits(const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics) override;
+
+ void drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap) override;
+
+ void drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
+ const SalBitmap& rMaskBitmap) override;
+
+ /** Render bitmap with alpha channel
+
+ @param rSourceBitmap
+ Source bitmap to blit
+
+ @param rAlphaBitmap
+ Alpha channel to use for blitting
+
+ @return true, if the operation succeeded, and false
+ otherwise. In this case, clients should try to emulate alpha
+ compositing themselves
+ */
+ bool drawAlphaBitmap(const SalTwoRect&, const SalBitmap& rSourceBitmap,
+ const SalBitmap& rAlphaBitmap) override;
+
+ void drawMask(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
+ Color nMaskColor) override;
+
+ std::shared_ptr<SalBitmap> getBitmap(tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::Long nHeight) override;
+
+ bool drawPolyLineBezier(sal_uInt32 nPoints, const Point* pPtAry,
+ const PolyFlags* pFlgAry) override;
+
+ bool drawPolygonBezier(sal_uInt32 nPoints, const Point* pPtAry,
+ const PolyFlags* pFlgAry) override;
+
+ bool drawPolyPolygonBezier(sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const Point* const* pPtAry,
+ const PolyFlags* const* pFlgAry) override;
+
+ bool drawEPS(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
+ void* pPtr, sal_uInt32 nSize) override;
+
+ bool hasFastDrawTransformedBitmap() const override;
+
+ /** draw transformed bitmap (maybe with alpha) where Null, X, Y define the coordinate system */
+ bool drawTransformedBitmap(const basegfx::B2DPoint& rNull, const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY, const SalBitmap& rSourceBitmap,
+ const SalBitmap* pAlphaBitmap, double fAlpha) override;
+
+ /** Blend bitmap with color channels */
+ bool blendBitmap(const SalTwoRect&, const SalBitmap& rBitmap) override;
+
+ /** Render bitmap by blending using the mask and alpha channel */
+ bool blendAlphaBitmap(const SalTwoRect&, const SalBitmap& rSrcBitmap,
+ const SalBitmap& rMaskBitmap, const SalBitmap& rAlphaBitmap) override;
+
+ bool supportsOperation(OutDevSupportType eType) const override;
+
+ void freeResources() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/cairo_xlib_cairo.cxx b/vcl/unx/generic/gdi/cairo_xlib_cairo.cxx
new file mode 100644
index 0000000000..87758f24d9
--- /dev/null
+++ b/vcl/unx/generic/gdi/cairo_xlib_cairo.cxx
@@ -0,0 +1,277 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+#include "cairo_xlib_cairo.hxx"
+
+#include <utility>
+#include <vcl/sysdata.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+
+#include <cairo-xlib.h>
+#include <cairo-xlib-xrender.h>
+
+namespace
+{
+ Pixmap limitXCreatePixmap(Display *display, Drawable d, unsigned int width, unsigned int height, unsigned int depth)
+ {
+ // The X protocol request CreatePixmap puts an upper bound
+ // of 16 bit to the size. And in practice some drivers
+ // fall over with values close to the max.
+
+ // see, e.g. moz#424333, fdo#48961, rhbz#1086714
+ // we've a duplicate of this in vcl :-(
+ if (width > SAL_MAX_INT16-10 || height > SAL_MAX_INT16-10)
+ {
+ SAL_WARN("canvas", "overlarge pixmap: " << width << " x " << height);
+ return None;
+ }
+ return XCreatePixmap(display, d, width, height, depth);
+ }
+}
+
+namespace cairo
+{
+
+ X11SysData::X11SysData() :
+ pDisplay(nullptr),
+ hDrawable(0),
+ pVisual(nullptr),
+ nScreen(0)
+ {}
+
+ X11SysData::X11SysData( const SystemGraphicsData& pSysDat ) :
+ pDisplay(static_cast<_XDisplay*>(pSysDat.pDisplay)),
+ hDrawable(pSysDat.hDrawable),
+ pVisual(static_cast<Visual*>(pSysDat.pVisual)),
+ nScreen(pSysDat.nScreen)
+ {}
+
+ X11SysData::X11SysData( const SystemEnvData& pSysDat, const SalFrame* pReference ) :
+ pDisplay(static_cast<_XDisplay*>(pSysDat.pDisplay)),
+ hDrawable(pSysDat.GetWindowHandle(pReference)),
+ pVisual(static_cast<Visual*>(pSysDat.pVisual)),
+ nScreen(pSysDat.nScreen)
+ {}
+
+ X11Pixmap::~X11Pixmap()
+ {
+ if( mpDisplay && mhDrawable )
+ XFreePixmap( mpDisplay, mhDrawable );
+ }
+
+ /**
+ * Surface::Surface: Create Canvas surface with existing data
+ * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
+ * @param pSurface Cairo surface
+ *
+ * pSysData contains the platform native Drawable reference
+ * This constructor only stores data, it does no processing.
+ * It is used by e.g. Surface::getSimilar()
+ *
+ * Set the mpSurface as pSurface
+ **/
+ X11Surface::X11Surface( const X11SysData& rSysData,
+ X11PixmapSharedPtr rPixmap,
+ CairoSurfaceSharedPtr pSurface ) :
+ maSysData(rSysData),
+ mpPixmap(std::move(rPixmap)),
+ mpSurface(std::move(pSurface))
+ {}
+
+ /**
+ * Surface::Surface: Create generic Canvas surface using given Cairo Surface
+ *
+ * @param pSurface Cairo Surface
+ *
+ * This constructor only stores data, it does no processing.
+ * It is used with e.g. cairo_image_surface_create_for_data()
+ * Unlike other constructors, mpSysData is set to NULL
+ *
+ * Set the mpSurface as pSurface
+ **/
+ X11Surface::X11Surface( CairoSurfaceSharedPtr pSurface ) :
+ maSysData(),
+ mpSurface(std::move(pSurface))
+ {}
+
+ /**
+ * Surface::Surface: Create Canvas surface from Window reference.
+ * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
+ * @param x horizontal location of the new surface
+ * @param y vertical location of the new surface
+ * @param width width of the new surface
+ * @param height height of the new surface
+ *
+ * pSysData contains the platform native Window reference.
+ *
+ * pSysData is used to create a surface on the Window
+ *
+ * Set the mpSurface to the new surface or NULL
+ **/
+ X11Surface::X11Surface( const X11SysData& rSysData, int x, int y, int width, int height ) :
+ maSysData(rSysData),
+ mpSurface(
+ cairo_xlib_surface_create( rSysData.pDisplay,
+ rSysData.hDrawable,
+ rSysData.pVisual,
+ width + x, height + y ),
+ &cairo_surface_destroy)
+ {
+ cairo_surface_set_device_offset(mpSurface.get(), x, y );
+ }
+
+ /**
+ * Surface::Surface: Create platform native Canvas surface from BitmapSystemData
+ * @param pSysData Platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
+ * @param pBmpData Platform native image data (struct BitmapSystemData in vcl/inc/bitmap.hxx)
+ * @param width width of the new surface
+ * @param height height of the new surface
+ *
+ * The pBmpData provides the imagedata that the created surface should contain.
+ *
+ * Set the mpSurface to the new surface or NULL
+ **/
+ X11Surface::X11Surface( const X11SysData& rSysData,
+ const BitmapSystemData& rData ) :
+ maSysData( rSysData ),
+ mpSurface(
+ cairo_xlib_surface_create( rSysData.pDisplay,
+ reinterpret_cast<Drawable>(rData.aPixmap),
+ rSysData.pVisual,
+ rData.mnWidth, rData.mnHeight ),
+ &cairo_surface_destroy)
+ {
+ }
+
+ /**
+ * Surface::getCairo: Create Cairo (drawing object) for the Canvas surface
+ *
+ * @return new Cairo or NULL
+ **/
+ CairoSharedPtr X11Surface::getCairo() const
+ {
+ return CairoSharedPtr( cairo_create(mpSurface.get()),
+ &cairo_destroy );
+ }
+
+ /**
+ * Surface::getSimilar: Create new similar Canvas surface
+ * @param cairo_content_type format of the new surface (cairo_content_t from cairo/src/cairo.h)
+ * @param width width of the new surface
+ * @param height height of the new surface
+ *
+ * Creates a new Canvas surface. This normally creates platform native surface, even though
+ * generic function is used.
+ *
+ * Cairo surface from cairo_content_type (cairo_content_t)
+ *
+ * @return new surface or NULL
+ **/
+ SurfaceSharedPtr X11Surface::getSimilar(int cairo_content_type, int width, int height ) const
+ {
+ if( maSysData.pDisplay && maSysData.hDrawable )
+ {
+ XRenderPictFormat* pFormat;
+ int nFormat;
+
+ switch (cairo_content_type)
+ {
+ case CAIRO_CONTENT_ALPHA:
+ nFormat = PictStandardA8;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ nFormat = PictStandardRGB24;
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ default:
+ nFormat = PictStandardARGB32;
+ break;
+ }
+
+ pFormat = XRenderFindStandardFormat( maSysData.pDisplay, nFormat );
+ Pixmap hPixmap = limitXCreatePixmap( maSysData.pDisplay, maSysData.hDrawable,
+ width > 0 ? width : 1, height > 0 ? height : 1,
+ pFormat->depth );
+
+ return SurfaceSharedPtr(
+ new X11Surface( maSysData,
+ std::make_shared<X11Pixmap>(hPixmap, maSysData.pDisplay),
+ CairoSurfaceSharedPtr(
+ cairo_xlib_surface_create_with_xrender_format(
+ maSysData.pDisplay,
+ hPixmap,
+ ScreenOfDisplay(maSysData.pDisplay, maSysData.nScreen),
+ pFormat, width, height ),
+ &cairo_surface_destroy) ));
+ }
+ else
+ return SurfaceSharedPtr(
+ new X11Surface( maSysData,
+ X11PixmapSharedPtr(),
+ CairoSurfaceSharedPtr(
+ cairo_surface_create_similar( mpSurface.get(),
+ static_cast<cairo_content_t>(cairo_content_type), width, height ),
+ &cairo_surface_destroy )));
+ }
+
+ VclPtr<VirtualDevice> X11Surface::createVirtualDevice() const
+ {
+ SystemGraphicsData aSystemGraphicsData;
+
+ cairo_surface_t* pSurface = mpSurface.get();
+
+ aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
+ aSystemGraphicsData.hDrawable = mpPixmap ? mpPixmap->mhDrawable : maSysData.hDrawable;
+ aSystemGraphicsData.pSurface = pSurface;
+
+ int width = cairo_xlib_surface_get_width(pSurface);
+ int height = cairo_xlib_surface_get_height(pSurface);
+
+ return VclPtr<VirtualDevice>::Create(aSystemGraphicsData,
+ Size(width, height),
+ DeviceFormat::WITHOUT_ALPHA);
+ }
+
+ /**
+ * Surface::Resize: Resizes the Canvas surface.
+ * @param width new width of the surface
+ * @param height new height of the surface
+ *
+ * Only used on X11.
+ *
+ * @return The new surface or NULL
+ **/
+ bool X11Surface::Resize(int width, int height)
+ {
+ cairo_xlib_surface_set_size(mpSurface.get(), width, height);
+ return true;
+ }
+
+ void X11Surface::flush() const
+ {
+ XSync( maSysData.pDisplay, false );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/cairo_xlib_cairo.hxx b/vcl/unx/generic/gdi/cairo_xlib_cairo.hxx
new file mode 100644
index 0000000000..f0b47a3744
--- /dev/null
+++ b/vcl/unx/generic/gdi/cairo_xlib_cairo.hxx
@@ -0,0 +1,93 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <vcl/cairo.hxx>
+#include <vcl/salgtype.hxx>
+
+struct BitmapSystemData;
+class SalFrame;
+struct SystemEnvData;
+struct SystemGraphicsData;
+
+namespace cairo {
+
+ /// Holds all X11-output relevant data
+ struct X11SysData
+ {
+ X11SysData();
+ explicit X11SysData( const SystemGraphicsData& );
+ explicit X11SysData( const SystemEnvData&, const SalFrame* pReference );
+
+ _XDisplay* pDisplay; // the relevant display connection
+ Drawable hDrawable; // a drawable
+ Visual* pVisual; // the visual in use
+ int nScreen; // the current screen of the drawable
+ };
+
+ /// RAII wrapper for a pixmap
+ struct X11Pixmap
+ {
+ _XDisplay* mpDisplay; // the relevant display connection
+ Pixmap mhDrawable; // a drawable
+
+ X11Pixmap( Pixmap hDrawable, _XDisplay* pDisplay ) :
+ mpDisplay(pDisplay),
+ mhDrawable(hDrawable)
+ {}
+
+ ~X11Pixmap();
+ };
+
+ typedef std::shared_ptr<X11Pixmap> X11PixmapSharedPtr;
+
+ class X11Surface : public Surface
+ {
+ const X11SysData maSysData;
+ X11PixmapSharedPtr mpPixmap;
+ CairoSurfaceSharedPtr mpSurface;
+
+ X11Surface( const X11SysData& rSysData, X11PixmapSharedPtr aPixmap, CairoSurfaceSharedPtr pSurface );
+
+ public:
+ /// takes over ownership of passed cairo_surface
+ explicit X11Surface( CairoSurfaceSharedPtr pSurface );
+ /// create surface on subarea of given drawable
+ X11Surface( const X11SysData& rSysData, int x, int y, int width, int height );
+ /// create surface for given bitmap data
+ X11Surface( const X11SysData& rSysData, const BitmapSystemData& rBmpData );
+
+ // Surface interface
+ virtual CairoSharedPtr getCairo() const override;
+ virtual CairoSurfaceSharedPtr getCairoSurface() const override { return mpSurface; }
+ virtual SurfaceSharedPtr getSimilar(int cairo_content_type, int width, int height) const override;
+
+ virtual VclPtr<VirtualDevice> createVirtualDevice() const override;
+
+ virtual bool Resize( int width, int height ) override;
+
+ virtual void flush() const override;
+
+ const X11PixmapSharedPtr& getPixmap() const { return mpPixmap; }
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx
new file mode 100644
index 0000000000..7e7ce9ca70
--- /dev/null
+++ b/vcl/unx/generic/gdi/cairotextrender.cxx
@@ -0,0 +1,526 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/scopeguard.hxx>
+#include <unx/cairotextrender.hxx>
+#include <unx/fc_fontoptions.hxx>
+#include <unx/freetype_glyphcache.hxx>
+#include <unx/gendata.hxx>
+#include <headless/CairoCommon.hxx>
+#include <vcl/svapp.hxx>
+#include <sallayout.hxx>
+#include <salinst.hxx>
+
+#include <cairo.h>
+#include <cairo-ft.h>
+#if defined(CAIRO_HAS_SVG_SURFACE)
+#include <cairo-svg.h>
+#elif defined(CAIRO_HAS_PDF_SURFACE)
+#include <cairo-pdf.h>
+#endif
+
+#include <deque>
+
+namespace {
+
+typedef struct FT_FaceRec_* FT_Face;
+
+class CairoFontsCache
+{
+public:
+ struct CacheId
+ {
+ FT_Face maFace;
+ const FontConfigFontOptions *mpOptions;
+ bool mbEmbolden;
+ bool mbVerticalMetrics;
+ bool operator ==(const CacheId& rOther) const
+ {
+ return maFace == rOther.maFace &&
+ mpOptions == rOther.mpOptions &&
+ mbEmbolden == rOther.mbEmbolden &&
+ mbVerticalMetrics == rOther.mbVerticalMetrics;
+ }
+ };
+
+private:
+ typedef std::deque< std::pair<cairo_font_face_t*, CacheId> > LRUFonts;
+ static LRUFonts maLRUFonts;
+public:
+ CairoFontsCache() = delete;
+
+ static void CacheFont(cairo_font_face_t* pFont, const CacheId &rId);
+ static cairo_font_face_t* FindCachedFont(const CacheId &rId);
+};
+
+CairoFontsCache::LRUFonts CairoFontsCache::maLRUFonts;
+
+void CairoFontsCache::CacheFont(cairo_font_face_t* pFont, const CairoFontsCache::CacheId &rId)
+{
+ maLRUFonts.push_front( std::pair<cairo_font_face_t*, CairoFontsCache::CacheId>(pFont, rId) );
+ if (maLRUFonts.size() > 8)
+ {
+ cairo_font_face_destroy(maLRUFonts.back().first);
+ maLRUFonts.pop_back();
+ }
+}
+
+cairo_font_face_t* CairoFontsCache::FindCachedFont(const CairoFontsCache::CacheId &rId)
+{
+ auto aI = std::find_if(maLRUFonts.begin(), maLRUFonts.end(),
+ [&rId](const LRUFonts::value_type& rFont) { return rFont.second == rId; });
+ if (aI != maLRUFonts.end())
+ return aI->first;
+ return nullptr;
+}
+
+}
+
+namespace
+{
+ bool hasRotation(int nRotation)
+ {
+ return nRotation != 0;
+ }
+
+ double toRadian(Degree10 nDegree10th)
+ {
+ return toRadians(3600_deg10 - nDegree10th);
+ }
+
+ cairo_t* syncCairoContext(cairo_t* cr)
+ {
+ //rhbz#1283420 tdf#117413 bodge to force a read from the underlying surface which has
+ //the side effect of making the mysterious xrender related problem go away
+ cairo_surface_t *target = cairo_get_target(cr);
+ if (cairo_surface_get_type(target) == CAIRO_SURFACE_TYPE_XLIB)
+ {
+ cairo_surface_t *throw_away = cairo_surface_create_similar(target, cairo_surface_get_content(target), 1, 1);
+ cairo_t *force_read_cr = cairo_create(throw_away);
+ cairo_set_source_surface(force_read_cr, target, 0, 0);
+ cairo_paint(force_read_cr);
+ cairo_destroy(force_read_cr);
+ cairo_surface_destroy(throw_away);
+ }
+ return cr;
+ }
+}
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+extern "C"
+{
+ __attribute__((weak)) void __lsan_disable();
+ __attribute__((weak)) void __lsan_enable();
+}
+#endif
+
+namespace {
+ struct CairoFontOptions
+ {
+ // https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/235
+ // I don't want to have CAIRO_ROUND_GLYPH_POS_ON set in the cairo
+ // surfaces font_options, but that's private, so tricky to achieve
+ cairo_font_options_t* mpRoundGlyphPosOffOptions;
+
+ CairoFontOptions()
+ {
+ // https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/235
+ // I don't want to have CAIRO_ROUND_GLYPH_POS_ON set in the cairo surfaces
+ // font_options when trying subpixel rendering, but that's a private
+ // feature of cairo_font_options_t, so tricky to achieve. Hack this by
+ // getting the font options of a backend known to set this private feature
+ // to CAIRO_ROUND_GLYPH_POS_OFF and then set to defaults the public
+ // features and the result can be merged with new font options to set
+ // CAIRO_ROUND_GLYPH_POS_OFF in those
+ mpRoundGlyphPosOffOptions = cairo_font_options_create();
+#if defined(CAIRO_HAS_SVG_SURFACE)
+ // svg, pdf and ps backends have CAIRO_ROUND_GLYPH_POS_OFF by default
+ cairo_surface_t* hack = cairo_svg_surface_create(nullptr, 1, 1);
+#elif defined(CAIRO_HAS_PDF_SURFACE)
+ cairo_surface_t* hack = cairo_pdf_surface_create(nullptr, 1, 1);
+#endif
+ cairo_surface_get_font_options(hack, mpRoundGlyphPosOffOptions);
+ cairo_surface_destroy(hack);
+ cairo_font_options_set_antialias(mpRoundGlyphPosOffOptions, CAIRO_ANTIALIAS_DEFAULT);
+ cairo_font_options_set_subpixel_order(mpRoundGlyphPosOffOptions, CAIRO_SUBPIXEL_ORDER_DEFAULT);
+ cairo_font_options_set_hint_style(mpRoundGlyphPosOffOptions, CAIRO_HINT_STYLE_DEFAULT);
+ cairo_font_options_set_hint_metrics(mpRoundGlyphPosOffOptions, CAIRO_HINT_METRICS_DEFAULT);
+ }
+ ~CairoFontOptions()
+ {
+ cairo_font_options_destroy(mpRoundGlyphPosOffOptions);
+ }
+ static const cairo_font_options_t *get()
+ {
+ static CairoFontOptions opts;
+ return opts.mpRoundGlyphPosOffOptions;
+ }
+ };
+}
+
+CairoTextRender::CairoTextRender(CairoCommon& rCairoCommon)
+ : mrCairoCommon(rCairoCommon)
+{
+}
+
+CairoTextRender::~CairoTextRender()
+{
+}
+
+static void ApplyFont(cairo_t* cr, const CairoFontsCache::CacheId& rId, double nWidth, double nHeight, int nGlyphRotation,
+ const GenericSalLayout& rLayout)
+{
+ cairo_font_face_t* font_face = CairoFontsCache::FindCachedFont(rId);
+ if (!font_face)
+ {
+ const FontConfigFontOptions *pOptions = rId.mpOptions;
+ FcPattern *pPattern = pOptions->GetPattern();
+ font_face = cairo_ft_font_face_create_for_pattern(pPattern);
+ CairoFontsCache::CacheFont(font_face, rId);
+ }
+ cairo_set_font_face(cr, font_face);
+
+ cairo_set_font_size(cr, nHeight);
+
+ cairo_matrix_t m;
+ cairo_matrix_init_identity(&m);
+
+ if (rLayout.GetOrientation())
+ cairo_matrix_rotate(&m, toRadian(rLayout.GetOrientation()));
+
+ cairo_matrix_scale(&m, nWidth, nHeight);
+
+ if (nGlyphRotation)
+ cairo_matrix_rotate(&m, toRadian(Degree10(nGlyphRotation * 900)));
+
+ const LogicalFontInstance& rInstance = rLayout.GetFont();
+ if (rInstance.NeedsArtificialItalic())
+ {
+ cairo_matrix_t shear;
+ cairo_matrix_init_identity(&shear);
+ shear.xy = -shear.xx * ARTIFICIAL_ITALIC_SKEW;
+ cairo_matrix_multiply(&m, &shear, &m);
+ }
+
+ cairo_set_font_matrix(cr, &m);
+}
+
+static CairoFontsCache::CacheId makeCacheId(const GenericSalLayout& rLayout)
+{
+ const FreetypeFontInstance& rInstance = static_cast<FreetypeFontInstance&>(rLayout.GetFont());
+ const FreetypeFont& rFont = rInstance.GetFreetypeFont();
+
+ FT_Face aFace = rFont.GetFtFace();
+ CairoFontsCache::CacheId aId;
+ aId.maFace = aFace;
+ aId.mpOptions = rFont.GetFontOptions();
+ aId.mbEmbolden = rInstance.NeedsArtificialBold();
+ aId.mbVerticalMetrics = false;
+
+ return aId;
+}
+
+void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalGraphics& rGraphics)
+{
+ const LogicalFontInstance& rInstance = rLayout.GetFont();
+
+ const bool bSubpixelPositioning = rLayout.GetSubpixelPositioning();
+
+ /*
+ * It might be ideal to cache surface and cairo context between calls and
+ * only destroy it when the drawable changes, but to do that we need to at
+ * least change the SalFrame etc impls to dtor the SalGraphics *before* the
+ * destruction of the windows they reference
+ */
+ cairo_t *cr = syncCairoContext(getCairoContext());
+ if (!cr)
+ {
+ SAL_WARN("vcl", "no cairo context for text");
+ return;
+ }
+ comphelper::ScopeGuard releaseContext([this, cr]() { releaseCairoContext(cr); });
+
+ std::vector<cairo_glyph_t> cairo_glyphs;
+ std::vector<int> glyph_extrarotation;
+ cairo_glyphs.reserve( 256 );
+
+ double nSnapToSubPixelDiff = 0.0;
+ double nXScale, nYScale;
+ dl_cairo_surface_get_device_scale(cairo_get_target(cr), &nXScale, &nYScale);
+
+ basegfx::B2DPoint aPos;
+ const GlyphItem* pGlyph;
+ const GlyphItem* pPrevGlyph = nullptr;
+ int nStart = 0;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ cairo_glyph_t aGlyph;
+ aGlyph.index = pGlyph->glyphId();
+ aGlyph.x = aPos.getX();
+ aGlyph.y = aPos.getY();
+
+ const bool bVertical = pGlyph->IsVertical();
+ glyph_extrarotation.push_back(bVertical ? 1 : 0);
+
+ if (bSubpixelPositioning)
+ {
+ // tdf#150507 like skia, even when subpixel rendering pixel, snap y
+ if (!bVertical)
+ aGlyph.y = std::floor(aGlyph.y + 0.5);
+ else
+ aGlyph.x = std::floor(aGlyph.x + 0.5);
+
+ // tdf#152094 snap to 1/4 of a pixel after a run of whitespace,
+ // probably a little dubious, but maybe worth a shot for lodpi
+ double& rGlyphDimension = !bVertical ? aGlyph.x : aGlyph.y;
+ const int nSubPixels = 4 * (!bVertical ? nXScale : nYScale);
+ if (pGlyph->IsSpacing())
+ nSnapToSubPixelDiff = 0;
+ else if (pPrevGlyph && pPrevGlyph->IsSpacing())
+ {
+ double nSnapToSubPixel = std::floor(rGlyphDimension * nSubPixels) / nSubPixels;
+ nSnapToSubPixelDiff = rGlyphDimension - nSnapToSubPixel;
+ rGlyphDimension = nSnapToSubPixel;
+ }
+ else
+ rGlyphDimension -= nSnapToSubPixelDiff;
+
+ pPrevGlyph = pGlyph;
+ }
+
+ cairo_glyphs.push_back(aGlyph);
+ }
+
+ const size_t nGlyphs = cairo_glyphs.size();
+ if (!nGlyphs)
+ return;
+
+ const vcl::font::FontSelectPattern& rFSD = rInstance.GetFontSelectPattern();
+ double nHeight = rFSD.mnHeight;
+ double nWidth = rFSD.mnWidth ? rFSD.mnWidth : nHeight;
+ if (nWidth == 0 || nHeight == 0)
+ return;
+
+ if (nHeight > SAL_MAX_UINT16)
+ {
+ // as seen with freetype 2.11.0, so cairo surface status is "fail"
+ // ("error occurred in libfreetype") and no further operations are
+ // executed, so this error then leads to later leaks
+ SAL_WARN("vcl", "rendering text would fail with height: " << nHeight);
+ return;
+ }
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ if (nHeight > 8000)
+ {
+ SAL_WARN("vcl", "rendering text would use > 2G Memory: " << nHeight);
+ return;
+ }
+
+ if (nWidth > 2000)
+ {
+ SAL_WARN("vcl", "rendering text would use > 2G Memory: " << nWidth);
+ return;
+ }
+#endif
+
+ clipRegion(cr);
+
+ cairo_set_source_rgba(cr,
+ mnTextColor.GetRed()/255.0,
+ mnTextColor.GetGreen()/255.0,
+ mnTextColor.GetBlue()/255.0,
+ mnTextColor.GetAlpha()/255.0);
+
+ int nRatio = nWidth * 10 / nHeight;
+
+ // tdf#132112 excessive stretch of underbrace and overbrace can trigger freetype into an error, which propagates to cairo
+ // and once a cairo surface is in an error state, that cannot be cleared and all subsequent drawing fails, so bodge that
+ // with a high degree of stretch we draw the brace without stretch to a temp surface and stretch that to give a far
+ // poorer visual result, but one that can be rendered.
+ if (nGlyphs == 1 && nRatio > 100 && (cairo_glyphs[0].index == 974 || cairo_glyphs[0].index == 975) &&
+ rFSD.maTargetName == "OpenSymbol" && !glyph_extrarotation.back() && !rLayout.GetOrientation())
+ {
+ CairoFontsCache::CacheId aId = makeCacheId(rLayout);
+
+ ApplyFont(cr, aId, nWidth, nHeight, 0, rLayout);
+ cairo_text_extents_t stretched_extents;
+ cairo_glyph_extents(cr, cairo_glyphs.data(), nGlyphs, &stretched_extents);
+
+ ApplyFont(cr, aId, nHeight, nHeight, 0, rLayout);
+ cairo_text_extents_t unstretched_extents;
+ cairo_glyph_extents(cr, cairo_glyphs.data(), nGlyphs, &unstretched_extents);
+
+ cairo_surface_t *target = cairo_get_target(cr);
+ cairo_surface_t *temp_surface = cairo_surface_create_similar(target, cairo_surface_get_content(target),
+ unstretched_extents.width, unstretched_extents.height);
+ cairo_t *temp_cr = cairo_create(temp_surface);
+ cairo_glyph_t glyph;
+ glyph.x = -unstretched_extents.x_bearing;
+ glyph.y = -unstretched_extents.y_bearing;
+ glyph.index = cairo_glyphs[0].index;
+
+ ApplyFont(temp_cr, aId, nHeight, nHeight, 0, rLayout);
+
+ cairo_set_source_rgb(temp_cr,
+ mnTextColor.GetRed()/255.0,
+ mnTextColor.GetGreen()/255.0,
+ mnTextColor.GetBlue()/255.0);
+
+ cairo_show_glyphs(temp_cr, &glyph, 1);
+ cairo_destroy(temp_cr);
+
+ cairo_set_source_surface(cr, temp_surface, cairo_glyphs[0].x, cairo_glyphs[0].y + stretched_extents.y_bearing);
+
+ cairo_pattern_t* sourcepattern = cairo_get_source(cr);
+ cairo_matrix_t matrix;
+ cairo_pattern_get_matrix(sourcepattern, &matrix);
+ cairo_matrix_scale(&matrix, unstretched_extents.width / stretched_extents.width, 1);
+ cairo_pattern_set_matrix(sourcepattern, &matrix);
+
+ cairo_rectangle(cr, cairo_glyphs[0].x, cairo_glyphs[0].y + stretched_extents.y_bearing, stretched_extents.width, stretched_extents.height);
+ cairo_fill(cr);
+
+ cairo_surface_destroy(temp_surface);
+
+ return;
+ }
+
+ if (nRatio >= 5120)
+ {
+ // as seen with freetype 2.12.1, so cairo surface status is "fail"
+ SAL_WARN("vcl", "rendering text would fail with stretch of: " << nRatio / 10.0);
+ return;
+ }
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ if (__lsan_disable)
+ __lsan_disable();
+#endif
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const bool bDisableAA = !rStyleSettings.GetUseFontAAFromSystem() && !rGraphics.getAntiAlias();
+ static bool bAllowDefaultHinting = getenv("SAL_ALLOW_DEFAULT_HINTING") != nullptr;
+
+ const cairo_font_options_t* pFontOptions = GetSalInstance()->GetCairoFontOptions();
+ if (pFontOptions || bDisableAA || bSubpixelPositioning)
+ {
+ cairo_hint_style_t eHintStyle = pFontOptions ? cairo_font_options_get_hint_style(pFontOptions) : CAIRO_HINT_STYLE_DEFAULT;
+ bool bAllowedHintStyle = !bSubpixelPositioning || bAllowDefaultHinting || (eHintStyle == CAIRO_HINT_STYLE_NONE || eHintStyle == CAIRO_HINT_STYLE_SLIGHT);
+
+ if (bDisableAA || !bAllowedHintStyle || bSubpixelPositioning)
+ {
+ // Disable font AA in case global AA setting is supposed to affect
+ // font rendering (not the default) and AA is disabled.
+ cairo_font_options_t* pOptions = pFontOptions ? cairo_font_options_copy(pFontOptions) : cairo_font_options_create();
+
+ if (bDisableAA)
+ cairo_font_options_set_antialias(pOptions, CAIRO_ANTIALIAS_NONE);
+ if (!bAllowedHintStyle)
+ cairo_font_options_set_hint_style(pOptions, CAIRO_HINT_STYLE_SLIGHT);
+ if (bSubpixelPositioning)
+ {
+ // Disable private CAIRO_ROUND_GLYPH_POS_ON by merging with
+ // font options known to have CAIRO_ROUND_GLYPH_POS_OFF
+ cairo_font_options_merge(pOptions, CairoFontOptions::get());
+
+ // a) tdf#153699 skip this with cairo 1.17.8 as it has a problem
+ // See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/643
+ // b) tdf#152675 a similar report for cairo: 1.16.0-4ubuntu1,
+ // assume that everything <= 1.17.8 is unsafe to disable this
+ if (cairo_version() > CAIRO_VERSION_ENCODE(1, 17, 8))
+ cairo_font_options_set_hint_metrics(pOptions, CAIRO_HINT_METRICS_OFF);
+ }
+ cairo_set_font_options(cr, pOptions);
+ cairo_font_options_destroy(pOptions);
+ }
+ else if (pFontOptions)
+ cairo_set_font_options(cr, pFontOptions);
+ }
+
+ CairoFontsCache::CacheId aId = makeCacheId(rLayout);
+
+ std::vector<int>::const_iterator aEnd = glyph_extrarotation.end();
+ std::vector<int>::const_iterator aStart = glyph_extrarotation.begin();
+ std::vector<int>::const_iterator aI = aStart;
+ while (aI != aEnd)
+ {
+ int nGlyphRotation = *aI;
+
+ std::vector<int>::const_iterator aNext = nGlyphRotation?(aI+1):std::find_if(aI+1, aEnd, hasRotation);
+
+ size_t nStartIndex = std::distance(aStart, aI);
+ size_t nLen = std::distance(aI, aNext);
+
+ aId.mbVerticalMetrics = nGlyphRotation != 0.0;
+
+ ApplyFont(cr, aId, nWidth, nHeight, nGlyphRotation, rLayout);
+
+ cairo_show_glyphs(cr, &cairo_glyphs[nStartIndex], nLen);
+ if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
+ {
+ SAL_WARN("vcl", "rendering text failed with stretch ratio of: " << nRatio << ", " << cairo_status_to_string(cairo_status(cr)));
+ }
+
+#if OSL_DEBUG_LEVEL > 2
+ //draw origin
+ cairo_save (cr);
+ cairo_rectangle (cr, cairo_glyphs[nStartIndex].x, cairo_glyphs[nStartIndex].y, 5, 5);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.80);
+ cairo_fill (cr);
+ cairo_restore (cr);
+#endif
+
+ aI = aNext;
+ }
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ if (__lsan_enable)
+ __lsan_enable();
+#endif
+}
+
+cairo_t* CairoTextRender::getCairoContext()
+{
+ // Note that cairo_set_antialias (bAntiAlias property) doesn't affect cairo
+ // text rendering. That's affected by cairo_font_options_set_antialias instead.
+ return mrCairoCommon.getCairoContext(/*bXorModeAllowed*/false, /*bAntiAlias*/true);
+}
+
+void CairoTextRender::clipRegion(cairo_t* cr)
+{
+ mrCairoCommon.clipRegion(cr);
+}
+
+void CairoTextRender::releaseCairoContext(cairo_t* cr)
+{
+ mrCairoCommon.releaseCairoContext(cr, /*bXorModeAllowed*/false, basegfx::B2DRange());
+}
+
+void FontConfigFontOptions::cairo_font_options_substitute(FcPattern* pPattern)
+{
+ const cairo_font_options_t* pFontOptions = GetSalInstance()->GetCairoFontOptions();
+ if( !pFontOptions )
+ return;
+ cairo_ft_font_options_substitute(pFontOptions, pPattern);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/font.cxx b/vcl/unx/generic/gdi/font.cxx
new file mode 100644
index 0000000000..19887a9af2
--- /dev/null
+++ b/vcl/unx/generic/gdi/font.cxx
@@ -0,0 +1,84 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <vcl/sysdata.hxx>
+#include <vcl/fontcharmap.hxx>
+
+#include <unx/salgdi.h>
+#include <textrender.hxx>
+#include <sallayout.hxx>
+
+void X11SalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ mxTextRenderImpl->DrawTextLayout(rLayout, *this);
+}
+
+FontCharMapRef X11SalGraphics::GetFontCharMap() const
+{
+ return mxTextRenderImpl->GetFontCharMap();
+}
+
+bool X11SalGraphics::GetFontCapabilities(vcl::FontCapabilities &rGetImplFontCapabilities) const
+{
+ return mxTextRenderImpl->GetFontCapabilities(rGetImplFontCapabilities);
+}
+
+// SalGraphics
+void X11SalGraphics::SetFont(LogicalFontInstance* pEntry, int nFallbackLevel)
+{
+ mxTextRenderImpl->SetFont(pEntry, nFallbackLevel);
+}
+
+void
+X11SalGraphics::SetTextColor( Color nColor )
+{
+ mxTextRenderImpl->SetTextColor(nColor);
+}
+
+bool X11SalGraphics::AddTempDevFont( vcl::font::PhysicalFontCollection* pFontCollection,
+ const OUString& rFileURL,
+ const OUString& rFontName )
+{
+ return mxTextRenderImpl->AddTempDevFont(pFontCollection, rFileURL, rFontName);
+}
+
+void X11SalGraphics::ClearDevFontCache()
+{
+ mxTextRenderImpl->ClearDevFontCache();
+}
+
+void X11SalGraphics::GetDevFontList( vcl::font::PhysicalFontCollection* pFontCollection )
+{
+ mxTextRenderImpl->GetDevFontList(pFontCollection);
+}
+
+void
+X11SalGraphics::GetFontMetric( FontMetricDataRef &rxFontMetric, int nFallbackLevel )
+{
+ mxTextRenderImpl->GetFontMetric(rxFontMetric, nFallbackLevel);
+}
+
+std::unique_ptr<GenericSalLayout> X11SalGraphics::GetTextLayout(int nFallbackLevel)
+{
+ return mxTextRenderImpl->GetTextLayout(nFallbackLevel);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/freetypetextrender.cxx b/vcl/unx/generic/gdi/freetypetextrender.cxx
new file mode 100644
index 0000000000..b524a45ee7
--- /dev/null
+++ b/vcl/unx/generic/gdi/freetypetextrender.cxx
@@ -0,0 +1,185 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <unx/freetypetextrender.hxx>
+
+#include <unotools/configmgr.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <sal/log.hxx>
+
+#include <unx/fontmanager.hxx>
+#include <unx/geninst.h>
+#include <unx/glyphcache.hxx>
+#include <unx/fc_fontoptions.hxx>
+#include <unx/freetype_glyphcache.hxx>
+#include <font/PhysicalFontFace.hxx>
+#include <font/FontMetricData.hxx>
+
+#include <sallayout.hxx>
+
+FreeTypeTextRenderImpl::FreeTypeTextRenderImpl()
+ : mnTextColor(Color(0x00, 0x00, 0x00)) //black
+{
+}
+
+FreeTypeTextRenderImpl::~FreeTypeTextRenderImpl()
+{
+ ReleaseFonts();
+}
+
+void FreeTypeTextRenderImpl::SetFont(LogicalFontInstance *pEntry, int nFallbackLevel)
+{
+ // release all no longer needed font resources
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ {
+ // old server side font is no longer referenced
+ mpFreetypeFont[i] = nullptr;
+ }
+
+ // return early if there is no new font
+ if( !pEntry )
+ return;
+
+ FreetypeFontInstance* pFreetypeFont = static_cast<FreetypeFontInstance*>(pEntry);
+ mpFreetypeFont[ nFallbackLevel ] = pFreetypeFont;
+
+ // ignore fonts with e.g. corrupted font files
+ if (!mpFreetypeFont[nFallbackLevel]->GetFreetypeFont().TestFont())
+ mpFreetypeFont[nFallbackLevel] = nullptr;
+}
+
+FontCharMapRef FreeTypeTextRenderImpl::GetFontCharMap() const
+{
+ if (!mpFreetypeFont[0])
+ return nullptr;
+ return mpFreetypeFont[0]->GetFontFace()->GetFontCharMap();
+}
+
+bool FreeTypeTextRenderImpl::GetFontCapabilities(vcl::FontCapabilities &rGetImplFontCapabilities) const
+{
+ if (!mpFreetypeFont[0])
+ return false;
+ return mpFreetypeFont[0]->GetFontFace()->GetFontCapabilities(rGetImplFontCapabilities);
+}
+
+// SalGraphics
+void
+FreeTypeTextRenderImpl::SetTextColor( Color nColor )
+{
+ if( mnTextColor != nColor )
+ {
+ mnTextColor = nColor;
+ }
+}
+
+bool FreeTypeTextRenderImpl::AddTempDevFont(vcl::font::PhysicalFontCollection* pFontCollection,
+ const OUString& rFileURL, const OUString& rFontName)
+{
+ // inform PSP font manager
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ std::vector<psp::fontID> aFontIds = rMgr.addFontFile(rFileURL);
+ if (aFontIds.empty())
+ return false;
+
+ FreetypeManager& rFreetypeManager = FreetypeManager::get();
+ for (auto const& nFontId : aFontIds)
+ {
+ // prepare font data
+ auto const* pFont = rMgr.getFont(nFontId);
+ if (!pFont)
+ continue;
+
+ // inform glyph cache of new font
+ FontAttributes aDFA = pFont->m_aFontAttributes;
+ aDFA.IncreaseQualityBy(5800);
+ if (!rFontName.isEmpty())
+ aDFA.SetFamilyName(rFontName);
+
+ int nFaceNum = rMgr.getFontFaceNumber(nFontId);
+ int nVariantNum = rMgr.getFontFaceVariation(nFontId);
+
+ const OString& rFileName = rMgr.getFontFileSysPath(nFontId);
+ rFreetypeManager.AddFontFile(rFileName, nFaceNum, nVariantNum, nFontId, aDFA);
+ }
+
+ // announce new font to device's font list
+ rFreetypeManager.AnnounceFonts(pFontCollection);
+ return true;
+}
+
+void FreeTypeTextRenderImpl::ClearDevFontCache()
+{
+ FreetypeManager::get().ClearFontCache();
+}
+
+void FreeTypeTextRenderImpl::GetDevFontList(vcl::font::PhysicalFontCollection* pFontCollection)
+{
+ // prepare the FreetypeManager using psprint's font infos
+ FreetypeManager& rFreetypeManager = FreetypeManager::get();
+
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ ::std::vector<psp::fontID> aList;
+ rMgr.getFontList(aList);
+ for (auto const& nFontId : aList)
+ {
+ auto const* pFont = rMgr.getFont(nFontId);
+ if (!pFont)
+ continue;
+
+ // normalize face number to the FreetypeManager
+ int nFaceNum = rMgr.getFontFaceNumber(nFontId);
+ int nVariantNum = rMgr.getFontFaceVariation(nFontId);
+
+ // inform FreetypeManager about this font provided by the PsPrint subsystem
+ FontAttributes aDFA = pFont->m_aFontAttributes;
+ aDFA.IncreaseQualityBy(4096);
+ const OString& rFileName = rMgr.getFontFileSysPath(nFontId);
+ rFreetypeManager.AddFontFile(rFileName, nFaceNum, nVariantNum, nFontId, aDFA);
+ }
+
+ // announce glyphcache fonts
+ rFreetypeManager.AnnounceFonts(pFontCollection);
+
+ // register platform specific font substitutions if available
+ SalGenericInstance::RegisterFontSubstitutors(pFontCollection);
+}
+
+void FreeTypeTextRenderImpl::GetFontMetric( FontMetricDataRef& rxFontMetric, int nFallbackLevel )
+{
+ if( nFallbackLevel >= MAX_FALLBACK )
+ return;
+
+ if (mpFreetypeFont[nFallbackLevel])
+ mpFreetypeFont[nFallbackLevel]->GetFreetypeFont().GetFontMetric(rxFontMetric);
+}
+
+std::unique_ptr<GenericSalLayout> FreeTypeTextRenderImpl::GetTextLayout(int nFallbackLevel)
+{
+ assert(mpFreetypeFont[nFallbackLevel]);
+ if (!mpFreetypeFont[nFallbackLevel])
+ return nullptr;
+ return std::make_unique<GenericSalLayout>(*mpFreetypeFont[nFallbackLevel]);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
new file mode 100644
index 0000000000..f296e3cf34
--- /dev/null
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -0,0 +1,298 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#if HAVE_FEATURE_SKIA
+#include <skia/x11/gdiimpl.hxx>
+#include <skia/x11/textrender.hxx>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+
+#include <headless/svpgdi.hxx>
+
+#include <vcl/sysdata.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+
+#include <unx/salunx.h>
+#include <unx/saldisp.hxx>
+#include <unx/salgdi.h>
+#include <unx/x11/xlimits.hxx>
+
+#include <salframe.hxx>
+#include <salgdiimpl.hxx>
+#include <textrender.hxx>
+#include <salvd.hxx>
+
+#include <unx/salframe.h>
+#include <unx/cairotextrender.hxx>
+#include "cairo_xlib_cairo.hxx"
+#include <cairo-xlib.h>
+
+#include "X11CairoSalGraphicsImpl.hxx"
+
+
+// X11Common
+
+X11Common::X11Common()
+ : m_hDrawable(None)
+ , m_pColormap(nullptr)
+{}
+
+// X11SalGraphics
+
+X11SalGraphics::X11SalGraphics():
+ m_pFrame(nullptr),
+ m_pVDev(nullptr),
+ m_nXScreen( 0 )
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ {
+ mxImpl.reset(new X11SkiaSalGraphicsImpl(*this));
+ mxTextRenderImpl.reset(new SkiaTextRender);
+ }
+ else
+#endif
+ {
+ mxImpl.reset(new X11CairoSalGraphicsImpl(*this, maCairoCommon));
+ mxTextRenderImpl.reset(new CairoTextRender(maCairoCommon));
+ }
+}
+
+X11SalGraphics::~X11SalGraphics() COVERITY_NOEXCEPT_FALSE
+{
+ DeInit();
+ ReleaseFonts();
+ freeResources();
+}
+
+void X11SalGraphics::freeResources()
+{
+ mxImpl->freeResources();
+
+ if( m_pDeleteColormap )
+ {
+ m_pDeleteColormap.reset();
+ maX11Common.m_pColormap = nullptr;
+ }
+}
+
+SalGraphicsImpl* X11SalGraphics::GetImpl() const
+{
+ return mxImpl.get();
+}
+
+void X11SalGraphics::SetDrawable(Drawable aDrawable, cairo_surface_t* pSurface, SalX11Screen nXScreen)
+{
+ maCairoCommon.m_pSurface = pSurface;
+ if (maCairoCommon.m_pSurface)
+ {
+ maCairoCommon.m_aFrameSize.setX(cairo_xlib_surface_get_width(pSurface));
+ maCairoCommon.m_aFrameSize.setY(cairo_xlib_surface_get_height(pSurface));
+ dl_cairo_surface_get_device_scale(pSurface, &maCairoCommon.m_fScale, nullptr);
+ }
+
+ // shortcut if nothing changed
+ if( maX11Common.m_hDrawable == aDrawable )
+ return;
+
+ // free screen specific resources if needed
+ if( nXScreen != m_nXScreen )
+ {
+ freeResources();
+ maX11Common.m_pColormap = &vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetColormap( nXScreen );
+ m_nXScreen = nXScreen;
+ }
+
+ maX11Common.m_hDrawable = aDrawable;
+}
+
+void X11SalGraphics::Init( X11SalFrame& rFrame, Drawable aTarget,
+ SalX11Screen nXScreen )
+{
+ maX11Common.m_pColormap = &vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetColormap(nXScreen);
+ m_nXScreen = nXScreen;
+
+ m_pFrame = &rFrame;
+ m_pVDev = nullptr;
+
+ SetDrawable(aTarget, rFrame.GetSurface(), nXScreen);
+ mxImpl->Init();
+}
+
+void X11SalGraphics::DeInit()
+{
+ mxImpl->DeInit();
+ SetDrawable(None, nullptr, m_nXScreen);
+}
+
+void X11SalGraphics::GetResolution( sal_Int32 &rDPIX, sal_Int32 &rDPIY ) // const
+{
+ char* pForceDpi;
+ if ((pForceDpi = getenv("SAL_FORCEDPI")))
+ {
+ OString sForceDPI(pForceDpi);
+ rDPIX = rDPIY = sForceDPI.toInt32();
+ return;
+ }
+
+ const SalDisplay *pDisplay = GetDisplay();
+ if (!pDisplay)
+ {
+ SAL_WARN( "vcl", "Null display");
+ rDPIX = rDPIY = 96;
+ return;
+ }
+
+ Pair dpi = pDisplay->GetResolution();
+ rDPIX = dpi.A();
+ rDPIY = dpi.B();
+
+ if ( rDPIY > 200 )
+ {
+ rDPIX = Divide( rDPIX * 200, rDPIY );
+ rDPIY = 200;
+ }
+
+ // #i12705# equalize x- and y-resolution if they are close enough
+ if( rDPIX == rDPIY )
+ return;
+
+ // different x- and y- resolutions are usually artifacts of
+ // a wrongly calculated screen size.
+#ifdef DEBUG
+ SAL_INFO("vcl.gdi", "Forcing Resolution from "
+ << std::hex << rDPIX
+ << std::dec << rDPIX
+ << " to "
+ << std::hex << rDPIY
+ << std::dec << rDPIY);
+#endif
+ rDPIX = rDPIY; // y-resolution is more trustworthy
+}
+
+SystemGraphicsData X11SalGraphics::GetGraphicsData() const
+{
+ SystemGraphicsData aRes;
+
+ aRes.nSize = sizeof(aRes);
+ aRes.pDisplay = GetXDisplay();
+ aRes.hDrawable = maX11Common.m_hDrawable;
+ aRes.pVisual = GetVisual().visual;
+ aRes.nScreen = m_nXScreen.getXScreen();
+ return aRes;
+}
+
+void X11SalGraphics::Flush()
+{
+ if( X11GraphicsImpl* x11Impl = dynamic_cast< X11GraphicsImpl* >( mxImpl.get()))
+ x11Impl->Flush();
+}
+
+#if ENABLE_CAIRO_CANVAS
+
+bool X11SalGraphics::SupportsCairo() const
+{
+ return true;
+}
+
+cairo::SurfaceSharedPtr X11SalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr& rSurface) const
+{
+ return std::make_shared<cairo::X11Surface>(rSurface);
+}
+
+namespace
+{
+ cairo::X11SysData getSysData( const vcl::Window& rWindow )
+ {
+ const SystemEnvData* pSysData = rWindow.GetSystemData();
+
+ if( !pSysData )
+ return cairo::X11SysData();
+ else
+ return cairo::X11SysData(*pSysData, rWindow.ImplGetFrame());
+ }
+
+ cairo::X11SysData getSysData( const VirtualDevice& rVirDev )
+ {
+ return cairo::X11SysData( rVirDev.GetSystemGfxData() );
+ }
+}
+
+cairo::SurfaceSharedPtr X11SalGraphics::CreateSurface( const OutputDevice& rRefDevice,
+ int x, int y, int width, int height ) const
+{
+ if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
+ return std::make_shared<cairo::X11Surface>(getSysData(*rRefDevice.GetOwnerWindow()),
+ x,y,width,height);
+ if( rRefDevice.IsVirtual() )
+ return std::make_shared<cairo::X11Surface>(getSysData(static_cast<const VirtualDevice&>(rRefDevice)),
+ x,y,width,height);
+ return cairo::SurfaceSharedPtr();
+}
+
+cairo::SurfaceSharedPtr X11SalGraphics::CreateBitmapSurface( const OutputDevice& rRefDevice,
+ const BitmapSystemData& rData,
+ const Size& rSize ) const
+{
+ SAL_INFO("vcl", "requested size: " << rSize.Width() << " x " << rSize.Height()
+ << " available size: " << rData.mnWidth << " x "
+ << rData.mnHeight);
+ if ( rData.mnWidth == rSize.Width() && rData.mnHeight == rSize.Height() )
+ {
+ if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
+ return std::make_shared<cairo::X11Surface>(getSysData(*rRefDevice.GetOwnerWindow()), rData );
+ else if( rRefDevice.IsVirtual() )
+ return std::make_shared<cairo::X11Surface>(getSysData(static_cast<const VirtualDevice&>(rRefDevice)), rData );
+ }
+
+ return cairo::SurfaceSharedPtr();
+}
+
+css::uno::Any X11SalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rSurface, const basegfx::B2ISize& /*rSize*/) const
+{
+ cairo::X11Surface& rXlibSurface=dynamic_cast<cairo::X11Surface&>(*rSurface);
+ css::uno::Sequence< css::uno::Any > args{
+ css::uno::Any(false), // do not call XFreePixmap on it
+ css::uno::Any(sal_Int64(rXlibSurface.getPixmap()->mhDrawable))
+ };
+ return css::uno::Any(args);
+}
+
+#endif // ENABLE_CAIRO_CANVAS
+
+SalGeometryProvider *X11SalGraphics::GetGeometryProvider() const
+{
+ if (m_pFrame)
+ return static_cast< SalGeometryProvider * >(m_pFrame);
+ else
+ return static_cast< SalGeometryProvider * >(m_pVDev);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/salvd.cxx b/vcl/unx/generic/gdi/salvd.cxx
new file mode 100644
index 0000000000..e0a9d33f6e
--- /dev/null
+++ b/vcl/unx/generic/gdi/salvd.cxx
@@ -0,0 +1,241 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/sysdata.hxx>
+
+#include <X11/Xlib.h>
+
+#include <unx/saldisp.hxx>
+#include <unx/salinst.h>
+#include <unx/salgdi.h>
+#include <unx/salvd.h>
+#include <unx/x11/xlimits.hxx>
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#if HAVE_FEATURE_SKIA
+#include <skia/x11/salvd.hxx>
+#endif
+#include <cairo-xlib.h>
+
+std::unique_ptr<SalVirtualDevice> X11SalInstance::CreateX11VirtualDevice(const SalGraphics& rGraphics,
+ tools::Long &nDX, tools::Long &nDY, DeviceFormat eFormat, const SystemGraphicsData *pData,
+ std::unique_ptr<X11SalGraphics> pNewGraphics)
+{
+ assert(pNewGraphics);
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ return std::unique_ptr<SalVirtualDevice>(new X11SkiaSalVirtualDevice(rGraphics, nDX, nDY, pData, std::move(pNewGraphics)));
+ else
+#endif
+ return std::unique_ptr<SalVirtualDevice>(new X11SalVirtualDevice(rGraphics, nDX, nDY, eFormat, pData, std::move(pNewGraphics)));
+}
+
+std::unique_ptr<SalVirtualDevice> X11SalInstance::CreateVirtualDevice(SalGraphics& rGraphics,
+ tools::Long &nDX, tools::Long &nDY, DeviceFormat eFormat, const SystemGraphicsData *pData)
+{
+ return CreateX11VirtualDevice(rGraphics, nDX, nDY, eFormat, pData, std::make_unique<X11SalGraphics>());
+}
+
+void X11SalGraphics::Init(X11SalVirtualDevice *pDevice, SalColormap* pColormap, bool bDeleteColormap)
+{
+ SalDisplay *pDisplay = pDevice->GetDisplay();
+ m_nXScreen = pDevice->GetXScreenNumber();
+
+ int nVisualDepth = pDisplay->GetColormap( m_nXScreen ).GetVisual().GetDepth();
+ int nDeviceDepth = pDevice->GetDepth();
+
+ if( pColormap )
+ {
+ maX11Common.m_pColormap = pColormap;
+ if( bDeleteColormap )
+ m_pDeleteColormap.reset(pColormap);
+ }
+ else if( nDeviceDepth == nVisualDepth )
+ maX11Common.m_pColormap = &pDisplay->GetColormap( m_nXScreen );
+ else if( nDeviceDepth == 1 )
+ {
+ m_pDeleteColormap.reset(new SalColormap());
+ maX11Common.m_pColormap = m_pDeleteColormap.get();
+ }
+
+ m_pVDev = pDevice;
+ m_pFrame = nullptr;
+
+ SetDrawable(pDevice->GetDrawable(), pDevice->GetSurface(), m_nXScreen);
+ mxImpl->Init();
+}
+
+X11SalVirtualDevice::X11SalVirtualDevice(const SalGraphics& rGraphics, tools::Long &nDX, tools::Long &nDY,
+ DeviceFormat /*eFormat*/, const SystemGraphicsData *pData,
+ std::unique_ptr<X11SalGraphics> pNewGraphics) :
+ pGraphics_(std::move(pNewGraphics)),
+ m_nXScreen(0),
+ bGraphics_(false)
+{
+ SalColormap* pColormap = nullptr;
+ bool bDeleteColormap = false;
+
+ sal_uInt16 nBitCount = rGraphics.GetBitCount();
+ pDisplay_ = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ nDepth_ = nBitCount;
+
+ if( pData && pData->hDrawable != None )
+ {
+ ::Window aRoot;
+ int x, y;
+ unsigned int w = 0, h = 0, bw, d;
+ Display* pDisp = pDisplay_->GetDisplay();
+ XGetGeometry( pDisp, pData->hDrawable,
+ &aRoot, &x, &y, &w, &h, &bw, &d );
+ int nScreen = 0;
+ while( nScreen < ScreenCount( pDisp ) )
+ {
+ if( RootWindow( pDisp, nScreen ) == aRoot )
+ break;
+ nScreen++;
+ }
+ nDX_ = static_cast<tools::Long>(w);
+ nDY_ = static_cast<tools::Long>(h);
+ nDX = nDX_;
+ nDY = nDY_;
+ m_nXScreen = SalX11Screen( nScreen );
+ hDrawable_ = pData->hDrawable;
+ bExternPixmap_ = true;
+ }
+ else
+ {
+ nDX_ = nDX;
+ nDY_ = nDY;
+ m_nXScreen = static_cast<const X11SalGraphics&>(rGraphics).GetScreenNumber();
+ hDrawable_ = limitXCreatePixmap( GetXDisplay(),
+ pDisplay_->GetDrawable( m_nXScreen ),
+ nDX_, nDY_,
+ GetDepth() );
+ bExternPixmap_ = false;
+ }
+
+ if( nBitCount != pDisplay_->GetVisual( m_nXScreen ).GetDepth() )
+ {
+ pColormap = new SalColormap( nBitCount );
+ bDeleteColormap = true;
+ }
+
+ pGraphics_->SetLayout( SalLayoutFlags::NONE ); // by default no! mirroring for VirtualDevices, can be enabled with EnableRTL()
+
+ // tdf#127529 see SvpSalInstance::CreateVirtualDevice for the rare case of a non-null pPreExistingTarget
+ cairo_surface_t* pPreExistingTarget = pData ? static_cast<cairo_surface_t*>(pData->pSurface) : nullptr;
+ if (pPreExistingTarget)
+ {
+ m_bOwnsSurface = false;
+ m_pSurface = pPreExistingTarget;
+ }
+ else
+ {
+ m_bOwnsSurface = true;
+ m_pSurface = cairo_xlib_surface_create(GetXDisplay(), hDrawable_,
+ pDisplay_->GetColormap(m_nXScreen).GetVisual().visual,
+ nDX_, nDY_);
+ }
+
+ pGraphics_->Init(this, pColormap, bDeleteColormap);
+}
+
+X11SalVirtualDevice::~X11SalVirtualDevice()
+{
+ pGraphics_.reset();
+
+ if (m_bOwnsSurface)
+ cairo_surface_destroy(m_pSurface);
+
+ if( GetDrawable() && !bExternPixmap_ )
+ XFreePixmap( GetXDisplay(), GetDrawable() );
+}
+
+SalGraphics* X11SalVirtualDevice::AcquireGraphics()
+{
+ if( bGraphics_ )
+ return nullptr;
+
+ if( pGraphics_ )
+ bGraphics_ = true;
+
+ return pGraphics_.get();
+}
+
+void X11SalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{ bGraphics_ = false; }
+
+bool X11SalVirtualDevice::SetSize( tools::Long nDX, tools::Long nDY )
+{
+ if( bExternPixmap_ )
+ return false;
+
+ if( !nDX ) nDX = 1;
+ if( !nDY ) nDY = 1;
+
+ if (m_bOwnsSurface)
+ cairo_surface_destroy(m_pSurface);
+
+ Pixmap h = limitXCreatePixmap( GetXDisplay(),
+ pDisplay_->GetDrawable( m_nXScreen ),
+ nDX, nDY, nDepth_ );
+
+ if( !h )
+ {
+ if( !GetDrawable() )
+ {
+ hDrawable_ = limitXCreatePixmap( GetXDisplay(),
+ pDisplay_->GetDrawable( m_nXScreen ),
+ 1, 1, nDepth_ );
+ nDX_ = 1;
+ nDY_ = 1;
+ }
+
+ if (m_bOwnsSurface)
+ {
+ m_pSurface = cairo_xlib_surface_create(GetXDisplay(), hDrawable_,
+ pDisplay_->GetColormap(m_nXScreen).GetVisual().visual,
+ nDX_, nDY_);
+ }
+
+ return false;
+ }
+
+ if( GetDrawable() )
+ XFreePixmap( GetXDisplay(), GetDrawable() );
+ hDrawable_ = h;
+
+ nDX_ = nDX;
+ nDY_ = nDY;
+
+ if (m_bOwnsSurface)
+ {
+ m_pSurface = cairo_xlib_surface_create(GetXDisplay(), hDrawable_,
+ pDisplay_->GetColormap(m_nXScreen).GetVisual().visual,
+ nDX_, nDY_);
+ }
+
+ if( pGraphics_ )
+ pGraphics_->Init( this );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */