diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/quartz/salbmp.cxx | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/vcl/quartz/salbmp.cxx b/vcl/quartz/salbmp.cxx new file mode 100644 index 000000000..c8f7bd7f3 --- /dev/null +++ b/vcl/quartz/salbmp.cxx @@ -0,0 +1,751 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <cstddef> +#include <limits> + +#include <o3tl/make_shared.hxx> +#include <tools/color.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/BitmapAccessMode.hxx> +#include <vcl/BitmapBuffer.hxx> +#include <vcl/BitmapColor.hxx> +#include <vcl/BitmapPalette.hxx> +#include <vcl/ColorMask.hxx> +#include <vcl/Scanline.hxx> + +#include <bitmap/bmpfast.hxx> +#include <quartz/salbmp.h> +#include <quartz/utils.h> +#include <bitmap/ScanlineTools.hxx> + +#ifdef MACOSX +#include <osx/saldata.hxx> +#else +#include <svdata.hxx> +#endif + +const unsigned long k32BitRedColorMask = 0x00ff0000; +const unsigned long k32BitGreenColorMask = 0x0000ff00; +const unsigned long k32BitBlueColorMask = 0x000000ff; + +QuartzSalBitmap::QuartzSalBitmap() + : mxCachedImage( nullptr ) + , mnBits(0) + , mnWidth(0) + , mnHeight(0) + , mnBytesPerRow(0) +{ +} + +QuartzSalBitmap::~QuartzSalBitmap() +{ + doDestroy(); +} + +bool QuartzSalBitmap::Create( const Size& rSize, vcl::PixelFormat ePixelFormat, const BitmapPalette& rBitmapPalette ) +{ + if (ePixelFormat == vcl::PixelFormat::INVALID) + return false; + + maPalette = rBitmapPalette; + mnBits = vcl::pixelFormatBitCount(ePixelFormat); + mnWidth = rSize.Width(); + mnHeight = rSize.Height(); + return AllocateUserData(); +} + +bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp ) +{ + vcl::PixelFormat ePixelFormat = vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount()); + return Create( rSalBmp, ePixelFormat); +} + +bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) +{ + vcl::PixelFormat ePixelFormat = vcl::PixelFormat::INVALID; + if (pGraphics) + ePixelFormat = vcl::bitDepthToPixelFormat(pGraphics->GetBitCount()); + else + ePixelFormat = vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount()); + + return Create( rSalBmp, ePixelFormat); +} + +bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, vcl::PixelFormat eNewPixelFormat ) +{ + const QuartzSalBitmap& rSourceBitmap = static_cast<const QuartzSalBitmap&>(rSalBmp); + + if (eNewPixelFormat != vcl::PixelFormat::INVALID && rSourceBitmap.m_pUserBuffer) + { + mnBits = vcl::pixelFormatBitCount(eNewPixelFormat); + mnWidth = rSourceBitmap.mnWidth; + mnHeight = rSourceBitmap.mnHeight; + maPalette = rSourceBitmap.maPalette; + + if( AllocateUserData() ) + { + ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette, + m_pUserBuffer.get(), rSourceBitmap.mnBits, + rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette, + rSourceBitmap.m_pUserBuffer.get() ); + return true; + } + } + return false; +} + +#if HAVE_FEATURE_SKIA + +bool QuartzSalBitmap::Create( const SkiaSalBitmap& rSalBmp, const SalTwoRect& rPosAry ) +{ + bool bRet = false; + + // Ugly but necessary to acquire the bitmap buffer because all of the + // SalBitmap instances that callers pass are already const. At least we + // only need to read, not write to the bitmap paramter. + SkiaSalBitmap& rSkiaSalBmp = const_cast<SkiaSalBitmap&>( rSalBmp ); + + BitmapBuffer *pSrcBuffer = rSkiaSalBmp.AcquireBuffer( BitmapAccessMode::Read ); + if ( !pSrcBuffer ) + return bRet; + + if ( !pSrcBuffer->mpBits ) + { + rSkiaSalBmp.ReleaseBuffer( pSrcBuffer, BitmapAccessMode::Read ); + return bRet; + } + + // Create only a 1 pixel buffer as it will always be discarded + mnBits = 32; + mnWidth = 1; + mnHeight = 1; + if( AllocateUserData() ) + { + BitmapBuffer *pDestBuffer = AcquireBuffer( BitmapAccessMode::Read ); + if ( pDestBuffer ) + { + std::unique_ptr<BitmapBuffer> pConvertedBuffer = StretchAndConvert( *pSrcBuffer, rPosAry, pDestBuffer->mnFormat, pDestBuffer->maPalette, &pDestBuffer->maColorMask ); + bool bUseDestBuffer = ( pConvertedBuffer && + pConvertedBuffer->mpBits && + pConvertedBuffer->mnFormat == pDestBuffer->mnFormat && + pConvertedBuffer->mnWidth == rPosAry.mnDestWidth && + pConvertedBuffer->mnHeight == rPosAry.mnDestHeight ); + + ReleaseBuffer( pDestBuffer, BitmapAccessMode::Read ); + + if ( bUseDestBuffer ) + { + // Surprisingly, BitmapBuffer does not delete the bits so + // discard our 1 pixel buffer and take ownership of the bits + DestroyContext(); + m_pUserBuffer.reset( pConvertedBuffer->mpBits ); + mnWidth = pConvertedBuffer->mnWidth; + mnHeight = pConvertedBuffer->mnHeight; + mnBytesPerRow = pConvertedBuffer->mnScanlineSize; + bRet = true; + } + } + } + + rSkiaSalBmp.ReleaseBuffer( pSrcBuffer, BitmapAccessMode::Read ); + + return bRet; +} + +#endif + +bool QuartzSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, + Size& /*rSize*/, bool /*bMask*/ ) +{ + return false; +} + +void QuartzSalBitmap::Destroy() +{ + doDestroy(); +} + +void QuartzSalBitmap::doDestroy() +{ + DestroyContext(); + m_pUserBuffer.reset(); +} + +void QuartzSalBitmap::DestroyContext() +{ + if( mxCachedImage ) + { + CGImageRelease( mxCachedImage ); + mxCachedImage = nullptr; + } + + if (maGraphicContext.isSet()) + { + CGContextRelease(maGraphicContext.get()); + maGraphicContext.set(nullptr); + m_pContextBuffer.reset(); + } +} + +bool QuartzSalBitmap::CreateContext() +{ + DestroyContext(); + + // prepare graphics context + // convert image from user input if available + const bool bSkipConversion = !m_pUserBuffer; + if( bSkipConversion ) + AllocateUserData(); + + // default to RGBA color space + CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; + CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst; + + // convert data into something accepted by CGBitmapContextCreate() + size_t bitsPerComponent = 8; + sal_uInt32 nContextBytesPerRow = mnBytesPerRow; + if( mnBits == 32 ) + { + // no conversion needed for truecolor + m_pContextBuffer = m_pUserBuffer; + } + else if( mnBits == 8 && maPalette.IsGreyPalette8Bit() ) + { + // no conversion needed for grayscale + m_pContextBuffer = m_pUserBuffer; + aCGColorSpace = GetSalData()->mxGraySpace; + aCGBmpInfo = kCGImageAlphaNone; + bitsPerComponent = mnBits; + } + // TODO: is special handling for 1bit input buffers worth it? + else + { + // convert user data to 32 bit + nContextBytesPerRow = mnWidth << 2; + try + { + m_pContextBuffer = o3tl::make_shared_array<sal_uInt8>(mnHeight * nContextBytesPerRow); + + if( !bSkipConversion ) + { + ConvertBitmapData( mnWidth, mnHeight, + 32, nContextBytesPerRow, maPalette, m_pContextBuffer.get(), + mnBits, mnBytesPerRow, maPalette, m_pUserBuffer.get() ); + } + } + catch( const std::bad_alloc& ) + { + maGraphicContext.set(nullptr); + } + } + + if (m_pContextBuffer) + { + maGraphicContext.set(CGBitmapContextCreate(m_pContextBuffer.get(), mnWidth, mnHeight, + bitsPerComponent, nContextBytesPerRow, + aCGColorSpace, aCGBmpInfo)); + } + + if (!maGraphicContext.isSet()) + m_pContextBuffer.reset(); + + return maGraphicContext.isSet(); +} + +bool QuartzSalBitmap::AllocateUserData() +{ + Destroy(); + + if( mnWidth && mnHeight ) + { + mnBytesPerRow = 0; + + switch( mnBits ) + { + case 1: mnBytesPerRow = (mnWidth + 7) >> 3; break; + case 4: mnBytesPerRow = (mnWidth + 1) >> 1; break; + case 8: mnBytesPerRow = mnWidth; break; + case 24: mnBytesPerRow = (mnWidth << 1) + mnWidth; break; + case 32: mnBytesPerRow = mnWidth << 2; break; + default: + assert(false && "vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!"); + } + } + + bool alloc = false; + if (mnBytesPerRow != 0 && + mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight) + { + try + { + m_pUserBuffer = o3tl::make_shared_array<sal_uInt8>(mnBytesPerRow * mnHeight); + alloc = true; + } + catch (std::bad_alloc &) {} + } + if (!alloc) + { + SAL_WARN( "vcl.quartz", "bad_alloc: " << mnWidth << "x" << mnHeight << " (" << mnBytesPerRow * mnHeight << " bytes)"); + m_pUserBuffer.reset(); + mnBytesPerRow = 0; + } + + return bool(m_pUserBuffer); +} + +void QuartzSalBitmap::ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight, + sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow, + const BitmapPalette& rDestPalette, sal_uInt8* pDestData, + sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow, + const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData ) + +{ + if( (nDestBytesPerRow == nSrcBytesPerRow) && + (nDestBits == nSrcBits) && ((nSrcBits != 8) || (rDestPalette.operator==( rSrcPalette ))) ) + { + // simple case, same format, so just copy + memcpy( pDestData, pSrcData, nHeight * nDestBytesPerRow ); + return; + } + + // try accelerated conversion if possible + // TODO: are other truecolor conversions except BGR->ARGB worth it? + bool bConverted = false; + if( (nSrcBits == 24) && (nDestBits == 32) ) + { + // TODO: extend bmpfast.cxx with a method that can be directly used here + BitmapBuffer aSrcBuf; + aSrcBuf.mnFormat = ScanlineFormat::N24BitTcBgr; + aSrcBuf.mpBits = pSrcData; + aSrcBuf.mnBitCount = nSrcBits; + aSrcBuf.mnScanlineSize = nSrcBytesPerRow; + BitmapBuffer aDstBuf; + aDstBuf.mnFormat = ScanlineFormat::N32BitTcArgb; + aDstBuf.mpBits = pDestData; + aDstBuf.mnBitCount = nDestBits; + aDstBuf.mnScanlineSize = nDestBytesPerRow; + + aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth; + aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight; + + SalTwoRect aTwoRects(0, 0, mnWidth, mnHeight, 0, 0, mnWidth, mnHeight); + bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects ); + } + + if( !bConverted ) + { + // TODO: this implementation is for clarity, not for speed + + auto pTarget = vcl::bitmap::getScanlineTransformer(nDestBits, rDestPalette); + auto pSource = vcl::bitmap::getScanlineTransformer(nSrcBits, rSrcPalette); + + if (pTarget && pSource) + { + sal_uInt32 nY = nHeight; + while( nY-- ) + { + pTarget->startLine(pDestData); + pSource->startLine(pSrcData); + + sal_uInt32 nX = nWidth; + while( nX-- ) + { + pTarget->writePixel(pSource->readPixel()); + } + pSrcData += nSrcBytesPerRow; + pDestData += nDestBytesPerRow; + } + } + } +} + +Size QuartzSalBitmap::GetSize() const +{ + return Size( mnWidth, mnHeight ); +} + +sal_uInt16 QuartzSalBitmap::GetBitCount() const +{ + return mnBits; +} + +namespace { + +struct pal_entry +{ + sal_uInt8 mnRed; + sal_uInt8 mnGreen; + sal_uInt8 mnBlue; +}; + +} + +pal_entry const aImplSalSysPalEntryAry[ 16 ] = +{ +{ 0, 0, 0 }, +{ 0, 0, 0x80 }, +{ 0, 0x80, 0 }, +{ 0, 0x80, 0x80 }, +{ 0x80, 0, 0 }, +{ 0x80, 0, 0x80 }, +{ 0x80, 0x80, 0 }, +{ 0x80, 0x80, 0x80 }, +{ 0xC0, 0xC0, 0xC0 }, +{ 0, 0, 0xFF }, +{ 0, 0xFF, 0 }, +{ 0, 0xFF, 0xFF }, +{ 0xFF, 0, 0 }, +{ 0xFF, 0, 0xFF }, +{ 0xFF, 0xFF, 0 }, +{ 0xFF, 0xFF, 0xFF } +}; + +static const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome ) +{ + if( bMonochrome ) + return Bitmap::GetGreyPalette( 1U << mnBits ); + + // at this point we should provide some kind of default palette + // since all other platforms do so, too. + static bool bDefPalInit = false; + static BitmapPalette aDefPalette256; + static BitmapPalette aDefPalette16; + static BitmapPalette aDefPalette2; + if( ! bDefPalInit ) + { + bDefPalInit = true; + aDefPalette256.SetEntryCount( 256 ); + aDefPalette16.SetEntryCount( 16 ); + aDefPalette2.SetEntryCount( 2 ); + + // Standard colors + unsigned int i; + for( i = 0; i < 16; i++ ) + { + aDefPalette16[i] = + aDefPalette256[i] = BitmapColor( aImplSalSysPalEntryAry[i].mnRed, + aImplSalSysPalEntryAry[i].mnGreen, + aImplSalSysPalEntryAry[i].mnBlue ); + } + + aDefPalette2[0] = BitmapColor( 0, 0, 0 ); + aDefPalette2[1] = BitmapColor( 0xff, 0xff, 0xff ); + + // own palette (6/6/6) + const int DITHER_PAL_STEPS = 6; + const sal_uInt8 DITHER_PAL_DELTA = 51; + int nB, nG, nR; + sal_uInt8 nRed, nGreen, nBlue; + for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA ) + { + for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA ) + { + for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA ) + { + aDefPalette256[ i ] = BitmapColor( nRed, nGreen, nBlue ); + i++; + } + } + } + } + + // now fill in appropriate palette + switch( mnBits ) + { + case 1: return aDefPalette2; + case 4: return aDefPalette16; + case 8: return aDefPalette256; + default: break; + } + + const static BitmapPalette aEmptyPalette; + return aEmptyPalette; +} + +BitmapBuffer* QuartzSalBitmap::AcquireBuffer( BitmapAccessMode /*nMode*/ ) +{ + // TODO: AllocateUserData(); + if (!m_pUserBuffer) + return nullptr; + + BitmapBuffer* pBuffer = new BitmapBuffer; + pBuffer->mnWidth = mnWidth; + pBuffer->mnHeight = mnHeight; + pBuffer->maPalette = maPalette; + pBuffer->mnScanlineSize = mnBytesPerRow; + pBuffer->mpBits = m_pUserBuffer.get(); + pBuffer->mnBitCount = mnBits; + switch( mnBits ) + { + case 1: + pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal; + break; + case 8: + pBuffer->mnFormat = ScanlineFormat::N8BitPal; + break; + case 24: + pBuffer->mnFormat = ScanlineFormat::N24BitTcBgr; + break; + case 32: + { + pBuffer->mnFormat = ScanlineFormat::N32BitTcArgb; + ColorMaskElement aRedMask(k32BitRedColorMask); + aRedMask.CalcMaskShift(); + ColorMaskElement aGreenMask(k32BitGreenColorMask); + aGreenMask.CalcMaskShift(); + ColorMaskElement aBlueMask(k32BitBlueColorMask); + aBlueMask.CalcMaskShift(); + pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask); + break; + } + default: assert(false); + } + + // some BitmapBuffer users depend on a complete palette + if( (mnBits <= 8) && !maPalette ) + pBuffer->maPalette = GetDefaultPalette( mnBits, true ); + + return pBuffer; +} + +void QuartzSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode ) +{ + // invalidate graphic context if we have different data + if( nMode == BitmapAccessMode::Write ) + { + maPalette = pBuffer->maPalette; + if (maGraphicContext.isSet()) + { + DestroyContext(); + } + InvalidateChecksum(); + } + + delete pBuffer; +} + +CGImageRef QuartzSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const +{ + if( !mxCachedImage ) + { + if (!maGraphicContext.isSet()) + { + if( !const_cast<QuartzSalBitmap*>(this)->CreateContext() ) + { + return nullptr; + } + } + mxCachedImage = CGBitmapContextCreateImage(maGraphicContext.get()); + } + + CGImageRef xCroppedImage = nullptr; + // short circuit if there is nothing to crop + if( !nX && !nY && (mnWidth == nNewWidth) && (mnHeight == nNewHeight) ) + { + xCroppedImage = mxCachedImage; + CFRetain( xCroppedImage ); + } + else + { + nY = mnHeight - (nY + nNewHeight); // adjust for y-mirrored context + const CGRect aCropRect = { { static_cast<CGFloat>(nX), static_cast<CGFloat>(nY) }, { static_cast<CGFloat>(nNewWidth), static_cast<CGFloat>(nNewHeight) } }; + xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect ); + } + + return xCroppedImage; +} + +static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/) +{ + std::free( const_cast<void*>(data) ); +} + +CGImageRef QuartzSalBitmap::CreateWithMask( const QuartzSalBitmap& rMask, + int nX, int nY, int nWidth, int nHeight ) const +{ + CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) ); + if( !xImage ) + return nullptr; + + CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight ); + if( !xMask ) + return xImage; + + // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed + // TODO: isolate in an extra method? + if( !CGImageIsMask(xMask) || rMask.GetBitCount() != 8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) ) + { + const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset + + // create the alpha mask image fitting our image + // TODO: is caching the full mask or the subimage mask worth it? + int nMaskBytesPerRow = ((nWidth + 3) & ~3); + void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight ); + CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem, + nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone ); + CGContextDrawImage( xMaskContext, xImageRect, xMask ); + CFRelease( xMask ); + CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr, + pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) ); + + static const CGFloat* pDecode = nullptr; + xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false ); + CFRelease( xDataProvider ); + CFRelease( xMaskContext ); + } + + if( !xMask ) + return xImage; + + // combine image and alpha mask + CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask ); + CFRelease( xMask ); + CFRelease( xImage ); + return xMaskedImage; +} + +/** creates an image from the given rectangle, replacing all black pixels + with nMaskColor and make all other full transparent */ +CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int nY, int nWidth, + int nHeight, Color nMaskColor ) const +{ + CGImageRef xMask = nullptr; + if (m_pUserBuffer && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight)) + { + const sal_uInt32 nDestBytesPerRow = nWidth << 2; + std::unique_ptr<sal_uInt32[]> pMaskBuffer(new (std::nothrow) sal_uInt32[ nHeight * nDestBytesPerRow / 4] ); + sal_uInt32* pDest = pMaskBuffer.get(); + + auto pSourcePixels = vcl::bitmap::getScanlineTransformer(mnBits, maPalette); + + if( pMaskBuffer && pSourcePixels ) + { + sal_uInt32 nColor; + reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff; + reinterpret_cast<sal_uInt8*>(&nColor)[1] = nMaskColor.GetRed(); + reinterpret_cast<sal_uInt8*>(&nColor)[2] = nMaskColor.GetGreen(); + reinterpret_cast<sal_uInt8*>(&nColor)[3] = nMaskColor.GetBlue(); + + sal_uInt8* pSource = m_pUserBuffer.get(); + // First to nY on y-axis, as that is our starting point (sub-image) + if( nY ) + pSource += nY * mnBytesPerRow; + + int y = nHeight; + while( y-- ) + { + pSourcePixels->startLine( pSource ); + pSourcePixels->skipPixel(nX); // Skip on x axis to nX + sal_uInt32 x = nWidth; + while( x-- ) + { + *pDest++ = (pSourcePixels->readPixel() == 0) ? nColor : 0; + } + pSource += mnBytesPerRow; + } + + CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(nullptr, pMaskBuffer.release(), nHeight * nDestBytesPerRow, &CFRTLFree) ); + xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, nullptr, true, kCGRenderingIntentDefault); + CFRelease(xDataProvider); + } + } + return xMask; +} + +/** QuartzSalBitmap::GetSystemData Get platform native image data from existing image + * + * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx + * @return true if successful +**/ +bool QuartzSalBitmap::GetSystemData( BitmapSystemData& rData ) +{ + bool bRet = false; + + if (!maGraphicContext.isSet()) + CreateContext(); + + if (maGraphicContext.isSet()) + { + bRet = true; + + if ((CGBitmapContextGetBitsPerPixel(maGraphicContext.get()) == 32) && + (CGBitmapContextGetBitmapInfo(maGraphicContext.get()) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host) + { + /** + * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it. + * + * Not sure what the above comment means. We don't use Cairo on macOS or iOS. + * + * This whole if statement was originally (before 2011) inside #ifdef CAIRO. Did we use Cairo on Mac back then? + * Anyway, nowadays (since many years, I think) we don't, so should this if statement be dropped? Fun. + */ + + CGImageRef xImage = CGBitmapContextCreateImage(maGraphicContext.get()); + + // re-create the context with single change: include kCGBitmapByteOrder32Host flag. + CGContextHolder aGraphicContextNew(CGBitmapContextCreate(CGBitmapContextGetData(maGraphicContext.get()), + CGBitmapContextGetWidth(maGraphicContext.get()), + CGBitmapContextGetHeight(maGraphicContext.get()), + CGBitmapContextGetBitsPerComponent(maGraphicContext.get()), + CGBitmapContextGetBytesPerRow(maGraphicContext.get()), + CGBitmapContextGetColorSpace(maGraphicContext.get()), + CGBitmapContextGetBitmapInfo(maGraphicContext.get()) | kCGBitmapByteOrder32Host)); + CFRelease(maGraphicContext.get()); + + // Needs to be flipped + aGraphicContextNew.saveState(); + CGContextTranslateCTM (aGraphicContextNew.get(), 0, CGBitmapContextGetHeight(aGraphicContextNew.get())); + CGContextScaleCTM (aGraphicContextNew.get(), 1.0, -1.0); + + CGContextDrawImage(aGraphicContextNew.get(), CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage); + + // Flip back + CGContextRestoreGState( aGraphicContextNew.get() ); + CGImageRelease( xImage ); + maGraphicContext = aGraphicContextNew; + } + + rData.mnWidth = mnWidth; + rData.mnHeight = mnHeight; + } + + return bRet; +} + +bool QuartzSalBitmap::ScalingSupported() const +{ + return false; +} + +bool QuartzSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ ) +{ + return false; +} + +bool QuartzSalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uInt8 /*nTol*/ ) +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |