summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--desktop/Executable_soffice_bin.mk12
-rw-r--r--vcl/inc/quartz/salgdi.h16
-rw-r--r--vcl/osx/salinst.cxx15
-rw-r--r--vcl/quartz/salbmp.cxx22
-rw-r--r--vcl/quartz/salgdi.cxx8
-rw-r--r--vcl/quartz/salgdicommon.cxx315
-rw-r--r--vcl/quartz/salgdiutils.cxx41
-rw-r--r--vcl/quartz/salvd.cxx124
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();
}