xref: /aoo42x/main/vcl/aqua/source/gdi/salbmp.cxx (revision cd426cce)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <boost/bind.hpp>
28 
29 #include "basebmp/scanlineformats.hxx"
30 #include "basebmp/color.hxx"
31 
32 #include "basegfx/vector/b2ivector.hxx"
33 
34 #include "tools/color.hxx"
35 
36 #include "vcl/bitmap.hxx" // for BitmapSystemData
37 #include "vcl/salbtype.hxx"
38 
39 #include "aqua/salbmp.h"
40 #include "aqua/salinst.h"
41 
42 #include "bmpfast.hxx"
43 
44 // =======================================================================
45 
46 static bool isValidBitCount( sal_uInt16 nBitCount )
47 {
48 	return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32);
49 }
50 
51 // =======================================================================
52 
53 AquaSalBitmap::AquaSalBitmap()
54 : mxGraphicContext( NULL )
55 , mxCachedImage( NULL )
56 , mnBits(0)
57 , mnWidth(0)
58 , mnHeight(0)
59 , mnBytesPerRow(0)
60 {
61 }
62 
63 // ------------------------------------------------------------------
64 
65 AquaSalBitmap::~AquaSalBitmap()
66 {
67 	Destroy();
68 }
69 
70 // ------------------------------------------------------------------
71 
72 bool AquaSalBitmap::Create( CGLayerRef xLayer, int nBitmapBits,
73 	int nX, int nY, int nWidth, int nHeight, bool /*bMirrorVert*/ )
74 {
75 	DBG_ASSERT( xLayer, "AquaSalBitmap::Create() from non-layered context" );
76 
77 	// sanitize input parameters
78 	if( nX < 0 )
79 		nWidth += nX, nX = 0;
80 	if( nY < 0 )
81 		nHeight += nY, nY = 0;
82 	const CGSize aLayerSize = CGLayerGetSize( xLayer );
83 	if( nWidth >= (int)aLayerSize.width - nX )
84 		nWidth = (int)aLayerSize.width - nX;
85 	if( nHeight >= (int)aLayerSize.height - nY )
86 		nHeight = (int)aLayerSize.height - nY;
87 	if( (nWidth < 0) || (nHeight < 0) )
88 		nWidth = nHeight = 0;
89 
90 	// initialize properties
91 	mnWidth  = nWidth;
92 	mnHeight = nHeight;
93 	mnBits   = nBitmapBits ? nBitmapBits : 32;
94 
95 	// initialize drawing context
96 	CreateContext();
97 
98 	// copy layer content into the bitmap buffer
99 	const CGPoint aSrcPoint = CGPointMake( -nX, -nY);
100 	::CGContextDrawLayerAtPoint( mxGraphicContext, aSrcPoint, xLayer );
101 	return true;
102 }
103 
104 // ------------------------------------------------------------------
105 
106 bool AquaSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
107 {
108 	if( !isValidBitCount( nBits ) )
109 		return false;
110 	maPalette = rBitmapPalette;
111 	mnBits = nBits;
112     mnWidth = rSize.Width();
113 	mnHeight = rSize.Height();
114 	return AllocateUserData();
115 }
116 
117 // ------------------------------------------------------------------
118 
119 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp )
120 {
121 	return Create( rSalBmp, rSalBmp.GetBitCount() );
122 }
123 
124 // ------------------------------------------------------------------
125 
126 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
127 {
128 	return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
129 }
130 
131 // ------------------------------------------------------------------
132 
133 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
134 {
135 	const AquaSalBitmap& rSourceBitmap = static_cast<const AquaSalBitmap&>(rSalBmp);
136 
137 	if( isValidBitCount( nNewBitCount ) && 	rSourceBitmap.maUserBuffer.get() )
138 	{
139 		mnBits = nNewBitCount;
140 		mnWidth = rSourceBitmap.mnWidth;
141 		mnHeight = rSourceBitmap.mnHeight;
142 		maPalette = rSourceBitmap.maPalette;
143 
144 		if( AllocateUserData() )
145 		{
146 			ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette, maUserBuffer.get(), rSourceBitmap.mnBits, rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette, rSourceBitmap.maUserBuffer.get() );
147 			return true;
148 		}
149 	}
150 	return false;
151 }
152 
153 // ------------------------------------------------------------------
154 
155 void AquaSalBitmap::Destroy()
156 {
157 	DestroyContext();
158 	maUserBuffer.reset();
159 }
160 
161 // ------------------------------------------------------------------
162 
163 void AquaSalBitmap::DestroyContext()
164 {
165 	CGImageRelease( mxCachedImage );
166 	mxCachedImage = NULL;
167 
168 	if( mxGraphicContext )
169 	{
170 		CGContextRelease( mxGraphicContext );
171 		mxGraphicContext = NULL;
172 		maContextBuffer.reset();
173 	}
174 }
175 
176 // ------------------------------------------------------------------
177 
178 bool AquaSalBitmap::CreateContext()
179 {
180 	DestroyContext();
181 
182 	// prepare graphics context
183 	// convert image from user input if available
184 	const bool bSkipConversion = !maUserBuffer;
185 	if( bSkipConversion )
186 		AllocateUserData();
187 
188 	// default to RGBA color space
189 	CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
190     CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
191 
192     // convert data into something accepted by CGBitmapContextCreate()
193 	size_t bitsPerComponent = (mnBits == 16) ? 5 : 8;
194 	sal_uInt32 nContextBytesPerRow = mnBytesPerRow;
195 	if( (mnBits == 16) || (mnBits == 32) )
196 	{
197 		// no conversion needed for truecolor
198 		maContextBuffer = maUserBuffer;
199 	}
200 	else if( (mnBits == 8) && maPalette.IsGreyPalette() )
201 	{
202 		// no conversion needed for grayscale
203 		maContextBuffer = maUserBuffer;
204 		aCGColorSpace = GetSalData()->mxGraySpace;
205 		aCGBmpInfo = kCGImageAlphaNone;
206 		bitsPerComponent = mnBits;
207 	}
208 	// TODO: is special handling for 1bit input buffers worth it?
209 	else
210 	{
211 		// convert user data to 32 bit
212 		nContextBytesPerRow = mnWidth << 2;
213         try
214         {
215             maContextBuffer.reset( new sal_uInt8[ mnHeight * nContextBytesPerRow ] );
216 
217             if( !bSkipConversion )
218                 ConvertBitmapData( mnWidth, mnHeight,
219                                32, nContextBytesPerRow, maPalette, maContextBuffer.get(),
220                                mnBits, mnBytesPerRow, maPalette, maUserBuffer.get() );
221         }
222         catch( std::bad_alloc )
223         {
224             mxGraphicContext = 0;
225         }
226 	}
227 
228     if( maContextBuffer.get() )
229     {
230         mxGraphicContext = ::CGBitmapContextCreate( maContextBuffer.get(), mnWidth, mnHeight,
231             bitsPerComponent, nContextBytesPerRow, aCGColorSpace, aCGBmpInfo );
232     }
233 
234 	if( !mxGraphicContext )
235 		maContextBuffer.reset();
236 
237 	return mxGraphicContext != NULL;
238 }
239 
240 // ------------------------------------------------------------------
241 
242 bool AquaSalBitmap::AllocateUserData()
243 {
244 	Destroy();
245 
246 	if( mnWidth && mnHeight )
247 	{
248 		mnBytesPerRow =  0;
249 
250 		switch( mnBits )
251 		{
252 		case 1:		mnBytesPerRow = (mnWidth + 7) >> 3; break;
253 		case 4:		mnBytesPerRow = (mnWidth + 1) >> 1; break;
254 		case 8:		mnBytesPerRow = mnWidth; break;
255 		case 16:	mnBytesPerRow = mnWidth << 1; break;
256 		case 24:	mnBytesPerRow = (mnWidth << 1) + mnWidth; break;
257 		case 32:	mnBytesPerRow = mnWidth << 2; break;
258 		default:
259 			DBG_ERROR("vcl::AquaSalBitmap::AllocateUserData(), illegal bitcount!");
260 		}
261 	}
262 
263     try
264     {
265         if( mnBytesPerRow )
266             maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] );
267     }
268     catch( const std::bad_alloc& )
269     {
270         DBG_ERROR( "vcl::AquaSalBitmap::AllocateUserData: bad alloc" );
271         maUserBuffer.reset( NULL );
272         mnBytesPerRow = 0;
273     }
274 
275 	return maUserBuffer.get() != 0;
276 }
277 
278 // ------------------------------------------------------------------
279 
280 class ImplPixelFormat
281 {
282 protected:
283 	sal_uInt8* pData;
284 public:
285 	static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
286 
287 	virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; }
288 	virtual void SkipPixel( sal_uInt32 nPixel ) = 0;
289 	virtual ColorData ReadPixel() = 0;
290 	virtual void WritePixel( ColorData nColor ) = 0;
291 };
292 
293 class ImplPixelFormat32 : public ImplPixelFormat
294 // currently ARGB-format for 32bit depth
295 {
296 public:
297 	virtual void SkipPixel( sal_uInt32 nPixel )
298 	{
299 		pData += nPixel << 2;
300 	}
301 	virtual ColorData ReadPixel()
302 	{
303 		const ColorData c = RGB_COLORDATA( pData[1], pData[2], pData[3] );
304 		pData += 4;
305 		return c;
306 	}
307 	virtual void WritePixel( ColorData nColor )
308 	{
309 		*pData++ = 0;
310 		*pData++ = COLORDATA_RED( nColor );
311 		*pData++ = COLORDATA_GREEN( nColor );
312 		*pData++ = COLORDATA_BLUE( nColor );
313 	}
314 };
315 
316 class ImplPixelFormat24 : public ImplPixelFormat
317 // currently BGR-format for 24bit depth
318 {
319 public:
320 	virtual void SkipPixel( sal_uInt32 nPixel )
321 	{
322 		pData += (nPixel << 1) + nPixel;
323 	}
324 	virtual ColorData ReadPixel()
325 	{
326 		const ColorData c = RGB_COLORDATA( pData[2], pData[1], pData[0] );
327 		pData += 3;
328 		return c;
329 	}
330 	virtual void WritePixel( ColorData nColor )
331 	{
332 		*pData++ = COLORDATA_BLUE( nColor );
333 		*pData++ = COLORDATA_GREEN( nColor );
334 		*pData++ = COLORDATA_RED( nColor );
335 	}
336 };
337 
338 class ImplPixelFormat16 : public ImplPixelFormat
339 // currently R5G6B5-format for 16bit depth
340 {
341 protected:
342 	sal_uInt16* pData16;
343 public:
344 
345 	virtual void StartLine( sal_uInt8* pLine )
346 	{
347 		pData16 = (sal_uInt16*)pLine;
348 	}
349 	virtual void SkipPixel( sal_uInt32 nPixel )
350 	{
351 		pData += nPixel;
352 	}
353 	virtual ColorData ReadPixel()
354 	{
355 		const ColorData c = RGB_COLORDATA( (*pData & 0x7c00) >> 7, (*pData & 0x03e0) >> 2 , (*pData & 0x001f) << 3 );
356 		pData++;
357 		return c;
358 	}
359 	virtual void WritePixel( ColorData nColor )
360 	{
361 		*pData++ =	((COLORDATA_RED( nColor ) & 0xf8 ) << 7 ) ||
362 					((COLORDATA_GREEN( nColor ) & 0xf8 ) << 2 ) ||
363 					((COLORDATA_BLUE( nColor ) & 0xf8 ) >> 3 );
364 	}
365 };
366 
367 class ImplPixelFormat8 : public ImplPixelFormat
368 {
369 private:
370 	const BitmapPalette& mrPalette;
371 
372 public:
373 	ImplPixelFormat8( const BitmapPalette& rPalette )
374 	: mrPalette( rPalette )
375 	{
376 	}
377 	virtual void SkipPixel( sal_uInt32 nPixel )
378 	{
379 		pData += nPixel;
380 	}
381 	virtual ColorData ReadPixel()
382 	{
383 		return mrPalette[ *pData++ ].operator Color().GetColor();
384 	}
385 	virtual void WritePixel( ColorData nColor )
386 	{
387 		const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
388 		*pData++ = static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) );
389 	}
390 };
391 
392 class ImplPixelFormat4 : public ImplPixelFormat
393 {
394 private:
395 	const BitmapPalette& mrPalette;
396 	sal_uInt32 mnX;
397 	sal_uInt32 mnShift;
398 
399 public:
400 	ImplPixelFormat4( const BitmapPalette& rPalette )
401 	: mrPalette( rPalette )
402 	{
403 	}
404 	virtual void SkipPixel( sal_uInt32 nPixel )
405 	{
406 		mnX += nPixel;
407 		if( (nPixel & 1) )
408 			mnShift ^= 4;
409 	}
410 	virtual void StartLine( sal_uInt8* pLine )
411 	{
412 		pData = pLine;
413 		mnX = 0;
414 		mnShift = 4;
415 	}
416 	virtual ColorData ReadPixel()
417 	{
418 		const BitmapColor& rColor = mrPalette[( pData[mnX >> 1] >> mnShift) & 0x0f];
419 		mnX++;
420 		mnShift ^= 4;
421 		return rColor.operator Color().GetColor();
422 	}
423 	virtual void WritePixel( ColorData nColor )
424 	{
425 		const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
426 		pData[mnX>>1] &= (0xf0 >> mnShift);
427 		pData[mnX>>1] |= (static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ) & 0x0f);
428 		mnX++;
429 		mnShift ^= 4;
430 	}
431 };
432 
433 class ImplPixelFormat1 : public ImplPixelFormat
434 {
435 private:
436 	const BitmapPalette& mrPalette;
437 	sal_uInt32 mnX;
438 
439 public:
440 	ImplPixelFormat1( const BitmapPalette& rPalette )
441 	: mrPalette( rPalette )
442 	{
443 	}
444 	virtual void SkipPixel( sal_uInt32 nPixel )
445 	{
446 		mnX += nPixel;
447 	}
448 	virtual void StartLine( sal_uInt8* pLine )
449 	{
450 		pData = pLine;
451 		mnX = 0;
452 	}
453 	virtual ColorData ReadPixel()
454 	{
455 		const BitmapColor& rColor = mrPalette[ (pData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
456 		mnX++;
457 		return rColor.operator Color().GetColor();
458 	}
459 	virtual void WritePixel( ColorData nColor )
460 	{
461 		const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
462 		if( mrPalette.GetBestIndex( aColor ) & 1 )
463 			pData[ mnX >> 3 ] |= 1 << ( 7 - ( mnX & 7 ) );
464 		else
465 			pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) );
466 		mnX++;
467 	}
468 };
469 
470 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
471 {
472 	switch( nBits )
473 	{
474 	case 1: return new ImplPixelFormat1( rPalette );
475 	case 4: return new ImplPixelFormat4( rPalette );
476 	case 8: return new ImplPixelFormat8( rPalette );
477 	case 16: return new ImplPixelFormat16;
478 	case 24: return new ImplPixelFormat24;
479 	case 32: return new ImplPixelFormat32;
480 	}
481 
482 	return 0;
483 }
484 
485 void AquaSalBitmap::ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight,
486 									   sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow, const BitmapPalette& rDestPalette, sal_uInt8* pDestData,
487 									   sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow, const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData )
488 
489 {
490 	if( (nDestBytesPerRow == nSrcBytesPerRow) && (nDestBits == nSrcBits) && ((nSrcBits != 8) || (rDestPalette.operator==( rSrcPalette ))) )
491 	{
492 		// simple case, same format, so just copy
493 		memcpy( pDestData, pSrcData, nHeight * nDestBytesPerRow );
494 		return;
495 	}
496 
497 	// try accelerated conversion if possible
498 	// TODO: are other truecolor conversions except BGR->ARGB worth it?
499 	bool bConverted = false;
500 	if( (nSrcBits == 24) && (nDestBits == 32) )
501 	{
502 		// TODO: extend bmpfast.cxx with a method that can be directly used here
503 		BitmapBuffer aSrcBuf;
504 		aSrcBuf.mnFormat = BMP_FORMAT_24BIT_TC_BGR;
505 		aSrcBuf.mpBits = pSrcData;
506 		aSrcBuf.mnBitCount = nSrcBits;
507 		aSrcBuf.mnScanlineSize = nSrcBytesPerRow;
508 		BitmapBuffer aDstBuf;
509 		aDstBuf.mnFormat = BMP_FORMAT_32BIT_TC_ARGB;
510 		aDstBuf.mpBits = pDestData;
511 		aSrcBuf.mnBitCount = nDestBits;
512 		aDstBuf.mnScanlineSize = nDestBytesPerRow;
513 
514 		aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth;
515 		aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight;
516 
517 		SalTwoRect aTwoRects;
518 		aTwoRects.mnSrcX = aTwoRects.mnDestX = 0;
519 		aTwoRects.mnSrcY = aTwoRects.mnDestY = 0;
520 		aTwoRects.mnSrcWidth = aTwoRects.mnDestWidth = mnWidth;
521 		aTwoRects.mnSrcHeight = aTwoRects.mnDestHeight = mnHeight;
522 		bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );
523 	}
524 
525 	if( !bConverted )
526 	{
527 		// TODO: this implementation is for clarety, not for speed
528 
529 		ImplPixelFormat* pD = ImplPixelFormat::GetFormat( nDestBits, rDestPalette );
530 		ImplPixelFormat* pS = ImplPixelFormat::GetFormat( nSrcBits, rSrcPalette );
531 
532 		if( pD && pS )
533 		{
534 			sal_uInt32 nY = nHeight;
535 			while( nY-- )
536 			{
537 				pD->StartLine( pDestData );
538 				pS->StartLine( pSrcData );
539 
540 				sal_uInt32 nX = nWidth;
541 				while( nX-- )
542 					pD->WritePixel( pS->ReadPixel() );
543 
544 				pSrcData += nSrcBytesPerRow;
545 				pDestData += nDestBytesPerRow;
546 			}
547 		}
548 		delete pS;
549 		delete pD;
550 	}
551 }
552 
553 // ------------------------------------------------------------------
554 
555 Size AquaSalBitmap::GetSize() const
556 {
557 	return Size( mnWidth, mnHeight );
558 }
559 
560 // ------------------------------------------------------------------
561 
562 sal_uInt16 AquaSalBitmap::GetBitCount() const
563 {
564 	return mnBits;
565 }
566 
567 // ------------------------------------------------------------------
568 
569 static struct pal_entry
570 {
571     sal_uInt8 mnRed;
572     sal_uInt8 mnGreen;
573     sal_uInt8 mnBlue;
574 }
575 const aImplSalSysPalEntryAry[ 16 ] =
576 {
577 {	 0,    0,	 0 },
578 {	 0,    0, 0x80 },
579 {	 0, 0x80,	 0 },
580 {	 0, 0x80, 0x80 },
581 { 0x80,    0,	 0 },
582 { 0x80,    0, 0x80 },
583 { 0x80, 0x80,	 0 },
584 { 0x80, 0x80, 0x80 },
585 { 0xC0, 0xC0, 0xC0 },
586 {	 0,    0, 0xFF },
587 {	 0, 0xFF,	 0 },
588 {	 0, 0xFF, 0xFF },
589 { 0xFF,    0,	 0 },
590 { 0xFF,    0, 0xFF },
591 { 0xFF, 0xFF,	 0 },
592 { 0xFF, 0xFF, 0xFF }
593 };
594 
595 const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome )
596 {
597 	if( bMonochrome )
598 		return Bitmap::GetGreyPalette( 1U << mnBits );
599 
600 	// at this point we should provide some kind of default palette
601 	// since all other platforms do so, too.
602 	static bool bDefPalInit = false;
603 	static BitmapPalette aDefPalette256;
604 	static BitmapPalette aDefPalette16;
605 	static BitmapPalette aDefPalette2;
606 	if( ! bDefPalInit )
607 	{
608 		bDefPalInit = true;
609 		aDefPalette256.SetEntryCount( 256 );
610 		aDefPalette16.SetEntryCount( 16 );
611 		aDefPalette2.SetEntryCount( 2 );
612 
613 		// Standard colors
614 		unsigned int i;
615 		for( i = 0; i < 16; i++ )
616 		{
617 			aDefPalette16[i] =
618 			aDefPalette256[i] = BitmapColor( aImplSalSysPalEntryAry[i].mnRed,
619 			                                 aImplSalSysPalEntryAry[i].mnGreen,
620 			                                 aImplSalSysPalEntryAry[i].mnBlue );
621 	        }
622 
623 		aDefPalette2[0] = BitmapColor( 0, 0, 0 );
624 		aDefPalette2[1] = BitmapColor( 0xff, 0xff, 0xff );
625 
626 		// own palette (6/6/6)
627 		const int DITHER_PAL_STEPS = 6;
628 		const sal_uInt8 DITHER_PAL_DELTA = 51;
629 		int nB, nG, nR;
630 		sal_uInt8 nRed, nGreen, nBlue;
631 		for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
632 		{
633 			for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
634 			{
635 				for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
636 				{
637 					aDefPalette256[ i ] = BitmapColor( nRed, nGreen, nBlue );
638 					i++;
639 				}
640 			}
641 		}
642 	}
643 
644 	// now fill in appropriate palette
645 	switch( mnBits )
646 	{
647 	case 1: return aDefPalette2;
648 	case 4: return aDefPalette16;
649 	case 8: return aDefPalette256;
650 	default: break;
651 	}
652 
653 	const static BitmapPalette aEmptyPalette;
654 	return aEmptyPalette;
655 }
656 
657 BitmapBuffer* AquaSalBitmap::AcquireBuffer( bool /*bReadOnly*/ )
658 {
659 	if( !maUserBuffer.get() )
660 //	|| maContextBuffer.get() && (maUserBuffer.get() != maContextBuffer.get()) )
661 	{
662 		fprintf(stderr,"ASB::Acq(%dx%d,d=%d)\n",mnWidth,mnHeight,mnBits);
663 		// TODO: AllocateUserData();
664 		return NULL;
665 	}
666 
667 	BitmapBuffer* pBuffer = new BitmapBuffer;
668 	pBuffer->mnWidth = mnWidth;
669 	pBuffer->mnHeight = mnHeight;
670 	pBuffer->maPalette = maPalette;
671 	pBuffer->mnScanlineSize = mnBytesPerRow;
672 	pBuffer->mpBits = maUserBuffer.get();
673 	pBuffer->mnBitCount = mnBits;
674 	switch( mnBits )
675 	{
676 	case 1:		pBuffer->mnFormat = BMP_FORMAT_1BIT_MSB_PAL; break;
677 	case 4:		pBuffer->mnFormat = BMP_FORMAT_4BIT_MSN_PAL; break;
678 	case 8:		pBuffer->mnFormat = BMP_FORMAT_8BIT_PAL; break;
679 	case 16:	pBuffer->mnFormat = BMP_FORMAT_16BIT_TC_MSB_MASK;
680 				pBuffer->maColorMask  = ColorMask( k16BitRedColorMask, k16BitGreenColorMask, k16BitBlueColorMask );
681 				break;
682 	case 24:	pBuffer->mnFormat = BMP_FORMAT_24BIT_TC_BGR; break;
683 	case 32:	pBuffer->mnFormat = BMP_FORMAT_32BIT_TC_ARGB;
684 				pBuffer->maColorMask  = ColorMask( k32BitRedColorMask, k32BitGreenColorMask, k32BitBlueColorMask );
685 				break;
686 	}
687 	pBuffer->mnFormat |= BMP_FORMAT_BOTTOM_UP;
688 
689 	// some BitmapBuffer users depend on a complete palette
690     if( (mnBits <= 8) && !maPalette )
691 		pBuffer->maPalette = GetDefaultPalette( mnBits, true );
692 
693 	return pBuffer;
694 }
695 
696 // ------------------------------------------------------------------
697 
698 void AquaSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly )
699 {
700 	// invalidate graphic context if we have different data
701 	if( !bReadOnly )
702 	{
703 		maPalette = pBuffer->maPalette;
704 		if( mxGraphicContext )
705 			DestroyContext();
706 	}
707 
708 	delete pBuffer;
709 }
710 
711 // ------------------------------------------------------------------
712 
713 CGImageRef AquaSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const
714 {
715 	if( !mxCachedImage )
716 	{
717 		if( !mxGraphicContext )
718 			if( !const_cast<AquaSalBitmap*>(this)->CreateContext() )
719 				return NULL;
720 
721 		mxCachedImage = CGBitmapContextCreateImage( mxGraphicContext );
722 	}
723 
724 	CGImageRef xCroppedImage = NULL;
725     // short circuit if there is nothing to crop
726 	if( !nX && !nY && (mnWidth == nNewWidth) && (mnHeight == nNewHeight) )
727 	{
728 		  xCroppedImage = mxCachedImage;
729 		  CFRetain( xCroppedImage );
730 	}
731 	else
732 	{
733 		nY = mnHeight - (nY + nNewHeight); // adjust for y-mirrored context
734 		const CGRect aCropRect = CGRectMake( nX, nY, nNewWidth, nNewHeight);
735 		xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect );
736 	}
737 
738 	return xCroppedImage;
739 }
740 
741 // ------------------------------------------------------------------
742 
743 static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
744 {
745     rtl_freeMemory( const_cast<void*>(data) );
746 }
747 
748 CGImageRef AquaSalBitmap::CreateWithMask( const AquaSalBitmap& rMask,
749 	int nX, int nY, int nWidth, int nHeight ) const
750 {
751     CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) );
752     if( !xImage )
753     	return NULL;
754 
755 	CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
756 	if( !xMask )
757 		return xImage;
758 
759 	// CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
760 	// TODO: isolate in an extra method?
761 	if( !CGImageIsMask(xMask) || (CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
762 	{
763 	    const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset
764 
765 	    // create the alpha mask image fitting our image
766 	    // TODO: is caching the full mask or the subimage mask worth it?
767 	    int nMaskBytesPerRow = ((nWidth + 3) & ~3);
768 	    void* pMaskMem = rtl_allocateMemory( nMaskBytesPerRow * nHeight );
769 	    CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
770 	    	nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone );
771 	    CGContextDrawImage( xMaskContext, xImageRect, xMask );
772 	    CFRelease( xMask );
773 		CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( NULL,
774 		pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
775 		static const float* pDecode = NULL;
776 		xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false );
777 		CFRelease( xDataProvider );
778 		CFRelease( xMaskContext );
779 	}
780 
781 	if( !xMask )
782 		return xImage;
783 
784     // combine image and alpha mask
785     CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
786     CFRelease( xMask );
787 	CFRelease( xImage );
788     return xMaskedImage;
789 }
790 
791 // ------------------------------------------------------------------
792 
793 /** creates an image from the given rectangle, replacing all black pixels with nMaskColor and make all other full transparent */
794 CGImageRef AquaSalBitmap::CreateColorMask( int nX, int nY, int nWidth, int nHeight, SalColor nMaskColor ) const
795 {
796 	CGImageRef xMask = 0;
797 	if( maUserBuffer.get() && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight) )
798 	{
799 		const sal_uInt32 nDestBytesPerRow = nWidth << 2;
800 		sal_uInt32* pMaskBuffer = static_cast<sal_uInt32*>( rtl_allocateMemory( nHeight * nDestBytesPerRow ) );
801 		sal_uInt32* pDest = pMaskBuffer;
802 
803 		ImplPixelFormat* pSourcePixels = ImplPixelFormat::GetFormat( mnBits, maPalette );
804 
805 		if( pMaskBuffer && pSourcePixels )
806 		{
807 			sal_uInt32 nColor;
808 			reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff;
809 			reinterpret_cast<sal_uInt8*>(&nColor)[1] = SALCOLOR_RED( nMaskColor );
810 			reinterpret_cast<sal_uInt8*>(&nColor)[2] = SALCOLOR_GREEN( nMaskColor );
811 			reinterpret_cast<sal_uInt8*>(&nColor)[3] = SALCOLOR_BLUE( nMaskColor );
812 
813 			sal_uInt8* pSource = maUserBuffer.get();
814 			if( nY )
815 				pSource += nY * mnBytesPerRow;
816 
817 			int y = nHeight;
818 			while( y-- )
819 			{
820 				pSourcePixels->StartLine( pSource );
821 				pSourcePixels->SkipPixel(nX);
822 				sal_uInt32 x = nWidth;
823 				while( x-- )
824 				{
825 					*pDest++ = ( pSourcePixels->ReadPixel() == 0 ) ? nColor : 0;
826 				}
827 				pSource += mnBytesPerRow;
828 			}
829 
830 			CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(NULL, pMaskBuffer, nHeight * nDestBytesPerRow, &CFRTLFree) );
831 			xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, NULL, true, kCGRenderingIntentDefault);
832 			CFRelease(xDataProvider);
833 		}
834         else
835         {
836             free(pMaskBuffer);
837         }
838 
839 		delete pSourcePixels;
840 	}
841 	return xMask;
842 }
843 
844 // =======================================================================
845 
846 /** AquaSalBitmap::GetSystemData Get platform native image data from existing image
847  *
848  *  @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx
849  *  @return true if successful
850 **/
851 bool AquaSalBitmap::GetSystemData( BitmapSystemData& rData )
852 {
853     bool bRet = false;
854 
855     if( !mxGraphicContext )
856         CreateContext();
857 
858     if ( mxGraphicContext )
859     {
860         bRet = true;
861 
862 #ifdef CAIRO
863         if ((CGBitmapContextGetBitsPerPixel(mxGraphicContext) == 32) &&
864             (CGBitmapContextGetBitmapInfo(mxGraphicContext) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host) {
865             /**
866              * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it.
867              */
868             OSL_TRACE("AquaSalBitmap::%s(): kCGBitmapByteOrder32Host not found => inserting it.",__func__);
869 
870             CGImageRef xImage = CGBitmapContextCreateImage (mxGraphicContext);
871 
872             // re-create the context with single change: include kCGBitmapByteOrder32Host flag.
873             CGContextRef mxGraphicContextNew = CGBitmapContextCreate( CGBitmapContextGetData(mxGraphicContext),
874                                                                       CGBitmapContextGetWidth(mxGraphicContext),
875                                                                       CGBitmapContextGetHeight(mxGraphicContext),
876                                                                       CGBitmapContextGetBitsPerComponent(mxGraphicContext),
877                                                                       CGBitmapContextGetBytesPerRow(mxGraphicContext),
878                                                                       CGBitmapContextGetColorSpace(mxGraphicContext),
879                                                                       CGBitmapContextGetBitmapInfo(mxGraphicContext) | kCGBitmapByteOrder32Host);
880             CFRelease(mxGraphicContext);
881 
882             // Needs to be flipped
883             CGContextSaveGState( mxGraphicContextNew );
884             CGContextTranslateCTM (mxGraphicContextNew, 0, CGBitmapContextGetHeight(mxGraphicContextNew));
885             CGContextScaleCTM (mxGraphicContextNew, 1.0, -1.0);
886 
887             CGContextDrawImage(mxGraphicContextNew, CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage);
888 
889             // Flip back
890             CGContextRestoreGState( mxGraphicContextNew );
891 
892             CGImageRelease( xImage );
893             mxGraphicContext = mxGraphicContextNew;
894         }
895 #endif
896 
897         rData.rImageContext = (void *) mxGraphicContext;
898         rData.mnWidth = mnWidth;
899         rData.mnHeight = mnHeight;
900     }
901 
902     return bRet;
903 }
904