diff options
-rw-r--r-- | desktop/Executable_soffice_bin.mk | 12 | ||||
-rw-r--r-- | vcl/inc/quartz/salgdi.h | 16 | ||||
-rw-r--r-- | vcl/osx/salinst.cxx | 15 | ||||
-rw-r--r-- | vcl/quartz/salbmp.cxx | 22 | ||||
-rw-r--r-- | vcl/quartz/salgdi.cxx | 8 | ||||
-rw-r--r-- | vcl/quartz/salgdicommon.cxx | 315 | ||||
-rw-r--r-- | vcl/quartz/salgdiutils.cxx | 41 | ||||
-rw-r--r-- | vcl/quartz/salvd.cxx | 124 |
8 files changed, 260 insertions, 293 deletions
diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk index 5c895ed0e7cb..9c08a28361df 100644 --- a/desktop/Executable_soffice_bin.mk +++ b/desktop/Executable_soffice_bin.mk @@ -23,18 +23,6 @@ $(eval $(call gb_Executable_add_cobjects,soffice_bin,\ desktop/source/app/main \ )) -ifeq ($(OS),MACOSX) -# At least when building against SDK 10.15, changing the LC_VERSION_MIN_MACOSX load command's sdk -# value from 10.15 to "n/a" (i.e., 0.0.0) is necessary to avoid blurry text in the LO UI (see -# <https://github.com/llvm/llvm-project/commit/25ce33a6e4f3b13732c0f851e68390dc2acb9123> -# "[driver][darwin] Pass -platform_version flag to the linker instead of the -# -<platform>_version_min flag", clang/test/Driver/darwin-ld-platform-version-macos.c in particular, -# for the -platform_version that Clang passes by default to new-enough ld): -$(eval $(call gb_Executable_add_ldflags,soffice_bin, \ - -Xlinker -platform_version -Xlinker macos -Xlinker $(MACOSX_DEPLOYMENT_TARGET) -Xlinker 0.0.0 \ -)) -endif - ifeq ($(OS),WNT) $(eval $(call gb_Executable_set_targettype_gui,soffice_bin,NO)) diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index 6d7db6a6dd60..eb3e7563c898 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -174,6 +174,7 @@ class AquaSalGraphics : public SalGraphics #ifdef MACOSX /// is this a window graphics bool mbWindow; + bool mbWindowScaling; #else // IOS @@ -189,10 +190,10 @@ public: bool IsPenVisible() const { return maLineColor.IsVisible(); } bool IsBrushVisible() const { return maFillColor.IsVisible(); } + float GetWindowScaling(); void SetWindowGraphics( AquaSalFrame* pFrame ); - void SetPrinterGraphics( - CGContextRef, sal_Int32 nRealDPIX, sal_Int32 nRealDPIY ); - void SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef, int nBitDepth = 0); + void SetPrinterGraphics(CGContextRef, sal_Int32 nRealDPIX, sal_Int32 nRealDPIY); + void SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRef, int nBitDepth = 0); #ifdef MACOSX void initResolution( NSWindow* ); void copyResolution( AquaSalGraphics& ); @@ -299,9 +300,14 @@ public: virtual bool drawAlphaRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt8 nTransparency ) override; +protected: + virtual void copyScaledArea( tools::Long nDestX, tools::Long nDestY, tools::Long nSrcX, tools::Long nSrcY, + tools::Long nSrcWidth, tools::Long nSrcHeight, SalGraphics* pSrcGraphics ); + // native widget rendering methods that require mirroring + #ifdef MACOSX -protected: + virtual bool isNativeControlSupported( ControlType nType, ControlPart nPart ) override; virtual bool hitTestNativeControl( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, @@ -313,9 +319,9 @@ protected: const ImplControlValue& aValue, const OUString& aCaption, tools::Rectangle &rNativeBoundingRegion, tools::Rectangle &rNativeContentRegion ) override; -public: #endif +public: // get device resolution virtual void GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY ) override; // get the depth of the device diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx index bbe6d9d704f7..1ab962284918 100644 --- a/vcl/osx/salinst.cxx +++ b/vcl/osx/salinst.cxx @@ -302,6 +302,21 @@ VCLPLUG_OSX_PUBLIC SalInstance* create_SalInstance() // put cocoa into multithreaded mode [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil]; + // Dark mode is disabled as long as it is not implemented completely. For development purposes, it may be controlled by + // environment variables: VCL_MACOS_FORCE_DARK_MODE enable dark mode independent of system settings, + // VCL_MACOS_USE_SYSTEM_APPEARANCE to use system settings (light mode oder dark mode as configured within system preferences). + + // TODO: After implementation of dark mode, this code has to be removed. + + if (getenv("VCL_MACOS_FORCE_DARK_MODE")) + { + if (@available(macOS 10.14, iOS 13, *)) + [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua]]; + } + else + if (!getenv("VCL_MACOS_USE_SYSTEM_APPEARANCE")) + [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]]; + // activate our delegate methods [NSApp setDelegate: NSApp]; diff --git a/vcl/quartz/salbmp.cxx b/vcl/quartz/salbmp.cxx index ecd3c974b081..cf1f26648b7f 100644 --- a/vcl/quartz/salbmp.cxx +++ b/vcl/quartz/salbmp.cxx @@ -71,6 +71,10 @@ QuartzSalBitmap::~QuartzSalBitmap() bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits, int nX, int nY, int nWidth, int nHeight, bool bFlipped) { + + // TODO: Bitmaps from scaled layers are reverted to single precision. This is a workaround only unless bitmaps with precision of + // source layer are implemented. + SAL_WARN_IF(!rLayerHolder.isSet(), "vcl", "QuartzSalBitmap::Create() from non-layered context"); // sanitize input parameters @@ -84,7 +88,10 @@ bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits nY = 0; } - const CGSize aLayerSize = CGLayerGetSize(rLayerHolder.get()); + CGSize aLayerSize = CGLayerGetSize(rLayerHolder.get()); + const float fScale = rLayerHolder.getScale(); + aLayerSize.width /= fScale; + aLayerSize.height /= fScale; if( nWidth >= static_cast<int>(aLayerSize.width) - nX ) nWidth = static_cast<int>(aLayerSize.width) - nX; @@ -104,17 +111,18 @@ bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits CreateContext(); // copy layer content into the bitmap buffer - const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX), static_cast<CGFloat>(-nY) }; - if (maGraphicContext.isSet()) // remove warning + const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX * fScale), static_cast<CGFloat>(-nY * fScale) }; + if (maGraphicContext.isSet()) { if( bFlipped ) { - CGContextTranslateCTM( maGraphicContext.get(), 0, +mnHeight ); - - CGContextScaleCTM( maGraphicContext.get(), +1, -1 ); + CGContextTranslateCTM(maGraphicContext.get(), 0, +mnHeight); + CGContextScaleCTM(maGraphicContext.get(), +1, -1); } - + maGraphicContext.saveState(); + CGContextScaleCTM(maGraphicContext.get(), 1 / fScale, 1 / fScale); CGContextDrawLayerAtPoint(maGraphicContext.get(), aSrcPoint, rLayerHolder.get()); + maGraphicContext.restoreState(); } return true; } diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index ae6a40cfd4e2..8dd9ec9222ad 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -205,6 +205,14 @@ AquaSalGraphics::AquaSalGraphics() , mbVirDev( false ) #ifdef MACOSX , mbWindow( false ) + + // Window scaling independent from main display may be forced by setting VCL_MACOS_FORCE_WINDOW_SCALING environment variable. If + // unset window scaling from main display will be used. After implementation of full support of scaled displays window scaling + // will be set to 2.0f for macOS as default. + + // TODO: After implementation of full support of scaled displays VCL_FORCE_WINDOW_SCALING control has to be removed. + + , mbWindowScaling( getenv("VCL_MACOS_FORCE_WINDOW_SCALING") ) #else , mbForeignContext( false ) #endif diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx index d33b690fdaff..0597fe3d021d 100644 --- a/vcl/quartz/salgdicommon.cxx +++ b/vcl/quartz/salgdicommon.cxx @@ -199,109 +199,6 @@ static void alignLinePoint( const Point* i_pIn, float& o_fX, float& o_fY ) o_fY = static_cast<float>(i_pIn->getY() ) + 0.5; } -void AquaSalGraphics::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 || - rPosAry.mnSrcHeight <= 0 || - rPosAry.mnDestWidth <= 0 || - rPosAry.mnDestHeight <= 0 ) - { - return; - } - -#ifdef IOS - // If called from idle layout, maContextHolder.get() is NULL, no idea what to do - if (!maContextHolder.isSet()) - return; -#endif - - // accelerate trivial operations - /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); - const bool bSameGraphics = (this == pSrc) -#ifdef MACOSX - || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame)) -#endif - ; - - if( bSameGraphics && - (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) && - (rPosAry.mnSrcHeight == rPosAry.mnDestHeight)) - { - // short circuit if there is nothing to do - if( (rPosAry.mnSrcX == rPosAry.mnDestX) && - (rPosAry.mnSrcY == rPosAry.mnDestY)) - { - return; - } - // use copyArea() if source and destination context are identical - copyArea( rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, false/*bWindowInvalidate*/ ); - return; - } - - ApplyXorContext(); - pSrc->ApplyXorContext(); - - SAL_WARN_IF (!pSrc->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 - { - // 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() ) - { - if( pSrcGraphics == this ) - { - aCopyContext.set(mpXorEmulation->GetTargetContext()); - } - } - aCopyContext.saveState(); - - const CGRect aDstRect = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight); - CGContextClipToRect(aCopyContext.get(), aDstRect); - - // draw at new destination - // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down - if( pSrc->IsFlipped() ) - { - CGContextTranslateCTM( aCopyContext.get(), 0, +mnHeight ); - CGContextScaleCTM( aCopyContext.get(), +1, -1 ); - } - - // TODO: pSrc->size() != this->size() - CGContextDrawLayerAtPoint(aCopyContext.get(), aDstPoint, pSrc->maLayer.get()); - - aCopyContext.restoreState(); - // mark the destination rectangle as updated - RefreshRect( aDstRect ); - } - else - { - std::shared_ptr<SalBitmap> pBitmap = pSrc->getBitmap( rPosAry.mnSrcX, rPosAry.mnSrcY, - rPosAry.mnSrcWidth, rPosAry.mnSrcHeight ); - if( pBitmap ) - { - SalTwoRect aPosAry( rPosAry ); - aPosAry.mnSrcX = 0; - aPosAry.mnSrcY = 0; - drawBitmap( aPosAry, *pBitmap ); - } - } -} - static void DrawPattern50( void*, CGContextRef rContext ) { static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } }; @@ -370,83 +267,115 @@ void AquaSalGraphics::ApplyXorContext() } } -void AquaSalGraphics::copyArea( - tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, - tools::Long nSrcWidth, tools::Long nSrcHeight, bool /*bWindowInvalidate*/ ) +void AquaSalGraphics::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraphics) { - SAL_WARN_IF (!maLayer.isSet(), "vcl.quartz", - "AquaSalGraphics::copyArea() for non-layered graphics this=" << this); + if (!pSrcGraphics) + pSrcGraphics = this; + AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0 || rPosAry.mnDestHeight <= 0) + return; + if (!maContextHolder.isSet()) + return; -#ifdef IOS - if (!maLayer.isSet()) + SAL_WARN_IF (!pSrc->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()) + copyScaledArea(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY, + rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, pSrcGraphics); + else + { + ApplyXorContext(); + pSrc->ApplyXorContext(); + std::shared_ptr<SalBitmap> pBitmap = pSrc->getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, + rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); + if (pBitmap) + { + SalTwoRect aPosAry(rPosAry); + aPosAry.mnSrcX = 0; + aPosAry.mnSrcY = 0; + drawBitmap(aPosAry, *pBitmap); + } + } +} + +void AquaSalGraphics::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, + tools::Long nSrcWidth, tools::Long nSrcHeight, bool) +{ + if (!maContextHolder.isSet()) return; -#endif - float fScale = maLayer.getScale(); - tools::Long nScaledSourceX = nSrcX * fScale; - tools::Long nScaledSourceY = nSrcY * fScale; + // Functionality is implemented in protected member function AquaSalGraphics::copyScaledArea() which requires an additional + // parameter of type SalGraphics to be used in AquaSalGraphics::copyBits() too. - tools::Long nScaledTargetX = nDstX * fScale; - tools::Long nScaledTargetY = nDstY * fScale; + copyScaledArea(nDstX, nDstY, nSrcX, nSrcY, nSrcWidth, nSrcHeight, this); +} - tools::Long nScaledSourceWidth = nSrcWidth * fScale; - tools::Long nScaledSourceHeight = nSrcHeight * fScale; +void AquaSalGraphics::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY, + tools::Long nSrcWidth, tools::Long nSrcHeight, SalGraphics *pSrcGraphics) +{ + if (!pSrcGraphics) + pSrcGraphics = this; + AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); - ApplyXorContext(); + SAL_WARN_IF(!maLayer.isSet(), "vcl.quartz", + "AquaSalGraphics::copyScaledArea() without graphics context or for non-layered graphics this=" << this); - maContextHolder.saveState(); + if (!maContextHolder.isSet() || !maLayer.isSet()) + return; - // in XOR mode the drawing context is redirected to the XOR mask - // copyArea() always works on the target context though - CGContextRef xCopyContext = maContextHolder.get(); + // Determine scaled geometry of source and target area assuming source and target area have the same scale - if( mpXorEmulation && mpXorEmulation->IsEnabled() ) - { - xCopyContext = mpXorEmulation->GetTargetContext(); - } + float fScale = maLayer.getScale(); + CGFloat nScaledSourceX = nSrcX * fScale; + CGFloat nScaledSourceY = nSrcY * fScale; + CGFloat nScaledTargetX = nDstX * fScale; + CGFloat nScaledTargetY = nDstY * fScale; + CGFloat nScaledSourceWidth = nSrcWidth * fScale; + CGFloat nScaledSourceHeight = nSrcHeight * fScale; - // If we have a scaled layer, we need to revert the scaling or else - // it will interfere with the coordinate calculation - CGContextScaleCTM(xCopyContext, 1.0 / fScale, 1.0 / fScale); + // Apply XOR context and get copy context from current graphics context or XOR context - // drawing a layer onto its own context causes trouble on OSX => copy it first - // TODO: is it possible to get rid of this unneeded copy more often? - // e.g. on OSX>=10.5 only this situation causes problems: - // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth + ApplyXorContext(); + maContextHolder.saveState(); + CGContextRef xCopyContext = maContextHolder.get(); + if (mpXorEmulation && mpXorEmulation->IsEnabled()) + xCopyContext = mpXorEmulation->GetTargetContext(); - CGLayerHolder sSourceLayerHolder(maLayer); - { - const CGSize aSrcSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight); - sSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSrcSize, nullptr)); + // Set scale matrix of copy context to consider layer scaling - const CGContextRef xSrcContext = CGLayerGetContext(sSourceLayerHolder.get()); + CGContextScaleCTM(xCopyContext, 1 / fScale, 1 / fScale); - CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY); - if( IsFlipped() ) - { - CGContextTranslateCTM( xSrcContext, 0, +nScaledSourceHeight ); - CGContextScaleCTM( xSrcContext, +1, -1 ); - aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mnHeight * fScale); - } - CGContextSetBlendMode(xSrcContext, kCGBlendModeCopy); + // Creating an additional layer is required for drawing with the required scale and extent at the drawing destination + // thereafter. - CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, maLayer.get()); + CGLayerHolder aSourceLayerHolder(pSrc->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()) + { + CGContextTranslateCTM(xSourceContext, 0, nScaledSourceHeight); + CGContextScaleCTM(xSourceContext, 1, -1); + aSrcPoint.y = nScaledSourceY + nScaledSourceHeight - mnHeight * fScale; } + CGContextSetBlendMode(xSourceContext, kCGBlendModeCopy); + CGContextDrawLayerAtPoint(xSourceContext, aSrcPoint, pSrc->maLayer.get()); + + // Copy source area from additional layer to traget area - // draw at new destination const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight); CGContextSetBlendMode(xCopyContext, kCGBlendModeCopy); - CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get()); + CGContextDrawLayerInRect(xCopyContext, aTargetRect, aSourceLayerHolder.get()); - maContextHolder.restoreState(); + // Housekeeping on exit - // cleanup - if (sSourceLayerHolder.get() != maLayer.get()) - { - CGLayerRelease(sSourceLayerHolder.get()); - } - // mark the destination rectangle as updated - RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); + maContextHolder.restoreState(); + if (aSourceLayerHolder.get() != maLayer.get()) + CGLayerRelease(aSourceLayerHolder.get()); + RefreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight); } #ifndef IOS @@ -1897,62 +1826,74 @@ bool XorEmulation::UpdateTarget() return true; } -void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef xContext, - int nBitmapDepth) +void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRef xContext, int nBitmapDepth) { - SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext ); + SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext); -#ifndef IOS + // Set member variables + + InvalidateContext(); mbWindow = false; -#endif mbPrinter = false; mbVirDev = true; - - // set graphics properties maLayer = rLayer; - maContextHolder.set(xContext); - mnBitmapDepth = nBitmapDepth; #ifdef IOS + mbForeignContext = xContext != NULL; + #endif - // return early if the virdev is being destroyed - if( !xContext ) - return; + // Get size and scale from layer if set else from bitmap and AquaSalGraphics::GetWindowScaling(), which is used to determine + // scaling for direct graphics output too - // get new graphics properties - if (!maLayer.isSet()) + CGSize aSize; + float fScale; + if (maLayer.isSet()) { - mnWidth = CGBitmapContextGetWidth( maContextHolder.get() ); - mnHeight = CGBitmapContextGetHeight( maContextHolder.get() ); + maContextHolder.set(CGLayerGetContext(maLayer.get())); + aSize = CGLayerGetSize(maLayer.get()); + fScale = maLayer.getScale(); } else { - const CGSize aSize = CGLayerGetSize(maLayer.get()); - mnWidth = static_cast<int>(aSize.width); - mnHeight = static_cast<int>(aSize.height); + maContextHolder.set(xContext); + if (!xContext) + return; + aSize.width = CGBitmapContextGetWidth(xContext); + aSize.height = CGBitmapContextGetHeight(xContext); + fScale = GetWindowScaling(); } + mnWidth = aSize.width / fScale; + mnHeight = aSize.height / fScale; + + // Set color space for fill and stroke + + CGColorSpaceRef aColorSpace = GetSalData()->mxRGBSpace; + CGContextSetFillColorSpace(maContextHolder.get(), aColorSpace); + CGContextSetStrokeColorSpace(maContextHolder.get(), aColorSpace); - // prepare graphics for drawing - const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; - CGContextSetFillColorSpace( maContextHolder.get(), aCGColorSpace ); - CGContextSetStrokeColorSpace( maContextHolder.get(), aCGColorSpace ); + // Apply scale matrix to virtual device graphics - // re-enable XorEmulation for the new context - if( mpXorEmulation ) + CGContextScaleCTM(maContextHolder.get(), fScale, fScale); + + // Apply XOR emulation if required + + if (mpXorEmulation) { mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); - if( mpXorEmulation->IsEnabled() ) - { + if (mpXorEmulation->IsEnabled()) maContextHolder.set(mpXorEmulation->GetMaskContext()); - } } - // initialize stack of CGContext states + // Housekeeping on exit + maContextHolder.saveState(); SetState(); + + SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this << + " (" << mnWidth << "x" << mnHeight << ") fScale=" << fScale << " mnBitmapDepth=" << mnBitmapDepth); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/salgdiutils.cxx b/vcl/quartz/salgdiutils.cxx index 94ecb9c5af53..01626d348999 100644 --- a/vcl/quartz/salgdiutils.cxx +++ b/vcl/quartz/salgdiutils.cxx @@ -35,6 +35,39 @@ #include <osx/salframe.h> #include <osx/saldata.hxx> +float AquaSalGraphics::GetWindowScaling() +{ + float fScale = 1.0f; + +#ifdef MACOSX + + // Window scaling independent from main display may be forced by setting VCL_MACOS_FORCE_WINDOW_SCALING environment variable + // whose setting is stored in mbWindowScaling. After implementation of full support of scaled displays window scaling will be + // set to 2.0f for macOS as default. This will allow moving of windows between non retina and retina displays without blurry + // text and graphics. + + // TODO: After implementation of full support of scaled displays code has to be modified to set a scaling of 2.0f as default. + + if (mbWindowScaling) + { + fScale = 2.0f; + return fScale; + } + +#endif + + AquaSalFrame *pSalFrame = mpFrame; + if (!pSalFrame) + pSalFrame = static_cast<AquaSalFrame *>(GetSalData()->mpInstance->anyFrame()); + if (pSalFrame) + { + NSWindow *pNSWindow = pSalFrame->getNSWindow(); + if (pNSWindow) + fScale = [pNSWindow backingScaleFactor]; + } + return fScale; +} + void AquaSalGraphics::SetWindowGraphics( AquaSalFrame* pFrame ) { mpFrame = pFrame; @@ -116,13 +149,7 @@ bool AquaSalGraphics::CheckContext() { const unsigned int nWidth = mpFrame->maGeometry.nWidth; const unsigned int nHeight = mpFrame->maGeometry.nHeight; - - // Let's get the window scaling factor if possible, or use 1.0 - // as the scaling factor. - float fScale = 1.0f; - if (mpFrame->getNSWindow()) - fScale = [mpFrame->getNSWindow() backingScaleFactor]; - + const float fScale = GetWindowScaling(); CGLayerRef rReleaseLayer = nullptr; // check if a new drawing context is needed (e.g. after a resize) diff --git a/vcl/quartz/salvd.cxx b/vcl/quartz/salvd.cxx index 1e6e5b2536d0..5d7facd3ef38 100644 --- a/vcl/quartz/salvd.cxx +++ b/vcl/quartz/salvd.cxx @@ -104,6 +104,10 @@ AquaSalVirtualDevice::AquaSalVirtualDevice( } mpGraphics->SetVirDevGraphics(maLayer, pData->rCGContext); + + SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::AquaSalVirtualDevice() this=" << this << + " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO")); + } else { @@ -180,8 +184,6 @@ void AquaSalVirtualDevice::Destroy() if (maBitmapContext.isSet()) { - void* pRawData = CGBitmapContextGetData(maBitmapContext.get()); - std::free(pRawData); CGContextRelease(maBitmapContext.get()); maBitmapContext.set(nullptr); } @@ -202,106 +204,78 @@ void AquaSalVirtualDevice::ReleaseGraphics( SalGraphics* ) mbGraphicsUsed = false; } -bool AquaSalVirtualDevice::SetSize( tools::Long nDX, tools::Long nDY ) +bool AquaSalVirtualDevice::SetSize(tools::Long nDX, tools::Long nDY) { - SAL_INFO( "vcl.virdev", "AquaSalVirtualDevice::SetSize() this=" << this << - " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO")); + SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::SetSize() this=" << this << + " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO")); - if( mbForeignContext ) - { - // Do not delete/resize mxContext that we have received from outside VCL + // Do not delete/resize graphics context if it has been received from outside VCL + + if (mbForeignContext) return true; - } + // Do not delete/resize graphics context if no change of geometry has been requested + + float fScale; if (maLayer.isSet()) { + fScale = maLayer.getScale(); const CGSize aSize = CGLayerGetSize(maLayer.get()); - if( (nDX == aSize.width) && (nDY == aSize.height) ) - { - // Yay, we do not have to do anything :) + if ((nDX == aSize.width / fScale) && (nDY == aSize.height / fScale)) return true; - } } + // Destroy graphics context if change of geometry has been requested + 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 + mnWidth = nDX; mnHeight = nDY; - - // create a CGLayer matching to the intended virdev usage - CGContextHolder xCGContextHolder; - if( mnBitmapDepth && (mnBitmapDepth < 16) ) + fScale = mpGraphics->GetWindowScaling(); + CGColorSpaceRef aColorSpace; + uint32_t nFlags; + if (mnBitmapDepth && (mnBitmapDepth < 16)) { - mnBitmapDepth = 8; // TODO: are 1bit vdevs worth it? - const int nBytesPerRow = (mnBitmapDepth * nDX + 7) / 8; - - void* pRawData = std::malloc( nBytesPerRow * nDY ); - maBitmapContext.set(CGBitmapContextCreate( pRawData, nDX, nDY, - mnBitmapDepth, nBytesPerRow, - GetSalData()->mxGraySpace, kCGImageAlphaNone)); - xCGContextHolder = maBitmapContext; + mnBitmapDepth = 8; + aColorSpace = GetSalData()->mxGraySpace; + nFlags = kCGImageAlphaNone; } else { + mnBitmapDepth = 32; + aColorSpace = GetSalData()->mxRGBSpace; + #ifdef MACOSX - // default to a NSView target context - AquaSalFrame* pSalFrame = mpGraphics->getGraphicsFrame(); - if( !pSalFrame || !AquaSalFrame::isAlive( pSalFrame )) - { - pSalFrame = static_cast<AquaSalFrame*>( GetSalData()->mpInstance->anyFrame() ); - if ( pSalFrame ) - // update the frame reference - mpGraphics->setGraphicsFrame( pSalFrame ); - } - if( pSalFrame ) - { - // #i91990# - NSWindow* pNSWindow = pSalFrame->getNSWindow(); - if ( pNSWindow ) - { - NSGraphicsContext* pNSContext = [NSGraphicsContext graphicsContextWithWindow: pNSWindow]; - if( pNSContext ) - { - xCGContextHolder.set([pNSContext CGContext]); - } - } - } -#endif - if (!xCGContextHolder.isSet()) - { - // assert(Application::IsBitmapRendering()); - mnBitmapDepth = 32; + nFlags = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; - const int nBytesPerRow = (mnBitmapDepth * nDX) / 8; - void* pRawData = std::malloc( nBytesPerRow * nDY ); -#ifdef MACOSX - const int nFlags = kCGImageAlphaNoneSkipFirst; #else - const int nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little; + + nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little; + #endif - maBitmapContext.set(CGBitmapContextCreate(pRawData, nDX, nDY, 8, nBytesPerRow, - GetSalData()->mxRGBSpace, nFlags)); - xCGContextHolder = maBitmapContext; - } + } - SAL_WARN_IF(!xCGContextHolder.isSet(), "vcl.quartz", "No context"); + // Allocate buffer for virtual device graphics as bitmap context to store graphics with highest required (scaled) resolution - const CGSize aNewSize = { static_cast<CGFloat>(nDX), static_cast<CGFloat>(nDY) }; - maLayer.set(CGLayerCreateWithContext(xCGContextHolder.get(), aNewSize, nullptr)); + size_t nScaledWidth = mnWidth * fScale; + size_t nScaledHeight = mnHeight * fScale; + size_t nBytesPerRow = mnBitmapDepth * nScaledWidth / 8; + maBitmapContext.set(CGBitmapContextCreate(nullptr, nScaledWidth, nScaledHeight, 8, nBytesPerRow, aColorSpace, nFlags)); - if (maLayer.isSet() && mpGraphics) - { - // get the matching Quartz context - CGContextRef xDrawContext = CGLayerGetContext( maLayer.get() ); - - // Here we pass the CGLayerRef that the CGLayerHolder maLayer holds as the first parameter - // to SetVirDevGraphics(). That parameter is of type CGLayerHolder, so what we actually pass - // is an implicitly constructed *separate* CGLayerHolder. Is that what we want? No idea. - // Possibly we could pass just maLayer as such? But doing that does not fix tdf#138122. - mpGraphics->SetVirDevGraphics(maLayer.get(), xDrawContext, mnBitmapDepth); - } + SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::SetSize() this=" << this << + " fScale=" << fScale << " mnBitmapDepth=" << mnBitmapDepth); + + CGSize aLayerSize = { static_cast<CGFloat>(nScaledWidth), static_cast<CGFloat>(nScaledHeight) }; + maLayer.set(CGLayerCreateWithContext(maBitmapContext.get(), aLayerSize, nullptr)); + maLayer.setScale(fScale); + mpGraphics->SetVirDevGraphics(maLayer, CGLayerGetContext(maLayer.get()), mnBitmapDepth); + + SAL_WARN_IF(!maBitmapContext.isSet(), "vcl.quartz", "No context"); return maLayer.isSet(); } |