summaryrefslogtreecommitdiff
path: root/vcl/quartz
diff options
context:
space:
mode:
authorTor Lillqvist <tml@iki.fi>2013-01-19 23:31:57 +0200
committerTor Lillqvist <tml@iki.fi>2013-01-19 23:47:15 +0200
commit28f794086f4ed0f456ad67a293e81b019a971197 (patch)
tree344c329f43151026556100e561370baa2e6d2f50 /vcl/quartz
parent621d1fba84e13a28843fb7a7befb28e26a6d0a15 (diff)
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.cxx911
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: */