Bump version to 4.1-6
[LibreOffice.git] / vcl / quartz / salbmp.cxx
blob4cfccb04bd6fe11a38587f87c5db928ff675fc90
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "basebmp/scanlineformats.hxx"
22 #include "basebmp/color.hxx"
24 #include "basegfx/vector/b2ivector.hxx"
26 #include "tools/color.hxx"
28 #include "vcl/bitmap.hxx"
29 #include "vcl/salbtype.hxx"
31 #include "quartz/salbmp.h"
33 #ifdef MACOSX
34 #include "aqua/saldata.hxx"
35 #else
36 #include "saldatabasic.hxx"
37 #endif
39 #include "bmpfast.hxx"
41 static const unsigned long k16BitRedColorMask = 0x00007c00;
42 static const unsigned long k16BitGreenColorMask = 0x000003e0;
43 static const unsigned long k16BitBlueColorMask = 0x0000001f;
45 static const unsigned long k32BitRedColorMask = 0x00ff0000;
46 static const unsigned long k32BitGreenColorMask = 0x0000ff00;
47 static const unsigned long k32BitBlueColorMask = 0x000000ff;
49 // =======================================================================
51 static bool isValidBitCount( sal_uInt16 nBitCount )
53 return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32);
56 // =======================================================================
58 QuartzSalBitmap::QuartzSalBitmap()
59 : mxGraphicContext( NULL )
60 , mxCachedImage( NULL )
61 , mnBits(0)
62 , mnWidth(0)
63 , mnHeight(0)
64 , mnBytesPerRow(0)
68 // ------------------------------------------------------------------
70 QuartzSalBitmap::~QuartzSalBitmap()
72 Destroy();
75 // ------------------------------------------------------------------
77 bool QuartzSalBitmap::Create( CGLayerRef xLayer, int nBitmapBits,
78 int nX, int nY, int nWidth, int nHeight, bool /*bMirrorVert*/ )
80 DBG_ASSERT( xLayer, "QuartzSalBitmap::Create() from non-layered context" );
82 // sanitize input parameters
83 if( nX < 0 )
84 nWidth += nX, nX = 0;
85 if( nY < 0 )
86 nHeight += nY, nY = 0;
87 const CGSize aLayerSize = CGLayerGetSize( xLayer );
88 if( nWidth >= (int)aLayerSize.width - nX )
89 nWidth = (int)aLayerSize.width - nX;
90 if( nHeight >= (int)aLayerSize.height - nY )
91 nHeight = (int)aLayerSize.height - nY;
92 if( (nWidth < 0) || (nHeight < 0) )
93 nWidth = nHeight = 0;
95 // initialize properties
96 mnWidth = nWidth;
97 mnHeight = nHeight;
98 mnBits = nBitmapBits ? nBitmapBits : 32;
100 // initialize drawing context
101 CreateContext();
103 // copy layer content into the bitmap buffer
104 const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX), static_cast<CGFloat>(-nY) };
105 ::CGContextDrawLayerAtPoint( mxGraphicContext, aSrcPoint, xLayer );
106 return true;
109 // ------------------------------------------------------------------
111 bool QuartzSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
113 if( !isValidBitCount( nBits ) )
114 return false;
115 maPalette = rBitmapPalette;
116 mnBits = nBits;
117 mnWidth = rSize.Width();
118 mnHeight = rSize.Height();
119 return AllocateUserData();
122 // ------------------------------------------------------------------
124 bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp )
126 return Create( rSalBmp, rSalBmp.GetBitCount() );
129 // ------------------------------------------------------------------
131 bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
133 return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
136 // ------------------------------------------------------------------
138 bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
140 const QuartzSalBitmap& rSourceBitmap = static_cast<const QuartzSalBitmap&>(rSalBmp);
142 if( isValidBitCount( nNewBitCount ) && rSourceBitmap.maUserBuffer.get() )
144 mnBits = nNewBitCount;
145 mnWidth = rSourceBitmap.mnWidth;
146 mnHeight = rSourceBitmap.mnHeight;
147 maPalette = rSourceBitmap.maPalette;
149 if( AllocateUserData() )
151 ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette, maUserBuffer.get(), rSourceBitmap.mnBits, rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette, rSourceBitmap.maUserBuffer.get() );
152 return true;
155 return false;
158 // ------------------------------------------------------------------
160 bool QuartzSalBitmap::Create( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmapCanvas > /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
162 return false;
165 // ------------------------------------------------------------------
167 void QuartzSalBitmap::Destroy()
169 DestroyContext();
170 maUserBuffer.reset();
173 // ------------------------------------------------------------------
175 void QuartzSalBitmap::DestroyContext()
177 CGImageRelease( mxCachedImage );
178 mxCachedImage = NULL;
180 if( mxGraphicContext )
182 CGContextRelease( mxGraphicContext );
183 mxGraphicContext = NULL;
184 maContextBuffer.reset();
188 // ------------------------------------------------------------------
190 bool QuartzSalBitmap::CreateContext()
192 DestroyContext();
194 // prepare graphics context
195 // convert image from user input if available
196 const bool bSkipConversion = !maUserBuffer;
197 if( bSkipConversion )
198 AllocateUserData();
200 // default to RGBA color space
201 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
202 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
204 // convert data into something accepted by CGBitmapContextCreate()
205 size_t bitsPerComponent = (mnBits == 16) ? 5 : 8;
206 sal_uInt32 nContextBytesPerRow = mnBytesPerRow;
207 if( (mnBits == 16) || (mnBits == 32) )
209 // no conversion needed for truecolor
210 maContextBuffer = maUserBuffer;
212 else if( (mnBits == 8) && maPalette.IsGreyPalette() )
214 // no conversion needed for grayscale
215 maContextBuffer = maUserBuffer;
216 aCGColorSpace = GetSalData()->mxGraySpace;
217 aCGBmpInfo = kCGImageAlphaNone;
218 bitsPerComponent = mnBits;
220 // TODO: is special handling for 1bit input buffers worth it?
221 else
223 // convert user data to 32 bit
224 nContextBytesPerRow = mnWidth << 2;
227 maContextBuffer.reset( new sal_uInt8[ mnHeight * nContextBytesPerRow ] );
229 if( !bSkipConversion )
230 ConvertBitmapData( mnWidth, mnHeight,
231 32, nContextBytesPerRow, maPalette, maContextBuffer.get(),
232 mnBits, mnBytesPerRow, maPalette, maUserBuffer.get() );
234 catch( const std::bad_alloc& )
236 mxGraphicContext = 0;
240 if( maContextBuffer.get() )
242 mxGraphicContext = ::CGBitmapContextCreate( maContextBuffer.get(), mnWidth, mnHeight,
243 bitsPerComponent, nContextBytesPerRow, aCGColorSpace, aCGBmpInfo );
246 if( !mxGraphicContext )
247 maContextBuffer.reset();
249 return mxGraphicContext != NULL;
252 // ------------------------------------------------------------------
254 bool QuartzSalBitmap::AllocateUserData()
256 Destroy();
258 if( mnWidth && mnHeight )
260 mnBytesPerRow = 0;
262 switch( mnBits )
264 case 1: mnBytesPerRow = (mnWidth + 7) >> 3; break;
265 case 4: mnBytesPerRow = (mnWidth + 1) >> 1; break;
266 case 8: mnBytesPerRow = mnWidth; break;
267 case 16: mnBytesPerRow = mnWidth << 1; break;
268 case 24: mnBytesPerRow = (mnWidth << 1) + mnWidth; break;
269 case 32: mnBytesPerRow = mnWidth << 2; break;
270 default:
271 OSL_FAIL("vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
277 if( mnBytesPerRow )
278 maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] );
280 catch( const std::bad_alloc& )
282 OSL_FAIL( "vcl::QuartzSalBitmap::AllocateUserData: bad alloc" );
283 maUserBuffer.reset( NULL );
284 mnBytesPerRow = 0;
287 return maUserBuffer.get() != 0;
290 // ------------------------------------------------------------------
292 class ImplPixelFormat
294 protected:
295 sal_uInt8* pData;
296 public:
297 static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
299 virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; }
300 virtual void SkipPixel( sal_uInt32 nPixel ) = 0;
301 virtual ColorData ReadPixel() = 0;
302 virtual void WritePixel( ColorData nColor ) = 0;
303 virtual ~ImplPixelFormat() { }
306 class ImplPixelFormat32 : public ImplPixelFormat
307 // currently ARGB-format for 32bit depth
309 public:
310 virtual void SkipPixel( sal_uInt32 nPixel )
312 pData += nPixel << 2;
314 virtual ColorData ReadPixel()
316 const ColorData c = RGB_COLORDATA( pData[1], pData[2], pData[3] );
317 pData += 4;
318 return c;
320 virtual void WritePixel( ColorData nColor )
322 *pData++ = 0;
323 *pData++ = COLORDATA_RED( nColor );
324 *pData++ = COLORDATA_GREEN( nColor );
325 *pData++ = COLORDATA_BLUE( nColor );
329 class ImplPixelFormat24 : public ImplPixelFormat
330 // currently BGR-format for 24bit depth
332 public:
333 virtual void SkipPixel( sal_uInt32 nPixel )
335 pData += (nPixel << 1) + nPixel;
337 virtual ColorData ReadPixel()
339 const ColorData c = RGB_COLORDATA( pData[2], pData[1], pData[0] );
340 pData += 3;
341 return c;
343 virtual void WritePixel( ColorData nColor )
345 *pData++ = COLORDATA_BLUE( nColor );
346 *pData++ = COLORDATA_GREEN( nColor );
347 *pData++ = COLORDATA_RED( nColor );
351 class ImplPixelFormat16 : public ImplPixelFormat
352 // currently R5G6B5-format for 16bit depth
354 protected:
355 sal_uInt16* pData16;
356 public:
358 virtual void StartLine( sal_uInt8* pLine )
360 pData16 = (sal_uInt16*)pLine;
362 virtual void SkipPixel( sal_uInt32 nPixel )
364 pData += nPixel;
366 virtual ColorData ReadPixel()
368 const ColorData c = RGB_COLORDATA( (*pData & 0x7c00) >> 7, (*pData & 0x03e0) >> 2 , (*pData & 0x001f) << 3 );
369 pData++;
370 return c;
372 virtual void WritePixel( ColorData nColor )
374 *pData++ = ((COLORDATA_RED( nColor ) & 0xf8 ) << 7 ) ||
375 ((COLORDATA_GREEN( nColor ) & 0xf8 ) << 2 ) ||
376 ((COLORDATA_BLUE( nColor ) & 0xf8 ) >> 3 );
380 class ImplPixelFormat8 : public ImplPixelFormat
382 private:
383 const BitmapPalette& mrPalette;
385 public:
386 ImplPixelFormat8( const BitmapPalette& rPalette )
387 : mrPalette( rPalette )
390 virtual void SkipPixel( sal_uInt32 nPixel )
392 pData += nPixel;
394 virtual ColorData ReadPixel()
396 return mrPalette[ *pData++ ].operator Color().GetColor();
398 virtual void WritePixel( ColorData nColor )
400 const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
401 *pData++ = static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) );
405 class ImplPixelFormat4 : public ImplPixelFormat
407 private:
408 const BitmapPalette& mrPalette;
409 sal_uInt32 mnX;
410 sal_uInt32 mnShift;
412 public:
413 ImplPixelFormat4( const BitmapPalette& rPalette )
414 : mrPalette( rPalette )
417 virtual void SkipPixel( sal_uInt32 nPixel )
419 mnX += nPixel;
420 if( (nPixel & 1) )
421 mnShift ^= 4;
423 virtual void StartLine( sal_uInt8* pLine )
425 pData = pLine;
426 mnX = 0;
427 mnShift = 4;
429 virtual ColorData ReadPixel()
431 const BitmapColor& rColor = mrPalette[( pData[mnX >> 1] >> mnShift) & 0x0f];
432 mnX++;
433 mnShift ^= 4;
434 return rColor.operator Color().GetColor();
436 virtual void WritePixel( ColorData nColor )
438 const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
439 pData[mnX>>1] &= (0xf0 >> mnShift);
440 pData[mnX>>1] |= (static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ) & 0x0f);
441 mnX++;
442 mnShift ^= 4;
446 class ImplPixelFormat1 : public ImplPixelFormat
448 private:
449 const BitmapPalette& mrPalette;
450 sal_uInt32 mnX;
452 public:
453 ImplPixelFormat1( const BitmapPalette& rPalette )
454 : mrPalette( rPalette )
457 virtual void SkipPixel( sal_uInt32 nPixel )
459 mnX += nPixel;
461 virtual void StartLine( sal_uInt8* pLine )
463 pData = pLine;
464 mnX = 0;
466 virtual ColorData ReadPixel()
468 const BitmapColor& rColor = mrPalette[ (pData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
469 mnX++;
470 return rColor.operator Color().GetColor();
472 virtual void WritePixel( ColorData nColor )
474 const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
475 if( mrPalette.GetBestIndex( aColor ) & 1 )
476 pData[ mnX >> 3 ] |= 1 << ( 7 - ( mnX & 7 ) );
477 else
478 pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) );
479 mnX++;
483 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
485 switch( nBits )
487 case 1: return new ImplPixelFormat1( rPalette );
488 case 4: return new ImplPixelFormat4( rPalette );
489 case 8: return new ImplPixelFormat8( rPalette );
490 case 16: return new ImplPixelFormat16;
491 case 24: return new ImplPixelFormat24;
492 case 32: return new ImplPixelFormat32;
495 return 0;
498 void QuartzSalBitmap::ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight,
499 sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow, const BitmapPalette& rDestPalette, sal_uInt8* pDestData,
500 sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow, const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData )
503 if( (nDestBytesPerRow == nSrcBytesPerRow) && (nDestBits == nSrcBits) && ((nSrcBits != 8) || (rDestPalette.operator==( rSrcPalette ))) )
505 // simple case, same format, so just copy
506 memcpy( pDestData, pSrcData, nHeight * nDestBytesPerRow );
507 return;
510 // try accelerated conversion if possible
511 // TODO: are other truecolor conversions except BGR->ARGB worth it?
512 bool bConverted = false;
513 if( (nSrcBits == 24) && (nDestBits == 32) )
515 // TODO: extend bmpfast.cxx with a method that can be directly used here
516 BitmapBuffer aSrcBuf;
517 aSrcBuf.mnFormat = BMP_FORMAT_24BIT_TC_BGR;
518 aSrcBuf.mpBits = pSrcData;
519 aSrcBuf.mnBitCount = nSrcBits;
520 aSrcBuf.mnScanlineSize = nSrcBytesPerRow;
521 BitmapBuffer aDstBuf;
522 aDstBuf.mnFormat = BMP_FORMAT_32BIT_TC_ARGB;
523 aDstBuf.mpBits = pDestData;
524 aSrcBuf.mnBitCount = nDestBits;
525 aDstBuf.mnScanlineSize = nDestBytesPerRow;
527 aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth;
528 aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight;
530 SalTwoRect aTwoRects;
531 aTwoRects.mnSrcX = aTwoRects.mnDestX = 0;
532 aTwoRects.mnSrcY = aTwoRects.mnDestY = 0;
533 aTwoRects.mnSrcWidth = aTwoRects.mnDestWidth = mnWidth;
534 aTwoRects.mnSrcHeight = aTwoRects.mnDestHeight = mnHeight;
535 bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );
538 if( !bConverted )
540 // TODO: this implementation is for clarety, not for speed
542 ImplPixelFormat* pD = ImplPixelFormat::GetFormat( nDestBits, rDestPalette );
543 ImplPixelFormat* pS = ImplPixelFormat::GetFormat( nSrcBits, rSrcPalette );
545 if( pD && pS )
547 sal_uInt32 nY = nHeight;
548 while( nY-- )
550 pD->StartLine( pDestData );
551 pS->StartLine( pSrcData );
553 sal_uInt32 nX = nWidth;
554 while( nX-- )
555 pD->WritePixel( pS->ReadPixel() );
557 pSrcData += nSrcBytesPerRow;
558 pDestData += nDestBytesPerRow;
561 delete pS;
562 delete pD;
566 // ------------------------------------------------------------------
568 Size QuartzSalBitmap::GetSize() const
570 return Size( mnWidth, mnHeight );
573 // ------------------------------------------------------------------
575 sal_uInt16 QuartzSalBitmap::GetBitCount() const
577 return mnBits;
580 // ------------------------------------------------------------------
582 static struct pal_entry
584 sal_uInt8 mnRed;
585 sal_uInt8 mnGreen;
586 sal_uInt8 mnBlue;
588 const aImplSalSysPalEntryAry[ 16 ] =
590 { 0, 0, 0 },
591 { 0, 0, 0x80 },
592 { 0, 0x80, 0 },
593 { 0, 0x80, 0x80 },
594 { 0x80, 0, 0 },
595 { 0x80, 0, 0x80 },
596 { 0x80, 0x80, 0 },
597 { 0x80, 0x80, 0x80 },
598 { 0xC0, 0xC0, 0xC0 },
599 { 0, 0, 0xFF },
600 { 0, 0xFF, 0 },
601 { 0, 0xFF, 0xFF },
602 { 0xFF, 0, 0 },
603 { 0xFF, 0, 0xFF },
604 { 0xFF, 0xFF, 0 },
605 { 0xFF, 0xFF, 0xFF }
608 const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome )
610 if( bMonochrome )
611 return Bitmap::GetGreyPalette( 1U << mnBits );
613 // at this point we should provide some kind of default palette
614 // since all other platforms do so, too.
615 static bool bDefPalInit = false;
616 static BitmapPalette aDefPalette256;
617 static BitmapPalette aDefPalette16;
618 static BitmapPalette aDefPalette2;
619 if( ! bDefPalInit )
621 bDefPalInit = true;
622 aDefPalette256.SetEntryCount( 256 );
623 aDefPalette16.SetEntryCount( 16 );
624 aDefPalette2.SetEntryCount( 2 );
626 // Standard colors
627 unsigned int i;
628 for( i = 0; i < 16; i++ )
630 aDefPalette16[i] =
631 aDefPalette256[i] = BitmapColor( aImplSalSysPalEntryAry[i].mnRed,
632 aImplSalSysPalEntryAry[i].mnGreen,
633 aImplSalSysPalEntryAry[i].mnBlue );
636 aDefPalette2[0] = BitmapColor( 0, 0, 0 );
637 aDefPalette2[1] = BitmapColor( 0xff, 0xff, 0xff );
639 // own palette (6/6/6)
640 const int DITHER_PAL_STEPS = 6;
641 const sal_uInt8 DITHER_PAL_DELTA = 51;
642 int nB, nG, nR;
643 sal_uInt8 nRed, nGreen, nBlue;
644 for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
646 for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
648 for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
650 aDefPalette256[ i ] = BitmapColor( nRed, nGreen, nBlue );
651 i++;
657 // now fill in appropriate palette
658 switch( mnBits )
660 case 1: return aDefPalette2;
661 case 4: return aDefPalette16;
662 case 8: return aDefPalette256;
663 default: break;
666 const static BitmapPalette aEmptyPalette;
667 return aEmptyPalette;
670 BitmapBuffer* QuartzSalBitmap::AcquireBuffer( bool /*bReadOnly*/ )
672 if( !maUserBuffer.get() )
673 // || maContextBuffer.get() && (maUserBuffer.get() != maContextBuffer.get()) )
675 fprintf(stderr,"ASB::Acq(%dx%d,d=%d)\n",mnWidth,mnHeight,mnBits);
676 // TODO: AllocateUserData();
677 return NULL;
680 BitmapBuffer* pBuffer = new BitmapBuffer;
681 pBuffer->mnWidth = mnWidth;
682 pBuffer->mnHeight = mnHeight;
683 pBuffer->maPalette = maPalette;
684 pBuffer->mnScanlineSize = mnBytesPerRow;
685 pBuffer->mpBits = maUserBuffer.get();
686 pBuffer->mnBitCount = mnBits;
687 switch( mnBits )
689 case 1: pBuffer->mnFormat = BMP_FORMAT_1BIT_MSB_PAL; break;
690 case 4: pBuffer->mnFormat = BMP_FORMAT_4BIT_MSN_PAL; break;
691 case 8: pBuffer->mnFormat = BMP_FORMAT_8BIT_PAL; break;
692 case 16: pBuffer->mnFormat = BMP_FORMAT_16BIT_TC_MSB_MASK;
693 pBuffer->maColorMask = ColorMask( k16BitRedColorMask, k16BitGreenColorMask, k16BitBlueColorMask );
694 break;
695 case 24: pBuffer->mnFormat = BMP_FORMAT_24BIT_TC_BGR; break;
696 case 32: pBuffer->mnFormat = BMP_FORMAT_32BIT_TC_ARGB;
697 pBuffer->maColorMask = ColorMask( k32BitRedColorMask, k32BitGreenColorMask, k32BitBlueColorMask );
698 break;
700 pBuffer->mnFormat |= BMP_FORMAT_BOTTOM_UP;
702 // some BitmapBuffer users depend on a complete palette
703 if( (mnBits <= 8) && !maPalette )
704 pBuffer->maPalette = GetDefaultPalette( mnBits, true );
706 return pBuffer;
709 // ------------------------------------------------------------------
711 void QuartzSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly )
713 // invalidate graphic context if we have different data
714 if( !bReadOnly )
716 maPalette = pBuffer->maPalette;
717 if( mxGraphicContext )
718 DestroyContext();
721 delete pBuffer;
724 // ------------------------------------------------------------------
726 CGImageRef QuartzSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const
728 if( !mxCachedImage )
730 if( !mxGraphicContext )
731 if( !const_cast<QuartzSalBitmap*>(this)->CreateContext() )
732 return NULL;
734 mxCachedImage = CGBitmapContextCreateImage( mxGraphicContext );
737 CGImageRef xCroppedImage = NULL;
738 // short circuit if there is nothing to crop
739 if( !nX && !nY && (mnWidth == nNewWidth) && (mnHeight == nNewHeight) )
741 xCroppedImage = mxCachedImage;
742 CFRetain( xCroppedImage );
744 else
746 nY = mnHeight - (nY + nNewHeight); // adjust for y-mirrored context
747 const CGRect aCropRect = { { static_cast<CGFloat>(nX), static_cast<CGFloat>(nY) }, { static_cast<CGFloat>(nNewWidth), static_cast<CGFloat>(nNewHeight) } };
748 xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect );
751 return xCroppedImage;
754 // ------------------------------------------------------------------
756 static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
758 rtl_freeMemory( const_cast<void*>(data) );
761 CGImageRef QuartzSalBitmap::CreateWithMask( const QuartzSalBitmap& rMask,
762 int nX, int nY, int nWidth, int nHeight ) const
764 CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) );
765 if( !xImage )
766 return NULL;
768 CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
769 if( !xMask )
770 return xImage;
772 // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
773 // TODO: isolate in an extra method?
774 if( !CGImageIsMask(xMask) || (CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
776 const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset
778 // create the alpha mask image fitting our image
779 // TODO: is caching the full mask or the subimage mask worth it?
780 int nMaskBytesPerRow = ((nWidth + 3) & ~3);
781 void* pMaskMem = rtl_allocateMemory( nMaskBytesPerRow * nHeight );
782 CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
783 nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone );
784 CGContextDrawImage( xMaskContext, xImageRect, xMask );
785 CFRelease( xMask );
786 CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( NULL,
787 pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
788 static const CGFloat* pDecode = NULL;
789 xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false );
790 CFRelease( xDataProvider );
791 CFRelease( xMaskContext );
794 if( !xMask )
795 return xImage;
797 // combine image and alpha mask
798 CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
799 CFRelease( xMask );
800 CFRelease( xImage );
801 return xMaskedImage;
804 // ------------------------------------------------------------------
806 /** creates an image from the given rectangle, replacing all black pixels with nMaskColor and make all other full transparent */
807 CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int nY, int nWidth, int nHeight, SalColor nMaskColor ) const
809 CGImageRef xMask = 0;
810 if( maUserBuffer.get() && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight) )
812 const sal_uInt32 nDestBytesPerRow = nWidth << 2;
813 sal_uInt32* pMaskBuffer = static_cast<sal_uInt32*>( rtl_allocateMemory( nHeight * nDestBytesPerRow ) );
814 sal_uInt32* pDest = pMaskBuffer;
816 ImplPixelFormat* pSourcePixels = ImplPixelFormat::GetFormat( mnBits, maPalette );
818 if( pMaskBuffer && pSourcePixels )
820 sal_uInt32 nColor;
821 reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff;
822 reinterpret_cast<sal_uInt8*>(&nColor)[1] = SALCOLOR_RED( nMaskColor );
823 reinterpret_cast<sal_uInt8*>(&nColor)[2] = SALCOLOR_GREEN( nMaskColor );
824 reinterpret_cast<sal_uInt8*>(&nColor)[3] = SALCOLOR_BLUE( nMaskColor );
826 sal_uInt8* pSource = maUserBuffer.get();
827 if( nY )
828 pSource += nY * mnBytesPerRow;
830 int y = nHeight;
831 while( y-- )
833 pSourcePixels->StartLine( pSource );
834 pSourcePixels->SkipPixel(nX);
835 sal_uInt32 x = nWidth;
836 while( x-- )
838 *pDest++ = ( pSourcePixels->ReadPixel() == 0 ) ? nColor : 0;
840 pSource += mnBytesPerRow;
843 CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(NULL, pMaskBuffer, nHeight * nDestBytesPerRow, &CFRTLFree) );
844 xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, NULL, true, kCGRenderingIntentDefault);
845 CFRelease(xDataProvider);
847 else
849 free(pMaskBuffer);
852 delete pSourcePixels;
854 return xMask;
857 // =======================================================================
859 /** QuartzSalBitmap::GetSystemData Get platform native image data from existing image
861 * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx
862 * @return true if successful
864 bool QuartzSalBitmap::GetSystemData( BitmapSystemData& rData )
866 bool bRet = false;
868 if( !mxGraphicContext )
869 CreateContext();
871 if ( mxGraphicContext )
873 bRet = true;
875 if ((CGBitmapContextGetBitsPerPixel(mxGraphicContext) == 32) &&
876 (CGBitmapContextGetBitmapInfo(mxGraphicContext) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host) {
878 * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it.
880 OSL_TRACE("QuartzSalBitmap::%s(): kCGBitmapByteOrder32Host not found => inserting it.",__func__);
882 CGImageRef xImage = CGBitmapContextCreateImage (mxGraphicContext);
884 // re-create the context with single change: include kCGBitmapByteOrder32Host flag.
885 CGContextRef mxGraphicContextNew = CGBitmapContextCreate( CGBitmapContextGetData(mxGraphicContext),
886 CGBitmapContextGetWidth(mxGraphicContext),
887 CGBitmapContextGetHeight(mxGraphicContext),
888 CGBitmapContextGetBitsPerComponent(mxGraphicContext),
889 CGBitmapContextGetBytesPerRow(mxGraphicContext),
890 CGBitmapContextGetColorSpace(mxGraphicContext),
891 CGBitmapContextGetBitmapInfo(mxGraphicContext) | kCGBitmapByteOrder32Host);
892 CFRelease(mxGraphicContext);
894 // Needs to be flipped
895 CGContextSaveGState( mxGraphicContextNew );
896 CGContextTranslateCTM (mxGraphicContextNew, 0, CGBitmapContextGetHeight(mxGraphicContextNew));
897 CGContextScaleCTM (mxGraphicContextNew, 1.0, -1.0);
899 CGContextDrawImage(mxGraphicContextNew, CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage);
901 // Flip back
902 CGContextRestoreGState( mxGraphicContextNew );
904 CGImageRelease( xImage );
905 mxGraphicContext = mxGraphicContextNew;
908 rData.rImageContext = (void *) mxGraphicContext;
909 rData.mnWidth = mnWidth;
910 rData.mnHeight = mnHeight;
913 return bRet;
916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */