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