bump product version to 7.2.5.1
[LibreOffice.git] / vcl / quartz / AquaGraphicsBackend.cxx
blob6bc44b39f89efd2278384b755161e391d99d4980
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cassert>
24 #include <cstring>
25 #include <numeric>
27 #include <basegfx/polygon/b2dpolygon.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <basegfx/polygon/b2dpolypolygontools.hxx>
30 #include <osl/endian.h>
31 #include <osl/file.hxx>
32 #include <sal/types.h>
33 #include <tools/long.hxx>
34 #include <vcl/sysdata.hxx>
36 #include <fontsubset.hxx>
37 #include <quartz/salbmp.h>
38 #ifdef MACOSX
39 #include <quartz/salgdi.h>
40 #endif
41 #include <quartz/utils.h>
42 #ifdef IOS
43 #include "saldatabasic.hxx"
44 #endif
45 #include <sft.hxx>
47 using namespace vcl;
49 namespace
51 const basegfx::B2DPoint aHalfPointOfs(0.5, 0.5);
53 void AddPolygonToPath(CGMutablePathRef xPath, const basegfx::B2DPolygon& rPolygon, bool bClosePath,
54 bool bPixelSnap, bool bLineDraw)
56 // short circuit if there is nothing to do
57 const int nPointCount = rPolygon.count();
58 if (nPointCount <= 0)
60 return;
63 const bool bHasCurves = rPolygon.areControlPointsUsed();
64 for (int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++)
66 int nClosedIdx = nPointIdx;
67 if (nPointIdx >= nPointCount)
69 // prepare to close last curve segment if needed
70 if (bClosePath && (nPointIdx == nPointCount))
72 nClosedIdx = 0;
74 else
76 break;
80 basegfx::B2DPoint aPoint = rPolygon.getB2DPoint(nClosedIdx);
82 if (bPixelSnap)
84 // snap device coordinates to full pixels
85 aPoint.setX(basegfx::fround(aPoint.getX()));
86 aPoint.setY(basegfx::fround(aPoint.getY()));
89 if (bLineDraw)
91 aPoint += aHalfPointOfs;
93 if (!nPointIdx)
95 // first point => just move there
96 CGPathMoveToPoint(xPath, nullptr, aPoint.getX(), aPoint.getY());
97 continue;
100 bool bPendingCurve = false;
101 if (bHasCurves)
103 bPendingCurve = rPolygon.isNextControlPointUsed(nPrevIdx);
104 bPendingCurve |= rPolygon.isPrevControlPointUsed(nClosedIdx);
107 if (!bPendingCurve) // line segment
109 CGPathAddLineToPoint(xPath, nullptr, aPoint.getX(), aPoint.getY());
111 else // cubic bezier segment
113 basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint(nPrevIdx);
114 basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint(nClosedIdx);
115 if (bLineDraw)
117 aCP1 += aHalfPointOfs;
118 aCP2 += aHalfPointOfs;
120 CGPathAddCurveToPoint(xPath, nullptr, aCP1.getX(), aCP1.getY(), aCP2.getX(),
121 aCP2.getY(), aPoint.getX(), aPoint.getY());
125 if (bClosePath)
127 CGPathCloseSubpath(xPath);
131 void alignLinePoint(const Point* i_pIn, float& o_fX, float& o_fY)
133 o_fX = static_cast<float>(i_pIn->getX()) + 0.5;
134 o_fY = static_cast<float>(i_pIn->getY()) + 0.5;
137 void getBoundRect(sal_uInt32 nPoints, const Point* pPtAry, tools::Long& rX, tools::Long& rY,
138 tools::Long& rWidth, tools::Long& rHeight)
140 tools::Long nX1 = pPtAry->getX();
141 tools::Long nX2 = nX1;
142 tools::Long nY1 = pPtAry->getY();
143 tools::Long nY2 = nY1;
145 for (sal_uInt32 n = 1; n < nPoints; n++)
147 if (pPtAry[n].getX() < nX1)
149 nX1 = pPtAry[n].getX();
151 else if (pPtAry[n].getX() > nX2)
153 nX2 = pPtAry[n].getX();
155 if (pPtAry[n].getY() < nY1)
157 nY1 = pPtAry[n].getY();
159 else if (pPtAry[n].getY() > nY2)
161 nY2 = pPtAry[n].getY();
164 rX = nX1;
165 rY = nY1;
166 rWidth = nX2 - nX1 + 1;
167 rHeight = nY2 - nY1 + 1;
170 Color ImplGetROPColor(SalROPColor nROPColor)
172 Color nColor;
173 if (nROPColor == SalROPColor::N0)
175 nColor = Color(0, 0, 0);
177 else
179 nColor = Color(255, 255, 255);
181 return nColor;
184 void drawPattern50(void*, CGContextRef rContext)
186 static const CGRect aRects[2] = { { { 0, 0 }, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } };
187 CGContextAddRects(rContext, aRects, 2);
188 CGContextFillPath(rContext);
192 AquaGraphicsBackend::AquaGraphicsBackend(AquaSharedAttributes& rShared)
193 : mrShared(rShared)
197 AquaGraphicsBackend::~AquaGraphicsBackend() {}
199 void AquaGraphicsBackend::Init() {}
200 void AquaGraphicsBackend::freeResources() {}
202 bool AquaGraphicsBackend::setClipRegion(vcl::Region const& rRegion)
204 // release old clip path
205 mrShared.unsetClipPath();
206 mrShared.mxClipPath = CGPathCreateMutable();
208 // set current path, either as polypolgon or sequence of rectangles
209 RectangleVector aRectangles;
210 rRegion.GetRegionRectangles(aRectangles);
212 for (const auto& rRect : aRectangles)
214 const tools::Long nW(rRect.Right() - rRect.Left() + 1); // uses +1 logic in original
216 if (nW)
218 const tools::Long nH(rRect.Bottom() - rRect.Top() + 1); // uses +1 logic in original
220 if (nH)
222 const CGRect aRect = CGRectMake(rRect.Left(), rRect.Top(), nW, nH);
223 CGPathAddRect(mrShared.mxClipPath, nullptr, aRect);
227 // set the current path as clip region
228 if (mrShared.checkContext())
229 mrShared.setState();
231 return true;
234 void AquaGraphicsBackend::ResetClipRegion()
236 // release old path and indicate no clipping
237 mrShared.unsetClipPath();
239 if (mrShared.checkContext())
241 mrShared.setState();
245 sal_uInt16 AquaGraphicsBackend::GetBitCount() const
247 sal_uInt16 nBits = mrShared.mnBitmapDepth ? mrShared.mnBitmapDepth : 32; //24;
248 return nBits;
251 tools::Long AquaGraphicsBackend::GetGraphicsWidth() const
253 tools::Long width = 0;
254 if (mrShared.maContextHolder.isSet()
255 && (
256 #ifndef IOS
257 mrShared.mbWindow ||
258 #endif
259 mrShared.mbVirDev))
261 width = mrShared.mnWidth;
264 #ifndef IOS
265 if (width == 0)
267 if (mrShared.mbWindow && mrShared.mpFrame)
269 width = mrShared.mpFrame->maGeometry.nWidth;
272 #endif
273 return width;
276 void AquaGraphicsBackend::SetLineColor()
278 mrShared.maLineColor.SetAlpha(0.0); // transparent
279 if (mrShared.checkContext())
281 CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), mrShared.maLineColor.GetRed(),
282 mrShared.maLineColor.GetGreen(), mrShared.maLineColor.GetBlue(),
283 mrShared.maLineColor.GetAlpha());
287 void AquaGraphicsBackend::SetLineColor(Color nColor)
289 mrShared.maLineColor = RGBAColor(nColor);
290 if (mrShared.checkContext())
292 CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), mrShared.maLineColor.GetRed(),
293 mrShared.maLineColor.GetGreen(), mrShared.maLineColor.GetBlue(),
294 mrShared.maLineColor.GetAlpha());
298 void AquaGraphicsBackend::SetFillColor()
300 mrShared.maFillColor.SetAlpha(0.0); // transparent
301 if (mrShared.checkContext())
303 CGContextSetRGBFillColor(mrShared.maContextHolder.get(), mrShared.maFillColor.GetRed(),
304 mrShared.maFillColor.GetGreen(), mrShared.maFillColor.GetBlue(),
305 mrShared.maFillColor.GetAlpha());
309 void AquaGraphicsBackend::SetFillColor(Color nColor)
311 mrShared.maFillColor = RGBAColor(nColor);
312 if (mrShared.checkContext())
314 CGContextSetRGBFillColor(mrShared.maContextHolder.get(), mrShared.maFillColor.GetRed(),
315 mrShared.maFillColor.GetGreen(), mrShared.maFillColor.GetBlue(),
316 mrShared.maFillColor.GetAlpha());
320 void AquaGraphicsBackend::SetXORMode(bool bSet, bool bInvertOnly)
322 // return early if XOR mode remains unchanged
323 if (mrShared.mbPrinter)
325 return;
327 if (!bSet && mrShared.mnXorMode == 2)
329 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeNormal);
330 mrShared.mnXorMode = 0;
331 return;
333 else if (bSet && bInvertOnly && mrShared.mnXorMode == 0)
335 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
336 mrShared.mnXorMode = 2;
337 return;
340 if (!mrShared.mpXorEmulation && !bSet)
342 return;
344 if (mrShared.mpXorEmulation && bSet == mrShared.mpXorEmulation->IsEnabled())
346 return;
348 if (!mrShared.checkContext())
350 return;
352 // prepare XOR emulation
353 if (!mrShared.mpXorEmulation)
355 mrShared.mpXorEmulation = std::make_unique<XorEmulation>();
356 mrShared.mpXorEmulation->SetTarget(mrShared.mnWidth, mrShared.mnHeight,
357 mrShared.mnBitmapDepth, mrShared.maContextHolder.get(),
358 mrShared.maLayer.get());
361 // change the XOR mode
362 if (bSet)
364 mrShared.mpXorEmulation->Enable();
365 mrShared.maContextHolder.set(mrShared.mpXorEmulation->GetMaskContext());
366 mrShared.mnXorMode = 1;
368 else
370 mrShared.mpXorEmulation->UpdateTarget();
371 mrShared.mpXorEmulation->Disable();
372 mrShared.maContextHolder.set(mrShared.mpXorEmulation->GetTargetContext());
373 mrShared.mnXorMode = 0;
377 void AquaGraphicsBackend::SetROPFillColor(SalROPColor nROPColor)
379 if (!mrShared.mbPrinter)
381 SetFillColor(ImplGetROPColor(nROPColor));
385 void AquaGraphicsBackend::SetROPLineColor(SalROPColor nROPColor)
387 if (!mrShared.mbPrinter)
389 SetLineColor(ImplGetROPColor(nROPColor));
393 void AquaGraphicsBackend::drawPixelImpl(tools::Long nX, tools::Long nY, const RGBAColor& rColor)
395 if (!mrShared.checkContext())
396 return;
398 // overwrite the fill color
399 CGContextSetFillColor(mrShared.maContextHolder.get(), rColor.AsArray());
400 // draw 1x1 rect, there is no pixel drawing in Quartz
401 const CGRect aDstRect = CGRectMake(nX, nY, 1, 1);
402 CGContextFillRect(mrShared.maContextHolder.get(), aDstRect);
404 refreshRect(aDstRect);
406 // reset the fill color
407 CGContextSetFillColor(mrShared.maContextHolder.get(), mrShared.maFillColor.AsArray());
410 void AquaGraphicsBackend::drawPixel(tools::Long nX, tools::Long nY)
412 // draw pixel with current line color
413 drawPixelImpl(nX, nY, mrShared.maLineColor);
416 void AquaGraphicsBackend::drawPixel(tools::Long nX, tools::Long nY, Color nColor)
418 const RGBAColor aPixelColor(nColor);
419 drawPixelImpl(nX, nY, aPixelColor);
422 void AquaGraphicsBackend::drawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2,
423 tools::Long nY2)
425 if (nX1 == nX2 && nY1 == nY2)
427 // #i109453# platform independent code expects at least one pixel to be drawn
428 drawPixel(nX1, nY1);
429 return;
432 if (!mrShared.checkContext())
433 return;
435 CGContextBeginPath(mrShared.maContextHolder.get());
436 CGContextMoveToPoint(mrShared.maContextHolder.get(), float(nX1) + 0.5, float(nY1) + 0.5);
437 CGContextAddLineToPoint(mrShared.maContextHolder.get(), float(nX2) + 0.5, float(nY2) + 0.5);
438 CGContextDrawPath(mrShared.maContextHolder.get(), kCGPathStroke);
440 tools::Rectangle aRefreshRect(nX1, nY1, nX2, nY2);
441 (void)aRefreshRect;
442 // Is a call to RefreshRect( aRefreshRect ) missing here?
445 void AquaGraphicsBackend::drawRect(tools::Long nX, tools::Long nY, tools::Long nWidth,
446 tools::Long nHeight)
448 if (!mrShared.checkContext())
449 return;
451 CGRect aRect = CGRectMake(nX, nY, nWidth, nHeight);
452 if (mrShared.isPenVisible())
454 aRect.origin.x += 0.5;
455 aRect.origin.y += 0.5;
456 aRect.size.width -= 1;
457 aRect.size.height -= 1;
460 if (mrShared.isBrushVisible())
462 CGContextFillRect(mrShared.maContextHolder.get(), aRect);
464 if (mrShared.isPenVisible())
466 CGContextStrokeRect(mrShared.maContextHolder.get(), aRect);
468 mrShared.refreshRect(nX, nY, nWidth, nHeight);
471 void AquaGraphicsBackend::drawPolyLine(sal_uInt32 nPoints, const Point* pPointArray)
473 if (nPoints < 1)
474 return;
476 if (!mrShared.checkContext())
477 return;
479 tools::Long nX = 0, nY = 0, nWidth = 0, nHeight = 0;
480 getBoundRect(nPoints, pPointArray, nX, nY, nWidth, nHeight);
482 float fX, fY;
483 CGContextBeginPath(mrShared.maContextHolder.get());
484 alignLinePoint(pPointArray, fX, fY);
485 CGContextMoveToPoint(mrShared.maContextHolder.get(), fX, fY);
486 pPointArray++;
488 for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPointArray++)
490 alignLinePoint(pPointArray, fX, fY);
491 CGContextAddLineToPoint(mrShared.maContextHolder.get(), fX, fY);
493 CGContextStrokePath(mrShared.maContextHolder.get());
495 mrShared.refreshRect(nX, nY, nWidth, nHeight);
498 void AquaGraphicsBackend::drawPolygon(sal_uInt32 nPoints, const Point* pPointArray)
500 if (nPoints <= 1)
501 return;
503 if (!mrShared.checkContext())
504 return;
506 tools::Long nX = 0, nY = 0, nWidth = 0, nHeight = 0;
507 getBoundRect(nPoints, pPointArray, nX, nY, nWidth, nHeight);
509 CGPathDrawingMode eMode;
510 if (mrShared.isBrushVisible() && mrShared.isPenVisible())
512 eMode = kCGPathEOFillStroke;
514 else if (mrShared.isPenVisible())
516 eMode = kCGPathStroke;
518 else if (mrShared.isBrushVisible())
520 eMode = kCGPathEOFill;
522 else
524 SAL_WARN("vcl.quartz", "Neither pen nor brush visible");
525 return;
528 CGContextBeginPath(mrShared.maContextHolder.get());
530 if (mrShared.isPenVisible())
532 float fX, fY;
533 alignLinePoint(pPointArray, fX, fY);
534 CGContextMoveToPoint(mrShared.maContextHolder.get(), fX, fY);
535 pPointArray++;
536 for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPointArray++)
538 alignLinePoint(pPointArray, fX, fY);
539 CGContextAddLineToPoint(mrShared.maContextHolder.get(), fX, fY);
542 else
544 CGContextMoveToPoint(mrShared.maContextHolder.get(), pPointArray->getX(),
545 pPointArray->getY());
546 pPointArray++;
547 for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPointArray++)
549 CGContextAddLineToPoint(mrShared.maContextHolder.get(), pPointArray->getX(),
550 pPointArray->getY());
554 CGContextClosePath(mrShared.maContextHolder.get());
555 CGContextDrawPath(mrShared.maContextHolder.get(), eMode);
557 mrShared.refreshRect(nX, nY, nWidth, nHeight);
560 void AquaGraphicsBackend::drawPolyPolygon(sal_uInt32 nPolyCount, const sal_uInt32* pPoints,
561 const Point** ppPtAry)
563 if (nPolyCount <= 0)
564 return;
566 if (!mrShared.checkContext())
567 return;
569 // find bound rect
570 tools::Long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0;
572 getBoundRect(pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight);
574 for (sal_uInt32 n = 1; n < nPolyCount; n++)
576 tools::Long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight;
577 getBoundRect(pPoints[n], ppPtAry[n], nX, nY, nW, nH);
578 if (nX < leftX)
580 maxWidth += leftX - nX;
581 leftX = nX;
583 if (nY < topY)
585 maxHeight += topY - nY;
586 topY = nY;
588 if (nX + nW > leftX + maxWidth)
590 maxWidth = nX + nW - leftX;
592 if (nY + nH > topY + maxHeight)
594 maxHeight = nY + nH - topY;
598 // prepare drawing mode
599 CGPathDrawingMode eMode;
600 if (mrShared.isBrushVisible() && mrShared.isPenVisible())
602 eMode = kCGPathEOFillStroke;
604 else if (mrShared.isPenVisible())
606 eMode = kCGPathStroke;
608 else if (mrShared.isBrushVisible())
610 eMode = kCGPathEOFill;
612 else
614 SAL_WARN("vcl.quartz", "Neither pen nor brush visible");
615 return;
618 // convert to CGPath
619 CGContextBeginPath(mrShared.maContextHolder.get());
620 if (mrShared.isPenVisible())
622 for (sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++)
624 const sal_uInt32 nPoints = pPoints[nPoly];
625 if (nPoints > 1)
627 const Point* pPtAry = ppPtAry[nPoly];
628 float fX, fY;
630 alignLinePoint(pPtAry, fX, fY);
631 CGContextMoveToPoint(mrShared.maContextHolder.get(), fX, fY);
632 pPtAry++;
634 for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++)
636 alignLinePoint(pPtAry, fX, fY);
637 CGContextAddLineToPoint(mrShared.maContextHolder.get(), fX, fY);
639 CGContextClosePath(mrShared.maContextHolder.get());
643 else
645 for (sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++)
647 const sal_uInt32 nPoints = pPoints[nPoly];
648 if (nPoints > 1)
650 const Point* pPtAry = ppPtAry[nPoly];
651 CGContextMoveToPoint(mrShared.maContextHolder.get(), pPtAry->getX(),
652 pPtAry->getY());
653 pPtAry++;
654 for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++)
656 CGContextAddLineToPoint(mrShared.maContextHolder.get(), pPtAry->getX(),
657 pPtAry->getY());
659 CGContextClosePath(mrShared.maContextHolder.get());
664 CGContextDrawPath(mrShared.maContextHolder.get(), eMode);
666 mrShared.refreshRect(leftX, topY, maxWidth, maxHeight);
669 bool AquaGraphicsBackend::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
670 const basegfx::B2DPolyPolygon& rPolyPolygon,
671 double fTransparency)
673 #ifdef IOS
674 if (!mrShared.maContextHolder.isSet())
675 return true;
676 #endif
678 // short circuit if there is nothing to do
679 if (rPolyPolygon.count() == 0)
680 return true;
682 // ignore invisible polygons
683 if ((fTransparency >= 1.0) || (fTransparency < 0))
684 return true;
686 // Fallback: Transform to DeviceCoordinates
687 basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
688 aPolyPolygon.transform(rObjectToDevice);
690 // setup poly-polygon path
691 CGMutablePathRef xPath = CGPathCreateMutable();
692 // tdf#120252 Use the correct, already transformed PolyPolygon (as long as
693 // the transformation is not used here...)
694 for (auto const& rPolygon : aPolyPolygon)
696 AddPolygonToPath(xPath, rPolygon, true, !getAntiAlias(), mrShared.isPenVisible());
699 const CGRect aRefreshRect = CGPathGetBoundingBox(xPath);
700 // #i97317# workaround for Quartz having problems with drawing small polygons
701 if (aRefreshRect.size.width > 0.125 || aRefreshRect.size.height > 0.125)
703 // prepare drawing mode
704 CGPathDrawingMode eMode;
705 if (mrShared.isBrushVisible() && mrShared.isPenVisible())
707 eMode = kCGPathEOFillStroke;
709 else if (mrShared.isPenVisible())
711 eMode = kCGPathStroke;
713 else if (mrShared.isBrushVisible())
715 eMode = kCGPathEOFill;
717 else
719 SAL_WARN("vcl.quartz", "Neither pen nor brush visible");
720 CGPathRelease(xPath);
721 return true;
724 // use the path to prepare the graphics context
725 mrShared.maContextHolder.saveState();
726 CGContextBeginPath(mrShared.maContextHolder.get());
727 CGContextAddPath(mrShared.maContextHolder.get(), xPath);
729 // draw path with antialiased polygon
730 CGContextSetShouldAntialias(mrShared.maContextHolder.get(), getAntiAlias());
731 CGContextSetAlpha(mrShared.maContextHolder.get(), 1.0 - fTransparency);
732 CGContextDrawPath(mrShared.maContextHolder.get(), eMode);
733 mrShared.maContextHolder.restoreState();
735 // mark modified rectangle as updated
736 refreshRect(aRefreshRect);
739 CGPathRelease(xPath);
741 return true;
744 bool AquaGraphicsBackend::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
745 const basegfx::B2DPolygon& rPolyLine, double fTransparency,
746 double fLineWidth,
747 const std::vector<double>* pStroke, // MM01
748 basegfx::B2DLineJoin eLineJoin,
749 css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
750 bool bPixelSnapHairline)
752 // MM01 check done for simple reasons
753 if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0)
755 return true;
758 #ifdef IOS
759 if (!mrShared.checkContext())
760 return false;
761 #endif
763 // tdf#124848 get correct LineWidth in discrete coordinates,
764 if (fLineWidth == 0) // hairline
765 fLineWidth = 1.0;
766 else // Adjust line width for object-to-device scale.
767 fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();
769 // #i101491# Aqua does not support B2DLineJoin::NONE; return false to use
770 // the fallback (own geometry preparation)
771 // #i104886# linejoin-mode and thus the above only applies to "fat" lines
772 if ((basegfx::B2DLineJoin::NONE == eLineJoin) && (fLineWidth > 1.3))
773 return false;
775 // MM01 need to do line dashing as fallback stuff here now
776 const double fDotDashLength(
777 nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
778 const bool bStrokeUsed(0.0 != fDotDashLength);
779 assert(!bStrokeUsed || (bStrokeUsed && pStroke));
780 basegfx::B2DPolyPolygon aPolyPolygonLine;
782 if (bStrokeUsed)
784 // apply LineStyle
785 basegfx::utils::applyLineDashing(rPolyLine, // source
786 *pStroke, // pattern
787 &aPolyPolygonLine, // target for lines
788 nullptr, // target for gaps
789 fDotDashLength); // full length if available
791 else
793 // no line dashing, just copy
794 aPolyPolygonLine.append(rPolyLine);
797 // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
798 aPolyPolygonLine.transform(rObjectToDevice);
799 if (bPixelSnapHairline)
801 aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine);
804 // setup line attributes
805 CGLineJoin aCGLineJoin = kCGLineJoinMiter;
806 switch (eLineJoin)
808 case basegfx::B2DLineJoin::NONE:
809 aCGLineJoin = /*TODO?*/ kCGLineJoinMiter;
810 break;
811 case basegfx::B2DLineJoin::Bevel:
812 aCGLineJoin = kCGLineJoinBevel;
813 break;
814 case basegfx::B2DLineJoin::Miter:
815 aCGLineJoin = kCGLineJoinMiter;
816 break;
817 case basegfx::B2DLineJoin::Round:
818 aCGLineJoin = kCGLineJoinRound;
819 break;
821 // convert miter minimum angle to miter limit
822 CGFloat fCGMiterLimit = 1.0 / sin(fMiterMinimumAngle / 2.0);
823 // setup cap attribute
824 CGLineCap aCGLineCap(kCGLineCapButt);
826 switch (eLineCap)
828 default: // css::drawing::LineCap_BUTT:
830 aCGLineCap = kCGLineCapButt;
831 break;
833 case css::drawing::LineCap_ROUND:
835 aCGLineCap = kCGLineCapRound;
836 break;
838 case css::drawing::LineCap_SQUARE:
840 aCGLineCap = kCGLineCapSquare;
841 break;
845 // setup poly-polygon path
846 CGMutablePathRef xPath = CGPathCreateMutable();
848 // MM01 todo - I assume that this is OKAY to be done in one run for quartz
849 // but this NEEDS to be checked/verified
850 for (sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
852 const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
853 AddPolygonToPath(xPath, aPolyLine, aPolyLine.isClosed(), !getAntiAlias(), true);
856 const CGRect aRefreshRect = CGPathGetBoundingBox(xPath);
857 // #i97317# workaround for Quartz having problems with drawing small polygons
858 if ((aRefreshRect.size.width > 0.125) || (aRefreshRect.size.height > 0.125))
860 // use the path to prepare the graphics context
861 mrShared.maContextHolder.saveState();
862 CGContextBeginPath(mrShared.maContextHolder.get());
863 CGContextAddPath(mrShared.maContextHolder.get(), xPath);
864 // draw path with antialiased line
865 CGContextSetShouldAntialias(mrShared.maContextHolder.get(), getAntiAlias());
866 CGContextSetAlpha(mrShared.maContextHolder.get(), 1.0 - fTransparency);
867 CGContextSetLineJoin(mrShared.maContextHolder.get(), aCGLineJoin);
868 CGContextSetLineCap(mrShared.maContextHolder.get(), aCGLineCap);
869 CGContextSetLineWidth(mrShared.maContextHolder.get(), fLineWidth);
870 CGContextSetMiterLimit(mrShared.maContextHolder.get(), fCGMiterLimit);
871 CGContextDrawPath(mrShared.maContextHolder.get(), kCGPathStroke);
872 mrShared.maContextHolder.restoreState();
874 // mark modified rectangle as updated
875 refreshRect(aRefreshRect);
878 CGPathRelease(xPath);
880 return true;
883 bool AquaGraphicsBackend::drawPolyLineBezier(sal_uInt32 /*nPoints*/, const Point* /*pPointArray*/,
884 const PolyFlags* /*pFlagArray*/)
886 return false;
889 bool AquaGraphicsBackend::drawPolygonBezier(sal_uInt32 /*nPoints*/, const Point* /*pPointArray*/,
890 const PolyFlags* /*pFlagArray*/)
892 return false;
895 bool AquaGraphicsBackend::drawPolyPolygonBezier(sal_uInt32 /*nPoly*/, const sal_uInt32* /*pPoints*/,
896 const Point* const* /*pPointArray*/,
897 const PolyFlags* const* /*pFlagArray*/)
899 return false;
902 void AquaGraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
904 if (!mrShared.checkContext())
905 return;
907 const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
908 CGImageRef xImage = rBitmap.CreateCroppedImage(
909 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
910 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight));
911 if (!xImage)
912 return;
914 const CGRect aDstRect
915 = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
916 CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xImage);
918 CGImageRelease(xImage);
919 refreshRect(aDstRect);
922 void AquaGraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
923 const SalBitmap& rTransparentBitmap)
925 if (!mrShared.checkContext())
926 return;
928 const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
929 const QuartzSalBitmap& rMask = static_cast<const QuartzSalBitmap&>(rTransparentBitmap);
931 CGImageRef xMaskedImage(rBitmap.CreateWithMask(rMask, rPosAry.mnSrcX, rPosAry.mnSrcY,
932 rPosAry.mnSrcWidth, rPosAry.mnSrcHeight));
933 if (!xMaskedImage)
934 return;
936 const CGRect aDstRect
937 = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
938 CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xMaskedImage);
939 CGImageRelease(xMaskedImage);
940 refreshRect(aDstRect);
943 void AquaGraphicsBackend::drawMask(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,
944 Color nMaskColor)
946 if (!mrShared.checkContext())
947 return;
949 const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
950 CGImageRef xImage = rBitmap.CreateColorMask(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth,
951 rPosAry.mnSrcHeight, nMaskColor);
952 if (!xImage)
953 return;
955 const CGRect aDstRect
956 = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
957 CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xImage);
958 CGImageRelease(xImage);
959 refreshRect(aDstRect);
962 std::shared_ptr<SalBitmap> AquaGraphicsBackend::getBitmap(tools::Long nX, tools::Long nY,
963 tools::Long nDX, tools::Long nDY)
965 SAL_WARN_IF(!mrShared.maLayer.isSet(), "vcl.quartz",
966 "AquaSalGraphics::getBitmap() with no layer this=" << this);
968 mrShared.applyXorContext();
970 std::shared_ptr<QuartzSalBitmap> pBitmap = std::make_shared<QuartzSalBitmap>();
971 if (!pBitmap->Create(mrShared.maLayer, mrShared.mnBitmapDepth, nX, nY, nDX, nDY,
972 mrShared.isFlipped()))
974 pBitmap = nullptr;
976 return pBitmap;
979 Color AquaGraphicsBackend::getPixel(tools::Long nX, tools::Long nY)
981 // return default value on printers or when out of bounds
982 if (!mrShared.maLayer.isSet() || (nX < 0) || (nX >= mrShared.mnWidth) || (nY < 0)
983 || (nY >= mrShared.mnHeight))
985 return COL_BLACK;
988 // prepare creation of matching a CGBitmapContext
989 #if defined OSL_BIGENDIAN
990 struct
992 unsigned char b, g, r, a;
993 } aPixel;
994 #else
995 struct
997 unsigned char a, r, g, b;
998 } aPixel;
999 #endif
1001 // create a one-pixel bitmap context
1002 // TODO: is it worth to cache it?
1003 CGContextRef xOnePixelContext = CGBitmapContextCreate(
1004 &aPixel, 1, 1, 8, 32, GetSalData()->mxRGBSpace,
1005 uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Big));
1007 // update this graphics layer
1008 mrShared.applyXorContext();
1010 // copy the requested pixel into the bitmap context
1011 if (mrShared.isFlipped())
1013 nY = mrShared.mnHeight - nY;
1015 const CGPoint aCGPoint = CGPointMake(-nX, -nY);
1016 CGContextDrawLayerAtPoint(xOnePixelContext, aCGPoint, mrShared.maLayer.get());
1018 CGContextRelease(xOnePixelContext);
1020 Color nColor(aPixel.r, aPixel.g, aPixel.b);
1021 return nColor;
1024 void AquaSalGraphics::GetResolution(sal_Int32& rDPIX, sal_Int32& rDPIY)
1026 #ifndef IOS
1027 if (!mnRealDPIY)
1029 initResolution((maShared.mbWindow && maShared.mpFrame) ? maShared.mpFrame->getNSWindow()
1030 : nil);
1033 rDPIX = mnRealDPIX;
1034 rDPIY = mnRealDPIY;
1035 #else
1036 // This *must* be 96 or else the iOS app will behave very badly (tiles are scaled wrongly and
1037 // don't match each others at their boundaries, and other issues). But *why* it must be 96 I
1038 // have no idea. The commit that changed it to 96 from (the arbitrary) 200 did not say. If you
1039 // know where else 96 is explicitly or implicitly hard-coded, please modify this comment.
1041 // Follow-up: It might be this: in 'online', loleaflet/src/map/Map.js:
1042 // 15 = 1440 twips-per-inch / 96 dpi.
1043 // Chosen to match previous hardcoded value of 3840 for
1044 // the current tile pixel size of 256.
1045 rDPIX = rDPIY = 96;
1046 #endif
1049 void AquaGraphicsBackend::pattern50Fill()
1051 static const CGFloat aFillCol[4] = { 1, 1, 1, 1 };
1052 static const CGPatternCallbacks aCallback = { 0, &drawPattern50, nullptr };
1053 static const CGColorSpaceRef mxP50Space = CGColorSpaceCreatePattern(GetSalData()->mxRGBSpace);
1054 static const CGPatternRef mxP50Pattern
1055 = CGPatternCreate(nullptr, CGRectMake(0, 0, 4, 4), CGAffineTransformIdentity, 4, 4,
1056 kCGPatternTilingConstantSpacing, false, &aCallback);
1057 SAL_WARN_IF(!mrShared.maContextHolder.get(), "vcl.quartz", "maContextHolder.get() is NULL");
1058 CGContextSetFillColorSpace(mrShared.maContextHolder.get(), mxP50Space);
1059 CGContextSetFillPattern(mrShared.maContextHolder.get(), mxP50Pattern, aFillCol);
1060 CGContextFillPath(mrShared.maContextHolder.get());
1063 void AquaGraphicsBackend::invert(tools::Long nX, tools::Long nY, tools::Long nWidth,
1064 tools::Long nHeight, SalInvert nFlags)
1066 if (mrShared.checkContext())
1068 CGRect aCGRect = CGRectMake(nX, nY, nWidth, nHeight);
1069 mrShared.maContextHolder.saveState();
1070 if (nFlags & SalInvert::TrackFrame)
1072 const CGFloat dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line
1073 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
1074 CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0);
1075 CGContextSetLineDash(mrShared.maContextHolder.get(), 0, dashLengths, 2);
1076 CGContextSetLineWidth(mrShared.maContextHolder.get(), 2.0);
1077 CGContextStrokeRect(mrShared.maContextHolder.get(), aCGRect);
1079 else if (nFlags & SalInvert::N50)
1081 //CGContextSetAllowsAntialiasing( maContextHolder.get(), false );
1082 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
1083 CGContextAddRect(mrShared.maContextHolder.get(), aCGRect);
1084 pattern50Fill();
1086 else // just invert
1088 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
1089 CGContextSetRGBFillColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0);
1090 CGContextFillRect(mrShared.maContextHolder.get(), aCGRect);
1092 mrShared.maContextHolder.restoreState();
1093 refreshRect(aCGRect);
1097 namespace
1099 CGPoint* makeCGptArray(sal_uInt32 nPoints, const Point* pPtAry)
1101 CGPoint* CGpoints = new CGPoint[nPoints];
1102 for (sal_uLong i = 0; i < nPoints; i++)
1104 CGpoints[i].x = pPtAry[i].getX();
1105 CGpoints[i].y = pPtAry[i].getY();
1107 return CGpoints;
1110 } // end anonymous ns
1112 void AquaGraphicsBackend::invert(sal_uInt32 nPoints, const Point* pPtAry, SalInvert nSalFlags)
1114 if (mrShared.checkContext())
1116 mrShared.maContextHolder.saveState();
1117 CGPoint* CGpoints = makeCGptArray(nPoints, pPtAry);
1118 CGContextAddLines(mrShared.maContextHolder.get(), CGpoints, nPoints);
1119 if (nSalFlags & SalInvert::TrackFrame)
1121 const CGFloat dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line
1122 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
1123 CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0);
1124 CGContextSetLineDash(mrShared.maContextHolder.get(), 0, dashLengths, 2);
1125 CGContextSetLineWidth(mrShared.maContextHolder.get(), 2.0);
1126 CGContextStrokePath(mrShared.maContextHolder.get());
1128 else if (nSalFlags & SalInvert::N50)
1130 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
1131 pattern50Fill();
1133 else // just invert
1135 CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference);
1136 CGContextSetRGBFillColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0);
1137 CGContextFillPath(mrShared.maContextHolder.get());
1139 const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrShared.maContextHolder.get());
1140 mrShared.maContextHolder.restoreState();
1141 delete[] CGpoints;
1142 refreshRect(aRefreshRect);
1146 #ifndef IOS
1147 bool AquaGraphicsBackend::drawEPS(tools::Long nX, tools::Long nY, tools::Long nWidth,
1148 tools::Long nHeight, void* pEpsData, sal_uInt32 nByteCount)
1150 // convert the raw data to an NSImageRef
1151 NSData* xNSData = [NSData dataWithBytes:pEpsData length:static_cast<int>(nByteCount)];
1152 NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData:xNSData];
1153 if (!xEpsImage)
1155 return false;
1157 // get the target context
1158 if (!mrShared.checkContext())
1160 return false;
1162 // NOTE: flip drawing, else the nsimage would be drawn upside down
1163 mrShared.maContextHolder.saveState();
1164 // CGContextTranslateCTM( maContextHolder.get(), 0, +mnHeight );
1165 CGContextScaleCTM(mrShared.maContextHolder.get(), +1, -1);
1166 nY = /*mnHeight*/ -(nY + nHeight);
1168 // prepare the target context
1169 NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext];
1170 [pOrigNSCtx retain];
1172 // create new context
1173 NSGraphicsContext* pDrawNSCtx =
1174 [NSGraphicsContext graphicsContextWithCGContext:mrShared.maContextHolder.get()
1175 flipped:mrShared.isFlipped()];
1176 // set it, setCurrentContext also releases the previously set one
1177 [NSGraphicsContext setCurrentContext:pDrawNSCtx];
1179 // draw the EPS
1180 const NSRect aDstRect = NSMakeRect(nX, nY, nWidth, nHeight);
1181 const bool bOK = [xEpsImage drawInRect:aDstRect];
1183 // restore the NSGraphicsContext
1184 [NSGraphicsContext setCurrentContext:pOrigNSCtx];
1185 [pOrigNSCtx release]; // restore the original retain count
1187 mrShared.maContextHolder.restoreState();
1188 // mark the destination rectangle as updated
1189 refreshRect(aDstRect);
1191 return bOK;
1193 #else
1194 bool AquaGraphicsBackend::drawEPS(tools::Long /*nX*/, tools::Long /*nY*/, tools::Long /*nWidth*/,
1195 tools::Long /*nHeight*/, void* /*pEpsData*/,
1196 sal_uInt32 /*nByteCount*/)
1198 return false;
1200 #endif
1202 bool AquaGraphicsBackend::blendBitmap(const SalTwoRect& /*rPosAry*/, const SalBitmap& /*rBitmap*/)
1204 return false;
1207 bool AquaGraphicsBackend::blendAlphaBitmap(const SalTwoRect& /*rPosAry*/,
1208 const SalBitmap& /*rSrcBitmap*/,
1209 const SalBitmap& /*rMaskBitmap*/,
1210 const SalBitmap& /*rAlphaBitmap*/)
1212 return false;
1215 bool AquaGraphicsBackend::drawAlphaBitmap(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap,
1216 const SalBitmap& rAlphaBmp)
1218 // An image mask can't have a depth > 8 bits (should be 1 to 8 bits)
1219 if (rAlphaBmp.GetBitCount() > 8)
1220 return false;
1222 // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx)
1223 // horizontal/vertical mirroring not implemented yet
1224 if (rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0)
1225 return false;
1227 const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap);
1228 const QuartzSalBitmap& rMaskSalBmp = static_cast<const QuartzSalBitmap&>(rAlphaBmp);
1229 CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask(rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY,
1230 rTR.mnSrcWidth, rTR.mnSrcHeight);
1231 if (!xMaskedImage)
1232 return false;
1234 if (mrShared.checkContext())
1236 const CGRect aDstRect
1237 = CGRectMake(rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight);
1238 CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xMaskedImage);
1239 refreshRect(aDstRect);
1242 CGImageRelease(xMaskedImage);
1244 return true;
1247 bool AquaGraphicsBackend::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
1248 const basegfx::B2DPoint& rX,
1249 const basegfx::B2DPoint& rY,
1250 const SalBitmap& rSrcBitmap,
1251 const SalBitmap* pAlphaBmp, double fAlpha)
1253 if (!mrShared.checkContext())
1254 return true;
1256 if (fAlpha != 1.0)
1257 return false;
1259 // get the Quartz image
1260 CGImageRef xImage = nullptr;
1261 const Size aSize = rSrcBitmap.GetSize();
1262 const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap);
1263 const QuartzSalBitmap* pMaskSalBmp = static_cast<const QuartzSalBitmap*>(pAlphaBmp);
1265 if (!pMaskSalBmp)
1266 xImage = rSrcSalBmp.CreateCroppedImage(0, 0, int(aSize.Width()), int(aSize.Height()));
1267 else
1268 xImage = rSrcSalBmp.CreateWithMask(*pMaskSalBmp, 0, 0, int(aSize.Width()),
1269 int(aSize.Height()));
1271 if (!xImage)
1272 return false;
1274 // setup the image transformation
1275 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points
1276 mrShared.maContextHolder.saveState();
1277 const basegfx::B2DVector aXRel = rX - rNull;
1278 const basegfx::B2DVector aYRel = rY - rNull;
1279 const CGAffineTransform aCGMat = CGAffineTransformMake(
1280 aXRel.getX() / aSize.Width(), aXRel.getY() / aSize.Width(), aYRel.getX() / aSize.Height(),
1281 aYRel.getY() / aSize.Height(), rNull.getX(), rNull.getY());
1283 CGContextConcatCTM(mrShared.maContextHolder.get(), aCGMat);
1285 // draw the transformed image
1286 const CGRect aSrcRect = CGRectMake(0, 0, aSize.Width(), aSize.Height());
1287 CGContextDrawImage(mrShared.maContextHolder.get(), aSrcRect, xImage);
1289 CGImageRelease(xImage);
1291 // restore the Quartz graphics state
1292 mrShared.maContextHolder.restoreState();
1294 // mark the destination as painted
1295 const CGRect aDstRect = CGRectApplyAffineTransform(aSrcRect, aCGMat);
1296 refreshRect(aDstRect);
1298 return true;
1301 bool AquaGraphicsBackend::hasFastDrawTransformedBitmap() const { return false; }
1303 bool AquaGraphicsBackend::drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth,
1304 tools::Long nHeight, sal_uInt8 nTransparency)
1306 if (!mrShared.checkContext())
1307 return true;
1309 // save the current state
1310 mrShared.maContextHolder.saveState();
1311 CGContextSetAlpha(mrShared.maContextHolder.get(), (100 - nTransparency) * (1.0 / 100));
1313 CGRect aRect = CGRectMake(nX, nY, nWidth - 1, nHeight - 1);
1314 if (mrShared.isPenVisible())
1316 aRect.origin.x += 0.5;
1317 aRect.origin.y += 0.5;
1320 CGContextBeginPath(mrShared.maContextHolder.get());
1321 CGContextAddRect(mrShared.maContextHolder.get(), aRect);
1322 CGContextDrawPath(mrShared.maContextHolder.get(), kCGPathFill);
1324 mrShared.maContextHolder.restoreState();
1325 refreshRect(aRect);
1327 return true;
1330 bool AquaGraphicsBackend::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
1331 const Gradient& /*rGradient*/)
1333 return false;
1336 bool AquaGraphicsBackend::implDrawGradient(basegfx::B2DPolyPolygon const& /*rPolyPolygon*/,
1337 SalGradient const& /*rGradient*/)
1339 return false;
1342 bool AquaGraphicsBackend::supportsOperation(OutDevSupportType eType) const
1344 switch (eType)
1346 case OutDevSupportType::TransparentRect:
1347 case OutDevSupportType::B2DDraw:
1348 return true;
1349 default:
1350 break;
1352 return false;
1355 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */