diff options
-rw-r--r-- | vcl/Library_vclplug_osx.mk | 1 | ||||
-rw-r--r-- | vcl/inc/quartz/salgdi.h | 445 | ||||
-rw-r--r-- | vcl/ios/dummies.cxx | 5 | ||||
-rw-r--r-- | vcl/ios/salios.cxx | 142 | ||||
-rw-r--r-- | vcl/osx/salgdiutils.cxx | 101 | ||||
-rw-r--r-- | vcl/osx/salmacos.cxx | 130 | ||||
-rw-r--r-- | vcl/osx/salnativewidgets.cxx | 88 | ||||
-rw-r--r-- | vcl/quartz/AquaGraphicsBackend.cxx | 1355 | ||||
-rw-r--r-- | vcl/quartz/salgdi.cxx | 106 | ||||
-rw-r--r-- | vcl/quartz/salgdicommon.cxx | 1298 |
10 files changed, 1906 insertions, 1765 deletions
diff --git a/vcl/Library_vclplug_osx.mk b/vcl/Library_vclplug_osx.mk index c80d3bc8d7f3..4b2b4a61b3f4 100644 --- a/vcl/Library_vclplug_osx.mk +++ b/vcl/Library_vclplug_osx.mk @@ -137,6 +137,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_osx,\ vcl/quartz/salgdicommon \ vcl/quartz/salvd \ vcl/quartz/utils \ + vcl/quartz/AquaGraphicsBackend \ )) $(eval $(call gb_Library_use_system_darwin_frameworks,vclplug_osx,\ diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index 80e6c66d1a00..e7559c5b9e7d 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -130,177 +130,326 @@ private: std::unordered_map<sal_IntPtr, rtl::Reference<CoreTextFontFace>> maFontContainer; }; -class AquaSalGraphics : public SalGraphics +namespace sal::aqua { +float getWindowScaling(); +} + +struct AquaSharedAttributes +{ + /// path representing current clip region + CGMutablePathRef mxClipPath; + + /// Drawing colors + /// pen color RGBA + RGBAColor maLineColor; + + /// brush color RGBA + RGBAColor maFillColor; + + // Graphics types +#ifdef MACOSX + AquaSalFrame* mpFrame; + /// is this a window graphics + bool mbWindow; +#else // IOS + // mirror AquaSalVirtualDevice::mbForeignContext for SvpSalGraphics objects related to such + bool mbForeignContext; +#endif + /// is this a printer graphics + bool mbPrinter; + /// is this a virtual device graphics + bool mbVirDev; + CGLayerHolder maLayer; // Quartz graphics layer CGContextHolder maContextHolder; // Quartz drawing context CGContextHolder maBGContextHolder; // Quartz drawing context for CGLayer CGContextHolder maCSContextHolder; // Quartz drawing context considering the color space + int mnWidth; + int mnHeight; + int mnXorMode; // 0: off 1: on 2: invert only + int mnBitmapDepth; // zero unless bitmap std::unique_ptr<XorEmulation> mpXorEmulation; - int mnXorMode; // 0: off 1: on 2: invert only - int mnWidth; - int mnHeight; - int mnBitmapDepth; // zero unless bitmap - /// device resolution of this graphics - sal_Int32 mnRealDPIX; - sal_Int32 mnRealDPIY; - /// path representing current clip region - CGMutablePathRef mxClipPath; + AquaSharedAttributes() + : mxClipPath(nullptr) + , maLineColor(COL_WHITE) + , maFillColor(COL_BLACK) +#ifdef MACOSX + , mpFrame(nullptr) + , mbWindow(false) +#else + , mbForeignContext(false) +#endif + , mbPrinter(false) + , mbVirDev(false) + , mnWidth(0) + , mnHeight(0) + , mnXorMode(0) + , mnBitmapDepth(0) + {} + + void unsetClipPath() + { + if (mxClipPath) + { + CGPathRelease(mxClipPath); + mxClipPath = nullptr; + } + } + + void unsetState() + { + unsetClipPath(); + } + + bool checkContext(); + void setState(); + + bool isPenVisible() const + { + return maLineColor.IsVisible(); + } + bool isBrushVisible() const + { + return maFillColor.IsVisible(); + } + + void refreshRect(float lX, float lY, float lWidth, float lHeight) + { +#ifdef MACOSX + if (!mbWindow) // view only on Window graphics + return; + + if (mpFrame) + { + // update a little more around the designated rectangle + // this helps with antialiased rendering + // Rounding down x and width can accumulate a rounding error of up to 2 + // The decrementing of x, the rounding error and the antialiasing border + // require that the width and the height need to be increased by four + const tools::Rectangle aVclRect( + Point(tools::Long(lX - 1), tools::Long(lY - 1)), + Size(tools::Long(lWidth + 4), tools::Long(lHeight + 4))); + + mpFrame->maInvalidRect.Union(aVclRect); + } +#else + (void) lX; + (void) lY; + (void) lWidth; + (void) lHeight; + return; +#endif + } + + // apply the XOR mask to the target context if active and dirty + void applyXorContext() + { + if (!mpXorEmulation) + return; + if (mpXorEmulation->UpdateTarget()) + { + refreshRect(0, 0, mnWidth, mnHeight); // TODO: refresh minimal changerect + } + } - /// Drawing colors - /// pen color RGBA - RGBAColor maLineColor; - /// brush color RGBA - RGBAColor maFillColor; + // differences between VCL, Quartz and kHiThemeOrientation coordinate systems + // make some graphics seem to be vertically-mirrored from a VCL perspective + bool isFlipped() const + { + #ifdef MACOSX + return mbWindow; + #else + return false; + #endif + } +}; - // Device Font settings - rtl::Reference<CoreTextStyle> mpTextStyle[MAX_FALLBACK]; - RGBAColor maTextColor; - /// allows text to be rendered without antialiasing - bool mbNonAntialiasedText; +class AquaGraphicsBackend final : public SalGraphicsImpl +{ +private: + AquaSharedAttributes& mrShared; + + void drawPixelImpl( tools::Long nX, tools::Long nY, const RGBAColor& rColor); // helper to draw single pixels #ifdef MACOSX - AquaSalFrame* mpFrame; + void refreshRect(const NSRect& rRect) + { + mrShared.refreshRect(rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height); + } +#else + void refreshRect(const CGRect& /*rRect*/) + {} #endif - // Graphics types + void pattern50Fill(); - /// is this a printer graphics - bool mbPrinter; - /// is this a virtual device graphics - bool mbVirDev; #ifdef MACOSX - /// is this a window graphics - bool mbWindow; +protected: + void copyScaledArea(tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX, tools::Long nSrcY, + tools::Long nSrcWidth, tools::Long nSrcHeight, AquaSharedAttributes* pSrcShared); +#endif -#else // IOS +public: + AquaGraphicsBackend(AquaSharedAttributes & rShared); + ~AquaGraphicsBackend() override; - // mirror AquaSalVirtualDevice::mbForeignContext for SvpSalGraphics objects related to such - bool mbForeignContext; + void Init() override; -#endif + void freeResources() override; + + OUString getRenderBackendName() const override + { + return "aqua"; + } + + bool setClipRegion(vcl::Region const& rRegion) override; + void ResetClipRegion() override; + + sal_uInt16 GetBitCount() const override; + + tools::Long GetGraphicsWidth() const override; + + void SetLineColor() override; + void SetLineColor(Color nColor) override; + void SetFillColor() override; + void SetFillColor(Color nColor) override; + void SetXORMode(bool bSet, bool bInvertOnly) override; + void SetROPLineColor(SalROPColor nROPColor) override; + void SetROPFillColor(SalROPColor nROPColor) override; + + void drawPixel(tools::Long nX, tools::Long nY) override; + void drawPixel(tools::Long nX, tools::Long nY, Color nColor) override; + + void drawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2) override; + void drawRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight) override; + void drawPolyLine(sal_uInt32 nPoints, const Point* pPointArray) override; + void drawPolygon(sal_uInt32 nPoints, const Point* pPointArray) override; + void drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPoints, + const Point** pPointArray) override; + + bool drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice, + const basegfx::B2DPolyPolygon&, double fTransparency) override; + + bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice, const basegfx::B2DPolygon&, + double fTransparency, double fLineWidth, const std::vector<double>* pStroke, + basegfx::B2DLineJoin, css::drawing::LineCap, double fMiterMinimumAngle, + bool bPixelSnapHairline) override; + + bool drawPolyLineBezier(sal_uInt32 nPoints, const Point* pPointArray, + const PolyFlags* pFlagArray) override; + + bool drawPolygonBezier(sal_uInt32 nPoints, const Point* pPointArray, + const PolyFlags* pFlagArray) override; + + bool drawPolyPolygonBezier(sal_uInt32 nPoly, const sal_uInt32* pPoints, + const Point* const* pPointArray, + const PolyFlags* const* pFlagArray) override; + + void copyArea(tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX, tools::Long nSrcY, + tools::Long nSrcWidth, tools::Long nSrcHeight, bool bWindowInvalidate) override; + + void copyBits(const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics) override; + + void drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap) override; + + void drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, + const SalBitmap& rMaskBitmap) override; + + void drawMask(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, + Color nMaskColor) override; + + std::shared_ptr<SalBitmap> getBitmap(tools::Long nX, tools::Long nY, tools::Long nWidth, + tools::Long nHeight) override; + + Color getPixel(tools::Long nX, tools::Long nY) override; + + void invert(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, + SalInvert nFlags) override; + + void invert(sal_uInt32 nPoints, const Point* pPtAry, SalInvert nFlags) override; + + bool drawEPS(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, + void* pPtr, sal_uInt32 nSize) override; + + bool blendBitmap(const SalTwoRect&, const SalBitmap& rBitmap) override; + + bool blendAlphaBitmap(const SalTwoRect&, const SalBitmap& rSrcBitmap, + const SalBitmap& rMaskBitmap, const SalBitmap& rAlphaBitmap) override; + + bool drawAlphaBitmap(const SalTwoRect&, const SalBitmap& rSourceBitmap, + const SalBitmap& rAlphaBitmap) override; + + bool drawTransformedBitmap(const basegfx::B2DPoint& rNull, const basegfx::B2DPoint& rX, + const basegfx::B2DPoint& rY, const SalBitmap& rSourceBitmap, + const SalBitmap* pAlphaBitmap, double fAlpha) override; + + bool hasFastDrawTransformedBitmap() const override; + + bool drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, + sal_uInt8 nTransparency) override; + + bool drawGradient(const tools::PolyPolygon& rPolygon, const Gradient& rGradient) override; + bool implDrawGradient(basegfx::B2DPolyPolygon const& rPolyPolygon, + SalGradient const& rGradient) override; + + bool supportsOperation(OutDevSupportType eType) const override; +}; + +class AquaSalGraphics : public SalGraphicsAutoDelegateToImpl +{ + AquaSharedAttributes maShared; + std::unique_ptr<AquaGraphicsBackend> mpBackend; + + /// device resolution of this graphics + sal_Int32 mnRealDPIX; + sal_Int32 mnRealDPIY; + + // Device Font settings + rtl::Reference<CoreTextStyle> mpTextStyle[MAX_FALLBACK]; + RGBAColor maTextColor; + /// allows text to be rendered without antialiasing + bool mbNonAntialiasedText; public: AquaSalGraphics(); virtual ~AquaSalGraphics() override; - bool IsPenVisible() const { return maLineColor.IsVisible(); } - bool IsBrushVisible() const { return maFillColor.IsVisible(); } - void SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRef, int nBitDepth = 0); #ifdef MACOSX void initResolution( NSWindow* ); void copyResolution( AquaSalGraphics& ); void updateResolution(); - static float GetWindowScaling(); void SetWindowGraphics( AquaSalFrame* pFrame ); - bool IsWindowGraphics() const { return mbWindow; } + bool IsWindowGraphics() const { return maShared.mbWindow; } void SetPrinterGraphics(CGContextRef, sal_Int32 nRealDPIX, sal_Int32 nRealDPIY); - AquaSalFrame* getGraphicsFrame() const { return mpFrame; } - void setGraphicsFrame( AquaSalFrame* pFrame ) { mpFrame = pFrame; } + AquaSalFrame* getGraphicsFrame() const { return maShared.mpFrame; } + void setGraphicsFrame( AquaSalFrame* pFrame ) { maShared.mpFrame = pFrame; } #endif - void ImplDrawPixel( tools::Long nX, tools::Long nY, const RGBAColor& ); // helper to draw single pixels - - bool CheckContext(); - #ifdef MACOSX void UpdateWindow( NSRect& ); // delivered in NSView coordinates - void RefreshRect( const NSRect& ); + void RefreshRect(const NSRect& rRect) + { + maShared.refreshRect(rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height); + } #else void RefreshRect( const CGRect& ) {} #endif - void RefreshRect(float lX, float lY, float lWidth, float lHeight); - void SetState(); void UnsetState(); // InvalidateContext does an UnsetState and sets mrContext to 0 void InvalidateContext(); - virtual SalGraphicsImpl* GetImpl() const override; + AquaGraphicsBackend* getAquaGraphicsBackend() const + { + return mpBackend.get(); + } - virtual bool setClipRegion( const vcl::Region& ) override; - - // draw --> LineColor and FillColor and RasterOp and ClipRegion - virtual void drawPixel( tools::Long nX, tools::Long nY ) override; - virtual void drawPixel( tools::Long nX, tools::Long nY, Color nColor ) override; - virtual void drawLine( - tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 ) override; - virtual void drawRect( - tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override; - virtual void drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry ) override; - virtual void drawPolygon( sal_uInt32 nPoints, const Point* pPtAry ) override; - virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, const Point** pPtAry ) override; - virtual bool drawPolyPolygon( - const basegfx::B2DHomMatrix& rObjectToDevice, - const basegfx::B2DPolyPolygon&, - double fTransparency) override; - virtual bool drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry ) override; - virtual bool drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry ) override; - virtual bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const Point* const* pPtAry, const PolyFlags* const* pFlgAry ) override; - virtual bool drawPolyLine( - const basegfx::B2DHomMatrix& rObjectToDevice, - const basegfx::B2DPolygon&, - double fTransparency, - double rLineWidth, - const std::vector< double >* pStroke, // MM01 - basegfx::B2DLineJoin, - css::drawing::LineCap eLineCap, - double fMiterMinimumAngle, - bool bPixelSnapHairline) override; - virtual bool drawGradient( const tools::PolyPolygon&, const Gradient& ) override { return false; }; - - // CopyArea --> No RasterOp, but ClipRegion - virtual void copyArea( tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX, tools::Long nSrcY, tools::Long nSrcWidth, - tools::Long nSrcHeight, bool bWindowInvalidate ) override; - - // CopyBits and DrawBitmap --> RasterOp and ClipRegion - // CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics - virtual void copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics ) override; - virtual void drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap ) override; - virtual void drawBitmap( const SalTwoRect& rPosAry, - const SalBitmap& rSalBitmap, - const SalBitmap& rTransparentBitmap ) override; - virtual void drawMask( const SalTwoRect& rPosAry, - const SalBitmap& rSalBitmap, - Color nMaskColor ) override; - - virtual std::shared_ptr<SalBitmap> getBitmap( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override; - virtual Color getPixel( tools::Long nX, tools::Long nY ) override; - - // invert --> ClipRegion (only Windows or VirDevs) - virtual void invert( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, SalInvert nFlags) override; - virtual void invert( sal_uInt32 nPoints, const Point* pPtAry, SalInvert nFlags ) override; - - virtual bool drawEPS( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, void* pPtr, sal_uInt32 nSize ) override; - - virtual bool blendBitmap( const SalTwoRect&, - const SalBitmap& rBitmap ) override; - - virtual bool blendAlphaBitmap( const SalTwoRect&, - const SalBitmap& rSrcBitmap, - const SalBitmap& rMaskBitmap, - const SalBitmap& rAlphaBitmap ) override; - - virtual bool drawAlphaBitmap( const SalTwoRect&, - const SalBitmap& rSourceBitmap, - const SalBitmap& rAlphaBitmap ) override; - - bool drawTransformedBitmap( - const basegfx::B2DPoint& rNull, - const basegfx::B2DPoint& rX, - const basegfx::B2DPoint& rY, - const SalBitmap& rSourceBitmap, - const SalBitmap* pAlphaBitmap, - double fAlpha) override; - - virtual bool hasFastDrawTransformedBitmap() const override; - - virtual bool drawAlphaRect( tools::Long nX, tools::Long nY, tools::Long nWidth, - tools::Long nHeight, sal_uInt8 nTransparency ) override; + virtual SalGraphicsImpl* GetImpl() const override; #ifdef MACOSX @@ -318,37 +467,11 @@ protected: virtual bool getNativeControlRegion( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, ControlState nState, const ImplControlValue& aValue, const OUString& aCaption, tools::Rectangle &rNativeBoundingRegion, tools::Rectangle &rNativeContentRegion ) override; - - void copyScaledArea( tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX, tools::Long nSrcY, - tools::Long nSrcWidth, tools::Long nSrcHeight, SalGraphics* pSrcGraphics ); #endif public: // get device resolution virtual void GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY ) override; - // get the depth of the device - virtual sal_uInt16 GetBitCount() const override; - // get the width of the device - virtual tools::Long GetGraphicsWidth() const override; - - // set the clip region to empty - virtual void ResetClipRegion() override; - - // set the line color to transparent (= don't draw lines) - virtual void SetLineColor() override; - // set the line color to a specific color - virtual void SetLineColor( Color nColor ) override; - // set the fill color to transparent (= don't fill) - virtual void SetFillColor() override; - // set the fill color to a specific color, shapes will be - // filled accordingly - virtual void SetFillColor( Color nColor ) override; - // enable/disable XOR drawing - virtual void SetXORMode( bool bSet, bool bInvertOnly ) override; - // set line color for raster operations - virtual void SetROPLineColor( SalROPColor nROPColor ) override; - // set fill color for raster operations - virtual void SetROPFillColor( SalROPColor nROPColor ) override; // set the text color to a specific color virtual void SetTextColor( Color nColor ) override; // set the font @@ -401,19 +524,11 @@ public: virtual std::unique_ptr<GenericSalLayout> GetTextLayout(int nFallbackLevel) override; virtual void DrawTextLayout( const GenericSalLayout& ) override; - virtual bool supportsOperation( OutDevSupportType ) const override; - virtual OUString getRenderBackendName() const override { return "aqua"; } virtual SystemGraphicsData GetGraphicsData() const override; private: - // differences between VCL, Quartz and kHiThemeOrientation coordinate systems - // make some graphics seem to be vertically-mirrored from a VCL perspective - bool IsFlipped() const; - - void ApplyXorContext(); - void Pattern50Fill(); UInt32 getState( ControlState nState ); UInt32 getTrackState( ControlState nState ); static bool GetRawFontData( const PhysicalFontFace* pFontData, @@ -421,16 +536,6 @@ private: bool* pJustCFF ); }; -// --- some trivial inlines - -#ifdef MACOSX - -inline void AquaSalGraphics::RefreshRect( const NSRect& rRect ) -{ - RefreshRect( rRect.origin.x, rRect.origin.y, rRect.size.width, rRect.size.height ); -} - -#endif #endif // INCLUDED_VCL_INC_QUARTZ_SALGDI_H diff --git a/vcl/ios/dummies.cxx b/vcl/ios/dummies.cxx index d62609dc95bf..135a534243fd 100644 --- a/vcl/ios/dummies.cxx +++ b/vcl/ios/dummies.cxx @@ -110,11 +110,6 @@ void SalGenericInstance::jobEndedPrinterUpdate() { } -bool AquaSalGraphics::drawEPS( long, long, long, long, void*, sal_uInt32 ) -{ - return false; -} - using namespace psp; GenericUnixSalData::GenericUnixSalData(GenericUnixSalDataType const t, SalInstance *const pInstance) diff --git a/vcl/ios/salios.cxx b/vcl/ios/salios.cxx index fc925b61cdf3..ac11faff3072 100644 --- a/vcl/ios/salios.cxx +++ b/vcl/ios/salios.cxx @@ -88,13 +88,8 @@ bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits // From salgdicommon.cxx -void AquaSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSrcGraphics ) +void AquaGraphicsBackend::copyBits(const SalTwoRect& rPosAry, SalGraphics *pSrcGraphics) { - - if( !pSrcGraphics ) - { - pSrcGraphics = this; - } //from unix salgdi2.cxx //[FIXME] find a better way to prevent calc from crashing when width and height are negative if( rPosAry.mnSrcWidth <= 0 || @@ -106,12 +101,21 @@ void AquaSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSrcGrap } // If called from idle layout, maContextHolder.get() is NULL, no idea what to do - if (!maContextHolder.isSet()) + if (!mrShared.maContextHolder.isSet()) return; + AquaSharedAttributes* pSrcShared = nullptr; + + if (pSrcGraphics) + { + AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + pSrcShared = &pSrc->getAquaGraphicsBackend()->mrShared; + } + else + pSrcShared = &mrShared; + // accelerate trivial operations - /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); - const bool bSameGraphics = (this == pSrc); + const bool bSameGraphics = (pSrcShared == &mrShared); if( bSameGraphics && (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) && @@ -129,26 +133,27 @@ void AquaSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSrcGrap return; } - ApplyXorContext(); - pSrc->ApplyXorContext(); + mrShared.applyXorContext(); + if (!bSameGraphics) + pSrcShared->applyXorContext(); - SAL_WARN_IF (!pSrc->maLayer.isSet(), "vcl.quartz", + SAL_WARN_IF (!pSrcShared->maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::copyBits() from non-layered graphics this=" << this); const CGPoint aDstPoint = CGPointMake(+rPosAry.mnDestX - rPosAry.mnSrcX, rPosAry.mnDestY - rPosAry.mnSrcY); if ((rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight) && - (!mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth) - && pSrc->maLayer.isSet()) // workaround for a Quartz crash + (!mrShared.mnBitmapDepth || (aDstPoint.x + pSrcShared->mnWidth) <= mrShared.mnWidth) + && pSrcShared->maLayer.isSet()) // workaround for a Quartz crash { // in XOR mode the drawing context is redirected to the XOR mask // if source and target are identical then copyBits() paints onto the target context though - CGContextHolder aCopyContext = maContextHolder; - if( mpXorEmulation && mpXorEmulation->IsEnabled() ) + CGContextHolder aCopyContext = mrShared.maContextHolder; + if (mrShared.mpXorEmulation && mrShared.mpXorEmulation->IsEnabled()) { - if( pSrcGraphics == this ) + if (bSameGraphics) { - aCopyContext.set(mpXorEmulation->GetTargetContext()); + aCopyContext.set(mrShared.mpXorEmulation->GetTargetContext()); } } aCopyContext.saveState(); @@ -158,43 +163,47 @@ void AquaSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSrcGrap // draw at new destination // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down - if( pSrc->IsFlipped() ) + if (pSrcShared->isFlipped()) { - CGContextTranslateCTM( aCopyContext.get(), 0, +mnHeight ); - CGContextScaleCTM( aCopyContext.get(), +1, -1 ); + CGContextTranslateCTM(aCopyContext.get(), 0, +mrShared.mnHeight); + CGContextScaleCTM(aCopyContext.get(), +1, -1); } // TODO: pSrc->size() != this->size() - CGContextDrawLayerAtPoint(aCopyContext.get(), aDstPoint, pSrc->maLayer.get()); + CGContextDrawLayerAtPoint(aCopyContext.get(), aDstPoint, pSrcShared->maLayer.get()); aCopyContext.restoreState(); // mark the destination rectangle as updated - RefreshRect( aDstRect ); + refreshRect(aDstRect); } else { - std::shared_ptr<SalBitmap> pBitmap = pSrc->getBitmap( rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight ); - if( pBitmap ) + std::shared_ptr<SalBitmap> pBitmap; + if (pSrcGraphics) + pBitmap = pSrcGraphics->GetImpl()->getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); + else + pBitmap = getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); + + if (pBitmap) { SalTwoRect aPosAry( rPosAry ); aPosAry.mnSrcX = 0; aPosAry.mnSrcY = 0; - drawBitmap( aPosAry, *pBitmap ); + drawBitmap(aPosAry, *pBitmap); } } } -void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, +void AquaGraphicsBackend::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, tools::Long nSrcWidth, tools::Long nSrcHeight, bool /*bWindowInvalidate*/) { - SAL_WARN_IF (!maLayer.isSet(), "vcl.quartz", + SAL_WARN_IF (!mrShared.maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::copyArea() for non-layered graphics this=" << this); - if (!maLayer.isSet()) + if (!mrShared.maLayer.isSet()) return; - float fScale = maLayer.getScale(); + float fScale = mrShared.maLayer.getScale(); tools::Long nScaledSourceX = nSrcX * fScale; tools::Long nScaledSourceY = nSrcY * fScale; @@ -205,17 +214,17 @@ void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long tools::Long nScaledSourceWidth = nSrcWidth * fScale; tools::Long nScaledSourceHeight = nSrcHeight * fScale; - ApplyXorContext(); + mrShared.applyXorContext(); - maContextHolder.saveState(); + mrShared.maContextHolder.saveState(); // in XOR mode the drawing context is redirected to the XOR mask // copyArea() always works on the target context though - CGContextRef xCopyContext = maContextHolder.get(); + CGContextRef xCopyContext = mrShared.maContextHolder.get(); - if( mpXorEmulation && mpXorEmulation->IsEnabled() ) + if (mrShared.mpXorEmulation && mrShared.mpXorEmulation->IsEnabled()) { - xCopyContext = mpXorEmulation->GetTargetContext(); + xCopyContext = mrShared.mpXorEmulation->GetTargetContext(); } // If we have a scaled layer, we need to revert the scaling or else @@ -227,7 +236,7 @@ void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long // e.g. on OSX>=10.5 only this situation causes problems: // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth - CGLayerHolder sSourceLayerHolder(maLayer); + CGLayerHolder sSourceLayerHolder(mrShared.maLayer); { const CGSize aSrcSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight); sSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSrcSize, nullptr)); @@ -235,15 +244,15 @@ void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long const CGContextRef xSrcContext = CGLayerGetContext(sSourceLayerHolder.get()); CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY); - if( IsFlipped() ) + if (mrShared.isFlipped()) { - CGContextTranslateCTM( xSrcContext, 0, +nScaledSourceHeight ); - CGContextScaleCTM( xSrcContext, +1, -1 ); - aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mnHeight * fScale); + CGContextTranslateCTM(xSrcContext, 0, +nScaledSourceHeight); + CGContextScaleCTM(xSrcContext, +1, -1); + aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mrShared.mnHeight * fScale); } CGContextSetBlendMode(xSrcContext, kCGBlendModeCopy); - CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, maLayer.get()); + CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, mrShared.maLayer.get()); } // draw at new destination @@ -251,15 +260,16 @@ void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long CGContextSetBlendMode(xCopyContext, kCGBlendModeCopy); CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get()); - maContextHolder.restoreState(); + mrShared.maContextHolder.restoreState(); // cleanup - if (sSourceLayerHolder.get() != maLayer.get()) + if (sSourceLayerHolder.get() != mrShared.maLayer.get()) { CGLayerRelease(sSourceLayerHolder.get()); } + // mark the destination rectangle as updated - RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); + mrShared.refreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight); } void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef xContext, @@ -267,52 +277,52 @@ void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextR { SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext ); - mbPrinter = false; - mbVirDev = true; + maShared.mbPrinter = false; + maShared.mbVirDev = true; // set graphics properties - maLayer = rLayer; - maContextHolder.set(xContext); + maShared.maLayer = rLayer; + maShared.maContextHolder.set(xContext); - mnBitmapDepth = nBitmapDepth; + maShared.mnBitmapDepth = nBitmapDepth; - mbForeignContext = xContext != NULL; + maShared.mbForeignContext = xContext != NULL; // return early if the virdev is being destroyed - if( !xContext ) + if (!xContext) return; // get new graphics properties - if (!maLayer.isSet()) + if (!maShared.maLayer.isSet()) { - mnWidth = CGBitmapContextGetWidth( maContextHolder.get() ); - mnHeight = CGBitmapContextGetHeight( maContextHolder.get() ); + maShared.mnWidth = CGBitmapContextGetWidth(maShared.maContextHolder.get()); + maShared.mnHeight = CGBitmapContextGetHeight(maShared.maContextHolder.get()); } else { - const CGSize aSize = CGLayerGetSize(maLayer.get()); - mnWidth = static_cast<int>(aSize.width); - mnHeight = static_cast<int>(aSize.height); + const CGSize aSize = CGLayerGetSize(maShared.maLayer.get()); + maShared.mnWidth = static_cast<int>(aSize.width); + maShared.mnHeight = static_cast<int>(aSize.height); } // prepare graphics for drawing const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; - CGContextSetFillColorSpace( maContextHolder.get(), aCGColorSpace ); - CGContextSetStrokeColorSpace( maContextHolder.get(), aCGColorSpace ); + CGContextSetFillColorSpace(maShared.maContextHolder.get(), aCGColorSpace); + CGContextSetStrokeColorSpace(maShared.maContextHolder.get(), aCGColorSpace); // re-enable XorEmulation for the new context - if( mpXorEmulation ) + if (maShared.mpXorEmulation) { - mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); - if( mpXorEmulation->IsEnabled() ) + maShared.mpXorEmulation->SetTarget(maShared.mnWidth, maShared.mnHeight, maShared.mnBitmapDepth, maShared.maContextHolder.get(), maShared.maLayer.get()); + if (maShared.mpXorEmulation->IsEnabled()) { - maContextHolder.set(mpXorEmulation->GetMaskContext()); + maShared.maContextHolder.set(maShared.mpXorEmulation->GetMaskContext()); } } // initialize stack of CGContext states - maContextHolder.saveState(); - SetState(); + maShared.maContextHolder.saveState(); + maShared.setState(); } /// From salvd.cxx diff --git a/vcl/osx/salgdiutils.cxx b/vcl/osx/salgdiutils.cxx index f3ddd946f699..81210c8e876c 100644 --- a/vcl/osx/salgdiutils.cxx +++ b/vcl/osx/salgdiutils.cxx @@ -46,7 +46,9 @@ static bool bWindowScaling = false; static float fWindowScale = 1.0f; -float AquaSalGraphics::GetWindowScaling() +namespace sal::aqua +{ +float getWindowScaling() { if (!bWindowScaling) { @@ -65,38 +67,35 @@ float AquaSalGraphics::GetWindowScaling() } return fWindowScale; } +} // end aqua void AquaSalGraphics::SetWindowGraphics( AquaSalFrame* pFrame ) { - mpFrame = pFrame; - mbWindow = true; - mbPrinter = false; - mbVirDev = false; + maShared.mpFrame = pFrame; + maShared.mbWindow = true; + maShared.mbPrinter = false; + maShared.mbVirDev = false; } void AquaSalGraphics::SetPrinterGraphics( CGContextRef xContext, sal_Int32 nDPIX, sal_Int32 nDPIY ) { - mbWindow = false; - mbPrinter = true; - mbVirDev = false; + maShared.mbWindow = false; + maShared.mbPrinter = true; + maShared.mbVirDev = false; - maContextHolder.set(xContext); + maShared.maContextHolder.set(xContext); mnRealDPIX = nDPIX; mnRealDPIY = nDPIY; // a previously set clip path is now invalid - if( mxClipPath ) - { - CGPathRelease( mxClipPath ); - mxClipPath = nullptr; - } + maShared.unsetClipPath(); - if (maContextHolder.isSet()) + if (maShared.maContextHolder.isSet()) { - CGContextSetFillColorSpace( maContextHolder.get(), GetSalData()->mxRGBSpace ); - CGContextSetStrokeColorSpace( maContextHolder.get(), GetSalData()->mxRGBSpace ); - CGContextSaveGState( maContextHolder.get() ); - SetState(); + CGContextSetFillColorSpace( maShared.maContextHolder.get(), GetSalData()->mxRGBSpace ); + CGContextSetStrokeColorSpace( maShared.maContextHolder.get(), GetSalData()->mxRGBSpace ); + CGContextSaveGState( maShared.maContextHolder.get() ); + maShared.setState(); } } @@ -104,50 +103,46 @@ void AquaSalGraphics::InvalidateContext() { UnsetState(); - CGContextRelease(maContextHolder.get()); - CGContextRelease(maBGContextHolder.get()); - CGContextRelease(maCSContextHolder.get()); + CGContextRelease(maShared.maContextHolder.get()); + CGContextRelease(maShared.maBGContextHolder.get()); + CGContextRelease(maShared.maCSContextHolder.get()); - maContextHolder.set(nullptr); - maCSContextHolder.set(nullptr); - maBGContextHolder.set(nullptr); + maShared.maContextHolder.set(nullptr); + maShared.maCSContextHolder.set(nullptr); + maShared.maBGContextHolder.set(nullptr); } void AquaSalGraphics::UnsetState() { - if (maBGContextHolder.isSet()) - { - CGContextRelease(maBGContextHolder.get()); - maBGContextHolder.set(nullptr); - } - if (maCSContextHolder.isSet()) + if (maShared.maBGContextHolder.isSet()) { - CGContextRelease(maCSContextHolder.get()); - maBGContextHolder.set(nullptr); + CGContextRelease(maShared.maBGContextHolder.get()); + maShared.maBGContextHolder.set(nullptr); } - if (maContextHolder.isSet()) + if (maShared.maCSContextHolder.isSet()) { - maContextHolder.restoreState(); - maContextHolder.set(nullptr); + CGContextRelease(maShared.maCSContextHolder.get()); + maShared.maBGContextHolder.set(nullptr); } - if( mxClipPath ) + if (maShared.maContextHolder.isSet()) { - CGPathRelease( mxClipPath ); - mxClipPath = nullptr; + maShared.maContextHolder.restoreState(); + maShared.maContextHolder.set(nullptr); } + maShared.unsetState(); } /** * (re-)create the off-screen maLayer we render everything to if * necessary: eg. not initialized yet, or it has an incorrect size. */ -bool AquaSalGraphics::CheckContext() +bool AquaSharedAttributes::checkContext() { if (mbWindow && mpFrame && (mpFrame->getNSWindow() || Application::IsBitmapRendering())) { const unsigned int nWidth = mpFrame->maGeometry.nWidth; const unsigned int nHeight = mpFrame->maGeometry.nHeight; - const float fScale = GetWindowScaling(); + const float fScale = sal::aqua::getWindowScaling(); CGLayerRef rReleaseLayer = nullptr; // check if a new drawing context is needed (e.g. after a resize) @@ -218,7 +213,7 @@ bool AquaSalGraphics::CheckContext() // apply a scale matrix so everything is auto-magically scaled CGContextScaleCTM(maContextHolder.get(), fScale, fScale); maContextHolder.saveState(); - SetState(); + setState(); // re-enable XOR emulation for the new context if (mpXorEmulation) @@ -239,19 +234,19 @@ bool AquaSalGraphics::CheckContext() */ void AquaSalGraphics::UpdateWindow( NSRect& ) { - if( !mpFrame ) + if (!maShared.mpFrame) { return; } NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; - if (maLayer.isSet() && pContext != nullptr) + if (maShared.maLayer.isSet() && pContext != nullptr) { CGContextHolder rCGContextHolder([pContext CGContext]); rCGContextHolder.saveState(); - CGMutablePathRef rClip = mpFrame->getClipPath(); + CGMutablePathRef rClip = maShared.mpFrame->getClipPath(); if (rClip) { CGContextBeginPath(rCGContextHolder.get()); @@ -259,16 +254,16 @@ void AquaSalGraphics::UpdateWindow( NSRect& ) CGContextClip(rCGContextHolder.get()); } - ApplyXorContext(); + maShared.applyXorContext(); - const CGSize aSize = maLayer.getSizePoints(); + const CGSize aSize = maShared.maLayer.getSizePoints(); const CGRect aRect = CGRectMake(0, 0, aSize.width, aSize.height); - const CGRect aRectPoints = { CGPointZero, maLayer.getSizePixels() }; - CGContextSetBlendMode(maCSContextHolder.get(), kCGBlendModeCopy); - CGContextDrawLayerInRect(maCSContextHolder.get(), aRectPoints, maLayer.get()); + const CGRect aRectPoints = { CGPointZero, maShared.maLayer.getSizePixels() }; + CGContextSetBlendMode(maShared.maCSContextHolder.get(), kCGBlendModeCopy); + CGContextDrawLayerInRect(maShared.maCSContextHolder.get(), aRectPoints, maShared.maLayer.get()); - CGImageRef img = CGBitmapContextCreateImage(maCSContextHolder.get()); - CGImageRef displayColorSpaceImage = CGImageCreateCopyWithColorSpace(img, [[mpFrame->getNSWindow() colorSpace] CGColorSpace]); + CGImageRef img = CGBitmapContextCreateImage(maShared.maCSContextHolder.get()); + CGImageRef displayColorSpaceImage = CGImageCreateCopyWithColorSpace(img, [[maShared.mpFrame->getNSWindow() colorSpace] CGColorSpace]); CGContextSetBlendMode(rCGContextHolder.get(), kCGBlendModeCopy); CGContextDrawImage(rCGContextHolder.get(), aRect, displayColorSpaceImage); @@ -279,7 +274,7 @@ void AquaSalGraphics::UpdateWindow( NSRect& ) } else { - SAL_WARN_IF( !mpFrame->mbInitShow, "vcl", "UpdateWindow called on uneligible graphics" ); + SAL_WARN_IF(!maShared.mpFrame->mbInitShow, "vcl", "UpdateWindow called on uneligible graphics"); } } diff --git a/vcl/osx/salmacos.cxx b/vcl/osx/salmacos.cxx index f6403dea2725..fa2520f056a6 100644 --- a/vcl/osx/salmacos.cxx +++ b/vcl/osx/salmacos.cxx @@ -96,29 +96,36 @@ bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits // From salgdicommon.cxx -void AquaSalGraphics::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraphics) +void AquaGraphicsBackend::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraphics) { - if (!pSrcGraphics) - pSrcGraphics = this; - AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + AquaSharedAttributes* pSrcShared = nullptr; + + if (pSrcGraphics) + { + AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + pSrcShared = &pSrc->getAquaGraphicsBackend()->mrShared; + } + else + pSrcShared = &mrShared; + if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0 || rPosAry.mnDestHeight <= 0) return; - if (!maContextHolder.isSet()) + if (!mrShared.maContextHolder.isSet()) return; - SAL_WARN_IF (!pSrc->maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::copyBits() from non-layered graphics this=" << this); + SAL_WARN_IF (!pSrcShared->maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::copyBits() from non-layered graphics this=" << this); // Layered graphics are copied by AquaSalGraphics::copyScaledArea() which is able to consider the layer's scaling. - if (pSrc->maLayer.isSet()) + if (pSrcShared->maLayer.isSet()) copyScaledArea(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, pSrcGraphics); + rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, pSrcShared); else { - ApplyXorContext(); - pSrc->ApplyXorContext(); - std::shared_ptr<SalBitmap> pBitmap = pSrc->getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); + mrShared.applyXorContext(); + pSrcShared->applyXorContext(); + std::shared_ptr<SalBitmap> pBitmap = pSrcGraphics->GetImpl()->getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, + rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); if (pBitmap) { SalTwoRect aPosAry(rPosAry); @@ -129,34 +136,30 @@ void AquaSalGraphics::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraph } } -void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, +void AquaGraphicsBackend::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, tools::Long nSrcWidth, tools::Long nSrcHeight, bool) { - if (!maContextHolder.isSet()) + if (!mrShared.maContextHolder.isSet()) return; // Functionality is implemented in protected member function AquaSalGraphics::copyScaledArea() which requires an additional // parameter of type SalGraphics to be used in AquaSalGraphics::copyBits() too. - copyScaledArea(nDstX, nDstY, nSrcX, nSrcY, nSrcWidth, nSrcHeight, this); + copyScaledArea(nDstX, nDstY, nSrcX, nSrcY, nSrcWidth, nSrcHeight, &mrShared); } -void AquaSalGraphics::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, - tools::Long nSrcWidth, tools::Long nSrcHeight, SalGraphics *pSrcGraphics) +void AquaGraphicsBackend::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, + tools::Long nSrcWidth, tools::Long nSrcHeight, AquaSharedAttributes* pSrcShared) { - if (!pSrcGraphics) - pSrcGraphics = this; - AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); - - SAL_WARN_IF(!maLayer.isSet(), "vcl.quartz", + SAL_WARN_IF(!mrShared.maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::copyScaledArea() without graphics context or for non-layered graphics this=" << this); - if (!maContextHolder.isSet() || !maLayer.isSet()) + if (!mrShared.maContextHolder.isSet() || !mrShared.maLayer.isSet()) return; // Determine scaled geometry of source and target area assuming source and target area have the same scale - float fScale = maLayer.getScale(); + float fScale = mrShared.maLayer.getScale(); CGFloat nScaledSourceX = nSrcX * fScale; CGFloat nScaledSourceY = nSrcY * fScale; CGFloat nScaledTargetX = nDstX * fScale; @@ -166,11 +169,11 @@ void AquaSalGraphics::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools: // Apply XOR context and get copy context from current graphics context or XOR context - ApplyXorContext(); - maContextHolder.saveState(); - CGContextRef xCopyContext = maContextHolder.get(); - if (mpXorEmulation && mpXorEmulation->IsEnabled()) - xCopyContext = mpXorEmulation->GetTargetContext(); + mrShared.applyXorContext(); + mrShared.maContextHolder.saveState(); + CGContextRef xCopyContext = mrShared.maContextHolder.get(); + if (mrShared.mpXorEmulation && mrShared.mpXorEmulation->IsEnabled()) + xCopyContext = mrShared.mpXorEmulation->GetTargetContext(); // Set scale matrix of copy context to consider layer scaling @@ -179,19 +182,19 @@ void AquaSalGraphics::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools: // Creating an additional layer is required for drawing with the required scale and extent at the drawing destination // thereafter. - CGLayerHolder aSourceLayerHolder(pSrc->maLayer); + CGLayerHolder aSourceLayerHolder(pSrcShared->maLayer); const CGSize aSourceSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight); aSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSourceSize, nullptr)); const CGContextRef xSourceContext = CGLayerGetContext(aSourceLayerHolder.get()); CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY); - if (pSrc->IsFlipped()) + if (pSrcShared->isFlipped()) { CGContextTranslateCTM(xSourceContext, 0, nScaledSourceHeight); CGContextScaleCTM(xSourceContext, 1, -1); - aSrcPoint.y = nScaledSourceY + nScaledSourceHeight - mnHeight * fScale; + aSrcPoint.y = nScaledSourceY + nScaledSourceHeight - mrShared.mnHeight * fScale; } CGContextSetBlendMode(xSourceContext, kCGBlendModeCopy); - CGContextDrawLayerAtPoint(xSourceContext, aSrcPoint, pSrc->maLayer.get()); + CGContextDrawLayerAtPoint(xSourceContext, aSrcPoint, pSrcShared->maLayer.get()); // Copy source area from additional layer to target area @@ -201,10 +204,11 @@ void AquaSalGraphics::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools: // Housekeeping on exit - maContextHolder.restoreState(); - if (aSourceLayerHolder.get() != maLayer.get()) + mrShared.maContextHolder.restoreState(); + if (aSourceLayerHolder.get() != mrShared.maLayer.get()) CGLayerRelease(aSourceLayerHolder.get()); - RefreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight); + + mrShared.refreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight); } void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRef xContext, int nBitmapDepth) @@ -214,61 +218,61 @@ void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRe // Set member variables InvalidateContext(); - mbWindow = false; - mbPrinter = false; - mbVirDev = true; - maLayer = rLayer; - mnBitmapDepth = nBitmapDepth; + maShared.mbWindow = false; + maShared.mbPrinter = false; + maShared.mbVirDev = true; + maShared.maLayer = rLayer; + maShared.mnBitmapDepth = nBitmapDepth; - // Get size and scale from layer if set else from bitmap and AquaSalGraphics::GetWindowScaling(), which is used to determine + // Get size and scale from layer if set else from bitmap and sal::aqua::getWindowScaling(), which is used to determine // scaling for direct graphics output too CGSize aSize; float fScale; - if (maLayer.isSet()) + if (maShared.maLayer.isSet()) { - maContextHolder.set(CGLayerGetContext(maLayer.get())); - aSize = CGLayerGetSize(maLayer.get()); - fScale = maLayer.getScale(); + maShared.maContextHolder.set(CGLayerGetContext(maShared.maLayer.get())); + aSize = CGLayerGetSize(maShared.maLayer.get()); + fScale = maShared.maLayer.getScale(); } else { - maContextHolder.set(xContext); + maShared.maContextHolder.set(xContext); if (!xContext) return; aSize.width = CGBitmapContextGetWidth(xContext); aSize.height = CGBitmapContextGetHeight(xContext); - fScale = GetWindowScaling(); + fScale = sal::aqua::getWindowScaling(); } - mnWidth = aSize.width / fScale; - mnHeight = aSize.height / fScale; + maShared.mnWidth = aSize.width / fScale; + maShared.mnHeight = aSize.height / fScale; // Set color space for fill and stroke CGColorSpaceRef aColorSpace = GetSalData()->mxRGBSpace; - CGContextSetFillColorSpace(maContextHolder.get(), aColorSpace); - CGContextSetStrokeColorSpace(maContextHolder.get(), aColorSpace); + CGContextSetFillColorSpace(maShared.maContextHolder.get(), aColorSpace); + CGContextSetStrokeColorSpace(maShared.maContextHolder.get(), aColorSpace); // Apply scale matrix to virtual device graphics - CGContextScaleCTM(maContextHolder.get(), fScale, fScale); + CGContextScaleCTM(maShared.maContextHolder.get(), fScale, fScale); // Apply XOR emulation if required - if (mpXorEmulation) + if (maShared.mpXorEmulation) { - mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); - if (mpXorEmulation->IsEnabled()) - maContextHolder.set(mpXorEmulation->GetMaskContext()); + maShared.mpXorEmulation->SetTarget(maShared.mnWidth, maShared.mnHeight, maShared.mnBitmapDepth, maShared.maContextHolder.get(), maShared.maLayer.get()); + if (maShared.mpXorEmulation->IsEnabled()) + maShared.maContextHolder.set(maShared.mpXorEmulation->GetMaskContext()); } // Housekeeping on exit - maContextHolder.saveState(); - SetState(); + maShared.maContextHolder.saveState(); + maShared.setState(); SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this << - " (" << mnWidth << "x" << mnHeight << ") fScale=" << fScale << " mnBitmapDepth=" << mnBitmapDepth); + " (" << maShared.mnWidth << "x" << maShared.mnHeight << ") fScale=" << fScale << " mnBitmapDepth=" << maShared.mnBitmapDepth); } // From salvd.cxx @@ -277,7 +281,7 @@ void AquaSalVirtualDevice::Destroy() { SAL_INFO( "vcl.virdev", "AquaSalVirtualDevice::Destroy() this=" << this << " mbForeignContext=" << mbForeignContext ); - if( mbForeignContext ) + if (mbForeignContext) { // Do not delete mxContext that we have received from outside VCL maLayer.set(nullptr); @@ -327,11 +331,11 @@ bool AquaSalVirtualDevice::SetSize(tools::Long nDX, tools::Long nDY) Destroy(); // Prepare new graphics context for initialization, use scaling independent of prior graphics context calculated by - // AquaSalGraphics::GetWindowScaling(), which is used to determine scaling for direct graphics output too + // sal::aqua::getWindowScaling(), which is used to determine scaling for direct graphics output too mnWidth = nDX; mnHeight = nDY; - fScale = AquaSalGraphics::GetWindowScaling(); + fScale = sal::aqua::getWindowScaling(); CGColorSpaceRef aColorSpace; uint32_t nFlags; if (mnBitmapDepth && (mnBitmapDepth < 16)) diff --git a/vcl/osx/salnativewidgets.cxx b/vcl/osx/salnativewidgets.cxx index bc132ee88d16..589feec62208 100644 --- a/vcl/osx/salnativewidgets.cxx +++ b/vcl/osx/salnativewidgets.cxx @@ -232,8 +232,8 @@ UInt32 AquaSalGraphics::getState(ControlState nState) // there are non key windows which are children of key windows, e.g. autofilter configuration dialog or sidebar dropdown dialogs. // To handle these windows correctly, parent frame's key window state is considered here additionally. - const bool bDrawActive = mpFrame == nullptr || [mpFrame->getNSWindow() isKeyWindow] - || mpFrame->mpParent == nullptr || [mpFrame->mpParent->getNSWindow() isKeyWindow]; + const bool bDrawActive = maShared.mpFrame == nullptr || [maShared.mpFrame->getNSWindow() isKeyWindow] + || maShared.mpFrame->mpParent == nullptr || [maShared.mpFrame->mpParent->getNSWindow() isKeyWindow]; if (!(nState & ControlState::ENABLED) || !bDrawActive) { return kThemeStateInactive; @@ -245,7 +245,7 @@ UInt32 AquaSalGraphics::getState(ControlState nState) UInt32 AquaSalGraphics::getTrackState(ControlState nState) { - const bool bDrawActive = mpFrame == nullptr || [mpFrame->getNSWindow() isKeyWindow]; + const bool bDrawActive = maShared.mpFrame == nullptr || [maShared.mpFrame->getNSWindow() isKeyWindow]; if (!(nState & ControlState::ENABLED) || !bDrawActive) return kThemeTrackInactive; return kThemeTrackActive; @@ -260,9 +260,9 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, const Color&) { bool bOK = false; - if (!CheckContext()) + if (!maShared.checkContext()) return false; - maContextHolder.saveState(); + maShared.maContextHolder.saveState(); tools::Rectangle buttonRect = rControlRegion; HIRect rc = ImplGetHIRectFromRectangle(buttonRect); switch (nType) @@ -274,15 +274,15 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aMenuItemDrawInfo.version = 0; aMenuItemDrawInfo.state = kThemeMenuActive; aMenuItemDrawInfo.itemType = kThemeMenuItemHierBackground; - HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); #else if (rControlRegion.Top() == 0 && nPart == ControlPart::DrawBackgroundHorz) { - const bool bDrawActive = mpFrame == nullptr || [mpFrame->getNSWindow() isKeyWindow]; + const bool bDrawActive = maShared.mpFrame == nullptr || [maShared.mpFrame->getNSWindow() isKeyWindow]; CGFloat unifiedHeight = rControlRegion.GetHeight(); CGRect drawRect = CGRectMake(rControlRegion.Left(), rControlRegion.Top(), rControlRegion.GetWidth(), rControlRegion.GetHeight()); - CUIDraw([NSWindow coreUIRenderer], drawRect, maContextHolder.get(), + CUIDraw([NSWindow coreUIRenderer], drawRect, maShared.maContextHolder.get(), reinterpret_cast<CFDictionaryRef>([NSDictionary dictionaryWithObjectsAndKeys: @"kCUIWidgetWindowFrame", @"widget", @@ -305,7 +305,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aMenuItemDrawInfo.version = 0; aMenuItemDrawInfo.state = kThemeMenuActive; aMenuItemDrawInfo.itemType = kThemeMenuItemHierBackground; - HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); } #endif bOK = true; @@ -322,8 +322,8 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, rc.size.width += 2; rc.size.height += 2; - HIThemeApplyBackground( &rc, &aThemeBackgroundInfo, maContextHolder.get(), kHIThemeOrientationNormal); - CGContextFillRect(maContextHolder.get(), rc); + HIThemeApplyBackground( &rc, &aThemeBackgroundInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); + CGContextFillRect(maShared.maContextHolder.get(), rc); bOK = true; } break; @@ -335,8 +335,8 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aThemeBackgroundInfo.kind = kThemeBrushAlertBackgroundActive; rc.size.width += 2; rc.size.height += 2; - HIThemeApplyBackground(&rc, &aThemeBackgroundInfo, maContextHolder.get(), kHIThemeOrientationNormal); - CGContextFillRect(maContextHolder.get(), rc); + HIThemeApplyBackground(&rc, &aThemeBackgroundInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); + CGContextFillRect(maShared.maContextHolder.get(), rc); bOK = true; } break; @@ -369,11 +369,11 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, // repaints the background of the pull down menu - HIThemeDrawMenuBackground(&rc, &aMenuInfo,maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawMenuBackground(&rc, &aMenuInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); // repaints the item either blue (selected) and/or grey (active only) - HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal, &rc); + HIThemeDrawMenuItem(&rc, &rc, &aMenuItemDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, &rc); bOK = true; } else if (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark) @@ -397,7 +397,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, if (nState & ControlState::SELECTED) aTextInfo.state = kThemeStatePressed; UniChar mark=(nPart == ControlPart::MenuItemCheckMark) ? kCheckUnicode: kBulletUnicode; CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &mark, 1, kCFAllocatorNull); - HIThemeDrawTextBox(cfString, &rc, &aTextInfo, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawTextBox(cfString, &rc, &aTextInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); if (cfString) CFRelease(cfString); bOK = true; @@ -449,7 +449,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aPushInfo.adornment = (nState & ControlState::DEFAULT) ? kThemeAdornmentDefault : kThemeAdornmentNone; if (nState & ControlState::FOCUSED) aPushInfo.adornment |= kThemeAdornmentFocus; - HIThemeDrawButton(&rc, &aPushInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawButton(&rc, &aPushInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); bOK = true; } break; @@ -485,7 +485,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, rc.size.height = RADIO_BUTTON_SMALL_SIZE; rc.origin.x += FOCUS_RING_WIDTH; rc.origin.y += FOCUS_RING_WIDTH; - HIThemeDrawButton(&rc, &aInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawButton(&rc, &aInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); bOK = true; } break; @@ -511,7 +511,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, default: break; } - HIThemeDrawButton(&rc, &aInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawButton(&rc, &aInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); bOK = true; } break; @@ -538,7 +538,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aTrackInfo.enableState = kThemeTrackActive; aTrackInfo.filler1 = 0; aTrackInfo.trackInfo.progress.phase = static_cast<long long>(CFAbsoluteTimeGetCurrent() * 10.0); - HIThemeDrawTrack(&aTrackInfo, nullptr, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawTrack(&aTrackInfo, nullptr, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; } break; @@ -562,7 +562,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aSlideInfo.thumbDir = kThemeThumbUpward; aSlideInfo.pressState = 0; aTrackDraw.trackInfo.slider = aSlideInfo; - HIThemeDrawTrack(&aTrackDraw, nullptr, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawTrack(&aTrackDraw, nullptr, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; } } @@ -597,7 +597,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, if (pScrollbarVal->mnThumbState & ControlState::PRESSED) aScrollInfo.pressState = kThemeThumbPressed; aTrackDraw.trackInfo.scrollbar = aScrollInfo; - HIThemeDrawTrack(&aTrackDraw, nullptr, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawTrack(&aTrackDraw, nullptr, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; } } @@ -617,7 +617,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, rc.origin.y -= TAB_HEIGHT / 2; rc.size.height += TAB_HEIGHT / 2; rc.size.width -= 2; - HIThemeDrawTabPane(&rc, &aTabPaneDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawTabPane(&rc, &aTabPaneDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; } break; @@ -661,7 +661,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, else if (aTabItemDrawInfo.position == kHIThemeTabPositionLast) aTabItemDrawInfo.position = kHIThemeTabPositionFirst; } - HIThemeDrawTab(&rc, &aTabItemDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawTab(&rc, &aTabItemDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); bOK=true; } break; @@ -683,10 +683,10 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, // fill a white background, because HIThemeDrawFrame only draws the border - CGContextFillRect(maContextHolder.get(), CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height)); - HIThemeDrawFrame(&rc, &aTextDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal); + CGContextFillRect(maShared.maContextHolder.get(), CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height)); + HIThemeDrawFrame(&rc, &aTextDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); if (nState & ControlState::FOCUSED) - HIThemeDrawFocusRect(&rc, true, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawFocusRect(&rc, true, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; } break; @@ -705,7 +705,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, rc.size.height = COMBOBOX_HEIGHT; rc.origin.x += FOCUS_RING_WIDTH; rc.origin.y += FOCUS_RING_WIDTH; - HIThemeDrawButton(&rc, &aComboInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawButton(&rc, &aComboInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); bOK = true; } break; @@ -726,7 +726,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, rc.size.height = LISTBOX_HEIGHT; rc.origin.x += FOCUS_RING_WIDTH; rc.origin.y += FOCUS_RING_WIDTH; - HIThemeDrawButton(&rc, &aListInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawButton(&rc, &aListInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); bOK = true; break; case ControlPart::ListboxWindow: @@ -735,7 +735,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aTextDrawInfo.kind = kHIThemeFrameListBox; aTextDrawInfo.state = kThemeStateActive; aTextDrawInfo.isFocused = false; - HIThemeDrawFrame(&rc, &aTextDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawFrame(&rc, &aTextDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; break; default: @@ -760,10 +760,10 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, // fill a white background, because HIThemeDrawFrame only draws the border - CGContextFillRect(maContextHolder.get(), CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height)); - HIThemeDrawFrame(&rc, &aTextDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal); + CGContextFillRect(maShared.maContextHolder.get(), CGRectMake(rc.origin.x, rc.origin.y, rc.size.width, rc.size.height)); + HIThemeDrawFrame(&rc, &aTextDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); if (nState & ControlState::FOCUSED) - HIThemeDrawFocusRect(&rc, true, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawFocusRect(&rc, true, maShared.maContextHolder.get(), kHIThemeOrientationNormal); // buttons @@ -808,7 +808,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, rc.origin.y -= 1; rc.size.width = SPIN_BUTTON_WIDTH; rc.size.height = SPIN_LOWER_BUTTON_HEIGHT + SPIN_LOWER_BUTTON_HEIGHT; - HIThemeDrawButton(&rc, &aSpinInfo, maContextHolder.get(), kHIThemeOrientationNormal, nullptr); + HIThemeDrawButton(&rc, &aSpinInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal, nullptr); } bOK = true; } @@ -824,14 +824,14 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, // strange effects start to happen when HIThemeDrawFrame meets the border of the window. // These can be avoided by clipping to the boundary of the frame (see issue 84756) - if (rc.origin.y + rc.size.height >= mpFrame->maGeometry.nHeight - 3) + if (rc.origin.y + rc.size.height >= maShared.mpFrame->maGeometry.nHeight - 3) { CGMutablePathRef rPath = CGPathCreateMutable(); CGPathAddRect(rPath, nullptr, - CGRectMake(0, 0, mpFrame->maGeometry.nWidth - 1, mpFrame->maGeometry.nHeight - 1)); - CGContextBeginPath(maContextHolder.get()); - CGContextAddPath(maContextHolder.get(), rPath); - CGContextClip(maContextHolder.get()); + CGRectMake(0, 0, maShared.mpFrame->maGeometry.nWidth - 1, maShared.mpFrame->maGeometry.nHeight - 1)); + CGContextBeginPath(maShared.maContextHolder.get()); + CGContextAddPath(maShared.maContextHolder.get(), rPath); + CGContextClip(maShared.maContextHolder.get()); CGPathRelease(rPath); } HIThemeFrameDrawInfo aTextDrawInfo; @@ -839,7 +839,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, aTextDrawInfo.kind = kHIThemeFrameListBox; aTextDrawInfo.state = kThemeStateActive; aTextDrawInfo.isFocused = false; - HIThemeDrawFrame(&rc, &aTextDrawInfo, maContextHolder.get(), kHIThemeOrientationNormal); + HIThemeDrawFrame(&rc, &aTextDrawInfo, maShared.maContextHolder.get(), kHIThemeOrientationNormal); bOK = true; } } @@ -854,7 +854,7 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, default: break; } - maContextHolder.restoreState(); + maShared.maContextHolder.restoreState(); // in most cases invalidating the whole control region instead of just the unclipped part of it is sufficient (and probably // faster). However for the window background we should not unnecessarily enlarge the really changed rectangle since the @@ -864,15 +864,15 @@ bool AquaSalGraphics::drawNativeControl(ControlType nType, if (nType == ControlType::WindowBackground) { CGRect aRect = {{0, 0}, {0, 0}}; - if (mxClipPath) - aRect = CGPathGetBoundingBox(mxClipPath); + if (maShared.mxClipPath) + aRect = CGPathGetBoundingBox(maShared.mxClipPath); if (aRect.size.width != 0 && aRect.size.height != 0) buttonRect.Intersection(tools::Rectangle(Point(static_cast<tools::Long>(aRect.origin.x), static_cast<tools::Long>(aRect.origin.y)), Size(static_cast<tools::Long>(aRect.size.width), static_cast<tools::Long>(aRect.size.height)))); } - RefreshRect(buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight()); + maShared.refreshRect(buttonRect.Left(), buttonRect.Top(), buttonRect.GetWidth(), buttonRect.GetHeight()); return bOK; } diff --git a/vcl/quartz/AquaGraphicsBackend.cxx b/vcl/quartz/AquaGraphicsBackend.cxx new file mode 100644 index 000000000000..0aa6042f7eb4 --- /dev/null +++ b/vcl/quartz/AquaGraphicsBackend.cxx @@ -0,0 +1,1355 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <cassert> +#include <cstring> +#include <numeric> + +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <osl/endian.h> +#include <osl/file.hxx> +#include <sal/types.h> +#include <tools/long.hxx> +#include <vcl/sysdata.hxx> + +#include <fontsubset.hxx> +#include <quartz/salbmp.h> +#ifdef MACOSX +#include <quartz/salgdi.h> +#endif +#include <quartz/utils.h> +#ifdef IOS +#include "saldatabasic.hxx" +#endif +#include <sft.hxx> + +using namespace vcl; + +namespace +{ +const basegfx::B2DPoint aHalfPointOfs(0.5, 0.5); + +static void AddPolygonToPath(CGMutablePathRef xPath, const basegfx::B2DPolygon& rPolygon, + bool bClosePath, bool bPixelSnap, bool bLineDraw) +{ + // short circuit if there is nothing to do + const int nPointCount = rPolygon.count(); + if (nPointCount <= 0) + { + return; + } + + const bool bHasCurves = rPolygon.areControlPointsUsed(); + for (int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++) + { + int nClosedIdx = nPointIdx; + if (nPointIdx >= nPointCount) + { + // prepare to close last curve segment if needed + if (bClosePath && (nPointIdx == nPointCount)) + { + nClosedIdx = 0; + } + else + { + break; + } + } + + basegfx::B2DPoint aPoint = rPolygon.getB2DPoint(nClosedIdx); + + if (bPixelSnap) + { + // snap device coordinates to full pixels + aPoint.setX(basegfx::fround(aPoint.getX())); + aPoint.setY(basegfx::fround(aPoint.getY())); + } + + if (bLineDraw) + { + aPoint += aHalfPointOfs; + } + if (!nPointIdx) + { + // first point => just move there + CGPathMoveToPoint(xPath, nullptr, aPoint.getX(), aPoint.getY()); + continue; + } + + bool bPendingCurve = false; + if (bHasCurves) + { + bPendingCurve = rPolygon.isNextControlPointUsed(nPrevIdx); + bPendingCurve |= rPolygon.isPrevControlPointUsed(nClosedIdx); + } + + if (!bPendingCurve) // line segment + { + CGPathAddLineToPoint(xPath, nullptr, aPoint.getX(), aPoint.getY()); + } + else // cubic bezier segment + { + basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint(nPrevIdx); + basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint(nClosedIdx); + if (bLineDraw) + { + aCP1 += aHalfPointOfs; + aCP2 += aHalfPointOfs; + } + CGPathAddCurveToPoint(xPath, nullptr, aCP1.getX(), aCP1.getY(), aCP2.getX(), + aCP2.getY(), aPoint.getX(), aPoint.getY()); + } + } + + if (bClosePath) + { + CGPathCloseSubpath(xPath); + } +} + +static void alignLinePoint(const Point* i_pIn, float& o_fX, float& o_fY) +{ + o_fX = static_cast<float>(i_pIn->getX()) + 0.5; + o_fY = static_cast<float>(i_pIn->getY()) + 0.5; +} + +static void getBoundRect(sal_uInt32 nPoints, const Point* pPtAry, tools::Long& rX, tools::Long& rY, + tools::Long& rWidth, tools::Long& rHeight) +{ + tools::Long nX1 = pPtAry->getX(); + tools::Long nX2 = nX1; + tools::Long nY1 = pPtAry->getY(); + tools::Long nY2 = nY1; + + for (sal_uInt32 n = 1; n < nPoints; n++) + { + if (pPtAry[n].getX() < nX1) + { + nX1 = pPtAry[n].getX(); + } + else if (pPtAry[n].getX() > nX2) + { + nX2 = pPtAry[n].getX(); + } + if (pPtAry[n].getY() < nY1) + { + nY1 = pPtAry[n].getY(); + } + else if (pPtAry[n].getY() > nY2) + { + nY2 = pPtAry[n].getY(); + } + } + rX = nX1; + rY = nY1; + rWidth = nX2 - nX1 + 1; + rHeight = nY2 - nY1 + 1; +} + +static Color ImplGetROPColor(SalROPColor nROPColor) +{ + Color nColor; + if (nROPColor == SalROPColor::N0) + { + nColor = Color(0, 0, 0); + } + else + { + nColor = Color(255, 255, 255); + } + return nColor; +} + +static void drawPattern50(void*, CGContextRef rContext) +{ + static const CGRect aRects[2] = { { { 0, 0 }, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } }; + CGContextAddRects(rContext, aRects, 2); + CGContextFillPath(rContext); +} +} + +AquaGraphicsBackend::AquaGraphicsBackend(AquaSharedAttributes& rShared) + : mrShared(rShared) +{ +} + +AquaGraphicsBackend::~AquaGraphicsBackend() {} + +void AquaGraphicsBackend::Init() {} +void AquaGraphicsBackend::freeResources() {} + +bool AquaGraphicsBackend::setClipRegion(vcl::Region const& rRegion) +{ + // release old clip path + mrShared.unsetClipPath(); + mrShared.mxClipPath = CGPathCreateMutable(); + + // set current path, either as polypolgon or sequence of rectangles + RectangleVector aRectangles; + rRegion.GetRegionRectangles(aRectangles); + + for (const auto& rRect : aRectangles) + { + const tools::Long nW(rRect.Right() - rRect.Left() + 1); // uses +1 logic in original + + if (nW) + { + const tools::Long nH(rRect.Bottom() - rRect.Top() + 1); // uses +1 logic in original + + if (nH) + { + const CGRect aRect = CGRectMake(rRect.Left(), rRect.Top(), nW, nH); + CGPathAddRect(mrShared.mxClipPath, nullptr, aRect); + } + } + } + // set the current path as clip region + if (mrShared.checkContext()) + mrShared.setState(); + + return true; +} + +void AquaGraphicsBackend::ResetClipRegion() +{ + // release old path and indicate no clipping + mrShared.unsetClipPath(); + + if (mrShared.checkContext()) + { + mrShared.setState(); + } +} + +sal_uInt16 AquaGraphicsBackend::GetBitCount() const +{ + sal_uInt16 nBits = mrShared.mnBitmapDepth ? mrShared.mnBitmapDepth : 32; //24; + return nBits; +} + +tools::Long AquaGraphicsBackend::GetGraphicsWidth() const +{ + tools::Long width = 0; + if (mrShared.maContextHolder.isSet() + && ( +#ifndef IOS + mrShared.mbWindow || +#endif + mrShared.mbVirDev)) + { + width = mrShared.mnWidth; + } + +#ifndef IOS + if (width == 0) + { + if (mrShared.mbWindow && mrShared.mpFrame) + { + width = mrShared.mpFrame->maGeometry.nWidth; + } + } +#endif + return width; +} + +void AquaGraphicsBackend::SetLineColor() +{ + mrShared.maLineColor.SetAlpha(0.0); // transparent + if (mrShared.checkContext()) + { + CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), mrShared.maLineColor.GetRed(), + mrShared.maLineColor.GetGreen(), mrShared.maLineColor.GetBlue(), + mrShared.maLineColor.GetAlpha()); + } +} + +void AquaGraphicsBackend::SetLineColor(Color nColor) +{ + mrShared.maLineColor = RGBAColor(nColor); + if (mrShared.checkContext()) + { + CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), mrShared.maLineColor.GetRed(), + mrShared.maLineColor.GetGreen(), mrShared.maLineColor.GetBlue(), + mrShared.maLineColor.GetAlpha()); + } +} + +void AquaGraphicsBackend::SetFillColor() +{ + mrShared.maFillColor.SetAlpha(0.0); // transparent + if (mrShared.checkContext()) + { + CGContextSetRGBFillColor(mrShared.maContextHolder.get(), mrShared.maFillColor.GetRed(), + mrShared.maFillColor.GetGreen(), mrShared.maFillColor.GetBlue(), + mrShared.maFillColor.GetAlpha()); + } +} + +void AquaGraphicsBackend::SetFillColor(Color nColor) +{ + mrShared.maFillColor = RGBAColor(nColor); + if (mrShared.checkContext()) + { + CGContextSetRGBFillColor(mrShared.maContextHolder.get(), mrShared.maFillColor.GetRed(), + mrShared.maFillColor.GetGreen(), mrShared.maFillColor.GetBlue(), + mrShared.maFillColor.GetAlpha()); + } +} + +void AquaGraphicsBackend::SetXORMode(bool bSet, bool bInvertOnly) +{ + // return early if XOR mode remains unchanged + if (mrShared.mbPrinter) + { + return; + } + if (!bSet && mrShared.mnXorMode == 2) + { + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeNormal); + mrShared.mnXorMode = 0; + return; + } + else if (bSet && bInvertOnly && mrShared.mnXorMode == 0) + { + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + mrShared.mnXorMode = 2; + return; + } + + if (!mrShared.mpXorEmulation && !bSet) + { + return; + } + if (mrShared.mpXorEmulation && bSet == mrShared.mpXorEmulation->IsEnabled()) + { + return; + } + if (!mrShared.checkContext()) + { + return; + } + // prepare XOR emulation + if (!mrShared.mpXorEmulation) + { + mrShared.mpXorEmulation = std::make_unique<XorEmulation>(); + mrShared.mpXorEmulation->SetTarget(mrShared.mnWidth, mrShared.mnHeight, + mrShared.mnBitmapDepth, mrShared.maContextHolder.get(), + mrShared.maLayer.get()); + } + + // change the XOR mode + if (bSet) + { + mrShared.mpXorEmulation->Enable(); + mrShared.maContextHolder.set(mrShared.mpXorEmulation->GetMaskContext()); + mrShared.mnXorMode = 1; + } + else + { + mrShared.mpXorEmulation->UpdateTarget(); + mrShared.mpXorEmulation->Disable(); + mrShared.maContextHolder.set(mrShared.mpXorEmulation->GetTargetContext()); + mrShared.mnXorMode = 0; + } +} + +void AquaGraphicsBackend::SetROPFillColor(SalROPColor nROPColor) +{ + if (!mrShared.mbPrinter) + { + SetFillColor(ImplGetROPColor(nROPColor)); + } +} + +void AquaGraphicsBackend::SetROPLineColor(SalROPColor nROPColor) +{ + if (!mrShared.mbPrinter) + { + SetLineColor(ImplGetROPColor(nROPColor)); + } +} + +void AquaGraphicsBackend::drawPixelImpl(tools::Long nX, tools::Long nY, const RGBAColor& rColor) +{ + if (!mrShared.checkContext()) + return; + + // overwrite the fill color + CGContextSetFillColor(mrShared.maContextHolder.get(), rColor.AsArray()); + // draw 1x1 rect, there is no pixel drawing in Quartz + const CGRect aDstRect = CGRectMake(nX, nY, 1, 1); + CGContextFillRect(mrShared.maContextHolder.get(), aDstRect); + + refreshRect(aDstRect); + + // reset the fill color + CGContextSetFillColor(mrShared.maContextHolder.get(), mrShared.maFillColor.AsArray()); +} + +void AquaGraphicsBackend::drawPixel(tools::Long nX, tools::Long nY) +{ + // draw pixel with current line color + drawPixelImpl(nX, nY, mrShared.maLineColor); +} + +void AquaGraphicsBackend::drawPixel(tools::Long nX, tools::Long nY, Color nColor) +{ + const RGBAColor aPixelColor(nColor); + drawPixelImpl(nX, nY, aPixelColor); +} + +void AquaGraphicsBackend::drawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2, + tools::Long nY2) +{ + if (nX1 == nX2 && nY1 == nY2) + { + // #i109453# platform independent code expects at least one pixel to be drawn + drawPixel(nX1, nY1); + return; + } + + if (!mrShared.checkContext()) + return; + + CGContextBeginPath(mrShared.maContextHolder.get()); + CGContextMoveToPoint(mrShared.maContextHolder.get(), float(nX1) + 0.5, float(nY1) + 0.5); + CGContextAddLineToPoint(mrShared.maContextHolder.get(), float(nX2) + 0.5, float(nY2) + 0.5); + CGContextDrawPath(mrShared.maContextHolder.get(), kCGPathStroke); + + tools::Rectangle aRefreshRect(nX1, nY1, nX2, nY2); + (void)aRefreshRect; + // Is a call to RefreshRect( aRefreshRect ) missing here? +} + +void AquaGraphicsBackend::drawRect(tools::Long nX, tools::Long nY, tools::Long nWidth, + tools::Long nHeight) +{ + if (!mrShared.checkContext()) + return; + + CGRect aRect = CGRectMake(nX, nY, nWidth, nHeight); + if (mrShared.isPenVisible()) + { + aRect.origin.x += 0.5; + aRect.origin.y += 0.5; + aRect.size.width -= 1; + aRect.size.height -= 1; + } + + if (mrShared.isBrushVisible()) + { + CGContextFillRect(mrShared.maContextHolder.get(), aRect); + } + if (mrShared.isPenVisible()) + { + CGContextStrokeRect(mrShared.maContextHolder.get(), aRect); + } + mrShared.refreshRect(nX, nY, nWidth, nHeight); +} + +void AquaGraphicsBackend::drawPolyLine(sal_uInt32 nPoints, const Point* pPointArray) +{ + if (nPoints < 1) + return; + + if (!mrShared.checkContext()) + return; + + tools::Long nX = 0, nY = 0, nWidth = 0, nHeight = 0; + getBoundRect(nPoints, pPointArray, nX, nY, nWidth, nHeight); + + float fX, fY; + CGContextBeginPath(mrShared.maContextHolder.get()); + alignLinePoint(pPointArray, fX, fY); + CGContextMoveToPoint(mrShared.maContextHolder.get(), fX, fY); + pPointArray++; + + for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPointArray++) + { + alignLinePoint(pPointArray, fX, fY); + CGContextAddLineToPoint(mrShared.maContextHolder.get(), fX, fY); + } + CGContextStrokePath(mrShared.maContextHolder.get()); + + mrShared.refreshRect(nX, nY, nWidth, nHeight); +} + +void AquaGraphicsBackend::drawPolygon(sal_uInt32 nPoints, const Point* pPointArray) +{ + if (nPoints <= 1) + return; + + if (!mrShared.checkContext()) + return; + + tools::Long nX = 0, nY = 0, nWidth = 0, nHeight = 0; + getBoundRect(nPoints, pPointArray, nX, nY, nWidth, nHeight); + + CGPathDrawingMode eMode; + if (mrShared.isBrushVisible() && mrShared.isPenVisible()) + { + eMode = kCGPathEOFillStroke; + } + else if (mrShared.isPenVisible()) + { + eMode = kCGPathStroke; + } + else if (mrShared.isBrushVisible()) + { + eMode = kCGPathEOFill; + } + else + { + SAL_WARN("vcl.quartz", "Neither pen nor brush visible"); + return; + } + + CGContextBeginPath(mrShared.maContextHolder.get()); + + if (mrShared.isPenVisible()) + { + float fX, fY; + alignLinePoint(pPointArray, fX, fY); + CGContextMoveToPoint(mrShared.maContextHolder.get(), fX, fY); + pPointArray++; + for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPointArray++) + { + alignLinePoint(pPointArray, fX, fY); + CGContextAddLineToPoint(mrShared.maContextHolder.get(), fX, fY); + } + } + else + { + CGContextMoveToPoint(mrShared.maContextHolder.get(), pPointArray->getX(), + pPointArray->getY()); + pPointArray++; + for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPointArray++) + { + CGContextAddLineToPoint(mrShared.maContextHolder.get(), pPointArray->getX(), + pPointArray->getY()); + } + } + + CGContextClosePath(mrShared.maContextHolder.get()); + CGContextDrawPath(mrShared.maContextHolder.get(), eMode); + + mrShared.refreshRect(nX, nY, nWidth, nHeight); +} + +void AquaGraphicsBackend::drawPolyPolygon(sal_uInt32 nPolyCount, const sal_uInt32* pPoints, + const Point** ppPtAry) +{ + if (nPolyCount <= 0) + return; + + if (!mrShared.checkContext()) + return; + + // find bound rect + tools::Long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0; + + getBoundRect(pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight); + + for (sal_uInt32 n = 1; n < nPolyCount; n++) + { + tools::Long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight; + getBoundRect(pPoints[n], ppPtAry[n], nX, nY, nW, nH); + if (nX < leftX) + { + maxWidth += leftX - nX; + leftX = nX; + } + if (nY < topY) + { + maxHeight += topY - nY; + topY = nY; + } + if (nX + nW > leftX + maxWidth) + { + maxWidth = nX + nW - leftX; + } + if (nY + nH > topY + maxHeight) + { + maxHeight = nY + nH - topY; + } + } + + // prepare drawing mode + CGPathDrawingMode eMode; + if (mrShared.isBrushVisible() && mrShared.isPenVisible()) + { + eMode = kCGPathEOFillStroke; + } + else if (mrShared.isPenVisible()) + { + eMode = kCGPathStroke; + } + else if (mrShared.isBrushVisible()) + { + eMode = kCGPathEOFill; + } + else + { + SAL_WARN("vcl.quartz", "Neither pen nor brush visible"); + return; + } + + // convert to CGPath + CGContextBeginPath(mrShared.maContextHolder.get()); + if (mrShared.isPenVisible()) + { + for (sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++) + { + const sal_uInt32 nPoints = pPoints[nPoly]; + if (nPoints > 1) + { + const Point* pPtAry = ppPtAry[nPoly]; + float fX, fY; + + alignLinePoint(pPtAry, fX, fY); + CGContextMoveToPoint(mrShared.maContextHolder.get(), fX, fY); + pPtAry++; + + for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++) + { + alignLinePoint(pPtAry, fX, fY); + CGContextAddLineToPoint(mrShared.maContextHolder.get(), fX, fY); + } + CGContextClosePath(mrShared.maContextHolder.get()); + } + } + } + else + { + for (sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++) + { + const sal_uInt32 nPoints = pPoints[nPoly]; + if (nPoints > 1) + { + const Point* pPtAry = ppPtAry[nPoly]; + CGContextMoveToPoint(mrShared.maContextHolder.get(), pPtAry->getX(), + pPtAry->getY()); + pPtAry++; + for (sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++) + { + CGContextAddLineToPoint(mrShared.maContextHolder.get(), pPtAry->getX(), + pPtAry->getY()); + } + CGContextClosePath(mrShared.maContextHolder.get()); + } + } + } + + CGContextDrawPath(mrShared.maContextHolder.get(), eMode); + + mrShared.refreshRect(leftX, topY, maxWidth, maxHeight); +} + +bool AquaGraphicsBackend::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice, + const basegfx::B2DPolyPolygon& rPolyPolygon, + double fTransparency) +{ +#ifdef IOS + if (!mrShared.maContextHolder.isSet()) + return true; +#endif + + // short circuit if there is nothing to do + if (rPolyPolygon.count() == 0) + return true; + + // ignore invisible polygons + if ((fTransparency >= 1.0) || (fTransparency < 0)) + return true; + + // Fallback: Transform to DeviceCoordinates + basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon); + aPolyPolygon.transform(rObjectToDevice); + + // setup poly-polygon path + CGMutablePathRef xPath = CGPathCreateMutable(); + // tdf#120252 Use the correct, already transformed PolyPolygon (as long as + // the transformation is not used here...) + for (auto const& rPolygon : aPolyPolygon) + { + AddPolygonToPath(xPath, rPolygon, true, !getAntiAlias(), mrShared.isPenVisible()); + } + + const CGRect aRefreshRect = CGPathGetBoundingBox(xPath); + // #i97317# workaround for Quartz having problems with drawing small polygons + if (aRefreshRect.size.width > 0.125 || aRefreshRect.size.height > 0.125) + { + // prepare drawing mode + CGPathDrawingMode eMode; + if (mrShared.isBrushVisible() && mrShared.isPenVisible()) + { + eMode = kCGPathEOFillStroke; + } + else if (mrShared.isPenVisible()) + { + eMode = kCGPathStroke; + } + else if (mrShared.isBrushVisible()) + { + eMode = kCGPathEOFill; + } + else + { + SAL_WARN("vcl.quartz", "Neither pen nor brush visible"); + CGPathRelease(xPath); + return true; + } + + // use the path to prepare the graphics context + mrShared.maContextHolder.saveState(); + CGContextBeginPath(mrShared.maContextHolder.get()); + CGContextAddPath(mrShared.maContextHolder.get(), xPath); + + // draw path with antialiased polygon + CGContextSetShouldAntialias(mrShared.maContextHolder.get(), getAntiAlias()); + CGContextSetAlpha(mrShared.maContextHolder.get(), 1.0 - fTransparency); + CGContextDrawPath(mrShared.maContextHolder.get(), eMode); + mrShared.maContextHolder.restoreState(); + + // mark modified rectangle as updated + refreshRect(aRefreshRect); + } + + CGPathRelease(xPath); + + return true; +} + +bool AquaGraphicsBackend::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice, + const basegfx::B2DPolygon& rPolyLine, double fTransparency, + double fLineWidth, + const std::vector<double>* pStroke, // MM01 + basegfx::B2DLineJoin eLineJoin, + css::drawing::LineCap eLineCap, double fMiterMinimumAngle, + bool bPixelSnapHairline) +{ + // MM01 check done for simple reasons + if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0) + { + return true; + } + +#ifdef IOS + if (!mrShared.checkContext()) + return false; +#endif + + // tdf#124848 get correct LineWidth in discrete coordinates, + if (fLineWidth == 0) // hairline + fLineWidth = 1.0; + else // Adjust line width for object-to-device scale. + fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength(); + + // #i101491# Aqua does not support B2DLineJoin::NONE; return false to use + // the fallback (own geometry preparation) + // #i104886# linejoin-mode and thus the above only applies to "fat" lines + if ((basegfx::B2DLineJoin::NONE == eLineJoin) && (fLineWidth > 1.3)) + return false; + + // MM01 need to do line dashing as fallback stuff here now + const double fDotDashLength( + nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0); + const bool bStrokeUsed(0.0 != fDotDashLength); + assert(!bStrokeUsed || (bStrokeUsed && pStroke)); + basegfx::B2DPolyPolygon aPolyPolygonLine; + + if (bStrokeUsed) + { + // apply LineStyle + basegfx::utils::applyLineDashing(rPolyLine, // source + *pStroke, // pattern + &aPolyPolygonLine, // target for lines + nullptr, // target for gaps + fDotDashLength); // full length if available + } + else + { + // no line dashing, just copy + aPolyPolygonLine.append(rPolyLine); + } + + // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline + aPolyPolygonLine.transform(rObjectToDevice); + if (bPixelSnapHairline) + { + aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); + } + + // setup line attributes + CGLineJoin aCGLineJoin = kCGLineJoinMiter; + switch (eLineJoin) + { + case basegfx::B2DLineJoin::NONE: + aCGLineJoin = /*TODO?*/ kCGLineJoinMiter; + break; + case basegfx::B2DLineJoin::Bevel: + aCGLineJoin = kCGLineJoinBevel; + break; + case basegfx::B2DLineJoin::Miter: + aCGLineJoin = kCGLineJoinMiter; + break; + case basegfx::B2DLineJoin::Round: + aCGLineJoin = kCGLineJoinRound; + break; + } + // convert miter minimum angle to miter limit + CGFloat fCGMiterLimit = 1.0 / sin(fMiterMinimumAngle / 2.0); + // setup cap attribute + CGLineCap aCGLineCap(kCGLineCapButt); + + switch (eLineCap) + { + default: // css::drawing::LineCap_BUTT: + { + aCGLineCap = kCGLineCapButt; + break; + } + case css::drawing::LineCap_ROUND: + { + aCGLineCap = kCGLineCapRound; + break; + } + case css::drawing::LineCap_SQUARE: + { + aCGLineCap = kCGLineCapSquare; + break; + } + } + + // setup poly-polygon path + CGMutablePathRef xPath = CGPathCreateMutable(); + + // MM01 todo - I assume that this is OKAY to be done in one run for quartz + // but this NEEDS to be checked/verified + for (sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++) + { + const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a)); + AddPolygonToPath(xPath, aPolyLine, aPolyLine.isClosed(), !getAntiAlias(), true); + } + + const CGRect aRefreshRect = CGPathGetBoundingBox(xPath); + // #i97317# workaround for Quartz having problems with drawing small polygons + if ((aRefreshRect.size.width > 0.125) || (aRefreshRect.size.height > 0.125)) + { + // use the path to prepare the graphics context + mrShared.maContextHolder.saveState(); + CGContextBeginPath(mrShared.maContextHolder.get()); + CGContextAddPath(mrShared.maContextHolder.get(), xPath); + // draw path with antialiased line + CGContextSetShouldAntialias(mrShared.maContextHolder.get(), getAntiAlias()); + CGContextSetAlpha(mrShared.maContextHolder.get(), 1.0 - fTransparency); + CGContextSetLineJoin(mrShared.maContextHolder.get(), aCGLineJoin); + CGContextSetLineCap(mrShared.maContextHolder.get(), aCGLineCap); + CGContextSetLineWidth(mrShared.maContextHolder.get(), fLineWidth); + CGContextSetMiterLimit(mrShared.maContextHolder.get(), fCGMiterLimit); + CGContextDrawPath(mrShared.maContextHolder.get(), kCGPathStroke); + mrShared.maContextHolder.restoreState(); + + // mark modified rectangle as updated + refreshRect(aRefreshRect); + } + + CGPathRelease(xPath); + + return true; +} + +bool AquaGraphicsBackend::drawPolyLineBezier(sal_uInt32 /*nPoints*/, const Point* /*pPointArray*/, + const PolyFlags* /*pFlagArray*/) +{ + return false; +} + +bool AquaGraphicsBackend::drawPolygonBezier(sal_uInt32 /*nPoints*/, const Point* /*pPointArray*/, + const PolyFlags* /*pFlagArray*/) +{ + return false; +} + +bool AquaGraphicsBackend::drawPolyPolygonBezier(sal_uInt32 /*nPoly*/, const sal_uInt32* /*pPoints*/, + const Point* const* /*pPointArray*/, + const PolyFlags* const* /*pFlagArray*/) +{ + return false; +} + +void AquaGraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap) +{ + if (!mrShared.checkContext()) + return; + + const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap); + CGImageRef xImage = rBitmap.CreateCroppedImage( + static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY), + static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight)); + if (!xImage) + return; + + const CGRect aDstRect + = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); + CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xImage); + + CGImageRelease(xImage); + refreshRect(aDstRect); +} + +void AquaGraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, + const SalBitmap& rTransparentBitmap) +{ + if (!mrShared.checkContext()) + return; + + const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap); + const QuartzSalBitmap& rMask = static_cast<const QuartzSalBitmap&>(rTransparentBitmap); + + CGImageRef xMaskedImage(rBitmap.CreateWithMask(rMask, rPosAry.mnSrcX, rPosAry.mnSrcY, + rPosAry.mnSrcWidth, rPosAry.mnSrcHeight)); + if (!xMaskedImage) + return; + + const CGRect aDstRect + = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); + CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xMaskedImage); + CGImageRelease(xMaskedImage); + refreshRect(aDstRect); +} + +void AquaGraphicsBackend::drawMask(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, + Color nMaskColor) +{ + if (!mrShared.checkContext()) + return; + + const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap); + CGImageRef xImage = rBitmap.CreateColorMask(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, + rPosAry.mnSrcHeight, nMaskColor); + if (!xImage) + return; + + const CGRect aDstRect + = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); + CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xImage); + CGImageRelease(xImage); + refreshRect(aDstRect); +} + +std::shared_ptr<SalBitmap> AquaGraphicsBackend::getBitmap(tools::Long nX, tools::Long nY, + tools::Long nDX, tools::Long nDY) +{ + SAL_WARN_IF(!mrShared.maLayer.isSet(), "vcl.quartz", + "AquaSalGraphics::getBitmap() with no layer this=" << this); + + mrShared.applyXorContext(); + + std::shared_ptr<QuartzSalBitmap> pBitmap = std::make_shared<QuartzSalBitmap>(); + if (!pBitmap->Create(mrShared.maLayer, mrShared.mnBitmapDepth, nX, nY, nDX, nDY, + mrShared.isFlipped())) + { + pBitmap = nullptr; + } + return pBitmap; +} + +Color AquaGraphicsBackend::getPixel(tools::Long nX, tools::Long nY) +{ + // return default value on printers or when out of bounds + if (!mrShared.maLayer.isSet() || (nX < 0) || (nX >= mrShared.mnWidth) || (nY < 0) + || (nY >= mrShared.mnHeight)) + { + return COL_BLACK; + } + + // prepare creation of matching a CGBitmapContext +#if defined OSL_BIGENDIAN + struct + { + unsigned char b, g, r, a; + } aPixel; +#else + struct + { + unsigned char a, r, g, b; + } aPixel; +#endif + + // create a one-pixel bitmap context + // TODO: is it worth to cache it? + CGContextRef xOnePixelContext = CGBitmapContextCreate( + &aPixel, 1, 1, 8, 32, GetSalData()->mxRGBSpace, + uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Big)); + + // update this graphics layer + mrShared.applyXorContext(); + + // copy the requested pixel into the bitmap context + if (mrShared.isFlipped()) + { + nY = mrShared.mnHeight - nY; + } + const CGPoint aCGPoint = CGPointMake(-nX, -nY); + CGContextDrawLayerAtPoint(xOnePixelContext, aCGPoint, mrShared.maLayer.get()); + + CGContextRelease(xOnePixelContext); + + Color nColor(aPixel.r, aPixel.g, aPixel.b); + return nColor; +} + +void AquaSalGraphics::GetResolution(sal_Int32& rDPIX, sal_Int32& rDPIY) +{ +#ifndef IOS + if (!mnRealDPIY) + { + initResolution((maShared.mbWindow && maShared.mpFrame) ? maShared.mpFrame->getNSWindow() + : nil); + } + + rDPIX = mnRealDPIX; + rDPIY = mnRealDPIY; +#else + // This *must* be 96 or else the iOS app will behave very badly (tiles are scaled wrongly and + // don't match each others at their boundaries, and other issues). But *why* it must be 96 I + // have no idea. The commit that changed it to 96 from (the arbitrary) 200 did not say. If you + // know where else 96 is explicitly or implicitly hard-coded, please modify this comment. + + // Follow-up: It might be this: in 'online', loleaflet/src/map/Map.js: + // 15 = 1440 twips-per-inch / 96 dpi. + // Chosen to match previous hardcoded value of 3840 for + // the current tile pixel size of 256. + rDPIX = rDPIY = 96; +#endif +} + +void AquaGraphicsBackend::pattern50Fill() +{ + static const CGFloat aFillCol[4] = { 1, 1, 1, 1 }; + static const CGPatternCallbacks aCallback = { 0, &drawPattern50, nullptr }; + static const CGColorSpaceRef mxP50Space = CGColorSpaceCreatePattern(GetSalData()->mxRGBSpace); + static const CGPatternRef mxP50Pattern + = CGPatternCreate(nullptr, CGRectMake(0, 0, 4, 4), CGAffineTransformIdentity, 4, 4, + kCGPatternTilingConstantSpacing, false, &aCallback); + SAL_WARN_IF(!mrShared.maContextHolder.get(), "vcl.quartz", "maContextHolder.get() is NULL"); + CGContextSetFillColorSpace(mrShared.maContextHolder.get(), mxP50Space); + CGContextSetFillPattern(mrShared.maContextHolder.get(), mxP50Pattern, aFillCol); + CGContextFillPath(mrShared.maContextHolder.get()); +} + +void AquaGraphicsBackend::invert(tools::Long nX, tools::Long nY, tools::Long nWidth, + tools::Long nHeight, SalInvert nFlags) +{ + if (mrShared.checkContext()) + { + CGRect aCGRect = CGRectMake(nX, nY, nWidth, nHeight); + mrShared.maContextHolder.saveState(); + if (nFlags & SalInvert::TrackFrame) + { + const CGFloat dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0); + CGContextSetLineDash(mrShared.maContextHolder.get(), 0, dashLengths, 2); + CGContextSetLineWidth(mrShared.maContextHolder.get(), 2.0); + CGContextStrokeRect(mrShared.maContextHolder.get(), aCGRect); + } + else if (nFlags & SalInvert::N50) + { + //CGContextSetAllowsAntialiasing( maContextHolder.get(), false ); + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + CGContextAddRect(mrShared.maContextHolder.get(), aCGRect); + pattern50Fill(); + } + else // just invert + { + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + CGContextSetRGBFillColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0); + CGContextFillRect(mrShared.maContextHolder.get(), aCGRect); + } + mrShared.maContextHolder.restoreState(); + refreshRect(aCGRect); + } +} + +namespace +{ +CGPoint* makeCGptArray(sal_uInt32 nPoints, const Point* pPtAry) +{ + CGPoint* CGpoints = new CGPoint[nPoints]; + for (sal_uLong i = 0; i < nPoints; i++) + { + CGpoints[i].x = pPtAry[i].getX(); + CGpoints[i].y = pPtAry[i].getY(); + } + return CGpoints; +} + +} // end anonymous ns + +void AquaGraphicsBackend::invert(sal_uInt32 nPoints, const Point* pPtAry, SalInvert nSalFlags) +{ + if (mrShared.checkContext()) + { + mrShared.maContextHolder.saveState(); + CGPoint* CGpoints = makeCGptArray(nPoints, pPtAry); + CGContextAddLines(mrShared.maContextHolder.get(), CGpoints, nPoints); + if (nSalFlags & SalInvert::TrackFrame) + { + const CGFloat dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + CGContextSetRGBStrokeColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0); + CGContextSetLineDash(mrShared.maContextHolder.get(), 0, dashLengths, 2); + CGContextSetLineWidth(mrShared.maContextHolder.get(), 2.0); + CGContextStrokePath(mrShared.maContextHolder.get()); + } + else if (nSalFlags & SalInvert::N50) + { + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + pattern50Fill(); + } + else // just invert + { + CGContextSetBlendMode(mrShared.maContextHolder.get(), kCGBlendModeDifference); + CGContextSetRGBFillColor(mrShared.maContextHolder.get(), 1.0, 1.0, 1.0, 1.0); + CGContextFillPath(mrShared.maContextHolder.get()); + } + const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrShared.maContextHolder.get()); + mrShared.maContextHolder.restoreState(); + delete[] CGpoints; + refreshRect(aRefreshRect); + } +} + +#ifndef IOS +bool AquaGraphicsBackend::drawEPS(tools::Long nX, tools::Long nY, tools::Long nWidth, + tools::Long nHeight, void* pEpsData, sal_uInt32 nByteCount) +{ + // convert the raw data to an NSImageRef + NSData* xNSData = [NSData dataWithBytes:pEpsData length:static_cast<int>(nByteCount)]; + NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData:xNSData]; + if (!xEpsImage) + { + return false; + } + // get the target context + if (!mrShared.checkContext()) + { + return false; + } + // NOTE: flip drawing, else the nsimage would be drawn upside down + mrShared.maContextHolder.saveState(); + // CGContextTranslateCTM( maContextHolder.get(), 0, +mnHeight ); + CGContextScaleCTM(mrShared.maContextHolder.get(), +1, -1); + nY = /*mnHeight*/ -(nY + nHeight); + + // prepare the target context + NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext]; + [pOrigNSCtx retain]; + + // create new context + NSGraphicsContext* pDrawNSCtx = + [NSGraphicsContext graphicsContextWithCGContext:mrShared.maContextHolder.get() + flipped:mrShared.isFlipped()]; + // set it, setCurrentContext also releases the previously set one + [NSGraphicsContext setCurrentContext:pDrawNSCtx]; + + // draw the EPS + const NSRect aDstRect = NSMakeRect(nX, nY, nWidth, nHeight); + const bool bOK = [xEpsImage drawInRect:aDstRect]; + + // restore the NSGraphicsContext + [NSGraphicsContext setCurrentContext:pOrigNSCtx]; + [pOrigNSCtx release]; // restore the original retain count + + mrShared.maContextHolder.restoreState(); + // mark the destination rectangle as updated + refreshRect(aDstRect); + + return bOK; +} +#else +bool AquaGraphicsBackend::drawEPS(tools::Long /*nX*/, tools::Long /*nY*/, tools::Long /*nWidth*/, + tools::Long /*nHeight*/, void* /*pEpsData*/, + sal_uInt32 /*nByteCount*/) +{ + return false; +} +#endif + +bool AquaGraphicsBackend::blendBitmap(const SalTwoRect& /*rPosAry*/, const SalBitmap& /*rBitmap*/) +{ + return false; +} + +bool AquaGraphicsBackend::blendAlphaBitmap(const SalTwoRect& /*rPosAry*/, + const SalBitmap& /*rSrcBitmap*/, + const SalBitmap& /*rMaskBitmap*/, + const SalBitmap& /*rAlphaBitmap*/) +{ + return false; +} + +bool AquaGraphicsBackend::drawAlphaBitmap(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap, + const SalBitmap& rAlphaBmp) +{ + // An image mask can't have a depth > 8 bits (should be 1 to 8 bits) + if (rAlphaBmp.GetBitCount() > 8) + return false; + + // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx) + // horizontal/vertical mirroring not implemented yet + if (rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0) + return false; + + const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap); + const QuartzSalBitmap& rMaskSalBmp = static_cast<const QuartzSalBitmap&>(rAlphaBmp); + CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask(rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY, + rTR.mnSrcWidth, rTR.mnSrcHeight); + if (!xMaskedImage) + return false; + + if (mrShared.checkContext()) + { + const CGRect aDstRect + = CGRectMake(rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight); + CGContextDrawImage(mrShared.maContextHolder.get(), aDstRect, xMaskedImage); + refreshRect(aDstRect); + } + + CGImageRelease(xMaskedImage); + + return true; +} + +bool AquaGraphicsBackend::drawTransformedBitmap(const basegfx::B2DPoint& rNull, + const basegfx::B2DPoint& rX, + const basegfx::B2DPoint& rY, + const SalBitmap& rSrcBitmap, + const SalBitmap* pAlphaBmp, double fAlpha) +{ + if (!mrShared.checkContext()) + return true; + + if (fAlpha != 1.0) + return false; + + // get the Quartz image + CGImageRef xImage = nullptr; + const Size aSize = rSrcBitmap.GetSize(); + const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap); + const QuartzSalBitmap* pMaskSalBmp = static_cast<const QuartzSalBitmap*>(pAlphaBmp); + + if (!pMaskSalBmp) + xImage = rSrcSalBmp.CreateCroppedImage(0, 0, int(aSize.Width()), int(aSize.Height())); + else + xImage = rSrcSalBmp.CreateWithMask(*pMaskSalBmp, 0, 0, int(aSize.Width()), + int(aSize.Height())); + + if (!xImage) + return false; + + // setup the image transformation + // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points + mrShared.maContextHolder.saveState(); + const basegfx::B2DVector aXRel = rX - rNull; + const basegfx::B2DVector aYRel = rY - rNull; + const CGAffineTransform aCGMat = CGAffineTransformMake( + aXRel.getX() / aSize.Width(), aXRel.getY() / aSize.Width(), aYRel.getX() / aSize.Height(), + aYRel.getY() / aSize.Height(), rNull.getX(), rNull.getY()); + + CGContextConcatCTM(mrShared.maContextHolder.get(), aCGMat); + + // draw the transformed image + const CGRect aSrcRect = CGRectMake(0, 0, aSize.Width(), aSize.Height()); + CGContextDrawImage(mrShared.maContextHolder.get(), aSrcRect, xImage); + + CGImageRelease(xImage); + + // restore the Quartz graphics state + mrShared.maContextHolder.restoreState(); + + // mark the destination as painted + const CGRect aDstRect = CGRectApplyAffineTransform(aSrcRect, aCGMat); + refreshRect(aDstRect); + + return true; +} + +bool AquaGraphicsBackend::hasFastDrawTransformedBitmap() const { return false; } + +bool AquaGraphicsBackend::drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long nWidth, + tools::Long nHeight, sal_uInt8 nTransparency) +{ + if (!mrShared.checkContext()) + return true; + + // save the current state + mrShared.maContextHolder.saveState(); + CGContextSetAlpha(mrShared.maContextHolder.get(), (100 - nTransparency) * (1.0 / 100)); + + CGRect aRect = CGRectMake(nX, nY, nWidth - 1, nHeight - 1); + if (mrShared.isPenVisible()) + { + aRect.origin.x += 0.5; + aRect.origin.y += 0.5; + } + + CGContextBeginPath(mrShared.maContextHolder.get()); + CGContextAddRect(mrShared.maContextHolder.get(), aRect); + CGContextDrawPath(mrShared.maContextHolder.get(), kCGPathFill); + + mrShared.maContextHolder.restoreState(); + refreshRect(aRect); + + return true; +} + +bool AquaGraphicsBackend::drawGradient(const tools::PolyPolygon& /*rPolygon*/, + const Gradient& /*rGradient*/) +{ + return false; +} + +bool AquaGraphicsBackend::implDrawGradient(basegfx::B2DPolyPolygon const& /*rPolyPolygon*/, + SalGradient const& /*rGradient*/) +{ + return false; +} + +bool AquaGraphicsBackend::supportsOperation(OutDevSupportType eType) const +{ + switch (eType) + { + case OutDevSupportType::TransparentRect: + case OutDevSupportType::B2DDraw: + return true; + default: + break; + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index 375fea64a5cb..c938cf772030 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -186,27 +186,11 @@ bool CoreTextFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilit } AquaSalGraphics::AquaSalGraphics() - : mnXorMode( 0 ) - , mnWidth( 0 ) - , mnHeight( 0 ) - , mnBitmapDepth( 0 ) + : mpBackend(new AquaGraphicsBackend(maShared)) , mnRealDPIX( 0 ) , mnRealDPIY( 0 ) - , mxClipPath( nullptr ) - , maLineColor( COL_WHITE ) - , maFillColor( COL_BLACK ) , maTextColor( COL_BLACK ) , mbNonAntialiasedText( false ) -#ifdef MACOSX - , mpFrame( nullptr ) -#endif - , mbPrinter( false ) - , mbVirDev( false ) -#ifdef MACOSX - , mbWindow( false ) -#else - , mbForeignContext( false ) -#endif { SAL_INFO( "vcl.quartz", "AquaSalGraphics::AquaSalGraphics() this=" << this ); @@ -221,38 +205,35 @@ AquaSalGraphics::~AquaSalGraphics() { SAL_INFO( "vcl.quartz", "AquaSalGraphics::~AquaSalGraphics() this=" << this ); - if( mxClipPath ) - { - CGPathRelease( mxClipPath ); - } + maShared.unsetClipPath(); ReleaseFonts(); - mpXorEmulation.reset(); + maShared.mpXorEmulation.reset(); #ifdef IOS - if (mbForeignContext) + if (maShared.mbForeignContext) return; #endif - if (maLayer.isSet()) + if (maShared.maLayer.isSet()) { - CGLayerRelease(maLayer.get()); + CGLayerRelease(maShared.maLayer.get()); } - else if (maContextHolder.isSet() + else if (maShared.maContextHolder.isSet() #ifdef MACOSX - && mbWindow + && maShared.mbWindow #endif ) { // destroy backbuffer bitmap context that we created ourself - CGContextRelease(maContextHolder.get()); - maContextHolder.set(nullptr); + CGContextRelease(maShared.maContextHolder.get()); + maShared.maContextHolder.set(nullptr); } } SalGraphicsImpl* AquaSalGraphics::GetImpl() const { - return nullptr; + return mpBackend.get(); } void AquaSalGraphics::SetTextColor( Color nColor ) @@ -370,7 +351,7 @@ bool AquaSalGraphics::AddTempDevFont( PhysicalFontCollection*, void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) { #ifdef IOS - if (!CheckContext()) + if (!maShared.checkContext()) { SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout() without context"); return; @@ -444,20 +425,20 @@ void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) std::cerr << "]\n"; #endif - maContextHolder.saveState(); + maShared.maContextHolder.saveState(); // The view is vertically flipped (no idea why), flip it back. - CGContextScaleCTM(maContextHolder.get(), 1.0, -1.0); - CGContextSetShouldAntialias(maContextHolder.get(), !mbNonAntialiasedText); - CGContextSetFillColor(maContextHolder.get(), maTextColor.AsArray()); + CGContextScaleCTM(maShared.maContextHolder.get(), 1.0, -1.0); + CGContextSetShouldAntialias(maShared.maContextHolder.get(), !mbNonAntialiasedText); + CGContextSetFillColor(maShared.maContextHolder.get(), maTextColor.AsArray()); if (rStyle.mbFauxBold) { float fSize = rFontSelect.mnHeight / 23.0f; - CGContextSetStrokeColor(maContextHolder.get(), maTextColor.AsArray()); - CGContextSetLineWidth(maContextHolder.get(), fSize); - CGContextSetTextDrawingMode(maContextHolder.get(), kCGTextFillStroke); + CGContextSetStrokeColor(maShared.maContextHolder.get(), maTextColor.AsArray()); + CGContextSetLineWidth(maShared.maContextHolder.get(), fSize); + CGContextSetTextDrawingMode(maShared.maContextHolder.get(), kCGTextFillStroke); } auto aIt = aGlyphOrientation.cbegin(); @@ -470,18 +451,18 @@ void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) size_t nStartIndex = std::distance(aGlyphOrientation.cbegin(), aIt); size_t nLen = std::distance(aIt, aNext); - maContextHolder.saveState(); + maShared.maContextHolder.saveState(); if (rStyle.mfFontRotation && !bUprightGlyph) { - CGContextRotateCTM(maContextHolder.get(), rStyle.mfFontRotation); + CGContextRotateCTM(maShared.maContextHolder.get(), rStyle.mfFontRotation); } - CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, maContextHolder.get()); - maContextHolder.restoreState(); + CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, maShared.maContextHolder.get()); + maShared.maContextHolder.restoreState(); aIt = aNext; } - maContextHolder.restoreState(); + maShared.maContextHolder.restoreState(); } void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int nFallbackLevel) @@ -780,46 +761,9 @@ void AquaSalGraphics::FreeEmbedFontData( const void* pData, tools::Long /*nDataL SAL_WARN_IF( (pData==nullptr), "vcl", "AquaSalGraphics::FreeEmbedFontData() is not implemented"); } -bool AquaSalGraphics::IsFlipped() const -{ -#ifdef MACOSX - return mbWindow; -#else - return false; -#endif -} - -void AquaSalGraphics::RefreshRect(float lX, float lY, float lWidth, float lHeight) -{ -#ifdef MACOSX - if( ! mbWindow ) // view only on Window graphics - return; - - if( mpFrame ) - { - // update a little more around the designated rectangle - // this helps with antialiased rendering - // Rounding down x and width can accumulate a rounding error of up to 2 - // The decrementing of x, the rounding error and the antialiasing border - // require that the width and the height need to be increased by four - const tools::Rectangle aVclRect(Point(static_cast<tools::Long>(lX-1), - static_cast<tools::Long>(lY-1) ), - Size( static_cast<tools::Long>(lWidth+4), - static_cast<tools::Long>(lHeight+4) ) ); - mpFrame->maInvalidRect.Union( aVclRect ); - } -#else - (void) lX; - (void) lY; - (void) lWidth; - (void) lHeight; - return; -#endif -} - #ifdef IOS -bool AquaSalGraphics::CheckContext() +bool AquaSharedAttributes::checkContext() { if (mbForeignContext) { diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx index db6715ce01e2..fec61453367d 100644 --- a/vcl/quartz/salgdicommon.cxx +++ b/vcl/quartz/salgdicommon.cxx @@ -46,87 +46,6 @@ using namespace vcl; -const basegfx::B2DPoint aHalfPointOfs ( 0.5, 0.5 ); - -static void AddPolygonToPath( CGMutablePathRef xPath, - const basegfx::B2DPolygon& rPolygon, - bool bClosePath, bool bPixelSnap, bool bLineDraw ) -{ - // short circuit if there is nothing to do - const int nPointCount = rPolygon.count(); - if( nPointCount <= 0 ) - { - return; - } - - const bool bHasCurves = rPolygon.areControlPointsUsed(); - for( int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++ ) - { - int nClosedIdx = nPointIdx; - if( nPointIdx >= nPointCount ) - { - // prepare to close last curve segment if needed - if( bClosePath && (nPointIdx == nPointCount) ) - { - nClosedIdx = 0; - } - else - { - break; - } - } - - basegfx::B2DPoint aPoint = rPolygon.getB2DPoint( nClosedIdx ); - - if( bPixelSnap) - { - // snap device coordinates to full pixels - aPoint.setX( basegfx::fround( aPoint.getX() ) ); - aPoint.setY( basegfx::fround( aPoint.getY() ) ); - } - - if( bLineDraw ) - { - aPoint += aHalfPointOfs; - } - if( !nPointIdx ) - { - // first point => just move there - CGPathMoveToPoint( xPath, nullptr, aPoint.getX(), aPoint.getY() ); - continue; - } - - bool bPendingCurve = false; - if( bHasCurves ) - { - bPendingCurve = rPolygon.isNextControlPointUsed( nPrevIdx ); - bPendingCurve |= rPolygon.isPrevControlPointUsed( nClosedIdx ); - } - - if( !bPendingCurve ) // line segment - { - CGPathAddLineToPoint( xPath, nullptr, aPoint.getX(), aPoint.getY() ); - } - else // cubic bezier segment - { - basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint( nPrevIdx ); - basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint( nClosedIdx ); - if( bLineDraw ) - { - aCP1 += aHalfPointOfs; - aCP2 += aHalfPointOfs; - } - CGPathAddCurveToPoint( xPath, nullptr, aCP1.getX(), aCP1.getY(), - aCP2.getX(), aCP2.getY(), aPoint.getX(), aPoint.getY() ); - } - } - - if( bClosePath ) - { - CGPathCloseSubpath( xPath ); - } -} - bool AquaSalGraphics::CreateFontSubset( const OUString& rToFile, const PhysicalFontFace* pFontData, const sal_GlyphId* pGlyphIds, const sal_uInt8* pEncoding, @@ -178,87 +97,13 @@ bool AquaSalGraphics::CreateFontSubset( const OUString& rToFile, return bRet; } -static void alignLinePoint( const Point* i_pIn, float& o_fX, float& o_fY ) -{ - o_fX = static_cast<float>(i_pIn->getX() ) + 0.5; - o_fY = static_cast<float>(i_pIn->getY() ) + 0.5; -} - -static void DrawPattern50( void*, CGContextRef rContext ) -{ - static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } }; - CGContextAddRects( rContext, aRects, 2 ); - CGContextFillPath( rContext ); -} - -static void getBoundRect( sal_uInt32 nPoints, const Point *pPtAry, - tools::Long &rX, tools::Long& rY, tools::Long& rWidth, - tools::Long& rHeight ) -{ - tools::Long nX1 = pPtAry->getX(); - tools::Long nX2 = nX1; - tools::Long nY1 = pPtAry->getY(); - tools::Long nY2 = nY1; - - for( sal_uInt32 n = 1; n < nPoints; n++ ) - { - if( pPtAry[n].getX() < nX1 ) - { - nX1 = pPtAry[n].getX(); - } - else if( pPtAry[n].getX() > nX2 ) - { - nX2 = pPtAry[n].getX(); - } - if( pPtAry[n].getY() < nY1 ) - { - nY1 = pPtAry[n].getY(); - } - else if( pPtAry[n].getY() > nY2 ) - { - nY2 = pPtAry[n].getY(); - } - } - rX = nX1; - rY = nY1; - rWidth = nX2 - nX1 + 1; - rHeight = nY2 - nY1 + 1; -} - -static Color ImplGetROPColor( SalROPColor nROPColor ) -{ - Color nColor; - if ( nROPColor == SalROPColor::N0 ) - { - nColor = Color( 0, 0, 0 ); - } - else - { - nColor = Color( 255, 255, 255 ); - } - return nColor; -} - -// apply the XOR mask to the target context if active and dirty -void AquaSalGraphics::ApplyXorContext() -{ - if (!mpXorEmulation) - { - return; - } - if (mpXorEmulation->UpdateTarget()) - { - RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect - } -} - #ifndef IOS void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) { - if( !rGraphics.mnRealDPIY && rGraphics.mbWindow && rGraphics.mpFrame ) + if (!rGraphics.mnRealDPIY && rGraphics.maShared.mbWindow && rGraphics.maShared.mpFrame) { - rGraphics.initResolution( rGraphics.mpFrame->getNSWindow() ); + rGraphics.initResolution(rGraphics.maShared.mpFrame->getNSWindow()); } mnRealDPIX = rGraphics.mnRealDPIX; mnRealDPIY = rGraphics.mnRealDPIY; @@ -266,852 +111,14 @@ void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) #endif -bool AquaSalGraphics::blendBitmap( const SalTwoRect&, - const SalBitmap& ) -{ - return false; -} - -bool AquaSalGraphics::blendAlphaBitmap( const SalTwoRect&, - const SalBitmap&, - const SalBitmap&, - const SalBitmap& ) -{ - return false; -} - -bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, - const SalBitmap& rSrcBitmap, - const SalBitmap& rAlphaBmp ) -{ - // An image mask can't have a depth > 8 bits (should be 1 to 8 bits) - if( rAlphaBmp.GetBitCount() > 8 ) - return false; - - // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx) - // horizontal/vertical mirroring not implemented yet - if( rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0 ) - return false; - - const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap); - const QuartzSalBitmap& rMaskSalBmp = static_cast<const QuartzSalBitmap&>(rAlphaBmp); - CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask( rMaskSalBmp, rTR.mnSrcX, - rTR.mnSrcY, rTR.mnSrcWidth, - rTR.mnSrcHeight ); - if( !xMaskedImage ) - return false; - - if ( CheckContext() ) - { - const CGRect aDstRect = CGRectMake( rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight); - CGContextDrawImage( maContextHolder.get(), aDstRect, xMaskedImage ); - RefreshRect( aDstRect ); - } - - CGImageRelease(xMaskedImage); - - return true; -} - -bool AquaSalGraphics::drawTransformedBitmap( - const basegfx::B2DPoint& rNull, const basegfx::B2DPoint& rX, const basegfx::B2DPoint& rY, - const SalBitmap& rSrcBitmap, const SalBitmap* pAlphaBmp, double fAlpha ) -{ - if( !CheckContext() ) - return true; - - if( fAlpha != 1.0 ) - return false; - - // get the Quartz image - CGImageRef xImage = nullptr; - const Size aSize = rSrcBitmap.GetSize(); - const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap); - const QuartzSalBitmap* pMaskSalBmp = static_cast<const QuartzSalBitmap*>(pAlphaBmp); - - if( !pMaskSalBmp) - xImage = rSrcSalBmp.CreateCroppedImage( 0, 0, static_cast<int>(aSize.Width()), static_cast<int>(aSize.Height()) ); - else - xImage = rSrcSalBmp.CreateWithMask( *pMaskSalBmp, 0, 0, static_cast<int>(aSize.Width()), static_cast<int>(aSize.Height()) ); - if( !xImage ) - return false; - - // setup the image transformation - // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points - maContextHolder.saveState(); - const basegfx::B2DVector aXRel = rX - rNull; - const basegfx::B2DVector aYRel = rY - rNull; - const CGAffineTransform aCGMat = CGAffineTransformMake( - aXRel.getX()/aSize.Width(), aXRel.getY()/aSize.Width(), - aYRel.getX()/aSize.Height(), aYRel.getY()/aSize.Height(), - rNull.getX(), rNull.getY()); - - CGContextConcatCTM( maContextHolder.get(), aCGMat ); - - // draw the transformed image - const CGRect aSrcRect = CGRectMake(0, 0, aSize.Width(), aSize.Height()); - CGContextDrawImage( maContextHolder.get(), aSrcRect, xImage ); - - CGImageRelease( xImage ); - // restore the Quartz graphics state - maContextHolder.restoreState(); - - // mark the destination as painted - const CGRect aDstRect = CGRectApplyAffineTransform( aSrcRect, aCGMat ); - RefreshRect( aDstRect ); - - return true; -} - -bool AquaSalGraphics::hasFastDrawTransformedBitmap() const -{ - return false; -} - -bool AquaSalGraphics::drawAlphaRect( tools::Long nX, tools::Long nY, tools::Long nWidth, - tools::Long nHeight, sal_uInt8 nTransparency ) -{ - if( !CheckContext() ) - return true; - - // save the current state - maContextHolder.saveState(); - CGContextSetAlpha( maContextHolder.get(), (100-nTransparency) * (1.0/100) ); - - CGRect aRect = CGRectMake(nX, nY, nWidth-1, nHeight-1); - if( IsPenVisible() ) - { - aRect.origin.x += 0.5; - aRect.origin.y += 0.5; - } - - CGContextBeginPath( maContextHolder.get() ); - CGContextAddRect( maContextHolder.get(), aRect ); - CGContextDrawPath( maContextHolder.get(), kCGPathFill ); - - maContextHolder.restoreState(); - RefreshRect( aRect ); - - return true; -} - -void AquaSalGraphics::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap ) -{ - if( !CheckContext() ) - return; - - const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap); - CGImageRef xImage = rBitmap.CreateCroppedImage( static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY), - static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight) ); - if( !xImage ) - return; - - const CGRect aDstRect = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); - CGContextDrawImage( maContextHolder.get(), aDstRect, xImage ); - - CGImageRelease( xImage ); - RefreshRect( aDstRect ); -} - -void AquaSalGraphics::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, - const SalBitmap& rTransparentBitmap ) -{ - if( !CheckContext() ) - return; - - const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap); - const QuartzSalBitmap& rMask = static_cast<const QuartzSalBitmap&>(rTransparentBitmap); - CGImageRef xMaskedImage( rBitmap.CreateWithMask( rMask, rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight ) ); - if( !xMaskedImage ) - return; - - const CGRect aDstRect = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); - CGContextDrawImage( maContextHolder.get(), aDstRect, xMaskedImage ); - CGImageRelease( xMaskedImage ); - RefreshRect( aDstRect ); -} - -#ifndef IOS - -bool AquaSalGraphics::drawEPS( - tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, - void* pEpsData, sal_uInt32 nByteCount ) -{ - // convert the raw data to an NSImageRef - NSData* xNSData = [NSData dataWithBytes:pEpsData length:static_cast<int>(nByteCount)]; - NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData: xNSData]; - if( !xEpsImage ) - { - return false; - } - // get the target context - if( !CheckContext() ) - { - return false; - } - // NOTE: flip drawing, else the nsimage would be drawn upside down - maContextHolder.saveState(); -// CGContextTranslateCTM( maContextHolder.get(), 0, +mnHeight ); - CGContextScaleCTM( maContextHolder.get(), +1, -1 ); - nY = /*mnHeight*/ - (nY + nHeight); - - // prepare the target context - NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext]; - [pOrigNSCtx retain]; - - // create new context - NSGraphicsContext* pDrawNSCtx = [NSGraphicsContext graphicsContextWithCGContext: maContextHolder.get() flipped: IsFlipped()]; - // set it, setCurrentContext also releases the previously set one - [NSGraphicsContext setCurrentContext: pDrawNSCtx]; - - // draw the EPS - const NSRect aDstRect = NSMakeRect( nX, nY, nWidth, nHeight); - const bool bOK = [xEpsImage drawInRect: aDstRect]; - - // restore the NSGraphicsContext - [NSGraphicsContext setCurrentContext: pOrigNSCtx]; - [pOrigNSCtx release]; // restore the original retain count - - maContextHolder.restoreState(); - // mark the destination rectangle as updated - RefreshRect( aDstRect ); - - return bOK; -} - -#endif - -void AquaSalGraphics::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 ) -{ - if( nX1 == nX2 && nY1 == nY2 ) - { - // #i109453# platform independent code expects at least one pixel to be drawn - drawPixel( nX1, nY1 ); - - return; - } - - if( !CheckContext() ) - return; - - CGContextBeginPath( maContextHolder.get() ); - CGContextMoveToPoint( maContextHolder.get(), static_cast<float>(nX1)+0.5, static_cast<float>(nY1)+0.5 ); - CGContextAddLineToPoint( maContextHolder.get(), static_cast<float>(nX2)+0.5, static_cast<float>(nY2)+0.5 ); - CGContextDrawPath( maContextHolder.get(), kCGPathStroke ); - - tools::Rectangle aRefreshRect( nX1, nY1, nX2, nY2 ); - (void) aRefreshRect; - // Is a call to RefreshRect( aRefreshRect ) missing here? -} - -void AquaSalGraphics::drawMask( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, Color nMaskColor ) -{ - if( !CheckContext() ) - return; - - const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap); - CGImageRef xImage = rBitmap.CreateColorMask( rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, - nMaskColor ); - if( !xImage ) - return; - - const CGRect aDstRect = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); - CGContextDrawImage( maContextHolder.get(), aDstRect, xImage ); - CGImageRelease( xImage ); - RefreshRect( aDstRect ); -} - -void AquaSalGraphics::drawPixel( tools::Long nX, tools::Long nY ) -{ - // draw pixel with current line color - ImplDrawPixel( nX, nY, maLineColor ); -} - -void AquaSalGraphics::drawPixel( tools::Long nX, tools::Long nY, Color nColor ) -{ - const RGBAColor aPixelColor( nColor ); - ImplDrawPixel( nX, nY, aPixelColor ); -} - -bool AquaSalGraphics::drawPolyLine( - const basegfx::B2DHomMatrix& rObjectToDevice, - const basegfx::B2DPolygon& rPolyLine, - double fTransparency, - double fLineWidth, - const std::vector< double >* pStroke, // MM01 - basegfx::B2DLineJoin eLineJoin, - css::drawing::LineCap eLineCap, - double fMiterMinimumAngle, - bool bPixelSnapHairline) -{ - // MM01 check done for simple reasons - if(!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0) - { - return true; - } - -#ifdef IOS - if( !CheckContext() ) - return false; -#endif - - // tdf#124848 get correct LineWidth in discrete coordinates, - if(fLineWidth == 0) // hairline - fLineWidth = 1.0; - else // Adjust line width for object-to-device scale. - fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength(); - - // #i101491# Aqua does not support B2DLineJoin::NONE; return false to use - // the fallback (own geometry preparation) - // #i104886# linejoin-mode and thus the above only applies to "fat" lines - if( (basegfx::B2DLineJoin::NONE == eLineJoin) && (fLineWidth > 1.3) ) - return false; - - // MM01 need to do line dashing as fallback stuff here now - const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0); - const bool bStrokeUsed(0.0 != fDotDashLength); - assert(!bStrokeUsed || (bStrokeUsed && pStroke)); - basegfx::B2DPolyPolygon aPolyPolygonLine; - - if(bStrokeUsed) - { - // apply LineStyle - basegfx::utils::applyLineDashing( - rPolyLine, // source - *pStroke, // pattern - &aPolyPolygonLine, // target for lines - nullptr, // target for gaps - fDotDashLength); // full length if available - } - else - { - // no line dashing, just copy - aPolyPolygonLine.append(rPolyLine); - } - - // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline - aPolyPolygonLine.transform(rObjectToDevice); - if(bPixelSnapHairline) { aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); } - - // setup line attributes - CGLineJoin aCGLineJoin = kCGLineJoinMiter; - switch( eLineJoin ) - { - case basegfx::B2DLineJoin::NONE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; - case basegfx::B2DLineJoin::Bevel: aCGLineJoin = kCGLineJoinBevel; break; - case basegfx::B2DLineJoin::Miter: aCGLineJoin = kCGLineJoinMiter; break; - case basegfx::B2DLineJoin::Round: aCGLineJoin = kCGLineJoinRound; break; - } - // convert miter minimum angle to miter limit - CGFloat fCGMiterLimit = 1.0 / sin(fMiterMinimumAngle / 2.0); - // setup cap attribute - CGLineCap aCGLineCap(kCGLineCapButt); - - switch(eLineCap) - { - default: // css::drawing::LineCap_BUTT: - { - aCGLineCap = kCGLineCapButt; - break; - } - case css::drawing::LineCap_ROUND: - { - aCGLineCap = kCGLineCapRound; - break; - } - case css::drawing::LineCap_SQUARE: - { - aCGLineCap = kCGLineCapSquare; - break; - } - } - - // setup poly-polygon path - CGMutablePathRef xPath = CGPathCreateMutable(); - - // MM01 todo - I assume that this is OKAY to be done in one run for quartz - // but this NEEDS to be checked/verified - for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++) - { - const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a)); - AddPolygonToPath( - xPath, - aPolyLine, - aPolyLine.isClosed(), - !getAntiAlias(), - true); - } - - const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); - // #i97317# workaround for Quartz having problems with drawing small polygons - if( (aRefreshRect.size.width > 0.125) || (aRefreshRect.size.height > 0.125) ) - { - // use the path to prepare the graphics context - maContextHolder.saveState(); - CGContextBeginPath( maContextHolder.get() ); - CGContextAddPath( maContextHolder.get(), xPath ); - // draw path with antialiased line - CGContextSetShouldAntialias( maContextHolder.get(), getAntiAlias() ); - CGContextSetAlpha( maContextHolder.get(), 1.0 - fTransparency ); - CGContextSetLineJoin( maContextHolder.get(), aCGLineJoin ); - CGContextSetLineCap( maContextHolder.get(), aCGLineCap ); - CGContextSetLineWidth( maContextHolder.get(), fLineWidth ); - CGContextSetMiterLimit(maContextHolder.get(), fCGMiterLimit); - CGContextDrawPath( maContextHolder.get(), kCGPathStroke ); - maContextHolder.restoreState(); - - // mark modified rectangle as updated - RefreshRect( aRefreshRect ); - } - - CGPathRelease( xPath ); - - return true; -} - -bool AquaSalGraphics::drawPolyLineBezier( sal_uInt32, const Point*, const PolyFlags* ) -{ - return false; -} - -bool AquaSalGraphics::drawPolyPolygon( - const basegfx::B2DHomMatrix& rObjectToDevice, - const basegfx::B2DPolyPolygon& rPolyPolygon, - double fTransparency) -{ -#ifdef IOS - if (!maContextHolder.isSet()) - return true; -#endif - - // short circuit if there is nothing to do - if( rPolyPolygon.count() == 0 ) - return true; - - // ignore invisible polygons - if( (fTransparency >= 1.0) || (fTransparency < 0) ) - return true; - - // Fallback: Transform to DeviceCoordinates - basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon); - aPolyPolygon.transform(rObjectToDevice); - - // setup poly-polygon path - CGMutablePathRef xPath = CGPathCreateMutable(); - // tdf#120252 Use the correct, already transformed PolyPolygon (as long as - // the transformation is not used here...) - for(auto const& rPolygon : aPolyPolygon) - { - AddPolygonToPath( xPath, rPolygon, true, !getAntiAlias(), IsPenVisible() ); - } - - const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); - // #i97317# workaround for Quartz having problems with drawing small polygons - if( (aRefreshRect.size.width > 0.125) || (aRefreshRect.size.height > 0.125) ) - { - // prepare drawing mode - CGPathDrawingMode eMode; - if( IsBrushVisible() && IsPenVisible() ) - { - eMode = kCGPathEOFillStroke; - } - else if( IsPenVisible() ) - { - eMode = kCGPathStroke; - } - else if( IsBrushVisible() ) - { - eMode = kCGPathEOFill; - } - else - { - SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" ); - CGPathRelease( xPath ); - return true; - } - - // use the path to prepare the graphics context - maContextHolder.saveState(); - CGContextBeginPath( maContextHolder.get() ); - CGContextAddPath( maContextHolder.get(), xPath ); - - // draw path with antialiased polygon - CGContextSetShouldAntialias( maContextHolder.get(), getAntiAlias() ); - CGContextSetAlpha( maContextHolder.get(), 1.0 - fTransparency ); - CGContextDrawPath( maContextHolder.get(), eMode ); - maContextHolder.restoreState(); - - // mark modified rectangle as updated - RefreshRect( aRefreshRect ); - } - - CGPathRelease( xPath ); - - return true; -} - -void AquaSalGraphics::drawPolyPolygon( sal_uInt32 nPolyCount, const sal_uInt32 *pPoints, const Point* *ppPtAry ) -{ - if( nPolyCount <= 0 ) - return; - - if( !CheckContext() ) - return; - - // find bound rect - tools::Long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0; - getBoundRect( pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight ); - - for( sal_uInt32 n = 1; n < nPolyCount; n++ ) - { - tools::Long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight; - getBoundRect( pPoints[n], ppPtAry[n], nX, nY, nW, nH ); - if( nX < leftX ) - { - maxWidth += leftX - nX; - leftX = nX; - } - if( nY < topY ) - { - maxHeight += topY - nY; - topY = nY; - } - if( nX + nW > leftX + maxWidth ) - { - maxWidth = nX + nW - leftX; - } - if( nY + nH > topY + maxHeight ) - { - maxHeight = nY + nH - topY; - } - } - - // prepare drawing mode - CGPathDrawingMode eMode; - if( IsBrushVisible() && IsPenVisible() ) - { - eMode = kCGPathEOFillStroke; - } - else if( IsPenVisible() ) - { - eMode = kCGPathStroke; - } - else if( IsBrushVisible() ) - { - eMode = kCGPathEOFill; - } - else - { - SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" ); - return; - } - - // convert to CGPath - CGContextBeginPath( maContextHolder.get() ); - if( IsPenVisible() ) - { - for( sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++ ) - { - const sal_uInt32 nPoints = pPoints[nPoly]; - if( nPoints > 1 ) - { - const Point *pPtAry = ppPtAry[nPoly]; - float fX, fY; - - alignLinePoint( pPtAry, fX, fY ); - CGContextMoveToPoint( maContextHolder.get(), fX, fY ); - pPtAry++; - - for( sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) - { - alignLinePoint( pPtAry, fX, fY ); - CGContextAddLineToPoint( maContextHolder.get(), fX, fY ); - } - CGContextClosePath(maContextHolder.get()); - } - } - } - else - { - for( sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++ ) - { - const sal_uInt32 nPoints = pPoints[nPoly]; - if( nPoints > 1 ) - { - const Point *pPtAry = ppPtAry[nPoly]; - CGContextMoveToPoint( maContextHolder.get(), pPtAry->getX(), pPtAry->getY() ); - pPtAry++; - for( sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) - { - CGContextAddLineToPoint( maContextHolder.get(), pPtAry->getX(), pPtAry->getY() ); - } - CGContextClosePath(maContextHolder.get()); - } - } - } - - CGContextDrawPath( maContextHolder.get(), eMode ); - - RefreshRect( leftX, topY, maxWidth, maxHeight ); -} - -void AquaSalGraphics::drawPolygon( sal_uInt32 nPoints, const Point *pPtAry ) -{ - if( nPoints <= 1 ) - return; - - if( !CheckContext() ) - return; - - tools::Long nX = 0, nY = 0, nWidth = 0, nHeight = 0; - getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); - - CGPathDrawingMode eMode; - if( IsBrushVisible() && IsPenVisible() ) - { - eMode = kCGPathEOFillStroke; - } - else if( IsPenVisible() ) - { - eMode = kCGPathStroke; - } - else if( IsBrushVisible() ) - { - eMode = kCGPathEOFill; - } - else - { - SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" ); - return; - } - - CGContextBeginPath( maContextHolder.get() ); - - if( IsPenVisible() ) - { - float fX, fY; - alignLinePoint( pPtAry, fX, fY ); - CGContextMoveToPoint( maContextHolder.get(), fX, fY ); - pPtAry++; - for( sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) - { - alignLinePoint( pPtAry, fX, fY ); - CGContextAddLineToPoint( maContextHolder.get(), fX, fY ); - } - } - else - { - CGContextMoveToPoint( maContextHolder.get(), pPtAry->getX(), pPtAry->getY() ); - pPtAry++; - for( sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) - { - CGContextAddLineToPoint( maContextHolder.get(), pPtAry->getX(), pPtAry->getY() ); - } - } - - CGContextClosePath( maContextHolder.get() ); - CGContextDrawPath( maContextHolder.get(), eMode ); - RefreshRect( nX, nY, nWidth, nHeight ); -} - -bool AquaSalGraphics::drawPolygonBezier( sal_uInt32, const Point*, const PolyFlags* ) -{ - return false; -} - -bool AquaSalGraphics::drawPolyPolygonBezier( sal_uInt32, const sal_uInt32*, - const Point* const*, const PolyFlags* const* ) -{ - return false; -} - -void AquaSalGraphics::drawRect( - tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) -{ - if( !CheckContext() ) - return; - - CGRect aRect( CGRectMake(nX, nY, nWidth, nHeight) ); - if( IsPenVisible() ) - { - aRect.origin.x += 0.5; - aRect.origin.y += 0.5; - aRect.size.width -= 1; - aRect.size.height -= 1; - } - - if( IsBrushVisible() ) - { - CGContextFillRect( maContextHolder.get(), aRect ); - } - if( IsPenVisible() ) - { - CGContextStrokeRect( maContextHolder.get(), aRect ); - } - RefreshRect( nX, nY, nWidth, nHeight ); -} - -void AquaSalGraphics::drawPolyLine( sal_uInt32 nPoints, const Point *pPtAry ) -{ - if( nPoints < 1 ) - return; - - if( !CheckContext() ) - return; - - tools::Long nX = 0, nY = 0, nWidth = 0, nHeight = 0; - getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); - - float fX, fY; - CGContextBeginPath( maContextHolder.get() ); - alignLinePoint( pPtAry, fX, fY ); - CGContextMoveToPoint( maContextHolder.get(), fX, fY ); - pPtAry++; - - for( sal_uInt32 nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) - { - alignLinePoint( pPtAry, fX, fY ); - CGContextAddLineToPoint( maContextHolder.get(), fX, fY ); - } - CGContextStrokePath(maContextHolder.get()); - - RefreshRect( nX, nY, nWidth, nHeight ); -} - -sal_uInt16 AquaSalGraphics::GetBitCount() const -{ - sal_uInt16 nBits = mnBitmapDepth ? mnBitmapDepth : 32;//24; - return nBits; -} - -std::shared_ptr<SalBitmap> AquaSalGraphics::getBitmap( - tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY ) -{ - SAL_WARN_IF(!maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::getBitmap() with no layer this=" << this); - - ApplyXorContext(); - - std::shared_ptr<QuartzSalBitmap> pBitmap = std::make_shared<QuartzSalBitmap>(); - if (!pBitmap->Create(maLayer, mnBitmapDepth, nX, nY, nDX, nDY, IsFlipped())) - { - pBitmap = nullptr; - } - return pBitmap; -} - SystemGraphicsData AquaSalGraphics::GetGraphicsData() const { SystemGraphicsData aRes; aRes.nSize = sizeof(aRes); - aRes.rCGContext = maContextHolder.get(); + aRes.rCGContext = maShared.maContextHolder.get(); return aRes; } -tools::Long AquaSalGraphics::GetGraphicsWidth() const -{ - tools::Long w = 0; - if( maContextHolder.isSet() && ( -#ifndef IOS - mbWindow || -#endif - mbVirDev) ) - { - w = mnWidth; - } - -#ifndef IOS - if( w == 0 ) - { - if( mbWindow && mpFrame ) - { - w = mpFrame->maGeometry.nWidth; - } - } -#endif - return w; -} - -Color AquaSalGraphics::getPixel( tools::Long nX, tools::Long nY ) -{ - // return default value on printers or when out of bounds - if (!maLayer.isSet() || (nX < 0) || (nX >= mnWidth) || - (nY < 0) || (nY >= mnHeight)) - { - return COL_BLACK; - } - // prepare creation of matching a CGBitmapContext -#if defined OSL_BIGENDIAN - struct{ unsigned char b, g, r, a; } aPixel; -#else - struct{ unsigned char a, r, g, b; } aPixel; -#endif - - // create a one-pixel bitmap context - // TODO: is it worth to cache it? - CGContextRef xOnePixelContext = - CGBitmapContextCreate( &aPixel, 1, 1, 8, 32, - GetSalData()->mxRGBSpace, - uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Big) ); - - // update this graphics layer - ApplyXorContext(); - - // copy the requested pixel into the bitmap context - if( IsFlipped() ) - { - nY = mnHeight - nY; - } - const CGPoint aCGPoint = CGPointMake(-nX, -nY); - CGContextDrawLayerAtPoint(xOnePixelContext, aCGPoint, maLayer.get()); - - CGContextRelease( xOnePixelContext ); - - Color nColor( aPixel.r, aPixel.g, aPixel.b ); - return nColor; -} - -void AquaSalGraphics::GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY ) -{ -#ifndef IOS - if( !mnRealDPIY ) - { - initResolution( (mbWindow && mpFrame) ? mpFrame->getNSWindow() : nil ); - } - - rDPIX = mnRealDPIX; - rDPIY = mnRealDPIY; -#else - // This *must* be 96 or else the iOS app will behave very badly (tiles are scaled wrongly and - // don't match each others at their boundaries, and other issues). But *why* it must be 96 I - // have no idea. The commit that changed it to 96 from (the arbitrary) 200 did not say. If you - // know where else 96 is explicitly or implicitly hard-coded, please modify this comment. - - // Follow-up: It might be this: in 'online', loleaflet/src/map/Map.js: - // 15 = 1440 twips-per-inch / 96 dpi. - // Chosen to match previous hardcoded value of 3840 for - // the current tile pixel size of 256. - rDPIX = rDPIY = 96; -#endif -} - -void AquaSalGraphics::ImplDrawPixel( tools::Long nX, tools::Long nY, const RGBAColor& rColor ) -{ - if( !CheckContext() ) - { - return; - } - // overwrite the fill color - CGContextSetFillColor( maContextHolder.get(), rColor.AsArray() ); - // draw 1x1 rect, there is no pixel drawing in Quartz - const CGRect aDstRect = CGRectMake(nX, nY, 1, 1); - CGContextFillRect( maContextHolder.get(), aDstRect ); - RefreshRect( aDstRect ); - // reset the fill color - CGContextSetFillColor( maContextHolder.get(), maFillColor.AsArray() ); -} - #ifndef IOS void AquaSalGraphics::initResolution(NSWindow* nsWindow) @@ -1222,302 +229,27 @@ void AquaSalGraphics::initResolution(NSWindow* nsWindow) #endif -void AquaSalGraphics::invert( - tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, SalInvert nFlags ) -{ - if ( CheckContext() ) - { - CGRect aCGRect = CGRectMake( nX, nY, nWidth, nHeight); - maContextHolder.saveState(); - if ( nFlags & SalInvert::TrackFrame ) - { - const CGFloat dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line - CGContextSetBlendMode( maContextHolder.get(), kCGBlendModeDifference ); - CGContextSetRGBStrokeColor ( maContextHolder.get(), 1.0, 1.0, 1.0, 1.0 ); - CGContextSetLineDash ( maContextHolder.get(), 0, dashLengths, 2 ); - CGContextSetLineWidth( maContextHolder.get(), 2.0); - CGContextStrokeRect ( maContextHolder.get(), aCGRect ); - } - else if ( nFlags & SalInvert::N50 ) - { - //CGContextSetAllowsAntialiasing( maContextHolder.get(), false ); - CGContextSetBlendMode(maContextHolder.get(), kCGBlendModeDifference); - CGContextAddRect( maContextHolder.get(), aCGRect ); - Pattern50Fill(); - } - else // just invert - { - CGContextSetBlendMode(maContextHolder.get(), kCGBlendModeDifference); - CGContextSetRGBFillColor ( maContextHolder.get(),1.0, 1.0, 1.0 , 1.0 ); - CGContextFillRect ( maContextHolder.get(), aCGRect ); - } - maContextHolder.restoreState(); - RefreshRect( aCGRect ); - } -} - -namespace { - -CGPoint* makeCGptArray(sal_uInt32 nPoints, const Point* pPtAry) -{ - CGPoint *CGpoints = new CGPoint[nPoints]; - for(sal_uLong i=0;i<nPoints;i++) - { - CGpoints[i].x = pPtAry[i].getX(); - CGpoints[i].y = pPtAry[i].getY(); - } - return CGpoints; -} - -} - -void AquaSalGraphics::invert( sal_uInt32 nPoints, const Point* pPtAry, SalInvert nSalFlags ) -{ - if ( CheckContext() ) - { - maContextHolder.saveState(); - CGPoint* CGpoints = makeCGptArray(nPoints,pPtAry); - CGContextAddLines ( maContextHolder.get(), CGpoints, nPoints ); - if ( nSalFlags & SalInvert::TrackFrame ) - { - const CGFloat dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line - CGContextSetBlendMode( maContextHolder.get(), kCGBlendModeDifference ); - CGContextSetRGBStrokeColor ( maContextHolder.get(), 1.0, 1.0, 1.0, 1.0 ); - CGContextSetLineDash ( maContextHolder.get(), 0, dashLengths, 2 ); - CGContextSetLineWidth( maContextHolder.get(), 2.0); - CGContextStrokePath ( maContextHolder.get() ); - } - else if ( nSalFlags & SalInvert::N50 ) - { - CGContextSetBlendMode(maContextHolder.get(), kCGBlendModeDifference); - Pattern50Fill(); - } - else // just invert - { - CGContextSetBlendMode( maContextHolder.get(), kCGBlendModeDifference ); - CGContextSetRGBFillColor( maContextHolder.get(), 1.0, 1.0, 1.0, 1.0 ); - CGContextFillPath( maContextHolder.get() ); - } - const CGRect aRefreshRect = CGContextGetClipBoundingBox(maContextHolder.get()); - maContextHolder.restoreState(); - delete [] CGpoints; - RefreshRect( aRefreshRect ); - } -} - -void AquaSalGraphics::Pattern50Fill() -{ - static const CGFloat aFillCol[4] = { 1,1,1,1 }; - static const CGPatternCallbacks aCallback = { 0, &DrawPattern50, nullptr }; - static const CGColorSpaceRef mxP50Space = CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace ); - static const CGPatternRef mxP50Pattern = CGPatternCreate( nullptr, CGRectMake( 0, 0, 4, 4 ), - CGAffineTransformIdentity, 4, 4, - kCGPatternTilingConstantSpacing, - false, &aCallback ); - SAL_WARN_IF( !maContextHolder.get(), "vcl.quartz", "maContextHolder.get() is NULL" ); - CGContextSetFillColorSpace( maContextHolder.get(), mxP50Space ); - CGContextSetFillPattern( maContextHolder.get(), mxP50Pattern, aFillCol ); - CGContextFillPath( maContextHolder.get() ); -} - -void AquaSalGraphics::ResetClipRegion() -{ - // release old path and indicate no clipping - if( mxClipPath ) - { - CGPathRelease( mxClipPath ); - mxClipPath = nullptr; - } - if( CheckContext() ) - { - SetState(); - } -} - -void AquaSalGraphics::SetState() +void AquaSharedAttributes::setState() { maContextHolder.restoreState(); maContextHolder.saveState(); // setup clipping - if( mxClipPath ) + if (mxClipPath) { - CGContextBeginPath( maContextHolder.get() ); // discard any existing path - CGContextAddPath( maContextHolder.get(), mxClipPath ); // set the current path to the clipping path - CGContextClip( maContextHolder.get() ); // use it for clipping + CGContextBeginPath(maContextHolder.get()); // discard any existing path + CGContextAddPath(maContextHolder.get(), mxClipPath); // set the current path to the clipping path + CGContextClip(maContextHolder.get()); // use it for clipping } // set RGB colorspace and line and fill colors - CGContextSetFillColor( maContextHolder.get(), maFillColor.AsArray() ); + CGContextSetFillColor(maContextHolder.get(), maFillColor.AsArray() ); - CGContextSetStrokeColor( maContextHolder.get(), maLineColor.AsArray() ); - CGContextSetShouldAntialias( maContextHolder.get(), false ); - if( mnXorMode == 2 ) - { - CGContextSetBlendMode( maContextHolder.get(), kCGBlendModeDifference ); - } -} - -void AquaSalGraphics::SetLineColor() -{ - maLineColor.SetAlpha( 0.0 ); // transparent - if( CheckContext() ) - { - CGContextSetRGBStrokeColor( maContextHolder.get(), maLineColor.GetRed(), maLineColor.GetGreen(), - maLineColor.GetBlue(), maLineColor.GetAlpha() ); - } -} - -void AquaSalGraphics::SetLineColor( Color nColor ) -{ - maLineColor = RGBAColor( nColor ); - if( CheckContext() ) - { - CGContextSetRGBStrokeColor( maContextHolder.get(), maLineColor.GetRed(), maLineColor.GetGreen(), - maLineColor.GetBlue(), maLineColor.GetAlpha() ); - } -} - -void AquaSalGraphics::SetFillColor() -{ - maFillColor.SetAlpha( 0.0 ); // transparent - if( CheckContext() ) - { - CGContextSetRGBFillColor( maContextHolder.get(), maFillColor.GetRed(), maFillColor.GetGreen(), - maFillColor.GetBlue(), maFillColor.GetAlpha() ); - } -} - -void AquaSalGraphics::SetFillColor( Color nColor ) -{ - maFillColor = RGBAColor( nColor ); - if( CheckContext() ) - { - CGContextSetRGBFillColor( maContextHolder.get(), maFillColor.GetRed(), maFillColor.GetGreen(), - maFillColor.GetBlue(), maFillColor.GetAlpha() ); - } -} - -bool AquaSalGraphics::supportsOperation( OutDevSupportType eType ) const -{ - bool bRet = false; - switch( eType ) - { - case OutDevSupportType::TransparentRect: - case OutDevSupportType::B2DDraw: - bRet = true; - break; - default: - break; - } - return bRet; -} - -bool AquaSalGraphics::setClipRegion( const vcl::Region& i_rClip ) -{ - // release old clip path - if( mxClipPath ) - { - CGPathRelease( mxClipPath ); - mxClipPath = nullptr; - } - mxClipPath = CGPathCreateMutable(); - - // set current path, either as polypolgon or sequence of rectangles - RectangleVector aRectangles; - i_rClip.GetRegionRectangles(aRectangles); - - for(const auto& rRect : aRectangles) - { - const tools::Long nW(rRect.Right() - rRect.Left() + 1); // uses +1 logic in original - - if(nW) - { - const tools::Long nH(rRect.Bottom() - rRect.Top() + 1); // uses +1 logic in original - - if(nH) - { - const CGRect aRect = CGRectMake( rRect.Left(), rRect.Top(), nW, nH); - CGPathAddRect( mxClipPath, nullptr, aRect ); - } - } - } - // set the current path as clip region - if( CheckContext() ) - { - SetState(); - } - return true; -} - -void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor ) -{ - if( ! mbPrinter ) - { - SetFillColor( ImplGetROPColor( nROPColor ) ); - } -} - -void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor ) -{ - if( ! mbPrinter ) - { - SetLineColor( ImplGetROPColor( nROPColor ) ); - } -} - -void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly ) -{ - // return early if XOR mode remains unchanged - if( mbPrinter ) - { - return; - } - if( ! bSet && mnXorMode == 2 ) - { - CGContextSetBlendMode( maContextHolder.get(), kCGBlendModeNormal ); - mnXorMode = 0; - return; - } - else if( bSet && bInvertOnly && mnXorMode == 0) - { - CGContextSetBlendMode( maContextHolder.get(), kCGBlendModeDifference ); - mnXorMode = 2; - return; - } - - if (!mpXorEmulation && !bSet) - { - return; - } - if (mpXorEmulation && bSet == mpXorEmulation->IsEnabled()) - { - return; - } - if( !CheckContext() ) - { - return; - } - // prepare XOR emulation - if (!mpXorEmulation) - { - mpXorEmulation = std::make_unique<XorEmulation>(); - mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); - } - - // change the XOR mode - if( bSet ) - { - mpXorEmulation->Enable(); - maContextHolder.set(mpXorEmulation->GetMaskContext()); - mnXorMode = 1; - } - else + CGContextSetStrokeColor(maContextHolder.get(), maLineColor.AsArray() ); + CGContextSetShouldAntialias(maContextHolder.get(), false ); + if (mnXorMode == 2) { - mpXorEmulation->UpdateTarget(); - mpXorEmulation->Disable(); - maContextHolder.set(mpXorEmulation->GetTargetContext()); - mnXorMode = 0; + CGContextSetBlendMode(maContextHolder.get(), kCGBlendModeDifference ); } } @@ -1525,9 +257,9 @@ void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly ) void AquaSalGraphics::updateResolution() { - SAL_WARN_IF( !mbWindow, "vcl", "updateResolution on inappropriate graphics" ); + SAL_WARN_IF(!maShared.mbWindow, "vcl", "updateResolution on inappropriate graphics"); - initResolution( (mbWindow && mpFrame) ? mpFrame->getNSWindow() : nil ); + initResolution((maShared.mbWindow && maShared.mpFrame) ? maShared.mpFrame->getNSWindow() : nil); } #endif |