summaryrefslogtreecommitdiff
path: root/vcl/ios/salios.cxx
diff options
context:
space:
mode:
authorThorsten Wagner <thorsten.wagner.4@gmail.com>2021-05-19 17:47:58 +0200
committerAdolfo Jayme Barrientos <fitojb@ubuntu.com>2021-05-22 18:02:29 +0200
commit2cce064e6bb570361c845f7b3d82960f89c70bb1 (patch)
treea301056c4cd056d4e2606e906e6d29d446eaa9ce /vcl/ios/salios.cxx
parent77b505b0b9fd86d2bd8e82f2be7d40307dfd1493 (diff)
tdf#142061 Add window scaling to XOR emulation on macOS
Window scaling for retina displays on macOS has been added to fix tdf#138122 in commit 1a167625314bf36b735176ed488e6ba9b5e9b675 Missing window scaling for XOR emulation is added by this change. Code modified for macOS is moved from quartz/salgidcommon.cxx to osx/salmacos.cxx while original code is copied to ios/salios.cxx to prevent modifications for iOS. Change-Id: Ia8c52f9045379cc37d5aff1279650db0dddee8c0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115816 Tested-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com> Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Diffstat (limited to 'vcl/ios/salios.cxx')
-rw-r--r--vcl/ios/salios.cxx147
1 files changed, 147 insertions, 0 deletions
diff --git a/vcl/ios/salios.cxx b/vcl/ios/salios.cxx
index ac11faff3072..335c14264c64 100644
--- a/vcl/ios/salios.cxx
+++ b/vcl/ios/salios.cxx
@@ -325,6 +325,153 @@ void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextR
maShared.setState();
}
+void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth,
+ CGContextRef xTargetContext, CGLayerRef xTargetLayer )
+{
+ SAL_INFO( "vcl.quartz", "XorEmulation::SetTarget() this=" << this <<
+ " (" << nWidth << "x" << nHeight << ") depth=" << nTargetDepth <<
+ " context=" << xTargetContext << " layer=" << xTargetLayer );
+
+ // prepare to replace old mask+temp context
+ if( m_xMaskContext )
+ {
+ // cleanup the mask context
+ CGContextRelease( m_xMaskContext );
+ delete[] m_pMaskBuffer;
+ m_xMaskContext = nullptr;
+ m_pMaskBuffer = nullptr;
+
+ // cleanup the temp context if needed
+ if( m_xTempContext )
+ {
+ CGContextRelease( m_xTempContext );
+ delete[] m_pTempBuffer;
+ m_xTempContext = nullptr;
+ m_pTempBuffer = nullptr;
+ }
+ }
+
+ // return early if there is nothing more to do
+ if( !xTargetContext )
+ {
+ return;
+ }
+ // retarget drawing operations to the XOR mask
+ m_xTargetLayer = xTargetLayer;
+ m_xTargetContext = xTargetContext;
+
+ // prepare creation of matching CGBitmaps
+ CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
+ CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
+ int nBitDepth = nTargetDepth;
+ if( !nBitDepth )
+ {
+ nBitDepth = 32;
+ }
+ int nBytesPerRow = 4;
+ const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8;
+ if( nBitDepth <= 8 )
+ {
+ aCGColorSpace = GetSalData()->mxGraySpace;
+ aCGBmpInfo = kCGImageAlphaNone;
+ nBytesPerRow = 1;
+ }
+ nBytesPerRow *= nWidth;
+ m_nBufferLongs = (nHeight * nBytesPerRow + sizeof(sal_uLong)-1) / sizeof(sal_uLong);
+
+ // create a XorMask context
+ m_pMaskBuffer = new sal_uLong[ m_nBufferLongs ];
+ m_xMaskContext = CGBitmapContextCreate( m_pMaskBuffer,
+ nWidth, nHeight,
+ nBitsPerComponent, nBytesPerRow,
+ aCGColorSpace, aCGBmpInfo );
+ SAL_WARN_IF( !m_xMaskContext, "vcl.quartz", "mask context creation failed" );
+
+ // reset the XOR mask to black
+ memset( m_pMaskBuffer, 0, m_nBufferLongs * sizeof(sal_uLong) );
+
+ // a bitmap context will be needed for manual XORing
+ // create one unless the target context is a bitmap context
+ if( nTargetDepth )
+ {
+ m_pTempBuffer = static_cast<sal_uLong*>(CGBitmapContextGetData( m_xTargetContext ));
+ }
+ if( !m_pTempBuffer )
+ {
+ // create a bitmap context matching to the target context
+ m_pTempBuffer = new sal_uLong[ m_nBufferLongs ];
+ m_xTempContext = CGBitmapContextCreate( m_pTempBuffer,
+ nWidth, nHeight,
+ nBitsPerComponent, nBytesPerRow,
+ aCGColorSpace, aCGBmpInfo );
+ SAL_WARN_IF( !m_xTempContext, "vcl.quartz", "temp context creation failed" );
+ }
+
+ // initialize XOR mask context for drawing
+ CGContextSetFillColorSpace( m_xMaskContext, aCGColorSpace );
+ CGContextSetStrokeColorSpace( m_xMaskContext, aCGColorSpace );
+ CGContextSetShouldAntialias( m_xMaskContext, false );
+
+ // improve the XorMask's XOR emulation a little
+ // NOTE: currently only enabled for monochrome contexts
+ if( aCGColorSpace == GetSalData()->mxGraySpace )
+ {
+ CGContextSetBlendMode( m_xMaskContext, kCGBlendModeDifference );
+ }
+ // initialize the transformation matrix to the drawing target
+ const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext );
+ CGContextConcatCTM( m_xMaskContext, aCTM );
+ if( m_xTempContext )
+ {
+ CGContextConcatCTM( m_xTempContext, aCTM );
+ }
+ // initialize the default XorMask graphics state
+ CGContextSaveGState( m_xMaskContext );
+}
+
+bool XorEmulation::UpdateTarget()
+{
+ SAL_INFO( "vcl.quartz", "XorEmulation::UpdateTarget() this=" << this );
+
+ if( !IsEnabled() )
+ {
+ return false;
+ }
+ // update the temp bitmap buffer if needed
+ if( m_xTempContext )
+ {
+ SAL_WARN_IF( m_xTargetContext == nullptr, "vcl.quartz", "Target layer is NULL");
+ CGContextDrawLayerAtPoint( m_xTempContext, CGPointZero, m_xTargetLayer );
+ }
+ // do a manual XOR with the XorMask
+ // this approach suffices for simple color manipulations
+ // and also the complex-clipping-XOR-trick used in metafiles
+ const sal_uLong* pSrc = m_pMaskBuffer;
+ sal_uLong* pDst = m_pTempBuffer;
+ for( int i = m_nBufferLongs; --i >= 0;)
+ {
+ *(pDst++) ^= *(pSrc++);
+ }
+ // write back the XOR results to the target context
+ if( m_xTempContext )
+ {
+ CGImageRef xXorImage = CGBitmapContextCreateImage( m_xTempContext );
+ const int nWidth = static_cast<int>(CGImageGetWidth( xXorImage ));
+ const int nHeight = static_cast<int>(CGImageGetHeight( xXorImage ));
+ // TODO: update minimal changerect
+ const CGRect aFullRect = CGRectMake(0, 0, nWidth, nHeight);
+ CGContextDrawImage( m_xTargetContext, aFullRect, xXorImage );
+ CGImageRelease( xXorImage );
+ }
+
+ // reset the XorMask to black again
+ // TODO: not needed for last update
+ memset( m_pMaskBuffer, 0, m_nBufferLongs * sizeof(sal_uLong) );
+
+ // TODO: return FALSE if target was not changed
+ return true;
+}
+
/// From salvd.cxx
void AquaSalVirtualDevice::Destroy()