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 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 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;
163 pDst
->mnScanlineSize
= AlignedWidth4Bytes(nScanlineBase
);
164 if (pDst
->mnScanlineSize
< nScanlineBase
/8)
166 SAL_WARN("vcl.gdi", "scanline calculation wraparound");
167 pDst
->mpBits
= nullptr;
174 pDst
->mpBits
= new sal_uInt8
[ pDst
->mnScanlineSize
* nHeight
];
176 catch (const std::bad_alloc
&)
178 // memory exception, clean up
179 pDst
->mpBits
= nullptr;
184 for (long y
= 0; y
< nHeight
; ++y
)
186 sal_uInt8
* pS
= pSrc
->mpBits
+ y
* pSrc
->mnScanlineSize
;
187 sal_uInt8
* pD
= pDst
->mpBits
+ y
* pDst
->mnScanlineSize
;
188 for (long x
= 0; x
< nWidth
; ++x
)
191 static_assert((SVP_CAIRO_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N32BitTcRgba
, "Expected SVP_CAIRO_FORMAT set to N32BitTcBgra");
192 static_assert((SVP_24BIT_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N24BitTcRgb
, "Expected SVP_24BIT_FORMAT set to N24BitTcRgb");
196 pD
[3] = 0xff; // Alpha
197 #elif defined OSL_BIGENDIAN
198 static_assert((SVP_CAIRO_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N32BitTcArgb
, "Expected SVP_CAIRO_FORMAT set to N32BitTcBgra");
199 static_assert((SVP_24BIT_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N24BitTcRgb
, "Expected SVP_24BIT_FORMAT set to N24BitTcRgb");
200 pD
[0] = 0xff; // Alpha
205 static_assert((SVP_CAIRO_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N32BitTcBgra
, "Expected SVP_CAIRO_FORMAT set to N32BitTcBgra");
206 static_assert((SVP_24BIT_FORMAT
& ~ScanlineFormat::TopDown
) == ScanlineFormat::N24BitTcBgr
, "Expected SVP_24BIT_FORMAT set to N24BitTcBgr");
210 pD
[3] = 0xff; // Alpha
224 explicit SourceHelper(const SalBitmap
& rSourceBitmap
, const bool bForceARGB32
= false)
225 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
226 : m_bForceARGB32(bForceARGB32
)
229 const SvpSalBitmap
& rSrcBmp
= static_cast<const SvpSalBitmap
&>(rSourceBitmap
);
230 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
231 if ((rSrcBmp
.GetBitCount() != 32 && rSrcBmp
.GetBitCount() != 24) || bForceARGB32
)
234 if (rSrcBmp
.GetBitCount() != 32)
237 //big stupid copy here
238 static bool bWarnedOnce
= false;
239 SAL_WARN_IF(!bWarnedOnce
, "vcl.gdi", "non default depth bitmap, slow convert, upscale the input");
242 const BitmapBuffer
* pSrc
= rSrcBmp
.GetBuffer();
243 const SalTwoRect aTwoRect
= { 0, 0, pSrc
->mnWidth
, pSrc
->mnHeight
,
244 0, 0, pSrc
->mnWidth
, pSrc
->mnHeight
};
245 BitmapBuffer
* pTmp
= (pSrc
->mnFormat
== SVP_24BIT_FORMAT
246 ? FastConvert24BitRgbTo32BitCairo(pSrc
)
247 : StretchAndConvert(*pSrc
, aTwoRect
, SVP_CAIRO_FORMAT
));
248 aTmpBmp
.Create(pTmp
);
250 assert(aTmpBmp
.GetBitCount() == 32);
251 source
= SvpSalGraphics::createCairoSurface(aTmpBmp
.GetBuffer());
254 source
= SvpSalGraphics::createCairoSurface(rSrcBmp
.GetBuffer());
258 cairo_surface_destroy(source
);
260 cairo_surface_t
* getSurface()
266 cairo_surface_mark_dirty(source
);
268 unsigned char* getBits(sal_Int32
&rStride
)
270 cairo_surface_flush(source
);
272 unsigned char *mask_data
= cairo_image_surface_get_data(source
);
274 const cairo_format_t nFormat
= cairo_image_surface_get_format(source
);
275 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
277 assert(nFormat
== CAIRO_FORMAT_RGB24_888
&& "Expected RGB24_888 image");
280 assert(nFormat
== CAIRO_FORMAT_ARGB32
&& "need to implement CAIRO_FORMAT_A1 after all here");
282 rStride
= cairo_format_stride_for_width(nFormat
, cairo_image_surface_get_width(source
));
287 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
288 const bool m_bForceARGB32
;
290 SvpSalBitmap aTmpBmp
;
291 cairo_surface_t
* source
;
293 SourceHelper(const SourceHelper
&) = delete;
294 SourceHelper
& operator=(const SourceHelper
&) = delete;
300 explicit MaskHelper(const SalBitmap
& rAlphaBitmap
)
302 const SvpSalBitmap
& rMask
= static_cast<const SvpSalBitmap
&>(rAlphaBitmap
);
303 const BitmapBuffer
* pMaskBuf
= rMask
.GetBuffer();
305 if (rAlphaBitmap
.GetBitCount() == 8)
307 // the alpha values need to be inverted for Cairo
308 // so big stupid copy and invert here
309 const int nImageSize
= pMaskBuf
->mnHeight
* pMaskBuf
->mnScanlineSize
;
310 pAlphaBits
.reset( new unsigned char[nImageSize
] );
311 memcpy(pAlphaBits
.get(), pMaskBuf
->mpBits
, nImageSize
);
313 // TODO: make upper layers use standard alpha
314 sal_uInt32
* pLDst
= reinterpret_cast<sal_uInt32
*>(pAlphaBits
.get());
315 for( int i
= nImageSize
/sizeof(sal_uInt32
); --i
>= 0; ++pLDst
)
317 assert(reinterpret_cast<unsigned char*>(pLDst
) == pAlphaBits
.get()+nImageSize
);
319 mask
= cairo_image_surface_create_for_data(pAlphaBits
.get(),
321 pMaskBuf
->mnWidth
, pMaskBuf
->mnHeight
,
322 pMaskBuf
->mnScanlineSize
);
326 // the alpha values need to be inverted for Cairo
327 // so big stupid copy and invert here
328 const int nImageSize
= pMaskBuf
->mnHeight
* pMaskBuf
->mnScanlineSize
;
329 pAlphaBits
.reset( new unsigned char[nImageSize
] );
330 memcpy(pAlphaBits
.get(), pMaskBuf
->mpBits
, nImageSize
);
332 const sal_Int32 nBlackIndex
= pMaskBuf
->maPalette
.GetBestIndex(BitmapColor(COL_BLACK
));
333 if (nBlackIndex
== 0)
335 // TODO: make upper layers use standard alpha
336 unsigned char* pDst
= pAlphaBits
.get();
337 for (int i
= nImageSize
; --i
>= 0; ++pDst
)
341 mask
= cairo_image_surface_create_for_data(pAlphaBits
.get(),
343 pMaskBuf
->mnWidth
, pMaskBuf
->mnHeight
,
344 pMaskBuf
->mnScanlineSize
);
349 cairo_surface_destroy(mask
);
351 cairo_surface_t
* getMask()
356 cairo_surface_t
*mask
;
357 std::unique_ptr
<unsigned char[]> pAlphaBits
;
359 MaskHelper(const MaskHelper
&) = delete;
360 MaskHelper
& operator=(const MaskHelper
&) = delete;
364 bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect
& rTR
, const SalBitmap
& rSourceBitmap
, const SalBitmap
& rAlphaBitmap
)
366 if (rAlphaBitmap
.GetBitCount() != 8 && rAlphaBitmap
.GetBitCount() != 1)
368 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap alpha depth case: " << rAlphaBitmap
.GetBitCount());
372 SourceHelper
aSurface(rSourceBitmap
);
373 cairo_surface_t
* source
= aSurface
.getSurface();
376 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
380 MaskHelper
aMask(rAlphaBitmap
);
381 cairo_surface_t
*mask
= aMask
.getMask();
384 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawAlphaBitmap case");
388 cairo_t
* cr
= getCairoContext(false);
391 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
393 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
397 cairo_pattern_t
* maskpattern
= cairo_pattern_create_for_surface(mask
);
398 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
399 double fXScale
= static_cast<double>(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
;
400 double fYScale
= static_cast<double>(rTR
.mnDestHeight
)/rTR
.mnSrcHeight
;
401 cairo_scale(cr
, fXScale
, fYScale
);
402 cairo_set_source_surface(cr
, source
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
404 //tdf#114117 when stretching a single pixel width/height source to fit an area
405 //set extend and filter to stretch it with simplest expected interpolation
406 if ((fXScale
!= 1.0 && rTR
.mnSrcWidth
== 1) || (fYScale
!= 1.0 && rTR
.mnSrcHeight
== 1))
408 cairo_pattern_t
* sourcepattern
= cairo_get_source(cr
);
409 cairo_pattern_set_extend(sourcepattern
, CAIRO_EXTEND_REPEAT
);
410 cairo_pattern_set_filter(sourcepattern
, CAIRO_FILTER_NEAREST
);
411 cairo_pattern_set_extend(maskpattern
, CAIRO_EXTEND_REPEAT
);
412 cairo_pattern_set_filter(maskpattern
, CAIRO_FILTER_NEAREST
);
415 //this block is just "cairo_mask_surface", but we have to make it explicit
416 //because of the cairo_pattern_set_filter etc we may want applied
417 cairo_matrix_t matrix
;
418 cairo_matrix_init_translate(&matrix
, rTR
.mnSrcX
, rTR
.mnSrcY
);
419 cairo_pattern_set_matrix(maskpattern
, &matrix
);
420 cairo_mask(cr
, maskpattern
);
422 cairo_pattern_destroy(maskpattern
);
424 releaseCairoContext(cr
, false, extents
);
429 bool SvpSalGraphics::drawTransformedBitmap(
430 const basegfx::B2DPoint
& rNull
,
431 const basegfx::B2DPoint
& rX
,
432 const basegfx::B2DPoint
& rY
,
433 const SalBitmap
& rSourceBitmap
,
434 const SalBitmap
* pAlphaBitmap
)
436 if (pAlphaBitmap
&& pAlphaBitmap
->GetBitCount() != 8 && pAlphaBitmap
->GetBitCount() != 1)
438 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap alpha depth case: " << pAlphaBitmap
->GetBitCount());
442 SourceHelper
aSurface(rSourceBitmap
);
443 cairo_surface_t
* source
= aSurface
.getSurface();
446 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
450 std::unique_ptr
<MaskHelper
> xMask
;
451 cairo_surface_t
*mask
= nullptr;
454 xMask
.reset(new MaskHelper(*pAlphaBitmap
));
455 mask
= xMask
->getMask();
458 SAL_WARN("vcl.gdi", "unsupported SvpSalGraphics::drawTransformedBitmap case");
463 const Size aSize
= rSourceBitmap
.GetSize();
465 cairo_t
* cr
= getCairoContext(false);
468 // setup the image transformation
469 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points
470 const basegfx::B2DVector aXRel
= rX
- rNull
;
471 const basegfx::B2DVector aYRel
= rY
- rNull
;
472 cairo_matrix_t matrix
;
473 cairo_matrix_init(&matrix
,
474 aXRel
.getX()/aSize
.Width(), aXRel
.getY()/aSize
.Width(),
475 aYRel
.getX()/aSize
.Height(), aYRel
.getY()/aSize
.Height(),
476 rNull
.getX(), rNull
.getY());
478 cairo_transform(cr
, &matrix
);
480 cairo_rectangle(cr
, 0, 0, aSize
.Width(), aSize
.Height());
481 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
484 cairo_set_source_surface(cr
, source
, 0, 0);
486 cairo_mask_surface(cr
, mask
, 0, 0);
490 releaseCairoContext(cr
, false, extents
);
495 void SvpSalGraphics::clipRegion(cairo_t
* cr
)
497 RectangleVector aRectangles
;
498 if (!m_aClipRegion
.IsEmpty())
500 m_aClipRegion
.GetRegionRectangles(aRectangles
);
502 if (!aRectangles
.empty())
504 for (auto const& rectangle
: aRectangles
)
506 cairo_rectangle(cr
, rectangle
.Left(), rectangle
.Top(), rectangle
.GetWidth(), rectangle
.GetHeight());
512 bool SvpSalGraphics::drawAlphaRect(long nX
, long nY
, long nWidth
, long nHeight
, sal_uInt8 nTransparency
)
514 cairo_t
* cr
= getCairoContext(false);
517 const double fTransparency
= (100 - nTransparency
) * (1.0/100);
519 basegfx::B2DRange
extents(0, 0, 0, 0);
521 cairo_rectangle(cr
, nX
, nY
, nWidth
, nHeight
);
523 if (m_aFillColor
!= SALCOLOR_NONE
)
525 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aFillColor
)/255.0,
526 SALCOLOR_GREEN(m_aFillColor
)/255.0,
527 SALCOLOR_BLUE(m_aFillColor
)/255.0,
530 if (m_aLineColor
== SALCOLOR_NONE
)
531 extents
= getClippedFillDamage(cr
);
533 cairo_fill_preserve(cr
);
536 if (m_aLineColor
!= SALCOLOR_NONE
)
538 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aLineColor
)/255.0,
539 SALCOLOR_GREEN(m_aLineColor
)/255.0,
540 SALCOLOR_BLUE(m_aLineColor
)/255.0,
543 extents
= getClippedStrokeDamage(cr
);
545 cairo_stroke_preserve(cr
);
548 releaseCairoContext(cr
, false, extents
);
553 SvpSalGraphics::SvpSalGraphics()
554 : m_pSurface(nullptr)
556 , m_aLineColor(MAKE_SALCOLOR(0x00, 0x00, 0x00))
557 , m_aFillColor(MAKE_SALCOLOR(0xFF, 0xFF, 0XFF))
558 , m_ePaintMode(PaintMode::Over
)
559 , m_aTextRenderImpl(*this)
563 SvpSalGraphics::~SvpSalGraphics()
567 void SvpSalGraphics::setSurface(cairo_surface_t
* pSurface
, const basegfx::B2IVector
& rSize
)
569 m_pSurface
= pSurface
;
570 m_aFrameSize
= rSize
;
571 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
572 cairo_surface_get_device_scale(pSurface
, &m_fScale
, nullptr);
577 void SvpSalGraphics::GetResolution( sal_Int32
& rDPIX
, sal_Int32
& rDPIY
)
582 sal_uInt16
SvpSalGraphics::GetBitCount() const
584 if (cairo_surface_get_content(m_pSurface
) != CAIRO_CONTENT_COLOR_ALPHA
)
589 long SvpSalGraphics::GetGraphicsWidth() const
591 return m_pSurface
? m_aFrameSize
.getX() : 0;
594 void SvpSalGraphics::ResetClipRegion()
596 m_aClipRegion
.SetNull();
599 bool SvpSalGraphics::setClipRegion( const vcl::Region
& i_rClip
)
601 m_aClipRegion
= i_rClip
;
605 void SvpSalGraphics::SetLineColor()
607 m_aLineColor
= SALCOLOR_NONE
;
610 void SvpSalGraphics::SetLineColor( SalColor nSalColor
)
612 m_aLineColor
= nSalColor
;
615 void SvpSalGraphics::SetFillColor()
617 m_aFillColor
= SALCOLOR_NONE
;
620 void SvpSalGraphics::SetFillColor( SalColor nSalColor
)
622 m_aFillColor
= nSalColor
;
625 void SvpSalGraphics::SetXORMode(bool bSet
)
627 m_ePaintMode
= bSet
? PaintMode::Xor
: PaintMode::Over
;
630 void SvpSalGraphics::SetROPLineColor( SalROPColor nROPColor
)
634 case SalROPColor::N0
:
635 m_aLineColor
= MAKE_SALCOLOR(0, 0, 0);
637 case SalROPColor::N1
:
638 m_aLineColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
640 case SalROPColor::Invert
:
641 m_aLineColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
646 void SvpSalGraphics::SetROPFillColor( SalROPColor nROPColor
)
650 case SalROPColor::N0
:
651 m_aFillColor
= MAKE_SALCOLOR(0, 0, 0);
653 case SalROPColor::N1
:
654 m_aFillColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
656 case SalROPColor::Invert
:
657 m_aFillColor
= MAKE_SALCOLOR(0xff, 0xff, 0xff);
662 void SvpSalGraphics::drawPixel( long nX
, long nY
)
664 if (m_aLineColor
!= SALCOLOR_NONE
)
666 drawPixel(nX
, nY
, m_aLineColor
);
670 void SvpSalGraphics::drawPixel( long nX
, long nY
, SalColor nSalColor
)
672 SalColor aOrigFillColor
= m_aFillColor
;
673 SalColor aOrigLineColor
= m_aLineColor
;
675 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+1, nY
+1));
676 m_aLineColor
= SALCOLOR_NONE
;
677 m_aFillColor
= nSalColor
;
679 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
681 m_aFillColor
= aOrigFillColor
;
682 m_aLineColor
= aOrigLineColor
;
685 void SvpSalGraphics::drawRect( long nX
, long nY
, long nWidth
, long nHeight
)
687 // because of the -1 hack we have to do fill and draw separately
688 SalColor aOrigFillColor
= m_aFillColor
;
689 SalColor aOrigLineColor
= m_aLineColor
;
690 m_aFillColor
= SALCOLOR_NONE
;
691 m_aLineColor
= SALCOLOR_NONE
;
693 if (aOrigFillColor
!= SALCOLOR_NONE
)
695 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+nWidth
, nY
+nHeight
));
696 m_aFillColor
= aOrigFillColor
;
697 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
698 m_aFillColor
= SALCOLOR_NONE
;
701 if (aOrigLineColor
!= SALCOLOR_NONE
)
703 // need same -1 hack as X11SalGraphicsImpl::drawRect
704 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle( nX
, nY
, nX
+nWidth
-1, nY
+nHeight
-1));
705 m_aLineColor
= aOrigLineColor
;
706 drawPolyPolygon(basegfx::B2DPolyPolygon(aRect
));
707 m_aLineColor
= SALCOLOR_NONE
;
710 m_aFillColor
= aOrigFillColor
;
711 m_aLineColor
= aOrigLineColor
;
714 void SvpSalGraphics::drawPolyLine(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
716 basegfx::B2DPolygon aPoly
;
717 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
718 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
719 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
720 aPoly
.setClosed(false);
722 drawPolyLine(aPoly
, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter
,
723 css::drawing::LineCap_BUTT
, 15.0 * F_PI180
/*default*/);
726 void SvpSalGraphics::drawPolygon(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
728 basegfx::B2DPolygon aPoly
;
729 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
730 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
731 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
733 drawPolyPolygon(basegfx::B2DPolyPolygon(aPoly
));
736 void SvpSalGraphics::drawPolyPolygon(sal_uInt32 nPoly
,
737 const sal_uInt32
* pPointCounts
,
738 PCONSTSALPOINT
* pPtAry
)
740 basegfx::B2DPolyPolygon aPolyPoly
;
741 for(sal_uInt32 nPolygon
= 0; nPolygon
< nPoly
; ++nPolygon
)
743 sal_uInt32 nPoints
= pPointCounts
[nPolygon
];
746 PCONSTSALPOINT pPoints
= pPtAry
[nPolygon
];
747 basegfx::B2DPolygon aPoly
;
748 aPoly
.append( basegfx::B2DPoint(pPoints
->mnX
, pPoints
->mnY
), nPoints
);
749 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
750 aPoly
.setB2DPoint(i
, basegfx::B2DPoint( pPoints
[i
].mnX
, pPoints
[i
].mnY
));
752 aPolyPoly
.append(aPoly
);
756 drawPolyPolygon(aPolyPoly
);
759 static const basegfx::B2DPoint
aHalfPointOfs(0.5, 0.5);
761 static void AddPolygonToPath(cairo_t
* cr
, const basegfx::B2DPolygon
& rPolygon
, bool bClosePath
,
762 bool bPixelSnap
, bool bLineDraw
)
764 // short circuit if there is nothing to do
765 const int nPointCount
= rPolygon
.count();
766 if( nPointCount
<= 0 )
771 const bool bHasCurves
= rPolygon
.areControlPointsUsed();
772 basegfx::B2DPoint aLast
;
774 for( int nPointIdx
= 0, nPrevIdx
= 0;; nPrevIdx
= nPointIdx
++ )
776 int nClosedIdx
= nPointIdx
;
777 if( nPointIdx
>= nPointCount
)
779 // prepare to close last curve segment if needed
780 if( bClosePath
&& (nPointIdx
== nPointCount
) )
790 basegfx::B2DPoint aPoint
= rPolygon
.getB2DPoint( nClosedIdx
);
794 // snap device coordinates to full pixels
795 aPoint
.setX( basegfx::fround( aPoint
.getX() ) );
796 aPoint
.setY( basegfx::fround( aPoint
.getY() ) );
801 aPoint
+= aHalfPointOfs
;
806 // first point => just move there
807 cairo_move_to(cr
, aPoint
.getX(), aPoint
.getY());
812 bool bPendingCurve
= false;
815 bPendingCurve
= rPolygon
.isNextControlPointUsed( nPrevIdx
);
816 bPendingCurve
|= rPolygon
.isPrevControlPointUsed( nClosedIdx
);
819 if( !bPendingCurve
) // line segment
821 cairo_line_to(cr
, aPoint
.getX(), aPoint
.getY());
823 else // cubic bezier segment
825 basegfx::B2DPoint aCP1
= rPolygon
.getNextControlPoint( nPrevIdx
);
826 basegfx::B2DPoint aCP2
= rPolygon
.getPrevControlPoint( nClosedIdx
);
829 aCP1
+= aHalfPointOfs
;
830 aCP2
+= aHalfPointOfs
;
833 // tdf#99165 if the control points are 'empty', create the mathematical
834 // correct replacement ones to avoid problems with the graphical sub-system
835 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
836 // vector was wrong. Best alternative is one as close as possible which means short.
837 if (aCP1
.equal(aLast
))
839 aCP1
= aLast
+ ((aCP2
- aLast
) * 0.0005);
842 if(aCP2
.equal(aPoint
))
844 aCP2
= aPoint
+ ((aCP1
- aPoint
) * 0.0005);
847 cairo_curve_to(cr
, aCP1
.getX(), aCP1
.getY(), aCP2
.getX(), aCP2
.getY(),
848 aPoint
.getX(), aPoint
.getY());
856 cairo_close_path(cr
);
860 void SvpSalGraphics::drawLine( long nX1
, long nY1
, long nX2
, long nY2
)
862 basegfx::B2DPolygon aPoly
;
863 aPoly
.append(basegfx::B2DPoint(nX1
, nY1
), 2);
864 aPoly
.setB2DPoint(1, basegfx::B2DPoint(nX2
, nY2
));
865 aPoly
.setClosed(false);
867 cairo_t
* cr
= getCairoContext(false);
870 AddPolygonToPath(cr
, aPoly
, aPoly
.isClosed(), !getAntiAliasB2DDraw(), true);
872 applyColor(cr
, m_aLineColor
);
874 basegfx::B2DRange extents
= getClippedStrokeDamage(cr
);
878 releaseCairoContext(cr
, false, extents
);
881 bool SvpSalGraphics::drawPolyLine(
882 const basegfx::B2DPolygon
& rPolyLine
,
883 double fTransparency
,
884 const basegfx::B2DVector
& rLineWidths
,
885 basegfx::B2DLineJoin eLineJoin
,
886 css::drawing::LineCap eLineCap
,
887 double fMiterMinimumAngle
)
889 // short circuit if there is nothing to do
890 const int nPointCount
= rPolyLine
.count();
891 if (nPointCount
<= 0)
896 const bool bNoJoin
= (basegfx::B2DLineJoin::NONE
== eLineJoin
&& basegfx::fTools::more(rLineWidths
.getX(), 0.0));
898 cairo_t
* cr
= getCairoContext(false);
901 // setup line attributes
902 cairo_line_join_t eCairoLineJoin
= CAIRO_LINE_JOIN_MITER
;
905 case basegfx::B2DLineJoin::Bevel
:
906 eCairoLineJoin
= CAIRO_LINE_JOIN_BEVEL
;
908 case basegfx::B2DLineJoin::Round
:
909 eCairoLineJoin
= CAIRO_LINE_JOIN_ROUND
;
911 case basegfx::B2DLineJoin::NONE
:
912 case basegfx::B2DLineJoin::Miter
:
913 eCairoLineJoin
= CAIRO_LINE_JOIN_MITER
;
917 // convert miter minimum angle to miter limit
918 double fMiterLimit
= 1.0 / sin( fMiterMinimumAngle
/ 2.0);
920 // setup cap attribute
921 cairo_line_cap_t
eCairoLineCap(CAIRO_LINE_CAP_BUTT
);
925 default: // css::drawing::LineCap_BUTT:
927 eCairoLineCap
= CAIRO_LINE_CAP_BUTT
;
930 case css::drawing::LineCap_ROUND
:
932 eCairoLineCap
= CAIRO_LINE_CAP_ROUND
;
935 case css::drawing::LineCap_SQUARE
:
937 eCairoLineCap
= CAIRO_LINE_CAP_SQUARE
;
942 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aLineColor
)/255.0,
943 SALCOLOR_GREEN(m_aLineColor
)/255.0,
944 SALCOLOR_BLUE(m_aLineColor
)/255.0,
947 cairo_set_line_join(cr
, eCairoLineJoin
);
948 cairo_set_line_cap(cr
, eCairoLineCap
);
949 cairo_set_line_width(cr
, rLineWidths
.getX());
950 cairo_set_miter_limit(cr
, fMiterLimit
);
953 basegfx::B2DRange
extents(0, 0, 0, 0);
957 AddPolygonToPath(cr
, rPolyLine
, rPolyLine
.isClosed(), !getAntiAliasB2DDraw(), true);
958 extents
= getClippedStrokeDamage(cr
);
963 // emulate rendering::PathJoinType::NONE by painting single edges
964 const sal_uInt32
nEdgeCount(rPolyLine
.isClosed() ? nPointCount
: nPointCount
- 1);
965 basegfx::B2DPolygon aEdge
;
966 aEdge
.append(rPolyLine
.getB2DPoint(0));
967 aEdge
.append(basegfx::B2DPoint(0.0, 0.0));
969 for (sal_uInt32 i
= 0; i
< nEdgeCount
; ++i
)
971 const sal_uInt32
nNextIndex((i
+ 1) % nPointCount
);
972 aEdge
.setB2DPoint(1, rPolyLine
.getB2DPoint(nNextIndex
));
973 aEdge
.setNextControlPoint(0, rPolyLine
.getNextControlPoint(i
% nPointCount
));
974 aEdge
.setPrevControlPoint(1, rPolyLine
.getPrevControlPoint(nNextIndex
));
976 AddPolygonToPath(cr
, aEdge
, false, !getAntiAliasB2DDraw(), true);
978 extents
.expand(getStrokeDamage(cr
));
983 aEdge
.setB2DPoint(0, aEdge
.getB2DPoint(1));
986 extents
.intersect(getClipBox(cr
));
989 releaseCairoContext(cr
, false, extents
);
994 bool SvpSalGraphics::drawPolyLineBezier( sal_uInt32
,
998 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolyLineBezier case");
1002 bool SvpSalGraphics::drawPolygonBezier( sal_uInt32
,
1006 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolygonBezier case");
1010 bool SvpSalGraphics::drawPolyPolygonBezier( sal_uInt32
,
1012 const SalPoint
* const*,
1013 const PolyFlags
* const* )
1015 SAL_INFO("vcl.gdi", "unsupported SvpSalGraphics::drawPolyPolygonBezier case");
1019 void SvpSalGraphics::setupPolyPolygon(cairo_t
* cr
, const basegfx::B2DPolyPolygon
& rPolyPoly
)
1023 for (const auto & rPoly
: rPolyPoly
)
1024 AddPolygonToPath(cr
, rPoly
, true, !getAntiAliasB2DDraw(), m_aLineColor
!= SALCOLOR_NONE
);
1027 bool SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
, double fTransparency
)
1029 cairo_t
* cr
= getCairoContext(true);
1031 setupPolyPolygon(cr
, rPolyPoly
);
1033 basegfx::B2DRange
extents(0, 0, 0, 0);
1035 if (m_aFillColor
!= SALCOLOR_NONE
)
1037 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aFillColor
)/255.0,
1038 SALCOLOR_GREEN(m_aFillColor
)/255.0,
1039 SALCOLOR_BLUE(m_aFillColor
)/255.0,
1042 if (m_aLineColor
== SALCOLOR_NONE
)
1043 extents
= getClippedFillDamage(cr
);
1045 cairo_fill_preserve(cr
);
1048 if (m_aLineColor
!= SALCOLOR_NONE
)
1050 cairo_set_source_rgba(cr
, SALCOLOR_RED(m_aLineColor
)/255.0,
1051 SALCOLOR_GREEN(m_aLineColor
)/255.0,
1052 SALCOLOR_BLUE(m_aLineColor
)/255.0,
1055 extents
= getClippedStrokeDamage(cr
);
1057 cairo_stroke_preserve(cr
);
1060 releaseCairoContext(cr
, true, extents
);
1065 void SvpSalGraphics::applyColor(cairo_t
*cr
, SalColor aColor
)
1067 if (cairo_surface_get_content(m_pSurface
) == CAIRO_CONTENT_COLOR_ALPHA
)
1069 cairo_set_source_rgba(cr
, SALCOLOR_RED(aColor
)/255.0,
1070 SALCOLOR_GREEN(aColor
)/255.0,
1071 SALCOLOR_BLUE(aColor
)/255.0,
1076 double fSet
= aColor
== sal_uInt32(COL_BLACK
) ? 1.0 : 0.0;
1077 cairo_set_source_rgba(cr
, 1, 1, 1, fSet
);
1078 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
1082 void SvpSalGraphics::drawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPoly
)
1084 cairo_t
* cr
= getCairoContext(true);
1086 setupPolyPolygon(cr
, rPolyPoly
);
1088 basegfx::B2DRange
extents(0, 0, 0, 0);
1090 if (m_aFillColor
!= SALCOLOR_NONE
)
1092 applyColor(cr
, m_aFillColor
);
1093 if (m_aLineColor
== SALCOLOR_NONE
)
1094 extents
= getClippedFillDamage(cr
);
1095 cairo_fill_preserve(cr
);
1098 if (m_aLineColor
!= SALCOLOR_NONE
)
1100 applyColor(cr
, m_aLineColor
);
1101 extents
= getClippedStrokeDamage(cr
);
1102 cairo_stroke_preserve(cr
);
1105 releaseCairoContext(cr
, true, extents
);
1108 void SvpSalGraphics::copyArea( long nDestX
,
1114 bool /*bWindowInvalidate*/ )
1116 SalTwoRect
aTR(nSrcX
, nSrcY
, nSrcWidth
, nSrcHeight
, nDestX
, nDestY
, nSrcWidth
, nSrcHeight
);
1117 copyBits(aTR
, this);
1120 static basegfx::B2DRange
renderSource(cairo_t
* cr
, const SalTwoRect
& rTR
,
1121 cairo_surface_t
* source
)
1123 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
1125 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
1129 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
1130 double fXScale
= 1.0f
;
1131 double fYScale
= 1.0f
;
1132 if (rTR
.mnSrcWidth
!= 0 && rTR
.mnSrcHeight
!= 0) {
1133 fXScale
= static_cast<double>(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
;
1134 fYScale
= static_cast<double>(rTR
.mnDestHeight
)/rTR
.mnSrcHeight
;
1135 cairo_scale(cr
, fXScale
, fYScale
);
1139 cairo_set_source_surface(cr
, source
, -rTR
.mnSrcX
, -rTR
.mnSrcY
);
1140 if ((fXScale
!= 1.0 && rTR
.mnSrcWidth
== 1) || (fYScale
!= 1.0 && rTR
.mnSrcHeight
== 1))
1142 cairo_pattern_t
* sourcepattern
= cairo_get_source(cr
);
1143 cairo_pattern_set_extend(sourcepattern
, CAIRO_EXTEND_REPEAT
);
1144 cairo_pattern_set_filter(sourcepattern
, CAIRO_FILTER_NEAREST
);
1146 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
1153 void SvpSalGraphics::copySource( const SalTwoRect
& rTR
,
1154 cairo_surface_t
* source
)
1156 cairo_t
* cr
= getCairoContext(false);
1159 basegfx::B2DRange extents
= renderSource(cr
, rTR
, source
);
1161 releaseCairoContext(cr
, false, extents
);
1164 void SvpSalGraphics::copyBits( const SalTwoRect
& rTR
,
1165 SalGraphics
* pSrcGraphics
)
1167 SalTwoRect
aTR(rTR
);
1169 SvpSalGraphics
* pSrc
= pSrcGraphics
?
1170 static_cast<SvpSalGraphics
*>(pSrcGraphics
) : this;
1172 cairo_surface_t
* source
= pSrc
->m_pSurface
;
1174 cairo_surface_t
*pCopy
= nullptr;
1177 //self copy is a problem, so dup source in that case
1178 pCopy
= cairo_surface_create_similar(source
,
1179 cairo_surface_get_content(m_pSurface
),
1180 aTR
.mnSrcWidth
* m_fScale
,
1181 aTR
.mnSrcHeight
* m_fScale
);
1182 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1183 cairo_surface_set_device_scale(pCopy
, m_fScale
, m_fScale
);
1185 cairo_t
* cr
= cairo_create(pCopy
);
1186 cairo_set_source_surface(cr
, source
, -aTR
.mnSrcX
, -aTR
.mnSrcY
);
1187 cairo_rectangle(cr
, 0, 0, aTR
.mnSrcWidth
, aTR
.mnSrcHeight
);
1197 copySource(aTR
, source
);
1200 cairo_surface_destroy(pCopy
);
1205 bool isBlackWhite(const SalBitmap
& rBitmap
)
1207 const SvpSalBitmap
& rSrcBmp
= static_cast<const SvpSalBitmap
&>(rBitmap
);
1208 const BitmapBuffer
* pSourceBuffer
= rSrcBmp
.GetBuffer();
1209 const BitmapPalette
& rPalette
= pSourceBuffer
->maPalette
;
1212 rPalette
.GetEntryCount() < 2 ||
1214 (rPalette
.GetEntryCount() == 2 &&
1215 rPalette
[0] == COL_BLACK
&&
1216 rPalette
[1] == COL_WHITE
) ||
1218 (rPalette
.GetEntryCount() == 2 &&
1219 rPalette
[1] == COL_BLACK
&&
1220 rPalette
[0] == COL_WHITE
)
1225 void SvpSalGraphics::drawBitmap(const SalTwoRect
& rTR
, const SalBitmap
& rSourceBitmap
)
1227 if (rSourceBitmap
.GetBitCount() == 1 && isBlackWhite(rSourceBitmap
))
1229 // This way we draw only monochrome b/w bitmaps
1230 MaskHelper
aMask(rSourceBitmap
);
1231 cairo_surface_t
* source
= aMask
.getMask();
1232 copySource(rTR
, source
);
1236 SourceHelper
aSurface(rSourceBitmap
);
1237 cairo_surface_t
* source
= aSurface
.getSurface();
1238 copySource(rTR
, source
);
1241 void SvpSalGraphics::drawBitmap( const SalTwoRect
& rTR
,
1242 const SalBitmap
& rSourceBitmap
,
1243 const SalBitmap
& rTransparentBitmap
)
1245 drawAlphaBitmap(rTR
, rSourceBitmap
, rTransparentBitmap
);
1248 static sal_uInt8
unpremultiply(sal_uInt8 c
, sal_uInt8 a
)
1250 return (a
> 0) ? (c
* 255 + a
/ 2) / a
: 0;
1253 static sal_uInt8
premultiply(sal_uInt8 c
, sal_uInt8 a
)
1255 return (c
* a
+ 127) / 255;
1258 void SvpSalGraphics::drawMask( const SalTwoRect
& rTR
,
1259 const SalBitmap
& rSalBitmap
,
1260 SalColor nMaskColor
)
1262 /** creates an image from the given rectangle, replacing all black pixels
1263 * with nMaskColor and make all other full transparent */
1264 SourceHelper
aSurface(rSalBitmap
, true); // The mask is argb32
1266 unsigned char *mask_data
= aSurface
.getBits(nStride
);
1267 for (sal_Int32 y
= rTR
.mnSrcY
; y
< rTR
.mnSrcY
+ rTR
.mnSrcHeight
; ++y
)
1269 unsigned char *row
= mask_data
+ (nStride
*y
);
1270 unsigned char *data
= row
+ (rTR
.mnSrcX
* 4);
1271 for (sal_Int32 x
= rTR
.mnSrcX
; x
< rTR
.mnSrcX
+ rTR
.mnSrcWidth
; ++x
)
1273 sal_uInt8 b
= unpremultiply(data
[SVP_CAIRO_BLUE
], data
[SVP_CAIRO_ALPHA
]);
1274 sal_uInt8 g
= unpremultiply(data
[SVP_CAIRO_GREEN
], data
[SVP_CAIRO_ALPHA
]);
1275 sal_uInt8 r
= unpremultiply(data
[SVP_CAIRO_RED
], data
[SVP_CAIRO_ALPHA
]);
1276 if (r
== 0 && g
== 0 && b
== 0)
1278 data
[0] = SALCOLOR_BLUE(nMaskColor
);
1279 data
[1] = SALCOLOR_GREEN(nMaskColor
);
1280 data
[2] = SALCOLOR_RED(nMaskColor
);
1293 aSurface
.mark_dirty();
1295 cairo_t
* cr
= getCairoContext(false);
1298 cairo_rectangle(cr
, rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
1300 basegfx::B2DRange extents
= getClippedFillDamage(cr
);
1304 cairo_translate(cr
, rTR
.mnDestX
, rTR
.mnDestY
);
1305 double fXScale
= static_cast<double>(rTR
.mnDestWidth
)/rTR
.mnSrcWidth
;
1306 double fYScale
= static_cast<double>(rTR
.mnDestHeight
)/rTR
.mnSrcHeight
;
1307 cairo_scale(cr
, fXScale
, fYScale
);
1308 cairo_set_source_surface(cr
, aSurface
.getSurface(), -rTR
.mnSrcX
, -rTR
.mnSrcY
);
1309 if ((fXScale
!= 1.0 && rTR
.mnSrcWidth
== 1) || (fYScale
!= 1.0 && rTR
.mnSrcHeight
== 1))
1311 cairo_pattern_t
* sourcepattern
= cairo_get_source(cr
);
1312 cairo_pattern_set_extend(sourcepattern
, CAIRO_EXTEND_REPEAT
);
1313 cairo_pattern_set_filter(sourcepattern
, CAIRO_FILTER_NEAREST
);
1317 releaseCairoContext(cr
, false, extents
);
1320 SalBitmap
* SvpSalGraphics::getBitmap( long nX
, long nY
, long nWidth
, long nHeight
)
1322 SvpSalBitmap
* pBitmap
= new SvpSalBitmap();
1324 if (GetBitCount() == 1)
1326 aPal
.SetEntryCount(2);
1327 aPal
[0] = COL_BLACK
;
1328 aPal
[1] = COL_WHITE
;
1331 if (!pBitmap
->Create(Size(nWidth
, nHeight
), GetBitCount(), aPal
))
1333 SAL_WARN("vcl.gdi", "SvpSalGraphics::getBitmap, cannot create bitmap");
1338 cairo_surface_t
* target
= SvpSalGraphics::createCairoSurface(pBitmap
->GetBuffer());
1339 cairo_t
* cr
= cairo_create(target
);
1341 SalTwoRect
aTR(nX
, nY
, nWidth
, nHeight
, 0, 0, nWidth
, nHeight
);
1342 renderSource(cr
, aTR
, m_pSurface
);
1345 cairo_surface_destroy(target
);
1347 Toggle1BitTransparency(*pBitmap
->GetBuffer());
1352 SalColor
SvpSalGraphics::getPixel( long nX
, long nY
)
1354 cairo_surface_t
*target
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, 1, 1);
1355 cairo_t
* cr
= cairo_create(target
);
1357 cairo_rectangle(cr
, 0, 0, 1, 1);
1358 cairo_set_source_surface(cr
, m_pSurface
, -nX
, -nY
);
1362 cairo_surface_flush(target
);
1363 unsigned char *data
= cairo_image_surface_get_data(target
);
1364 sal_uInt8 b
= unpremultiply(data
[SVP_CAIRO_BLUE
], data
[SVP_CAIRO_ALPHA
]);
1365 sal_uInt8 g
= unpremultiply(data
[SVP_CAIRO_GREEN
], data
[SVP_CAIRO_ALPHA
]);
1366 sal_uInt8 r
= unpremultiply(data
[SVP_CAIRO_RED
], data
[SVP_CAIRO_ALPHA
]);
1367 SalColor nRet
= MAKE_SALCOLOR(r
, g
, b
);
1369 cairo_surface_destroy(target
);
1376 cairo_pattern_t
* create_stipple()
1378 static unsigned char data
[16] = { 0xFF, 0xFF, 0x00, 0x00,
1379 0xFF, 0xFF, 0x00, 0x00,
1380 0x00, 0x00, 0xFF, 0xFF,
1381 0x00, 0x00, 0xFF, 0xFF };
1382 cairo_surface_t
* surface
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_A8
, 4, 4, 4);
1383 cairo_pattern_t
* pattern
= cairo_pattern_create_for_surface(surface
);
1384 cairo_surface_destroy(surface
);
1385 cairo_pattern_set_extend(pattern
, CAIRO_EXTEND_REPEAT
);
1386 cairo_pattern_set_filter(pattern
, CAIRO_FILTER_NEAREST
);
1391 void SvpSalGraphics::invert(const basegfx::B2DPolygon
&rPoly
, SalInvert nFlags
)
1393 cairo_t
* cr
= getCairoContext(false);
1396 basegfx::B2DRange
extents(0, 0, 0, 0);
1398 AddPolygonToPath(cr
, rPoly
, true, !getAntiAliasB2DDraw(), false);
1400 cairo_set_source_rgb(cr
, 1.0, 1.0, 1.0);
1402 if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 10, 0))
1404 cairo_set_operator(cr
, CAIRO_OPERATOR_DIFFERENCE
);
1408 SAL_WARN("vcl.gdi", "SvpSalGraphics::invert, archaic cairo");
1411 if (nFlags
& SalInvert::TrackFrame
)
1413 cairo_set_line_width(cr
, 2.0);
1414 const double dashLengths
[2] = { 4.0, 4.0 };
1415 cairo_set_dash(cr
, dashLengths
, 2, 0);
1417 extents
= getClippedStrokeDamage(cr
);
1418 //see tdf#106577 under wayland, some pixel droppings seen, maybe we're
1419 //out by one somewhere, or cairo_stroke_extents is confused by
1427 extents
= getClippedFillDamage(cr
);
1431 if (nFlags
& SalInvert::N50
)
1433 cairo_pattern_t
*pattern
= create_stipple();
1434 cairo_surface_t
* surface
= cairo_surface_create_similar(m_pSurface
,
1435 cairo_surface_get_content(m_pSurface
),
1436 extents
.getWidth() * m_fScale
,
1437 extents
.getHeight() * m_fScale
);
1439 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1440 cairo_surface_set_device_scale(surface
, m_fScale
, m_fScale
);
1442 cairo_t
* stipple_cr
= cairo_create(surface
);
1443 cairo_set_source_rgb(stipple_cr
, 1.0, 1.0, 1.0);
1444 cairo_mask(stipple_cr
, pattern
);
1445 cairo_pattern_destroy(pattern
);
1446 cairo_destroy(stipple_cr
);
1447 cairo_mask_surface(cr
, surface
, extents
.getMinX(), extents
.getMinY());
1448 cairo_surface_destroy(surface
);
1456 releaseCairoContext(cr
, false, extents
);
1459 void SvpSalGraphics::invert( long nX
, long nY
, long nWidth
, long nHeight
, SalInvert nFlags
)
1461 basegfx::B2DPolygon aRect
= basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(nX
, nY
, nX
+nWidth
, nY
+nHeight
));
1463 invert(aRect
, nFlags
);
1466 void SvpSalGraphics::invert(sal_uInt32 nPoints
, const SalPoint
* pPtAry
, SalInvert nFlags
)
1468 basegfx::B2DPolygon aPoly
;
1469 aPoly
.append(basegfx::B2DPoint(pPtAry
->mnX
, pPtAry
->mnY
), nPoints
);
1470 for (sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1471 aPoly
.setB2DPoint(i
, basegfx::B2DPoint(pPtAry
[i
].mnX
, pPtAry
[i
].mnY
));
1472 aPoly
.setClosed(true);
1474 invert(aPoly
, nFlags
);
1477 bool SvpSalGraphics::drawEPS( long, long, long, long, void*, sal_uLong
)
1484 bool isCairoCompatible(const BitmapBuffer
* pBuffer
)
1489 // We use Cairo that supports 24-bit RGB.
1490 #ifdef HAVE_CAIRO_FORMAT_RGB24_888
1491 if (pBuffer
->mnBitCount
!= 32 && pBuffer
->mnBitCount
!= 24 && pBuffer
->mnBitCount
!= 1)
1493 if (pBuffer
->mnBitCount
!= 32 && pBuffer
->mnBitCount
!= 1)
1497 cairo_format_t nFormat
= getCairoFormat(*pBuffer
);
1498 return (cairo_format_stride_for_width(nFormat
, pBuffer
->mnWidth
) == pBuffer
->mnScanlineSize
);
1502 cairo_surface_t
* SvpSalGraphics::createCairoSurface(const BitmapBuffer
*pBuffer
)
1504 if (!isCairoCompatible(pBuffer
))
1507 cairo_format_t nFormat
= getCairoFormat(*pBuffer
);
1508 cairo_surface_t
*target
=
1509 cairo_image_surface_create_for_data(pBuffer
->mpBits
,
1511 pBuffer
->mnWidth
, pBuffer
->mnHeight
,
1512 pBuffer
->mnScanlineSize
);
1516 cairo_t
* SvpSalGraphics::createTmpCompatibleCairoContext() const
1518 cairo_surface_t
*target
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
1519 m_aFrameSize
.getX() * m_fScale
,
1520 m_aFrameSize
.getY() * m_fScale
);
1521 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
1522 cairo_surface_set_device_scale(target
, m_fScale
, m_fScale
);
1525 return cairo_create(target
);
1528 cairo_t
* SvpSalGraphics::getCairoContext(bool bXorModeAllowed
) const
1531 if (m_ePaintMode
== PaintMode::Xor
&& bXorModeAllowed
)
1532 cr
= createTmpCompatibleCairoContext();
1534 cr
= cairo_create(m_pSurface
);
1535 cairo_set_line_width(cr
, 1);
1536 cairo_set_fill_rule(cr
, CAIRO_FILL_RULE_EVEN_ODD
);
1537 cairo_set_antialias(cr
, getAntiAliasB2DDraw() ? CAIRO_ANTIALIAS_DEFAULT
: CAIRO_ANTIALIAS_NONE
);
1538 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
1542 cairo_user_data_key_t
* SvpSalGraphics::getDamageKey()
1544 static cairo_user_data_key_t aDamageKey
;
1548 void SvpSalGraphics::releaseCairoContext(cairo_t
* cr
, bool bXorModeAllowed
, const basegfx::B2DRange
& rExtents
) const
1550 const bool bXoring
= (m_ePaintMode
== PaintMode::Xor
&& bXorModeAllowed
);
1552 if (rExtents
.isEmpty())
1554 //nothing changed, return early
1557 cairo_surface_t
* surface
= cairo_get_target(cr
);
1558 cairo_surface_destroy(surface
);
1564 sal_Int32
nExtentsLeft(rExtents
.getMinX()), nExtentsTop(rExtents
.getMinY());
1565 sal_Int32
nExtentsRight(rExtents
.getMaxX()), nExtentsBottom(rExtents
.getMaxY());
1566 sal_Int32 nWidth
= m_aFrameSize
.getX();
1567 sal_Int32 nHeight
= m_aFrameSize
.getY();
1568 nExtentsLeft
= std::max
<sal_Int32
>(nExtentsLeft
, 0);
1569 nExtentsTop
= std::max
<sal_Int32
>(nExtentsTop
, 0);
1570 nExtentsRight
= std::min
<sal_Int32
>(nExtentsRight
, nWidth
);
1571 nExtentsBottom
= std::min
<sal_Int32
>(nExtentsBottom
, nHeight
);
1573 cairo_surface_t
* surface
= cairo_get_target(cr
);
1574 cairo_surface_flush(surface
);
1576 //For the most part we avoid the use of XOR these days, but there
1577 //are some edge cases where legacy stuff still supports it, so
1578 //emulate it (slowly) here.
1581 cairo_surface_t
* target_surface
= m_pSurface
;
1582 if (cairo_surface_get_type(target_surface
) != CAIRO_SURFACE_TYPE_IMAGE
)
1584 //in the unlikely case we can't use m_pSurface directly, copy contents
1585 //to another temp image surface
1586 cairo_t
* copycr
= createTmpCompatibleCairoContext();
1587 cairo_rectangle(copycr
, nExtentsLeft
, nExtentsTop
,
1588 nExtentsRight
- nExtentsLeft
,
1589 nExtentsBottom
- nExtentsTop
);
1590 cairo_set_source_surface(copycr
, m_pSurface
, 0, 0);
1591 cairo_paint(copycr
);
1592 target_surface
= cairo_get_target(copycr
);
1593 cairo_destroy(copycr
);
1596 cairo_surface_flush(target_surface
);
1597 unsigned char *target_surface_data
= cairo_image_surface_get_data(target_surface
);
1598 unsigned char *xor_surface_data
= cairo_image_surface_get_data(surface
);
1600 cairo_format_t nFormat
= cairo_image_surface_get_format(target_surface
);
1601 assert(nFormat
== CAIRO_FORMAT_ARGB32
&& "need to implement CAIRO_FORMAT_A1 after all here");
1602 sal_Int32 nStride
= cairo_format_stride_for_width(nFormat
, nWidth
* m_fScale
);
1603 sal_Int32 nUnscaledExtentsLeft
= nExtentsLeft
* m_fScale
;
1604 sal_Int32 nUnscaledExtentsRight
= nExtentsRight
* m_fScale
;
1605 sal_Int32 nUnscaledExtentsTop
= nExtentsTop
* m_fScale
;
1606 sal_Int32 nUnscaledExtentsBottom
= nExtentsBottom
* m_fScale
;
1607 for (sal_Int32 y
= nUnscaledExtentsTop
; y
< nUnscaledExtentsBottom
; ++y
)
1609 unsigned char *true_row
= target_surface_data
+ (nStride
*y
);
1610 unsigned char *xor_row
= xor_surface_data
+ (nStride
*y
);
1611 unsigned char *true_data
= true_row
+ (nUnscaledExtentsLeft
* 4);
1612 unsigned char *xor_data
= xor_row
+ (nUnscaledExtentsLeft
* 4);
1613 for (sal_Int32 x
= nUnscaledExtentsLeft
; x
< nUnscaledExtentsRight
; ++x
)
1615 sal_uInt8 b
= unpremultiply(true_data
[SVP_CAIRO_BLUE
], true_data
[SVP_CAIRO_ALPHA
]) ^
1616 unpremultiply(xor_data
[SVP_CAIRO_BLUE
], xor_data
[SVP_CAIRO_ALPHA
]);
1617 sal_uInt8 g
= unpremultiply(true_data
[SVP_CAIRO_GREEN
], true_data
[SVP_CAIRO_ALPHA
]) ^
1618 unpremultiply(xor_data
[SVP_CAIRO_GREEN
], xor_data
[SVP_CAIRO_ALPHA
]);
1619 sal_uInt8 r
= unpremultiply(true_data
[SVP_CAIRO_RED
], true_data
[SVP_CAIRO_ALPHA
]) ^
1620 unpremultiply(xor_data
[SVP_CAIRO_RED
], xor_data
[SVP_CAIRO_ALPHA
]);
1621 true_data
[0] = premultiply(b
, true_data
[SVP_CAIRO_ALPHA
]);
1622 true_data
[1] = premultiply(g
, true_data
[SVP_CAIRO_ALPHA
]);
1623 true_data
[2] = premultiply(r
, true_data
[SVP_CAIRO_ALPHA
]);
1628 cairo_surface_mark_dirty(target_surface
);
1630 if (target_surface
!= m_pSurface
)
1632 cairo_t
* copycr
= cairo_create(m_pSurface
);
1633 //unlikely case we couldn't use m_pSurface directly, copy contents
1634 //back from image surface
1635 cairo_rectangle(copycr
, nExtentsLeft
, nExtentsTop
,
1636 nExtentsRight
- nExtentsLeft
,
1637 nExtentsBottom
- nExtentsTop
);
1638 cairo_set_source_surface(copycr
, target_surface
, 0, 0);
1639 cairo_paint(copycr
);
1640 cairo_destroy(copycr
);
1641 cairo_surface_destroy(target_surface
);
1644 cairo_surface_destroy(surface
);
1647 cairo_destroy(cr
); // unref
1649 DamageHandler
* pDamage
= static_cast<DamageHandler
*>(cairo_surface_get_user_data(m_pSurface
, getDamageKey()));
1653 pDamage
->damaged(pDamage
->handle
, nExtentsLeft
, nExtentsTop
,
1654 nExtentsRight
- nExtentsLeft
,
1655 nExtentsBottom
- nExtentsTop
);
1659 #if ENABLE_CAIRO_CANVAS
1660 bool SvpSalGraphics::SupportsCairo() const
1665 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr
& /*rSurface*/) const
1667 return cairo::SurfaceSharedPtr();
1670 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateSurface(const OutputDevice
& /*rRefDevice*/, int /*x*/, int /*y*/, int /*width*/, int /*height*/) const
1672 return cairo::SurfaceSharedPtr();
1675 cairo::SurfaceSharedPtr
SvpSalGraphics::CreateBitmapSurface(const OutputDevice
& /*rRefDevice*/, const BitmapSystemData
& /*rData*/, const Size
& /*rSize*/) const
1677 return cairo::SurfaceSharedPtr();
1680 css::uno::Any
SvpSalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr
& /*rSurface*/, const basegfx::B2ISize
& /*rSize*/) const
1682 return css::uno::Any();
1685 #endif // ENABLE_CAIRO_CANVAS
1687 SystemGraphicsData
SvpSalGraphics::GetGraphicsData() const
1689 return SystemGraphicsData();
1692 bool SvpSalGraphics::supportsOperation(OutDevSupportType eType
) const
1696 case OutDevSupportType::TransparentRect
:
1697 case OutDevSupportType::B2DDraw
:
1703 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */