1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/extensions/Xrender.h>
23 
24 
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <basegfx/polygon/b2dpolypolygon.hxx>
27 #include <basegfx/polygon/b2dpolypolygontools.hxx>
28 #include <basegfx/matrix/b2dhommatrix.hxx>
29 #include <basegfx/curve/b2dcubicbezier.hxx>
30 
31 #include <headless/svpgdi.hxx>
32 
33 #include <vcl/sysdata.hxx>
34 #include <vcl/virdev.hxx>
35 #include <sal/log.hxx>
36 
37 #include <unx/salunx.h>
38 #include <unx/saldisp.hxx>
39 #include <unx/salgdi.h>
40 #include <unx/x11/xlimits.hxx>
41 
42 #include <salframe.hxx>
43 #include <salgdiimpl.hxx>
44 #include <textrender.hxx>
45 #include <salvd.hxx>
46 #include "gdiimpl.hxx"
47 
48 #include <unx/x11/x11cairotextrender.hxx>
49 #include <unx/x11/xrender_peer.hxx>
50 #include "cairo_xlib_cairo.hxx"
51 #include <cairo-xlib.h>
52 
53 #include <config_features.h>
54 #include <vcl/skia/SkiaHelper.hxx>
55 #if HAVE_FEATURE_SKIA
56 #include <skia/x11/gdiimpl.hxx>
57 #include <skia/x11/textrender.hxx>
58 #endif
59 
X11SalGraphics()60 X11SalGraphics::X11SalGraphics():
61     m_pFrame(nullptr),
62     m_pVDev(nullptr),
63     m_pColormap(nullptr),
64     hDrawable_(None),
65     m_pExternalSurface(nullptr),
66     m_nXScreen( 0 ),
67     m_pXRenderFormat(nullptr),
68     m_aXRenderPicture(0),
69     mpClipRegion(nullptr),
70 #if ENABLE_CAIRO_CANVAS
71     maClipRegion(),
72     mnPenColor(SALCOLOR_NONE),
73     mnFillColor(SALCOLOR_NONE),
74 #endif // ENABLE_CAIRO_CANVAS
75     hBrush_(None),
76     bWindow_(false),
77     bVirDev_(false),
78     m_bSkia(SkiaHelper::isVCLSkiaEnabled())
79 {
80 #if HAVE_FEATURE_SKIA
81     if (m_bSkia)
82     {
83         mxImpl.reset(new X11SkiaSalGraphicsImpl(*this));
84         mxTextRenderImpl.reset(new SkiaTextRender);
85     }
86     else
87 #endif
88     {
89         mxTextRenderImpl.reset(new X11CairoTextRender(*this));
90         mxImpl.reset(new X11SalGraphicsImpl(*this));
91     }
92 
93 }
94 
~X11SalGraphics()95 X11SalGraphics::~X11SalGraphics() COVERITY_NOEXCEPT_FALSE
96 {
97     DeInit();
98     ReleaseFonts();
99     freeResources();
100 }
101 
freeResources()102 void X11SalGraphics::freeResources()
103 {
104     Display *pDisplay = GetXDisplay();
105 
106     if( mpClipRegion )
107     {
108         XDestroyRegion( mpClipRegion );
109         mpClipRegion = None;
110     }
111 
112     mxImpl->freeResources();
113 
114     if( hBrush_ )
115     {
116         XFreePixmap( pDisplay, hBrush_ );
117         hBrush_ = None;
118     }
119     if( m_pDeleteColormap )
120     {
121         m_pDeleteColormap.reset();
122         m_pColormap = nullptr;
123     }
124     if( m_aXRenderPicture )
125     {
126         XRenderPeer::GetInstance().FreePicture( m_aXRenderPicture );
127         m_aXRenderPicture = 0;
128     }
129 }
130 
GetImpl() const131 SalGraphicsImpl* X11SalGraphics::GetImpl() const
132 {
133     return mxImpl.get();
134 }
135 
SetDrawable(Drawable aDrawable,cairo_surface_t * pExternalSurface,SalX11Screen nXScreen)136 void X11SalGraphics::SetDrawable(Drawable aDrawable, cairo_surface_t* pExternalSurface, SalX11Screen nXScreen)
137 {
138     m_pExternalSurface = pExternalSurface;
139 
140     // shortcut if nothing changed
141     if( hDrawable_ == aDrawable )
142         return;
143 
144     // free screen specific resources if needed
145     if( nXScreen != m_nXScreen )
146     {
147         freeResources();
148         m_pColormap = &vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetColormap( nXScreen );
149         m_nXScreen = nXScreen;
150     }
151 
152     hDrawable_ = aDrawable;
153     SetXRenderFormat( nullptr );
154     if( m_aXRenderPicture )
155     {
156         XRenderPeer::GetInstance().FreePicture( m_aXRenderPicture );
157         m_aXRenderPicture = 0;
158     }
159 }
160 
Init(SalFrame * pFrame,Drawable aTarget,SalX11Screen nXScreen)161 void X11SalGraphics::Init( SalFrame *pFrame, Drawable aTarget,
162                            SalX11Screen nXScreen )
163 {
164     m_pColormap = &vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetColormap(nXScreen);
165     m_nXScreen  = nXScreen;
166 
167     m_pFrame    = pFrame;
168     m_pVDev     = nullptr;
169 
170     bWindow_    = true;
171     bVirDev_    = false;
172 
173     SetDrawable(aTarget, nullptr, nXScreen);
174     mxImpl->Init();
175 }
176 
DeInit()177 void X11SalGraphics::DeInit()
178 {
179     mxImpl->DeInit();
180     SetDrawable(None, nullptr, m_nXScreen);
181 }
182 
SetClipRegion(GC pGC,Region pXReg) const183 void X11SalGraphics::SetClipRegion( GC pGC, Region pXReg ) const
184 {
185     Display *pDisplay = GetXDisplay();
186 
187     int n = 0;
188     Region Regions[3];
189 
190     if( mpClipRegion )
191         Regions[n++] = mpClipRegion;
192 
193     if( pXReg && !XEmptyRegion( pXReg ) )
194         Regions[n++] = pXReg;
195 
196     if( 0 == n )
197         XSetClipMask( pDisplay, pGC, None );
198     else if( 1 == n )
199         XSetRegion( pDisplay, pGC, Regions[0] );
200     else
201     {
202         Region pTmpRegion = XCreateRegion();
203         XIntersectRegion( Regions[0], Regions[1], pTmpRegion );
204 
205         XSetRegion( pDisplay, pGC, pTmpRegion );
206         XDestroyRegion( pTmpRegion );
207     }
208 }
209 
210 // Calculate a dither-pixmap and make a brush of it
211 #define P_DELTA         51
212 #define DMAP( v, m )    ((v % P_DELTA) > m ? (v / P_DELTA) + 1 : (v / P_DELTA))
213 
GetDitherPixmap(Color nColor)214 bool X11SalGraphics::GetDitherPixmap( Color nColor )
215 {
216     static const short nOrdDither8Bit[ 8 ][ 8 ] =
217     {
218         { 0, 38,  9, 48,  2, 40, 12, 50},
219         {25, 12, 35, 22, 28, 15, 37, 24},
220         { 6, 44,  3, 41,  8, 47,  5, 44},
221         {32, 19, 28, 16, 34, 21, 31, 18},
222         { 1, 40, 11, 49,  0, 39, 10, 48},
223         {27, 14, 36, 24, 26, 13, 36, 23},
224         { 8, 46,  4, 43,  7, 45,  4, 42},
225         {33, 20, 30, 17, 32, 20, 29, 16}
226     };
227 
228     // test for correct depth (8bit)
229     if( GetColormap().GetVisual().GetDepth() != 8 )
230         return false;
231 
232     char    pBits[64];
233     char   *pBitsPtr = pBits;
234 
235     // Set the palette-entries for the dithering tile
236     sal_uInt8 nColorRed   = nColor.GetRed();
237     sal_uInt8 nColorGreen = nColor.GetGreen();
238     sal_uInt8 nColorBlue  = nColor.GetBlue();
239 
240     for(auto & nY : nOrdDither8Bit)
241     {
242         for( int nX = 0; nX < 8; nX++ )
243         {
244             short nMagic = nY[nX];
245             sal_uInt8 nR   = P_DELTA * DMAP( nColorRed,   nMagic );
246             sal_uInt8 nG   = P_DELTA * DMAP( nColorGreen, nMagic );
247             sal_uInt8 nB   = P_DELTA * DMAP( nColorBlue,  nMagic );
248 
249             *pBitsPtr++ = GetColormap().GetPixel( Color( nR, nG, nB ) );
250         }
251     }
252 
253     // create the tile as ximage and an according pixmap -> caching
254     XImage *pImage = XCreateImage( GetXDisplay(),
255                                    GetColormap().GetXVisual(),
256                                    8,
257                                    ZPixmap,
258                                    0,               // offset
259                                    pBits,           // data
260                                    8, 8,            // width & height
261                                    8,               // bitmap_pad
262                                    0 );             // (default) bytes_per_line
263 
264     if( !hBrush_ )
265         hBrush_ = limitXCreatePixmap( GetXDisplay(), GetDrawable(), 8, 8, 8 );
266 
267     // put the ximage to the pixmap
268     XPutImage( GetXDisplay(),
269                hBrush_,
270                GetDisplay()->GetCopyGC( m_nXScreen ),
271                pImage,
272                0, 0,                        // Source
273                0, 0,                        // Destination
274                8, 8 );                      // width & height
275 
276     // destroy image-frame but not palette-data
277     pImage->data = nullptr;
278     XDestroyImage( pImage );
279 
280     return true;
281 }
282 
GetResolution(sal_Int32 & rDPIX,sal_Int32 & rDPIY)283 void X11SalGraphics::GetResolution( sal_Int32 &rDPIX, sal_Int32 &rDPIY ) // const
284 {
285     char* pForceDpi;
286     if ((pForceDpi = getenv("SAL_FORCEDPI")))
287     {
288         OString sForceDPI(pForceDpi);
289         rDPIX = rDPIY = sForceDPI.toInt32();
290         return;
291     }
292 
293     const SalDisplay *pDisplay = GetDisplay();
294     if (!pDisplay)
295     {
296         SAL_WARN( "vcl", "Null display");
297         rDPIX = rDPIY = 96;
298         return;
299     }
300 
301     Pair dpi = pDisplay->GetResolution();
302     rDPIX = dpi.A();
303     rDPIY = dpi.B();
304 
305     if ( rDPIY > 200 )
306     {
307         rDPIX = Divide( rDPIX * 200, rDPIY );
308         rDPIY = 200;
309     }
310 
311     // #i12705# equalize x- and y-resolution if they are close enough
312     if( rDPIX == rDPIY )
313         return;
314 
315     // different x- and y- resolutions are usually artifacts of
316     // a wrongly calculated screen size.
317 #ifdef DEBUG
318     SAL_INFO("vcl.gdi", "Forcing Resolution from "
319         << std::hex << rDPIX
320         << std::dec << rDPIX
321         << " to "
322         << std::hex << rDPIY
323         << std::dec << rDPIY);
324 #endif
325     rDPIX = rDPIY; // y-resolution is more trustworthy
326 }
327 
GetBitCount() const328 sal_uInt16 X11SalGraphics::GetBitCount() const
329 {
330     return mxImpl->GetBitCount();
331 }
332 
GetGraphicsWidth() const333 tools::Long X11SalGraphics::GetGraphicsWidth() const
334 {
335     return mxImpl->GetGraphicsWidth();
336 }
337 
ResetClipRegion()338 void X11SalGraphics::ResetClipRegion()
339 {
340 #if ENABLE_CAIRO_CANVAS
341     maClipRegion.SetNull();
342 #endif
343     mxImpl->ResetClipRegion();
344 }
345 
setClipRegion(const vcl::Region & i_rClip)346 bool X11SalGraphics::setClipRegion( const vcl::Region& i_rClip )
347 {
348 #if ENABLE_CAIRO_CANVAS
349     maClipRegion = i_rClip;
350 #endif
351     return mxImpl->setClipRegion( i_rClip );
352 }
353 
SetLineColor()354 void X11SalGraphics::SetLineColor()
355 {
356 #if ENABLE_CAIRO_CANVAS
357     mnPenColor = SALCOLOR_NONE;
358 #endif // ENABLE_CAIRO_CANVAS
359 
360     mxImpl->SetLineColor();
361 }
362 
SetLineColor(Color nColor)363 void X11SalGraphics::SetLineColor( Color nColor )
364 {
365 #if ENABLE_CAIRO_CANVAS
366     mnPenColor = nColor;
367 #endif // ENABLE_CAIRO_CANVAS
368 
369     mxImpl->SetLineColor( nColor );
370 }
371 
SetFillColor()372 void X11SalGraphics::SetFillColor()
373 {
374 #if ENABLE_CAIRO_CANVAS
375     mnFillColor = SALCOLOR_NONE;
376 #endif // ENABLE_CAIRO_CANVAS
377 
378     mxImpl->SetFillColor();
379 }
380 
SetFillColor(Color nColor)381 void X11SalGraphics::SetFillColor( Color nColor )
382 {
383 #if ENABLE_CAIRO_CANVAS
384     mnFillColor = nColor;
385 #endif // ENABLE_CAIRO_CANVAS
386 
387     mxImpl->SetFillColor( nColor );
388 }
389 
SetROPLineColor(SalROPColor nROPColor)390 void X11SalGraphics::SetROPLineColor( SalROPColor nROPColor )
391 {
392     mxImpl->SetROPLineColor( nROPColor );
393 }
394 
SetROPFillColor(SalROPColor nROPColor)395 void X11SalGraphics::SetROPFillColor( SalROPColor nROPColor )
396 {
397     mxImpl->SetROPFillColor( nROPColor );
398 }
399 
SetXORMode(bool bSet,bool bInvertOnly)400 void X11SalGraphics::SetXORMode( bool bSet, bool bInvertOnly )
401 {
402     mxImpl->SetXORMode( bSet, bInvertOnly );
403 }
404 
drawPixel(tools::Long nX,tools::Long nY)405 void X11SalGraphics::drawPixel( tools::Long nX, tools::Long nY )
406 {
407     mxImpl->drawPixel( nX, nY );
408 }
409 
drawPixel(tools::Long nX,tools::Long nY,Color nColor)410 void X11SalGraphics::drawPixel( tools::Long nX, tools::Long nY, Color nColor )
411 {
412     mxImpl->drawPixel( nX, nY, nColor );
413 }
414 
drawLine(tools::Long nX1,tools::Long nY1,tools::Long nX2,tools::Long nY2)415 void X11SalGraphics::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 )
416 {
417     mxImpl->drawLine( nX1, nY1, nX2, nY2 );
418 }
419 
drawRect(tools::Long nX,tools::Long nY,tools::Long nDX,tools::Long nDY)420 void X11SalGraphics::drawRect( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY )
421 {
422     mxImpl->drawRect( nX, nY, nDX, nDY );
423 }
424 
drawPolyLine(sal_uInt32 nPoints,const Point * pPtAry)425 void X11SalGraphics::drawPolyLine( sal_uInt32 nPoints, const Point *pPtAry )
426 {
427     mxImpl->drawPolyLine( nPoints, pPtAry );
428 }
429 
drawPolygon(sal_uInt32 nPoints,const Point * pPtAry)430 void X11SalGraphics::drawPolygon( sal_uInt32 nPoints, const Point* pPtAry )
431 {
432     mxImpl->drawPolygon( nPoints, pPtAry );
433 }
434 
drawPolyPolygon(sal_uInt32 nPoly,const sal_uInt32 * pPoints,const Point ** pPtAry)435 void X11SalGraphics::drawPolyPolygon( sal_uInt32 nPoly,
436                                    const sal_uInt32    *pPoints,
437                                    const Point*  *pPtAry )
438 {
439     mxImpl->drawPolyPolygon( nPoly, pPoints, pPtAry );
440 }
441 
drawPolyLineBezier(sal_uInt32 nPoints,const Point * pPtAry,const PolyFlags * pFlgAry)442 bool X11SalGraphics::drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
443 {
444     return mxImpl->drawPolyLineBezier( nPoints, pPtAry, pFlgAry );
445 }
446 
drawPolygonBezier(sal_uInt32 nPoints,const Point * pPtAry,const PolyFlags * pFlgAry)447 bool X11SalGraphics::drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
448 {
449     return mxImpl->drawPolygonBezier( nPoints, pPtAry, pFlgAry );
450 }
451 
drawPolyPolygonBezier(sal_uInt32 nPoints,const sal_uInt32 * pPoints,const Point * const * pPtAry,const PolyFlags * const * pFlgAry)452 bool X11SalGraphics::drawPolyPolygonBezier( sal_uInt32 nPoints, const sal_uInt32* pPoints,
453                                                 const Point* const* pPtAry, const PolyFlags* const* pFlgAry)
454 {
455     return mxImpl->drawPolyPolygonBezier( nPoints, pPoints, pPtAry, pFlgAry );
456 }
457 
invert(sal_uInt32 nPoints,const Point * pPtAry,SalInvert nFlags)458 void X11SalGraphics::invert( sal_uInt32 nPoints,
459                              const Point* pPtAry,
460                              SalInvert nFlags )
461 {
462     mxImpl->invert( nPoints, pPtAry, nFlags );
463 }
464 
drawEPS(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,void * pPtr,sal_uInt32 nSize)465 bool X11SalGraphics::drawEPS( tools::Long nX, tools::Long nY, tools::Long nWidth,
466         tools::Long nHeight, void* pPtr, sal_uInt32 nSize )
467 {
468     return mxImpl->drawEPS( nX, nY, nWidth, nHeight, pPtr, nSize );
469 }
470 
GetXRenderFormat() const471 XRenderPictFormat* X11SalGraphics::GetXRenderFormat() const
472 {
473     if( m_pXRenderFormat == nullptr )
474         m_pXRenderFormat = XRenderPeer::GetInstance().FindVisualFormat( GetVisual().visual );
475     return m_pXRenderFormat;
476 }
477 
GetGraphicsData() const478 SystemGraphicsData X11SalGraphics::GetGraphicsData() const
479 {
480     SystemGraphicsData aRes;
481 
482     aRes.nSize = sizeof(aRes);
483     aRes.pDisplay  = GetXDisplay();
484     aRes.hDrawable = hDrawable_;
485     aRes.pVisual   = GetVisual().visual;
486     aRes.nScreen   = m_nXScreen.getXScreen();
487     aRes.pXRenderFormat = m_pXRenderFormat;
488     return aRes;
489 }
490 
Flush()491 void X11SalGraphics::Flush()
492 {
493     if( X11GraphicsImpl* x11Impl = dynamic_cast< X11GraphicsImpl* >( mxImpl.get()))
494         x11Impl->Flush();
495 }
496 
497 #if ENABLE_CAIRO_CANVAS
498 
SupportsCairo() const499 bool X11SalGraphics::SupportsCairo() const
500 {
501     static bool bSupportsCairo = [this] {
502         Display *pDisplay = GetXDisplay();
503         int nDummy;
504         return XQueryExtension(pDisplay, "RENDER", &nDummy, &nDummy, &nDummy);
505     }();
506     return bSupportsCairo;
507 }
508 
CreateSurface(const cairo::CairoSurfaceSharedPtr & rSurface) const509 cairo::SurfaceSharedPtr X11SalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr& rSurface) const
510 {
511     return std::make_shared<cairo::X11Surface>(rSurface);
512 }
513 
514 namespace
515 {
getSysData(const vcl::Window & rWindow)516     cairo::X11SysData getSysData( const vcl::Window& rWindow )
517     {
518         const SystemEnvData* pSysData = rWindow.GetSystemData();
519 
520         if( !pSysData )
521             return cairo::X11SysData();
522         else
523             return cairo::X11SysData(*pSysData, rWindow.ImplGetFrame());
524     }
525 
getSysData(const VirtualDevice & rVirDev)526     cairo::X11SysData getSysData( const VirtualDevice& rVirDev )
527     {
528         return cairo::X11SysData( rVirDev.GetSystemGfxData() );
529     }
530 }
531 
CreateSurface(const OutputDevice & rRefDevice,int x,int y,int width,int height) const532 cairo::SurfaceSharedPtr X11SalGraphics::CreateSurface( const OutputDevice& rRefDevice,
533                                 int x, int y, int width, int height ) const
534 {
535     if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
536         return std::make_shared<cairo::X11Surface>(getSysData(*rRefDevice.GetOwnerWindow()),
537                                                x,y,width,height);
538     if( rRefDevice.IsVirtual() )
539         return std::make_shared<cairo::X11Surface>(getSysData(static_cast<const VirtualDevice&>(rRefDevice)),
540                                                x,y,width,height);
541     return cairo::SurfaceSharedPtr();
542 }
543 
CreateBitmapSurface(const OutputDevice & rRefDevice,const BitmapSystemData & rData,const Size & rSize) const544 cairo::SurfaceSharedPtr X11SalGraphics::CreateBitmapSurface( const OutputDevice&     rRefDevice,
545                                       const BitmapSystemData& rData,
546                                       const Size&             rSize ) const
547 {
548     SAL_INFO("vcl", "requested size: " << rSize.Width() << " x " << rSize.Height()
549               << " available size: " << rData.mnWidth << " x "
550               << rData.mnHeight);
551     if ( rData.mnWidth == rSize.Width() && rData.mnHeight == rSize.Height() )
552     {
553         if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
554             return std::make_shared<cairo::X11Surface>(getSysData(*rRefDevice.GetOwnerWindow()), rData );
555         else if( rRefDevice.IsVirtual() )
556             return std::make_shared<cairo::X11Surface>(getSysData(static_cast<const VirtualDevice&>(rRefDevice)), rData );
557     }
558 
559     return cairo::SurfaceSharedPtr();
560 }
561 
GetNativeSurfaceHandle(cairo::SurfaceSharedPtr & rSurface,const basegfx::B2ISize &) const562 css::uno::Any X11SalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rSurface, const basegfx::B2ISize& /*rSize*/) const
563 {
564     cairo::X11Surface& rXlibSurface=dynamic_cast<cairo::X11Surface&>(*rSurface);
565     css::uno::Sequence< css::uno::Any > args( 3 );
566     args[0] <<= false;  // do not call XFreePixmap on it
567     args[1] <<= sal_Int64(rXlibSurface.getPixmap()->mhDrawable);
568     args[2] <<= sal_Int32( rXlibSurface.getDepth() );
569     return css::uno::Any(args);
570 }
571 
572 #endif // ENABLE_CAIRO_CANVAS
573 
574 // draw a poly-polygon
drawPolyPolygon(const basegfx::B2DHomMatrix & rObjectToDevice,const basegfx::B2DPolyPolygon & rPolyPolygon,double fTransparency)575 bool X11SalGraphics::drawPolyPolygon(
576     const basegfx::B2DHomMatrix& rObjectToDevice,
577     const basegfx::B2DPolyPolygon& rPolyPolygon,
578     double fTransparency)
579 {
580     if(fTransparency >= 1.0)
581     {
582         return true;
583     }
584 
585     if(rPolyPolygon.count() == 0)
586     {
587         return true;
588     }
589 
590 #if ENABLE_CAIRO_CANVAS
591     // Fallback: Transform to DeviceCoordinates
592     basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
593     aPolyPolygon.transform(rObjectToDevice);
594 
595     if(SALCOLOR_NONE == mnFillColor && SALCOLOR_NONE == mnPenColor)
596     {
597         return true;
598     }
599 
600     // enable by setting to something
601     static const char* pUseCairoForPolygons(getenv("SAL_ENABLE_USE_CAIRO_FOR_POLYGONS"));
602 
603     if (!m_bSkia && nullptr != pUseCairoForPolygons && SupportsCairo())
604     {
605         // snap to raster if requested
606         const bool bSnapPoints(!getAntiAlias());
607 
608         if(bSnapPoints)
609         {
610             aPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygon);
611         }
612 
613         cairo_t* cr = getCairoContext();
614         clipRegion(cr);
615 
616         for(auto const& rPolygon : aPolyPolygon)
617         {
618             const sal_uInt32 nPointCount(rPolygon.count());
619 
620             if(nPointCount)
621             {
622                 const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nPointCount : nPointCount - 1);
623 
624                 if(nEdgeCount)
625                 {
626                     basegfx::B2DCubicBezier aEdge;
627 
628                     for(sal_uInt32 b = 0; b < nEdgeCount; ++b)
629                     {
630                         rPolygon.getBezierSegment(b, aEdge);
631 
632                         if(!b)
633                         {
634                             const basegfx::B2DPoint aStart(aEdge.getStartPoint());
635                             cairo_move_to(cr, aStart.getX(), aStart.getY());
636                         }
637 
638                         const basegfx::B2DPoint aEnd(aEdge.getEndPoint());
639 
640                         if(aEdge.isBezier())
641                         {
642                             const basegfx::B2DPoint aCP1(aEdge.getControlPointA());
643                             const basegfx::B2DPoint aCP2(aEdge.getControlPointB());
644                             cairo_curve_to(cr,
645                                 aCP1.getX(), aCP1.getY(),
646                                 aCP2.getX(), aCP2.getY(),
647                                 aEnd.getX(), aEnd.getY());
648                         }
649                         else
650                         {
651                             cairo_line_to(cr, aEnd.getX(), aEnd.getY());
652                         }
653                     }
654 
655                     cairo_close_path(cr);
656                 }
657             }
658         }
659 
660         if(SALCOLOR_NONE != mnFillColor)
661         {
662             cairo_set_source_rgba(cr,
663                 mnFillColor.GetRed()/255.0,
664                 mnFillColor.GetGreen()/255.0,
665                 mnFillColor.GetBlue()/255.0,
666                 1.0 - fTransparency);
667             cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
668             cairo_fill_preserve(cr);
669         }
670 
671         if(SALCOLOR_NONE != mnPenColor)
672         {
673             cairo_set_source_rgba(cr,
674                     mnPenColor.GetRed()/255.0,
675                     mnPenColor.GetGreen()/255.0,
676                     mnPenColor.GetBlue()/255.0,
677                     1.0 - fTransparency);
678             cairo_stroke_preserve(cr);
679         }
680 
681         releaseCairoContext(cr);
682         return true;
683     }
684 #endif // ENABLE_CAIRO_CANVAS
685 
686     return mxImpl->drawPolyPolygon(
687         rObjectToDevice,
688         rPolyPolygon,
689         fTransparency);
690 }
691 
692 #if ENABLE_CAIRO_CANVAS
clipRegion(cairo_t * cr)693 void X11SalGraphics::clipRegion(cairo_t* cr)
694 {
695     SvpSalGraphics::clipRegion(cr, maClipRegion);
696 }
697 #endif // ENABLE_CAIRO_CANVAS
698 
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)699 bool X11SalGraphics::drawPolyLine(
700     const basegfx::B2DHomMatrix& rObjectToDevice,
701     const basegfx::B2DPolygon& rPolygon,
702     double fTransparency,
703     double fLineWidth,
704     const std::vector< double >* pStroke, // MM01
705     basegfx::B2DLineJoin eLineJoin,
706     css::drawing::LineCap eLineCap,
707     double fMiterMinimumAngle,
708     bool bPixelSnapHairline)
709 {
710     if(0 == rPolygon.count())
711     {
712         return true;
713     }
714 
715     if(fTransparency >= 1.0)
716     {
717         return true;
718     }
719 
720 #if ENABLE_CAIRO_CANVAS
721     // disable by setting to something
722     static const char* pUseCairoForFatLines(getenv("SAL_DISABLE_USE_CAIRO_FOR_FATLINES"));
723 
724     if (!m_bSkia && nullptr == pUseCairoForFatLines && SupportsCairo())
725     {
726         cairo_t* cr = getCairoContext();
727         clipRegion(cr);
728 
729         // Use the now available static drawPolyLine from the Cairo-Headless-Fallback
730         // that will take care of all needed stuff
731         const bool bRetval(
732             SvpSalGraphics::drawPolyLine(
733                 cr,
734                 nullptr,
735                 mnPenColor,
736                 getAntiAlias(),
737                 rObjectToDevice,
738                 rPolygon,
739                 fTransparency,
740                 fLineWidth,
741                 pStroke, // MM01
742                 eLineJoin,
743                 eLineCap,
744                 fMiterMinimumAngle,
745                 bPixelSnapHairline));
746 
747         releaseCairoContext(cr);
748 
749         if(bRetval)
750         {
751             return true;
752         }
753     }
754 #endif // ENABLE_CAIRO_CANVAS
755 
756     return mxImpl->drawPolyLine(
757         rObjectToDevice,
758         rPolygon,
759         fTransparency,
760         fLineWidth,
761         pStroke, // MM01
762         eLineJoin,
763         eLineCap,
764         fMiterMinimumAngle,
765         bPixelSnapHairline);
766 }
767 
drawGradient(const tools::PolyPolygon & rPoly,const Gradient & rGradient)768 bool X11SalGraphics::drawGradient(const tools::PolyPolygon& rPoly, const Gradient& rGradient)
769 {
770     return mxImpl->drawGradient(rPoly, rGradient);
771 }
772 
implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon,SalGradient const & rGradient)773 bool X11SalGraphics::implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient)
774 {
775     return mxImpl->implDrawGradient(rPolyPolygon, rGradient);
776 }
777 
GetGeometryProvider() const778 SalGeometryProvider *X11SalGraphics::GetGeometryProvider() const
779 {
780     if (m_pFrame)
781         return static_cast< SalGeometryProvider * >(m_pFrame);
782     else
783         return static_cast< SalGeometryProvider * >(m_pVDev);
784 }
785 
getCairoContext()786 cairo_t* X11SalGraphics::getCairoContext()
787 {
788     if (m_pExternalSurface)
789         return cairo_create(m_pExternalSurface);
790 
791     cairo_surface_t* surface = cairo_xlib_surface_create(GetXDisplay(), hDrawable_,
792             GetVisual().visual, SAL_MAX_INT16, SAL_MAX_INT16);
793 
794     cairo_t *cr = cairo_create(surface);
795     cairo_surface_destroy(surface);
796 
797     return cr;
798 }
799 
releaseCairoContext(cairo_t * cr)800 void X11SalGraphics::releaseCairoContext(cairo_t* cr)
801 {
802    cairo_destroy(cr);
803 }
804 
805 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
806