summaryrefslogtreecommitdiff
path: root/vcl/quartz
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2019-05-21 14:46:56 +0900
committerTomaž Vajngerl <quikee@gmail.com>2019-05-22 01:33:59 +0200
commit959e8ae7ea33ce94dd80ee8ea172b6db64593873 (patch)
treec2bd09cd7407f3e830b7552fbeee6fe710e5c142 /vcl/quartz
parent3b67ad5f11714d6bea83ec8511f7723dbd999c56 (diff)
tdf#124271 use the bitmap context, handle scaling
The problem with latest macOS versions is that creating a graphic context with window (NSGraphicsContext graphicsContextWithWindow:) only works when actually drawing and otherwise it returns null. This caused problems before in AquaVrtualDevice, but we also use this when creating a device backing storage. This interestingly caused slowdowns and eventual crash, but the backtrace looked very misterious as it didn't crash because of a nullptr, but it halted all drawing commands and it crashed because of that. This changes the graphic context with a bitmap context, as it was already done in VirtualDevice and use that instead. The problem with a bitmap context is that we need to handle HiDPI scaling by ourselves now. LayerHolder was extended to store the scaling information of the layer (and its underlaying bitmap context) and provides methods that get the size of the layer in pixels or in scaling independent points (which is just size in pixels multiplied by the scaling factor). An known issue is that VirtualDevice also needs to take scaling into account, which it currently doesn't, so the text is still blurry on a HiDPI screen, but that was already true previously and is something that will be done in a different change. Change-Id: I8e10c518ecba285125746bd20525c4cb5ca67279 Reviewed-on: https://gerrit.libreoffice.org/72663 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl/quartz')
-rw-r--r--vcl/quartz/salgdicommon.cxx37
-rw-r--r--vcl/quartz/salgdiutils.cxx112
2 files changed, 89 insertions, 60 deletions
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index dd35917cd949..05e84b98b810 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -483,48 +483,67 @@ void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY,
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.get());
- // TODO: if( mnBitmapDepth > 0 )
+ CGLayerHolder sSourceLayerHolder(maLayer);
{
- const CGSize aSrcSize = CGSizeMake(nSrcWidth, nSrcHeight);
+ const CGSize aSrcSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight);
sSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSrcSize, nullptr));
SAL_INFO( "vcl.cg", "CGLayerCreateWithContext(" << xCopyContext << "," << aSrcSize << ",NULL) = " << sSourceLayerHolder.get());
const CGContextRef xSrcContext = CGLayerGetContext(sSourceLayerHolder.get());
SAL_INFO( "vcl.cg", "CGLayerGetContext(" << sSourceLayerHolder.get() << ") = " << xSrcContext);
- CGPoint aSrcPoint = CGPointMake(-nSrcX, -nSrcY);
+ CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY);
if( IsFlipped() )
{
SAL_INFO( "vcl.cg", "CGContextTranslateCTM(" << xSrcContext << ",0," << nSrcHeight << ")" );
- CGContextTranslateCTM( xSrcContext, 0, +nSrcHeight );
+ CGContextTranslateCTM( xSrcContext, 0, +nScaledSourceHeight );
SAL_INFO( "vcl.cg", "CGContextScaleCTM(" << xSrcContext << ",+1,-1)" );
CGContextScaleCTM( xSrcContext, +1, -1 );
- aSrcPoint.y = (nSrcY + nSrcHeight) - mnHeight;
+ aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mnHeight * fScale);
}
SAL_INFO( "vcl.cg", "CGContextDrawLayerAtPoint(" << xSrcContext << "," << aSrcPoint << "," << maLayer.get() << ")" );
CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, maLayer.get());
}
// draw at new destination
- const CGPoint aDstPoint = CGPointMake(+nDstX, +nDstY);
- SAL_INFO( "vcl.cg", "CGContextDrawLayerAtPoint(" << xCopyContext << "," << aDstPoint << "," << sSourceLayerHolder.get() << ")" );
- CGContextDrawLayerAtPoint(xCopyContext, aDstPoint, sSourceLayerHolder.get());
+ const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight);
+ SAL_INFO( "vcl.cg", "CGContextDrawLayerInRect(" << xCopyContext << "," << aTargetRect << "," << sSourceLayerHolder.get() << ")" );
+ CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get());
+
+ maContextHolder.restoreState();
// cleanup
if (sSourceLayerHolder.get() != maLayer.get())
diff --git a/vcl/quartz/salgdiutils.cxx b/vcl/quartz/salgdiutils.cxx
index 4c13ad788183..4b0db0c901e9 100644
--- a/vcl/quartz/salgdiutils.cxx
+++ b/vcl/quartz/salgdiutils.cxx
@@ -98,6 +98,12 @@ 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];
+
CGLayerRef rReleaseLayer = nullptr;
// check if a new drawing context is needed (e.g. after a resize)
@@ -107,7 +113,9 @@ bool AquaSalGraphics::CheckContext()
mnHeight = nHeight;
// prepare to release the corresponding resources
if (maLayer.isSet())
+ {
rReleaseLayer = maLayer.get();
+ }
else if (maContextHolder.isSet())
{
SAL_INFO("vcl.cg", "CGContextRelease(" << maContextHolder.get() << ")");
@@ -119,50 +127,49 @@ bool AquaSalGraphics::CheckContext()
if (!maContextHolder.isSet())
{
- if (mpFrame->getNSWindow())
- {
- const CGSize aLayerSize = { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) };
- NSGraphicsContext* pNSGContext = [NSGraphicsContext graphicsContextWithWindow: mpFrame->getNSWindow()];
- CGContextRef xCGContext = [pNSGContext CGContext];
- maLayer.set(CGLayerCreateWithContext(xCGContext, aLayerSize, nullptr));
- SAL_INFO("vcl.cg", "CGLayerCreateWithContext(" << xCGContext << "," << aLayerSize << ",NULL) = " << maLayer.get());
- if (maLayer.isSet())
- {
- maContextHolder.set(CGLayerGetContext(maLayer.get()));
- SAL_INFO( "vcl.cg", "CGLayerGetContext(" << maLayer.get() << ") = " << maContextHolder.get() );
- }
+ const int nBitmapDepth = 32;
+
+ float nScaledWidth = mnWidth * fScale;
+ float nScaledHeight = mnHeight * fScale;
+
+ const CGSize aLayerSize = { static_cast<CGFloat>(nScaledWidth), static_cast<CGFloat>(nScaledHeight) };
+
+ const int nBytesPerRow = (nBitmapDepth * nScaledWidth) / 8;
+ void* pRawData = std::malloc(nBytesPerRow * nScaledHeight);
+#ifdef MACOSX
+ const int nFlags = kCGImageAlphaNoneSkipFirst;
+#else
+ const int nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little;
+#endif
+ CGContextHolder aContextHolder(CGBitmapContextCreate(
+ pRawData, nScaledWidth, nScaledHeight, 8, nBytesPerRow, GetSalData()->mxRGBSpace, nFlags));
+
+ maLayer.set(CGLayerCreateWithContext(aContextHolder.get(), aLayerSize, nullptr));
+ maLayer.setScale(fScale);
+
+ CGContextRef xDrawContext = CGLayerGetContext(maLayer.get());
+ maContextHolder = xDrawContext;
- if (rReleaseLayer)
+ if (rReleaseLayer)
+ {
+ // copy original layer to resized layer
+ if (maContextHolder.isSet())
{
- // copy original layer to resized layer
- if (maContextHolder.isSet())
- {
- SAL_INFO("vcl.cg", "CGContextDrawLayerAtPoint(" << maContextHolder.get() << "," << CGPointZero << "," << rReleaseLayer << ")");
- CGContextDrawLayerAtPoint(maContextHolder.get(), CGPointZero, rReleaseLayer);
- }
- SAL_INFO("vcl.cg", "CGLayerRelease(" << rReleaseLayer << ")");
- CGLayerRelease(rReleaseLayer);
+ SAL_INFO("vcl.cg", "CGContextDrawLayerAtPoint(" << maContextHolder.get() << "," << CGPointZero << "," << rReleaseLayer << ")");
+ CGContextDrawLayerAtPoint(maContextHolder.get(), CGPointZero, rReleaseLayer);
}
- }
- else
- {
- assert(Application::IsBitmapRendering());
- const int nBitmapDepth = 32;
- const int nBytesPerRow = (nBitmapDepth * mnWidth) / 8;
- void* pRawData = std::malloc(nBytesPerRow * mnHeight);
- const int nFlags = kCGImageAlphaNoneSkipFirst;
- maContextHolder.set(CGBitmapContextCreate(pRawData, mnWidth, mnHeight, 8, nBytesPerRow,
- GetSalData()->mxRGBSpace, nFlags));
- SAL_INFO("vcl.cg", "CGBitmapContextCreate(" << mnWidth << "x" << mnHeight
- << "x" << nBitmapDepth << ") = " << maContextHolder.get());
+ SAL_INFO("vcl.cg", "CGLayerRelease(" << rReleaseLayer << ")");
+ CGLayerRelease(rReleaseLayer);
}
if (maContextHolder.isSet())
{
- CGContextTranslateCTM(maContextHolder.get(), 0, nHeight);
+ CGContextTranslateCTM(maContextHolder.get(), 0, nScaledHeight);
CGContextScaleCTM(maContextHolder.get(), 1.0, -1.0);
CGContextSetFillColorSpace(maContextHolder.get(), GetSalData()->mxRGBSpace);
CGContextSetStrokeColorSpace(maContextHolder.get(), GetSalData()->mxRGBSpace);
+ // apply a scale matrix so everything is auto-magically scaled
+ CGContextScaleCTM(maContextHolder.get(), fScale, fScale);
maContextHolder.saveState();
SetState();
@@ -173,7 +180,8 @@ bool AquaSalGraphics::CheckContext()
}
}
- SAL_WARN_IF( !maContextHolder.get() && !mbPrinter, "vcl", "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!" );
+ SAL_WARN_IF(!maContextHolder.isSet() && !mbPrinter, "vcl", "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!");
+
return maContextHolder.isSet();
}
@@ -201,28 +209,30 @@ void AquaSalGraphics::UpdateWindow( NSRect& )
NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
if (maLayer.isSet() && pContext != nullptr)
{
- CGContextRef rCGContext = [pContext CGContext];
- SAL_INFO( "vcl.cg", "[[NSGraphicsContext currentContext] CGContext] = " << rCGContext );
+ CGContextHolder rCGContextHolder([pContext CGContext]);
+ SAL_INFO("vcl.cg", "[[NSGraphicsContext currentContext] CGContext] = " << rCGContextHolder.get());
+
+ rCGContextHolder.saveState();
CGMutablePathRef rClip = mpFrame->getClipPath();
- if( rClip )
+ if (rClip)
{
- CGContextSaveGState( rCGContext );
- SAL_INFO( "vcl.cg", "CGContextBeginPath(" << rCGContext << ")" );
- CGContextBeginPath( rCGContext );
- SAL_INFO( "vcl.cg", "CGContextAddPath(" << rCGContext << "," << rClip << ")" );
- CGContextAddPath( rCGContext, rClip );
- SAL_INFO( "vcl.cg", "CGContextClip(" << rCGContext << ")" );
- CGContextClip( rCGContext );
+ CGContextBeginPath(rCGContextHolder.get());
+ SAL_INFO( "vcl.cg", "CGContextAddPath(" << rCGContextHolder.get() << "," << rClip << ")" );
+ CGContextAddPath(rCGContextHolder.get(), rClip );
+ SAL_INFO( "vcl.cg", "CGContextClip(" << rCGContextHolder.get() << ")" );
+ CGContextClip(rCGContextHolder.get());
}
ApplyXorContext();
- SAL_INFO( "vcl.cg", "CGContextDrawLayerAtPoint(" << rCGContext << "," << CGPointZero << "," << maLayer.get() << ")" );
- CGContextDrawLayerAtPoint( rCGContext, CGPointZero, maLayer.get() );
- if( rClip ) // cleanup clipping
- {
- CGContextRestoreGState( rCGContext );
- }
+
+ const CGSize aSize = maLayer.getSizePoints();
+ const CGRect aRect = CGRectMake(0, 0, aSize.width, aSize.height);
+
+ SAL_INFO( "vcl.cg", "CGContextDrawLayerInRect(" << rCGContextHolder.get() << "," << aRect << "," << maLayer.get() << ")" );
+ CGContextDrawLayerInRect(rCGContextHolder.get(), aRect, maLayer.get());
+
+ rCGContextHolder.restoreState();
}
else
{