diff options
author | Tor Lillqvist <tml@iki.fi> | 2013-01-19 23:31:57 +0200 |
---|---|---|
committer | Tor Lillqvist <tml@iki.fi> | 2013-01-19 23:47:15 +0200 |
commit | 28f794086f4ed0f456ad67a293e81b019a971197 (patch) | |
tree | 344c329f43151026556100e561370baa2e6d2f50 /vcl/quartz | |
parent | convert split cells dialog to our 99th .ui file (diff) | |
download | core-28f794086f4ed0f456ad67a293e81b019a971197.tar.gz core-28f794086f4ed0f456ad67a293e81b019a971197.zip |
AquaSalBitmap and IosSalBitmap were identical so unify
Call it QuartzSalBitmap. The more we get rid of that "Aqua" term the
better. Aqua is the name of a visual theme, not of an API. The Quartz
2D API is shared between OS X and iOS.
Diffstat (limited to 'vcl/quartz')
-rw-r--r-- | vcl/quartz/salbmp.cxx | 911 |
1 files changed, 911 insertions, 0 deletions
diff --git a/vcl/quartz/salbmp.cxx b/vcl/quartz/salbmp.cxx new file mode 100644 index 000000000000..9211c5221f87 --- /dev/null +++ b/vcl/quartz/salbmp.cxx @@ -0,0 +1,911 @@ +/* -*- 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 "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 "quartz/salbmp.h" +//#include "aqua/salinst.h" + +#include "bmpfast.hxx" + +static const unsigned long k16BitRedColorMask = 0x00007c00; +static const unsigned long k16BitGreenColorMask = 0x000003e0; +static const unsigned long k16BitBlueColorMask = 0x0000001f; + +static const unsigned long k32BitRedColorMask = 0x00ff0000; +static const unsigned long k32BitGreenColorMask = 0x0000ff00; +static const unsigned long k32BitBlueColorMask = 0x000000ff; + +// ======================================================================= + +static bool isValidBitCount( sal_uInt16 nBitCount ) +{ + return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32); +} + +// ======================================================================= + +QuartzSalBitmap::QuartzSalBitmap() +: mxGraphicContext( NULL ) +, mxCachedImage( NULL ) +, mnBits(0) +, mnWidth(0) +, mnHeight(0) +, mnBytesPerRow(0) +{ +} + +// ------------------------------------------------------------------ + +QuartzSalBitmap::~QuartzSalBitmap() +{ + Destroy(); +} + +// ------------------------------------------------------------------ + +bool QuartzSalBitmap::Create( CGLayerRef xLayer, int nBitmapBits, + int nX, int nY, int nWidth, int nHeight, bool /*bMirrorVert*/ ) +{ + DBG_ASSERT( xLayer, "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( 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 = { static_cast<CGFloat>(-nX), static_cast<CGFloat>(-nY) }; + ::CGContextDrawLayerAtPoint( mxGraphicContext, aSrcPoint, xLayer ); + return true; +} + +// ------------------------------------------------------------------ + +bool QuartzSalBitmap::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 QuartzSalBitmap::Create( const SalBitmap& rSalBmp ) +{ + return Create( rSalBmp, rSalBmp.GetBitCount() ); +} + +// ------------------------------------------------------------------ + +bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) +{ + return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() ); +} + +// ------------------------------------------------------------------ + +bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount ) +{ + const QuartzSalBitmap& rSourceBitmap = static_cast<const QuartzSalBitmap&>(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; +} + +// ------------------------------------------------------------------ + +bool QuartzSalBitmap::Create( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmapCanvas > /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ ) +{ + return false; +} + +// ------------------------------------------------------------------ + +void QuartzSalBitmap::Destroy() +{ + DestroyContext(); + maUserBuffer.reset(); +} + +// ------------------------------------------------------------------ + +void QuartzSalBitmap::DestroyContext() +{ + CGImageRelease( mxCachedImage ); + mxCachedImage = NULL; + + if( mxGraphicContext ) + { + CGContextRelease( mxGraphicContext ); + mxGraphicContext = NULL; + maContextBuffer.reset(); + } +} + +// ------------------------------------------------------------------ + +bool QuartzSalBitmap::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( const 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 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 16: mnBytesPerRow = mnWidth << 1; break; + case 24: mnBytesPerRow = (mnWidth << 1) + mnWidth; break; + case 32: mnBytesPerRow = mnWidth << 2; break; + default: + OSL_FAIL("vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!"); + } + } + + try + { + if( mnBytesPerRow ) + maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] ); + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "vcl::QuartzSalBitmap::AllocateUserData: bad alloc" ); + maUserBuffer.reset( NULL ); + 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; + virtual ~ImplPixelFormat() { } +}; + +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 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 = 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 QuartzSalBitmap::GetSize() const +{ + return Size( mnWidth, mnHeight ); +} + +// ------------------------------------------------------------------ + +sal_uInt16 QuartzSalBitmap::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* QuartzSalBitmap::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 QuartzSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly ) +{ + // invalidate graphic context if we have different data + if( !bReadOnly ) + { + maPalette = pBuffer->maPalette; + if( mxGraphicContext ) + DestroyContext(); + } + + delete pBuffer; +} + +// ------------------------------------------------------------------ + +CGImageRef QuartzSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const +{ + if( !mxCachedImage ) + { + if( !mxGraphicContext ) + if( !const_cast<QuartzSalBitmap*>(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 = { { 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*/) +{ + rtl_freeMemory( 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 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 CGFloat* 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 QuartzSalBitmap::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<sal_uInt32*>( rtl_allocateMemory( nHeight * nDestBytesPerRow ) ); + sal_uInt32* pDest = pMaskBuffer; + + ImplPixelFormat* pSourcePixels = ImplPixelFormat::GetFormat( mnBits, maPalette ); + + if( pMaskBuffer && pSourcePixels ) + { + sal_uInt32 nColor; + reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff; + reinterpret_cast<sal_uInt8*>(&nColor)[1] = SALCOLOR_RED( nMaskColor ); + reinterpret_cast<sal_uInt8*>(&nColor)[2] = SALCOLOR_GREEN( nMaskColor ); + reinterpret_cast<sal_uInt8*>(&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; +} + +// ======================================================================= + +/** 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( !mxGraphicContext ) + CreateContext(); + + if ( mxGraphicContext ) + { + bRet = true; + + 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("QuartzSalBitmap::%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; + } + + rData.rImageContext = (void *) mxGraphicContext; + rData.mnWidth = mnWidth; + rData.mnHeight = mnHeight; + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |