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 <o3tl/safeint.hxx>
28 #include <vcl/sysdata.hxx>
29 #include <config_cairo_canvas.h>
30 #include <basegfx/numeric/ftools.hxx>
31 #include <basegfx/range/b2drange.hxx>
32 #include <basegfx/range/b2ibox.hxx>
33 #include <basegfx/polygon/b2dpolypolygon.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #if ENABLE_CAIRO_CANVAS
41 # if defined CAIRO_VERSION && CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 10, 0)
42 # define CAIRO_OPERATOR_DIFFERENCE (static_cast<cairo_operator_t>(23))
48 basegfx::B2DRange
getClipBox(cairo_t
* cr
)
50 double x1
, y1
, x2
, y2
;
52 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
54 return basegfx::B2DRange(x1
, y1
, x2
, y2
);
57 basegfx::B2DRange
getFillDamage(cairo_t
* cr
)
59 double x1
, y1
, x2
, y2
;
61 cairo_fill_extents(cr
, &x1
, &y1
, &x2
, &y2
);
63 return basegfx::B2DRange(x1
, y1
, x2
, y2
);
66 basegfx::B2DRange
getClippedFillDamage(cairo_t
* cr
)
68 basegfx::B2DRange
aDamageRect(getFillDamage(cr
));
69 aDamageRect
.intersect(getClipBox(cr
));
73 basegfx::B2DRange
getStrokeDamage(cairo_t
* cr
)
75 double x1
, y1
, x2
, y2
;
77 cairo_stroke_extents(cr
, &x1
, &y1
, &x2
, &y2
);
79 return basegfx::B2DRange(x1
, y1
, x2
, y2
);
82 basegfx::B2DRange
getClippedStrokeDamage(cairo_t
* cr
)
84 basegfx::B2DRange
aDamageRect(getStrokeDamage(cr
));
85 aDamageRect
.intersect(getClipBox(cr
));
90 bool SvpSalGraphics::blendBitmap( const SalTwoRect
&, const SalBitmap
& /*rBitmap*/ )
92 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::blendBitmap case");
96 bool SvpSalGraphics::blendAlphaBitmap( const SalTwoRect
&, const SalBitmap
&, const SalBitmap
&, const SalBitmap
& )
98 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::blendAlphaBitmap case");
104 cairo_format_t
getCairoFormat(const BitmapBuffer
& rBuffer
)
106 cairo_format_t nFormat
;
107 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
108 assert(rBuffer
.mnBitCount
== 32 || rBuffer
.mnBitCount
== 24 || rBuffer
.mnBitCount
== 1);
110 assert(rBuffer
.mnBitCount
== 32 || rBuffer
.mnBitCount
== 1);
113 if (rBuffer
.mnBitCount
== 32)
114 nFormat
= CAIRO_FORMAT_ARGB32
;
115 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
116 else if (rBuffer
.mnBitCount
== 24)
117 nFormat
= CAIRO_FORMAT_RGB24_888
;
120 nFormat
= CAIRO_FORMAT_A1
;
124 void Toggle1BitTransparency(const BitmapBuffer
& rBuf
)
126 assert(rBuf
.maPalette
.GetBestIndex(BitmapColor(COL_BLACK
)) == 0);
127 // TODO: make upper layers use standard alpha
128 if (getCairoFormat(rBuf
) == CAIRO_FORMAT_A1
)
130 const int nImageSize
= rBuf
.mnHeight
* rBuf
.mnScanlineSize
;
131 unsigned char* pDst
= rBuf
.mpBits
;
132 for (int i
= nImageSize
; --i
>= 0; ++pDst
)
137 std::unique_ptr
<BitmapBuffer
> FastConvert24BitRgbTo32BitCairo(const BitmapBuffer
* pSrc
)
142 assert(pSrc
->mnFormat
== SVP_24BIT_FORMAT
);
143 const long nWidth
= pSrc
->mnWidth
;
144 const long nHeight
= pSrc
->mnHeight
;
145 std::unique_ptr
<BitmapBuffer
> pDst(new BitmapBuffer
);
146 pDst
->mnFormat
= (ScanlineFormat::N32BitTcArgb
| ScanlineFormat::TopDown
);
147 pDst
->mnWidth
= nWidth
;
148 pDst
->mnHeight
= nHeight
;
149 pDst
->mnBitCount
= 32;
150 pDst
->maColorMask
= pSrc
->maColorMask
;
151 pDst
->maPalette
= pSrc
->maPalette
;
154 const bool bFail
= o3tl::checked_multiply
<long>(pDst
->mnBitCount
, nWidth
, nScanlineBase
);
157 SAL_WARN("vcl.gdi", "checked multiply failed");
158 pDst
->mpBits
= nullptr;
162 pDst
->mnScanlineSize
= AlignedWidth4Bytes(nScanlineBase
);
163 if (pDst
->mnScanlineSize
< nScanlineBase
/8)
165 SAL_WARN("vcl.gdi", "scanline calculation wraparound");
166 pDst
->mpBits
= nullptr;
172 pDst
->mpBits
= new sal_uInt8
[ pDst
->mnScanlineSize
* nHeight
];
174 catch (const std::bad_alloc
&)
176 // memory exception, clean up
177 pDst
->mpBits
= nullptr;
181 for (long y
= 0; y
< nHeight
; ++y
)
183 sal_uInt8
* pS
= pSrc
->mpBits
+ y
* pSrc
->mnScanlineSize
;
184 sal_uInt8
* pD
= pDst
->mpBits
+ y
* pDst
->mnScanlineSize
;
185 for (long x
= 0; x
< nWidth
; ++x
)
188 static_assert((SVP_CAIRO_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N32BitTcRgba
, "Expected SVP_CAIRO_FORMAT set to N32BitTcBgra");
189 static_assert((SVP_24BIT_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N24BitTcRgb
, "Expected SVP_24BIT_FORMAT set to N24BitTcRgb");
193 pD
[3] = 0xff; // Alpha
194 #elif defined OSL_BIGENDIAN
195 static_assert((SVP_CAIRO_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N32BitTcArgb
, "Expected SVP_CAIRO_FORMAT set to N32BitTcBgra");
196 static_assert((SVP_24BIT_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N24BitTcRgb
, "Expected SVP_24BIT_FORMAT set to N24BitTcRgb");
197 pD
[0] = 0xff; // Alpha
202 static_assert((SVP_CAIRO_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N32BitTcBgra
, "Expected SVP_CAIRO_FORMAT set to N32BitTcBgra");
203 static_assert((SVP_24BIT_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N24BitTcBgr
, "Expected SVP_24BIT_FORMAT set to N24BitTcBgr");
207 pD
[3] = 0xff; // Alpha
221 explicit SourceHelper(const SalBitmap
& rSourceBitmap
, const bool bForceARGB32
= false)
222 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
223 : m_bForceARGB32(bForceARGB32
)
226 const SvpSalBitmap
& rSrcBmp
= static_cast<const SvpSalBitmap
&>(rSourceBitmap
);
227 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
228 if ((rSrcBmp
.GetBitCount() != 32 && rSrcBmp
.GetBitCount() != 24) || bForceARGB32
)
231 if (rSrcBmp
.GetBitCount() != 32)
234 //big stupid copy here
235 static bool bWarnedOnce
= false;
236 SAL_WARN_IF(!bWarnedOnce
, "vcl.gdi", "non default depth bitmap, slow convert, upscale the input");
239 const BitmapBuffer
* pSrc
= rSrcBmp
.GetBuffer();
240 const SalTwoRect aTwoRect
= { 0, 0, pSrc
->mnWidth
, pSrc
->mnHeight
,
241 0, 0, pSrc
->mnWidth
, pSrc
->mnHeight
};
242 std::unique_ptr
<BitmapBuffer
> pTmp
= (pSrc
->mnFormat
== SVP_24BIT_FORMAT
243 ? FastConvert24BitRgbTo32BitCairo(pSrc
)
244 : StretchAndConvert(*pSrc
, aTwoRect
, SVP_CAIRO_FORMAT
));
245 aTmpBmp
.Create(std::move(pTmp
));
247 assert(aTmpBmp
.GetBitCount() == 32);
248 source
= SvpSalGraphics::createCairoSurface(aTmpBmp
.GetBuffer());
251 source
= SvpSalGraphics::createCairoSurface(rSrcBmp
.GetBuffer());
255 cairo_surface_destroy(source
);
257 cairo_surface_t
* getSurface()
263 cairo_surface_mark_dirty(source
);
265 unsigned char* getBits(sal_Int32
&rStride
)
267 cairo_surface_flush(source
);
269 unsigned char *mask_data
= cairo_image_surface_get_data(source
);
271 const cairo_format_t nFormat
= cairo_image_surface_get_format(source
);
272 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
274 assert(nFormat
== CAIRO_FORMAT_RGB24_888
&& "Expected RGB24_888 image");
277 assert(nFormat
== CAIRO_FORMAT_ARGB32
&& "need to implement CAIRO_FORMAT_A1 after all here");
279 rStride
= cairo_format_stride_for_width(nFormat
, cairo_image_surface_get_width(source
));
284 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
285 const bool m_bForceARGB32
;
287 SvpSalBitmap aTmpBmp
;
288 cairo_surface_t
* source
;
290 SourceHelper(const SourceHelper
&) = delete;
291 SourceHelper
& operator=(const SourceHelper
&) = delete;
297 explicit MaskHelper(const SalBitmap
& rAlphaBitmap
)
299 const SvpSalBitmap
& rMask
= static_cast<const SvpSalBitmap
&>(rAlphaBitmap
);
300 const BitmapBuffer
* pMaskBuf
= rMask
.GetBuffer();
302 if (rAlphaBitmap
.GetBitCount() == 8)
304 // the alpha values need to be inverted for Cairo
305 // so big stupid copy and invert here
306 const int nImageSize
= pMaskBuf
->mnHeight
* pMaskBuf
->mnScanlineSize
;
307 pAlphaBits
.reset( new unsigned char[nImageSize
] );
308 memcpy(pAlphaBits
.get(), pMaskBuf
->mpBits
, nImageSize
);
310 // TODO: make upper layers use standard alpha
311 sal_uInt32
* pLDst
= reinterpret_cast<sal_uInt32
*>(pAlphaBits
.get());
312 for( int i
= nImageSize
/sizeof(sal_uInt32
); --i
>= 0; ++pLDst
)
314 assert(reinterpret_cast<unsigned char*>(pLDst
) == pAlphaBits
.get()+nImageSize
);
316 mask
= cairo_image_surface_create_for_data(pAlphaBits
.get(),
318 pMaskBuf
->mnWidth
, pMaskBuf
->mnHeight
,
319 pMaskBuf
->mnScanlineSize
);
323 // the alpha values need to be inverted for Cairo
324 // so big stupid copy and invert here
325 const int nImageSize
= pMaskBuf
->mnHeight
* pMaskBuf
->mnScanlineSize
;
326 pAlphaBits
.reset( new unsigned char[nImageSize
] );
327 memcpy(pAlphaBits
.get(), pMaskBuf
->mpBits
, nImageSize
);
329 const sal_Int32 nBlackIndex
= pMaskBuf
->maPalette
.GetBestIndex(BitmapColor(COL_BLACK
));
330 if (nBlackIndex
== 0)
332 // TODO: make upper layers use standard alpha
333 unsigned char* pDst
= pAlphaBits
.get();
334 for (int i
= nImageSize
; --i
>= 0; ++pDst
)
338 mask
= cairo_image_surface_create_for_data(pAlphaBits
.get(),
340 pMaskBuf
->mnWidth
, pMaskBuf
->mnHeight
,
341 pMaskBuf
->mnScanlineSize
);
346 cairo_surface_destroy(mask
);
348 cairo_surface_t
* getMask()
353 cairo_surface_t
*mask
;
354 std::unique_ptr
<unsigned char[]> pAlphaBits
;
356 MaskHelper(const MaskHelper
&) = delete;
357 MaskHelper
& operator=(const MaskHelper
&) = delete;
361 bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect
& rTR
, const SalBitmap
& rSourceBitmap
, const SalBitmap
& rAlphaBitmap
)
363 if (rAlphaBitmap
.GetBitCount() != 8 && rAlphaBitmap
.GetBitCount() != 1)
365 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap alpha depth case: " << rAlphaBitmap
.GetBitCount());
369 SourceHelper
aSurface(rSourceBitmap
);
370 cairo_surface_t
* source
= aSurface
.getSurface();
373 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
377 MaskHelper
aMask(rAlphaBitmap
);
378 cairo_surface_t
*mask
= aMask
.getMask();
381 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
385 cairo_t
* cr
= getCairoContext(false);
388 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
390 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
394 cairo_pattern_t
* maskpattern
= cairo_pattern_create_for_surface(mask
);
395 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
396 double fXScale
= static_cast<double>(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
;
397 double fYScale
= static_cast<double>(rTR
.mnDestHeight
)/rTR
.mnSrcHeight
;
398 cairo_scale(cr
, fXScale
, fYScale
);
399 cairo_set_source_surface(cr
, source
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
401 //tdf#114117 when stretching a single pixel width/height source to fit an area
402 //set extend and filter to stretch it with simplest expected interpolation
403 if ((fXScale
!= 1.0 && rTR
.mnSrcWidth
== 1) || (fYScale
!= 1.0 && rTR
.mnSrcHeight
== 1))
405 cairo_pattern_t
* sourcepattern
= cairo_get_source(cr
);
406 cairo_pattern_set_extend(sourcepattern
, CAIRO_EXTEND_REPEAT
);
407 cairo_pattern_set_filter(sourcepattern
, CAIRO_FILTER_NEAREST
);
408 cairo_pattern_set_extend(maskpattern
, CAIRO_EXTEND_REPEAT
);
409 cairo_pattern_set_filter(maskpattern
, CAIRO_FILTER_NEAREST
);
412 //this block is just "cairo_mask_surface", but we have to make it explicit
413 //because of the cairo_pattern_set_filter etc we may want applied
414 cairo_matrix_t matrix
;
415 cairo_matrix_init_translate(&matrix
, rTR
.mnSrcX
, rTR
.mnSrcY
);
416 cairo_pattern_set_matrix(maskpattern
, &matrix
);
417 cairo_mask(cr
, maskpattern
);
419 cairo_pattern_destroy(maskpattern
);
421 releaseCairoContext(cr
, false, extents
);
426 bool SvpSalGraphics::drawTransformedBitmap(
427 const basegfx::B2DPoint
& rNull
,
428 const basegfx::B2DPoint
& rX
,
429 const basegfx::B2DPoint
& rY
,
430 const SalBitmap
& rSourceBitmap
,
431 const SalBitmap
* pAlphaBitmap
)
433 if (pAlphaBitmap
&& pAlphaBitmap
->GetBitCount() != 8 && pAlphaBitmap
->GetBitCount() != 1)
435 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap alpha depth case: " << pAlphaBitmap
->GetBitCount());
439 SourceHelper
aSurface(rSourceBitmap
);
440 cairo_surface_t
* source
= aSurface
.getSurface();
443 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
447 std::unique_ptr
<MaskHelper
> xMask
;
448 cairo_surface_t
*mask
= nullptr;
451 xMask
.reset(new MaskHelper(*pAlphaBitmap
));
452 mask
= xMask
->getMask();
455 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
460 const Size aSize
= rSourceBitmap
.GetSize();
462 cairo_t
* cr
= getCairoContext(false);
465 // setup the image transformation
466 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points
467 const basegfx::B2DVector aXRel
= rX
- rNull
;
468 const basegfx::B2DVector aYRel
= rY
- rNull
;
469 cairo_matrix_t matrix
;
470 cairo_matrix_init(&matrix
,
471 aXRel
.getX()/aSize
.Width(), aXRel
.getY()/aSize
.Width(),
472 aYRel
.getX()/aSize
.Height(), aYRel
.getY()/aSize
.Height(),
473 rNull
.getX(), rNull
.getY());
475 cairo_transform(cr
, &matrix
);
477 cairo_rectangle(cr
, 0, 0, aSize
.Width(), aSize
.Height());
478 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
481 cairo_set_source_surface(cr
, source
, 0, 0);
483 cairo_mask_surface(cr
, mask
, 0, 0);
487 releaseCairoContext(cr
, false, extents
);
492 void SvpSalGraphics::clipRegion(cairo_t
* cr
)
494 RectangleVector aRectangles
;
495 if (!m_aClipRegion
.IsEmpty())
497 m_aClipRegion
.GetRegionRectangles(aRectangles
);
499 if (!aRectangles
.empty())
501 for (auto const& rectangle
: aRectangles
)
503 cairo_rectangle(cr
, rectangle
.Left(), rectangle
.Top(), rectangle
.GetWidth(), rectangle
.GetHeight());
509 bool SvpSalGraphics::drawAlphaRect(long nX
, long nY
, long nWidth
, long nHeight
, sal_uInt8 nTransparency
)
511 cairo_t
* cr
= getCairoContext(false);
514 const double fTransparency
= (100 - nTransparency
) * (1.0/100);
516 basegfx::B2DRange
extents(0, 0, 0, 0);
518 cairo_rectangle(cr
, nX
, nY
, nWidth
, nHeight
);
520 if (m_aFillColor
!= SALCOLOR_NONE
)
522 cairo_set_source_rgba(cr
, m_aFillColor
.GetRed()/255.0,
523 m_aFillColor
.GetGreen()/255.0,
524 m_aFillColor
.GetBlue()/255.0,
527 if (m_aLineColor
== SALCOLOR_NONE
)
528 extents
= getClippedFillDamage(cr
);
530 cairo_fill_preserve(cr
);
533 if (m_aLineColor
!= SALCOLOR_NONE
)
535 cairo_set_source_rgba(cr
, m_aLineColor
.GetRed()/255.0,
536 m_aLineColor
.GetGreen()/255.0,
537 m_aLineColor
.GetBlue()/255.0,
540 extents
= getClippedStrokeDamage(cr
);
542 cairo_stroke_preserve(cr
);
545 releaseCairoContext(cr
, false, extents
);
550 SvpSalGraphics::SvpSalGraphics()
551 : m_pSurface(nullptr)
553 , m_aLineColor(Color(0x00, 0x00, 0x00))
554 , m_aFillColor(Color(0xFF, 0xFF, 0XFF))
555 , m_ePaintMode(PaintMode::Over
)
556 , m_aTextRenderImpl(*this)
560 SvpSalGraphics::~SvpSalGraphics()
564 void SvpSalGraphics::setSurface(cairo_surface_t
* pSurface
, const basegfx::B2IVector
& rSize
)
566 m_pSurface
= pSurface
;
567 m_aFrameSize
= rSize
;
568 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
569 cairo_surface_get_device_scale(pSurface
, &m_fScale
, nullptr);
574 void SvpSalGraphics::GetResolution( sal_Int32
& rDPIX
, sal_Int32
& rDPIY
)
579 sal_uInt16
SvpSalGraphics::GetBitCount() const
581 if (cairo_surface_get_content(m_pSurface
) != CAIRO_CONTENT_COLOR_ALPHA
)
586 long SvpSalGraphics::GetGraphicsWidth() const
588 return m_pSurface
? m_aFrameSize
.getX() : 0;
591 void SvpSalGraphics::ResetClipRegion()
593 m_aClipRegion
.SetNull();
596 bool SvpSalGraphics::setClipRegion( const vcl::Region
& i_rClip
)
598 m_aClipRegion
= i_rClip
;
602 void SvpSalGraphics::SetLineColor()
604 m_aLineColor
= SALCOLOR_NONE
;
607 void SvpSalGraphics::SetLineColor( Color nColor
)
609 m_aLineColor
= nColor
;
612 void SvpSalGraphics::SetFillColor()
614 m_aFillColor
= SALCOLOR_NONE
;
617 void SvpSalGraphics::SetFillColor( Color nColor
)
619 m_aFillColor
= nColor
;
622 void SvpSalGraphics::SetXORMode(bool bSet
)
624 m_ePaintMode
= bSet
? PaintMode::Xor
: PaintMode::Over
;
627 void SvpSalGraphics::SetROPLineColor( SalROPColor nROPColor
)
631 case SalROPColor::N0
:
632 m_aLineColor
= Color(0, 0, 0);
634 case SalROPColor::N1
:
635 m_aLineColor
= Color(0xff, 0xff, 0xff);
637 case SalROPColor::Invert
:
638 m_aLineColor
= Color(0xff, 0xff, 0xff);
643 void SvpSalGraphics::SetROPFillColor( SalROPColor nROPColor
)
647 case SalROPColor::N0
:
648 m_aFillColor
= Color(0, 0, 0);
650 case SalROPColor::N1
:
651 m_aFillColor
= Color(0xff, 0xff, 0xff);
653 case SalROPColor::Invert
:
654 m_aFillColor
= Color(0xff, 0xff, 0xff);
659 void SvpSalGraphics::drawPixel( long nX
, long nY
)
661 if (m_aLineColor
!= SALCOLOR_NONE
)
663 drawPixel(nX
, nY
, m_aLineColor
);
667 void SvpSalGraphics::drawPixel( long nX
, long nY
, Color nColor
)
669 Color aOrigFillColor
= m_aFillColor
;
670 Color aOrigLineColor
= m_aLineColor
;
672 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+1, nY
+1));
673 m_aLineColor
= SALCOLOR_NONE
;
674 m_aFillColor
= nColor
;
676 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
678 m_aFillColor
= aOrigFillColor
;
679 m_aLineColor
= aOrigLineColor
;
682 void SvpSalGraphics::drawRect( long nX
, long nY
, long nWidth
, long nHeight
)
684 // because of the -1 hack we have to do fill and draw separately
685 Color aOrigFillColor
= m_aFillColor
;
686 Color aOrigLineColor
= m_aLineColor
;
687 m_aFillColor
= SALCOLOR_NONE
;
688 m_aLineColor
= SALCOLOR_NONE
;
690 if (aOrigFillColor
!= SALCOLOR_NONE
)
692 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+nWidth
, nY
+nHeight
));
693 m_aFillColor
= aOrigFillColor
;
694 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
695 m_aFillColor
= SALCOLOR_NONE
;
698 if (aOrigLineColor
!= SALCOLOR_NONE
)
700 // need same -1 hack as X11SalGraphicsImpl::drawRect
701 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle( nX
, nY
, nX
+nWidth
-1, nY
+nHeight
-1));
702 m_aLineColor
= aOrigLineColor
;
703 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
704 m_aLineColor
= SALCOLOR_NONE
;
707 m_aFillColor
= aOrigFillColor
;
708 m_aLineColor
= aOrigLineColor
;
711 void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
713 basegfx::B2DPolygon aPoly
;
714 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
715 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
716 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
717 aPoly
.setClosed(false);
719 drawPolyLine(aPoly
, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter
,
720 css::drawing::LineCap_BUTT
, 15.0 * F_PI180
/*default*/);
723 void SvpSalGraphics::drawPolygon(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
725 basegfx::B2DPolygon aPoly
;
726 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
727 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
728 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
730 drawPolyPolygon(basegfx::B2DPolyPolygon(aPoly
));
733 void SvpSalGraphics::drawPolyPolygon(sal_uInt32 nPoly
,
734 const sal_uInt32
* pPointCounts
,
735 PCONSTSALPOINT
* pPtAry
)
737 basegfx::B2DPolyPolygon aPolyPoly
;
738 for(sal_uInt32 nPolygon
= 0; nPolygon
< nPoly
; ++nPolygon
)
740 sal_uInt32 nPoints
= pPointCounts
[nPolygon
];
743 PCONSTSALPOINT pPoints
= pPtAry
[nPolygon
];
744 basegfx::B2DPolygon aPoly
;
745 aPoly
.append( basegfx::B2DPoint(pPoints
->mnX
, pPoints
->mnY
), nPoints
);
746 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
747 aPoly
.setB2DPoint(i
, basegfx::B2DPoint( pPoints
[i
].mnX
, pPoints
[i
].mnY
));
749 aPolyPoly
.append(aPoly
);
753 drawPolyPolygon(aPolyPoly
);
756 static const basegfx::B2DPoint
aHalfPointOfs(0.5, 0.5);
758 static void AddPolygonToPath(cairo_t
* cr
, const basegfx::B2DPolygon
& rPolygon
, bool bClosePath
,
759 bool bPixelSnap
, bool bLineDraw
)
761 // short circuit if there is nothing to do
762 const int nPointCount
= rPolygon
.count();
763 if( nPointCount
<= 0 )
768 const bool bHasCurves
= rPolygon
.areControlPointsUsed();
769 basegfx::B2DPoint aLast
;
771 for( int nPointIdx
= 0, nPrevIdx
= 0;; nPrevIdx
= nPointIdx
++ )
773 int nClosedIdx
= nPointIdx
;
774 if( nPointIdx
>= nPointCount
)
776 // prepare to close last curve segment if needed
777 if( bClosePath
&& (nPointIdx
== nPointCount
) )
787 basegfx::B2DPoint aPoint
= rPolygon
.getB2DPoint( nClosedIdx
);
791 // snap device coordinates to full pixels
792 aPoint
.setX( basegfx::fround( aPoint
.getX() ) );
793 aPoint
.setY( basegfx::fround( aPoint
.getY() ) );
798 aPoint
+= aHalfPointOfs
;
803 // first point => just move there
804 cairo_move_to(cr
, aPoint
.getX(), aPoint
.getY());
809 bool bPendingCurve
= false;
812 bPendingCurve
= rPolygon
.isNextControlPointUsed( nPrevIdx
);
813 bPendingCurve
|= rPolygon
.isPrevControlPointUsed( nClosedIdx
);
816 if( !bPendingCurve
) // line segment
818 cairo_line_to(cr
, aPoint
.getX(), aPoint
.getY());
820 else // cubic bezier segment
822 basegfx::B2DPoint aCP1
= rPolygon
.getNextControlPoint( nPrevIdx
);
823 basegfx::B2DPoint aCP2
= rPolygon
.getPrevControlPoint( nClosedIdx
);
826 aCP1
+= aHalfPointOfs
;
827 aCP2
+= aHalfPointOfs
;
830 // tdf#99165 if the control points are 'empty', create the mathematical
831 // correct replacement ones to avoid problems with the graphical sub-system
832 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
833 // vector was wrong. Best alternative is one as close as possible which means short.
834 if (aCP1
.equal(aLast
))
836 aCP1
= aLast
+ ((aCP2
- aLast
) * 0.0005);
839 if(aCP2
.equal(aPoint
))
841 aCP2
= aPoint
+ ((aCP1
- aPoint
) * 0.0005);
844 cairo_curve_to(cr
, aCP1
.getX(), aCP1
.getY(), aCP2
.getX(), aCP2
.getY(),
845 aPoint
.getX(), aPoint
.getY());
853 cairo_close_path(cr
);
857 void SvpSalGraphics::drawLine( long nX1
, long nY1
, long nX2
, long nY2
)
859 basegfx::B2DPolygon aPoly
;
860 aPoly
.append(basegfx::B2DPoint(nX1
, nY1
), 2);
861 aPoly
.setB2DPoint(1, basegfx::B2DPoint(nX2
, nY2
));
862 aPoly
.setClosed(false);
864 cairo_t
* cr
= getCairoContext(false);
867 AddPolygonToPath(cr
, aPoly
, aPoly
.isClosed(), !getAntiAliasB2DDraw(), true);
869 applyColor(cr
, m_aLineColor
);
871 basegfx::B2DRange extents
= getClippedStrokeDamage(cr
);
875 releaseCairoContext(cr
, false, extents
);
878 bool SvpSalGraphics::drawPolyLine(
879 const basegfx::B2DPolygon
& rPolyLine
,
880 double fTransparency
,
881 const basegfx::B2DVector
& rLineWidths
,
882 basegfx::B2DLineJoin eLineJoin
,
883 css::drawing::LineCap eLineCap
,
884 double fMiterMinimumAngle
)
886 // short circuit if there is nothing to do
887 const int nPointCount
= rPolyLine
.count();
888 if (nPointCount
<= 0)
893 const bool bNoJoin
= (basegfx::B2DLineJoin::NONE
== eLineJoin
&& basegfx::fTools::more(rLineWidths
.getX(), 0.0));
895 cairo_t
* cr
= getCairoContext(false);
898 // setup line attributes
899 cairo_line_join_t eCairoLineJoin
= CAIRO_LINE_JOIN_MITER
;
902 case basegfx::B2DLineJoin::Bevel
:
903 eCairoLineJoin
= CAIRO_LINE_JOIN_BEVEL
;
905 case basegfx::B2DLineJoin::Round
:
906 eCairoLineJoin
= CAIRO_LINE_JOIN_ROUND
;
908 case basegfx::B2DLineJoin::NONE
:
909 case basegfx::B2DLineJoin::Miter
:
910 eCairoLineJoin
= CAIRO_LINE_JOIN_MITER
;
914 // convert miter minimum angle to miter limit
915 double fMiterLimit
= 1.0 / sin( fMiterMinimumAngle
/ 2.0);
917 // setup cap attribute
918 cairo_line_cap_t
eCairoLineCap(CAIRO_LINE_CAP_BUTT
);
922 default: // css::drawing::LineCap_BUTT:
924 eCairoLineCap
= CAIRO_LINE_CAP_BUTT
;
927 case css::drawing::LineCap_ROUND
:
929 eCairoLineCap
= CAIRO_LINE_CAP_ROUND
;
932 case css::drawing::LineCap_SQUARE
:
934 eCairoLineCap
= CAIRO_LINE_CAP_SQUARE
;
939 cairo_set_source_rgba(cr
, m_aLineColor
.GetRed()/255.0,
940 m_aLineColor
.GetGreen()/255.0,
941 m_aLineColor
.GetBlue()/255.0,
944 cairo_set_line_join(cr
, eCairoLineJoin
);
945 cairo_set_line_cap(cr
, eCairoLineCap
);
946 cairo_set_line_width(cr
, rLineWidths
.getX());
947 cairo_set_miter_limit(cr
, fMiterLimit
);
950 basegfx::B2DRange
extents(0, 0, 0, 0);
954 AddPolygonToPath(cr
, rPolyLine
, rPolyLine
.isClosed(), !getAntiAliasB2DDraw(), true);
955 extents
= getClippedStrokeDamage(cr
);
960 // emulate rendering::PathJoinType::NONE by painting single edges
961 const sal_uInt32
nEdgeCount(rPolyLine
.isClosed() ? nPointCount
: nPointCount
- 1);
962 basegfx::B2DPolygon aEdge
;
963 aEdge
.append(rPolyLine
.getB2DPoint(0));
964 aEdge
.append(basegfx::B2DPoint(0.0, 0.0));
966 for (sal_uInt32 i
= 0; i
< nEdgeCount
; ++i
)
968 const sal_uInt32
nNextIndex((i
+ 1) % nPointCount
);
969 aEdge
.setB2DPoint(1, rPolyLine
.getB2DPoint(nNextIndex
));
970 aEdge
.setNextControlPoint(0, rPolyLine
.getNextControlPoint(i
% nPointCount
));
971 aEdge
.setPrevControlPoint(1, rPolyLine
.getPrevControlPoint(nNextIndex
));
973 AddPolygonToPath(cr
, aEdge
, false, !getAntiAliasB2DDraw(), true);
975 extents
.expand(getStrokeDamage(cr
));
980 aEdge
.setB2DPoint(0, aEdge
.getB2DPoint(1));
983 extents
.intersect(getClipBox(cr
));
986 releaseCairoContext(cr
, false, extents
);
991 bool SvpSalGraphics::drawPolyLineBezier( sal_uInt32
,
995 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolyLineBezier case");
999 bool SvpSalGraphics::drawPolygonBezier( sal_uInt32
,
1003 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolygonBezier case");
1007 bool SvpSalGraphics::drawPolyPolygonBezier( sal_uInt32
,
1009 const SalPoint
* const*,
1010 const PolyFlags
* const* )
1012 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolyPolygonBezier case");
1016 void SvpSalGraphics::setupPolyPolygon(cairo_t
* cr
, const basegfx::B2DPolyPolygon
& rPolyPoly
)
1020 for (const auto & rPoly
: rPolyPoly
)
1021 AddPolygonToPath(cr
, rPoly
, true, !getAntiAliasB2DDraw(), m_aLineColor
!= SALCOLOR_NONE
);
1024 bool SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
, double fTransparency
)
1026 cairo_t
* cr
= getCairoContext(true);
1028 setupPolyPolygon(cr
, rPolyPoly
);
1030 basegfx::B2DRange
extents(0, 0, 0, 0);
1032 if (m_aFillColor
!= SALCOLOR_NONE
)
1034 cairo_set_source_rgba(cr
, m_aFillColor
.GetRed()/255.0,
1035 m_aFillColor
.GetGreen()/255.0,
1036 m_aFillColor
.GetBlue()/255.0,
1039 if (m_aLineColor
== SALCOLOR_NONE
)
1040 extents
= getClippedFillDamage(cr
);
1042 cairo_fill_preserve(cr
);
1045 if (m_aLineColor
!= SALCOLOR_NONE
)
1047 cairo_set_source_rgba(cr
, m_aLineColor
.GetRed()/255.0,
1048 m_aLineColor
.GetGreen()/255.0,
1049 m_aLineColor
.GetBlue()/255.0,
1052 extents
= getClippedStrokeDamage(cr
);
1054 cairo_stroke_preserve(cr
);
1057 releaseCairoContext(cr
, true, extents
);
1062 void SvpSalGraphics::applyColor(cairo_t
*cr
, Color aColor
)
1064 if (cairo_surface_get_content(m_pSurface
) == CAIRO_CONTENT_COLOR_ALPHA
)
1066 cairo_set_source_rgba(cr
, aColor
.GetRed()/255.0,
1067 aColor
.GetGreen()/255.0,
1068 aColor
.GetBlue()/255.0,
1073 double fSet
= aColor
== COL_BLACK
? 1.0 : 0.0;
1074 cairo_set_source_rgba(cr
, 1, 1, 1, fSet
);
1075 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
1079 void SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
)
1081 cairo_t
* cr
= getCairoContext(true);
1083 setupPolyPolygon(cr
, rPolyPoly
);
1085 basegfx::B2DRange
extents(0, 0, 0, 0);
1087 if (m_aFillColor
!= SALCOLOR_NONE
)
1089 applyColor(cr
, m_aFillColor
);
1090 if (m_aLineColor
== SALCOLOR_NONE
)
1091 extents
= getClippedFillDamage(cr
);
1092 cairo_fill_preserve(cr
);
1095 if (m_aLineColor
!= SALCOLOR_NONE
)
1097 applyColor(cr
, m_aLineColor
);
1098 extents
= getClippedStrokeDamage(cr
);
1099 cairo_stroke_preserve(cr
);
1102 releaseCairoContext(cr
, true, extents
);
1105 void SvpSalGraphics::copyArea( long nDestX
,
1111 bool /*bWindowInvalidate*/ )
1113 SalTwoRect
aTR(nSrcX
, nSrcY
, nSrcWidth
, nSrcHeight
, nDestX
, nDestY
, nSrcWidth
, nSrcHeight
);
1114 copyBits(aTR
, this);
1117 static basegfx::B2DRange
renderSource(cairo_t
* cr
, const SalTwoRect
& rTR
,
1118 cairo_surface_t
* source
)
1120 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
1122 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
1126 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
1127 double fXScale
= 1.0f
;
1128 double fYScale
= 1.0f
;
1129 if (rTR
.mnSrcWidth
!= 0 && rTR
.mnSrcHeight
!= 0) {
1130 fXScale
= static_cast<double>(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
;
1131 fYScale
= static_cast<double>(rTR
.mnDestHeight
)/rTR
.mnSrcHeight
;
1132 cairo_scale(cr
, fXScale
, fYScale
);
1136 cairo_set_source_surface(cr
, source
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
1137 if ((fXScale
!= 1.0 && rTR
.mnSrcWidth
== 1) || (fYScale
!= 1.0 && rTR
.mnSrcHeight
== 1))
1139 cairo_pattern_t
* sourcepattern
= cairo_get_source(cr
);
1140 cairo_pattern_set_extend(sourcepattern
, CAIRO_EXTEND_REPEAT
);
1141 cairo_pattern_set_filter(sourcepattern
, CAIRO_FILTER_NEAREST
);
1143 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
1150 void SvpSalGraphics::copySource( const SalTwoRect
& rTR
,
1151 cairo_surface_t
* source
)
1153 cairo_t
* cr
= getCairoContext(false);
1156 basegfx::B2DRange extents
= renderSource(cr
, rTR
, source
);
1158 releaseCairoContext(cr
, false, extents
);
1161 void SvpSalGraphics::copyBits( const SalTwoRect
& rTR
,
1162 SalGraphics
* pSrcGraphics
)
1164 SalTwoRect
aTR(rTR
);
1166 SvpSalGraphics
* pSrc
= pSrcGraphics
?
1167 static_cast<SvpSalGraphics
*>(pSrcGraphics
) : this;
1169 cairo_surface_t
* source
= pSrc
->m_pSurface
;
1171 cairo_surface_t
*pCopy
= nullptr;
1174 //self copy is a problem, so dup source in that case
1175 pCopy
= cairo_surface_create_similar(source
,
1176 cairo_surface_get_content(m_pSurface
),
1177 aTR
.mnSrcWidth
* m_fScale
,
1178 aTR
.mnSrcHeight
* m_fScale
);
1179 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1180 cairo_surface_set_device_scale(pCopy
, m_fScale
, m_fScale
);
1182 cairo_t
* cr
= cairo_create(pCopy
);
1183 cairo_set_source_surface(cr
, source
, -aTR
.mnSrcX
, -aTR
.mnSrcY
);
1184 cairo_rectangle(cr
, 0, 0, aTR
.mnSrcWidth
, aTR
.mnSrcHeight
);
1194 copySource(aTR
, source
);
1197 cairo_surface_destroy(pCopy
);
1200 void SvpSalGraphics::drawBitmap(const SalTwoRect
& rTR
, const SalBitmap
& rSourceBitmap
)
1202 SourceHelper
aSurface(rSourceBitmap
);
1203 cairo_surface_t
* source
= aSurface
.getSurface();
1204 copySource(rTR
, source
);
1207 void SvpSalGraphics::drawBitmap( const SalTwoRect
& rTR
,
1208 const SalBitmap
& rSourceBitmap
,
1209 const SalBitmap
& rTransparentBitmap
)
1211 drawAlphaBitmap(rTR
, rSourceBitmap
, rTransparentBitmap
);
1214 static sal_uInt8
unpremultiply(sal_uInt8 c
, sal_uInt8 a
)
1216 return (a
> 0) ? (c
* 255 + a
/ 2) / a
: 0;
1219 static sal_uInt8
premultiply(sal_uInt8 c
, sal_uInt8 a
)
1221 return (c
* a
+ 127) / 255;
1224 void SvpSalGraphics::drawMask( const SalTwoRect
& rTR
,
1225 const SalBitmap
& rSalBitmap
,
1228 /** creates an image from the given rectangle, replacing all black pixels
1229 * with nMaskColor and make all other full transparent */
1230 SourceHelper
aSurface(rSalBitmap
, true); // The mask is argb32
1231 if (!aSurface
.getSurface())
1233 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawMask case");
1237 unsigned char *mask_data
= aSurface
.getBits(nStride
);
1238 for (sal_Int32 y
= rTR
.mnSrcY
; y
< rTR
.mnSrcY
+ rTR
.mnSrcHeight
; ++y
)
1240 unsigned char *row
= mask_data
+ (nStride
*y
);
1241 unsigned char *data
= row
+ (rTR
.mnSrcX
* 4);
1242 for (sal_Int32 x
= rTR
.mnSrcX
; x
< rTR
.mnSrcX
+ rTR
.mnSrcWidth
; ++x
)
1244 sal_uInt8 b
= unpremultiply(data
[SVP_CAIRO_BLUE
], data
[SVP_CAIRO_ALPHA
]);
1245 sal_uInt8 g
= unpremultiply(data
[SVP_CAIRO_GREEN
], data
[SVP_CAIRO_ALPHA
]);
1246 sal_uInt8 r
= unpremultiply(data
[SVP_CAIRO_RED
], data
[SVP_CAIRO_ALPHA
]);
1247 if (r
== 0 && g
== 0 && b
== 0)
1249 data
[0] = nMaskColor
.GetBlue();
1250 data
[1] = nMaskColor
.GetGreen();
1251 data
[2] = nMaskColor
.GetRed();
1264 aSurface
.mark_dirty();
1266 cairo_t
* cr
= getCairoContext(false);
1269 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
1271 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
1275 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
1276 double fXScale
= static_cast<double>(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
;
1277 double fYScale
= static_cast<double>(rTR
.mnDestHeight
)/rTR
.mnSrcHeight
;
1278 cairo_scale(cr
, fXScale
, fYScale
);
1279 cairo_set_source_surface(cr
, aSurface
.getSurface(), -rTR
.mnSrcX
, -rTR
.mnSrcY
);
1280 if ((fXScale
!= 1.0 && rTR
.mnSrcWidth
== 1) || (fYScale
!= 1.0 && rTR
.mnSrcHeight
== 1))
1282 cairo_pattern_t
* sourcepattern
= cairo_get_source(cr
);
1283 cairo_pattern_set_extend(sourcepattern
, CAIRO_EXTEND_REPEAT
);
1284 cairo_pattern_set_filter(sourcepattern
, CAIRO_FILTER_NEAREST
);
1288 releaseCairoContext(cr
, false, extents
);
1291 SalBitmap
* SvpSalGraphics::getBitmap( long nX
, long nY
, long nWidth
, long nHeight
)
1293 SvpSalBitmap
* pBitmap
= new SvpSalBitmap();
1295 if (GetBitCount() == 1)
1297 aPal
.SetEntryCount(2);
1298 aPal
[0] = COL_BLACK
;
1299 aPal
[1] = COL_WHITE
;
1302 if (!pBitmap
->Create(Size(nWidth
, nHeight
), GetBitCount(), aPal
))
1304 SAL_WARN("vcl.gdi", "SvpSalGraphics::getBitmap, cannot create bitmap");
1309 cairo_surface_t
* target
= SvpSalGraphics::createCairoSurface(pBitmap
->GetBuffer());
1310 cairo_t
* cr
= cairo_create(target
);
1312 SalTwoRect
aTR(nX
, nY
, nWidth
, nHeight
, 0, 0, nWidth
, nHeight
);
1313 renderSource(cr
, aTR
, m_pSurface
);
1316 cairo_surface_destroy(target
);
1318 Toggle1BitTransparency(*pBitmap
->GetBuffer());
1323 Color
SvpSalGraphics::getPixel( long nX
, long nY
)
1325 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
1326 cairo_surface_t
*target
= cairo_surface_create_similar_image(m_pSurface
,
1328 cairo_surface_t
*target
= cairo_image_surface_create(
1330 CAIRO_FORMAT_ARGB32
, 1, 1);
1332 cairo_t
* cr
= cairo_create(target
);
1334 cairo_rectangle(cr
, 0, 0, 1, 1);
1335 cairo_set_source_surface(cr
, m_pSurface
, -nX
, -nY
);
1339 cairo_surface_flush(target
);
1340 unsigned char *data
= cairo_image_surface_get_data(target
);
1341 sal_uInt8 b
= unpremultiply(data
[SVP_CAIRO_BLUE
], data
[SVP_CAIRO_ALPHA
]);
1342 sal_uInt8 g
= unpremultiply(data
[SVP_CAIRO_GREEN
], data
[SVP_CAIRO_ALPHA
]);
1343 sal_uInt8 r
= unpremultiply(data
[SVP_CAIRO_RED
], data
[SVP_CAIRO_ALPHA
]);
1344 Color nRet
= Color(r
, g
, b
);
1346 cairo_surface_destroy(target
);
1353 cairo_pattern_t
* create_stipple()
1355 static unsigned char data
[16] = { 0xFF, 0xFF, 0x00, 0x00,
1356 0xFF, 0xFF, 0x00, 0x00,
1357 0x00, 0x00, 0xFF, 0xFF,
1358 0x00, 0x00, 0xFF, 0xFF };
1359 cairo_surface_t
* surface
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_A8
, 4, 4, 4);
1360 cairo_pattern_t
* pattern
= cairo_pattern_create_for_surface(surface
);
1361 cairo_surface_destroy(surface
);
1362 cairo_pattern_set_extend(pattern
, CAIRO_EXTEND_REPEAT
);
1363 cairo_pattern_set_filter(pattern
, CAIRO_FILTER_NEAREST
);
1368 void SvpSalGraphics::invert(const basegfx::B2DPolygon
&rPoly
, SalInvert nFlags
)
1370 cairo_t
* cr
= getCairoContext(false);
1373 basegfx::B2DRange
extents(0, 0, 0, 0);
1375 AddPolygonToPath(cr
, rPoly
, true, !getAntiAliasB2DDraw(), false);
1377 cairo_set_source_rgb(cr
, 1.0, 1.0, 1.0);
1379 if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 10, 0))
1381 cairo_set_operator(cr
, CAIRO_OPERATOR_DIFFERENCE
);
1385 SAL_WARN("vcl.gdi", "SvpSalGraphics::invert, archaic cairo");
1388 if (nFlags
& SalInvert::TrackFrame
)
1390 cairo_set_line_width(cr
, 2.0);
1391 const double dashLengths
[2] = { 4.0, 4.0 };
1392 cairo_set_dash(cr
, dashLengths
, 2, 0);
1394 extents
= getClippedStrokeDamage(cr
);
1395 //see tdf#106577 under wayland, some pixel droppings seen, maybe we're
1396 //out by one somewhere, or cairo_stroke_extents is confused by
1404 extents
= getClippedFillDamage(cr
);
1408 if (nFlags
& SalInvert::N50
)
1410 cairo_pattern_t
*pattern
= create_stipple();
1411 cairo_surface_t
* surface
= cairo_surface_create_similar(m_pSurface
,
1412 cairo_surface_get_content(m_pSurface
),
1413 extents
.getWidth() * m_fScale
,
1414 extents
.getHeight() * m_fScale
);
1416 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1417 cairo_surface_set_device_scale(surface
, m_fScale
, m_fScale
);
1419 cairo_t
* stipple_cr
= cairo_create(surface
);
1420 cairo_set_source_rgb(stipple_cr
, 1.0, 1.0, 1.0);
1421 cairo_mask(stipple_cr
, pattern
);
1422 cairo_pattern_destroy(pattern
);
1423 cairo_destroy(stipple_cr
);
1424 cairo_mask_surface(cr
, surface
, extents
.getMinX(), extents
.getMinY());
1425 cairo_surface_destroy(surface
);
1433 releaseCairoContext(cr
, false, extents
);
1436 void SvpSalGraphics::invert( long nX
, long nY
, long nWidth
, long nHeight
, SalInvert nFlags
)
1438 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+nWidth
, nY
+nHeight
));
1440 invert(aRect
, nFlags
);
1443 void SvpSalGraphics::invert(sal_uInt32 nPoints
, const SalPoint
* pPtAry
, SalInvert nFlags
)
1445 basegfx::B2DPolygon aPoly
;
1446 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
1447 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1448 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
1449 aPoly
.setClosed(true);
1451 invert(aPoly
, nFlags
);
1454 bool SvpSalGraphics::drawEPS( long, long, long, long, void*, sal_uLong
)
1461 bool isCairoCompatible(const BitmapBuffer
* pBuffer
)
1466 // We use Cairo that supports 24-bit RGB.
1467 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
1468 if (pBuffer
->mnBitCount
!= 32 && pBuffer
->mnBitCount
!= 24 && pBuffer
->mnBitCount
!= 1)
1470 if (pBuffer
->mnBitCount
!= 32 && pBuffer
->mnBitCount
!= 1)
1474 cairo_format_t nFormat
= getCairoFormat(*pBuffer
);
1475 return (cairo_format_stride_for_width(nFormat
, pBuffer
->mnWidth
) == pBuffer
->mnScanlineSize
);
1479 cairo_surface_t
* SvpSalGraphics::createCairoSurface(const BitmapBuffer
*pBuffer
)
1481 if (!isCairoCompatible(pBuffer
))
1484 cairo_format_t nFormat
= getCairoFormat(*pBuffer
);
1485 cairo_surface_t
*target
=
1486 cairo_image_surface_create_for_data(pBuffer
->mpBits
,
1488 pBuffer
->mnWidth
, pBuffer
->mnHeight
,
1489 pBuffer
->mnScanlineSize
);
1490 if (cairo_surface_status(target
) != CAIRO_STATUS_SUCCESS
)
1492 cairo_surface_destroy(target
);
1498 cairo_t
* SvpSalGraphics::createTmpCompatibleCairoContext() const
1500 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
1501 cairo_surface_t
*target
= cairo_surface_create_similar_image(m_pSurface
,
1503 cairo_surface_t
*target
= cairo_image_surface_create(
1505 CAIRO_FORMAT_ARGB32
,
1506 m_aFrameSize
.getX() * m_fScale
,
1507 m_aFrameSize
.getY() * m_fScale
);
1509 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1510 cairo_surface_set_device_scale(target
, m_fScale
, m_fScale
);
1513 return cairo_create(target
);
1516 cairo_t
* SvpSalGraphics::getCairoContext(bool bXorModeAllowed
) const
1519 if (m_ePaintMode
== PaintMode::Xor
&& bXorModeAllowed
)
1520 cr
= createTmpCompatibleCairoContext();
1522 cr
= cairo_create(m_pSurface
);
1523 cairo_set_line_width(cr
, 1);
1524 cairo_set_fill_rule(cr
, CAIRO_FILL_RULE_EVEN_ODD
);
1525 cairo_set_antialias(cr
, getAntiAliasB2DDraw() ? CAIRO_ANTIALIAS_DEFAULT
: CAIRO_ANTIALIAS_NONE
);
1526 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
1530 cairo_user_data_key_t
* SvpSalGraphics::getDamageKey()
1532 static cairo_user_data_key_t aDamageKey
;
1536 void SvpSalGraphics::releaseCairoContext(cairo_t
* cr
, bool bXorModeAllowed
, const basegfx::B2DRange
& rExtents
) const
1538 const bool bXoring
= (m_ePaintMode
== PaintMode::Xor
&& bXorModeAllowed
);
1540 if (rExtents
.isEmpty())
1542 //nothing changed, return early
1545 cairo_surface_t
* surface
= cairo_get_target(cr
);
1546 cairo_surface_destroy(surface
);
1552 sal_Int32
nExtentsLeft(rExtents
.getMinX()), nExtentsTop(rExtents
.getMinY());
1553 sal_Int32
nExtentsRight(rExtents
.getMaxX()), nExtentsBottom(rExtents
.getMaxY());
1554 sal_Int32 nWidth
= m_aFrameSize
.getX();
1555 sal_Int32 nHeight
= m_aFrameSize
.getY();
1556 nExtentsLeft
= std::max
<sal_Int32
>(nExtentsLeft
, 0);
1557 nExtentsTop
= std::max
<sal_Int32
>(nExtentsTop
, 0);
1558 nExtentsRight
= std::min
<sal_Int32
>(nExtentsRight
, nWidth
);
1559 nExtentsBottom
= std::min
<sal_Int32
>(nExtentsBottom
, nHeight
);
1561 cairo_surface_t
* surface
= cairo_get_target(cr
);
1562 cairo_surface_flush(surface
);
1564 //For the most part we avoid the use of XOR these days, but there
1565 //are some edge cases where legacy stuff still supports it, so
1566 //emulate it (slowly) here.
1569 cairo_surface_t
* target_surface
= m_pSurface
;
1570 if (cairo_surface_get_type(target_surface
) != CAIRO_SURFACE_TYPE_IMAGE
)
1572 //in the unlikely case we can't use m_pSurface directly, copy contents
1573 //to another temp image surface
1574 cairo_t
* copycr
= createTmpCompatibleCairoContext();
1575 cairo_rectangle(copycr
, nExtentsLeft
, nExtentsTop
,
1576 nExtentsRight
- nExtentsLeft
,
1577 nExtentsBottom
- nExtentsTop
);
1578 cairo_set_source_surface(copycr
, m_pSurface
, 0, 0);
1579 cairo_paint(copycr
);
1580 target_surface
= cairo_get_target(copycr
);
1581 cairo_destroy(copycr
);
1584 cairo_surface_flush(target_surface
);
1585 unsigned char *target_surface_data
= cairo_image_surface_get_data(target_surface
);
1586 unsigned char *xor_surface_data
= cairo_image_surface_get_data(surface
);
1588 cairo_format_t nFormat
= cairo_image_surface_get_format(target_surface
);
1589 assert(nFormat
== CAIRO_FORMAT_ARGB32
&& "need to implement CAIRO_FORMAT_A1 after all here");
1590 sal_Int32 nStride
= cairo_format_stride_for_width(nFormat
, nWidth
* m_fScale
);
1591 sal_Int32 nUnscaledExtentsLeft
= nExtentsLeft
* m_fScale
;
1592 sal_Int32 nUnscaledExtentsRight
= nExtentsRight
* m_fScale
;
1593 sal_Int32 nUnscaledExtentsTop
= nExtentsTop
* m_fScale
;
1594 sal_Int32 nUnscaledExtentsBottom
= nExtentsBottom
* m_fScale
;
1595 for (sal_Int32 y
= nUnscaledExtentsTop
; y
< nUnscaledExtentsBottom
; ++y
)
1597 unsigned char *true_row
= target_surface_data
+ (nStride
*y
);
1598 unsigned char *xor_row
= xor_surface_data
+ (nStride
*y
);
1599 unsigned char *true_data
= true_row
+ (nUnscaledExtentsLeft
* 4);
1600 unsigned char *xor_data
= xor_row
+ (nUnscaledExtentsLeft
* 4);
1601 for (sal_Int32 x
= nUnscaledExtentsLeft
; x
< nUnscaledExtentsRight
; ++x
)
1603 sal_uInt8 b
= unpremultiply(true_data
[SVP_CAIRO_BLUE
], true_data
[SVP_CAIRO_ALPHA
]) ^
1604 unpremultiply(xor_data
[SVP_CAIRO_BLUE
], xor_data
[SVP_CAIRO_ALPHA
]);
1605 sal_uInt8 g
= unpremultiply(true_data
[SVP_CAIRO_GREEN
], true_data
[SVP_CAIRO_ALPHA
]) ^
1606 unpremultiply(xor_data
[SVP_CAIRO_GREEN
], xor_data
[SVP_CAIRO_ALPHA
]);
1607 sal_uInt8 r
= unpremultiply(true_data
[SVP_CAIRO_RED
], true_data
[SVP_CAIRO_ALPHA
]) ^
1608 unpremultiply(xor_data
[SVP_CAIRO_RED
], xor_data
[SVP_CAIRO_ALPHA
]);
1609 true_data
[0] = premultiply(b
, true_data
[SVP_CAIRO_ALPHA
]);
1610 true_data
[1] = premultiply(g
, true_data
[SVP_CAIRO_ALPHA
]);
1611 true_data
[2] = premultiply(r
, true_data
[SVP_CAIRO_ALPHA
]);
1616 cairo_surface_mark_dirty(target_surface
);
1618 if (target_surface
!= m_pSurface
)
1620 cairo_t
* copycr
= cairo_create(m_pSurface
);
1621 //unlikely case we couldn't use m_pSurface directly, copy contents
1622 //back from image surface
1623 cairo_rectangle(copycr
, nExtentsLeft
, nExtentsTop
,
1624 nExtentsRight
- nExtentsLeft
,
1625 nExtentsBottom
- nExtentsTop
);
1626 cairo_set_source_surface(copycr
, target_surface
, 0, 0);
1627 cairo_paint(copycr
);
1628 cairo_destroy(copycr
);
1629 cairo_surface_destroy(target_surface
);
1632 cairo_surface_destroy(surface
);
1635 cairo_destroy(cr
); // unref
1637 DamageHandler
* pDamage
= static_cast<DamageHandler
*>(cairo_surface_get_user_data(m_pSurface
, getDamageKey()));
1641 pDamage
->damaged(pDamage
->handle
, nExtentsLeft
, nExtentsTop
,
1642 nExtentsRight
- nExtentsLeft
,
1643 nExtentsBottom
- nExtentsTop
);
1647 #if ENABLE_CAIRO_CANVAS
1648 bool SvpSalGraphics::SupportsCairo() const
1653 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr
& /*rSurface*/) const
1655 return cairo::SurfaceSharedPtr();
1658 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateSurface(const OutputDevice
& /*rRefDevice*/, int /*x*/, int /*y*/, int /*width*/, int /*height*/) const
1660 return cairo::SurfaceSharedPtr();
1663 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateBitmapSurface(const OutputDevice
& /*rRefDevice*/, const BitmapSystemData
& /*rData*/, const Size
& /*rSize*/) const
1665 return cairo::SurfaceSharedPtr();
1668 css::uno::Any
SvpSalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr
& /*rSurface*/, const basegfx::B2ISize
& /*rSize*/) const
1670 return css::uno::Any();
1673 #endif // ENABLE_CAIRO_CANVAS
1675 SystemGraphicsData
SvpSalGraphics::GetGraphicsData() const
1677 return SystemGraphicsData();
1680 bool SvpSalGraphics::supportsOperation(OutDevSupportType eType
) const
1684 case OutDevSupportType::TransparentRect
:
1685 case OutDevSupportType::B2DDraw
:
1691 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */