/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" #include #include "basebmp/scanlineformats.hxx" #include "basebmp/color.hxx" #include "basegfx/vector/b2ivector.hxx" #include "tools/color.hxx" #include "vcl/bitmap.hxx" // for BitmapSystemData #include "vcl/salbtype.hxx" #include "aqua/salbmp.h" #include "aqua/salinst.h" #include "bmpfast.hxx" // ======================================================================= static bool isValidBitCount( sal_uInt16 nBitCount ) { return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32); } // ======================================================================= AquaSalBitmap::AquaSalBitmap() : mxGraphicContext( NULL ) , mxCachedImage( NULL ) , mnBits(0) , mnWidth(0) , mnHeight(0) , mnBytesPerRow(0) { } // ------------------------------------------------------------------ AquaSalBitmap::~AquaSalBitmap() { Destroy(); } // ------------------------------------------------------------------ bool AquaSalBitmap::Create( CGLayerRef xLayer, int nBitmapBits, int nX, int nY, int nWidth, int nHeight, bool /*bMirrorVert*/ ) { DBG_ASSERT( xLayer, "AquaSalBitmap::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( xLayer ); if( nWidth >= (int)aLayerSize.width - nX ) nWidth = (int)aLayerSize.width - nX; if( nHeight >= (int)aLayerSize.height - nY ) nHeight = (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 = CGPointMake( -nX, -nY); ::CGContextDrawLayerAtPoint( mxGraphicContext, aSrcPoint, xLayer ); return true; } // ------------------------------------------------------------------ bool AquaSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette ) { if( !isValidBitCount( nBits ) ) return false; maPalette = rBitmapPalette; mnBits = nBits; mnWidth = rSize.Width(); mnHeight = rSize.Height(); return AllocateUserData(); } // ------------------------------------------------------------------ bool AquaSalBitmap::Create( const SalBitmap& rSalBmp ) { return Create( rSalBmp, rSalBmp.GetBitCount() ); } // ------------------------------------------------------------------ bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) { return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() ); } // ------------------------------------------------------------------ bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount ) { const AquaSalBitmap& rSourceBitmap = static_cast(rSalBmp); if( isValidBitCount( nNewBitCount ) && rSourceBitmap.maUserBuffer.get() ) { mnBits = nNewBitCount; mnWidth = rSourceBitmap.mnWidth; mnHeight = rSourceBitmap.mnHeight; maPalette = rSourceBitmap.maPalette; if( AllocateUserData() ) { ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette, maUserBuffer.get(), rSourceBitmap.mnBits, rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette, rSourceBitmap.maUserBuffer.get() ); return true; } } return false; } // ------------------------------------------------------------------ void AquaSalBitmap::Destroy() { DestroyContext(); maUserBuffer.reset(); } // ------------------------------------------------------------------ void AquaSalBitmap::DestroyContext() { CGImageRelease( mxCachedImage ); mxCachedImage = NULL; if( mxGraphicContext ) { CGContextRelease( mxGraphicContext ); mxGraphicContext = NULL; maContextBuffer.reset(); } } // ------------------------------------------------------------------ bool AquaSalBitmap::CreateContext() { DestroyContext(); // prepare graphics context // convert image from user input if available const bool bSkipConversion = !maUserBuffer; 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 = (mnBits == 16) ? 5 : 8; sal_uInt32 nContextBytesPerRow = mnBytesPerRow; if( (mnBits == 16) || (mnBits == 32) ) { // no conversion needed for truecolor maContextBuffer = maUserBuffer; } else if( (mnBits == 8) && maPalette.IsGreyPalette() ) { // no conversion needed for grayscale maContextBuffer = maUserBuffer; 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 { maContextBuffer.reset( new sal_uInt8[ mnHeight * nContextBytesPerRow ] ); if( !bSkipConversion ) ConvertBitmapData( mnWidth, mnHeight, 32, nContextBytesPerRow, maPalette, maContextBuffer.get(), mnBits, mnBytesPerRow, maPalette, maUserBuffer.get() ); } catch( std::bad_alloc ) { mxGraphicContext = 0; } } if( maContextBuffer.get() ) { mxGraphicContext = ::CGBitmapContextCreate( maContextBuffer.get(), mnWidth, mnHeight, bitsPerComponent, nContextBytesPerRow, aCGColorSpace, aCGBmpInfo ); } if( !mxGraphicContext ) maContextBuffer.reset(); return mxGraphicContext != NULL; } // ------------------------------------------------------------------ bool AquaSalBitmap::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 16: mnBytesPerRow = mnWidth << 1; break; case 24: mnBytesPerRow = (mnWidth << 1) + mnWidth; break; case 32: mnBytesPerRow = mnWidth << 2; break; default: DBG_ERROR("vcl::AquaSalBitmap::AllocateUserData(), illegal bitcount!"); } } try { if( mnBytesPerRow ) maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] ); } catch( const std::bad_alloc& ) { DBG_ERROR( "vcl::AquaSalBitmap::AllocateUserData: bad alloc" ); maUserBuffer.reset(); mnBytesPerRow = 0; } return maUserBuffer.get() != 0; } // ------------------------------------------------------------------ class ImplPixelFormat { protected: sal_uInt8* pData; public: static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette ); virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; } virtual void SkipPixel( sal_uInt32 nPixel ) = 0; virtual ColorData ReadPixel() = 0; virtual void WritePixel( ColorData nColor ) = 0; }; class ImplPixelFormat32 : public ImplPixelFormat // currently ARGB-format for 32bit depth { public: virtual void SkipPixel( sal_uInt32 nPixel ) { pData += nPixel << 2; } virtual ColorData ReadPixel() { const ColorData c = RGB_COLORDATA( pData[1], pData[2], pData[3] ); pData += 4; return c; } virtual void WritePixel( ColorData nColor ) { *pData++ = 0; *pData++ = COLORDATA_RED( nColor ); *pData++ = COLORDATA_GREEN( nColor ); *pData++ = COLORDATA_BLUE( nColor ); } }; class ImplPixelFormat24 : public ImplPixelFormat // currently BGR-format for 24bit depth { public: virtual void SkipPixel( sal_uInt32 nPixel ) { pData += (nPixel << 1) + nPixel; } virtual ColorData ReadPixel() { const ColorData c = RGB_COLORDATA( pData[2], pData[1], pData[0] ); pData += 3; return c; } virtual void WritePixel( ColorData nColor ) { *pData++ = COLORDATA_BLUE( nColor ); *pData++ = COLORDATA_GREEN( nColor ); *pData++ = COLORDATA_RED( nColor ); } }; class ImplPixelFormat16 : public ImplPixelFormat // currently R5G6B5-format for 16bit depth { protected: sal_uInt16* pData16; public: virtual void StartLine( sal_uInt8* pLine ) { pData16 = (sal_uInt16*)pLine; } virtual void SkipPixel( sal_uInt32 nPixel ) { pData += nPixel; } virtual ColorData ReadPixel() { const ColorData c = RGB_COLORDATA( (*pData & 0x7c00) >> 7, (*pData & 0x03e0) >> 2 , (*pData & 0x001f) << 3 ); pData++; return c; } virtual void WritePixel( ColorData nColor ) { *pData++ = ((COLORDATA_RED( nColor ) & 0xf8 ) << 7 ) || ((COLORDATA_GREEN( nColor ) & 0xf8 ) << 2 ) || ((COLORDATA_BLUE( nColor ) & 0xf8 ) >> 3 ); } }; class ImplPixelFormat8 : public ImplPixelFormat { private: const BitmapPalette& mrPalette; public: ImplPixelFormat8( const BitmapPalette& rPalette ) : mrPalette( rPalette ) { } virtual void SkipPixel( sal_uInt32 nPixel ) { pData += nPixel; } virtual ColorData ReadPixel() { return mrPalette[ *pData++ ].operator Color().GetColor(); } virtual void WritePixel( ColorData nColor ) { const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) ); *pData++ = static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ); } }; class ImplPixelFormat4 : public ImplPixelFormat { private: const BitmapPalette& mrPalette; sal_uInt32 mnX; sal_uInt32 mnShift; public: ImplPixelFormat4( const BitmapPalette& rPalette ) : mrPalette( rPalette ) { } virtual void SkipPixel( sal_uInt32 nPixel ) { mnX += nPixel; if( (nPixel & 1) ) mnShift ^= 4; } virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; mnX = 0; mnShift = 4; } virtual ColorData ReadPixel() { const BitmapColor& rColor = mrPalette[( pData[mnX >> 1] >> mnShift) & 0x0f]; mnX++; mnShift ^= 4; return rColor.operator Color().GetColor(); } virtual void WritePixel( ColorData nColor ) { const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) ); pData[mnX>>1] &= (0xf0 >> mnShift); pData[mnX>>1] |= (static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ) & 0x0f); mnX++; mnShift ^= 4; } }; class ImplPixelFormat1 : public ImplPixelFormat { private: const BitmapPalette& mrPalette; sal_uInt32 mnX; public: ImplPixelFormat1( const BitmapPalette& rPalette ) : mrPalette( rPalette ) { } virtual void SkipPixel( sal_uInt32 nPixel ) { mnX += nPixel; } virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; mnX = 0; } virtual ColorData ReadPixel() { const BitmapColor& rColor = mrPalette[ (pData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1]; mnX++; return rColor.operator Color().GetColor(); } virtual void WritePixel( ColorData nColor ) { const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) ); if( mrPalette.GetBestIndex( aColor ) & 1 ) pData[ mnX >> 3 ] |= 1 << ( 7 - ( mnX & 7 ) ); else pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) ); mnX++; } }; ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette ) { switch( nBits ) { case 1: return new ImplPixelFormat1( rPalette ); case 4: return new ImplPixelFormat4( rPalette ); case 8: return new ImplPixelFormat8( rPalette ); case 16: return new ImplPixelFormat16; case 24: return new ImplPixelFormat24; case 32: return new ImplPixelFormat32; } return 0; } void AquaSalBitmap::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 = BMP_FORMAT_24BIT_TC_BGR; aSrcBuf.mpBits = pSrcData; aSrcBuf.mnBitCount = nSrcBits; aSrcBuf.mnScanlineSize = nSrcBytesPerRow; BitmapBuffer aDstBuf; aDstBuf.mnFormat = BMP_FORMAT_32BIT_TC_ARGB; aDstBuf.mpBits = pDestData; aSrcBuf.mnBitCount = nDestBits; aDstBuf.mnScanlineSize = nDestBytesPerRow; aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth; aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight; SalTwoRect aTwoRects; aTwoRects.mnSrcX = aTwoRects.mnDestX = 0; aTwoRects.mnSrcY = aTwoRects.mnDestY = 0; aTwoRects.mnSrcWidth = aTwoRects.mnDestWidth = mnWidth; aTwoRects.mnSrcHeight = aTwoRects.mnDestHeight = mnHeight; bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects ); } if( !bConverted ) { // TODO: this implementation is for clarety, not for speed ImplPixelFormat* pD = ImplPixelFormat::GetFormat( nDestBits, rDestPalette ); ImplPixelFormat* pS = ImplPixelFormat::GetFormat( nSrcBits, rSrcPalette ); if( pD && pS ) { sal_uInt32 nY = nHeight; while( nY-- ) { pD->StartLine( pDestData ); pS->StartLine( pSrcData ); sal_uInt32 nX = nWidth; while( nX-- ) pD->WritePixel( pS->ReadPixel() ); pSrcData += nSrcBytesPerRow; pDestData += nDestBytesPerRow; } } delete pS; delete pD; } } // ------------------------------------------------------------------ Size AquaSalBitmap::GetSize() const { return Size( mnWidth, mnHeight ); } // ------------------------------------------------------------------ sal_uInt16 AquaSalBitmap::GetBitCount() const { return mnBits; } // ------------------------------------------------------------------ static struct pal_entry { sal_uInt8 mnRed; sal_uInt8 mnGreen; sal_uInt8 mnBlue; } 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 } }; 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* AquaSalBitmap::AcquireBuffer( bool /*bReadOnly*/ ) { if( !maUserBuffer.get() ) // || maContextBuffer.get() && (maUserBuffer.get() != maContextBuffer.get()) ) { fprintf(stderr,"ASB::Acq(%dx%d,d=%d)\n",mnWidth,mnHeight,mnBits); // TODO: AllocateUserData(); return NULL; } BitmapBuffer* pBuffer = new BitmapBuffer; pBuffer->mnWidth = mnWidth; pBuffer->mnHeight = mnHeight; pBuffer->maPalette = maPalette; pBuffer->mnScanlineSize = mnBytesPerRow; pBuffer->mpBits = maUserBuffer.get(); pBuffer->mnBitCount = mnBits; switch( mnBits ) { case 1: pBuffer->mnFormat = BMP_FORMAT_1BIT_MSB_PAL; break; case 4: pBuffer->mnFormat = BMP_FORMAT_4BIT_MSN_PAL; break; case 8: pBuffer->mnFormat = BMP_FORMAT_8BIT_PAL; break; case 16: pBuffer->mnFormat = BMP_FORMAT_16BIT_TC_MSB_MASK; pBuffer->maColorMask = ColorMask( k16BitRedColorMask, k16BitGreenColorMask, k16BitBlueColorMask ); break; case 24: pBuffer->mnFormat = BMP_FORMAT_24BIT_TC_BGR; break; case 32: pBuffer->mnFormat = BMP_FORMAT_32BIT_TC_ARGB; pBuffer->maColorMask = ColorMask( k32BitRedColorMask, k32BitGreenColorMask, k32BitBlueColorMask ); break; } pBuffer->mnFormat |= BMP_FORMAT_BOTTOM_UP; // some BitmapBuffer users depend on a complete palette if( (mnBits <= 8) && !maPalette ) pBuffer->maPalette = GetDefaultPalette( mnBits, true ); return pBuffer; } // ------------------------------------------------------------------ void AquaSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly ) { // invalidate graphic context if we have different data if( !bReadOnly ) { maPalette = pBuffer->maPalette; if( mxGraphicContext ) DestroyContext(); } delete pBuffer; } // ------------------------------------------------------------------ CGImageRef AquaSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const { if( !mxCachedImage ) { if( !mxGraphicContext ) if( !const_cast(this)->CreateContext() ) return NULL; mxCachedImage = CGBitmapContextCreateImage( mxGraphicContext ); } CGImageRef xCroppedImage = NULL; // 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 = CGRectMake( nX, nY, nNewWidth, nNewHeight); xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect ); } return xCroppedImage; } // ------------------------------------------------------------------ static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/) { rtl_freeMemory( const_cast(data) ); } CGImageRef AquaSalBitmap::CreateWithMask( const AquaSalBitmap& rMask, int nX, int nY, int nWidth, int nHeight ) const { CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) ); if( !xImage ) return NULL; 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) || (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 = rtl_allocateMemory( nMaskBytesPerRow * nHeight ); CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem, nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone ); CGContextDrawImage( xMaskContext, xImageRect, xMask ); CFRelease( xMask ); CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( NULL, pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) ); static const float* pDecode = NULL; 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 AquaSalBitmap::CreateColorMask( int nX, int nY, int nWidth, int nHeight, SalColor nMaskColor ) const { CGImageRef xMask = 0; if( maUserBuffer.get() && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight) ) { const sal_uInt32 nDestBytesPerRow = nWidth << 2; sal_uInt32* pMaskBuffer = static_cast( rtl_allocateMemory( nHeight * nDestBytesPerRow ) ); sal_uInt32* pDest = pMaskBuffer; ImplPixelFormat* pSourcePixels = ImplPixelFormat::GetFormat( mnBits, maPalette ); if( pMaskBuffer && pSourcePixels ) { sal_uInt32 nColor; reinterpret_cast(&nColor)[0] = 0xff; reinterpret_cast(&nColor)[1] = SALCOLOR_RED( nMaskColor ); reinterpret_cast(&nColor)[2] = SALCOLOR_GREEN( nMaskColor ); reinterpret_cast(&nColor)[3] = SALCOLOR_BLUE( nMaskColor ); sal_uInt8* pSource = maUserBuffer.get(); if( nY ) pSource += nY * mnBytesPerRow; int y = nHeight; while( y-- ) { pSourcePixels->StartLine( pSource ); pSourcePixels->SkipPixel(nX); sal_uInt32 x = nWidth; while( x-- ) { *pDest++ = ( pSourcePixels->ReadPixel() == 0 ) ? nColor : 0; } pSource += mnBytesPerRow; } CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(NULL, pMaskBuffer, nHeight * nDestBytesPerRow, &CFRTLFree) ); xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, NULL, true, kCGRenderingIntentDefault); CFRelease(xDataProvider); } else { free(pMaskBuffer); } delete pSourcePixels; } return xMask; } // ======================================================================= /** AquaSalBitmap::GetSystemData Get platform native image data from existing image * * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx * @return true if successful **/ bool AquaSalBitmap::GetSystemData( BitmapSystemData& rData ) { bool bRet = false; if( !mxGraphicContext ) CreateContext(); if ( mxGraphicContext ) { bRet = true; #ifdef CAIRO if ((CGBitmapContextGetBitsPerPixel(mxGraphicContext) == 32) && (CGBitmapContextGetBitmapInfo(mxGraphicContext) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host) { /** * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it. */ OSL_TRACE("AquaSalBitmap::%s(): kCGBitmapByteOrder32Host not found => inserting it.",__func__); CGImageRef xImage = CGBitmapContextCreateImage (mxGraphicContext); // re-create the context with single change: include kCGBitmapByteOrder32Host flag. CGContextRef mxGraphicContextNew = CGBitmapContextCreate( CGBitmapContextGetData(mxGraphicContext), CGBitmapContextGetWidth(mxGraphicContext), CGBitmapContextGetHeight(mxGraphicContext), CGBitmapContextGetBitsPerComponent(mxGraphicContext), CGBitmapContextGetBytesPerRow(mxGraphicContext), CGBitmapContextGetColorSpace(mxGraphicContext), CGBitmapContextGetBitmapInfo(mxGraphicContext) | kCGBitmapByteOrder32Host); CFRelease(mxGraphicContext); // Needs to be flipped CGContextSaveGState( mxGraphicContextNew ); CGContextTranslateCTM (mxGraphicContextNew, 0, CGBitmapContextGetHeight(mxGraphicContextNew)); CGContextScaleCTM (mxGraphicContextNew, 1.0, -1.0); CGContextDrawImage(mxGraphicContextNew, CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage); // Flip back CGContextRestoreGState( mxGraphicContextNew ); CGImageRelease( xImage ); mxGraphicContext = mxGraphicContextNew; } #endif rData.rImageContext = (void *) mxGraphicContext; rData.mnWidth = mnWidth; rData.mnHeight = mnHeight; } return bRet; }