536 lines
19 KiB
C++
536 lines
19 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 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/ios/salios.cxx.
|
|
|
|
#include <sal/config.h>
|
|
#include <sal/log.hxx>
|
|
#include <osl/diagnose.h>
|
|
|
|
#include <vcl/bitmap.hxx>
|
|
#include <vcl/skia/SkiaHelper.hxx>
|
|
|
|
#include <quartz/salbmp.h>
|
|
#include <quartz/salgdi.h>
|
|
#include <quartz/salvd.h>
|
|
#include <quartz/utils.h>
|
|
|
|
#include <osx/saldata.hxx>
|
|
|
|
// 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 AquaGraphicsBackend::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraphics)
|
|
{
|
|
AquaSharedAttributes* pSrcShared = nullptr;
|
|
|
|
if (pSrcGraphics)
|
|
{
|
|
AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
|
|
pSrcShared = &pSrc->getAquaGraphicsBackend()->GetShared();
|
|
}
|
|
else
|
|
pSrcShared = &mrShared;
|
|
|
|
if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0 || rPosAry.mnDestHeight <= 0)
|
|
return;
|
|
if (!mrShared.maContextHolder.isSet())
|
|
return;
|
|
|
|
SAL_WARN_IF (!pSrcShared->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 (pSrcShared->maLayer.isSet())
|
|
copyScaledArea(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY,
|
|
rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, pSrcShared);
|
|
else
|
|
{
|
|
mrShared.applyXorContext();
|
|
pSrcShared->applyXorContext();
|
|
std::shared_ptr<SalBitmap> pBitmap = pSrcGraphics->GetImpl()->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)
|
|
{
|
|
if (!mrShared.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, &mrShared);
|
|
}
|
|
|
|
void AquaGraphicsBackend::copyScaledArea(tools::Long nDstX, tools::Long nDstY,tools::Long nSrcX, tools::Long nSrcY,
|
|
tools::Long nSrcWidth, tools::Long nSrcHeight, AquaSharedAttributes* pSrcShared)
|
|
{
|
|
SAL_WARN_IF(!mrShared.maLayer.isSet(), "vcl.quartz",
|
|
"AquaSalGraphics::copyScaledArea() without graphics context or for non-layered graphics this=" << this);
|
|
|
|
if (!mrShared.maContextHolder.isSet() || !mrShared.maLayer.isSet())
|
|
return;
|
|
|
|
// Determine scaled geometry of source and target area assuming source and target area have the same scale
|
|
|
|
float fScale = mrShared.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
|
|
|
|
mrShared.applyXorContext();
|
|
mrShared.maContextHolder.saveState();
|
|
CGContextRef xCopyContext = mrShared.maContextHolder.get();
|
|
if (mrShared.mpXorEmulation && mrShared.mpXorEmulation->IsEnabled())
|
|
xCopyContext = mrShared.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(pSrcShared->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 (pSrcShared->isFlipped())
|
|
{
|
|
CGContextTranslateCTM(xSourceContext, 0, nScaledSourceHeight);
|
|
CGContextScaleCTM(xSourceContext, 1, -1);
|
|
aSrcPoint.y = nScaledSourceY + nScaledSourceHeight - mrShared.mnHeight * fScale;
|
|
}
|
|
CGContextSetBlendMode(xSourceContext, kCGBlendModeCopy);
|
|
CGContextDrawLayerAtPoint(xSourceContext, aSrcPoint, pSrcShared->maLayer.get());
|
|
|
|
// Copy source area from additional layer to target area
|
|
|
|
const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight);
|
|
CGContextSetBlendMode(xCopyContext, kCGBlendModeCopy);
|
|
CGContextDrawLayerInRect(xCopyContext, aTargetRect, aSourceLayerHolder.get());
|
|
|
|
// Housekeeping on exit
|
|
|
|
mrShared.maContextHolder.restoreState();
|
|
if (aSourceLayerHolder.get() != mrShared.maLayer.get())
|
|
CGLayerRelease(aSourceLayerHolder.get());
|
|
|
|
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);
|
|
|
|
// Set member variables
|
|
|
|
InvalidateContext();
|
|
maShared.mbWindow = false;
|
|
maShared.mbPrinter = false;
|
|
maShared.mbVirDev = true;
|
|
maShared.maLayer = rLayer;
|
|
maShared.mnBitmapDepth = nBitmapDepth;
|
|
|
|
mpBackend->UpdateGeometryProvider(pVirDev);
|
|
|
|
// Get size and scale from layer if set else from bitmap and sal::aqua::getWindowScaling(), which is used to determine
|
|
// scaling for direct graphics output too
|
|
|
|
CGSize aSize;
|
|
float fScale;
|
|
if (maShared.maLayer.isSet())
|
|
{
|
|
maShared.maContextHolder.set(CGLayerGetContext(maShared.maLayer.get()));
|
|
aSize = CGLayerGetSize(maShared.maLayer.get());
|
|
fScale = maShared.maLayer.getScale();
|
|
}
|
|
else
|
|
{
|
|
maShared.maContextHolder.set(xContext);
|
|
if (!xContext)
|
|
return;
|
|
aSize.width = CGBitmapContextGetWidth(xContext);
|
|
aSize.height = CGBitmapContextGetHeight(xContext);
|
|
fScale = sal::aqua::getWindowScaling();
|
|
}
|
|
maShared.mnWidth = aSize.width / fScale;
|
|
maShared.mnHeight = aSize.height / fScale;
|
|
|
|
// Set color space for fill and stroke
|
|
|
|
CGColorSpaceRef aColorSpace = GetSalData()->mxRGBSpace;
|
|
CGContextSetFillColorSpace(maShared.maContextHolder.get(), aColorSpace);
|
|
CGContextSetStrokeColorSpace(maShared.maContextHolder.get(), aColorSpace);
|
|
|
|
// Apply scale matrix to virtual device graphics
|
|
|
|
CGContextScaleCTM(maShared.maContextHolder.get(), fScale, fScale);
|
|
|
|
// Apply XOR emulation if required
|
|
|
|
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());
|
|
}
|
|
|
|
// Housekeeping on exit
|
|
|
|
maShared.maContextHolder.saveState();
|
|
maShared.setState();
|
|
|
|
SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this <<
|
|
" (" << maShared.mnWidth << "x" << maShared.mnHeight << ") fScale=" << fScale << " mnBitmapDepth=" << maShared.mnBitmapDepth);
|
|
}
|
|
|
|
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 and temporary context
|
|
|
|
if (m_xMaskContext)
|
|
{
|
|
CGContextRelease(m_xMaskContext);
|
|
delete[] m_pMaskBuffer;
|
|
m_xMaskContext = nullptr;
|
|
m_pMaskBuffer = nullptr;
|
|
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 bitmaps
|
|
|
|
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;
|
|
}
|
|
float fScale = sal::aqua::getWindowScaling();
|
|
size_t nScaledWidth = nWidth * fScale;
|
|
size_t nScaledHeight = nHeight * fScale;
|
|
nBytesPerRow *= nScaledWidth;
|
|
m_nBufferLongs = (nScaledHeight * nBytesPerRow + sizeof(sal_uLong) - 1) / sizeof(sal_uLong);
|
|
|
|
// Create XOR mask context
|
|
|
|
m_pMaskBuffer = new sal_uLong[m_nBufferLongs];
|
|
m_xMaskContext = CGBitmapContextCreate(m_pMaskBuffer, nScaledWidth, nScaledHeight,
|
|
nBitsPerComponent, nBytesPerRow, aCGColorSpace, aCGBmpInfo);
|
|
SAL_WARN_IF(!m_xMaskContext, "vcl.quartz", "mask context creation failed");
|
|
|
|
// Reset XOR mask to black
|
|
|
|
memset(m_pMaskBuffer, 0, m_nBufferLongs * sizeof(sal_uLong));
|
|
|
|
// Create bitmap context for manual XOR unless target context is a bitmap context
|
|
|
|
if (nTargetDepth)
|
|
m_pTempBuffer = static_cast<sal_uLong*>(CGBitmapContextGetData(m_xTargetContext));
|
|
if (!m_pTempBuffer)
|
|
{
|
|
m_pTempBuffer = new sal_uLong[m_nBufferLongs];
|
|
m_xTempContext = CGBitmapContextCreate(m_pTempBuffer, nScaledWidth, nScaledHeight,
|
|
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 XOR emulation for monochrome contexts
|
|
|
|
if (aCGColorSpace == GetSalData()->mxGraySpace)
|
|
CGContextSetBlendMode(m_xMaskContext, kCGBlendModeDifference);
|
|
|
|
// Initialize XOR mask transformation matrix and apply scale matrix to consider layer scaling
|
|
|
|
const CGAffineTransform aCTM = CGContextGetCTM(xTargetContext);
|
|
CGContextConcatCTM(m_xMaskContext, aCTM);
|
|
if (m_xTempContext)
|
|
{
|
|
CGContextConcatCTM( m_xTempContext, aCTM );
|
|
CGContextScaleCTM(m_xTempContext, 1 / fScale, 1 / fScale);
|
|
}
|
|
CGContextSaveGState(m_xMaskContext);
|
|
}
|
|
|
|
bool XorEmulation::UpdateTarget()
|
|
{
|
|
SAL_INFO("vcl.quartz", "XorEmulation::UpdateTarget() this=" << this);
|
|
|
|
if (!IsEnabled())
|
|
return false;
|
|
|
|
// Update temporary bitmap buffer
|
|
|
|
if (m_xTempContext)
|
|
{
|
|
SAL_WARN_IF(m_xTargetContext == nullptr, "vcl.quartz", "Target layer is NULL");
|
|
CGContextDrawLayerAtPoint(m_xTempContext, CGPointZero, m_xTargetLayer);
|
|
}
|
|
|
|
// XOR using XOR mask (sufficient for simple color manipulations as well as for complex XOR clipping 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 XOR results to target context
|
|
|
|
if (m_xTempContext)
|
|
{
|
|
CGImageRef xXorImage = CGBitmapContextCreateImage(m_xTempContext);
|
|
size_t nWidth = CGImageGetWidth(xXorImage);
|
|
size_t nHeight = CGImageGetHeight(xXorImage);
|
|
|
|
// Set scale matrix of target context to consider layer scaling and update target context
|
|
// TODO: Update minimal change rectangle
|
|
|
|
const CGRect aFullRect = CGRectMake(0, 0, nWidth, nHeight);
|
|
CGContextSaveGState(m_xTargetContext);
|
|
float fScale = sal::aqua::getWindowScaling();
|
|
CGContextScaleCTM(m_xTargetContext, 1 / fScale, 1 / fScale);
|
|
CGContextDrawImage(m_xTargetContext, aFullRect, xXorImage);
|
|
CGContextRestoreGState(m_xTargetContext);
|
|
CGImageRelease(xXorImage);
|
|
}
|
|
|
|
// Reset XOR mask 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())
|
|
{
|
|
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"));
|
|
|
|
// 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
|
|
// sal::aqua::getWindowScaling(), which is used to determine scaling for direct graphics output too
|
|
|
|
mnWidth = nDX;
|
|
mnHeight = nDY;
|
|
fScale = sal::aqua::getWindowScaling();
|
|
CGColorSpaceRef aColorSpace;
|
|
uint32_t nFlags;
|
|
if (mnBitmapDepth && (mnBitmapDepth < 16))
|
|
{
|
|
mnBitmapDepth = 8;
|
|
aColorSpace = GetSalData()->mxGraySpace;
|
|
nFlags = kCGImageAlphaNone;
|
|
}
|
|
else
|
|
{
|
|
mnBitmapDepth = 32;
|
|
aColorSpace = GetSalData()->mxRGBSpace;
|
|
|
|
nFlags = uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Host);
|
|
}
|
|
|
|
if (SkiaHelper::isVCLSkiaEnabled())
|
|
{
|
|
mpGraphics->SetVirDevGraphics(this, maLayer, nullptr, mnBitmapDepth);
|
|
return true;
|
|
}
|
|
|
|
// 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(this, maLayer, CGLayerGetContext(maLayer.get()), mnBitmapDepth);
|
|
|
|
SAL_WARN_IF(!maBitmapContext.isSet(), "vcl.quartz", "No context");
|
|
|
|
return maLayer.isSet();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|