1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include "headless/svpgdi.hxx"
22 #include "headless/svpbmp.hxx"
23 #include "headless/svpframe.hxx"
24 #include "headless/svpcairotextrender.hxx"
25 #include "saldatabasic.hxx"
27 #include <vcl/sysdata.hxx>
28 #include <config_cairo_canvas.h>
29 #include <basegfx/numeric/ftools.hxx>
30 #include <basegfx/range/b2drange.hxx>
31 #include <basegfx/range/b2ibox.hxx>
32 #include <basegfx/polygon/b2dpolypolygon.hxx>
33 #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #if ENABLE_CAIRO_CANVAS
40 #if defined CAIRO_VERSION && CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 10, 0)
41 # define CAIRO_OPERATOR_DIFFERENCE (static_cast<cairo_operator_t>(23))
47 basegfx::B2DRange
getClipBox(cairo_t
* cr
)
49 double x1
, y1
, x2
, y2
;
51 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
53 return basegfx::B2DRange(x1
, y1
, x2
, y2
);
56 basegfx::B2DRange
getFillDamage(cairo_t
* cr
)
58 double x1
, y1
, x2
, y2
;
60 cairo_fill_extents(cr
, &x1
, &y1
, &x2
, &y2
);
62 return basegfx::B2DRange(x1
, y1
, x2
, y2
);
65 basegfx::B2DRange
getClippedFillDamage(cairo_t
* cr
)
67 basegfx::B2DRange
aDamageRect(getFillDamage(cr
));
68 aDamageRect
.intersect(getClipBox(cr
));
72 basegfx::B2DRange
getStrokeDamage(cairo_t
* cr
)
74 double x1
, y1
, x2
, y2
;
76 cairo_stroke_extents(cr
, &x1
, &y1
, &x2
, &y2
);
78 return basegfx::B2DRange(x1
, y1
, x2
, y2
);
81 basegfx::B2DRange
getClippedStrokeDamage(cairo_t
* cr
)
83 basegfx::B2DRange
aDamageRect(getStrokeDamage(cr
));
84 aDamageRect
.intersect(getClipBox(cr
));
89 bool SvpSalGraphics::blendBitmap( const SalTwoRect
&, const SalBitmap
& /*rBitmap*/ )
91 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::blendBitmap case");
95 bool SvpSalGraphics::blendAlphaBitmap( const SalTwoRect
&, const SalBitmap
&, const SalBitmap
&, const SalBitmap
& )
97 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::blendAlphaBitmap case");
103 cairo_format_t
getCairoFormat(const BitmapBuffer
& rBuffer
)
105 cairo_format_t nFormat
;
106 assert(rBuffer
.mnBitCount
== 32 || rBuffer
.mnBitCount
== 1);
107 if (rBuffer
.mnBitCount
== 32)
108 nFormat
= CAIRO_FORMAT_ARGB32
;
110 nFormat
= CAIRO_FORMAT_A1
;
114 void Toggle1BitTransparency(const BitmapBuffer
& rBuf
)
116 assert(rBuf
.maPalette
.GetBestIndex(BitmapColor(Color(COL_BLACK
))) == 0);
117 // TODO: make upper layers use standard alpha
118 if (getCairoFormat(rBuf
) == CAIRO_FORMAT_A1
)
120 const int nImageSize
= rBuf
.mnHeight
* rBuf
.mnScanlineSize
;
121 unsigned char* pDst
= rBuf
.mpBits
;
122 for (int i
= nImageSize
; --i
>= 0; ++pDst
)
130 explicit SourceHelper(const SalBitmap
& rSourceBitmap
)
132 const SvpSalBitmap
& rSrcBmp
= static_cast<const SvpSalBitmap
&>(rSourceBitmap
);
134 if (rSrcBmp
.GetBitCount() != 32)
136 //big stupid copy here
137 static bool bWarnedOnce
;
138 SAL_WARN_IF(!bWarnedOnce
, "vcl.gdi", "non default depth bitmap, slow convert, upscale the input");
141 const BitmapBuffer
* pSrc
= rSrcBmp
.GetBuffer();
142 const SalTwoRect aTwoRect
= { 0, 0, pSrc
->mnWidth
, pSrc
->mnHeight
,
143 0, 0, pSrc
->mnWidth
, pSrc
->mnHeight
};
144 aTmpBmp
.Create(StretchAndConvert(*pSrc
, aTwoRect
, SVP_CAIRO_FORMAT
));
147 assert(aTmpBmp
.GetBitCount() == 32);
148 source
= SvpSalGraphics::createCairoSurface(aTmpBmp
.GetBuffer());
151 source
= SvpSalGraphics::createCairoSurface(rSrcBmp
.GetBuffer());
155 cairo_surface_destroy(source
);
157 cairo_surface_t
* getSurface()
163 cairo_surface_mark_dirty(source
);
165 unsigned char* getBits(sal_Int32
&rStride
)
167 cairo_surface_flush(source
);
169 unsigned char *mask_data
= cairo_image_surface_get_data(source
);
171 cairo_format_t nFormat
= cairo_image_surface_get_format(source
);
172 assert(nFormat
== CAIRO_FORMAT_ARGB32
&& "need to implement CAIRO_FORMAT_A1 after all here");
173 rStride
= cairo_format_stride_for_width(nFormat
, cairo_image_surface_get_width(source
));
178 SvpSalBitmap aTmpBmp
;
179 cairo_surface_t
* source
;
181 SourceHelper(const SourceHelper
&) = delete;
182 SourceHelper
& operator=(const SourceHelper
&) = delete;
188 explicit MaskHelper(const SalBitmap
& rAlphaBitmap
)
190 const SvpSalBitmap
& rMask
= static_cast<const SvpSalBitmap
&>(rAlphaBitmap
);
191 const BitmapBuffer
* pMaskBuf
= rMask
.GetBuffer();
193 if (rAlphaBitmap
.GetBitCount() == 8)
195 // the alpha values need to be inverted for Cairo
196 // so big stupid copy and invert here
197 const int nImageSize
= pMaskBuf
->mnHeight
* pMaskBuf
->mnScanlineSize
;
198 pAlphaBits
= new unsigned char[nImageSize
];
199 memcpy(pAlphaBits
, pMaskBuf
->mpBits
, nImageSize
);
201 // TODO: make upper layers use standard alpha
202 sal_uInt32
* pLDst
= reinterpret_cast<sal_uInt32
*>(pAlphaBits
);
203 for( int i
= nImageSize
/sizeof(sal_uInt32
); --i
>= 0; ++pLDst
)
205 assert(reinterpret_cast<unsigned char*>(pLDst
) == pAlphaBits
+nImageSize
);
207 mask
= cairo_image_surface_create_for_data(pAlphaBits
,
209 pMaskBuf
->mnWidth
, pMaskBuf
->mnHeight
,
210 pMaskBuf
->mnScanlineSize
);
214 // the alpha values need to be inverted for Cairo
215 // so big stupid copy and invert here
216 const int nImageSize
= pMaskBuf
->mnHeight
* pMaskBuf
->mnScanlineSize
;
217 pAlphaBits
= new unsigned char[nImageSize
];
218 memcpy(pAlphaBits
, pMaskBuf
->mpBits
, nImageSize
);
220 const sal_Int32 nBlackIndex
= pMaskBuf
->maPalette
.GetBestIndex(BitmapColor(Color(COL_BLACK
)));
221 if (nBlackIndex
== 0)
223 // TODO: make upper layers use standard alpha
224 unsigned char* pDst
= pAlphaBits
;
225 for (int i
= nImageSize
; --i
>= 0; ++pDst
)
229 mask
= cairo_image_surface_create_for_data(pAlphaBits
,
231 pMaskBuf
->mnWidth
, pMaskBuf
->mnHeight
,
232 pMaskBuf
->mnScanlineSize
);
237 cairo_surface_destroy(mask
);
240 cairo_surface_t
* getMask()
245 cairo_surface_t
*mask
;
246 unsigned char* pAlphaBits
;
248 MaskHelper(const MaskHelper
&) = delete;
249 MaskHelper
& operator=(const MaskHelper
&) = delete;
253 bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect
& rTR
, const SalBitmap
& rSourceBitmap
, const SalBitmap
& rAlphaBitmap
)
255 if (rAlphaBitmap
.GetBitCount() != 8 && rAlphaBitmap
.GetBitCount() != 1)
257 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap alpha depth case: " << rAlphaBitmap
.GetBitCount());
261 SourceHelper
aSurface(rSourceBitmap
);
262 cairo_surface_t
* source
= aSurface
.getSurface();
265 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
269 MaskHelper
aMask(rAlphaBitmap
);
270 cairo_surface_t
*mask
= aMask
.getMask();
273 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
277 cairo_t
* cr
= getCairoContext(false);
280 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
282 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
286 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
287 cairo_scale(cr
, (double)(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
, ((double)rTR
.mnDestHeight
)/rTR
.mnSrcHeight
);
288 cairo_set_source_surface(cr
, source
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
289 cairo_mask_surface(cr
, mask
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
291 releaseCairoContext(cr
, false, extents
);
296 bool SvpSalGraphics::drawTransformedBitmap(
297 const basegfx::B2DPoint
& rNull
,
298 const basegfx::B2DPoint
& rX
,
299 const basegfx::B2DPoint
& rY
,
300 const SalBitmap
& rSourceBitmap
,
301 const SalBitmap
* pAlphaBitmap
)
303 if (pAlphaBitmap
&& pAlphaBitmap
->GetBitCount() != 8 && pAlphaBitmap
->GetBitCount() != 1)
305 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap alpha depth case: " << pAlphaBitmap
->GetBitCount());
309 SourceHelper
aSurface(rSourceBitmap
);
310 cairo_surface_t
* source
= aSurface
.getSurface();
313 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
317 std::unique_ptr
<MaskHelper
> xMask
;
318 cairo_surface_t
*mask
= nullptr;
321 xMask
.reset(new MaskHelper(*pAlphaBitmap
));
322 mask
= xMask
->getMask();
325 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
330 const Size aSize
= rSourceBitmap
.GetSize();
332 cairo_t
* cr
= getCairoContext(false);
335 // setup the image transformation
336 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points
337 const basegfx::B2DVector aXRel
= rX
- rNull
;
338 const basegfx::B2DVector aYRel
= rY
- rNull
;
339 cairo_matrix_t matrix
;
340 cairo_matrix_init(&matrix
,
341 aXRel
.getX()/aSize
.Width(), aXRel
.getY()/aSize
.Width(),
342 aYRel
.getX()/aSize
.Height(), aYRel
.getY()/aSize
.Height(),
343 rNull
.getX(), rNull
.getY());
345 cairo_transform(cr
, &matrix
);
347 cairo_rectangle(cr
, 0, 0, aSize
.Width(), aSize
.Height());
348 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
351 cairo_set_source_surface(cr
, source
, 0, 0);
353 cairo_mask_surface(cr
, mask
, 0, 0);
357 releaseCairoContext(cr
, false, extents
);
362 void SvpSalGraphics::clipRegion(cairo_t
* cr
)
364 RectangleVector aRectangles
;
365 if (!m_aClipRegion
.IsEmpty())
367 m_aClipRegion
.GetRegionRectangles(aRectangles
);
369 if (!aRectangles
.empty())
371 for (RectangleVector::const_iterator
aRectIter(aRectangles
.begin()); aRectIter
!= aRectangles
.end(); ++aRectIter
)
373 cairo_rectangle(cr
, aRectIter
->Left(), aRectIter
->Top(), aRectIter
->GetWidth(), aRectIter
->GetHeight());
379 bool SvpSalGraphics::drawAlphaRect(long nX
, long nY
, long nWidth
, long nHeight
, sal_uInt8 nTransparency
)
381 cairo_t
* cr
= getCairoContext(false);
384 const double fTransparency
= (100 - nTransparency
) * (1.0/100);
386 basegfx::B2DRange
extents(0, 0, 0, 0);
388 cairo_rectangle(cr
, nX
, nY
, nWidth
, nHeight
);
390 if (m_aFillColor
!= SALCOLOR_NONE
)
392 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aFillColor
)/255.0,
393 SALCOLOR_GREEN(m_aFillColor
)/255.0,
394 SALCOLOR_BLUE(m_aFillColor
)/255.0,
397 if (m_aLineColor
== SALCOLOR_NONE
)
398 extents
= getClippedFillDamage(cr
);
400 cairo_fill_preserve(cr
);
403 if (m_aLineColor
!= SALCOLOR_NONE
)
405 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aLineColor
)/255.0,
406 SALCOLOR_GREEN(m_aLineColor
)/255.0,
407 SALCOLOR_BLUE(m_aLineColor
)/255.0,
410 extents
= getClippedStrokeDamage(cr
);
412 cairo_stroke_preserve(cr
);
415 releaseCairoContext(cr
, false, extents
);
420 SvpSalGraphics::SvpSalGraphics()
421 : m_pSurface(nullptr)
423 , m_aLineColor(MAKE_SALCOLOR(0x00, 0x00, 0x00))
424 , m_aFillColor(MAKE_SALCOLOR(0xFF, 0xFF, 0XFF))
425 , m_ePaintMode(PaintMode::Over
)
426 , m_aTextRenderImpl(*this)
430 SvpSalGraphics::~SvpSalGraphics()
434 void SvpSalGraphics::setSurface(cairo_surface_t
* pSurface
, const basegfx::B2IVector
& rSize
)
436 m_pSurface
= pSurface
;
437 m_aFrameSize
= rSize
;
438 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
439 cairo_surface_get_device_scale(pSurface
, &m_fScale
, nullptr);
444 void SvpSalGraphics::GetResolution( sal_Int32
& rDPIX
, sal_Int32
& rDPIY
)
449 sal_uInt16
SvpSalGraphics::GetBitCount() const
451 if (cairo_surface_get_content(m_pSurface
) != CAIRO_CONTENT_COLOR_ALPHA
)
456 long SvpSalGraphics::GetGraphicsWidth() const
458 return m_pSurface
? m_aFrameSize
.getX() : 0;
461 void SvpSalGraphics::ResetClipRegion()
463 m_aClipRegion
.SetNull();
466 bool SvpSalGraphics::setClipRegion( const vcl::Region
& i_rClip
)
468 m_aClipRegion
= i_rClip
;
472 void SvpSalGraphics::SetLineColor()
474 m_aLineColor
= SALCOLOR_NONE
;
477 void SvpSalGraphics::SetLineColor( SalColor nSalColor
)
479 m_aLineColor
= nSalColor
;
482 void SvpSalGraphics::SetFillColor()
484 m_aFillColor
= SALCOLOR_NONE
;
487 void SvpSalGraphics::SetFillColor( SalColor nSalColor
)
489 m_aFillColor
= nSalColor
;
492 void SvpSalGraphics::SetXORMode(bool bSet
)
494 m_ePaintMode
= bSet
? PaintMode::Xor
: PaintMode::Over
;
497 void SvpSalGraphics::SetROPLineColor( SalROPColor nROPColor
)
501 case SalROPColor::N0
:
502 m_aLineColor
= MAKE_SALCOLOR(0, 0, 0);
504 case SalROPColor::N1
:
505 m_aLineColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
507 case SalROPColor::Invert
:
508 m_aLineColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
513 void SvpSalGraphics::SetROPFillColor( SalROPColor nROPColor
)
517 case SalROPColor::N0
:
518 m_aFillColor
= MAKE_SALCOLOR(0, 0, 0);
520 case SalROPColor::N1
:
521 m_aFillColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
523 case SalROPColor::Invert
:
524 m_aFillColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
529 void SvpSalGraphics::drawPixel( long nX
, long nY
)
531 if (m_aLineColor
!= SALCOLOR_NONE
)
533 drawPixel(nX
, nY
, m_aLineColor
);
537 void SvpSalGraphics::drawPixel( long nX
, long nY
, SalColor nSalColor
)
539 SalColor aOrigFillColor
= m_aFillColor
;
540 SalColor aOrigLineColor
= m_aLineColor
;
542 basegfx::B2DPolygon aRect
= basegfx::tools::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+1, nY
+1));
543 m_aLineColor
= SALCOLOR_NONE
;
544 m_aFillColor
= nSalColor
;
546 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
548 m_aFillColor
= aOrigFillColor
;
549 m_aLineColor
= aOrigLineColor
;
552 void SvpSalGraphics::drawRect( long nX
, long nY
, long nWidth
, long nHeight
)
554 // because of the -1 hack we have to do fill and draw separately
555 SalColor aOrigFillColor
= m_aFillColor
;
556 SalColor aOrigLineColor
= m_aLineColor
;
557 m_aFillColor
= SALCOLOR_NONE
;
558 m_aLineColor
= SALCOLOR_NONE
;
560 if (aOrigFillColor
!= SALCOLOR_NONE
)
562 basegfx::B2DPolygon aRect
= basegfx::tools::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+nWidth
, nY
+nHeight
));
563 m_aFillColor
= aOrigFillColor
;
564 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
565 m_aFillColor
= SALCOLOR_NONE
;
568 if (aOrigLineColor
!= SALCOLOR_NONE
)
570 // need same -1 hack as X11SalGraphicsImpl::drawRect
571 basegfx::B2DPolygon aRect
= basegfx::tools::createPolygonFromRect(basegfx::B2DRectangle( nX
, nY
, nX
+nWidth
-1, nY
+nHeight
-1));
572 m_aLineColor
= aOrigLineColor
;
573 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
574 m_aLineColor
= SALCOLOR_NONE
;
577 m_aFillColor
= aOrigFillColor
;
578 m_aLineColor
= aOrigLineColor
;
581 void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
583 basegfx::B2DPolygon aPoly
;
584 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
585 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
586 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
587 aPoly
.setClosed(false);
589 drawPolyLine(aPoly
, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter
,
590 css::drawing::LineCap_BUTT
, 15.0 * F_PI180
/*default*/);
593 void SvpSalGraphics::drawPolygon(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
595 basegfx::B2DPolygon aPoly
;
596 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
597 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
598 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
600 drawPolyPolygon(basegfx::B2DPolyPolygon(aPoly
));
603 void SvpSalGraphics::drawPolyPolygon(sal_uInt32 nPoly
,
604 const sal_uInt32
* pPointCounts
,
605 PCONSTSALPOINT
* pPtAry
)
607 basegfx::B2DPolyPolygon aPolyPoly
;
608 for(sal_uInt32 nPolygon
= 0; nPolygon
< nPoly
; ++nPolygon
)
610 sal_uInt32 nPoints
= pPointCounts
[nPolygon
];
613 PCONSTSALPOINT pPoints
= pPtAry
[nPolygon
];
614 basegfx::B2DPolygon aPoly
;
615 aPoly
.append( basegfx::B2DPoint(pPoints
->mnX
, pPoints
->mnY
), nPoints
);
616 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
617 aPoly
.setB2DPoint(i
, basegfx::B2DPoint( pPoints
[i
].mnX
, pPoints
[i
].mnY
));
619 aPolyPoly
.append(aPoly
);
623 drawPolyPolygon(aPolyPoly
);
626 static const basegfx::B2DPoint
aHalfPointOfs(0.5, 0.5);
628 static void AddPolygonToPath(cairo_t
* cr
, const basegfx::B2DPolygon
& rPolygon
, bool bClosePath
,
629 bool bPixelSnap
, bool bLineDraw
)
631 // short circuit if there is nothing to do
632 const int nPointCount
= rPolygon
.count();
633 if( nPointCount
<= 0 )
638 const bool bHasCurves
= rPolygon
.areControlPointsUsed();
639 basegfx::B2DPoint aLast
;
641 for( int nPointIdx
= 0, nPrevIdx
= 0;; nPrevIdx
= nPointIdx
++ )
643 int nClosedIdx
= nPointIdx
;
644 if( nPointIdx
>= nPointCount
)
646 // prepare to close last curve segment if needed
647 if( bClosePath
&& (nPointIdx
== nPointCount
) )
657 basegfx::B2DPoint aPoint
= rPolygon
.getB2DPoint( nClosedIdx
);
661 // snap device coordinates to full pixels
662 aPoint
.setX( basegfx::fround( aPoint
.getX() ) );
663 aPoint
.setY( basegfx::fround( aPoint
.getY() ) );
668 aPoint
+= aHalfPointOfs
;
673 // first point => just move there
674 cairo_move_to(cr
, aPoint
.getX(), aPoint
.getY());
679 bool bPendingCurve
= false;
682 bPendingCurve
= rPolygon
.isNextControlPointUsed( nPrevIdx
);
683 bPendingCurve
|= rPolygon
.isPrevControlPointUsed( nClosedIdx
);
686 if( !bPendingCurve
) // line segment
688 cairo_line_to(cr
, aPoint
.getX(), aPoint
.getY());
690 else // cubic bezier segment
692 basegfx::B2DPoint aCP1
= rPolygon
.getNextControlPoint( nPrevIdx
);
693 basegfx::B2DPoint aCP2
= rPolygon
.getPrevControlPoint( nClosedIdx
);
696 aCP1
+= aHalfPointOfs
;
697 aCP2
+= aHalfPointOfs
;
700 // tdf#99165 if the control points are 'empty', create the mathematical
701 // correct replacement ones to avoid problems with the graphical sub-system
702 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
703 // vector was wrong. Best alternative is one as close as possible which means short.
704 if (aCP1
.equal(aLast
))
706 aCP1
= aLast
+ ((aCP2
- aLast
) * 0.0005);
709 if(aCP2
.equal(aPoint
))
711 aCP2
= aPoint
+ ((aCP1
- aPoint
) * 0.0005);
714 cairo_curve_to(cr
, aCP1
.getX(), aCP1
.getY(), aCP2
.getX(), aCP2
.getY(),
715 aPoint
.getX(), aPoint
.getY());
723 cairo_close_path(cr
);
727 void SvpSalGraphics::drawLine( long nX1
, long nY1
, long nX2
, long nY2
)
729 basegfx::B2DPolygon aPoly
;
730 aPoly
.append(basegfx::B2DPoint(nX1
, nY1
), 2);
731 aPoly
.setB2DPoint(1, basegfx::B2DPoint(nX2
, nY2
));
732 aPoly
.setClosed(false);
734 cairo_t
* cr
= getCairoContext(false);
737 AddPolygonToPath(cr
, aPoly
, aPoly
.isClosed(), !getAntiAliasB2DDraw(), true);
739 applyColor(cr
, m_aLineColor
);
741 basegfx::B2DRange extents
= getClippedStrokeDamage(cr
);
745 releaseCairoContext(cr
, false, extents
);
748 bool SvpSalGraphics::drawPolyLine(
749 const basegfx::B2DPolygon
& rPolyLine
,
750 double fTransparency
,
751 const basegfx::B2DVector
& rLineWidths
,
752 basegfx::B2DLineJoin eLineJoin
,
753 css::drawing::LineCap eLineCap
,
754 double fMiterMinimumAngle
)
756 // short circuit if there is nothing to do
757 const int nPointCount
= rPolyLine
.count();
758 if (nPointCount
<= 0)
763 const bool bNoJoin
= (basegfx::B2DLineJoin::NONE
== eLineJoin
&& basegfx::fTools::more(rLineWidths
.getX(), 0.0));
765 cairo_t
* cr
= getCairoContext(false);
768 // setup line attributes
769 cairo_line_join_t eCairoLineJoin
= CAIRO_LINE_JOIN_MITER
;
772 case basegfx::B2DLineJoin::Bevel
:
773 eCairoLineJoin
= CAIRO_LINE_JOIN_BEVEL
;
775 case basegfx::B2DLineJoin::Round
:
776 eCairoLineJoin
= CAIRO_LINE_JOIN_ROUND
;
778 case basegfx::B2DLineJoin::NONE
:
779 case basegfx::B2DLineJoin::Miter
:
780 eCairoLineJoin
= CAIRO_LINE_JOIN_MITER
;
784 // convert miter minimum angle to miter limit
785 double fMiterLimit
= 1.0 / sin( fMiterMinimumAngle
/ 2.0);
787 // setup cap attribute
788 cairo_line_cap_t
eCairoLineCap(CAIRO_LINE_CAP_BUTT
);
792 default: // css::drawing::LineCap_BUTT:
794 eCairoLineCap
= CAIRO_LINE_CAP_BUTT
;
797 case css::drawing::LineCap_ROUND
:
799 eCairoLineCap
= CAIRO_LINE_CAP_ROUND
;
802 case css::drawing::LineCap_SQUARE
:
804 eCairoLineCap
= CAIRO_LINE_CAP_SQUARE
;
809 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aLineColor
)/255.0,
810 SALCOLOR_GREEN(m_aLineColor
)/255.0,
811 SALCOLOR_BLUE(m_aLineColor
)/255.0,
814 cairo_set_line_join(cr
, eCairoLineJoin
);
815 cairo_set_line_cap(cr
, eCairoLineCap
);
816 cairo_set_line_width(cr
, rLineWidths
.getX());
817 cairo_set_miter_limit(cr
, fMiterLimit
);
820 basegfx::B2DRange
extents(0, 0, 0, 0);
824 AddPolygonToPath(cr
, rPolyLine
, rPolyLine
.isClosed(), !getAntiAliasB2DDraw(), true);
825 extents
= getClippedStrokeDamage(cr
);
830 // emulate rendering::PathJoinType::NONE by painting single edges
831 const sal_uInt32
nEdgeCount(rPolyLine
.isClosed() ? nPointCount
: nPointCount
- 1);
832 basegfx::B2DPolygon aEdge
;
833 aEdge
.append(rPolyLine
.getB2DPoint(0));
834 aEdge
.append(basegfx::B2DPoint(0.0, 0.0));
836 for (sal_uInt32 i
= 0; i
< nEdgeCount
; ++i
)
838 const sal_uInt32
nNextIndex((i
+ 1) % nPointCount
);
839 aEdge
.setB2DPoint(1, rPolyLine
.getB2DPoint(nNextIndex
));
840 aEdge
.setNextControlPoint(0, rPolyLine
.getNextControlPoint(i
% nPointCount
));
841 aEdge
.setPrevControlPoint(1, rPolyLine
.getPrevControlPoint(nNextIndex
));
843 AddPolygonToPath(cr
, aEdge
, false, !getAntiAliasB2DDraw(), true);
845 extents
.expand(getStrokeDamage(cr
));
850 aEdge
.setB2DPoint(0, aEdge
.getB2DPoint(1));
853 extents
.intersect(getClipBox(cr
));
856 releaseCairoContext(cr
, false, extents
);
861 bool SvpSalGraphics::drawPolyLineBezier( sal_uInt32
,
865 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolyLineBezier case");
869 bool SvpSalGraphics::drawPolygonBezier( sal_uInt32
,
873 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolygonBezier case");
877 bool SvpSalGraphics::drawPolyPolygonBezier( sal_uInt32
,
879 const SalPoint
* const*,
880 const PolyFlags
* const* )
882 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolyPolygonBezier case");
886 void SvpSalGraphics::setupPolyPolygon(cairo_t
* cr
, const basegfx::B2DPolyPolygon
& rPolyPoly
)
890 for (const auto & rPoly
: rPolyPoly
)
891 AddPolygonToPath(cr
, rPoly
, true, !getAntiAliasB2DDraw(), m_aLineColor
!= SALCOLOR_NONE
);
894 bool SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
, double fTransparency
)
896 cairo_t
* cr
= getCairoContext(true);
898 setupPolyPolygon(cr
, rPolyPoly
);
900 basegfx::B2DRange
extents(0, 0, 0, 0);
902 if (m_aFillColor
!= SALCOLOR_NONE
)
904 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aFillColor
)/255.0,
905 SALCOLOR_GREEN(m_aFillColor
)/255.0,
906 SALCOLOR_BLUE(m_aFillColor
)/255.0,
909 if (m_aLineColor
== SALCOLOR_NONE
)
910 extents
= getClippedFillDamage(cr
);
912 cairo_fill_preserve(cr
);
915 if (m_aLineColor
!= SALCOLOR_NONE
)
917 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aLineColor
)/255.0,
918 SALCOLOR_GREEN(m_aLineColor
)/255.0,
919 SALCOLOR_BLUE(m_aLineColor
)/255.0,
922 extents
= getClippedStrokeDamage(cr
);
924 cairo_stroke_preserve(cr
);
927 releaseCairoContext(cr
, true, extents
);
932 void SvpSalGraphics::applyColor(cairo_t
*cr
, SalColor aColor
)
934 if (cairo_surface_get_content(m_pSurface
) == CAIRO_CONTENT_COLOR_ALPHA
)
936 cairo_set_source_rgba(cr
, SALCOLOR_RED(aColor
)/255.0,
937 SALCOLOR_GREEN(aColor
)/255.0,
938 SALCOLOR_BLUE(aColor
)/255.0,
943 double fSet
= aColor
== COL_BLACK
? 1.0 : 0.0;
944 cairo_set_source_rgba(cr
, 1, 1, 1, fSet
);
945 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
949 void SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
)
951 cairo_t
* cr
= getCairoContext(true);
953 setupPolyPolygon(cr
, rPolyPoly
);
955 basegfx::B2DRange
extents(0, 0, 0, 0);
957 if (m_aFillColor
!= SALCOLOR_NONE
)
959 applyColor(cr
, m_aFillColor
);
960 if (m_aLineColor
== SALCOLOR_NONE
)
961 extents
= getClippedFillDamage(cr
);
962 cairo_fill_preserve(cr
);
965 if (m_aLineColor
!= SALCOLOR_NONE
)
967 applyColor(cr
, m_aLineColor
);
968 extents
= getClippedStrokeDamage(cr
);
969 cairo_stroke_preserve(cr
);
972 releaseCairoContext(cr
, true, extents
);
975 void SvpSalGraphics::copyArea( long nDestX
,
981 bool /*bWindowInvalidate*/ )
983 SalTwoRect
aTR(nSrcX
, nSrcY
, nSrcWidth
, nSrcHeight
, nDestX
, nDestY
, nSrcWidth
, nSrcHeight
);
987 static basegfx::B2DRange
renderSource(cairo_t
* cr
, const SalTwoRect
& rTR
,
988 cairo_surface_t
* source
)
990 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
992 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
996 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
997 if (rTR
.mnSrcWidth
!= 0 && rTR
.mnSrcHeight
!= 0) {
998 cairo_scale(cr
, (double)(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
, ((double)rTR
.mnDestHeight
)/rTR
.mnSrcHeight
);
1002 cairo_set_source_surface(cr
, source
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
1003 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
1010 void SvpSalGraphics::copySource( const SalTwoRect
& rTR
,
1011 cairo_surface_t
* source
)
1013 cairo_t
* cr
= getCairoContext(false);
1016 basegfx::B2DRange extents
= renderSource(cr
, rTR
, source
);
1018 releaseCairoContext(cr
, false, extents
);
1021 void SvpSalGraphics::copyBits( const SalTwoRect
& rTR
,
1022 SalGraphics
* pSrcGraphics
)
1024 SalTwoRect
aTR(rTR
);
1026 SvpSalGraphics
* pSrc
= pSrcGraphics
?
1027 static_cast<SvpSalGraphics
*>(pSrcGraphics
) : this;
1029 cairo_surface_t
* source
= pSrc
->m_pSurface
;
1031 cairo_surface_t
*pCopy
= nullptr;
1034 //self copy is a problem, so dup source in that case
1035 pCopy
= cairo_surface_create_similar(source
,
1036 cairo_surface_get_content(m_pSurface
),
1037 aTR
.mnSrcWidth
* m_fScale
,
1038 aTR
.mnSrcHeight
* m_fScale
);
1039 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1040 cairo_surface_set_device_scale(pCopy
, m_fScale
, m_fScale
);
1042 cairo_t
* cr
= cairo_create(pCopy
);
1043 cairo_set_source_surface(cr
, source
, -aTR
.mnSrcX
, -aTR
.mnSrcY
);
1044 cairo_rectangle(cr
, 0, 0, aTR
.mnSrcWidth
, aTR
.mnSrcHeight
);
1054 copySource(aTR
, source
);
1057 cairo_surface_destroy(pCopy
);
1060 void SvpSalGraphics::drawBitmap(const SalTwoRect
& rTR
, const SalBitmap
& rSourceBitmap
)
1062 if (rSourceBitmap
.GetBitCount() == 1)
1064 MaskHelper
aMask(rSourceBitmap
);
1065 cairo_surface_t
* source
= aMask
.getMask();
1066 copySource(rTR
, source
);
1070 SourceHelper
aSurface(rSourceBitmap
);
1071 cairo_surface_t
* source
= aSurface
.getSurface();
1072 copySource(rTR
, source
);
1075 void SvpSalGraphics::drawBitmap( const SalTwoRect
& rTR
,
1076 const SalBitmap
& rSourceBitmap
,
1077 const SalBitmap
& rTransparentBitmap
)
1079 drawAlphaBitmap(rTR
, rSourceBitmap
, rTransparentBitmap
);
1082 static sal_uInt8
unpremultiply(sal_uInt8 c
, sal_uInt8 a
)
1084 return (a
> 0) ? (c
* 255 + a
/ 2) / a
: 0;
1087 static sal_uInt8
premultiply(sal_uInt8 c
, sal_uInt8 a
)
1089 return (c
* a
+ 127) / 255;
1092 void SvpSalGraphics::drawMask( const SalTwoRect
& rTR
,
1093 const SalBitmap
& rSalBitmap
,
1094 SalColor nMaskColor
)
1096 /** creates an image from the given rectangle, replacing all black pixels
1097 * with nMaskColor and make all other full transparent */
1098 SourceHelper
aSurface(rSalBitmap
);
1100 unsigned char *mask_data
= aSurface
.getBits(nStride
);
1101 for (sal_Int32 y
= rTR
.mnSrcY
; y
< rTR
.mnSrcY
+ rTR
.mnSrcHeight
; ++y
)
1103 unsigned char *row
= mask_data
+ (nStride
*y
);
1104 unsigned char *data
= row
+ (rTR
.mnSrcX
* 4);
1105 for (sal_Int32 x
= rTR
.mnSrcX
; x
< rTR
.mnSrcX
+ rTR
.mnSrcWidth
; ++x
)
1107 sal_uInt8 b
= unpremultiply(data
[SVP_CAIRO_BLUE
], data
[SVP_CAIRO_ALPHA
]);
1108 sal_uInt8 g
= unpremultiply(data
[SVP_CAIRO_GREEN
], data
[SVP_CAIRO_ALPHA
]);
1109 sal_uInt8 r
= unpremultiply(data
[SVP_CAIRO_RED
], data
[SVP_CAIRO_ALPHA
]);
1110 if (r
== 0 && g
== 0 && b
== 0)
1112 data
[0] = SALCOLOR_BLUE(nMaskColor
);
1113 data
[1] = SALCOLOR_GREEN(nMaskColor
);
1114 data
[2] = SALCOLOR_RED(nMaskColor
);
1127 aSurface
.mark_dirty();
1129 cairo_t
* cr
= getCairoContext(false);
1132 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
1134 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
1138 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
1139 cairo_scale(cr
, (double)(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
, ((double)rTR
.mnDestHeight
)/rTR
.mnSrcHeight
);
1140 cairo_set_source_surface(cr
, aSurface
.getSurface(), -rTR
.mnSrcX
, -rTR
.mnSrcY
);
1143 releaseCairoContext(cr
, false, extents
);
1146 SalBitmap
* SvpSalGraphics::getBitmap( long nX
, long nY
, long nWidth
, long nHeight
)
1148 SvpSalBitmap
* pBitmap
= new SvpSalBitmap();
1150 if (GetBitCount() == 1)
1152 aPal
.SetEntryCount(2);
1153 aPal
[0] = Color(COL_BLACK
);
1154 aPal
[1] = Color(COL_WHITE
);
1156 pBitmap
->Create(Size(nWidth
, nHeight
), GetBitCount(), aPal
);
1158 cairo_surface_t
* target
= SvpSalGraphics::createCairoSurface(pBitmap
->GetBuffer());
1159 cairo_t
* cr
= cairo_create(target
);
1161 SalTwoRect
aTR(nX
, nY
, nWidth
, nHeight
, 0, 0, nWidth
, nHeight
);
1162 renderSource(cr
, aTR
, m_pSurface
);
1165 cairo_surface_destroy(target
);
1167 Toggle1BitTransparency(*pBitmap
->GetBuffer());
1172 SalColor
SvpSalGraphics::getPixel( long nX
, long nY
)
1174 cairo_surface_t
*target
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, 1, 1);
1175 cairo_t
* cr
= cairo_create(target
);
1177 cairo_rectangle(cr
, 0, 0, 1, 1);
1178 cairo_set_source_surface(cr
, m_pSurface
, -nX
, -nY
);
1182 cairo_surface_flush(target
);
1183 unsigned char *data
= cairo_image_surface_get_data(target
);
1184 sal_uInt8 b
= unpremultiply(data
[SVP_CAIRO_BLUE
], data
[SVP_CAIRO_ALPHA
]);
1185 sal_uInt8 g
= unpremultiply(data
[SVP_CAIRO_GREEN
], data
[SVP_CAIRO_ALPHA
]);
1186 sal_uInt8 r
= unpremultiply(data
[SVP_CAIRO_RED
], data
[SVP_CAIRO_ALPHA
]);
1187 SalColor nRet
= MAKE_SALCOLOR(r
, g
, b
);
1189 cairo_surface_destroy(target
);
1196 cairo_pattern_t
* create_stipple()
1198 static unsigned char data
[16] = { 0xFF, 0xFF, 0x00, 0x00,
1199 0xFF, 0xFF, 0x00, 0x00,
1200 0x00, 0x00, 0xFF, 0xFF,
1201 0x00, 0x00, 0xFF, 0xFF };
1202 cairo_surface_t
* surface
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_A8
, 4, 4, 4);
1203 cairo_pattern_t
* pattern
= cairo_pattern_create_for_surface(surface
);
1204 cairo_surface_destroy(surface
);
1205 cairo_pattern_set_extend(pattern
, CAIRO_EXTEND_REPEAT
);
1206 cairo_pattern_set_filter(pattern
, CAIRO_FILTER_NEAREST
);
1211 void SvpSalGraphics::invert(const basegfx::B2DPolygon
&rPoly
, SalInvert nFlags
)
1213 cairo_t
* cr
= getCairoContext(false);
1216 basegfx::B2DRange
extents(0, 0, 0, 0);
1218 AddPolygonToPath(cr
, rPoly
, true, !getAntiAliasB2DDraw(), false);
1220 cairo_set_source_rgb(cr
, 1.0, 1.0, 1.0);
1222 if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 10, 0))
1224 cairo_set_operator(cr
, CAIRO_OPERATOR_DIFFERENCE
);
1228 SAL_WARN("vcl.gdi", "SvpSalGraphics::invert, archaic cairo");
1231 if (nFlags
& SalInvert::TrackFrame
)
1233 cairo_set_line_width(cr
, 2.0);
1234 const double dashLengths
[2] = { 4.0, 4.0 };
1235 cairo_set_dash(cr
, dashLengths
, 2, 0);
1237 extents
= getClippedStrokeDamage(cr
);
1238 //see tdf#106577 under wayland, some pixel droppings seen, maybe we're
1239 //out by one somewhere, or cairo_stroke_extents is confused by
1247 extents
= getClippedFillDamage(cr
);
1251 if (nFlags
& SalInvert::N50
)
1253 cairo_pattern_t
*pattern
= create_stipple();
1254 cairo_surface_t
* surface
= cairo_surface_create_similar(m_pSurface
,
1255 cairo_surface_get_content(m_pSurface
),
1256 extents
.getWidth() * m_fScale
,
1257 extents
.getHeight() * m_fScale
);
1259 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1260 cairo_surface_set_device_scale(surface
, m_fScale
, m_fScale
);
1262 cairo_t
* stipple_cr
= cairo_create(surface
);
1263 cairo_set_source_rgb(stipple_cr
, 1.0, 1.0, 1.0);
1264 cairo_mask(stipple_cr
, pattern
);
1265 cairo_pattern_destroy(pattern
);
1266 cairo_destroy(stipple_cr
);
1267 cairo_mask_surface(cr
, surface
, extents
.getMinX(), extents
.getMinY());
1268 cairo_surface_destroy(surface
);
1276 releaseCairoContext(cr
, false, extents
);
1279 void SvpSalGraphics::invert( long nX
, long nY
, long nWidth
, long nHeight
, SalInvert nFlags
)
1281 basegfx::B2DPolygon aRect
= basegfx::tools::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+nWidth
, nY
+nHeight
));
1283 invert(aRect
, nFlags
);
1286 void SvpSalGraphics::invert(sal_uInt32 nPoints
, const SalPoint
* pPtAry
, SalInvert nFlags
)
1288 basegfx::B2DPolygon aPoly
;
1289 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
1290 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1291 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
1292 aPoly
.setClosed(true);
1294 invert(aPoly
, nFlags
);
1297 bool SvpSalGraphics::drawEPS( long, long, long, long, void*, sal_uLong
)
1304 bool isCairoCompatible(const BitmapBuffer
* pBuffer
)
1309 if (pBuffer
->mnBitCount
!= 32 && pBuffer
->mnBitCount
!= 1)
1312 cairo_format_t nFormat
= getCairoFormat(*pBuffer
);
1313 return (cairo_format_stride_for_width(nFormat
, pBuffer
->mnWidth
) == pBuffer
->mnScanlineSize
);
1317 cairo_surface_t
* SvpSalGraphics::createCairoSurface(const BitmapBuffer
*pBuffer
)
1319 if (!isCairoCompatible(pBuffer
))
1322 cairo_format_t nFormat
= getCairoFormat(*pBuffer
);
1323 cairo_surface_t
*target
=
1324 cairo_image_surface_create_for_data(pBuffer
->mpBits
,
1326 pBuffer
->mnWidth
, pBuffer
->mnHeight
,
1327 pBuffer
->mnScanlineSize
);
1331 cairo_t
* SvpSalGraphics::createTmpCompatibleCairoContext() const
1333 cairo_surface_t
*target
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
1334 m_aFrameSize
.getX() * m_fScale
,
1335 m_aFrameSize
.getY() * m_fScale
);
1336 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1337 cairo_surface_set_device_scale(target
, m_fScale
, m_fScale
);
1340 return cairo_create(target
);
1343 cairo_t
* SvpSalGraphics::getCairoContext(bool bXorModeAllowed
) const
1346 if (m_ePaintMode
== PaintMode::Xor
&& bXorModeAllowed
)
1347 cr
= createTmpCompatibleCairoContext();
1349 cr
= cairo_create(m_pSurface
);
1350 cairo_set_line_width(cr
, 1);
1351 cairo_set_fill_rule(cr
, CAIRO_FILL_RULE_EVEN_ODD
);
1352 cairo_set_antialias(cr
, getAntiAliasB2DDraw() ? CAIRO_ANTIALIAS_DEFAULT
: CAIRO_ANTIALIAS_NONE
);
1353 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
1357 cairo_user_data_key_t
* SvpSalGraphics::getDamageKey()
1359 static cairo_user_data_key_t aDamageKey
;
1363 void SvpSalGraphics::releaseCairoContext(cairo_t
* cr
, bool bXorModeAllowed
, const basegfx::B2DRange
& rExtents
) const
1365 const bool bXoring
= (m_ePaintMode
== PaintMode::Xor
&& bXorModeAllowed
);
1367 if (rExtents
.isEmpty())
1369 //nothing changed, return early
1372 cairo_surface_t
* surface
= cairo_get_target(cr
);
1373 cairo_surface_destroy(surface
);
1379 sal_Int32
nExtentsLeft(rExtents
.getMinX()), nExtentsTop(rExtents
.getMinY());
1380 sal_Int32
nExtentsRight(rExtents
.getMaxX()), nExtentsBottom(rExtents
.getMaxY());
1381 sal_Int32 nWidth
= m_aFrameSize
.getX();
1382 sal_Int32 nHeight
= m_aFrameSize
.getY();
1383 nExtentsLeft
= std::max
<sal_Int32
>(nExtentsLeft
, 0);
1384 nExtentsTop
= std::max
<sal_Int32
>(nExtentsTop
, 0);
1385 nExtentsRight
= std::min
<sal_Int32
>(nExtentsRight
, nWidth
);
1386 nExtentsBottom
= std::min
<sal_Int32
>(nExtentsBottom
, nHeight
);
1388 cairo_surface_t
* surface
= cairo_get_target(cr
);
1389 cairo_surface_flush(surface
);
1391 //For the most part we avoid the use of XOR these days, but there
1392 //are some edge cases where legacy stuff still supports it, so
1393 //emulate it (slowly) here.
1396 cairo_surface_t
* target_surface
= m_pSurface
;
1397 if (cairo_surface_get_type(target_surface
) != CAIRO_SURFACE_TYPE_IMAGE
)
1399 //in the unlikely case we can't use m_pSurface directly, copy contents
1400 //to another temp image surface
1401 cairo_t
* copycr
= createTmpCompatibleCairoContext();
1402 cairo_rectangle(copycr
, nExtentsLeft
, nExtentsTop
,
1403 nExtentsRight
- nExtentsLeft
,
1404 nExtentsBottom
- nExtentsTop
);
1405 cairo_set_source_surface(copycr
, m_pSurface
, 0, 0);
1406 cairo_paint(copycr
);
1407 target_surface
= cairo_get_target(copycr
);
1408 cairo_destroy(copycr
);
1411 cairo_surface_flush(target_surface
);
1412 unsigned char *target_surface_data
= cairo_image_surface_get_data(target_surface
);
1413 unsigned char *xor_surface_data
= cairo_image_surface_get_data(surface
);
1415 cairo_format_t nFormat
= cairo_image_surface_get_format(target_surface
);
1416 assert(nFormat
== CAIRO_FORMAT_ARGB32
&& "need to implement CAIRO_FORMAT_A1 after all here");
1417 sal_Int32 nStride
= cairo_format_stride_for_width(nFormat
, nWidth
* m_fScale
);
1418 sal_Int32 nUnscaledExtentsLeft
= nExtentsLeft
* m_fScale
;
1419 sal_Int32 nUnscaledExtentsRight
= nExtentsRight
* m_fScale
;
1420 sal_Int32 nUnscaledExtentsTop
= nExtentsTop
* m_fScale
;
1421 sal_Int32 nUnscaledExtentsBottom
= nExtentsBottom
* m_fScale
;
1422 for (sal_Int32 y
= nUnscaledExtentsTop
; y
< nUnscaledExtentsBottom
; ++y
)
1424 unsigned char *true_row
= target_surface_data
+ (nStride
*y
);
1425 unsigned char *xor_row
= xor_surface_data
+ (nStride
*y
);
1426 unsigned char *true_data
= true_row
+ (nUnscaledExtentsLeft
* 4);
1427 unsigned char *xor_data
= xor_row
+ (nUnscaledExtentsLeft
* 4);
1428 for (sal_Int32 x
= nUnscaledExtentsLeft
; x
< nUnscaledExtentsRight
; ++x
)
1430 sal_uInt8 b
= unpremultiply(true_data
[SVP_CAIRO_BLUE
], true_data
[SVP_CAIRO_ALPHA
]) ^
1431 unpremultiply(xor_data
[SVP_CAIRO_BLUE
], xor_data
[SVP_CAIRO_ALPHA
]);
1432 sal_uInt8 g
= unpremultiply(true_data
[SVP_CAIRO_GREEN
], true_data
[SVP_CAIRO_ALPHA
]) ^
1433 unpremultiply(xor_data
[SVP_CAIRO_GREEN
], xor_data
[SVP_CAIRO_ALPHA
]);
1434 sal_uInt8 r
= unpremultiply(true_data
[SVP_CAIRO_RED
], true_data
[SVP_CAIRO_ALPHA
]) ^
1435 unpremultiply(xor_data
[SVP_CAIRO_RED
], xor_data
[SVP_CAIRO_ALPHA
]);
1436 true_data
[0] = premultiply(b
, true_data
[SVP_CAIRO_ALPHA
]);
1437 true_data
[1] = premultiply(g
, true_data
[SVP_CAIRO_ALPHA
]);
1438 true_data
[2] = premultiply(r
, true_data
[SVP_CAIRO_ALPHA
]);
1443 cairo_surface_mark_dirty(target_surface
);
1445 if (target_surface
!= m_pSurface
)
1447 cairo_t
* copycr
= cairo_create(m_pSurface
);
1448 //unlikely case we couldn't use m_pSurface directly, copy contents
1449 //back from image surface
1450 cairo_rectangle(copycr
, nExtentsLeft
, nExtentsTop
,
1451 nExtentsRight
- nExtentsLeft
,
1452 nExtentsBottom
- nExtentsTop
);
1453 cairo_set_source_surface(copycr
, target_surface
, 0, 0);
1454 cairo_paint(copycr
);
1455 cairo_destroy(copycr
);
1456 cairo_surface_destroy(target_surface
);
1459 cairo_surface_destroy(surface
);
1462 cairo_destroy(cr
); // unref
1464 DamageHandler
* pDamage
= static_cast<DamageHandler
*>(cairo_surface_get_user_data(m_pSurface
, getDamageKey()));
1468 pDamage
->damaged(pDamage
->handle
, nExtentsLeft
, nExtentsTop
,
1469 nExtentsRight
- nExtentsLeft
,
1470 nExtentsBottom
- nExtentsTop
);
1474 #if ENABLE_CAIRO_CANVAS
1475 bool SvpSalGraphics::SupportsCairo() const
1480 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr
& /*rSurface*/) const
1482 return cairo::SurfaceSharedPtr();
1485 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateSurface(const OutputDevice
& /*rRefDevice*/, int /*x*/, int /*y*/, int /*width*/, int /*height*/) const
1487 return cairo::SurfaceSharedPtr();
1490 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateBitmapSurface(const OutputDevice
& /*rRefDevice*/, const BitmapSystemData
& /*rData*/, const Size
& /*rSize*/) const
1492 return cairo::SurfaceSharedPtr();
1495 css::uno::Any
SvpSalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr
& /*rSurface*/, const basegfx::B2ISize
& /*rSize*/) const
1497 return css::uno::Any();
1500 #endif // ENABLE_CAIRO_CANVAS
1502 SystemGraphicsData
SvpSalGraphics::GetGraphicsData() const
1504 return SystemGraphicsData();
1507 bool SvpSalGraphics::supportsOperation(OutDevSupportType eType
) const
1511 case OutDevSupportType::TransparentRect
:
1512 case OutDevSupportType::B2DDraw
:
1518 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */