585 lines
21 KiB
C++
585 lines
21 KiB
C++
/* -*- 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 functions 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 <vcl/bitmap.hxx>
|
|
|
|
#include <ios/iosinst.hxx>
|
|
#include <quartz/salbmp.h>
|
|
#include <quartz/salgdi.h>
|
|
#include <quartz/salvd.h>
|
|
#include <quartz/utils.h>
|
|
|
|
#include <svdata.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 AquaGraphicsBackend::copyBits(const SalTwoRect& rPosAry, SalGraphics *pSrcGraphics)
|
|
{
|
|
//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 (!mrShared.maContextHolder.isSet())
|
|
return;
|
|
|
|
AquaSharedAttributes* pSrcShared = nullptr;
|
|
|
|
if (pSrcGraphics)
|
|
{
|
|
AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
|
|
pSrcShared = &pSrc->getAquaGraphicsBackend()->GetShared();
|
|
}
|
|
else
|
|
pSrcShared = &mrShared;
|
|
|
|
// accelerate trivial operations
|
|
const bool bSameGraphics = (pSrcShared == &mrShared);
|
|
|
|
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;
|
|
}
|
|
|
|
mrShared.applyXorContext();
|
|
if (!bSameGraphics)
|
|
pSrcShared->applyXorContext();
|
|
|
|
SAL_WARN_IF (!pSrcShared->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) &&
|
|
(!mrShared.mnBitmapDepth || (aDstPoint.x + pSrcShared->mnWidth) <= mrShared.mnWidth)
|
|
&& pSrcShared->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 = mrShared.maContextHolder;
|
|
if (mrShared.mpXorEmulation && mrShared.mpXorEmulation->IsEnabled())
|
|
{
|
|
if (bSameGraphics)
|
|
{
|
|
aCopyContext.set(mrShared.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 (pSrcShared->isFlipped())
|
|
{
|
|
CGContextTranslateCTM(aCopyContext.get(), 0, +mrShared.mnHeight);
|
|
CGContextScaleCTM(aCopyContext.get(), +1, -1);
|
|
}
|
|
|
|
// TODO: pSrc->size() != this->size()
|
|
CGContextDrawLayerAtPoint(aCopyContext.get(), aDstPoint, pSrcShared->maLayer.get());
|
|
|
|
aCopyContext.restoreState();
|
|
// mark the destination rectangle as updated
|
|
refreshRect(aDstRect);
|
|
}
|
|
else
|
|
{
|
|
std::shared_ptr<SalBitmap> pBitmap;
|
|
if (pSrcGraphics)
|
|
pBitmap = pSrcGraphics->GetImpl()->getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
|
|
else
|
|
pBitmap = getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
|
|
|
|
if (pBitmap)
|
|
{
|
|
SalTwoRect aPosAry( rPosAry );
|
|
aPosAry.mnSrcX = 0;
|
|
aPosAry.mnSrcY = 0;
|
|
drawBitmap(aPosAry, *pBitmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AquaGraphicsBackend::copyArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY,
|
|
tools::Long nSrcWidth, tools::Long nSrcHeight, bool /*bWindowInvalidate*/)
|
|
{
|
|
SAL_WARN_IF (!mrShared.maLayer.isSet(), "vcl.quartz",
|
|
"AquaSalGraphics::copyArea() for non-layered graphics this=" << this);
|
|
|
|
if (!mrShared.maLayer.isSet())
|
|
return;
|
|
|
|
float fScale = mrShared.maLayer.getScale();
|
|
|
|
tools::Long nScaledSourceX = nSrcX * fScale;
|
|
tools::Long nScaledSourceY = nSrcY * fScale;
|
|
|
|
tools::Long nScaledTargetX = nDstX * fScale;
|
|
tools::Long nScaledTargetY = nDstY * fScale;
|
|
|
|
tools::Long nScaledSourceWidth = nSrcWidth * fScale;
|
|
tools::Long nScaledSourceHeight = nSrcHeight * fScale;
|
|
|
|
mrShared.applyXorContext();
|
|
|
|
mrShared.maContextHolder.saveState();
|
|
|
|
// in XOR mode the drawing context is redirected to the XOR mask
|
|
// copyArea() always works on the target context though
|
|
CGContextRef xCopyContext = mrShared.maContextHolder.get();
|
|
|
|
if (mrShared.mpXorEmulation && mrShared.mpXorEmulation->IsEnabled())
|
|
{
|
|
xCopyContext = mrShared.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(mrShared.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 (mrShared.isFlipped())
|
|
{
|
|
CGContextTranslateCTM(xSrcContext, 0, +nScaledSourceHeight);
|
|
CGContextScaleCTM(xSrcContext, +1, -1);
|
|
aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mrShared.mnHeight * fScale);
|
|
}
|
|
CGContextSetBlendMode(xSrcContext, kCGBlendModeCopy);
|
|
|
|
CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, mrShared.maLayer.get());
|
|
}
|
|
|
|
// draw at new destination
|
|
const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight);
|
|
CGContextSetBlendMode(xCopyContext, kCGBlendModeCopy);
|
|
CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get());
|
|
|
|
mrShared.maContextHolder.restoreState();
|
|
|
|
// cleanup
|
|
if (sSourceLayerHolder.get() != mrShared.maLayer.get())
|
|
{
|
|
CGLayerRelease(sSourceLayerHolder.get());
|
|
}
|
|
|
|
// mark the destination rectangle as updated
|
|
mrShared.refreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight);
|
|
}
|
|
|
|
void AquaSalGraphics::SetVirDevGraphics(SalVirtualDevice* pVirDev, CGLayerHolder const & rLayer, CGContextRef xContext,
|
|
int nBitmapDepth)
|
|
{
|
|
SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext );
|
|
|
|
maShared.mbPrinter = false;
|
|
maShared.mbVirDev = true;
|
|
|
|
// set graphics properties
|
|
maShared.maLayer = rLayer;
|
|
maShared.maContextHolder.set(xContext);
|
|
|
|
maShared.mnBitmapDepth = nBitmapDepth;
|
|
|
|
maShared.mbForeignContext = xContext != NULL;
|
|
|
|
mpBackend->UpdateGeometryProvider(pVirDev);
|
|
|
|
// return early if the virdev is being destroyed
|
|
if (!xContext)
|
|
return;
|
|
|
|
// get new graphics properties
|
|
if (!maShared.maLayer.isSet())
|
|
{
|
|
maShared.mnWidth = CGBitmapContextGetWidth(maShared.maContextHolder.get());
|
|
maShared.mnHeight = CGBitmapContextGetHeight(maShared.maContextHolder.get());
|
|
}
|
|
else
|
|
{
|
|
const CGSize aSize = CGLayerGetSize(maShared.maLayer.get());
|
|
maShared.mnWidth = static_cast<int>(aSize.width);
|
|
maShared.mnHeight = static_cast<int>(aSize.height);
|
|
}
|
|
|
|
// prepare graphics for drawing
|
|
const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
|
|
CGContextSetFillColorSpace(maShared.maContextHolder.get(), aCGColorSpace);
|
|
CGContextSetStrokeColorSpace(maShared.maContextHolder.get(), aCGColorSpace);
|
|
|
|
// re-enable XorEmulation for the new context
|
|
if (maShared.mpXorEmulation)
|
|
{
|
|
maShared.mpXorEmulation->SetTarget(maShared.mnWidth, maShared.mnHeight, maShared.mnBitmapDepth, maShared.maContextHolder.get(), maShared.maLayer.get());
|
|
if (maShared.mpXorEmulation->IsEnabled())
|
|
{
|
|
maShared.maContextHolder.set(maShared.mpXorEmulation->GetMaskContext());
|
|
}
|
|
}
|
|
|
|
// initialize stack of CGContext states
|
|
maShared.maContextHolder.saveState();
|
|
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()
|
|
{
|
|
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(this, 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( tools::Long nDX, tools::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 CGLayer 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() );
|
|
|
|
// 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(this, maLayer.get(), xDrawContext, mnBitmapDepth);
|
|
}
|
|
|
|
return maLayer.isSet();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|