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