summaryrefslogtreecommitdiffstats
path: root/vcl/unx/generic/gdi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/unx/generic/gdi
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
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.cxx185
-rw-r--r--vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.hxx97
-rw-r--r--vcl/unx/generic/gdi/cairo_xlib_cairo.cxx307
-rw-r--r--vcl/unx/generic/gdi/cairo_xlib_cairo.hxx96
-rw-r--r--vcl/unx/generic/gdi/cairotextrender.cxx382
-rw-r--r--vcl/unx/generic/gdi/font.cxx116
-rw-r--r--vcl/unx/generic/gdi/freetypetextrender.cxx216
-rw-r--r--vcl/unx/generic/gdi/gdiimpl.cxx2015
-rw-r--r--vcl/unx/generic/gdi/gdiimpl.hxx297
-rw-r--r--vcl/unx/generic/gdi/salbmp.cxx1001
-rw-r--r--vcl/unx/generic/gdi/salgdi.cxx482
-rw-r--r--vcl/unx/generic/gdi/salgdi2.cxx89
-rw-r--r--vcl/unx/generic/gdi/salvd.cxx222
-rw-r--r--vcl/unx/generic/gdi/x11cairotextrender.cxx66
-rw-r--r--vcl/unx/generic/gdi/xrender_peer.cxx48
15 files changed, 5619 insertions, 0 deletions
diff --git a/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.cxx b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.cxx
new file mode 100644
index 000000000..2109e19b9
--- /dev/null
+++ b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.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 "X11CairoSalGraphicsImpl.hxx"
+
+#if ENABLE_CAIRO_CANVAS
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+
+X11CairoSalGraphicsImpl::X11CairoSalGraphicsImpl(X11SalGraphics& rParent, X11Common& rX11Common)
+ : X11SalGraphicsImpl(rParent)
+ , mrX11Common(rX11Common)
+ , mnPenColor(SALCOLOR_NONE)
+ , mnFillColor(SALCOLOR_NONE)
+{
+}
+
+bool X11CairoSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ if (fTransparency >= 1.0)
+ {
+ return true;
+ }
+
+ if (rPolyPolygon.count() == 0)
+ {
+ return true;
+ }
+
+ // Fallback: Transform to DeviceCoordinates
+ basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
+ aPolyPolygon.transform(rObjectToDevice);
+
+ if (SALCOLOR_NONE == mnFillColor && SALCOLOR_NONE == mnPenColor)
+ {
+ return true;
+ }
+
+ // enable by setting to something
+ static const char* pUseCairoForPolygons(getenv("SAL_ENABLE_USE_CAIRO_FOR_POLYGONS"));
+
+ if (nullptr != pUseCairoForPolygons && mrX11Common.SupportsCairo())
+ {
+ // snap to raster if requested
+ const bool bSnapPoints(!getAntiAlias());
+
+ if (bSnapPoints)
+ {
+ aPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygon);
+ }
+
+ cairo_t* cr = mrX11Common.getCairoContext();
+ clipRegion(cr);
+
+ for (auto const& rPolygon : std::as_const(aPolyPolygon))
+ {
+ const sal_uInt32 nPointCount(rPolygon.count());
+
+ if (nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nPointCount : nPointCount - 1);
+
+ if (nEdgeCount)
+ {
+ basegfx::B2DCubicBezier aEdge;
+
+ for (sal_uInt32 b = 0; b < nEdgeCount; ++b)
+ {
+ rPolygon.getBezierSegment(b, aEdge);
+
+ if (!b)
+ {
+ const basegfx::B2DPoint aStart(aEdge.getStartPoint());
+ cairo_move_to(cr, aStart.getX(), aStart.getY());
+ }
+
+ const basegfx::B2DPoint aEnd(aEdge.getEndPoint());
+
+ if (aEdge.isBezier())
+ {
+ const basegfx::B2DPoint aCP1(aEdge.getControlPointA());
+ const basegfx::B2DPoint aCP2(aEdge.getControlPointB());
+ cairo_curve_to(cr, aCP1.getX(), aCP1.getY(), aCP2.getX(), aCP2.getY(),
+ aEnd.getX(), aEnd.getY());
+ }
+ else
+ {
+ cairo_line_to(cr, aEnd.getX(), aEnd.getY());
+ }
+ }
+
+ cairo_close_path(cr);
+ }
+ }
+ }
+
+ if (SALCOLOR_NONE != mnFillColor)
+ {
+ cairo_set_source_rgba(cr, mnFillColor.GetRed() / 255.0, mnFillColor.GetGreen() / 255.0,
+ mnFillColor.GetBlue() / 255.0, 1.0 - fTransparency);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_fill_preserve(cr);
+ }
+
+ if (SALCOLOR_NONE != mnPenColor)
+ {
+ cairo_set_source_rgba(cr, mnPenColor.GetRed() / 255.0, mnPenColor.GetGreen() / 255.0,
+ mnPenColor.GetBlue() / 255.0, 1.0 - fTransparency);
+ cairo_stroke_preserve(cr);
+ }
+
+ X11Common::releaseCairoContext(cr);
+ return true;
+ }
+
+ return X11SalGraphicsImpl::drawPolyPolygon(rObjectToDevice, rPolyPolygon, fTransparency);
+}
+
+bool X11CairoSalGraphicsImpl::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)
+{
+ if (0 == rPolygon.count())
+ {
+ return true;
+ }
+
+ if (fTransparency >= 1.0)
+ {
+ return true;
+ }
+
+ // disable by setting to something
+ static const char* pUseCairoForFatLines(getenv("SAL_DISABLE_USE_CAIRO_FOR_FATLINES"));
+
+ if (nullptr == pUseCairoForFatLines && mrX11Common.SupportsCairo())
+ {
+ cairo_t* cr = mrX11Common.getCairoContext();
+ clipRegion(cr);
+
+ // Use the now available static drawPolyLine from the Cairo-Headless-Fallback
+ // that will take care of all needed stuff
+ const bool bRetval(CairoCommon::drawPolyLine(
+ cr, nullptr, mnPenColor, getAntiAlias(), rObjectToDevice, rPolygon, fTransparency,
+ fLineWidth, pStroke, eLineJoin, eLineCap, fMiterMinimumAngle, bPixelSnapHairline));
+
+ X11Common::releaseCairoContext(cr);
+
+ if (bRetval)
+ {
+ return true;
+ }
+ }
+
+ return X11SalGraphicsImpl::drawPolyLine(rObjectToDevice, rPolygon, fTransparency, fLineWidth,
+ pStroke, eLineJoin, eLineCap, fMiterMinimumAngle,
+ bPixelSnapHairline);
+}
+
+#endif
+
+/* 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 000000000..beb07e3ff
--- /dev/null
+++ b/vcl/unx/generic/gdi/X11CairoSalGraphicsImpl.hxx
@@ -0,0 +1,97 @@
+/* -*- 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 <config_cairo_canvas.h>
+
+#if ENABLE_CAIRO_CANVAS
+
+#include <cairo-xlib.h>
+#include <unx/salgdi.h>
+#include "gdiimpl.hxx"
+#include "cairo_xlib_cairo.hxx"
+
+#include <headless/CairoCommon.hxx>
+
+class X11CairoSalGraphicsImpl : public X11SalGraphicsImpl
+{
+private:
+ X11Common& mrX11Common;
+ vcl::Region maClipRegion;
+ Color mnPenColor;
+ Color mnFillColor;
+
+ using X11SalGraphicsImpl::drawPolyPolygon;
+ using X11SalGraphicsImpl::drawPolyLine;
+
+public:
+ X11CairoSalGraphicsImpl(X11SalGraphics& rParent, X11Common& rX11Common);
+
+ void ResetClipRegion() override
+ {
+ maClipRegion.SetNull();
+ X11SalGraphicsImpl::ResetClipRegion();
+ }
+
+ bool setClipRegion(const vcl::Region& i_rClip) override
+ {
+ maClipRegion = i_rClip;
+ return X11SalGraphicsImpl::setClipRegion(i_rClip);
+ }
+
+ void SetLineColor() override
+ {
+ mnPenColor = SALCOLOR_NONE;
+ X11SalGraphicsImpl::SetLineColor();
+ }
+
+ void SetLineColor(Color nColor) override
+ {
+ mnPenColor = nColor;
+ X11SalGraphicsImpl::SetLineColor(nColor);
+ }
+
+ void SetFillColor() override
+ {
+ mnFillColor = SALCOLOR_NONE;
+ X11SalGraphicsImpl::SetFillColor();
+ }
+
+ void SetFillColor(Color nColor) override
+ {
+ mnFillColor = nColor;
+ X11SalGraphicsImpl::SetFillColor(nColor);
+ }
+
+ void clipRegion(cairo_t* cr) { CairoCommon::clipRegion(cr, maClipRegion); }
+
+ bool drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency) 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;
+};
+
+#endif
+
+/* 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 000000000..17b34442b
--- /dev/null
+++ b/vcl/unx/generic/gdi/cairo_xlib_cairo.cxx
@@ -0,0 +1,307 @@
+/* -*- 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 <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),
+ pRenderFormat(nullptr)
+ {}
+
+ X11SysData::X11SysData( const SystemGraphicsData& pSysDat ) :
+ pDisplay(pSysDat.pDisplay),
+ hDrawable(pSysDat.hDrawable),
+ pVisual(pSysDat.pVisual),
+ nScreen(pSysDat.nScreen),
+ pRenderFormat(pSysDat.pXRenderFormat)
+ {}
+
+ X11SysData::X11SysData( const SystemEnvData& pSysDat, const SalFrame* pReference ) :
+ pDisplay(pSysDat.pDisplay),
+ hDrawable(pSysDat.GetWindowHandle(pReference)),
+ pVisual(pSysDat.pVisual),
+ nScreen(pSysDat.nScreen),
+ pRenderFormat(nullptr)
+ {}
+
+ X11Pixmap::~X11Pixmap()
+ {
+ if( mpDisplay && mhDrawable )
+ XFreePixmap( static_cast<Display*>(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,
+ const X11PixmapSharedPtr& rPixmap,
+ const CairoSurfaceSharedPtr& pSurface ) :
+ maSysData(rSysData),
+ mpPixmap(rPixmap),
+ mpSurface(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( const CairoSurfaceSharedPtr& pSurface ) :
+ maSysData(),
+ mpSurface(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( static_cast<Display*>(rSysData.pDisplay),
+ rSysData.hDrawable,
+ static_cast<Visual*>(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( static_cast<Display*>(rSysData.pDisplay),
+ reinterpret_cast<Drawable>(rData.aPixmap),
+ static_cast<Visual*>(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( static_cast<Display*>(maSysData.pDisplay), nFormat );
+ Pixmap hPixmap = limitXCreatePixmap( static_cast<Display*>(maSysData.pDisplay), maSysData.hDrawable,
+ width > 0 ? width : 1, height > 0 ? height : 1,
+ pFormat->depth );
+
+ X11SysData aSysData(maSysData);
+ aSysData.pRenderFormat = pFormat;
+ return SurfaceSharedPtr(
+ new X11Surface( aSysData,
+ std::make_shared<X11Pixmap>(hPixmap, maSysData.pDisplay),
+ CairoSurfaceSharedPtr(
+ cairo_xlib_surface_create_with_xrender_format(
+ static_cast<Display*>(maSysData.pDisplay),
+ hPixmap,
+ ScreenOfDisplay(static_cast<Display *>(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.pXRenderFormat = maSysData.pRenderFormat;
+ 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),
+ getFormat());
+ }
+
+ /**
+ * 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( static_cast<Display*>(maSysData.pDisplay), false );
+ }
+
+ /**
+ * Surface::getDepth: Get the color depth of the Canvas surface.
+ *
+ * @return color depth
+ **/
+ int X11Surface::getDepth() const
+ {
+ if (maSysData.pRenderFormat)
+ return static_cast<XRenderPictFormat*>(maSysData.pRenderFormat)->depth;
+ return -1;
+ }
+
+ /**
+ * Surface::getFormat: Get the device format of the Canvas surface.
+ *
+ * @return color format
+ **/
+ DeviceFormat X11Surface::getFormat() const
+ {
+ if (!maSysData.pRenderFormat)
+ return DeviceFormat::DEFAULT;
+ assert (static_cast<XRenderPictFormat*>(maSysData.pRenderFormat)->depth != 1 && "unsupported");
+ return DeviceFormat::DEFAULT;
+ }
+}
+
+/* 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 000000000..c44fde437
--- /dev/null
+++ b/vcl/unx/generic/gdi/cairo_xlib_cairo.hxx
@@ -0,0 +1,96 @@
+/* -*- 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 );
+
+ void* pDisplay; // the relevant display connection
+ Drawable hDrawable; // a drawable
+ void* pVisual; // the visual in use
+ int nScreen; // the current screen of the drawable
+ void* pRenderFormat; // render format for drawable
+ };
+
+ /// RAII wrapper for a pixmap
+ struct X11Pixmap
+ {
+ void* mpDisplay; // the relevant display connection
+ Pixmap mhDrawable; // a drawable
+
+ X11Pixmap( Pixmap hDrawable, void* 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, const X11PixmapSharedPtr& rPixmap, const CairoSurfaceSharedPtr& pSurface );
+
+ public:
+ /// takes over ownership of passed cairo_surface
+ explicit X11Surface( const 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;
+
+ int getDepth() const;
+ DeviceFormat getFormat() const;
+ 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 000000000..898110fee
--- /dev/null
+++ b/vcl/unx/generic/gdi/cairotextrender.cxx
@@ -0,0 +1,382 @@
+/* -*- 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/cairotextrender.hxx>
+
+#include <unx/fc_fontoptions.hxx>
+#include <unx/freetype_glyphcache.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
+
+CairoTextRender::CairoTextRender()
+{
+ // 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);
+}
+
+CairoTextRender::~CairoTextRender()
+{
+ cairo_font_options_destroy(mpRoundGlyphPosOffOptions);
+}
+
+void CairoTextRender::DrawTextLayout(const GenericSalLayout& rLayout, const SalGraphics& rGraphics)
+{
+ const FreetypeFontInstance& rInstance = static_cast<FreetypeFontInstance&>(rLayout.GetFont());
+ const FreetypeFont& rFont = rInstance.GetFreetypeFont();
+
+ const bool bResolutionIndependentLayoutEnabled = rGraphics.getTextRenderModeForResolutionIndependentLayoutEnabled();
+
+ std::vector<cairo_glyph_t> cairo_glyphs;
+ std::vector<int> glyph_extrarotation;
+ cairo_glyphs.reserve( 256 );
+
+ DevicePoint aPos;
+ const GlyphItem* pGlyph;
+ 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);
+
+ // tdf#150507 like skia even when subpixel rendering pixel snap y
+ if (bResolutionIndependentLayoutEnabled)
+ {
+ if (!bVertical)
+ aGlyph.y = std::floor(aGlyph.y + 0.5);
+ else
+ aGlyph.x = std::floor(aGlyph.x + 0.5);
+ }
+
+ cairo_glyphs.push_back(aGlyph);
+ }
+
+ if (cairo_glyphs.empty())
+ return;
+
+ const vcl::font::FontSelectPattern& rFSD = rInstance.GetFontSelectPattern();
+ int nHeight = rFSD.mnHeight;
+ int 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;
+ }
+
+ int nRatio = nWidth * 10 / nHeight;
+ if (FreetypeFont::AlmostHorizontalDrainsRenderingPool(nRatio, rFSD))
+ return;
+
+ /*
+ * 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;
+ }
+
+#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();
+
+ const cairo_font_options_t* pFontOptions = GetSalInstance()->GetCairoFontOptions();
+ if (pFontOptions || bDisableAA || bResolutionIndependentLayoutEnabled)
+ {
+ cairo_hint_style_t eHintStyle = pFontOptions ? cairo_font_options_get_hint_style(pFontOptions) : CAIRO_HINT_STYLE_DEFAULT;
+ bool bAllowedHintStyle = !bResolutionIndependentLayoutEnabled || (eHintStyle == CAIRO_HINT_STYLE_NONE || eHintStyle == CAIRO_HINT_STYLE_SLIGHT);
+
+ if (bDisableAA || !bAllowedHintStyle || bResolutionIndependentLayoutEnabled)
+ {
+ // 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);
+ // Disable private CAIRO_ROUND_GLYPH_POS_ON by merging with font options known to have
+ // CAIRO_ROUND_GLYPH_POS_OFF
+ if (bResolutionIndependentLayoutEnabled)
+ cairo_font_options_merge(pOptions, mpRoundGlyphPosOffOptions);
+ cairo_set_font_options(cr, pOptions);
+ cairo_font_options_destroy(pOptions);
+ }
+ else if (pFontOptions)
+ cairo_set_font_options(cr, pFontOptions);
+ }
+
+ double nDX, nDY;
+ getSurfaceOffset(nDX, nDY);
+ cairo_translate(cr, nDX, nDY);
+
+ clipRegion(cr);
+
+ cairo_set_source_rgb(cr,
+ mnTextColor.GetRed()/255.0,
+ mnTextColor.GetGreen()/255.0,
+ mnTextColor.GetBlue()/255.0);
+
+ FT_Face aFace = rFont.GetFtFace();
+ CairoFontsCache::CacheId aId;
+ aId.maFace = aFace;
+ aId.mpOptions = rFont.GetFontOptions();
+ aId.mbEmbolden = rFont.NeedsArtificialBold();
+
+ cairo_matrix_t m;
+
+ 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;
+ cairo_font_face_t* font_face = CairoFontsCache::FindCachedFont(aId);
+ if (!font_face)
+ {
+ const FontConfigFontOptions *pOptions = aId.mpOptions;
+ FcPattern *pPattern = pOptions->GetPattern();
+ font_face = cairo_ft_font_face_create_for_pattern(pPattern);
+ CairoFontsCache::CacheFont(font_face, aId);
+ }
+ cairo_set_font_face(cr, font_face);
+
+ cairo_set_font_size(cr, nHeight);
+
+ 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)));
+
+ cairo_matrix_t em_square;
+ cairo_matrix_init_identity(&em_square);
+ cairo_get_matrix(cr, &em_square);
+
+ cairo_matrix_scale(&em_square, aFace->units_per_EM,
+ aFace->units_per_EM);
+ cairo_set_matrix(cr, &em_square);
+
+ cairo_font_extents_t font_extents;
+ cairo_font_extents(cr, &font_extents);
+
+ cairo_matrix_init_identity(&em_square);
+ cairo_set_matrix(cr, &em_square);
+ }
+
+ if (rFont.NeedsArtificialItalic())
+ {
+ cairo_matrix_t shear;
+ cairo_matrix_init_identity(&shear);
+ shear.xy = -shear.xx * 0x6000L / 0x10000L;
+ cairo_matrix_multiply(&m, &shear, &m);
+ }
+
+ cairo_set_font_matrix(cr, &m);
+ 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;
+ }
+
+ releaseCairoContext(cr);
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ if (__lsan_enable)
+ __lsan_enable();
+#endif
+}
+
+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 000000000..57ff99a1c
--- /dev/null
+++ b/vcl/unx/generic/gdi/font.cxx
@@ -0,0 +1,116 @@
+/* -*- 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( ImplFontMetricDataRef &rxFontMetric, int nFallbackLevel )
+{
+ mxTextRenderImpl->GetFontMetric(rxFontMetric, nFallbackLevel);
+}
+
+std::unique_ptr<GenericSalLayout> X11SalGraphics::GetTextLayout(int nFallbackLevel)
+{
+ return mxTextRenderImpl->GetTextLayout(nFallbackLevel);
+}
+
+bool X11SalGraphics::CreateFontSubset(
+ const OUString& rToFile,
+ const vcl::font::PhysicalFontFace* pFont,
+ const sal_GlyphId* pGlyphIds,
+ const sal_uInt8* pEncoding,
+ sal_Int32* pWidths,
+ int nGlyphCount,
+ FontSubsetInfo& rInfo
+ )
+{
+ return mxTextRenderImpl->CreateFontSubset(rToFile, pFont,
+ pGlyphIds, pEncoding, pWidths, nGlyphCount, rInfo);
+}
+
+const void* X11SalGraphics::GetEmbedFontData(const vcl::font::PhysicalFontFace* pFont, tools::Long* pDataLen)
+{
+ return mxTextRenderImpl->GetEmbedFontData(pFont, pDataLen);
+}
+
+void X11SalGraphics::FreeEmbedFontData( const void* pData, tools::Long nLen )
+{
+ mxTextRenderImpl->FreeEmbedFontData(pData, nLen);
+}
+
+void X11SalGraphics::GetGlyphWidths( const vcl::font::PhysicalFontFace* pFont,
+ bool bVertical,
+ std::vector< sal_Int32 >& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ mxTextRenderImpl->GetGlyphWidths(pFont, bVertical, rWidths, rUnicodeEnc);
+}
+
+/* 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 000000000..ea8474e69
--- /dev/null
+++ b/vcl/unx/generic/gdi/freetypetextrender.cxx
@@ -0,0 +1,216 @@
+/* -*- 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/genpspgraphics.h>
+#include <unx/geninst.h>
+#include <unx/glyphcache.hxx>
+#include <unx/fc_fontoptions.hxx>
+#include <unx/freetype_glyphcache.hxx>
+#include <font/PhysicalFontFace.hxx>
+#include <impfontmetricdata.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]->GetFreetypeFont().GetFontCharMap();
+}
+
+bool FreeTypeTextRenderImpl::GetFontCapabilities(vcl::FontCapabilities &rGetImplFontCapabilities) const
+{
+ if (!mpFreetypeFont[0])
+ return false;
+ return mpFreetypeFont[0]->GetFreetypeFont().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 )
+{
+ return GenPspGraphics::AddTempDevFontHelper(pFontCollection, rFileURL, rFontName);
+}
+
+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;
+ psp::FastPrintFontInfo aInfo;
+ rMgr.getFontList( aList );
+ for (auto const& elem : aList)
+ {
+ if( !rMgr.getFontFastInfo( elem, aInfo ) )
+ continue;
+
+ // normalize face number to the FreetypeManager
+ int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID );
+ int nVariantNum = rMgr.getFontFaceVariation( aInfo.m_nID );
+
+ // inform FreetypeManager about this font provided by the PsPrint subsystem
+ FontAttributes aDFA = GenPspGraphics::Info2FontAttributes( aInfo );
+ aDFA.IncreaseQualityBy( 4096 );
+ const OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID );
+ rFreetypeManager.AddFontFile(rFileName, nFaceNum, nVariantNum, aInfo.m_nID, aDFA);
+ }
+
+ // announce glyphcache fonts
+ rFreetypeManager.AnnounceFonts(pFontCollection);
+
+ // register platform specific font substitutions if available
+ if (!utl::ConfigManager::IsFuzzing())
+ SalGenericInstance::RegisterFontSubstitutors( pFontCollection );
+}
+
+void FreeTypeTextRenderImpl::GetFontMetric( ImplFontMetricDataRef& 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]);
+}
+
+bool FreeTypeTextRenderImpl::CreateFontSubset(
+ const OUString& rToFile,
+ const vcl::font::PhysicalFontFace* pFont,
+ const sal_GlyphId* pGlyphIds,
+ const sal_uInt8* pEncoding,
+ sal_Int32* pWidths,
+ int nGlyphCount,
+ FontSubsetInfo& rInfo
+ )
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the FreetypeManager search for the PhysicalFontFace pFont
+ psp::fontID aFont = pFont->GetFontId();
+
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ bool bSuccess = rMgr.createFontSubset( rInfo,
+ aFont,
+ rToFile,
+ pGlyphIds,
+ pEncoding,
+ pWidths,
+ nGlyphCount );
+ return bSuccess;
+}
+
+const void* FreeTypeTextRenderImpl::GetEmbedFontData(const vcl::font::PhysicalFontFace* pFont, tools::Long* pDataLen)
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the FreetypeManager search for the PhysicalFontFace pFont
+ psp::fontID aFont = pFont->GetFontId();
+ return GenPspGraphics::DoGetEmbedFontData(aFont, pDataLen);
+}
+
+void FreeTypeTextRenderImpl::FreeEmbedFontData( const void* pData, tools::Long nLen )
+{
+ GenPspGraphics::DoFreeEmbedFontData( pData, nLen );
+}
+
+void FreeTypeTextRenderImpl::GetGlyphWidths( const vcl::font::PhysicalFontFace* pFont,
+ bool bVertical,
+ std::vector< sal_Int32 >& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the FreetypeManager search for the PhysicalFontFace pFont
+ psp::fontID aFont = pFont->GetFontId();
+ GenPspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx
new file mode 100644
index 000000000..d5fc4d6c1
--- /dev/null
+++ b/vcl/unx/generic/gdi/gdiimpl.cxx
@@ -0,0 +1,2015 @@
+/* -*- 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 <memory>
+#include <numeric>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/Xproto.h>
+
+#include "gdiimpl.hxx"
+
+#include <vcl/gradient.hxx>
+#include <sal/log.hxx>
+
+#include <unx/saldisp.hxx>
+#include <unx/salbmp.h>
+#include <unx/salgdi.h>
+#include <unx/salvd.h>
+#include <unx/x11/xlimits.hxx>
+#include <salframe.hxx>
+#include <unx/x11/xrender_peer.hxx>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dtrapezoid.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
+
+#undef SALGDI2_TESTTRANS
+
+#if (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS
+#define DBG_TESTTRANS( _def_drawable ) \
+{ \
+ XCopyArea( pXDisp, _def_drawable, aDrawable, GetCopyGC(), \
+ 0, 0, \
+ rPosAry.mnDestWidth, rPosAry.mnDestHeight, \
+ 0, 0 ); \
+}
+#else // (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS
+#define DBG_TESTTRANS( _def_drawable )
+#endif // (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS
+
+/* From <X11/Intrinsic.h> */
+typedef unsigned long Pixel;
+
+class SalPolyLine
+{
+ std::vector<XPoint> Points_;
+public:
+ SalPolyLine(sal_uLong nPoints, const Point *p)
+ : Points_(nPoints+1)
+ {
+ for (sal_uLong i = 0; i < nPoints; ++i)
+ {
+ Points_[i].x = static_cast<short>(p[i].getX());
+ Points_[i].y = static_cast<short>(p[i].getY());
+ }
+ Points_[nPoints] = Points_[0]; // close polyline
+ }
+
+ const XPoint &operator[](sal_uLong n) const
+ {
+ return Points_[n];
+ }
+
+ XPoint &operator[](sal_uLong n)
+ {
+ return Points_[n];
+ }
+};
+
+namespace
+{
+ void setForeBack(XGCValues& rValues, const SalColormap& rColMap, const SalBitmap& rSalBitmap)
+ {
+ rValues.foreground = rColMap.GetWhitePixel();
+ rValues.background = rColMap.GetBlackPixel();
+
+ //fdo#33455 and fdo#80160 handle 1 bit depth pngs with palette entries
+ //to set fore/back colors
+ SalBitmap& rBitmap = const_cast<SalBitmap&>(rSalBitmap);
+ BitmapBuffer* pBitmapBuffer = rBitmap.AcquireBuffer(BitmapAccessMode::Read);
+ if (!pBitmapBuffer)
+ return;
+
+ const BitmapPalette& rPalette = pBitmapBuffer->maPalette;
+ if (rPalette.GetEntryCount() == 2)
+ {
+ const BitmapColor aWhite(rPalette[rPalette.GetBestIndex(COL_WHITE)]);
+ rValues.foreground = rColMap.GetPixel(aWhite);
+
+ const BitmapColor aBlack(rPalette[rPalette.GetBestIndex(COL_BLACK)]);
+ rValues.background = rColMap.GetPixel(aBlack);
+ }
+ rBitmap.ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Read);
+ }
+}
+
+X11SalGraphicsImpl::X11SalGraphicsImpl(X11SalGraphics& rParent):
+ mrParent(rParent),
+ mnBrushColor( 0xFF, 0xFF, 0XFF ),
+ mpBrushGC(nullptr),
+ mnBrushPixel(0),
+ mbPenGC(false),
+ mbBrushGC(false),
+ mbCopyGC(false),
+ mbInvertGC(false),
+ mbInvert50GC(false),
+ mbStippleGC(false),
+ mbTrackingGC(false),
+ mbDitherBrush(false),
+ mbXORMode(false),
+ mpPenGC(nullptr),
+ mnPenColor( 0x00, 0x00, 0x00 ),
+ mnPenPixel(0),
+ mpMonoGC(nullptr),
+ mpCopyGC(nullptr),
+ mpMaskGC(nullptr),
+ mpInvertGC(nullptr),
+ mpInvert50GC(nullptr),
+ mpStippleGC(nullptr),
+ mpTrackingGC(nullptr)
+{
+}
+
+X11SalGraphicsImpl::~X11SalGraphicsImpl()
+{
+}
+
+void X11SalGraphicsImpl::Init()
+{
+ mnPenPixel = mrParent.GetPixel( mnPenColor );
+ mnBrushPixel = mrParent.GetPixel( mnBrushColor );
+}
+
+XID X11SalGraphicsImpl::GetXRenderPicture()
+{
+ XRenderPeer& rRenderPeer = XRenderPeer::GetInstance();
+
+ if( !mrParent.m_aXRenderPicture )
+ {
+ // check xrender support for matching visual
+ XRenderPictFormat* pXRenderFormat = mrParent.GetXRenderFormat();
+ if( !pXRenderFormat )
+ return 0;
+ // get the matching xrender target for drawable
+ mrParent.m_aXRenderPicture = rRenderPeer.CreatePicture( mrParent.GetDrawable(), pXRenderFormat, 0, nullptr );
+ }
+
+ {
+ // reset clip region
+ // TODO: avoid clip reset if already done
+ XRenderPictureAttributes aAttr;
+ aAttr.clip_mask = None;
+ rRenderPeer.ChangePicture( mrParent.m_aXRenderPicture, CPClipMask, &aAttr );
+ }
+
+ return mrParent.m_aXRenderPicture;
+}
+
+static void freeGC(Display *pDisplay, GC& rGC)
+{
+ if( rGC )
+ {
+ XFreeGC( pDisplay, rGC );
+ rGC = None;
+ }
+}
+
+void X11SalGraphicsImpl::freeResources()
+{
+ Display *pDisplay = mrParent.GetXDisplay();
+
+ freeGC( pDisplay, mpPenGC );
+ freeGC( pDisplay, mpBrushGC );
+ freeGC( pDisplay, mpMonoGC );
+ freeGC( pDisplay, mpTrackingGC );
+ freeGC( pDisplay, mpCopyGC );
+ freeGC( pDisplay, mpMaskGC );
+ freeGC( pDisplay, mpInvertGC );
+ freeGC( pDisplay, mpInvert50GC );
+ freeGC( pDisplay, mpStippleGC );
+ mbTrackingGC = mbPenGC = mbBrushGC = mbCopyGC = mbInvertGC = mbInvert50GC = mbStippleGC = false;
+}
+
+GC X11SalGraphicsImpl::CreateGC( Drawable hDrawable, unsigned long nMask )
+{
+ XGCValues values;
+
+ values.graphics_exposures = False;
+ values.foreground = mrParent.GetColormap().GetBlackPixel()
+ ^ mrParent.GetColormap().GetWhitePixel();
+ values.function = GXxor;
+ values.line_width = 1;
+ values.fill_style = FillStippled;
+ values.stipple = mrParent.GetDisplay()->GetInvert50( mrParent.m_nXScreen );
+ values.subwindow_mode = ClipByChildren;
+
+ return XCreateGC( mrParent.GetXDisplay(), hDrawable, nMask | GCSubwindowMode, &values );
+}
+
+inline GC X11SalGraphicsImpl::GetCopyGC()
+{
+ if( mbXORMode ) return GetInvertGC();
+
+ if( !mpCopyGC )
+ mpCopyGC = CreateGC( mrParent.GetDrawable() );
+
+ if( !mbCopyGC )
+ {
+ mrParent.SetClipRegion( mpCopyGC );
+ mbCopyGC = true;
+ }
+ return mpCopyGC;
+}
+
+GC X11SalGraphicsImpl::GetTrackingGC()
+{
+ if( !mpTrackingGC )
+ {
+ XGCValues values;
+
+ values.graphics_exposures = False;
+ values.foreground = mrParent.GetColormap().GetBlackPixel()
+ ^ mrParent.GetColormap().GetWhitePixel();
+ values.function = GXxor;
+ values.line_width = 1;
+ values.line_style = LineOnOffDash;
+
+ mpTrackingGC = XCreateGC( mrParent.GetXDisplay(), mrParent.GetDrawable(),
+ GCGraphicsExposures | GCForeground | GCFunction
+ | GCLineWidth | GCLineStyle,
+ &values );
+ const char dash_list[2] = {2, 2};
+ XSetDashes( mrParent.GetXDisplay(), mpTrackingGC, 0, dash_list, 2 );
+ }
+
+ if( !mbTrackingGC )
+ {
+ mrParent.SetClipRegion( mpTrackingGC );
+ mbTrackingGC = true;
+ }
+
+ return mpTrackingGC;
+}
+
+GC X11SalGraphicsImpl::GetInvertGC()
+{
+ if( !mpInvertGC )
+ mpInvertGC = CreateGC( mrParent.GetDrawable(),
+ GCGraphicsExposures
+ | GCForeground
+ | GCFunction
+ | GCLineWidth );
+
+ if( !mbInvertGC )
+ {
+ mrParent.SetClipRegion( mpInvertGC );
+ mbInvertGC = true;
+ }
+ return mpInvertGC;
+}
+
+GC X11SalGraphicsImpl::GetInvert50GC()
+{
+ if( !mpInvert50GC )
+ {
+ XGCValues values;
+
+ values.graphics_exposures = False;
+ values.foreground = mrParent.GetColormap().GetWhitePixel();
+ values.background = mrParent.GetColormap().GetBlackPixel();
+ values.function = GXinvert;
+ values.line_width = 1;
+ values.line_style = LineSolid;
+ unsigned long const nValueMask =
+ GCGraphicsExposures
+ | GCForeground
+ | GCBackground
+ | GCFunction
+ | GCLineWidth
+ | GCLineStyle
+ | GCFillStyle
+ | GCStipple;
+
+ values.fill_style = FillStippled;
+ values.stipple = mrParent.GetDisplay()->GetInvert50( mrParent.m_nXScreen );
+
+ mpInvert50GC = XCreateGC( mrParent.GetXDisplay(), mrParent.GetDrawable(),
+ nValueMask,
+ &values );
+ }
+
+ if( !mbInvert50GC )
+ {
+ mrParent.SetClipRegion( mpInvert50GC );
+ mbInvert50GC = true;
+ }
+ return mpInvert50GC;
+}
+
+inline GC X11SalGraphicsImpl::GetStippleGC()
+{
+ if( !mpStippleGC )
+ mpStippleGC = CreateGC( mrParent.GetDrawable(),
+ GCGraphicsExposures
+ | GCFillStyle
+ | GCLineWidth );
+
+ if( !mbStippleGC )
+ {
+ XSetFunction( mrParent.GetXDisplay(), mpStippleGC, mbXORMode ? GXxor : GXcopy );
+ mrParent.SetClipRegion( mpStippleGC );
+ mbStippleGC = true;
+ }
+
+ return mpStippleGC;
+}
+
+GC X11SalGraphicsImpl::SelectBrush()
+{
+ Display *pDisplay = mrParent.GetXDisplay();
+
+ SAL_WARN_IF( mnBrushColor == SALCOLOR_NONE, "vcl", "Brush Transparent" );
+
+ if( !mpBrushGC )
+ {
+ XGCValues values;
+ values.subwindow_mode = ClipByChildren;
+ values.fill_rule = EvenOddRule; // Pict import/ Gradient
+ values.graphics_exposures = False;
+
+ mpBrushGC = XCreateGC( pDisplay, mrParent.GetDrawable(),
+ GCSubwindowMode | GCFillRule | GCGraphicsExposures,
+ &values );
+ }
+
+ if( !mbBrushGC )
+ {
+ if( !mbDitherBrush )
+ {
+ XSetFillStyle ( pDisplay, mpBrushGC, FillSolid );
+ XSetForeground( pDisplay, mpBrushGC, mnBrushPixel );
+ }
+ else
+ {
+ XSetFillStyle ( pDisplay, mpBrushGC, FillTiled );
+ XSetTile ( pDisplay, mpBrushGC, mrParent.hBrush_ );
+ }
+ XSetFunction ( pDisplay, mpBrushGC, mbXORMode ? GXxor : GXcopy );
+ mrParent.SetClipRegion( mpBrushGC );
+
+ mbBrushGC = true;
+ }
+
+ return mpBrushGC;
+}
+
+GC X11SalGraphicsImpl::SelectPen()
+{
+ Display *pDisplay = mrParent.GetXDisplay();
+
+ if( !mpPenGC )
+ {
+ XGCValues values;
+ values.subwindow_mode = ClipByChildren;
+ values.fill_rule = EvenOddRule; // Pict import/ Gradient
+ values.graphics_exposures = False;
+
+ mpPenGC = XCreateGC( pDisplay, mrParent.GetDrawable(),
+ GCSubwindowMode | GCFillRule | GCGraphicsExposures,
+ &values );
+ }
+
+ if( !mbPenGC )
+ {
+ if( mnPenColor != SALCOLOR_NONE )
+ XSetForeground( pDisplay, mpPenGC, mnPenPixel );
+ XSetFunction ( pDisplay, mpPenGC, mbXORMode ? GXxor : GXcopy );
+ mrParent.SetClipRegion( mpPenGC );
+ mbPenGC = true;
+ }
+
+ return mpPenGC;
+}
+
+void X11SalGraphicsImpl::DrawLines(sal_uInt32 nPoints,
+ const SalPolyLine &rPoints,
+ GC pGC,
+ bool bClose)
+{
+ // calculate how many lines XWindow can draw in one go
+ sal_uLong nMaxLines = (mrParent.GetDisplay()->GetMaxRequestSize() - sizeof(xPolyPointReq))
+ / sizeof(xPoint);
+ if( nMaxLines > nPoints ) nMaxLines = nPoints;
+
+ // print all lines that XWindows can draw
+ sal_uLong n;
+ for( n = 0; nPoints - n > nMaxLines; n += nMaxLines - 1 )
+ XDrawLines( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ pGC,
+ const_cast<XPoint*>(&rPoints[n]),
+ nMaxLines,
+ CoordModeOrigin );
+
+ if( n < nPoints )
+ XDrawLines( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ pGC,
+ const_cast<XPoint*>(&rPoints[n]),
+ nPoints - n,
+ CoordModeOrigin );
+ if( bClose )
+ {
+ if( rPoints[nPoints-1].x != rPoints[0].x || rPoints[nPoints-1].y != rPoints[0].y )
+ drawLine( rPoints[nPoints-1].x, rPoints[nPoints-1].y, rPoints[0].x, rPoints[0].y );
+ }
+}
+
+void X11SalGraphicsImpl::copyBits( const SalTwoRect& rPosAry,
+ SalGraphics *pSSrcGraphics )
+{
+ X11SalGraphics* pSrcGraphics = pSSrcGraphics
+ ? static_cast<X11SalGraphics*>(pSSrcGraphics)
+ : &mrParent;
+
+ if( rPosAry.mnSrcWidth <= 0
+ || rPosAry.mnSrcHeight <= 0
+ || rPosAry.mnDestWidth <= 0
+ || rPosAry.mnDestHeight <= 0 )
+ {
+ return;
+ }
+
+ int n;
+ if( pSrcGraphics == &mrParent )
+ {
+ n = 2;
+ }
+ else if( pSrcGraphics->bWindow_ )
+ {
+ // window or compatible virtual device
+ if( pSrcGraphics->GetDisplay() == mrParent.GetDisplay() &&
+ pSrcGraphics->m_nXScreen == mrParent.m_nXScreen &&
+ pSrcGraphics->GetVisual().GetDepth() == mrParent.GetVisual().GetDepth()
+ )
+ n = 2; // same Display
+ else
+ n = 1; // printer or other display
+ }
+ else if( pSrcGraphics->bVirDev_ )
+ {
+ n = 1; // window or compatible virtual device
+ }
+ else
+ n = 0;
+
+ if( n == 2
+ && rPosAry.mnSrcWidth == rPosAry.mnDestWidth
+ && rPosAry.mnSrcHeight == rPosAry.mnDestHeight
+ )
+ {
+ // #i60699# Need to generate graphics exposures (to repaint
+ // obscured areas beneath overlapping windows), src and dest
+ // are the same window.
+ const bool bNeedGraphicsExposures( pSrcGraphics == &mrParent &&
+ !mrParent.bVirDev_ &&
+ pSrcGraphics->bWindow_ );
+
+ GC pCopyGC = GetCopyGC();
+
+ if( bNeedGraphicsExposures )
+ XSetGraphicsExposures( mrParent.GetXDisplay(),
+ pCopyGC,
+ True );
+
+ XCopyArea( mrParent.GetXDisplay(),
+ pSrcGraphics->GetDrawable(), // source
+ mrParent.GetDrawable(), // destination
+ pCopyGC, // destination clipping
+ rPosAry.mnSrcX, rPosAry.mnSrcY,
+ rPosAry.mnSrcWidth, rPosAry.mnSrcHeight,
+ rPosAry.mnDestX, rPosAry.mnDestY );
+
+ if( bNeedGraphicsExposures )
+ {
+ mrParent.YieldGraphicsExpose();
+
+ if( pCopyGC )
+ XSetGraphicsExposures( mrParent.GetXDisplay(),
+ pCopyGC,
+ False );
+ }
+ }
+ else if( n )
+ {
+ // #i60699# No chance to handle graphics exposures - we copy
+ // to a temp bitmap first, into which no repaints are
+ // technically possible.
+ std::shared_ptr<SalBitmap> xDDB(pSrcGraphics->getBitmap( rPosAry.mnSrcX,
+ rPosAry.mnSrcY,
+ rPosAry.mnSrcWidth,
+ rPosAry.mnSrcHeight ));
+
+ if( !xDDB )
+ {
+ SAL_WARN( "vcl", "SalGraphics::CopyBits !pSrcGraphics->GetBitmap()" );
+ return;
+ }
+
+ SalTwoRect aPosAry( rPosAry );
+
+ aPosAry.mnSrcX = 0;
+ aPosAry.mnSrcY = 0;
+ drawBitmap( aPosAry, *xDDB );
+ }
+ else {
+ SAL_WARN( "vcl", "X11SalGraphicsImpl::CopyBits from Printer not yet implemented" );
+ }
+}
+
+void X11SalGraphicsImpl::copyArea ( tools::Long nDestX, tools::Long nDestY,
+ tools::Long nSrcX, tools::Long nSrcY,
+ tools::Long nSrcWidth, tools::Long nSrcHeight,
+ bool /*bWindowInvalidate*/)
+{
+ SalTwoRect aPosAry(nSrcX, nSrcY, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight);
+ copyBits(aPosAry, nullptr);
+}
+
+void X11SalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap )
+{
+ const SalDisplay* pSalDisp = mrParent.GetDisplay();
+ Display* pXDisp = pSalDisp->GetDisplay();
+ const Drawable aDrawable( mrParent.GetDrawable() );
+ const SalColormap& rColMap = pSalDisp->GetColormap( mrParent.m_nXScreen );
+ const tools::Long nDepth = mrParent.GetDisplay()->GetVisual( mrParent.m_nXScreen ).GetDepth();
+ GC aGC( GetCopyGC() );
+ XGCValues aOldVal, aNewVal;
+ int nValues = GCForeground | GCBackground;
+
+ if( rSalBitmap.GetBitCount() == 1 )
+ {
+ // set foreground/background values for 1Bit bitmaps
+ XGetGCValues( pXDisp, aGC, nValues, &aOldVal );
+ setForeBack(aNewVal, rColMap, rSalBitmap);
+ XChangeGC( pXDisp, aGC, nValues, &aNewVal );
+ }
+
+ static_cast<const X11SalBitmap&>(rSalBitmap).ImplDraw( aDrawable, mrParent.m_nXScreen, nDepth, rPosAry, aGC );
+
+ if( rSalBitmap.GetBitCount() == 1 )
+ XChangeGC( pXDisp, aGC, nValues, &aOldVal );
+ XFlush( pXDisp );
+}
+
+void X11SalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap& rMaskBitmap )
+{
+ // decide if alpha masking or transparency masking is needed
+ BitmapBuffer* pAlphaBuffer = const_cast<SalBitmap&>(rMaskBitmap).AcquireBuffer( BitmapAccessMode::Read );
+ if( pAlphaBuffer != nullptr )
+ {
+ ScanlineFormat nMaskFormat = pAlphaBuffer->mnFormat;
+ const_cast<SalBitmap&>(rMaskBitmap).ReleaseBuffer( pAlphaBuffer, BitmapAccessMode::Read );
+ if( nMaskFormat == ScanlineFormat::N8BitPal )
+ drawAlphaBitmap( rPosAry, rSrcBitmap, rMaskBitmap );
+ }
+
+ drawMaskedBitmap( rPosAry, rSrcBitmap, rMaskBitmap );
+}
+
+void X11SalGraphicsImpl::drawMaskedBitmap( const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ const SalBitmap& rTransBitmap )
+{
+ const SalDisplay* pSalDisp = mrParent.GetDisplay();
+ Display* pXDisp = pSalDisp->GetDisplay();
+ Drawable aDrawable( mrParent.GetDrawable() );
+
+ // figure work mode depth. If this is a VDev Drawable, use its
+ // bitdepth to create pixmaps for, otherwise, XCopyArea will
+ // refuse to work.
+ const sal_uInt16 nDepth( mrParent.m_pVDev ?
+ static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetDepth() :
+ pSalDisp->GetVisual( mrParent.m_nXScreen ).GetDepth() );
+ Pixmap aFG( limitXCreatePixmap( pXDisp, aDrawable, rPosAry.mnDestWidth,
+ rPosAry.mnDestHeight, nDepth ) );
+ Pixmap aBG( limitXCreatePixmap( pXDisp, aDrawable, rPosAry.mnDestWidth,
+ rPosAry.mnDestHeight, nDepth ) );
+
+ if( aFG && aBG )
+ {
+ GC aTmpGC;
+ XGCValues aValues;
+ setForeBack(aValues, pSalDisp->GetColormap(mrParent.m_nXScreen), rSalBitmap);
+ const int nValues = GCFunction | GCForeground | GCBackground;
+ SalTwoRect aTmpRect( rPosAry ); aTmpRect.mnDestX = aTmpRect.mnDestY = 0;
+
+ // draw paint bitmap in pixmap #1
+ aValues.function = GXcopy;
+ aTmpGC = XCreateGC( pXDisp, aFG, nValues, &aValues );
+ static_cast<const X11SalBitmap&>(rSalBitmap).ImplDraw( aFG, mrParent.m_nXScreen, nDepth, aTmpRect, aTmpGC );
+ DBG_TESTTRANS( aFG );
+
+ // draw background in pixmap #2
+ XCopyArea( pXDisp, aDrawable, aBG, aTmpGC,
+ rPosAry.mnDestX, rPosAry.mnDestY,
+ rPosAry.mnDestWidth, rPosAry.mnDestHeight,
+ 0, 0 );
+
+ DBG_TESTTRANS( aBG );
+
+ // mask out paint bitmap in pixmap #1 (transparent areas 0)
+ aValues.function = GXand;
+ aValues.foreground = 0x00000000;
+ aValues.background = 0xffffffff;
+ XChangeGC( pXDisp, aTmpGC, nValues, &aValues );
+ static_cast<const X11SalBitmap&>(rTransBitmap).ImplDraw( aFG, mrParent.m_nXScreen, 1, aTmpRect, aTmpGC );
+
+ DBG_TESTTRANS( aFG );
+
+ // #105055# For XOR mode, keep background behind bitmap intact
+ if( !mbXORMode )
+ {
+ // mask out background in pixmap #2 (nontransparent areas 0)
+ aValues.function = GXand;
+ aValues.foreground = 0xffffffff;
+ aValues.background = 0x00000000;
+ XChangeGC( pXDisp, aTmpGC, nValues, &aValues );
+ static_cast<const X11SalBitmap&>(rTransBitmap).ImplDraw( aBG, mrParent.m_nXScreen, 1, aTmpRect, aTmpGC );
+
+ DBG_TESTTRANS( aBG );
+ }
+
+ // merge pixmap #1 and pixmap #2 in pixmap #2
+ aValues.function = GXxor;
+ aValues.foreground = 0xffffffff;
+ aValues.background = 0x00000000;
+ XChangeGC( pXDisp, aTmpGC, nValues, &aValues );
+ XCopyArea( pXDisp, aFG, aBG, aTmpGC,
+ 0, 0,
+ rPosAry.mnDestWidth, rPosAry.mnDestHeight,
+ 0, 0 );
+ DBG_TESTTRANS( aBG );
+
+ // #105055# Disable XOR temporarily
+ bool bOldXORMode( mbXORMode );
+ mbXORMode = false;
+
+ // copy pixmap #2 (result) to background
+ XCopyArea( pXDisp, aBG, aDrawable, GetCopyGC(),
+ 0, 0,
+ rPosAry.mnDestWidth, rPosAry.mnDestHeight,
+ rPosAry.mnDestX, rPosAry.mnDestY );
+
+ DBG_TESTTRANS( aBG );
+
+ mbXORMode = bOldXORMode;
+
+ XFreeGC( pXDisp, aTmpGC );
+ XFlush( pXDisp );
+ }
+ else
+ drawBitmap( rPosAry, rSalBitmap );
+
+ if( aFG )
+ XFreePixmap( pXDisp, aFG );
+
+ if( aBG )
+ XFreePixmap( pXDisp, aBG );
+}
+
+bool X11SalGraphicsImpl::blendBitmap( const SalTwoRect&,
+ const SalBitmap& )
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::blendAlphaBitmap( const SalTwoRect&,
+ const SalBitmap&, const SalBitmap&, const SalBitmap& )
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::drawAlphaBitmap( const SalTwoRect& rTR,
+ const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp )
+{
+ // non 8-bit alpha not implemented yet
+ if( rAlphaBmp.GetBitCount() != 8 )
+ return false;
+ // #i75531# the workaround below can go when
+ // X11SalGraphics::drawAlphaBitmap()'s render acceleration
+ // can handle the bitmap depth mismatch directly
+ if( rSrcBitmap.GetBitCount() < rAlphaBmp.GetBitCount() )
+ return false;
+
+ // horizontal mirroring not implemented yet
+ if( rTR.mnDestWidth < 0 )
+ return false;
+
+ // stretched conversion is not implemented yet
+ if( rTR.mnDestWidth != rTR.mnSrcWidth )
+ return false;
+ if( rTR.mnDestHeight!= rTR.mnSrcHeight )
+ return false;
+
+ // create destination picture
+ Picture aDstPic = GetXRenderPicture();
+ if( !aDstPic )
+ return false;
+
+ const SalDisplay* pSalDisp = mrParent.GetDisplay();
+ const SalVisual& rSalVis = pSalDisp->GetVisual( mrParent.m_nXScreen );
+ Display* pXDisplay = pSalDisp->GetDisplay();
+
+ // create source Picture
+ int nDepth = mrParent.m_pVDev ? static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetDepth() : rSalVis.GetDepth();
+ const X11SalBitmap& rSrcX11Bmp = static_cast<const X11SalBitmap&>( rSrcBitmap );
+ ImplSalDDB* pSrcDDB = rSrcX11Bmp.ImplGetDDB( mrParent.GetDrawable(), mrParent.m_nXScreen, nDepth, rTR );
+ if( !pSrcDDB )
+ return false;
+
+ //#i75249# workaround for ImplGetDDB() giving us back a different depth than
+ // we requested. E.g. mask pixmaps are always compatible with the drawable
+ // TODO: find an appropriate picture format for these cases
+ // then remove the workaround below and the one for #i75531#
+ if( nDepth != pSrcDDB->ImplGetDepth() )
+ return false;
+
+ Pixmap aSrcPM = pSrcDDB->ImplGetPixmap();
+ if( !aSrcPM )
+ return false;
+
+ // create source picture
+ // TODO: use scoped picture
+ Visual* pSrcXVisual = rSalVis.GetVisual();
+ XRenderPeer& rPeer = XRenderPeer::GetInstance();
+ XRenderPictFormat* pSrcVisFmt = rPeer.FindVisualFormat( pSrcXVisual );
+ if( !pSrcVisFmt )
+ return false;
+ Picture aSrcPic = rPeer.CreatePicture( aSrcPM, pSrcVisFmt, 0, nullptr );
+ if( !aSrcPic )
+ return false;
+
+ // create alpha Picture
+
+ // TODO: use SalX11Bitmap functionality and caching for the Alpha Pixmap
+ // problem is that they don't provide an 8bit Pixmap on a non-8bit display
+ BitmapBuffer* pAlphaBuffer = const_cast<SalBitmap&>(rAlphaBmp).AcquireBuffer( BitmapAccessMode::Read );
+
+ // an XImage needs its data top_down
+ // TODO: avoid wrongly oriented images in upper layers!
+ const int nImageSize = pAlphaBuffer->mnHeight * pAlphaBuffer->mnScanlineSize;
+ const char* pSrcBits = reinterpret_cast<char*>(pAlphaBuffer->mpBits);
+ char* pAlphaBits = new char[ nImageSize ];
+ if( pAlphaBuffer->mnFormat & ScanlineFormat::TopDown )
+ memcpy( pAlphaBits, pSrcBits, nImageSize );
+ else
+ {
+ char* pDstBits = pAlphaBits + nImageSize;
+ const int nLineSize = pAlphaBuffer->mnScanlineSize;
+ for(; (pDstBits -= nLineSize) >= pAlphaBits; pSrcBits += nLineSize )
+ memcpy( pDstBits, pSrcBits, nLineSize );
+ }
+
+ // the alpha values need to be inverted for XRender
+ // TODO: make upper layers use standard alpha
+ tools::Long* pLDst = reinterpret_cast<long*>(pAlphaBits);
+ for( int i = nImageSize/sizeof(long); --i >= 0; ++pLDst )
+ *pLDst = ~*pLDst;
+
+ char* pCDst = reinterpret_cast<char*>(pLDst);
+ for( int i = nImageSize & (sizeof(long)-1); --i >= 0; ++pCDst )
+ *pCDst = ~*pCDst;
+
+ const XRenderPictFormat* pAlphaFormat = rPeer.GetStandardFormatA8();
+ XImage* pAlphaImg = XCreateImage( pXDisplay, pSrcXVisual, 8, ZPixmap, 0,
+ pAlphaBits, pAlphaBuffer->mnWidth, pAlphaBuffer->mnHeight,
+ pAlphaFormat->depth, pAlphaBuffer->mnScanlineSize );
+
+ Pixmap aAlphaPM = limitXCreatePixmap( pXDisplay, mrParent.GetDrawable(),
+ rTR.mnDestWidth, rTR.mnDestHeight, 8 );
+
+ XGCValues aAlphaGCV;
+ aAlphaGCV.function = GXcopy;
+ GC aAlphaGC = XCreateGC( pXDisplay, aAlphaPM, GCFunction, &aAlphaGCV );
+ XPutImage( pXDisplay, aAlphaPM, aAlphaGC, pAlphaImg,
+ rTR.mnSrcX, rTR.mnSrcY, 0, 0, rTR.mnDestWidth, rTR.mnDestHeight );
+ XFreeGC( pXDisplay, aAlphaGC );
+ XFree( pAlphaImg );
+ if( pAlphaBits != reinterpret_cast<char*>(pAlphaBuffer->mpBits) )
+ delete[] pAlphaBits;
+
+ const_cast<SalBitmap&>(rAlphaBmp).ReleaseBuffer( pAlphaBuffer, BitmapAccessMode::Read );
+
+ XRenderPictureAttributes aAttr;
+ aAttr.repeat = int(true);
+ Picture aAlphaPic = rPeer.CreatePicture( aAlphaPM, pAlphaFormat, CPRepeat, &aAttr );
+ if( !aAlphaPic )
+ return false;
+
+ // set clipping
+ if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) )
+ rPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion );
+
+ // paint source * mask over destination picture
+ rPeer.CompositePicture( PictOpOver, aSrcPic, aAlphaPic, aDstPic,
+ rTR.mnSrcX, rTR.mnSrcY,
+ rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight );
+
+ rPeer.FreePicture( aAlphaPic );
+ XFreePixmap(pXDisplay, aAlphaPM);
+ rPeer.FreePicture( aSrcPic );
+ return true;
+}
+
+bool X11SalGraphicsImpl::drawTransformedBitmap(
+ const basegfx::B2DPoint&,
+ const basegfx::B2DPoint&,
+ const basegfx::B2DPoint&,
+ const SalBitmap&,
+ const SalBitmap*,
+ double)
+{
+ // here direct support for transformed bitmaps can be implemented
+ return false;
+}
+
+bool X11SalGraphicsImpl::hasFastDrawTransformedBitmap() const
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::drawAlphaRect( tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::Long nHeight, sal_uInt8 nTransparency )
+{
+ if( ! mrParent.m_pFrame && ! mrParent.m_pVDev )
+ return false;
+
+ if( mbPenGC || !mbBrushGC || mbXORMode )
+ return false; // can only perform solid fills without XOR.
+
+ if( mrParent.m_pVDev && static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetDepth() < 8 )
+ return false;
+
+ Picture aDstPic = GetXRenderPicture();
+ if( !aDstPic )
+ return false;
+
+ const double fTransparency = (100 - nTransparency) * (1.0/100);
+ const XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency);
+
+ XRenderPeer& rPeer = XRenderPeer::GetInstance();
+ rPeer.FillRectangle( PictOpOver,
+ aDstPic,
+ &aRenderColor,
+ nX, nY,
+ nWidth, nHeight );
+
+ return true;
+}
+
+void X11SalGraphicsImpl::drawMask( const SalTwoRect& rPosAry,
+ const SalBitmap &rSalBitmap,
+ Color nMaskColor )
+{
+ const SalDisplay* pSalDisp = mrParent.GetDisplay();
+ Display* pXDisp = pSalDisp->GetDisplay();
+ Drawable aDrawable( mrParent.GetDrawable() );
+ Pixmap aStipple( limitXCreatePixmap( pXDisp, aDrawable,
+ rPosAry.mnDestWidth,
+ rPosAry.mnDestHeight, 1 ) );
+
+ if( aStipple )
+ {
+ SalTwoRect aTwoRect( rPosAry ); aTwoRect.mnDestX = aTwoRect.mnDestY = 0;
+ GC aTmpGC;
+ XGCValues aValues;
+
+ // create a stipple bitmap first (set bits are changed to unset bits and vice versa)
+ aValues.function = GXcopyInverted;
+ aValues.foreground = 1;
+ aValues.background = 0;
+ aTmpGC = XCreateGC( pXDisp, aStipple, GCFunction | GCForeground | GCBackground, &aValues );
+ static_cast<const X11SalBitmap&>(rSalBitmap).ImplDraw( aStipple, mrParent.m_nXScreen, 1, aTwoRect, aTmpGC );
+
+ XFreeGC( pXDisp, aTmpGC );
+
+ // Set stipple and draw rectangle
+ GC aStippleGC( GetStippleGC() );
+ int nX = rPosAry.mnDestX, nY = rPosAry.mnDestY;
+
+ XSetStipple( pXDisp, aStippleGC, aStipple );
+ XSetTSOrigin( pXDisp, aStippleGC, nX, nY );
+ XSetForeground( pXDisp, aStippleGC, mrParent.GetPixel( nMaskColor ) );
+ XFillRectangle( pXDisp, aDrawable, aStippleGC,
+ nX, nY,
+ rPosAry.mnDestWidth, rPosAry.mnDestHeight );
+ XFreePixmap( pXDisp, aStipple );
+ XFlush( pXDisp );
+ }
+ else
+ drawBitmap( rPosAry, rSalBitmap );
+}
+
+void X11SalGraphicsImpl::ResetClipRegion()
+{
+ if( !mrParent.mpClipRegion )
+ return;
+
+ mbPenGC = false;
+ mbBrushGC = false;
+ mbCopyGC = false;
+ mbInvertGC = false;
+ mbInvert50GC = false;
+ mbStippleGC = false;
+ mbTrackingGC = false;
+
+ XDestroyRegion( mrParent.mpClipRegion );
+ mrParent.mpClipRegion = nullptr;
+}
+
+bool X11SalGraphicsImpl::setClipRegion( const vcl::Region& i_rClip )
+{
+ if( mrParent.mpClipRegion )
+ XDestroyRegion( mrParent.mpClipRegion );
+ mrParent.mpClipRegion = XCreateRegion();
+
+ RectangleVector aRectangles;
+ i_rClip.GetRegionRectangles(aRectangles);
+
+ for (auto const& rectangle : aRectangles)
+ {
+ const tools::Long nW(rectangle.GetWidth());
+
+ if(nW)
+ {
+ const tools::Long nH(rectangle.GetHeight());
+
+ if(nH)
+ {
+ XRectangle aRect;
+
+ aRect.x = static_cast<short>(rectangle.Left());
+ aRect.y = static_cast<short>(rectangle.Top());
+ aRect.width = static_cast<unsigned short>(nW);
+ aRect.height = static_cast<unsigned short>(nH);
+ XUnionRectWithRegion(&aRect, mrParent.mpClipRegion, mrParent.mpClipRegion);
+ }
+ }
+ }
+
+ //ImplRegionInfo aInfo;
+ //long nX, nY, nW, nH;
+ //bool bRegionRect = i_rClip.ImplGetFirstRect(aInfo, nX, nY, nW, nH );
+ //while( bRegionRect )
+ //{
+ // if ( nW && nH )
+ // {
+ // XRectangle aRect;
+ // aRect.x = (short)nX;
+ // aRect.y = (short)nY;
+ // aRect.width = (unsigned short)nW;
+ // aRect.height = (unsigned short)nH;
+
+ // XUnionRectWithRegion( &aRect, mrParent.mpClipRegion, mrParent.mpClipRegion );
+ // }
+ // bRegionRect = i_rClip.ImplGetNextRect( aInfo, nX, nY, nW, nH );
+ //}
+
+ // done, invalidate GCs
+ mbPenGC = false;
+ mbBrushGC = false;
+ mbCopyGC = false;
+ mbInvertGC = false;
+ mbInvert50GC = false;
+ mbStippleGC = false;
+ mbTrackingGC = false;
+
+ if( XEmptyRegion( mrParent.mpClipRegion ) )
+ {
+ XDestroyRegion( mrParent.mpClipRegion );
+ mrParent.mpClipRegion= nullptr;
+ }
+ return true;
+}
+
+void X11SalGraphicsImpl::SetLineColor()
+{
+ if( mnPenColor != SALCOLOR_NONE )
+ {
+ mnPenColor = SALCOLOR_NONE;
+ mbPenGC = false;
+ }
+}
+
+void X11SalGraphicsImpl::SetLineColor( Color nColor )
+{
+ if( mnPenColor != nColor )
+ {
+ mnPenColor = nColor;
+ mnPenPixel = mrParent.GetPixel( nColor );
+ mbPenGC = false;
+ }
+}
+
+void X11SalGraphicsImpl::SetFillColor()
+{
+ if( mnBrushColor != SALCOLOR_NONE )
+ {
+ mbDitherBrush = false;
+ mnBrushColor = SALCOLOR_NONE;
+ mbBrushGC = false;
+ }
+}
+
+void X11SalGraphicsImpl::SetFillColor( Color nColor )
+{
+ if( mnBrushColor == nColor )
+ return;
+
+ mbDitherBrush = false;
+ mnBrushColor = nColor;
+ mnBrushPixel = mrParent.GetPixel( nColor );
+ if( TrueColor != mrParent.GetColormap().GetVisual().GetClass()
+ && mrParent.GetColormap().GetColor( mnBrushPixel ) != mnBrushColor
+ && nColor != Color( 0x00, 0x00, 0x00 ) // black
+ && nColor != Color( 0x00, 0x00, 0x80 ) // blue
+ && nColor != Color( 0x00, 0x80, 0x00 ) // green
+ && nColor != Color( 0x00, 0x80, 0x80 ) // cyan
+ && nColor != Color( 0x80, 0x00, 0x00 ) // red
+ && nColor != Color( 0x80, 0x00, 0x80 ) // magenta
+ && nColor != Color( 0x80, 0x80, 0x00 ) // brown
+ && nColor != Color( 0x80, 0x80, 0x80 ) // gray
+ && nColor != Color( 0xC0, 0xC0, 0xC0 ) // light gray
+ && nColor != Color( 0x00, 0x00, 0xFF ) // light blue
+ && nColor != Color( 0x00, 0xFF, 0x00 ) // light green
+ && nColor != Color( 0x00, 0xFF, 0xFF ) // light cyan
+ && nColor != Color( 0xFF, 0x00, 0x00 ) // light red
+ && nColor != Color( 0xFF, 0x00, 0xFF ) // light magenta
+ && nColor != Color( 0xFF, 0xFF, 0x00 ) // light brown
+ && nColor != Color( 0xFF, 0xFF, 0xFF ) )
+ mbDitherBrush = mrParent.GetDitherPixmap(nColor);
+ mbBrushGC = false;
+}
+
+void X11SalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor )
+{
+ switch( nROPColor )
+ {
+ case SalROPColor::N0 : // 0
+ mnPenPixel = Pixel(0);
+ break;
+ case SalROPColor::N1 : // 1
+ mnPenPixel = static_cast<Pixel>(1 << mrParent.GetVisual().GetDepth()) - 1;
+ break;
+ case SalROPColor::Invert : // 2
+ mnPenPixel = static_cast<Pixel>(1 << mrParent.GetVisual().GetDepth()) - 1;
+ break;
+ }
+ mnPenColor = mrParent.GetColormap().GetColor( mnPenPixel );
+ mbPenGC = false;
+}
+
+void X11SalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor )
+{
+ switch( nROPColor )
+ {
+ case SalROPColor::N0 : // 0
+ mnBrushPixel = Pixel(0);
+ break;
+ case SalROPColor::N1 : // 1
+ mnBrushPixel = static_cast<Pixel>(1 << mrParent.GetVisual().GetDepth()) - 1;
+ break;
+ case SalROPColor::Invert : // 2
+ mnBrushPixel = static_cast<Pixel>(1 << mrParent.GetVisual().GetDepth()) - 1;
+ break;
+ }
+ mbDitherBrush = false;
+ mnBrushColor = mrParent.GetColormap().GetColor( mnBrushPixel );
+ mbBrushGC = false;
+}
+
+void X11SalGraphicsImpl::SetXORMode( bool bSet, bool )
+{
+ if (mbXORMode != bSet)
+ {
+ mbXORMode = bSet;
+ mbPenGC = false;
+ mbBrushGC = false;
+ mbCopyGC = false;
+ mbInvertGC = false;
+ mbInvert50GC = false;
+ mbStippleGC = false;
+ mbTrackingGC = false;
+ }
+}
+
+void X11SalGraphicsImpl::drawPixel( tools::Long nX, tools::Long nY )
+{
+ if( mnPenColor != SALCOLOR_NONE )
+ XDrawPoint( mrParent.GetXDisplay(), mrParent.GetDrawable(), SelectPen(), nX, nY );
+}
+
+void X11SalGraphicsImpl::drawPixel( tools::Long nX, tools::Long nY, Color nColor )
+{
+ if( nColor == SALCOLOR_NONE )
+ return;
+
+ Display *pDisplay = mrParent.GetXDisplay();
+
+ if( (mnPenColor == SALCOLOR_NONE) && !mbPenGC )
+ {
+ SetLineColor( nColor );
+ XDrawPoint( pDisplay, mrParent.GetDrawable(), SelectPen(), nX, nY );
+ mnPenColor = SALCOLOR_NONE;
+ mbPenGC = False;
+ }
+ else
+ {
+ GC pGC = SelectPen();
+
+ if( nColor != mnPenColor )
+ XSetForeground( pDisplay, pGC, mrParent.GetPixel( nColor ) );
+
+ XDrawPoint( pDisplay, mrParent.GetDrawable(), pGC, nX, nY );
+
+ if( nColor != mnPenColor )
+ XSetForeground( pDisplay, pGC, mnPenPixel );
+ }
+}
+
+void X11SalGraphicsImpl::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 )
+{
+ if( mnPenColor != SALCOLOR_NONE )
+ {
+ XDrawLine( mrParent.GetXDisplay(), mrParent.GetDrawable(),SelectPen(),
+ nX1, nY1, nX2, nY2 );
+ }
+}
+
+void X11SalGraphicsImpl::drawRect( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY )
+{
+ if( mnBrushColor != SALCOLOR_NONE )
+ {
+ XFillRectangle( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ SelectBrush(),
+ nX, nY, nDX, nDY );
+ }
+ // description DrawRect is wrong; thus -1
+ if( mnPenColor != SALCOLOR_NONE )
+ XDrawRectangle( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ SelectPen(),
+ nX, nY, nDX-1, nDY-1 );
+}
+
+void X11SalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const Point *pPtAry )
+{
+ internalDrawPolyLine( nPoints, pPtAry, false );
+}
+
+void X11SalGraphicsImpl::internalDrawPolyLine( sal_uInt32 nPoints, const Point *pPtAry, bool bClose )
+{
+ if( mnPenColor != SALCOLOR_NONE )
+ {
+ SalPolyLine Points( nPoints, pPtAry );
+
+ DrawLines( nPoints, Points, SelectPen(), bClose );
+ }
+}
+
+void X11SalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const Point* pPtAry )
+{
+ if( nPoints == 0 )
+ return;
+
+ if( nPoints < 3 )
+ {
+ if( !mbXORMode )
+ {
+ if( 1 == nPoints )
+ drawPixel( pPtAry[0].getX(), pPtAry[0].getY() );
+ else
+ drawLine( pPtAry[0].getX(), pPtAry[0].getY(),
+ pPtAry[1].getX(), pPtAry[1].getY() );
+ }
+ return;
+ }
+
+ SalPolyLine Points( nPoints, pPtAry );
+
+ nPoints++;
+
+ /* WORKAROUND: some Xservers (Xorg, VIA chipset in this case)
+ * do not draw the visible part of a polygon
+ * if it overlaps to the left of screen 0,y.
+ * This happens to be the case in the gradient drawn in the
+ * menubar background. workaround for the special case of
+ * of a rectangle overlapping to the left.
+ */
+ if (nPoints == 5 &&
+ Points[ 0 ].x == Points[ 1 ].x &&
+ Points[ 1 ].y == Points[ 2 ].y &&
+ Points[ 2 ].x == Points[ 3 ].x &&
+ Points[ 0 ].x == Points[ 4 ].x && Points[ 0 ].y == Points[ 4 ].y
+ )
+ {
+ bool bLeft = false;
+ bool bRight = false;
+ for(unsigned int i = 0; i < nPoints; i++ )
+ {
+ if( Points[i].x < 0 )
+ bLeft = true;
+ else
+ bRight= true;
+ }
+ if( bLeft && ! bRight )
+ return;
+ if( bLeft && bRight )
+ {
+ for( unsigned int i = 0; i < nPoints; i++ )
+ if( Points[i].x < 0 )
+ Points[i].x = 0;
+ }
+ }
+
+ if( mnBrushColor != SALCOLOR_NONE )
+ XFillPolygon( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ SelectBrush(),
+ &Points[0], nPoints,
+ Complex, CoordModeOrigin );
+
+ if( mnPenColor != SALCOLOR_NONE )
+ DrawLines( nPoints, Points, SelectPen(), true );
+}
+
+void X11SalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly,
+ const sal_uInt32 *pPoints,
+ const Point* *pPtAry )
+{
+ if( mnBrushColor != SALCOLOR_NONE )
+ {
+ sal_uInt32 i, n;
+ Region pXRegA = nullptr;
+
+ for( i = 0; i < nPoly; i++ ) {
+ n = pPoints[i];
+ SalPolyLine Points( n, pPtAry[i] );
+ if( n > 2 )
+ {
+ Region pXRegB = XPolygonRegion( &Points[0], n+1, WindingRule );
+ if( !pXRegA )
+ pXRegA = pXRegB;
+ else
+ {
+ XXorRegion( pXRegA, pXRegB, pXRegA );
+ XDestroyRegion( pXRegB );
+ }
+ }
+ }
+
+ if( pXRegA )
+ {
+ XRectangle aXRect;
+ XClipBox( pXRegA, &aXRect );
+
+ GC pGC = SelectBrush();
+ mrParent.SetClipRegion( pGC, pXRegA ); // ??? twice
+ XDestroyRegion( pXRegA );
+ mbBrushGC = false;
+
+ XFillRectangle( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ pGC,
+ aXRect.x, aXRect.y, aXRect.width, aXRect.height );
+ }
+ }
+
+ if( mnPenColor != SALCOLOR_NONE )
+ for( sal_uInt32 i = 0; i < nPoly; i++ )
+ internalDrawPolyLine( pPoints[i], pPtAry[i], true );
+}
+
+bool X11SalGraphicsImpl::drawPolyLineBezier( sal_uInt32, const Point*, const PolyFlags* )
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::drawPolygonBezier( sal_uInt32, const Point*, const PolyFlags* )
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32, const sal_uInt32*,
+ const Point* const*, const PolyFlags* const* )
+{
+ return false;
+}
+
+void X11SalGraphicsImpl::invert( tools::Long nX,
+ tools::Long nY,
+ tools::Long nDX,
+ tools::Long nDY,
+ SalInvert nFlags )
+{
+ GC pGC;
+ if( SalInvert::N50 & nFlags )
+ {
+ pGC = GetInvert50GC();
+ XFillRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, nX, nY, nDX, nDY );
+ }
+ else
+ {
+ if ( SalInvert::TrackFrame & nFlags )
+ {
+ pGC = GetTrackingGC();
+ XDrawRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, nX, nY, nDX, nDY );
+ }
+ else
+ {
+ pGC = GetInvertGC();
+ XFillRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, nX, nY, nDX, nDY );
+ }
+ }
+}
+
+void X11SalGraphicsImpl::invert( sal_uInt32 nPoints,
+ const Point* pPtAry,
+ SalInvert nFlags )
+{
+ SalPolyLine Points ( nPoints, pPtAry );
+
+ GC pGC;
+ if( SalInvert::N50 & nFlags )
+ pGC = GetInvert50GC();
+ else
+ if ( SalInvert::TrackFrame & nFlags )
+ pGC = GetTrackingGC();
+ else
+ pGC = GetInvertGC();
+
+ if( SalInvert::TrackFrame & nFlags )
+ DrawLines ( nPoints, Points, pGC, true );
+ else
+ XFillPolygon( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ pGC,
+ &Points[0], nPoints,
+ Complex, CoordModeOrigin );
+}
+
+bool X11SalGraphicsImpl::drawEPS( tools::Long,tools::Long,tools::Long,tools::Long,void*,sal_uInt32 )
+{
+ return false;
+}
+
+// draw a poly-polygon
+bool X11SalGraphicsImpl::drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ // nothing to do for empty polypolygons
+ const int nOrigPolyCount = rPolyPolygon.count();
+ if( nOrigPolyCount <= 0 )
+ return true;
+
+ // nothing to do if everything is transparent
+ if( (mnBrushColor == SALCOLOR_NONE)
+ && (mnPenColor == SALCOLOR_NONE) )
+ return true;
+
+ // cannot handle pencolor!=brushcolor yet
+ if( (mnPenColor != SALCOLOR_NONE)
+ && (mnPenColor != mnBrushColor) )
+ return false;
+
+ // TODO: remove the env-variable when no longer needed
+ static const char* pRenderEnv = getenv( "SAL_DISABLE_RENDER_POLY" );
+ if( pRenderEnv )
+ return false;
+
+ // Fallback: Transform to DeviceCoordinates
+ basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
+ aPolyPolygon.transform(rObjectToDevice);
+
+ // snap to raster if requested
+ const bool bSnapToRaster = !mrParent.getAntiAlias();
+ if( bSnapToRaster )
+ aPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges( aPolyPolygon );
+
+ // don't bother with polygons outside of visible area
+ const basegfx::B2DRange aViewRange( 0, 0, GetGraphicsWidth(), GetGraphicsHeight() );
+ aPolyPolygon = basegfx::utils::clipPolyPolygonOnRange( aPolyPolygon, aViewRange, true, false );
+ if( !aPolyPolygon.count() )
+ return true;
+
+ // tessellate the polypolygon into trapezoids
+ basegfx::B2DTrapezoidVector aB2DTrapVector;
+ basegfx::utils::trapezoidSubdivide( aB2DTrapVector, aPolyPolygon );
+ const int nTrapCount = aB2DTrapVector.size();
+ if( !nTrapCount )
+ return true;
+ const bool bDrawn = drawFilledTrapezoids( aB2DTrapVector.data(), nTrapCount, fTransparency );
+ return bDrawn;
+}
+
+tools::Long X11SalGraphicsImpl::GetGraphicsHeight() const
+{
+ if( mrParent.m_pFrame )
+ return mrParent.m_pFrame->maGeometry.nHeight;
+ else if( mrParent.m_pVDev )
+ return static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetHeight();
+ else
+ return 0;
+}
+
+bool X11SalGraphicsImpl::drawFilledTrapezoids( const basegfx::B2DTrapezoid* pB2DTraps, int nTrapCount, double fTransparency )
+{
+ if( nTrapCount <= 0 )
+ return true;
+
+ Picture aDstPic = GetXRenderPicture();
+ // check xrender support for this drawable
+ if( !aDstPic )
+ return false;
+
+ // convert the B2DTrapezoids into XRender-Trapezoids
+ std::vector<XTrapezoid> aTrapVector( nTrapCount );
+ const basegfx::B2DTrapezoid* pB2DTrap = pB2DTraps;
+ for( int i = 0; i < nTrapCount; ++pB2DTrap, ++i )
+ {
+ XTrapezoid& rTrap = aTrapVector[ i ] ;
+
+ // set y-coordinates
+ const double fY1 = pB2DTrap->getTopY();
+ rTrap.left.p1.y = rTrap.right.p1.y = rTrap.top = XDoubleToFixed( fY1 );
+ const double fY2 = pB2DTrap->getBottomY();
+ rTrap.left.p2.y = rTrap.right.p2.y = rTrap.bottom = XDoubleToFixed( fY2 );
+
+ // set x-coordinates
+ const double fXL1 = pB2DTrap->getTopXLeft();
+ rTrap.left.p1.x = XDoubleToFixed( fXL1 );
+ const double fXR1 = pB2DTrap->getTopXRight();
+ rTrap.right.p1.x = XDoubleToFixed( fXR1 );
+ const double fXL2 = pB2DTrap->getBottomXLeft();
+ rTrap.left.p2.x = XDoubleToFixed( fXL2 );
+ const double fXR2 = pB2DTrap->getBottomXRight();
+ rTrap.right.p2.x = XDoubleToFixed( fXR2 );
+ }
+
+ // get xrender Picture for polygon foreground
+ // TODO: cache it like the target picture which uses GetXRenderPicture()
+ XRenderPeer& rRenderPeer = XRenderPeer::GetInstance();
+ SalDisplay::RenderEntry& rEntry = mrParent.GetDisplay()->GetRenderEntries( mrParent.m_nXScreen )[ 32 ];
+ if( !rEntry.m_aPicture )
+ {
+ Display* pXDisplay = mrParent.GetXDisplay();
+
+ rEntry.m_aPixmap = limitXCreatePixmap( pXDisplay, mrParent.GetDrawable(), 1, 1, 32 );
+ XRenderPictureAttributes aAttr;
+ aAttr.repeat = int(true);
+
+ XRenderPictFormat* pXRPF = rRenderPeer.FindStandardFormat( PictStandardARGB32 );
+ rEntry.m_aPicture = rRenderPeer.CreatePicture( rEntry.m_aPixmap, pXRPF, CPRepeat, &aAttr );
+ }
+
+ // set polygon foreground color and opacity
+ XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency );
+ rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 );
+
+ // set clipping
+ // TODO: move into GetXRenderPicture?
+ if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) )
+ rRenderPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion );
+
+ // render the trapezoids
+ const XRenderPictFormat* pMaskFormat = rRenderPeer.GetStandardFormatA8();
+ rRenderPeer.CompositeTrapezoids( PictOpOver,
+ rEntry.m_aPicture, aDstPic, pMaskFormat, 0, 0, aTrapVector.data(), aTrapVector.size() );
+
+ return true;
+}
+
+bool X11SalGraphicsImpl::drawFilledTriangles(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::triangulator::B2DTriangleVector& rTriangles,
+ double fTransparency)
+{
+ if(rTriangles.empty())
+ return true;
+
+ Picture aDstPic = GetXRenderPicture();
+ // check xrender support for this drawable
+ if( !aDstPic )
+ {
+ return false;
+ }
+
+ // prepare transformation for ObjectToDevice coordinate system
+ basegfx::B2DHomMatrix aObjectToDevice = basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) * rObjectToDevice;
+
+ // convert the Triangles into XRender-Triangles
+ std::vector<XTriangle> aTriVector(rTriangles.size());
+ sal_uInt32 nIndex(0);
+
+ for(const auto& rCandidate : rTriangles)
+ {
+ const basegfx::B2DPoint aP1(aObjectToDevice * rCandidate.getA());
+ const basegfx::B2DPoint aP2(aObjectToDevice * rCandidate.getB());
+ const basegfx::B2DPoint aP3(aObjectToDevice * rCandidate.getC());
+ XTriangle& rTri(aTriVector[nIndex++]);
+
+ rTri.p1.x = XDoubleToFixed(aP1.getX());
+ rTri.p1.y = XDoubleToFixed(aP1.getY());
+
+ rTri.p2.x = XDoubleToFixed(aP2.getX());
+ rTri.p2.y = XDoubleToFixed(aP2.getY());
+
+ rTri.p3.x = XDoubleToFixed(aP3.getX());
+ rTri.p3.y = XDoubleToFixed(aP3.getY());
+ }
+
+ // get xrender Picture for polygon foreground
+ // TODO: cache it like the target picture which uses GetXRenderPicture()
+ XRenderPeer& rRenderPeer = XRenderPeer::GetInstance();
+ SalDisplay::RenderEntry& rEntry = mrParent.GetDisplay()->GetRenderEntries( mrParent.m_nXScreen )[ 32 ];
+ if( !rEntry.m_aPicture )
+ {
+ Display* pXDisplay = mrParent.GetXDisplay();
+
+ rEntry.m_aPixmap = limitXCreatePixmap( pXDisplay, mrParent.GetDrawable(), 1, 1, 32 );
+ XRenderPictureAttributes aAttr;
+ aAttr.repeat = int(true);
+
+ XRenderPictFormat* pXRPF = rRenderPeer.FindStandardFormat( PictStandardARGB32 );
+ rEntry.m_aPicture = rRenderPeer.CreatePicture( rEntry.m_aPixmap, pXRPF, CPRepeat, &aAttr );
+ }
+
+ // set polygon foreground color and opacity
+ XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency );
+ rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 );
+
+ // set clipping
+ // TODO: move into GetXRenderPicture?
+ if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) )
+ rRenderPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion );
+
+ // render the trapezoids
+ const XRenderPictFormat* pMaskFormat = rRenderPeer.GetStandardFormatA8();
+ rRenderPeer.CompositeTriangles( PictOpOver,
+ rEntry.m_aPicture, aDstPic, pMaskFormat, 0, 0, aTriVector.data(), aTriVector.size() );
+
+ return true;
+}
+
+namespace {
+
+class SystemDependentData_Triangulation : public basegfx::SystemDependentData
+{
+private:
+ // the triangulation itself
+ basegfx::triangulator::B2DTriangleVector maTriangles;
+
+ // all other values the triangulation is based on and
+ // need to be compared with to check for data validity
+ double mfLineWidth;
+ basegfx::B2DLineJoin meJoin;
+ css::drawing::LineCap meCap;
+ double mfMiterMinimumAngle;
+ std::vector< double > maStroke;
+
+public:
+ SystemDependentData_Triangulation(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ basegfx::triangulator::B2DTriangleVector&& rTriangles,
+ double fLineWidth,
+ basegfx::B2DLineJoin eJoin,
+ css::drawing::LineCap eCap,
+ double fMiterMinimumAngle,
+ const std::vector< double >* pStroke); // MM01
+
+ // read access
+ const basegfx::triangulator::B2DTriangleVector& getTriangles() const { return maTriangles; }
+ double getLineWidth() const { return mfLineWidth; }
+ const basegfx::B2DLineJoin& getJoin() const { return meJoin; }
+ const css::drawing::LineCap& getCap() const { return meCap; }
+ double getMiterMinimumAngle() const { return mfMiterMinimumAngle; }
+ const std::vector< double >& getStroke() const { return maStroke; }
+
+ virtual sal_Int64 estimateUsageInBytes() const override;
+};
+
+}
+
+SystemDependentData_Triangulation::SystemDependentData_Triangulation(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ basegfx::triangulator::B2DTriangleVector&& rTriangles,
+ double fLineWidth,
+ basegfx::B2DLineJoin eJoin,
+ css::drawing::LineCap eCap,
+ double fMiterMinimumAngle,
+ const std::vector< double >* pStroke)
+: basegfx::SystemDependentData(rSystemDependentDataManager),
+ maTriangles(std::move(rTriangles)),
+ mfLineWidth(fLineWidth),
+ meJoin(eJoin),
+ meCap(eCap),
+ mfMiterMinimumAngle(fMiterMinimumAngle)
+{
+ if(nullptr != pStroke)
+ {
+ maStroke = *pStroke;
+ }
+}
+
+sal_Int64 SystemDependentData_Triangulation::estimateUsageInBytes() const
+{
+ sal_Int64 nRetval(0);
+
+ if(!maTriangles.empty())
+ {
+ nRetval = maTriangles.size() * sizeof(basegfx::triangulator::B2DTriangle);
+ }
+
+ return nRetval;
+}
+
+bool X11SalGraphicsImpl::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolygon,
+ double fTransparency,
+ double fLineWidth,
+ const std::vector< double >* pStroke, // MM01
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
+{
+ // short circuit if there is nothing to do
+ if(0 == rPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0)
+ {
+ return true;
+ }
+
+ // need to check/handle LineWidth when ObjectToDevice transformation is used
+ const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
+ basegfx::B2DHomMatrix aObjectToDeviceInv;
+
+ // tdf#124848 calculate-back logical LineWidth for a hairline.
+ // This implementation does not hand over the transformation to
+ // the graphic sub-system, but the triangulation data is prepared
+ // view-independent based on the logic LineWidth, so we need to
+ // know it
+ if(fLineWidth == 0)
+ {
+ fLineWidth = 1.0;
+
+ if(!bObjectToDeviceIsIdentity)
+ {
+ if(aObjectToDeviceInv.isIdentity())
+ {
+ aObjectToDeviceInv = rObjectToDevice;
+ aObjectToDeviceInv.invert();
+ }
+
+ fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
+ }
+ }
+
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_Triangulation> pSystemDependentData_Triangulation(
+ rPolygon.getSystemDependentData<SystemDependentData_Triangulation>());
+
+ // MM01 need to do line dashing as fallback stuff here now
+ const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
+ const bool bStrokeUsed(0.0 != fDotDashLength);
+ assert(!bStrokeUsed || (bStrokeUsed && pStroke));
+
+ if(pSystemDependentData_Triangulation)
+ {
+ // MM01 - check on stroke change. Used against not used, or if oth used,
+ // equal or different? Triangulation geometry creation depends heavily
+ // on stroke, independent of being transformation independent
+ const bool bStrokeWasUsed(!pSystemDependentData_Triangulation->getStroke().empty());
+
+ if(bStrokeWasUsed != bStrokeUsed
+ || (bStrokeUsed && *pStroke != pSystemDependentData_Triangulation->getStroke()))
+ {
+ // data invalid, forget
+ pSystemDependentData_Triangulation.reset();
+ }
+ }
+
+ if(pSystemDependentData_Triangulation)
+ {
+ // check data validity (I)
+ if(pSystemDependentData_Triangulation->getJoin() != eLineJoin
+ || pSystemDependentData_Triangulation->getCap() != eLineCap
+ || pSystemDependentData_Triangulation->getMiterMinimumAngle() != fMiterMinimumAngle)
+ {
+ // data invalid, forget
+ pSystemDependentData_Triangulation.reset();
+ }
+ }
+
+ if(pSystemDependentData_Triangulation)
+ {
+ // check data validity (II)
+ if(pSystemDependentData_Triangulation->getLineWidth() != fLineWidth)
+ {
+ // sometimes small inconsistencies, use a percentage tolerance
+ const double fFactor(basegfx::fTools::equalZero(fLineWidth)
+ ? 0.0
+ : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth() / fLineWidth)));
+ // compare with 5.0% tolerance
+ if(basegfx::fTools::more(fFactor, 0.05))
+ {
+ // data invalid, forget
+ pSystemDependentData_Triangulation.reset();
+ }
+ }
+ }
+
+ if(!pSystemDependentData_Triangulation)
+ {
+ // MM01 need to do line dashing as fallback stuff here now
+ basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+ if(bStrokeUsed)
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(
+ rPolygon, // source
+ *pStroke, // pattern
+ &aPolyPolygonLine, // target for lines
+ nullptr, // target for gaps
+ fDotDashLength); // full length if available
+ }
+ else
+ {
+ // no line dashing, just copy
+ aPolyPolygonLine.append(rPolygon);
+ }
+
+ // try to create data
+ if(bPixelSnapHairline)
+ {
+ // Do NOT transform, but keep device-independent. To
+ // do so, transform to device for snap, but back again after
+ if(!bObjectToDeviceIsIdentity)
+ {
+ aPolyPolygonLine.transform(rObjectToDevice);
+ }
+
+ aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
+
+ if(!bObjectToDeviceIsIdentity)
+ {
+ if(aObjectToDeviceInv.isIdentity())
+ {
+ aObjectToDeviceInv = rObjectToDevice;
+ aObjectToDeviceInv.invert();
+ }
+
+ aPolyPolygonLine.transform(aObjectToDeviceInv);
+ }
+ }
+
+ basegfx::triangulator::B2DTriangleVector aTriangles;
+
+ // MM01 checked/verified for X11 (linux)
+ for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+ {
+ const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+ // MM01 upps - commit 51b5b93092d6231615de470c62494c24e54828a1 removed
+ // this *central* geometry-creating lines (!) probably due to aAreaPolyPoly
+ // *not* being used - that's true, but the work is inside of filling
+ // aTriangles data (!)
+ basegfx::utils::createAreaGeometry(
+ aPolyLine,
+ 0.5 * fLineWidth,
+ eLineJoin,
+ eLineCap,
+ basegfx::deg2rad(12.5),
+ 0.4,
+ fMiterMinimumAngle,
+ &aTriangles); // CAUTION! This is *needed* since it creates the data!
+ }
+
+ if(!aTriangles.empty())
+ {
+ // Add to buffering mechanism
+ // Add all values the triangulation is based off, too, to check for
+ // validity (see above)
+ pSystemDependentData_Triangulation = rPolygon.addOrReplaceSystemDependentData<SystemDependentData_Triangulation>(
+ ImplGetSystemDependentDataManager(),
+ std::move(aTriangles),
+ fLineWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ pStroke);
+ }
+ }
+
+ if(!pSystemDependentData_Triangulation)
+ {
+ return false;
+ }
+
+ // temporarily adjust brush color to pen color
+ // since the line is drawn as an area-polygon
+ const Color aKeepBrushColor = mnBrushColor;
+ mnBrushColor = mnPenColor;
+
+ // create the area-polygon for the line
+ const bool bDrawnOk(
+ drawFilledTriangles(
+ rObjectToDevice,
+ pSystemDependentData_Triangulation->getTriangles(),
+ fTransparency));
+
+ // restore the original brush GC
+ mnBrushColor = aKeepBrushColor;
+ return bDrawnOk;
+}
+
+Color X11SalGraphicsImpl::getPixel( tools::Long nX, tools::Long nY )
+{
+ if( mrParent.bWindow_ && !mrParent.bVirDev_ )
+ {
+ XWindowAttributes aAttrib;
+
+ XGetWindowAttributes( mrParent.GetXDisplay(), mrParent.GetDrawable(), &aAttrib );
+ if( aAttrib.map_state != IsViewable )
+ {
+ SAL_WARN( "vcl", "X11SalGraphics::GetPixel drawable not viewable" );
+ return 0;
+ }
+ }
+
+ XImage *pXImage = XGetImage( mrParent.GetXDisplay(),
+ mrParent.GetDrawable(),
+ nX, nY,
+ 1, 1,
+ AllPlanes,
+ ZPixmap );
+ if( !pXImage )
+ {
+ SAL_WARN( "vcl", "X11SalGraphics::GetPixel !XGetImage()" );
+ return 0;
+ }
+
+ XColor aXColor;
+
+ aXColor.pixel = XGetPixel( pXImage, 0, 0 );
+ XDestroyImage( pXImage );
+
+ return mrParent.GetColormap().GetColor( aXColor.pixel );
+}
+
+std::shared_ptr<SalBitmap> X11SalGraphicsImpl::getBitmap( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY )
+{
+ bool bFakeWindowBG = false;
+
+ // normalize
+ if( nDX < 0 )
+ {
+ nX += nDX;
+ nDX = -nDX;
+ }
+ if ( nDY < 0 )
+ {
+ nY += nDY;
+ nDY = -nDY;
+ }
+
+ if( mrParent.bWindow_ && !mrParent.bVirDev_ )
+ {
+ XWindowAttributes aAttrib;
+
+ XGetWindowAttributes( mrParent.GetXDisplay(), mrParent.GetDrawable(), &aAttrib );
+ if( aAttrib.map_state != IsViewable )
+ bFakeWindowBG = true;
+ else
+ {
+ tools::Long nOrgDX = nDX, nOrgDY = nDY;
+
+ // clip to window size
+ if ( nX < 0 )
+ {
+ nDX += nX;
+ nX = 0;
+ }
+ if ( nY < 0 )
+ {
+ nDY += nY;
+ nY = 0;
+ }
+ if( nX + nDX > aAttrib.width )
+ nDX = aAttrib.width - nX;
+ if( nY + nDY > aAttrib.height )
+ nDY = aAttrib.height - nY;
+
+ // inside ?
+ if( nDX <= 0 || nDY <= 0 )
+ {
+ bFakeWindowBG = true;
+ nDX = nOrgDX;
+ nDY = nOrgDY;
+ }
+ }
+ }
+
+ std::shared_ptr<X11SalBitmap> pSalBitmap = std::make_shared<X11SalBitmap>();
+ sal_uInt16 nBitCount = GetBitCount();
+ vcl::PixelFormat ePixelFormat = vcl::bitDepthToPixelFormat(nBitCount);
+
+ if( &mrParent.GetDisplay()->GetColormap( mrParent.m_nXScreen ) != &mrParent.GetColormap() )
+ {
+ ePixelFormat = vcl::PixelFormat::N1_BPP;
+ nBitCount = 1;
+ }
+
+ if (nBitCount > 8)
+ ePixelFormat = vcl::PixelFormat::N24_BPP;
+
+ if( ! bFakeWindowBG )
+ pSalBitmap->ImplCreateFromDrawable( mrParent.GetDrawable(), mrParent.m_nXScreen, nBitCount, nX, nY, nDX, nDY );
+ else
+ pSalBitmap->Create( Size( nDX, nDY ), ePixelFormat, BitmapPalette( nBitCount > 8 ? nBitCount : 0 ) );
+
+ return pSalBitmap;
+}
+
+sal_uInt16 X11SalGraphicsImpl::GetBitCount() const
+{
+ return mrParent.GetVisual().GetDepth();
+}
+
+tools::Long X11SalGraphicsImpl::GetGraphicsWidth() const
+{
+ if( mrParent.m_pFrame )
+ return mrParent.m_pFrame->maGeometry.nWidth;
+ else if( mrParent.m_pVDev )
+ return static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetWidth();
+ else
+ return 0;
+}
+
+bool X11SalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/, const Gradient& /*rGradient*/)
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon const & /*rPolyPolygon*/, SalGradient const & /*rGradient*/)
+{
+ return false;
+}
+
+bool X11SalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
+{
+ bool bRet = false;
+ switch (eType)
+ {
+ case OutDevSupportType::TransparentRect:
+ case OutDevSupportType::B2DDraw:
+ {
+ XRenderPeer& rPeer = XRenderPeer::GetInstance();
+ const SalDisplay* pSalDisp = mrParent.GetDisplay();
+ const SalVisual& rSalVis = pSalDisp->GetVisual(mrParent.GetScreenNumber());
+
+ Visual* pDstXVisual = rSalVis.GetVisual();
+ XRenderPictFormat* pDstVisFmt = rPeer.FindVisualFormat(pDstXVisual);
+ if (pDstVisFmt)
+ bRet = true;
+ }
+ break;
+ default:
+ break;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx
new file mode 100644
index 000000000..48211b13d
--- /dev/null
+++ b/vcl/unx/generic/gdi/gdiimpl.hxx
@@ -0,0 +1,297 @@
+/* -*- 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 <X11/Xlib.h>
+
+#include <unx/x11/x11gdiimpl.h>
+
+#include <salgdiimpl.hxx>
+
+#include <basegfx/polygon/b2dtrapezoid.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <ControlCacheKey.hxx>
+
+/* From <X11/Intrinsic.h> */
+typedef unsigned long Pixel;
+
+class SalGraphics;
+class SalBitmap;
+class SalPolyLine;
+class X11SalGraphics;
+class Gradient;
+
+class X11SalGraphicsImpl : public SalGraphicsImpl, public X11GraphicsImpl
+{
+private:
+ X11SalGraphics& mrParent;
+
+ Color mnBrushColor;
+ GC mpBrushGC; // Brush attributes
+ Pixel mnBrushPixel;
+
+ bool mbPenGC : 1; // is Pen GC valid
+ bool mbBrushGC : 1; // is Brush GC valid
+ bool mbCopyGC : 1; // is Copy GC valid
+ bool mbInvertGC : 1; // is Invert GC valid
+ bool mbInvert50GC : 1; // is Invert50 GC valid
+ bool mbStippleGC : 1; // is Stipple GC valid
+ bool mbTrackingGC : 1; // is Tracking GC valid
+ bool mbDitherBrush : 1; // is solid or tile
+
+ bool mbXORMode : 1; // is ROP XOR Mode set
+
+ GC mpPenGC; // Pen attributes
+ Color mnPenColor;
+ Pixel mnPenPixel;
+
+
+ GC mpMonoGC;
+ GC mpCopyGC;
+ GC mpMaskGC;
+ GC mpInvertGC;
+ GC mpInvert50GC;
+ GC mpStippleGC;
+ GC mpTrackingGC;
+
+ GC CreateGC( Drawable hDrawable,
+ unsigned long nMask = GCGraphicsExposures );
+
+ GC SelectBrush();
+ GC SelectPen();
+ inline GC GetCopyGC();
+ inline GC GetStippleGC();
+ GC GetTrackingGC();
+ GC GetInvertGC();
+ GC GetInvert50GC();
+
+ void DrawLines( sal_uInt32 nPoints,
+ const SalPolyLine &rPoints,
+ GC pGC,
+ bool bClose
+ );
+
+ XID GetXRenderPicture();
+ bool drawFilledTrapezoids( const basegfx::B2DTrapezoid*, int nTrapCount, double fTransparency );
+ bool drawFilledTriangles(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::triangulator::B2DTriangleVector& rTriangles,
+ double fTransparency);
+
+ tools::Long GetGraphicsHeight() const;
+
+ void drawMaskedBitmap( const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ const SalBitmap& rTransparentBitmap );
+
+ void internalDrawPolyLine( sal_uInt32 nPoints, const Point* pPtAry, bool bClose );
+
+public:
+
+ explicit X11SalGraphicsImpl(X11SalGraphics& rParent);
+
+ virtual void freeResources() override;
+
+ virtual ~X11SalGraphicsImpl() override;
+
+ virtual OUString getRenderBackendName() const override { return "gen"; }
+
+ virtual bool setClipRegion( const vcl::Region& ) override;
+ //
+ // get the depth of the device
+ virtual sal_uInt16 GetBitCount() const override;
+
+ // get the width of the device
+ virtual tools::Long GetGraphicsWidth() const override;
+
+ // set the clip region to empty
+ virtual void ResetClipRegion() override;
+
+ // set the line color to transparent (= don't draw lines)
+
+ virtual void SetLineColor() override;
+
+ // set the line color to a specific color
+ virtual void SetLineColor( Color nColor ) override;
+
+ // set the fill color to transparent (= don't fill)
+ virtual void SetFillColor() override;
+
+ // set the fill color to a specific color, shapes will be
+ // filled accordingly
+ virtual void SetFillColor( Color nColor ) override;
+
+ // enable/disable XOR drawing
+ virtual void SetXORMode( bool bSet, bool bInvertOnly ) override;
+
+ // set line color for raster operations
+ virtual void SetROPLineColor( SalROPColor nROPColor ) override;
+
+ // set fill color for raster operations
+ virtual void SetROPFillColor( SalROPColor nROPColor ) override;
+
+ // draw --> LineColor and FillColor and RasterOp and ClipRegion
+ virtual void drawPixel( tools::Long nX, tools::Long nY ) override;
+ virtual void drawPixel( tools::Long nX, tools::Long nY, Color nColor ) override;
+
+ virtual void drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 ) override;
+
+ virtual void drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override;
+
+ virtual void drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry ) override;
+
+ virtual void drawPolygon( sal_uInt32 nPoints, const Point* pPtAry ) override;
+
+ virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, const Point** pPtAry ) override;
+
+ virtual bool drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon&,
+ double fTransparency) override;
+
+ virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon&,
+ double fTransparency,
+ double fLineWidth,
+ const std::vector< double >* pStroke, // MM01
+ basegfx::B2DLineJoin,
+ css::drawing::LineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
+
+ virtual bool drawPolyLineBezier(
+ sal_uInt32 nPoints,
+ const Point* pPtAry,
+ const PolyFlags* pFlgAry ) override;
+
+ virtual bool drawPolygonBezier(
+ sal_uInt32 nPoints,
+ const Point* pPtAry,
+ const PolyFlags* pFlgAry ) override;
+
+ virtual bool drawPolyPolygonBezier(
+ sal_uInt32 nPoly,
+ const sal_uInt32* pPoints,
+ const Point* const* pPtAry,
+ const PolyFlags* const* pFlgAry ) override;
+
+ // CopyArea --> No RasterOp, but ClipRegion
+ virtual 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
+ virtual void copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics ) override;
+
+ virtual void drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap ) override;
+
+ virtual void drawBitmap(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ const SalBitmap& rMaskBitmap ) override;
+
+ virtual void drawMask(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ Color nMaskColor ) override;
+
+ virtual std::shared_ptr<SalBitmap> getBitmap( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override;
+
+ virtual Color getPixel( tools::Long nX, tools::Long nY ) override;
+
+ // invert --> ClipRegion (only Windows or VirDevs)
+ virtual void invert(
+ tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ SalInvert nFlags) override;
+
+ virtual void invert( sal_uInt32 nPoints, const Point* pPtAry, SalInvert nFlags ) override;
+
+ virtual bool drawEPS(
+ tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ void* pPtr,
+ sal_uInt32 nSize ) override;
+
+ /** Blend bitmap with color channels */
+ virtual bool blendBitmap(
+ const SalTwoRect&,
+ const SalBitmap& rBitmap ) override;
+
+ /** Render bitmap by blending using the mask and alpha channel */
+ virtual bool blendAlphaBitmap(
+ const SalTwoRect&,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap& rMaskBitmap,
+ const SalBitmap& rAlphaBitmap ) 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
+ */
+ virtual bool drawAlphaBitmap(
+ const SalTwoRect&,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap& rAlphaBitmap ) override;
+
+ /** draw transformed bitmap (maybe with alpha) where Null, X, Y define the coordinate system */
+ virtual bool drawTransformedBitmap(
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap* pAlphaBitmap,
+ double fAlpha) override;
+
+ virtual bool hasFastDrawTransformedBitmap() const override;
+
+ /** Render solid rectangle with given transparency
+
+ @param nTransparency
+ Transparency value (0-255) to use. 0 blits and opaque, 255 a
+ fully transparent rectangle
+ */
+ virtual bool drawAlphaRect(
+ tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ sal_uInt8 nTransparency ) override;
+
+ virtual bool drawGradient(const tools::PolyPolygon& rPolygon, const Gradient& rGradient) override;
+ virtual bool implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient) override;
+
+ virtual bool supportsOperation(OutDevSupportType eType) const override;
+
+public:
+ void Init() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/salbmp.cxx b/vcl/unx/generic/gdi/salbmp.cxx
new file mode 100644
index 000000000..804b50184
--- /dev/null
+++ b/vcl/unx/generic/gdi/salbmp.cxx
@@ -0,0 +1,1001 @@
+/* -*- 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 <string.h>
+
+#ifdef FREEBSD
+#include <sys/types.h>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <osl/endian.h>
+#include <sal/log.hxx>
+
+#include <tools/helpers.hxx>
+#include <tools/debug.hxx>
+#include <vcl/bitmap.hxx>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+
+#include <unx/saldisp.hxx>
+#include <unx/salbmp.h>
+#include <unx/salinst.h>
+#include <unx/x11/xlimits.hxx>
+
+#include <o3tl/safeint.hxx>
+
+#include <config_features.h>
+
+#if defined HAVE_VALGRIND_HEADERS
+#include <valgrind/valgrind.h>
+#endif
+
+#include <memory>
+
+ImplSalBitmapCache* X11SalBitmap::mpCache = nullptr;
+unsigned int X11SalBitmap::mnCacheInstCount = 0;
+
+X11SalBitmap::X11SalBitmap()
+ : mbGrey( false )
+{
+}
+
+X11SalBitmap::~X11SalBitmap()
+{
+ Destroy();
+}
+
+void X11SalBitmap::ImplCreateCache()
+{
+ if( !mnCacheInstCount++ )
+ mpCache = new ImplSalBitmapCache;
+}
+
+void X11SalBitmap::ImplDestroyCache()
+{
+ SAL_WARN_IF( !mnCacheInstCount, "vcl", "X11SalBitmap::ImplDestroyCache(): underflow" );
+
+ if( mnCacheInstCount && !--mnCacheInstCount )
+ {
+ delete mpCache;
+ mpCache = nullptr;
+ }
+}
+
+void X11SalBitmap::ImplRemovedFromCache()
+{
+ mpDDB.reset();
+}
+
+#if defined HAVE_VALGRIND_HEADERS
+namespace
+{
+ void blankExtraSpace(BitmapBuffer* pDIB)
+ {
+ size_t nExtraSpaceInScanLine = pDIB->mnScanlineSize - pDIB->mnWidth * pDIB->mnBitCount / 8;
+ if (nExtraSpaceInScanLine)
+ {
+ for (tools::Long i = 0; i < pDIB->mnHeight; ++i)
+ {
+ sal_uInt8 *pRow = pDIB->mpBits + (i * pDIB->mnScanlineSize);
+ memset(pRow + (pDIB->mnScanlineSize - nExtraSpaceInScanLine), 0, nExtraSpaceInScanLine);
+ }
+ }
+ }
+}
+#endif
+
+std::unique_ptr<BitmapBuffer> X11SalBitmap::ImplCreateDIB(
+ const Size& rSize,
+ vcl::PixelFormat ePixelFormat,
+ const BitmapPalette& rPal)
+{
+ std::unique_ptr<BitmapBuffer> pDIB;
+
+ if( !rSize.Width() || !rSize.Height() )
+ return nullptr;
+
+ try
+ {
+ pDIB.reset(new BitmapBuffer);
+ }
+ catch (const std::bad_alloc&)
+ {
+ return nullptr;
+ }
+
+ pDIB->mnFormat = ScanlineFormat::NONE;
+
+ switch(ePixelFormat)
+ {
+ case vcl::PixelFormat::N1_BPP:
+ pDIB->mnFormat |= ScanlineFormat::N1BitMsbPal;
+ break;
+ case vcl::PixelFormat::N8_BPP:
+ pDIB->mnFormat |= ScanlineFormat::N8BitPal;
+ break;
+ case vcl::PixelFormat::N24_BPP:
+ pDIB->mnFormat |= ScanlineFormat::N24BitTcBgr;
+ break;
+ case vcl::PixelFormat::N32_BPP:
+ default:
+ SAL_WARN("vcl.gdi", "32-bit images not supported, converting to 24-bit");
+ ePixelFormat = vcl::PixelFormat::N24_BPP;
+ pDIB->mnFormat |= ScanlineFormat::N24BitTcBgr;
+ break;
+ }
+
+ sal_uInt16 nColors = 0;
+ if (ePixelFormat <= vcl::PixelFormat::N8_BPP)
+ nColors = vcl::numberOfColors(ePixelFormat);
+
+ pDIB->mnWidth = rSize.Width();
+ pDIB->mnHeight = rSize.Height();
+ tools::Long nScanlineBase;
+ bool bFail = o3tl::checked_multiply<tools::Long>(pDIB->mnWidth, vcl::pixelFormatBitCount(ePixelFormat), nScanlineBase);
+ if (bFail)
+ {
+ SAL_WARN("vcl.gdi", "checked multiply failed");
+ return nullptr;
+ }
+ pDIB->mnScanlineSize = AlignedWidth4Bytes(nScanlineBase);
+ if (pDIB->mnScanlineSize < nScanlineBase/8)
+ {
+ SAL_WARN("vcl.gdi", "scanline calculation wraparound");
+ return nullptr;
+ }
+ pDIB->mnBitCount = vcl::pixelFormatBitCount(ePixelFormat);
+
+ if( nColors )
+ {
+ pDIB->maPalette = rPal;
+ pDIB->maPalette.SetEntryCount( nColors );
+ }
+
+ try
+ {
+ pDIB->mpBits = new sal_uInt8[ pDIB->mnScanlineSize * pDIB->mnHeight ];
+#if defined HAVE_VALGRIND_HEADERS
+ if (RUNNING_ON_VALGRIND)
+ blankExtraSpace(pDIB.get());
+#endif
+ }
+ catch (const std::bad_alloc&)
+ {
+ return nullptr;
+ }
+
+ return pDIB;
+}
+
+std::unique_ptr<BitmapBuffer> X11SalBitmap::ImplCreateDIB(
+ Drawable aDrawable,
+ SalX11Screen nScreen,
+ tools::Long nDrawableDepth,
+ tools::Long nX,
+ tools::Long nY,
+ tools::Long nWidth,
+ tools::Long nHeight,
+ bool bGrey
+) {
+ std::unique_ptr<BitmapBuffer> pDIB;
+
+ if( aDrawable && nWidth && nHeight && nDrawableDepth )
+ {
+ SalDisplay* pSalDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ Display* pXDisp = pSalDisp->GetDisplay();
+
+ // do not die on XError here
+ // alternatively one could check the coordinates for being offscreen
+ // but this call can actually work on servers with backing store
+ // defaults even if the rectangle is offscreen
+ // so better catch the XError
+ GetGenericUnixSalData()->ErrorTrapPush();
+ XImage* pImage = XGetImage( pXDisp, aDrawable, nX, nY, nWidth, nHeight, AllPlanes, ZPixmap );
+ bool bWasError = GetGenericUnixSalData()->ErrorTrapPop( false );
+
+ if( ! bWasError && pImage && pImage->data )
+ {
+ const SalTwoRect aTwoRect = { 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight };
+ BitmapBuffer aSrcBuf;
+ std::optional<BitmapPalette> pDstPal;
+
+ aSrcBuf.mnFormat = ScanlineFormat::TopDown;
+ aSrcBuf.mnWidth = nWidth;
+ aSrcBuf.mnHeight = nHeight;
+ aSrcBuf.mnBitCount = pImage->bits_per_pixel;
+ aSrcBuf.mnScanlineSize = pImage->bytes_per_line;
+ aSrcBuf.mpBits = reinterpret_cast<sal_uInt8*>(pImage->data);
+
+ pImage->red_mask = pSalDisp->GetVisual( nScreen ).red_mask;
+ pImage->green_mask = pSalDisp->GetVisual( nScreen ).green_mask;
+ pImage->blue_mask = pSalDisp->GetVisual( nScreen ).blue_mask;
+
+ switch( aSrcBuf.mnBitCount )
+ {
+ case 1:
+ {
+ aSrcBuf.mnFormat |= ( LSBFirst == pImage->bitmap_bit_order
+ ? ScanlineFormat::N1BitLsbPal
+ : ScanlineFormat::N1BitMsbPal
+ );
+ }
+ break;
+
+ case 8:
+ {
+ aSrcBuf.mnFormat |= ScanlineFormat::N8BitPal;
+ }
+ break;
+
+ case 24:
+ {
+ if( ( LSBFirst == pImage->byte_order ) && ( pImage->red_mask == 0xFF ) )
+ aSrcBuf.mnFormat |= ScanlineFormat::N24BitTcRgb;
+ else
+ aSrcBuf.mnFormat |= ScanlineFormat::N24BitTcBgr;
+ }
+ break;
+
+ case 32:
+ {
+ if( LSBFirst == pImage->byte_order )
+ aSrcBuf.mnFormat |= ( pSalDisp->GetVisual(nScreen).red_mask == 0xFF
+ ? ScanlineFormat::N32BitTcRgba
+ : ScanlineFormat::N32BitTcBgra
+ );
+ else
+ aSrcBuf.mnFormat |= ( pSalDisp->GetVisual(nScreen).red_mask == 0xFF
+ ? ScanlineFormat::N32BitTcAbgr
+ : ScanlineFormat::N32BitTcArgb
+ );
+ }
+ break;
+
+ default: assert(false);
+ }
+
+ BitmapPalette& rPal = aSrcBuf.maPalette;
+
+ if( aSrcBuf.mnBitCount == 1 )
+ {
+ rPal.SetEntryCount( 2 );
+ rPal[ 0 ] = COL_BLACK;
+ rPal[ 1 ] = COL_WHITE;
+ pDstPal = rPal;
+ }
+ else if( pImage->depth == 8 && bGrey )
+ {
+ rPal.SetEntryCount( 256 );
+
+ for( sal_uInt16 i = 0; i < 256; i++ )
+ {
+ BitmapColor& rBmpCol = rPal[ i ];
+
+ rBmpCol.SetRed( i );
+ rBmpCol.SetGreen( i );
+ rBmpCol.SetBlue( i );
+ }
+
+ pDstPal = rPal;
+ }
+ else if( aSrcBuf.mnBitCount <= 8 )
+ {
+ const SalColormap& rColMap = pSalDisp->GetColormap( nScreen );
+ const sal_uInt16 nCols = std::min(static_cast<sal_uLong>(rColMap.GetUsed()),
+ sal_uLong(1) << nDrawableDepth);
+
+ rPal.SetEntryCount( nCols );
+
+ for( sal_uInt16 i = 0; i < nCols; i++ )
+ {
+ const Color nColor( rColMap.GetColor( i ) );
+ BitmapColor& rBmpCol = rPal[ i ];
+
+ rBmpCol.SetRed( nColor.GetRed() );
+ rBmpCol.SetGreen( nColor.GetGreen() );
+ rBmpCol.SetBlue( nColor.GetBlue() );
+ }
+ pDstPal = rPal;
+ }
+
+ pDIB = StretchAndConvert( aSrcBuf, aTwoRect, aSrcBuf.mnFormat,
+ pDstPal, &aSrcBuf.maColorMask );
+ XDestroyImage( pImage );
+ }
+ }
+
+ return pDIB;
+}
+
+XImage* X11SalBitmap::ImplCreateXImage(
+ SalDisplay const *pSalDisp,
+ SalX11Screen nScreen,
+ tools::Long nDepth,
+ const SalTwoRect& rTwoRect
+) const
+{
+ XImage* pImage = nullptr;
+
+ if( !mpDIB && mpDDB )
+ {
+ const_cast<X11SalBitmap*>(this)->mpDIB =
+ ImplCreateDIB( mpDDB->ImplGetPixmap(),
+ mpDDB->ImplGetScreen(),
+ mpDDB->ImplGetDepth(),
+ 0, 0,
+ mpDDB->ImplGetWidth(),
+ mpDDB->ImplGetHeight(),
+ mbGrey );
+ }
+
+ if( mpDIB && mpDIB->mnWidth && mpDIB->mnHeight )
+ {
+ Display* pXDisp = pSalDisp->GetDisplay();
+ tools::Long nWidth = rTwoRect.mnDestWidth;
+ tools::Long nHeight = rTwoRect.mnDestHeight;
+
+ if( 1 == GetBitCount() )
+ nDepth = 1;
+
+ pImage = XCreateImage( pXDisp, pSalDisp->GetVisual( nScreen ).GetVisual(),
+ nDepth, ( 1 == nDepth ) ? XYBitmap :ZPixmap, 0, nullptr,
+ nWidth, nHeight, 32, 0 );
+
+ if( pImage )
+ {
+ std::unique_ptr<BitmapBuffer> pDstBuf;
+ ScanlineFormat nDstFormat = ScanlineFormat::TopDown;
+ std::optional<BitmapPalette> xPal;
+ std::unique_ptr<ColorMask> xMask;
+
+ switch( pImage->bits_per_pixel )
+ {
+ case 1:
+ nDstFormat |= ( LSBFirst == pImage->bitmap_bit_order
+ ? ScanlineFormat::N1BitLsbPal
+ : ScanlineFormat::N1BitMsbPal
+ );
+ break;
+
+ case 8:
+ nDstFormat |= ScanlineFormat::N8BitPal;
+ break;
+
+ case 24:
+ {
+ if( ( LSBFirst == pImage->byte_order ) && ( pImage->red_mask == 0xFF ) )
+ nDstFormat |= ScanlineFormat::N24BitTcRgb;
+ else
+ nDstFormat |= ScanlineFormat::N24BitTcBgr;
+ }
+ break;
+
+ case 32:
+ {
+ if( LSBFirst == pImage->byte_order )
+ nDstFormat |= ( pImage->red_mask == 0xFF
+ ? ScanlineFormat::N32BitTcRgba
+ : ScanlineFormat::N32BitTcBgra
+ );
+ else
+ nDstFormat |= ( pImage->red_mask == 0xFF
+ ? ScanlineFormat::N32BitTcAbgr
+ : ScanlineFormat::N32BitTcArgb
+ );
+ }
+ break;
+
+ default: assert(false);
+ }
+
+ if( pImage->depth == 1 )
+ {
+ xPal.emplace(2);
+ (*xPal)[ 0 ] = COL_BLACK;
+ (*xPal)[ 1 ] = COL_WHITE;
+ }
+ else if( pImage->depth == 8 && mbGrey )
+ {
+ xPal.emplace(256);
+
+ for( sal_uInt16 i = 0; i < 256; i++ )
+ {
+ BitmapColor& rBmpCol = (*xPal)[ i ];
+
+ rBmpCol.SetRed( i );
+ rBmpCol.SetGreen( i );
+ rBmpCol.SetBlue( i );
+ }
+
+ }
+ else if( pImage->depth <= 8 )
+ {
+ const SalColormap& rColMap = pSalDisp->GetColormap( nScreen );
+ const sal_uInt16 nCols = std::min( static_cast<sal_uLong>(rColMap.GetUsed())
+ , static_cast<sal_uLong>(1 << pImage->depth)
+ );
+
+ xPal.emplace(nCols);
+
+ for( sal_uInt16 i = 0; i < nCols; i++ )
+ {
+ const Color nColor( rColMap.GetColor( i ) );
+ BitmapColor& rBmpCol = (*xPal)[ i ];
+
+ rBmpCol.SetRed( nColor.GetRed() );
+ rBmpCol.SetGreen( nColor.GetGreen() );
+ rBmpCol.SetBlue( nColor.GetBlue() );
+ }
+ }
+
+ pDstBuf = StretchAndConvert( *mpDIB, rTwoRect, nDstFormat, xPal, xMask.get() );
+ xPal.reset();
+ xMask.reset();
+
+ if( pDstBuf && pDstBuf->mpBits )
+ {
+#if defined HAVE_VALGRIND_HEADERS
+ if (RUNNING_ON_VALGRIND)
+ blankExtraSpace(pDstBuf.get());
+#endif
+ // set data in buffer as data member in pImage
+ pImage->data = reinterpret_cast<char*>(pDstBuf->mpBits);
+ }
+ else
+ {
+ XDestroyImage( pImage );
+ pImage = nullptr;
+ }
+
+ // note that pDstBuf it deleted here, but that doesn't destroy allocated data in buffer
+ }
+ }
+
+ return pImage;
+}
+
+bool X11SalBitmap::ImplCreateFromDrawable(
+ Drawable aDrawable,
+ SalX11Screen nScreen,
+ tools::Long nDrawableDepth,
+ tools::Long nX,
+ tools::Long nY,
+ tools::Long nWidth,
+ tools::Long nHeight
+) {
+ Destroy();
+
+ if( aDrawable && nWidth && nHeight && nDrawableDepth )
+ mpDDB.reset(new ImplSalDDB( aDrawable, nScreen, nDrawableDepth, nX, nY, nWidth, nHeight ));
+
+ return( mpDDB != nullptr );
+}
+
+ImplSalDDB* X11SalBitmap::ImplGetDDB(
+ Drawable aDrawable,
+ SalX11Screen nXScreen,
+ tools::Long nDrawableDepth,
+ const SalTwoRect& rTwoRect
+) const
+{
+ if( !mpDDB || !mpDDB->ImplMatches( nXScreen, nDrawableDepth, rTwoRect ) )
+ {
+ if( mpDDB )
+ {
+ // do we already have a DIB? if not, create aDIB from current DDB first
+ if( !mpDIB )
+ {
+ const_cast<X11SalBitmap*>(this)->mpDIB = ImplCreateDIB( mpDDB->ImplGetPixmap(),
+ mpDDB->ImplGetScreen(),
+ mpDDB->ImplGetDepth(),
+ 0, 0,
+ mpDDB->ImplGetWidth(),
+ mpDDB->ImplGetHeight(),
+ mbGrey );
+ }
+
+ mpDDB.reset();
+ }
+
+ if( mpCache )
+ mpCache->ImplRemove( this );
+
+ SalTwoRect aTwoRect( rTwoRect );
+ if( aTwoRect.mnSrcX < 0 )
+ {
+ aTwoRect.mnSrcWidth += aTwoRect.mnSrcX;
+ aTwoRect.mnSrcX = 0;
+ }
+ if( aTwoRect.mnSrcY < 0 )
+ {
+ aTwoRect.mnSrcHeight += aTwoRect.mnSrcY;
+ aTwoRect.mnSrcY = 0;
+ }
+
+ // create new DDB from DIB
+ const Size aSize( GetSize() );
+ if( aTwoRect.mnSrcWidth == aTwoRect.mnDestWidth &&
+ aTwoRect.mnSrcHeight == aTwoRect.mnDestHeight )
+ {
+ aTwoRect.mnSrcX = aTwoRect.mnSrcY = aTwoRect.mnDestX = aTwoRect.mnDestY = 0;
+ aTwoRect.mnSrcWidth = aTwoRect.mnDestWidth = aSize.Width();
+ aTwoRect.mnSrcHeight = aTwoRect.mnDestHeight = aSize.Height();
+ }
+ else if( aTwoRect.mnSrcWidth+aTwoRect.mnSrcX > aSize.Width() ||
+ aTwoRect.mnSrcHeight+aTwoRect.mnSrcY > aSize.Height() )
+ {
+ // #i47823# this should not happen at all, but does nonetheless
+ // because BitmapEx allows for mask bitmaps of different size
+ // than image bitmap (broken)
+ if( aTwoRect.mnSrcX >= aSize.Width() ||
+ aTwoRect.mnSrcY >= aSize.Height() )
+ return nullptr; // this would be a really mad case
+
+ if( aTwoRect.mnSrcWidth+aTwoRect.mnSrcX > aSize.Width() )
+ {
+ aTwoRect.mnSrcWidth = aSize.Width()-aTwoRect.mnSrcX;
+ if( aTwoRect.mnSrcWidth < 1 )
+ {
+ aTwoRect.mnSrcX = 0;
+ aTwoRect.mnSrcWidth = aSize.Width();
+ }
+ }
+ if( aTwoRect.mnSrcHeight+aTwoRect.mnSrcY > aSize.Height() )
+ {
+ aTwoRect.mnSrcHeight = aSize.Height() - aTwoRect.mnSrcY;
+ if( aTwoRect.mnSrcHeight < 1 )
+ {
+ aTwoRect.mnSrcY = 0;
+ aTwoRect.mnSrcHeight = aSize.Height();
+ }
+ }
+ }
+
+ XImage* pImage = ImplCreateXImage( vcl_sal::getSalDisplay(GetGenericUnixSalData()), nXScreen,
+ nDrawableDepth, aTwoRect );
+
+ if( pImage )
+ {
+ mpDDB.reset(new ImplSalDDB( pImage, aDrawable, nXScreen, aTwoRect ));
+ delete[] pImage->data;
+ pImage->data = nullptr;
+ XDestroyImage( pImage );
+
+ if( mpCache )
+ mpCache->ImplAdd( const_cast<X11SalBitmap*>(this) );
+ }
+ }
+
+ return mpDDB.get();
+}
+
+void X11SalBitmap::ImplDraw(
+ Drawable aDrawable,
+ SalX11Screen nXScreen,
+ tools::Long nDrawableDepth,
+ const SalTwoRect& rTwoRect,
+ const GC& rGC
+) const
+{
+ ImplGetDDB( aDrawable, nXScreen, nDrawableDepth, rTwoRect );
+ if( mpDDB )
+ mpDDB->ImplDraw( aDrawable, rTwoRect, rGC );
+}
+
+bool X11SalBitmap::Create( const Size& rSize, vcl::PixelFormat ePixelFormat, const BitmapPalette& rPal )
+{
+ Destroy();
+ mpDIB = ImplCreateDIB( rSize, ePixelFormat, rPal );
+
+ return( mpDIB != nullptr );
+}
+
+bool X11SalBitmap::Create( const SalBitmap& rSSalBmp )
+{
+ Destroy();
+
+ auto pX11Bmp = dynamic_cast<const X11SalBitmap*>( &rSSalBmp );
+ if (!pX11Bmp)
+ return false;
+
+ const X11SalBitmap& rSalBmp = *pX11Bmp;
+
+ if( rSalBmp.mpDIB )
+ {
+ // TODO: reference counting...
+ mpDIB.reset(new BitmapBuffer( *rSalBmp.mpDIB ));
+ // TODO: get rid of this when BitmapBuffer gets copy constructor
+ try
+ {
+ mpDIB->mpBits = new sal_uInt8[ mpDIB->mnScanlineSize * mpDIB->mnHeight ];
+#if defined HAVE_VALGRIND_HEADERS
+ if (RUNNING_ON_VALGRIND)
+ blankExtraSpace(mpDIB.get());
+#endif
+ }
+ catch (const std::bad_alloc&)
+ {
+ mpDIB.reset();
+ }
+
+ if( mpDIB )
+ memcpy( mpDIB->mpBits, rSalBmp.mpDIB->mpBits, mpDIB->mnScanlineSize * mpDIB->mnHeight );
+ }
+ else if( rSalBmp.mpDDB )
+ ImplCreateFromDrawable( rSalBmp.mpDDB->ImplGetPixmap(),
+ rSalBmp.mpDDB->ImplGetScreen(),
+ rSalBmp.mpDDB->ImplGetDepth(),
+ 0, 0, rSalBmp.mpDDB->ImplGetWidth(), rSalBmp.mpDDB->ImplGetHeight() );
+
+ return( ( !rSalBmp.mpDIB && !rSalBmp.mpDDB ) ||
+ ( rSalBmp.mpDIB && ( mpDIB != nullptr ) ) ||
+ ( rSalBmp.mpDDB && ( mpDDB != nullptr ) ) );
+}
+
+bool X11SalBitmap::Create( const SalBitmap&, SalGraphics* )
+{
+ return false;
+}
+
+bool X11SalBitmap::Create(const SalBitmap&, vcl::PixelFormat /*eNewPixelFormat*/)
+{
+ return false;
+}
+
+bool X11SalBitmap::Create(
+ const css::uno::Reference< css::rendering::XBitmapCanvas >& rBitmapCanvas,
+ Size& rSize,
+ bool bMask
+) {
+ css::uno::Reference< css::beans::XFastPropertySet > xFastPropertySet( rBitmapCanvas, css::uno::UNO_QUERY );
+
+ if( xFastPropertySet ) {
+ css::uno::Sequence< css::uno::Any > args;
+
+ if( xFastPropertySet->getFastPropertyValue(bMask ? 2 : 1) >>= args ) {
+ sal_Int64 pixmapHandle = {}; // spurious -Werror=maybe-uninitialized
+ sal_Int32 depth;
+ if( ( args[1] >>= pixmapHandle ) && ( args[2] >>= depth ) ) {
+
+ mbGrey = bMask;
+ bool bSuccess = ImplCreateFromDrawable(
+ pixmapHandle,
+ // FIXME: this seems multi-screen broken to me
+ SalX11Screen( 0 ),
+ depth,
+ 0,
+ 0,
+ rSize.Width(),
+ rSize.Height()
+ );
+ bool bFreePixmap = false;
+ if( bSuccess && (args[0] >>= bFreePixmap) && bFreePixmap )
+ XFreePixmap( vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay(), pixmapHandle );
+
+ return bSuccess;
+ }
+ }
+ }
+
+ return false;
+}
+
+void X11SalBitmap::Destroy()
+{
+ if( mpDIB )
+ {
+ delete[] mpDIB->mpBits;
+ mpDIB.reset();
+ }
+
+ mpDDB.reset();
+
+ if( mpCache )
+ mpCache->ImplRemove( this );
+}
+
+Size X11SalBitmap::GetSize() const
+{
+ Size aSize;
+
+ if( mpDIB )
+ {
+ aSize.setWidth( mpDIB->mnWidth );
+ aSize.setHeight( mpDIB->mnHeight );
+ }
+ else if( mpDDB )
+ {
+ aSize.setWidth( mpDDB->ImplGetWidth() );
+ aSize.setHeight( mpDDB->ImplGetHeight() );
+ }
+
+ return aSize;
+}
+
+sal_uInt16 X11SalBitmap::GetBitCount() const
+{
+ sal_uInt16 nBitCount;
+
+ if( mpDIB )
+ nBitCount = mpDIB->mnBitCount;
+ else if( mpDDB )
+ nBitCount = mpDDB->ImplGetDepth();
+ else
+ nBitCount = 0;
+
+ return nBitCount;
+}
+
+BitmapBuffer* X11SalBitmap::AcquireBuffer( BitmapAccessMode /*nMode*/ )
+{
+ if( !mpDIB && mpDDB )
+ {
+ mpDIB = ImplCreateDIB(
+ mpDDB->ImplGetPixmap(),
+ mpDDB->ImplGetScreen(),
+ mpDDB->ImplGetDepth(),
+ 0, 0,
+ mpDDB->ImplGetWidth(),
+ mpDDB->ImplGetHeight(),
+ mbGrey
+ );
+ }
+
+ return mpDIB.get();
+}
+
+void X11SalBitmap::ReleaseBuffer( BitmapBuffer*, BitmapAccessMode nMode )
+{
+ if( nMode == BitmapAccessMode::Write )
+ {
+ mpDDB.reset();
+
+ if( mpCache )
+ mpCache->ImplRemove( this );
+ InvalidateChecksum();
+ }
+}
+
+bool X11SalBitmap::GetSystemData( BitmapSystemData& rData )
+{
+ if( mpDDB )
+ {
+ // Rename/retype pDummy to your likings (though X11 Pixmap is
+ // prolly not a good idea, since it's accessed from
+ // non-platform aware code in vcl/bitmap.hxx)
+ rData.aPixmap = reinterpret_cast<void*>(mpDDB->ImplGetPixmap());
+ rData.mnWidth = mpDDB->ImplGetWidth ();
+ rData.mnHeight = mpDDB->ImplGetHeight ();
+ return true;
+ }
+
+ return false;
+}
+
+bool X11SalBitmap::ScalingSupported() const
+{
+ return false;
+}
+
+bool X11SalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ )
+{
+ return false;
+}
+
+bool X11SalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uInt8 /*nTol*/ )
+{
+ return false;
+}
+
+
+ImplSalDDB::ImplSalDDB( XImage* pImage, Drawable aDrawable,
+ SalX11Screen nXScreen, const SalTwoRect& rTwoRect )
+ : maPixmap ( 0 )
+ , maTwoRect ( rTwoRect )
+ , mnDepth ( pImage->depth )
+ , mnXScreen ( nXScreen )
+{
+ SalDisplay* pSalDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ Display* pXDisp = pSalDisp->GetDisplay();
+
+ maPixmap = limitXCreatePixmap( pXDisp, aDrawable, ImplGetWidth(), ImplGetHeight(), ImplGetDepth() );
+ if (!maPixmap)
+ return;
+
+ XGCValues aValues;
+ GC aGC;
+ int nValues = GCFunction;
+
+ aValues.function = GXcopy;
+
+ if( 1 == mnDepth )
+ {
+ nValues |= ( GCForeground | GCBackground );
+ aValues.foreground = 1;
+ aValues.background = 0;
+ }
+
+ aGC = XCreateGC( pXDisp, maPixmap, nValues, &aValues );
+ XPutImage( pXDisp, maPixmap, aGC, pImage, 0, 0, 0, 0, maTwoRect.mnDestWidth, maTwoRect.mnDestHeight );
+ XFreeGC( pXDisp, aGC );
+}
+
+ImplSalDDB::ImplSalDDB(
+ Drawable aDrawable,
+ SalX11Screen nXScreen,
+ tools::Long nDrawableDepth,
+ tools::Long nX,
+ tools::Long nY,
+ tools::Long nWidth,
+ tools::Long nHeight
+) : maTwoRect(0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight)
+ , mnDepth( nDrawableDepth )
+ , mnXScreen( nXScreen )
+{
+ SalDisplay* pSalDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ Display* pXDisp = pSalDisp->GetDisplay();
+
+ if( (maPixmap = limitXCreatePixmap( pXDisp, aDrawable, nWidth, nHeight, nDrawableDepth )) )
+ {
+ XGCValues aValues;
+ GC aGC;
+ int nValues = GCFunction;
+
+ aValues.function = GXcopy;
+
+ if( 1 == mnDepth )
+ {
+ nValues |= ( GCForeground | GCBackground );
+ aValues.foreground = 1;
+ aValues.background = 0;
+ }
+
+ aGC = XCreateGC( pXDisp, maPixmap, nValues, &aValues );
+ ImplDraw( aDrawable, nDrawableDepth, maPixmap,
+ nX, nY, nWidth, nHeight, 0, 0, aGC );
+ XFreeGC( pXDisp, aGC );
+ }
+ else
+ {
+ maTwoRect.mnSrcWidth = maTwoRect.mnDestWidth = 0;
+ maTwoRect.mnSrcHeight = maTwoRect.mnDestHeight = 0;
+ }
+}
+
+ImplSalDDB::~ImplSalDDB()
+{
+ if( maPixmap && ImplGetSVData() )
+ XFreePixmap( vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay(), maPixmap );
+}
+
+bool ImplSalDDB::ImplMatches( SalX11Screen nXScreen, tools::Long nDepth, const SalTwoRect& rTwoRect ) const
+{
+ bool bRet = false;
+
+ if( ( maPixmap != 0 ) && ( ( mnDepth == nDepth ) || ( 1 == mnDepth ) ) && nXScreen == mnXScreen)
+ {
+ if ( rTwoRect.mnSrcX == maTwoRect.mnSrcX
+ && rTwoRect.mnSrcY == maTwoRect.mnSrcY
+ && rTwoRect.mnSrcWidth == maTwoRect.mnSrcWidth
+ && rTwoRect.mnSrcHeight == maTwoRect.mnSrcHeight
+ && rTwoRect.mnDestWidth == maTwoRect.mnDestWidth
+ && rTwoRect.mnDestHeight == maTwoRect.mnDestHeight
+ )
+ {
+ // absolutely identically
+ bRet = true;
+ }
+ else if( rTwoRect.mnSrcWidth == rTwoRect.mnDestWidth
+ && rTwoRect.mnSrcHeight == rTwoRect.mnDestHeight
+ && maTwoRect.mnSrcWidth == maTwoRect.mnDestWidth
+ && maTwoRect.mnSrcHeight == maTwoRect.mnDestHeight
+ && rTwoRect.mnSrcX >= maTwoRect.mnSrcX
+ && rTwoRect.mnSrcY >= maTwoRect.mnSrcY
+ && ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) <= ( maTwoRect.mnSrcX + maTwoRect.mnSrcWidth )
+ && ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) <= ( maTwoRect.mnSrcY + maTwoRect.mnSrcHeight )
+ )
+ {
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+void ImplSalDDB::ImplDraw(
+ Drawable aDrawable,
+ const SalTwoRect& rTwoRect,
+ const GC& rGC
+) const
+{
+ ImplDraw( maPixmap, mnDepth, aDrawable,
+ rTwoRect.mnSrcX - maTwoRect.mnSrcX, rTwoRect.mnSrcY - maTwoRect.mnSrcY,
+ rTwoRect.mnDestWidth, rTwoRect.mnDestHeight,
+ rTwoRect.mnDestX, rTwoRect.mnDestY, rGC );
+}
+
+void ImplSalDDB::ImplDraw(
+ Drawable aSrcDrawable,
+ tools::Long nSrcDrawableDepth,
+ Drawable aDstDrawable,
+ tools::Long nSrcX,
+ tools::Long nSrcY,
+ tools::Long nDestWidth,
+ tools::Long nDestHeight,
+ tools::Long nDestX,
+ tools::Long nDestY,
+ const GC& rGC
+) {
+ SalDisplay* pSalDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ Display* pXDisp = pSalDisp->GetDisplay();
+
+ if( 1 == nSrcDrawableDepth )
+ {
+ XCopyPlane( pXDisp, aSrcDrawable, aDstDrawable, rGC,
+ nSrcX, nSrcY, nDestWidth, nDestHeight, nDestX, nDestY, 1 );
+ }
+ else
+ {
+ XCopyArea( pXDisp, aSrcDrawable, aDstDrawable, rGC,
+ nSrcX, nSrcY, nDestWidth, nDestHeight, nDestX, nDestY );
+ }
+}
+
+
+ImplSalBitmapCache::ImplSalBitmapCache()
+{
+}
+
+ImplSalBitmapCache::~ImplSalBitmapCache()
+{
+ ImplClear();
+}
+
+void ImplSalBitmapCache::ImplAdd( X11SalBitmap* pBmp )
+{
+ for(auto pObj : maBmpList)
+ {
+ if( pObj == pBmp )
+ return;
+ }
+ maBmpList.push_back( pBmp );
+}
+
+void ImplSalBitmapCache::ImplRemove( X11SalBitmap const * pBmp )
+{
+ auto it = std::find(maBmpList.begin(), maBmpList.end(), pBmp);
+ if( it != maBmpList.end() )
+ {
+ (*it)->ImplRemovedFromCache();
+ maBmpList.erase( it );
+ }
+}
+
+void ImplSalBitmapCache::ImplClear()
+{
+ for(auto pObj : maBmpList)
+ {
+ pObj->ImplRemovedFromCache();
+ }
+ maBmpList.clear();
+}
+
+/* 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 000000000..d6eecfecb
--- /dev/null
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -0,0 +1,482 @@
+/* -*- 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 <X11/extensions/Xrender.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 "gdiimpl.hxx"
+
+#include <unx/x11/x11cairotextrender.hxx>
+#include <unx/x11/xrender_peer.hxx>
+#include "cairo_xlib_cairo.hxx"
+#include <cairo-xlib.h>
+
+#if ENABLE_CAIRO_CANVAS
+#include "X11CairoSalGraphicsImpl.hxx"
+#endif
+
+
+// X11Common
+
+X11Common::X11Common()
+ : m_hDrawable(None)
+ , m_pColormap(nullptr)
+ , m_pExternalSurface(nullptr)
+{}
+
+cairo_t* X11Common::getCairoContext()
+{
+ if (m_pExternalSurface)
+ return cairo_create(m_pExternalSurface);
+
+ cairo_surface_t* surface = cairo_xlib_surface_create(GetXDisplay(), m_hDrawable, GetVisual().visual, SAL_MAX_INT16, SAL_MAX_INT16);
+
+ cairo_t *cr = cairo_create(surface);
+ cairo_surface_destroy(surface);
+
+ return cr;
+}
+
+void X11Common::releaseCairoContext(cairo_t* cr)
+{
+ cairo_destroy(cr);
+}
+
+bool X11Common::SupportsCairo() const
+{
+ static bool bSupportsCairo = [this] {
+ Display *pDisplay = GetXDisplay();
+ int nDummy;
+ return XQueryExtension(pDisplay, "RENDER", &nDummy, &nDummy, &nDummy);
+ }();
+ return bSupportsCairo;
+}
+
+// X11SalGraphics
+
+X11SalGraphics::X11SalGraphics():
+ m_pFrame(nullptr),
+ m_pVDev(nullptr),
+ m_nXScreen( 0 ),
+ m_pXRenderFormat(nullptr),
+ m_aXRenderPicture(0),
+ mpClipRegion(nullptr),
+ hBrush_(None),
+ bWindow_(false),
+ bVirDev_(false)
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ {
+ mxImpl.reset(new X11SkiaSalGraphicsImpl(*this));
+ mxTextRenderImpl.reset(new SkiaTextRender);
+ }
+ else
+#endif
+ {
+ mxTextRenderImpl.reset(new X11CairoTextRender(*this));
+#if ENABLE_CAIRO_CANVAS
+ mxImpl.reset(new X11CairoSalGraphicsImpl(*this, maX11Common));
+#else
+ mxImpl.reset(new X11SalGraphicsImpl(*this));
+#endif
+ }
+}
+
+X11SalGraphics::~X11SalGraphics() COVERITY_NOEXCEPT_FALSE
+{
+ DeInit();
+ ReleaseFonts();
+ freeResources();
+}
+
+void X11SalGraphics::freeResources()
+{
+ Display *pDisplay = GetXDisplay();
+
+ if( mpClipRegion )
+ {
+ XDestroyRegion( mpClipRegion );
+ mpClipRegion = nullptr;
+ }
+
+ mxImpl->freeResources();
+
+ if( hBrush_ )
+ {
+ XFreePixmap( pDisplay, hBrush_ );
+ hBrush_ = None;
+ }
+ if( m_pDeleteColormap )
+ {
+ m_pDeleteColormap.reset();
+ maX11Common.m_pColormap = nullptr;
+ }
+ if( m_aXRenderPicture )
+ {
+ XRenderPeer::GetInstance().FreePicture( m_aXRenderPicture );
+ m_aXRenderPicture = 0;
+ }
+}
+
+SalGraphicsImpl* X11SalGraphics::GetImpl() const
+{
+ return mxImpl.get();
+}
+
+void X11SalGraphics::SetDrawable(Drawable aDrawable, cairo_surface_t* pExternalSurface, SalX11Screen nXScreen)
+{
+ maX11Common.m_pExternalSurface = pExternalSurface;
+
+ // 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;
+ SetXRenderFormat( nullptr );
+ if( m_aXRenderPicture )
+ {
+ XRenderPeer::GetInstance().FreePicture( m_aXRenderPicture );
+ m_aXRenderPicture = 0;
+ }
+}
+
+void X11SalGraphics::Init( SalFrame *pFrame, Drawable aTarget,
+ SalX11Screen nXScreen )
+{
+ maX11Common.m_pColormap = &vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetColormap(nXScreen);
+ m_nXScreen = nXScreen;
+
+ m_pFrame = pFrame;
+ m_pVDev = nullptr;
+
+ bWindow_ = true;
+ bVirDev_ = false;
+
+ SetDrawable(aTarget, nullptr, nXScreen);
+ mxImpl->Init();
+}
+
+void X11SalGraphics::DeInit()
+{
+ mxImpl->DeInit();
+ SetDrawable(None, nullptr, m_nXScreen);
+}
+
+void X11SalGraphics::SetClipRegion( GC pGC, Region pXReg ) const
+{
+ Display *pDisplay = GetXDisplay();
+
+ int n = 0;
+ Region Regions[3];
+
+ if( mpClipRegion )
+ Regions[n++] = mpClipRegion;
+
+ if( pXReg && !XEmptyRegion( pXReg ) )
+ Regions[n++] = pXReg;
+
+ if( 0 == n )
+ XSetClipMask( pDisplay, pGC, None );
+ else if( 1 == n )
+ XSetRegion( pDisplay, pGC, Regions[0] );
+ else
+ {
+ Region pTmpRegion = XCreateRegion();
+ XIntersectRegion( Regions[0], Regions[1], pTmpRegion );
+
+ XSetRegion( pDisplay, pGC, pTmpRegion );
+ XDestroyRegion( pTmpRegion );
+ }
+}
+
+// Calculate a dither-pixmap and make a brush of it
+#define P_DELTA 51
+#define DMAP( v, m ) ((v % P_DELTA) > m ? (v / P_DELTA) + 1 : (v / P_DELTA))
+
+bool X11SalGraphics::GetDitherPixmap( Color nColor )
+{
+ static const short nOrdDither8Bit[ 8 ][ 8 ] =
+ {
+ { 0, 38, 9, 48, 2, 40, 12, 50},
+ {25, 12, 35, 22, 28, 15, 37, 24},
+ { 6, 44, 3, 41, 8, 47, 5, 44},
+ {32, 19, 28, 16, 34, 21, 31, 18},
+ { 1, 40, 11, 49, 0, 39, 10, 48},
+ {27, 14, 36, 24, 26, 13, 36, 23},
+ { 8, 46, 4, 43, 7, 45, 4, 42},
+ {33, 20, 30, 17, 32, 20, 29, 16}
+ };
+
+ // test for correct depth (8bit)
+ if( GetColormap().GetVisual().GetDepth() != 8 )
+ return false;
+
+ char pBits[64];
+ char *pBitsPtr = pBits;
+
+ // Set the palette-entries for the dithering tile
+ sal_uInt8 nColorRed = nColor.GetRed();
+ sal_uInt8 nColorGreen = nColor.GetGreen();
+ sal_uInt8 nColorBlue = nColor.GetBlue();
+
+ for(auto & nY : nOrdDither8Bit)
+ {
+ for( int nX = 0; nX < 8; nX++ )
+ {
+ short nMagic = nY[nX];
+ sal_uInt8 nR = P_DELTA * DMAP( nColorRed, nMagic );
+ sal_uInt8 nG = P_DELTA * DMAP( nColorGreen, nMagic );
+ sal_uInt8 nB = P_DELTA * DMAP( nColorBlue, nMagic );
+
+ *pBitsPtr++ = GetColormap().GetPixel( Color( nR, nG, nB ) );
+ }
+ }
+
+ // create the tile as ximage and an according pixmap -> caching
+ XImage *pImage = XCreateImage( GetXDisplay(),
+ GetColormap().GetXVisual(),
+ 8,
+ ZPixmap,
+ 0, // offset
+ pBits, // data
+ 8, 8, // width & height
+ 8, // bitmap_pad
+ 0 ); // (default) bytes_per_line
+
+ if( !hBrush_ )
+ hBrush_ = limitXCreatePixmap( GetXDisplay(), GetDrawable(), 8, 8, 8 );
+
+ // put the ximage to the pixmap
+ XPutImage( GetXDisplay(),
+ hBrush_,
+ GetDisplay()->GetCopyGC( m_nXScreen ),
+ pImage,
+ 0, 0, // Source
+ 0, 0, // Destination
+ 8, 8 ); // width & height
+
+ // destroy image-frame but not palette-data
+ pImage->data = nullptr;
+ XDestroyImage( pImage );
+
+ return true;
+}
+
+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
+}
+
+XRenderPictFormat* X11SalGraphics::GetXRenderFormat() const
+{
+ if( m_pXRenderFormat == nullptr )
+ m_pXRenderFormat = XRenderPeer::GetInstance().FindVisualFormat( GetVisual().visual );
+ return m_pXRenderFormat;
+}
+
+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();
+ aRes.pXRenderFormat = m_pXRenderFormat;
+ return aRes;
+}
+
+void X11SalGraphics::Flush()
+{
+ if( X11GraphicsImpl* x11Impl = dynamic_cast< X11GraphicsImpl* >( mxImpl.get()))
+ x11Impl->Flush();
+}
+
+#if ENABLE_CAIRO_CANVAS
+
+bool X11SalGraphics::SupportsCairo() const
+{
+ return maX11Common.SupportsCairo();
+}
+
+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)),
+ css::uno::Any(sal_Int32( rXlibSurface.getDepth() ))
+ };
+ 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);
+}
+
+cairo_t* X11SalGraphics::getCairoContext()
+{
+ return maX11Common.getCairoContext();
+}
+
+void X11SalGraphics::releaseCairoContext(cairo_t* cr)
+{
+ X11Common::releaseCairoContext(cr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/salgdi2.cxx b/vcl/unx/generic/gdi/salgdi2.cxx
new file mode 100644
index 000000000..f26048ae1
--- /dev/null
+++ b/vcl/unx/generic/gdi/salgdi2.cxx
@@ -0,0 +1,89 @@
+/* -*- 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 <salgdiimpl.hxx>
+
+#include <vcl/sysdata.hxx>
+
+#include <unx/saldisp.hxx>
+#include <unx/salgdi.h>
+#include <unx/x11/xrender_peer.hxx>
+#include <salframe.hxx>
+
+extern "C"
+{
+ static Bool GraphicsExposePredicate( Display*, XEvent* pEvent, const XPointer pFrameWindow )
+ {
+ Bool bRet = False;
+ if( (pEvent->type == GraphicsExpose || pEvent->type == NoExpose) &&
+ pEvent->xnoexpose.drawable == reinterpret_cast<Drawable>(pFrameWindow) )
+ {
+ bRet = True;
+ }
+ return bRet;
+ }
+}
+
+void X11SalGraphics::YieldGraphicsExpose()
+{
+ // get frame if necessary
+ SalFrame* pFrame = m_pFrame;
+ Display* pDisplay = GetXDisplay();
+ ::Window aWindow = GetDrawable();
+ if( ! pFrame )
+ {
+ for (auto pSalFrame : vcl_sal::getSalDisplay(GetGenericUnixSalData())->getFrames() )
+ {
+ const SystemEnvData* pEnvData = pSalFrame->GetSystemData();
+ if( Drawable(pEnvData->GetWindowHandle(pSalFrame)) == aWindow )
+ {
+ pFrame = pSalFrame;
+ break;
+ }
+ }
+ if( ! pFrame )
+ return;
+ }
+
+ XEvent aEvent;
+ while( XCheckTypedWindowEvent( pDisplay, aWindow, Expose, &aEvent ) )
+ {
+ SalPaintEvent aPEvt( aEvent.xexpose.x, aEvent.xexpose.y, aEvent.xexpose.width+1, aEvent.xexpose.height+1 );
+ pFrame->CallCallback( SalEvent::Paint, &aPEvt );
+ }
+
+ do
+ {
+ if( ! GetDisplay()->XIfEventWithTimeout( &aEvent, reinterpret_cast<XPointer>(aWindow), GraphicsExposePredicate ) )
+ // this should not happen at all; still sometimes it happens
+ break;
+
+ if( aEvent.type == NoExpose )
+ break;
+
+ if( pFrame )
+ {
+ SalPaintEvent aPEvt( aEvent.xgraphicsexpose.x, aEvent.xgraphicsexpose.y, aEvent.xgraphicsexpose.width+1, aEvent.xgraphicsexpose.height+1 );
+ pFrame->CallCallback( SalEvent::Paint, &aPEvt );
+ }
+ } while( aEvent.xgraphicsexpose.count != 0 );
+}
+
+
+/* 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 000000000..f5e4449c6
--- /dev/null
+++ b/vcl/unx/generic/gdi/salvd.cxx
@@ -0,0 +1,222 @@
+/* -*- 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 <X11/extensions/Xrender.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
+
+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, cairo_surface_t* pPreExistingTarget, 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;
+
+ bWindow_ = pDisplay->IsDisplay();
+ bVirDev_ = true;
+
+ SetDrawable(pDevice->GetDrawable(), pPreExistingTarget, 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;
+ }
+
+ XRenderPictFormat* pXRenderFormat = pData ? static_cast<XRenderPictFormat*>(pData->pXRenderFormat) : nullptr;
+ if( pXRenderFormat )
+ {
+ pGraphics_->SetXRenderFormat( pXRenderFormat );
+ if( pXRenderFormat->colormap )
+ pColormap = new SalColormap( pDisplay_, pXRenderFormat->colormap, m_nXScreen );
+ else
+ pColormap = new SalColormap( nBitCount );
+ bDeleteColormap = true;
+ }
+ else 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;
+
+ pGraphics_->Init( this, pPreExistingTarget, pColormap, bDeleteColormap );
+}
+
+X11SalVirtualDevice::~X11SalVirtualDevice()
+{
+ pGraphics_.reset();
+
+ 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;
+
+ 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;
+ }
+ return false;
+ }
+
+ if( GetDrawable() )
+ XFreePixmap( GetXDisplay(), GetDrawable() );
+ hDrawable_ = h;
+
+ nDX_ = nDX;
+ nDY_ = nDY;
+
+ if( pGraphics_ )
+ pGraphics_->Init( this );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/x11cairotextrender.cxx b/vcl/unx/generic/gdi/x11cairotextrender.cxx
new file mode 100644
index 000000000..6bbbbc1bf
--- /dev/null
+++ b/vcl/unx/generic/gdi/x11cairotextrender.cxx
@@ -0,0 +1,66 @@
+/* -*- 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 <unx/x11/x11cairotextrender.hxx>
+
+#include <unx/glyphcache.hxx>
+#include <X11/Xregion.h>
+#include <cairo.h>
+#include <salframe.hxx>
+#include <salvd.hxx>
+
+X11CairoTextRender::X11CairoTextRender(X11SalGraphics& rParent)
+ : mrParent(rParent)
+{
+}
+
+cairo_t* X11CairoTextRender::getCairoContext()
+{
+ return mrParent.getCairoContext();
+}
+
+void X11CairoTextRender::getSurfaceOffset( double& nDX, double& nDY )
+{
+ nDX = 0;
+ nDY = 0;
+}
+
+void X11CairoTextRender::clipRegion(cairo_t* cr)
+{
+ Region pClipRegion = mrParent.mpClipRegion;
+ if( pClipRegion && !XEmptyRegion( pClipRegion ) )
+ {
+ for (tools::Long i = 0; i < pClipRegion->numRects; ++i)
+ {
+ cairo_rectangle(cr,
+ pClipRegion->rects[i].x1,
+ pClipRegion->rects[i].y1,
+ pClipRegion->rects[i].x2 - pClipRegion->rects[i].x1,
+ pClipRegion->rects[i].y2 - pClipRegion->rects[i].y1);
+ }
+ cairo_clip(cr);
+ }
+}
+
+void X11CairoTextRender::releaseCairoContext(cairo_t* cr)
+{
+ X11SalGraphics::releaseCairoContext(cr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/gdi/xrender_peer.cxx b/vcl/unx/generic/gdi/xrender_peer.cxx
new file mode 100644
index 000000000..961f4cd3a
--- /dev/null
+++ b/vcl/unx/generic/gdi/xrender_peer.cxx
@@ -0,0 +1,48 @@
+/* -*- 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 <unx/saldisp.hxx>
+
+#include <unx/x11/xrender_peer.hxx>
+
+XRenderPeer::XRenderPeer()
+ : mpDisplay( vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay() )
+ , mpStandardFormatA8( nullptr )
+{
+ InitRenderLib();
+}
+
+XRenderPeer& XRenderPeer::GetInstance()
+{
+ static XRenderPeer aPeer;
+ return aPeer;
+}
+
+void XRenderPeer::InitRenderLib()
+{
+ int nDummy;
+ // needed to initialize libXrender internals
+ XRenderQueryExtension( mpDisplay, &nDummy, &nDummy );
+
+ // the 8bit alpha mask format must be there
+ XRenderPictFormat aPictFormat={0,0,8,{0,0,0,0,0,0,0,0xFF},0};
+ mpStandardFormatA8 = XRenderFindFormat( mpDisplay, PictFormatAlphaMask|PictFormatDepth, &aPictFormat, 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */