1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
34 #include "aqua/saldata.hxx"
36 #include "saldatabasic.hxx"
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
)
68 // ------------------------------------------------------------------
70 QuartzSalBitmap::~QuartzSalBitmap()
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
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) )
95 // initialize properties
98 mnBits
= nBitmapBits
? nBitmapBits
: 32;
100 // initialize drawing context
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
);
109 // ------------------------------------------------------------------
111 bool QuartzSalBitmap::Create( const Size
& rSize
, sal_uInt16 nBits
, const BitmapPalette
& rBitmapPalette
)
113 if( !isValidBitCount( nBits
) )
115 maPalette
= rBitmapPalette
;
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() );
158 // ------------------------------------------------------------------
160 bool QuartzSalBitmap::Create( const ::com::sun::star::uno::Reference
< ::com::sun::star::rendering::XBitmapCanvas
> /*xBitmapCanvas*/, Size
& /*rSize*/, bool /*bMask*/ )
165 // ------------------------------------------------------------------
167 void QuartzSalBitmap::Destroy()
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()
194 // prepare graphics context
195 // convert image from user input if available
196 const bool bSkipConversion
= !maUserBuffer
;
197 if( bSkipConversion
)
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?
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()
258 if( mnWidth
&& mnHeight
)
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;
271 OSL_FAIL("vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
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
);
287 return maUserBuffer
.get() != 0;
290 // ------------------------------------------------------------------
292 class ImplPixelFormat
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
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] );
320 virtual void WritePixel( ColorData nColor
)
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
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] );
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
358 virtual void StartLine( sal_uInt8
* pLine
)
360 pData16
= (sal_uInt16
*)pLine
;
362 virtual void SkipPixel( sal_uInt32 nPixel
)
366 virtual ColorData
ReadPixel()
368 const ColorData c
= RGB_COLORDATA( (*pData
& 0x7c00) >> 7, (*pData
& 0x03e0) >> 2 , (*pData
& 0x001f) << 3 );
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
383 const BitmapPalette
& mrPalette
;
386 ImplPixelFormat8( const BitmapPalette
& rPalette
)
387 : mrPalette( rPalette
)
390 virtual void SkipPixel( sal_uInt32 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
408 const BitmapPalette
& mrPalette
;
413 ImplPixelFormat4( const BitmapPalette
& rPalette
)
414 : mrPalette( rPalette
)
417 virtual void SkipPixel( sal_uInt32 nPixel
)
423 virtual void StartLine( sal_uInt8
* pLine
)
429 virtual ColorData
ReadPixel()
431 const BitmapColor
& rColor
= mrPalette
[( pData
[mnX
>> 1] >> mnShift
) & 0x0f];
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);
446 class ImplPixelFormat1
: public ImplPixelFormat
449 const BitmapPalette
& mrPalette
;
453 ImplPixelFormat1( const BitmapPalette
& rPalette
)
454 : mrPalette( rPalette
)
457 virtual void SkipPixel( sal_uInt32 nPixel
)
461 virtual void StartLine( sal_uInt8
* pLine
)
466 virtual ColorData
ReadPixel()
468 const BitmapColor
& rColor
= mrPalette
[ (pData
[mnX
>> 3 ] >> ( 7 - ( mnX
& 7 ) )) & 1];
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 ) );
478 pData
[ mnX
>> 3 ] &= ~( 1 << ( 7 - ( mnX
& 7 ) ) );
483 ImplPixelFormat
* ImplPixelFormat::GetFormat( sal_uInt16 nBits
, const BitmapPalette
& rPalette
)
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
;
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
);
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
);
540 // TODO: this implementation is for clarety, not for speed
542 ImplPixelFormat
* pD
= ImplPixelFormat::GetFormat( nDestBits
, rDestPalette
);
543 ImplPixelFormat
* pS
= ImplPixelFormat::GetFormat( nSrcBits
, rSrcPalette
);
547 sal_uInt32 nY
= nHeight
;
550 pD
->StartLine( pDestData
);
551 pS
->StartLine( pSrcData
);
553 sal_uInt32 nX
= nWidth
;
555 pD
->WritePixel( pS
->ReadPixel() );
557 pSrcData
+= nSrcBytesPerRow
;
558 pDestData
+= nDestBytesPerRow
;
566 // ------------------------------------------------------------------
568 Size
QuartzSalBitmap::GetSize() const
570 return Size( mnWidth
, mnHeight
);
573 // ------------------------------------------------------------------
575 sal_uInt16
QuartzSalBitmap::GetBitCount() const
580 // ------------------------------------------------------------------
582 static struct pal_entry
588 const aImplSalSysPalEntryAry
[ 16 ] =
597 { 0x80, 0x80, 0x80 },
598 { 0xC0, 0xC0, 0xC0 },
608 const BitmapPalette
& GetDefaultPalette( int mnBits
, bool 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
;
622 aDefPalette256
.SetEntryCount( 256 );
623 aDefPalette16
.SetEntryCount( 16 );
624 aDefPalette2
.SetEntryCount( 2 );
628 for( i
= 0; i
< 16; 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;
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
);
657 // now fill in appropriate palette
660 case 1: return aDefPalette2
;
661 case 4: return aDefPalette16
;
662 case 8: return aDefPalette256
;
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();
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
;
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
);
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
);
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 );
709 // ------------------------------------------------------------------
711 void QuartzSalBitmap::ReleaseBuffer( BitmapBuffer
* pBuffer
, bool bReadOnly
)
713 // invalidate graphic context if we have different data
716 maPalette
= pBuffer
->maPalette
;
717 if( mxGraphicContext
)
724 // ------------------------------------------------------------------
726 CGImageRef
QuartzSalBitmap::CreateCroppedImage( int nX
, int nY
, int nNewWidth
, int nNewHeight
) const
730 if( !mxGraphicContext
)
731 if( !const_cast<QuartzSalBitmap
*>(this)->CreateContext() )
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
);
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
) );
768 CGImageRef xMask
= rMask
.CreateCroppedImage( nX
, nY
, nWidth
, nHeight
);
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
);
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
);
797 // combine image and alpha mask
798 CGImageRef xMaskedImage
= CGImageCreateWithMask( xImage
, xMask
);
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
)
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();
828 pSource
+= nY
* mnBytesPerRow
;
833 pSourcePixels
->StartLine( pSource
);
834 pSourcePixels
->SkipPixel(nX
);
835 sal_uInt32 x
= nWidth
;
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
);
852 delete pSourcePixels
;
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
)
868 if( !mxGraphicContext
)
871 if ( mxGraphicContext
)
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
);
902 CGContextRestoreGState( mxGraphicContextNew
);
904 CGImageRelease( xImage
);
905 mxGraphicContext
= mxGraphicContextNew
;
908 rData
.rImageContext
= (void *) mxGraphicContext
;
909 rData
.mnWidth
= mnWidth
;
910 rData
.mnHeight
= mnHeight
;
916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */