diff options
-rw-r--r-- | desktop/Executable_soffice_bin.mk | 13 | ||||
-rw-r--r-- | solenv/clang-format/blacklist | 4 | ||||
-rw-r--r-- | vcl/Library_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/Library_vclplug_osx.mk | 3 | ||||
-rw-r--r-- | vcl/inc/quartz/salgdi.h | 15 | ||||
-rw-r--r-- | vcl/ios/salios.cxx | 433 | ||||
-rw-r--r-- | vcl/osx/salgdiutils.cxx (renamed from vcl/quartz/salgdiutils.cxx) | 41 | ||||
-rw-r--r-- | vcl/osx/salinst.cxx | 15 | ||||
-rw-r--r-- | vcl/osx/salmacos.cxx | 383 | ||||
-rw-r--r-- | vcl/quartz/salbmp.cxx | 50 | ||||
-rw-r--r-- | vcl/quartz/salgdi.cxx | 8 | ||||
-rw-r--r-- | vcl/quartz/salgdicommon.cxx | 236 | ||||
-rw-r--r-- | vcl/quartz/salvd.cxx | 133 |
13 files changed, 894 insertions, 441 deletions
diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk index ecf75893ab96..cabd31af29f2 100644 --- a/desktop/Executable_soffice_bin.mk +++ b/desktop/Executable_soffice_bin.mk @@ -31,19 +31,6 @@ $(eval $(call gb_Executable_set_ldflags,\ endif -ifeq ($(OS)-$(HAVE_MACOS_LD_PLATFORMVERSION),MACOSX-TRUE) -# 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 $(MAC_OS_X_VERSION_MIN_REQUIRED_DOTS) \ - -Xlinker 0.0.0 \ -)) -endif - ifeq ($(OS),WNT) $(eval $(call gb_Executable_set_targettype_gui,soffice_bin,NO)) diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist index 521ff3e43683..864518c389e4 100644 --- a/solenv/clang-format/blacklist +++ b/solenv/clang-format/blacklist @@ -17554,6 +17554,7 @@ vcl/inc/window.h vcl/inc/wizdlg.hxx vcl/ios/dummies.cxx vcl/ios/iosinst.cxx +vcl/ios/salios.cxx vcl/null/printerinfomanager.cxx vcl/opengl/DeviceInfo.cxx vcl/opengl/FixedTextureAtlas.cxx @@ -17623,7 +17624,9 @@ vcl/osx/documentfocuslistener.cxx vcl/osx/documentfocuslistener.hxx vcl/osx/saldata.cxx vcl/osx/salframe.cxx +vcl/osx/salgdiutils.cxx vcl/osx/salinst.cxx +vcl/osx/salmacos.cxx vcl/osx/salmenu.cxx vcl/osx/salnativewidgets.cxx vcl/osx/salobj.cxx @@ -17656,7 +17659,6 @@ vcl/quartz/ctfonts.cxx vcl/quartz/salbmp.cxx vcl/quartz/salgdi.cxx vcl/quartz/salgdicommon.cxx -vcl/quartz/salgdiutils.cxx vcl/quartz/salvd.cxx vcl/quartz/utils.cxx vcl/source/app/ITiledRenderable.cxx diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 9afdac80ef86..15a083df19a6 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -671,6 +671,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/ios/iosinst \ vcl/ios/dummies \ vcl/ios/clipboard \ + vcl/ios/salios \ vcl/ios/iOSTransferable \ vcl/ios/DataFlavorMapping \ vcl/ios/HtmlFmtFlt \ diff --git a/vcl/Library_vclplug_osx.mk b/vcl/Library_vclplug_osx.mk index b1170cca2b83..e21c286f87ec 100644 --- a/vcl/Library_vclplug_osx.mk +++ b/vcl/Library_vclplug_osx.mk @@ -124,7 +124,9 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_osx,\ vcl/osx/documentfocuslistener \ vcl/osx/saldata \ vcl/osx/salframe \ + vcl/osx/salgdiutils \ vcl/osx/salinst \ + vcl/osx/salmacos \ vcl/osx/salmenu \ vcl/osx/salnativewidgets \ vcl/osx/salobj \ @@ -136,7 +138,6 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_osx,\ vcl/quartz/salbmp \ vcl/quartz/salgdi \ vcl/quartz/salgdicommon \ - vcl/quartz/salgdiutils \ vcl/quartz/salvd \ vcl/quartz/utils \ )) diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index 433fce10f029..aac4e140f5f9 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -172,6 +172,7 @@ class AquaSalGraphics : public SalGraphics #ifdef MACOSX /// is this a window graphics bool mbWindow; + bool mbWindowScaling; #else // IOS @@ -187,15 +188,16 @@ public: bool IsPenVisible() const { return maLineColor.IsVisible(); } bool IsBrushVisible() const { return maFillColor.IsVisible(); } - void SetWindowGraphics( AquaSalFrame* pFrame ); - void SetPrinterGraphics( CGContextRef, long nRealDPIX, long nRealDPIY ); void SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef, int nBitDepth = 0); #ifdef MACOSX void initResolution( NSWindow* ); void copyResolution( AquaSalGraphics& ); void updateResolution(); + float GetWindowScaling(); + void SetWindowGraphics( AquaSalFrame* pFrame ); bool IsWindowGraphics() const { return mbWindow; } + void SetPrinterGraphics(CGContextRef, long nRealDPIX, long nRealDPIY); AquaSalFrame* getGraphicsFrame() const { return mpFrame; } void setGraphicsFrame( AquaSalFrame* pFrame ) { mpFrame = pFrame; } #endif @@ -294,9 +296,12 @@ public: virtual bool drawAlphaRect( long nX, long nY, long nWidth, long nHeight, sal_uInt8 nTransparency ) override; - // native widget rendering methods that require mirroring #ifdef MACOSX + protected: + + // native widget rendering methods that require mirroring + virtual bool isNativeControlSupported( ControlType nType, ControlPart nPart ) override; virtual bool hitTestNativeControl( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, @@ -308,9 +313,11 @@ protected: const ImplControlValue& aValue, const OUString& aCaption, tools::Rectangle &rNativeBoundingRegion, tools::Rectangle &rNativeContentRegion ) override; -public: + void copyScaledArea( long nDestX, long nDestY, long nSrcX, long nSrcY, + long nSrcWidth, 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 diff --git a/vcl/ios/salios.cxx b/vcl/ios/salios.cxx new file mode 100644 index 000000000000..fc972909cdd2 --- /dev/null +++ b/vcl/ios/salios.cxx @@ -0,0 +1,433 @@ +/* -*- 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 . + */ + +// This file contains the iOS-specific versions of the functions which were touched in the commit to +// fix tdf#138122. The funtions are here (for now) as they were before that commit. The +// macOS-specific versions of these functions are in vcl/osx/salmacos.cxx. + +#include <sal/config.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <cstddef> +#include <limits> + +#include <o3tl/make_shared.hxx> +#include <basegfx/vector/b2ivector.hxx> +#include <tools/color.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/BitmapAccessMode.hxx> +#include <vcl/BitmapBuffer.hxx> +#include <vcl/BitmapColor.hxx> +#include <vcl/BitmapPalette.hxx> +#include <vcl/ColorMask.hxx> +#include <vcl/Scanline.hxx> + +#include <headless/svpframe.hxx> +#include <headless/svpinst.hxx> +#include <headless/svpvd.hxx> + +#include <quartz/salbmp.h> +#include <quartz/salgdi.h> +#include <quartz/salvd.h> +#include <quartz/utils.h> + +#include "saldatabasic.hxx" + +// From salbmp.cxx + +bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits, int nX, int nY, int nWidth, int nHeight, bool bFlipped) +{ + SAL_WARN_IF(!rLayerHolder.isSet(), "vcl", "QuartzSalBitmap::Create() from non-layered context"); + + // sanitize input parameters + if( nX < 0 ) { + nWidth += nX; + nX = 0; + } + + if( nY < 0 ) { + nHeight += nY; + nY = 0; + } + + const CGSize aLayerSize = CGLayerGetSize(rLayerHolder.get()); + + if( nWidth >= static_cast<int>(aLayerSize.width) - nX ) + nWidth = static_cast<int>(aLayerSize.width) - nX; + + if( nHeight >= static_cast<int>(aLayerSize.height) - nY ) + nHeight = static_cast<int>(aLayerSize.height) - nY; + + if( (nWidth < 0) || (nHeight < 0) ) + nWidth = nHeight = 0; + + // initialize properties + mnWidth = nWidth; + mnHeight = nHeight; + mnBits = nBitmapBits ? nBitmapBits : 32; + + // initialize drawing context + CreateContext(); + + // copy layer content into the bitmap buffer + const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX), static_cast<CGFloat>(-nY) }; + if (maGraphicContext.isSet()) // remove warning + { + if( bFlipped ) + { + CGContextTranslateCTM( maGraphicContext.get(), 0, +mnHeight ); + + CGContextScaleCTM( maGraphicContext.get(), +1, -1 ); + } + + CGContextDrawLayerAtPoint(maGraphicContext.get(), aSrcPoint, rLayerHolder.get()); + } + return true; +} + +// From salgdicommon.cxx + +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; + } + + // If called from idle layout, maContextHolder.get() is NULL, no idea what to do + if (!maContextHolder.isSet()) + return; + + // accelerate trivial operations + /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + const bool bSameGraphics = (this == pSrc); + + 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 ); + } + } +} + +void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, + long nSrcWidth, long nSrcHeight, bool /*bWindowInvalidate*/ ) +{ + SAL_WARN_IF (!maLayer.isSet(), "vcl.quartz", + "AquaSalGraphics::copyArea() for non-layered graphics this=" << this); + + if (!maLayer.isSet()) + return; + + float fScale = maLayer.getScale(); + + long nScaledSourceX = nSrcX * fScale; + long nScaledSourceY = nSrcY * fScale; + + long nScaledTargetX = nDstX * fScale; + long nScaledTargetY = nDstY * fScale; + + long nScaledSourceWidth = nSrcWidth * fScale; + long nScaledSourceHeight = nSrcHeight * fScale; + + ApplyXorContext(); + + 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(); + + if( mpXorEmulation && mpXorEmulation->IsEnabled() ) + { + xCopyContext = mpXorEmulation->GetTargetContext(); + } + + // 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); + + // 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 + + CGLayerHolder sSourceLayerHolder(maLayer); + { + const CGSize aSrcSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight); + sSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSrcSize, nullptr)); + + const CGContextRef xSrcContext = CGLayerGetContext(sSourceLayerHolder.get()); + + CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY); + if( IsFlipped() ) + { + CGContextTranslateCTM( xSrcContext, 0, +nScaledSourceHeight ); + CGContextScaleCTM( xSrcContext, +1, -1 ); + aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mnHeight * fScale); + } + CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, maLayer.get()); + } + + // draw at new destination + const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight); + CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get()); + + maContextHolder.restoreState(); + + // cleanup + if (sSourceLayerHolder.get() != maLayer.get()) + { + CGLayerRelease(sSourceLayerHolder.get()); + } + // mark the destination rectangle as updated + RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); +} + +void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef xContext, + int nBitmapDepth) +{ + SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext ); + + mbPrinter = false; + mbVirDev = true; + + // set graphics properties + maLayer = rLayer; + maContextHolder.set(xContext); + + mnBitmapDepth = nBitmapDepth; + + mbForeignContext = xContext != NULL; + + // return early if the virdev is being destroyed + if( !xContext ) + return; + + // get new graphics properties + if (!maLayer.isSet()) + { + mnWidth = CGBitmapContextGetWidth( maContextHolder.get() ); + mnHeight = CGBitmapContextGetHeight( maContextHolder.get() ); + } + else + { + const CGSize aSize = CGLayerGetSize(maLayer.get()); + mnWidth = static_cast<int>(aSize.width); + mnHeight = static_cast<int>(aSize.height); + } + + // prepare graphics for drawing + const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; + CGContextSetFillColorSpace( maContextHolder.get(), aCGColorSpace ); + CGContextSetStrokeColorSpace( maContextHolder.get(), aCGColorSpace ); + + // re-enable XorEmulation for the new context + if( mpXorEmulation ) + { + mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); + if( mpXorEmulation->IsEnabled() ) + { + maContextHolder.set(mpXorEmulation->GetMaskContext()); + } + } + + // initialize stack of CGContext states + maContextHolder.saveState(); + SetState(); +} + +/// From salvd.cxx + +void AquaSalVirtualDevice::Destroy() +{ + SAL_INFO( "vcl.virdev", "AquaSalVirtualDevice::Destroy() this=" << this << " mbForeignContext=" << mbForeignContext ); + + if( mbForeignContext ) + { + // Do not delete mxContext that we have received from outside VCL + maLayer.set(nullptr); + return; + } + + if (maLayer.isSet()) + { + if( mpGraphics ) + { + mpGraphics->SetVirDevGraphics(nullptr, nullptr); + } + CGLayerRelease(maLayer.get()); + maLayer.set(nullptr); + } + + if (maBitmapContext.isSet()) + { + void* pRawData = CGBitmapContextGetData(maBitmapContext.get()); + std::free(pRawData); + CGContextRelease(maBitmapContext.get()); + maBitmapContext.set(nullptr); + } +} + +bool AquaSalVirtualDevice::SetSize( long nDX, long nDY ) +{ + 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 + return true; + } + + if (maLayer.isSet()) + { + const CGSize aSize = CGLayerGetSize(maLayer.get()); + if( (nDX == aSize.width) && (nDY == aSize.height) ) + { + // Yay, we do not have to do anything :) + return true; + } + } + + Destroy(); + + mnWidth = nDX; + mnHeight = nDY; + + // create a Quartz layer matching to the intended virdev usage + CGContextHolder xCGContextHolder; + 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; + } + else + { + if (!xCGContextHolder.isSet()) + { + // assert(Application::IsBitmapRendering()); + mnBitmapDepth = 32; + + const int nBytesPerRow = (mnBitmapDepth * nDX) / 8; + void* pRawData = std::malloc( nBytesPerRow * nDY ); + const int nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little; + maBitmapContext.set(CGBitmapContextCreate(pRawData, nDX, nDY, 8, nBytesPerRow, + GetSalData()->mxRGBSpace, nFlags)); + xCGContextHolder = maBitmapContext; + } + } + + SAL_WARN_IF(!xCGContextHolder.isSet(), "vcl.quartz", "No context"); + + const CGSize aNewSize = { static_cast<CGFloat>(nDX), static_cast<CGFloat>(nDY) }; + maLayer.set(CGLayerCreateWithContext(xCGContextHolder.get(), aNewSize, nullptr)); + + if (maLayer.isSet() && mpGraphics) + { + // get the matching Quartz context + CGContextRef xDrawContext = CGLayerGetContext( maLayer.get() ); + mpGraphics->SetVirDevGraphics(maLayer.get(), xDrawContext, mnBitmapDepth); + } + + return maLayer.isSet(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/salgdiutils.cxx b/vcl/osx/salgdiutils.cxx index 426aea29dc78..abd90deaa84b 100644 --- a/vcl/quartz/salgdiutils.cxx +++ b/vcl/osx/salgdiutils.cxx @@ -32,6 +32,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; @@ -96,13 +129,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/osx/salinst.cxx b/vcl/osx/salinst.cxx index 45f9ba3546f3..a524f24f0293 100644 --- a/vcl/osx/salinst.cxx +++ b/vcl/osx/salinst.cxx @@ -310,6 +310,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/osx/salmacos.cxx b/vcl/osx/salmacos.cxx new file mode 100644 index 000000000000..14ced4b96afe --- /dev/null +++ b/vcl/osx/salmacos.cxx @@ -0,0 +1,383 @@ +/* -*- 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 . + */ + +// This file contains the macOS-specific versions of the functions which were touched in the commit +// to fix tdf#138122. The iOS-specific versions of these functions are kept (for now, when this +// comment is written) as they were before that commit in vcl/isx/salios.cxx. + +#include <sal/config.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <cstddef> +#include <limits> + +#include <o3tl/make_shared.hxx> +#include <basegfx/vector/b2ivector.hxx> +#include <tools/color.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/BitmapAccessMode.hxx> +#include <vcl/BitmapBuffer.hxx> +#include <vcl/BitmapColor.hxx> +#include <vcl/BitmapPalette.hxx> +#include <vcl/ColorMask.hxx> +#include <vcl/Scanline.hxx> + +#include <quartz/salbmp.h> +#include <quartz/salgdi.h> +#include <quartz/salvd.h> +#include <quartz/utils.h> + +#include <osx/saldata.hxx> +#include <osx/salframe.h> +#include <osx/salinst.h> + +// From salbmp.cxx + +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 + if( nX < 0 ) { + nWidth += nX; + nX = 0; + } + + if( nY < 0 ) { + nHeight += nY; + nY = 0; + } + + 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; + + if( nHeight >= static_cast<int>(aLayerSize.height) - nY ) + nHeight = static_cast<int>(aLayerSize.height) - nY; + + if( (nWidth < 0) || (nHeight < 0) ) + nWidth = nHeight = 0; + + // initialize properties + mnWidth = nWidth; + mnHeight = nHeight; + mnBits = nBitmapBits ? nBitmapBits : 32; + + // initialize drawing context + CreateContext(); + + // copy layer content into the bitmap buffer + 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); + } + maGraphicContext.saveState(); + CGContextScaleCTM(maGraphicContext.get(), 1 / fScale, 1 / fScale); + CGContextDrawLayerAtPoint(maGraphicContext.get(), aSrcPoint, rLayerHolder.get()); + maGraphicContext.restoreState(); + } + return true; +} + +// From salgdicommon.cxx + +void AquaSalGraphics::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraphics) +{ + 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; + + 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(long nDstX, long nDstY, long nSrcX, long nSrcY, + long nSrcWidth, long nSrcHeight, bool) +{ + if (!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); +} + +void AquaSalGraphics::copyScaledArea(long nDstX, long nDstY, long nSrcX, long nSrcY, + long nSrcWidth, long nSrcHeight, SalGraphics *pSrcGraphics) +{ + if (!pSrcGraphics) + pSrcGraphics = this; + AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); + + SAL_WARN_IF(!maLayer.isSet(), "vcl.quartz", + "AquaSalGraphics::copyScaledArea() without graphics context or for non-layered graphics this=" << this); + + if (!maContextHolder.isSet() || !maLayer.isSet()) + return; + + // Determine scaled geometry of source and target area assuming source and target area have the same scale + + 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; + + // 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(); + + // Set scale matrix of copy context to consider layer scaling + + CGContextScaleCTM(xCopyContext, 1 / fScale, 1 / fScale); + + // Creating an additional layer is required for drawing with the required scale and extent at the drawing destination + // thereafter. + 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 + + const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight); + CGContextDrawLayerInRect(xCopyContext, aTargetRect, aSourceLayerHolder.get()); + + // Housekeeping on exit + + maContextHolder.restoreState(); + if (aSourceLayerHolder.get() != maLayer.get()) + CGLayerRelease(aSourceLayerHolder.get()); + RefreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight); +} + +void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRef xContext, int nBitmapDepth) +{ + SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext); + + // Set member variables + + InvalidateContext(); + mbWindow = false; + mbPrinter = false; + mbVirDev = true; + maLayer = rLayer; + mnBitmapDepth = nBitmapDepth; + + // 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 + + CGSize aSize; + float fScale; + if (maLayer.isSet()) + { + maContextHolder.set(CGLayerGetContext(maLayer.get())); + aSize = CGLayerGetSize(maLayer.get()); + fScale = maLayer.getScale(); + } + else + { + 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); + + // Apply scale matrix to virtual device graphics + + CGContextScaleCTM(maContextHolder.get(), fScale, fScale); + + // Apply XOR emulation if required + + if (mpXorEmulation) + { + mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); + if (mpXorEmulation->IsEnabled()) + maContextHolder.set(mpXorEmulation->GetMaskContext()); + } + + // Housekeeping on exit + + maContextHolder.saveState(); + SetState(); + + SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this << + " (" << mnWidth << "x" << mnHeight << ") fScale=" << fScale << " mnBitmapDepth=" << mnBitmapDepth); +} + +// From salvd.cxx + +void AquaSalVirtualDevice::Destroy() +{ + SAL_INFO( "vcl.virdev", "AquaSalVirtualDevice::Destroy() this=" << this << " mbForeignContext=" << mbForeignContext ); + + if( mbForeignContext ) + { + // Do not delete mxContext that we have received from outside VCL + maLayer.set(nullptr); + return; + } + + if (maLayer.isSet()) + { + if( mpGraphics ) + { + mpGraphics->SetVirDevGraphics(nullptr, nullptr); + } + CGLayerRelease(maLayer.get()); + maLayer.set(nullptr); + } + + if (maBitmapContext.isSet()) + { + CGContextRelease(maBitmapContext.get()); + maBitmapContext.set(nullptr); + } +} + +bool AquaSalVirtualDevice::SetSize(long nDX, long nDY) +{ + SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::SetSize() this=" << this << + " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO")); + + // 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 / 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; + fScale = mpGraphics->GetWindowScaling(); + CGColorSpaceRef aColorSpace; + uint32_t nFlags; + if (mnBitmapDepth && (mnBitmapDepth < 16)) + { + mnBitmapDepth = 8; + aColorSpace = GetSalData()->mxGraySpace; + nFlags = kCGImageAlphaNone; + } + else + { + mnBitmapDepth = 32; + aColorSpace = GetSalData()->mxRGBSpace; + + nFlags = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; + } + + // Allocate buffer for virtual device graphics as bitmap context to store graphics with highest required (scaled) resolution + + 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)); + + 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(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/salbmp.cxx b/vcl/quartz/salbmp.cxx index 117c038c844e..ea248d9763d9 100644 --- a/vcl/quartz/salbmp.cxx +++ b/vcl/quartz/salbmp.cxx @@ -69,56 +69,6 @@ QuartzSalBitmap::~QuartzSalBitmap() doDestroy(); } -bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits, int nX, int nY, int nWidth, int nHeight, bool bFlipped) -{ - SAL_WARN_IF(!rLayerHolder.isSet(), "vcl", "QuartzSalBitmap::Create() from non-layered context"); - - // sanitize input parameters - if( nX < 0 ) { - nWidth += nX; - nX = 0; - } - - if( nY < 0 ) { - nHeight += nY; - nY = 0; - } - - const CGSize aLayerSize = CGLayerGetSize(rLayerHolder.get()); - - if( nWidth >= static_cast<int>(aLayerSize.width) - nX ) - nWidth = static_cast<int>(aLayerSize.width) - nX; - - if( nHeight >= static_cast<int>(aLayerSize.height) - nY ) - nHeight = static_cast<int>(aLayerSize.height) - nY; - - if( (nWidth < 0) || (nHeight < 0) ) - nWidth = nHeight = 0; - - // initialize properties - mnWidth = nWidth; - mnHeight = nHeight; - mnBits = nBitmapBits ? nBitmapBits : 32; - - // initialize drawing context - CreateContext(); - - // copy layer content into the bitmap buffer - const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX), static_cast<CGFloat>(-nY) }; - if (maGraphicContext.isSet()) // remove warning - { - if( bFlipped ) - { - CGContextTranslateCTM( maGraphicContext.get(), 0, +mnHeight ); - - CGContextScaleCTM( maGraphicContext.get(), +1, -1 ); - } - - CGContextDrawLayerAtPoint(maGraphicContext.get(), aSrcPoint, rLayerHolder.get()); - } - return true; -} - bool QuartzSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette ) { if( !isValidBitCount( nBits ) ) diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index b6df53319e28..db272df57587 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -200,6 +200,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 ac86808147e1..f2122dd5e2fb 100644 --- a/vcl/quartz/salgdicommon.cxx +++ b/vcl/quartz/salgdicommon.cxx @@ -294,109 +294,6 @@ static void alignLinePoint( const SalPoint* i_pIn, float& o_fX, float& o_fY ) o_fY = static_cast<float>(i_pIn->mnY ) + 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 } } }; @@ -464,81 +361,6 @@ void AquaSalGraphics::ApplyXorContext() } } -void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, - long nSrcWidth, long nSrcHeight, bool /*bWindowInvalidate*/ ) -{ - SAL_WARN_IF (!maLayer.isSet(), "vcl.quartz", - "AquaSalGraphics::copyArea() for non-layered graphics this=" << this); - -#ifdef IOS - if (!maLayer.isSet()) - return; -#endif - float fScale = maLayer.getScale(); - - long nScaledSourceX = nSrcX * fScale; - long nScaledSourceY = nSrcY * fScale; - - long nScaledTargetX = nDstX * fScale; - long nScaledTargetY = nDstY * fScale; - - long nScaledSourceWidth = nSrcWidth * fScale; - long nScaledSourceHeight = nSrcHeight * fScale; - - ApplyXorContext(); - - 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(); - - if( mpXorEmulation && mpXorEmulation->IsEnabled() ) - { - xCopyContext = mpXorEmulation->GetTargetContext(); - } - - // 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); - - // 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 - - CGLayerHolder sSourceLayerHolder(maLayer); - { - const CGSize aSrcSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight); - sSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSrcSize, nullptr)); - - const CGContextRef xSrcContext = CGLayerGetContext(sSourceLayerHolder.get()); - - CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY); - if( IsFlipped() ) - { - CGContextTranslateCTM( xSrcContext, 0, +nScaledSourceHeight ); - CGContextScaleCTM( xSrcContext, +1, -1 ); - aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mnHeight * fScale); - } - CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, maLayer.get()); - } - - // draw at new destination - const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight); - CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get()); - - maContextHolder.restoreState(); - - // cleanup - if (sSourceLayerHolder.get() != maLayer.get()) - { - CGLayerRelease(sSourceLayerHolder.get()); - } - // mark the destination rectangle as updated - RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); -} - #ifndef IOS void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) @@ -1986,62 +1808,4 @@ bool XorEmulation::UpdateTarget() return true; } -void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef xContext, - int nBitmapDepth) -{ - SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext ); - -#ifndef IOS - 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 new graphics properties - if (!maLayer.isSet()) - { - mnWidth = CGBitmapContextGetWidth( maContextHolder.get() ); - mnHeight = CGBitmapContextGetHeight( maContextHolder.get() ); - } - else - { - const CGSize aSize = CGLayerGetSize(maLayer.get()); - mnWidth = static_cast<int>(aSize.width); - mnHeight = static_cast<int>(aSize.height); - } - - // prepare graphics for drawing - const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; - CGContextSetFillColorSpace( maContextHolder.get(), aCGColorSpace ); - CGContextSetStrokeColorSpace( maContextHolder.get(), aCGColorSpace ); - - // re-enable XorEmulation for the new context - if( mpXorEmulation ) - { - mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get()); - if( mpXorEmulation->IsEnabled() ) - { - maContextHolder.set(mpXorEmulation->GetMaskContext()); - } - } - - // initialize stack of CGContext states - maContextHolder.saveState(); - SetState(); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/salvd.cxx b/vcl/quartz/salvd.cxx index c06ba33c5b6f..e9fe55d27406 100644 --- a/vcl/quartz/salvd.cxx +++ b/vcl/quartz/salvd.cxx @@ -103,6 +103,10 @@ AquaSalVirtualDevice::AquaSalVirtualDevice( AquaSalGraphics* pGraphic, long &nDX } mpGraphics->SetVirDevGraphics(maLayer, pData->rCGContext); + + SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::AquaSalVirtualDevice() this=" << this << + " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO")); + } else { @@ -156,36 +160,6 @@ AquaSalVirtualDevice::~AquaSalVirtualDevice() Destroy(); } -void AquaSalVirtualDevice::Destroy() -{ - SAL_INFO( "vcl.virdev", "AquaSalVirtualDevice::Destroy() this=" << this << " mbForeignContext=" << mbForeignContext ); - - if( mbForeignContext ) - { - // Do not delete mxContext that we have received from outside VCL - maLayer.set(nullptr); - return; - } - - if (maLayer.isSet()) - { - if( mpGraphics ) - { - mpGraphics->SetVirDevGraphics(nullptr, nullptr); - } - CGLayerRelease(maLayer.get()); - maLayer.set(nullptr); - } - - if (maBitmapContext.isSet()) - { - void* pRawData = CGBitmapContextGetData(maBitmapContext.get()); - std::free(pRawData); - CGContextRelease(maBitmapContext.get()); - maBitmapContext.set(nullptr); - } -} - SalGraphics* AquaSalVirtualDevice::AcquireGraphics() { if( mbGraphicsUsed || !mpGraphics ) @@ -201,103 +175,4 @@ void AquaSalVirtualDevice::ReleaseGraphics( SalGraphics* ) mbGraphicsUsed = false; } -bool AquaSalVirtualDevice::SetSize( long nDX, long nDY ) -{ - 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 - return true; - } - - if (maLayer.isSet()) - { - const CGSize aSize = CGLayerGetSize(maLayer.get()); - if( (nDX == aSize.width) && (nDY == aSize.height) ) - { - // Yay, we do not have to do anything :) - return true; - } - } - - Destroy(); - - mnWidth = nDX; - mnHeight = nDY; - - // create a Quartz layer matching to the intended virdev usage - CGContextHolder xCGContextHolder; - 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; - } - else - { -#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; - - const int nBytesPerRow = (mnBitmapDepth * nDX) / 8; - void* pRawData = std::malloc( nBytesPerRow * nDY ); -#ifdef MACOSX - const int nFlags = kCGImageAlphaNoneSkipFirst; -#else - const int 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"); - - const CGSize aNewSize = { static_cast<CGFloat>(nDX), static_cast<CGFloat>(nDY) }; - maLayer.set(CGLayerCreateWithContext(xCGContextHolder.get(), aNewSize, nullptr)); - - if (maLayer.isSet() && mpGraphics) - { - // get the matching Quartz context - CGContextRef xDrawContext = CGLayerGetContext( maLayer.get() ); - mpGraphics->SetVirDevGraphics(maLayer.get(), xDrawContext, mnBitmapDepth); - } - - return maLayer.isSet(); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |