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