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 .
20 #include <sal/config.h>
25 #include "basebmp/scanlineformats.hxx"
26 #include "basebmp/color.hxx"
28 #include "basegfx/vector/b2ivector.hxx"
30 #include "tools/color.hxx"
32 #include "vcl/bitmap.hxx"
33 #include "vcl/salbtype.hxx"
35 #include "quartz/salbmp.h"
36 #include "quartz/utils.h"
39 #include "osx/saldata.hxx"
41 #include "saldatabasic.hxx"
44 #include "bmpfast.hxx"
46 static const unsigned long k16BitRedColorMask
= 0x00007c00;
47 static const unsigned long k16BitGreenColorMask
= 0x000003e0;
48 static const unsigned long k16BitBlueColorMask
= 0x0000001f;
50 static const unsigned long k32BitRedColorMask
= 0x00ff0000;
51 static const unsigned long k32BitGreenColorMask
= 0x0000ff00;
52 static const unsigned long k32BitBlueColorMask
= 0x000000ff;
54 #if defined IOS && defined DBG_UTIL
56 #include <MobileCoreServices/UTCoreTypes.h>
57 #include <ImageIO/ImageIO.h>
59 static void writeImageToFile(CGImageRef image
, const char *baseName
)
61 static bool bDoIt
= getenv("DBG_WRITE_CGIMAGES");
64 static int counter
= 0;
65 NSArray
*paths
= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory
, NSUserDomainMask
, YES
);
66 NSString
*documentsDirectory
= [paths objectAtIndex
:0];
67 NSString
*path
= [NSString stringWithFormat
:@
"%@/%s.%d.png", documentsDirectory
, baseName
, counter
++];
68 CFURLRef url
= (CFURLRef
)[NSURL fileURLWithPath
:path
];
69 CGImageDestinationRef destination
= CGImageDestinationCreateWithURL(url
, kUTTypePNG
, 1, NULL
);
70 CGImageDestinationAddImage(destination
, image
, nil
);
72 if (!CGImageDestinationFinalize(destination
)) {
73 NSLog(@
"Failed to write image to %@", path
);
75 SAL_DEBUG("--- saved image " << baseName
<< " to " << [path UTF8String
]);
78 CFRelease(destination
);
81 #define DBG_WRITE_IMAGE(image, name) writeImageToFile(image, name)
85 #define DBG_WRITE_IMAGE(image, name) /* empty */
89 static bool isValidBitCount( sal_uInt16 nBitCount
)
91 return (nBitCount
== 1) || (nBitCount
== 4) || (nBitCount
== 8) || (nBitCount
== 16) || (nBitCount
== 24) || (nBitCount
== 32);
94 QuartzSalBitmap::QuartzSalBitmap()
95 : mxGraphicContext( NULL
)
96 , mxCachedImage( NULL
)
104 QuartzSalBitmap::~QuartzSalBitmap()
109 bool QuartzSalBitmap::Create( CGLayerRef xLayer
, int nBitmapBits
,
110 int nX
, int nY
, int nWidth
, int nHeight
)
112 DBG_ASSERT( xLayer
, "QuartzSalBitmap::Create() from non-layered context" );
114 // sanitize input parameters
116 nWidth
+= nX
, nX
= 0;
118 nHeight
+= nY
, nY
= 0;
119 const CGSize aLayerSize
= CGLayerGetSize( xLayer
);
120 CG_TRACE( "CGLayerGetSize(" << xLayer
<< ") = " << aLayerSize
);
121 if( nWidth
>= (int)aLayerSize
.width
- nX
)
122 nWidth
= (int)aLayerSize
.width
- nX
;
123 if( nHeight
>= (int)aLayerSize
.height
- nY
)
124 nHeight
= (int)aLayerSize
.height
- nY
;
125 if( (nWidth
< 0) || (nHeight
< 0) )
126 nWidth
= nHeight
= 0;
128 // initialize properties
131 mnBits
= nBitmapBits
? nBitmapBits
: 32;
133 // initialize drawing context
136 // copy layer content into the bitmap buffer
137 const CGPoint aSrcPoint
= { static_cast<CGFloat
>(-nX
), static_cast<CGFloat
>(-nY
) };
138 if(mxGraphicContext
) // remove warning
140 CG_TRACE( "CGContextDrawLayerAtPoint(" << mxGraphicContext
<< "," << aSrcPoint
<< "," << xLayer
<< ")" );
141 CGContextDrawLayerAtPoint( mxGraphicContext
, aSrcPoint
, xLayer
);
146 bool QuartzSalBitmap::Create( const Size
& rSize
, sal_uInt16 nBits
, const BitmapPalette
& rBitmapPalette
)
148 if( !isValidBitCount( nBits
) )
150 maPalette
= rBitmapPalette
;
152 mnWidth
= rSize
.Width();
153 mnHeight
= rSize
.Height();
154 return AllocateUserData();
157 bool QuartzSalBitmap::Create( const SalBitmap
& rSalBmp
)
159 return Create( rSalBmp
, rSalBmp
.GetBitCount() );
162 bool QuartzSalBitmap::Create( const SalBitmap
& rSalBmp
, SalGraphics
* pGraphics
)
164 return Create( rSalBmp
, pGraphics
? pGraphics
->GetBitCount() : rSalBmp
.GetBitCount() );
167 bool QuartzSalBitmap::Create( const SalBitmap
& rSalBmp
, sal_uInt16 nNewBitCount
)
169 const QuartzSalBitmap
& rSourceBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBmp
);
171 if( isValidBitCount( nNewBitCount
) && rSourceBitmap
.maUserBuffer
.get() )
173 mnBits
= nNewBitCount
;
174 mnWidth
= rSourceBitmap
.mnWidth
;
175 mnHeight
= rSourceBitmap
.mnHeight
;
176 maPalette
= rSourceBitmap
.maPalette
;
178 if( AllocateUserData() )
180 ConvertBitmapData( mnWidth
, mnHeight
, mnBits
, mnBytesPerRow
, maPalette
, maUserBuffer
.get(), rSourceBitmap
.mnBits
, rSourceBitmap
.mnBytesPerRow
, rSourceBitmap
.maPalette
, rSourceBitmap
.maUserBuffer
.get() );
187 bool QuartzSalBitmap::Create( const ::com::sun::star::uno::Reference
< ::com::sun::star::rendering::XBitmapCanvas
>& /*xBitmapCanvas*/, Size
& /*rSize*/, bool /*bMask*/ )
192 void QuartzSalBitmap::Destroy()
195 maUserBuffer
.reset();
198 void QuartzSalBitmap::DestroyContext()
202 CG_TRACE( "CGImageRelease(" << mxCachedImage
<< ")" );
203 CGImageRelease( mxCachedImage
);
204 mxCachedImage
= NULL
;
207 if( mxGraphicContext
)
209 CG_TRACE( "CGContextRelease(" << mxGraphicContext
<< ")" );
210 CGContextRelease( mxGraphicContext
);
211 mxGraphicContext
= NULL
;
212 maContextBuffer
.reset();
216 bool QuartzSalBitmap::CreateContext()
220 // prepare graphics context
221 // convert image from user input if available
222 const bool bSkipConversion
= !maUserBuffer
;
223 if( bSkipConversion
)
226 // default to RGBA color space
227 CGColorSpaceRef aCGColorSpace
= GetSalData()->mxRGBSpace
;
228 CGBitmapInfo aCGBmpInfo
= kCGImageAlphaNoneSkipFirst
;
230 // convert data into something accepted by CGBitmapContextCreate()
231 size_t bitsPerComponent
= (mnBits
== 16) ? 5 : 8;
232 sal_uInt32 nContextBytesPerRow
= mnBytesPerRow
;
233 if( (mnBits
== 16) || (mnBits
== 32) )
235 // no conversion needed for truecolor
236 maContextBuffer
= maUserBuffer
;
238 else if( mnBits
== 8 && maPalette
.IsGreyPalette() )
240 // no conversion needed for grayscale
241 maContextBuffer
= maUserBuffer
;
242 aCGColorSpace
= GetSalData()->mxGraySpace
;
243 aCGBmpInfo
= kCGImageAlphaNone
;
244 bitsPerComponent
= mnBits
;
246 // TODO: is special handling for 1bit input buffers worth it?
249 // convert user data to 32 bit
250 nContextBytesPerRow
= mnWidth
<< 2;
253 maContextBuffer
.reset( new sal_uInt8
[ mnHeight
* nContextBytesPerRow
] );
255 for (size_t i
= 0; i
< mnHeight
* nContextBytesPerRow
; i
++)
256 maContextBuffer
.get()[i
] = (i
& 0xFF);
259 if( !bSkipConversion
)
260 ConvertBitmapData( mnWidth
, mnHeight
,
261 32, nContextBytesPerRow
, maPalette
, maContextBuffer
.get(),
262 mnBits
, mnBytesPerRow
, maPalette
, maUserBuffer
.get() );
264 catch( const std::bad_alloc
& )
266 mxGraphicContext
= 0;
270 if( maContextBuffer
.get() )
272 mxGraphicContext
= CGBitmapContextCreate( maContextBuffer
.get(), mnWidth
, mnHeight
,
273 bitsPerComponent
, nContextBytesPerRow
, aCGColorSpace
, aCGBmpInfo
);
274 CG_TRACE( "CGBitmapContextCreate(" << mnWidth
<< "x" << mnHeight
<< "x" << bitsPerComponent
<< ") = " << mxGraphicContext
);
277 if( !mxGraphicContext
)
278 maContextBuffer
.reset();
280 return mxGraphicContext
!= NULL
;
283 bool QuartzSalBitmap::AllocateUserData()
287 if( mnWidth
&& mnHeight
)
293 case 1: mnBytesPerRow
= (mnWidth
+ 7) >> 3; break;
294 case 4: mnBytesPerRow
= (mnWidth
+ 1) >> 1; break;
295 case 8: mnBytesPerRow
= mnWidth
; break;
296 case 16: mnBytesPerRow
= mnWidth
<< 1; break;
297 case 24: mnBytesPerRow
= (mnWidth
<< 1) + mnWidth
; break;
298 case 32: mnBytesPerRow
= mnWidth
<< 2; break;
300 OSL_FAIL("vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
305 if (mnBytesPerRow
!= 0
306 && mnBytesPerRow
<= std::numeric_limits
<sal_uInt32
>::max() / mnHeight
)
310 maUserBuffer
.reset( new sal_uInt8
[mnBytesPerRow
* mnHeight
] );
313 catch (std::bad_alloc
&) {}
318 "vcl.quartz", "bad alloc " << mnBytesPerRow
<< "x" << mnHeight
);
319 maUserBuffer
.reset( static_cast<sal_uInt8
*>(NULL
) );
325 for (size_t i
= 0; i
< mnBytesPerRow
* mnHeight
; i
++)
326 maUserBuffer
.get()[i
] = (i
& 0xFF);
330 return maUserBuffer
.get() != 0;
335 class ImplPixelFormat
338 static ImplPixelFormat
* GetFormat( sal_uInt16 nBits
, const BitmapPalette
& rPalette
);
340 virtual void StartLine( sal_uInt8
* pLine
) = 0;
341 virtual void SkipPixel( sal_uInt32 nPixel
) = 0;
342 virtual ColorData
ReadPixel() = 0;
343 virtual void WritePixel( ColorData nColor
) = 0;
344 virtual ~ImplPixelFormat() { }
347 class ImplPixelFormat32
: public ImplPixelFormat
348 // currently ARGB-format for 32bit depth
352 virtual void StartLine( sal_uInt8
* pLine
) SAL_OVERRIDE
{ pData
= pLine
; }
353 virtual void SkipPixel( sal_uInt32 nPixel
) SAL_OVERRIDE
355 pData
+= nPixel
<< 2;
357 virtual ColorData
ReadPixel() SAL_OVERRIDE
359 const ColorData c
= RGB_COLORDATA( pData
[1], pData
[2], pData
[3] );
363 virtual void WritePixel( ColorData nColor
) SAL_OVERRIDE
366 *pData
++ = COLORDATA_RED( nColor
);
367 *pData
++ = COLORDATA_GREEN( nColor
);
368 *pData
++ = COLORDATA_BLUE( nColor
);
372 class ImplPixelFormat24
: public ImplPixelFormat
373 // currently BGR-format for 24bit depth
377 virtual void StartLine( sal_uInt8
* pLine
) SAL_OVERRIDE
{ pData
= pLine
; }
378 virtual void SkipPixel( sal_uInt32 nPixel
) SAL_OVERRIDE
380 pData
+= (nPixel
<< 1) + nPixel
;
382 virtual ColorData
ReadPixel() SAL_OVERRIDE
384 const ColorData c
= RGB_COLORDATA( pData
[2], pData
[1], pData
[0] );
388 virtual void WritePixel( ColorData nColor
) SAL_OVERRIDE
390 *pData
++ = COLORDATA_BLUE( nColor
);
391 *pData
++ = COLORDATA_GREEN( nColor
);
392 *pData
++ = COLORDATA_RED( nColor
);
396 class ImplPixelFormat16
: public ImplPixelFormat
397 // currently R5G6B5-format for 16bit depth
402 virtual void StartLine( sal_uInt8
* pLine
) SAL_OVERRIDE
404 pData
= reinterpret_cast<sal_uInt16
*>(pLine
);
406 virtual void SkipPixel( sal_uInt32 nPixel
) SAL_OVERRIDE
410 virtual ColorData
ReadPixel() SAL_OVERRIDE
412 const ColorData c
= RGB_COLORDATA( (*pData
& 0xf800) >> 8, (*pData
& 0x07e0) >> 3 , (*pData
& 0x001f) << 3 );
416 virtual void WritePixel( ColorData nColor
) SAL_OVERRIDE
418 *pData
++ = ((COLORDATA_RED( nColor
) & 0xf8 ) << 8 ) |
419 ((COLORDATA_GREEN( nColor
) & 0xfc ) << 3 ) |
420 ((COLORDATA_BLUE( nColor
) & 0xf8 ) >> 3 );
424 class ImplPixelFormat8
: public ImplPixelFormat
428 const BitmapPalette
& mrPalette
;
431 ImplPixelFormat8( const BitmapPalette
& rPalette
)
432 : mrPalette( rPalette
)
435 virtual void StartLine( sal_uInt8
* pLine
) SAL_OVERRIDE
{ pData
= pLine
; }
436 virtual void SkipPixel( sal_uInt32 nPixel
) SAL_OVERRIDE
440 virtual ColorData
ReadPixel() SAL_OVERRIDE
442 return mrPalette
[ *pData
++ ].operator Color().GetColor();
444 virtual void WritePixel( ColorData nColor
) SAL_OVERRIDE
446 const BitmapColor
aColor( COLORDATA_RED( nColor
), COLORDATA_GREEN( nColor
), COLORDATA_BLUE( nColor
) );
447 *pData
++ = static_cast< sal_uInt8
>( mrPalette
.GetBestIndex( aColor
) );
451 class ImplPixelFormat4
: public ImplPixelFormat
455 const BitmapPalette
& mrPalette
;
460 ImplPixelFormat4( const BitmapPalette
& rPalette
)
461 : mrPalette( rPalette
)
464 virtual void SkipPixel( sal_uInt32 nPixel
) SAL_OVERRIDE
470 virtual void StartLine( sal_uInt8
* pLine
) SAL_OVERRIDE
476 virtual ColorData
ReadPixel() SAL_OVERRIDE
478 const BitmapColor
& rColor
= mrPalette
[( pData
[mnX
>> 1] >> mnShift
) & 0x0f];
481 return rColor
.operator Color().GetColor();
483 virtual void WritePixel( ColorData nColor
) SAL_OVERRIDE
485 const BitmapColor
aColor( COLORDATA_RED( nColor
), COLORDATA_GREEN( nColor
), COLORDATA_BLUE( nColor
) );
486 pData
[mnX
>>1] &= (0xf0 >> mnShift
);
487 pData
[mnX
>>1] |= (static_cast< sal_uInt8
>( mrPalette
.GetBestIndex( aColor
) ) & 0x0f);
493 class ImplPixelFormat1
: public ImplPixelFormat
497 const BitmapPalette
& mrPalette
;
501 ImplPixelFormat1( const BitmapPalette
& rPalette
)
502 : mrPalette( rPalette
)
505 virtual void SkipPixel( sal_uInt32 nPixel
) SAL_OVERRIDE
509 virtual void StartLine( sal_uInt8
* pLine
) SAL_OVERRIDE
514 virtual ColorData
ReadPixel() SAL_OVERRIDE
516 const BitmapColor
& rColor
= mrPalette
[ (pData
[mnX
>> 3 ] >> ( 7 - ( mnX
& 7 ) )) & 1];
518 return rColor
.operator Color().GetColor();
520 virtual void WritePixel( ColorData nColor
) SAL_OVERRIDE
522 const BitmapColor
aColor( COLORDATA_RED( nColor
), COLORDATA_GREEN( nColor
), COLORDATA_BLUE( nColor
) );
523 if( mrPalette
.GetBestIndex( aColor
) & 1 )
524 pData
[ mnX
>> 3 ] |= 1 << ( 7 - ( mnX
& 7 ) );
526 pData
[ mnX
>> 3 ] &= ~( 1 << ( 7 - ( mnX
& 7 ) ) );
531 ImplPixelFormat
* ImplPixelFormat::GetFormat( sal_uInt16 nBits
, const BitmapPalette
& rPalette
)
535 case 1: return new ImplPixelFormat1( rPalette
);
536 case 4: return new ImplPixelFormat4( rPalette
);
537 case 8: return new ImplPixelFormat8( rPalette
);
538 case 16: return new ImplPixelFormat16
;
539 case 24: return new ImplPixelFormat24
;
540 case 32: return new ImplPixelFormat32
;
548 void QuartzSalBitmap::ConvertBitmapData( sal_uInt32 nWidth
, sal_uInt32 nHeight
,
549 sal_uInt16 nDestBits
, sal_uInt32 nDestBytesPerRow
, const BitmapPalette
& rDestPalette
, sal_uInt8
* pDestData
,
550 sal_uInt16 nSrcBits
, sal_uInt32 nSrcBytesPerRow
, const BitmapPalette
& rSrcPalette
, sal_uInt8
* pSrcData
)
553 if( (nDestBytesPerRow
== nSrcBytesPerRow
) && (nDestBits
== nSrcBits
) && ((nSrcBits
!= 8) || (rDestPalette
.operator==( rSrcPalette
))) )
555 // simple case, same format, so just copy
556 memcpy( pDestData
, pSrcData
, nHeight
* nDestBytesPerRow
);
560 // try accelerated conversion if possible
561 // TODO: are other truecolor conversions except BGR->ARGB worth it?
562 bool bConverted
= false;
563 if( (nSrcBits
== 24) && (nDestBits
== 32) )
565 // TODO: extend bmpfast.cxx with a method that can be directly used here
566 BitmapBuffer aSrcBuf
;
567 aSrcBuf
.mnFormat
= BMP_FORMAT_24BIT_TC_BGR
;
568 aSrcBuf
.mpBits
= pSrcData
;
569 aSrcBuf
.mnBitCount
= nSrcBits
;
570 aSrcBuf
.mnScanlineSize
= nSrcBytesPerRow
;
571 BitmapBuffer aDstBuf
;
572 aDstBuf
.mnFormat
= BMP_FORMAT_32BIT_TC_ARGB
;
573 aDstBuf
.mpBits
= pDestData
;
574 aSrcBuf
.mnBitCount
= nDestBits
;
575 aDstBuf
.mnScanlineSize
= nDestBytesPerRow
;
577 aSrcBuf
.mnWidth
= aDstBuf
.mnWidth
= nWidth
;
578 aSrcBuf
.mnHeight
= aDstBuf
.mnHeight
= nHeight
;
580 SalTwoRect
aTwoRects(0, 0, mnWidth
, mnHeight
, 0, 0, mnWidth
, mnHeight
);
581 bConverted
= ::ImplFastBitmapConversion( aDstBuf
, aSrcBuf
, aTwoRects
);
586 // TODO: this implementation is for clarety, not for speed
588 ImplPixelFormat
* pD
= ImplPixelFormat::GetFormat( nDestBits
, rDestPalette
);
589 ImplPixelFormat
* pS
= ImplPixelFormat::GetFormat( nSrcBits
, rSrcPalette
);
593 sal_uInt32 nY
= nHeight
;
596 pD
->StartLine( pDestData
);
597 pS
->StartLine( pSrcData
);
599 sal_uInt32 nX
= nWidth
;
601 pD
->WritePixel( pS
->ReadPixel() );
603 pSrcData
+= nSrcBytesPerRow
;
604 pDestData
+= nDestBytesPerRow
;
612 Size
QuartzSalBitmap::GetSize() const
614 return Size( mnWidth
, mnHeight
);
617 sal_uInt16
QuartzSalBitmap::GetBitCount() const
622 static struct pal_entry
628 const aImplSalSysPalEntryAry
[ 16 ] =
637 { 0x80, 0x80, 0x80 },
638 { 0xC0, 0xC0, 0xC0 },
648 const BitmapPalette
& GetDefaultPalette( int mnBits
, bool bMonochrome
)
651 return Bitmap::GetGreyPalette( 1U << mnBits
);
653 // at this point we should provide some kind of default palette
654 // since all other platforms do so, too.
655 static bool bDefPalInit
= false;
656 static BitmapPalette aDefPalette256
;
657 static BitmapPalette aDefPalette16
;
658 static BitmapPalette aDefPalette2
;
662 aDefPalette256
.SetEntryCount( 256 );
663 aDefPalette16
.SetEntryCount( 16 );
664 aDefPalette2
.SetEntryCount( 2 );
668 for( i
= 0; i
< 16; i
++ )
671 aDefPalette256
[i
] = BitmapColor( aImplSalSysPalEntryAry
[i
].mnRed
,
672 aImplSalSysPalEntryAry
[i
].mnGreen
,
673 aImplSalSysPalEntryAry
[i
].mnBlue
);
676 aDefPalette2
[0] = BitmapColor( 0, 0, 0 );
677 aDefPalette2
[1] = BitmapColor( 0xff, 0xff, 0xff );
679 // own palette (6/6/6)
680 const int DITHER_PAL_STEPS
= 6;
681 const sal_uInt8 DITHER_PAL_DELTA
= 51;
683 sal_uInt8 nRed
, nGreen
, nBlue
;
684 for( nB
=0, nBlue
=0; nB
< DITHER_PAL_STEPS
; nB
++, nBlue
+= DITHER_PAL_DELTA
)
686 for( nG
=0, nGreen
=0; nG
< DITHER_PAL_STEPS
; nG
++, nGreen
+= DITHER_PAL_DELTA
)
688 for( nR
=0, nRed
=0; nR
< DITHER_PAL_STEPS
; nR
++, nRed
+= DITHER_PAL_DELTA
)
690 aDefPalette256
[ i
] = BitmapColor( nRed
, nGreen
, nBlue
);
697 // now fill in appropriate palette
700 case 1: return aDefPalette2
;
701 case 4: return aDefPalette16
;
702 case 8: return aDefPalette256
;
706 const static BitmapPalette aEmptyPalette
;
707 return aEmptyPalette
;
710 BitmapBuffer
* QuartzSalBitmap::AcquireBuffer( BitmapAccessMode
/*nMode*/ )
712 if( !maUserBuffer
.get() )
713 // || maContextBuffer.get() && (maUserBuffer.get() != maContextBuffer.get()) )
715 // fprintf(stderr,"ASB::Acq(%dx%d,d=%d)\n",mnWidth,mnHeight,mnBits);
716 // TODO: AllocateUserData();
720 BitmapBuffer
* pBuffer
= new BitmapBuffer
;
721 pBuffer
->mnWidth
= mnWidth
;
722 pBuffer
->mnHeight
= mnHeight
;
723 pBuffer
->maPalette
= maPalette
;
724 pBuffer
->mnScanlineSize
= mnBytesPerRow
;
725 pBuffer
->mpBits
= maUserBuffer
.get();
726 pBuffer
->mnBitCount
= mnBits
;
729 case 1: pBuffer
->mnFormat
= BMP_FORMAT_1BIT_MSB_PAL
; break;
730 case 4: pBuffer
->mnFormat
= BMP_FORMAT_4BIT_MSN_PAL
; break;
731 case 8: pBuffer
->mnFormat
= BMP_FORMAT_8BIT_PAL
; break;
732 case 16: pBuffer
->mnFormat
= BMP_FORMAT_16BIT_TC_MSB_MASK
;
733 pBuffer
->maColorMask
= ColorMask( k16BitRedColorMask
, k16BitGreenColorMask
, k16BitBlueColorMask
);
735 case 24: pBuffer
->mnFormat
= BMP_FORMAT_24BIT_TC_BGR
; break;
736 case 32: pBuffer
->mnFormat
= BMP_FORMAT_32BIT_TC_ARGB
;
737 pBuffer
->maColorMask
= ColorMask( k32BitRedColorMask
, k32BitGreenColorMask
, k32BitBlueColorMask
);
740 pBuffer
->mnFormat
|= BMP_FORMAT_BOTTOM_UP
;
742 // some BitmapBuffer users depend on a complete palette
743 if( (mnBits
<= 8) && !maPalette
)
744 pBuffer
->maPalette
= GetDefaultPalette( mnBits
, true );
749 void QuartzSalBitmap::ReleaseBuffer( BitmapBuffer
* pBuffer
, BitmapAccessMode nMode
)
751 // invalidate graphic context if we have different data
752 if( nMode
== BITMAP_WRITE_ACCESS
)
754 maPalette
= pBuffer
->maPalette
;
755 if( mxGraphicContext
)
762 CGImageRef
QuartzSalBitmap::CreateCroppedImage( int nX
, int nY
, int nNewWidth
, int nNewHeight
) const
766 if( !mxGraphicContext
)
767 if( !const_cast<QuartzSalBitmap
*>(this)->CreateContext() )
770 mxCachedImage
= CGBitmapContextCreateImage( mxGraphicContext
);
771 CG_TRACE( "CGBitmapContextCreateImage(" << mxGraphicContext
<< ") = " << mxCachedImage
);
774 CGImageRef xCroppedImage
= NULL
;
775 // short circuit if there is nothing to crop
776 if( !nX
&& !nY
&& (mnWidth
== nNewWidth
) && (mnHeight
== nNewHeight
) )
778 xCroppedImage
= mxCachedImage
;
779 CG_TRACE( "CFRetain(" << xCroppedImage
<< ")" );
780 CFRetain( xCroppedImage
);
784 nY
= mnHeight
- (nY
+ nNewHeight
); // adjust for y-mirrored context
785 const CGRect aCropRect
= { { static_cast<CGFloat
>(nX
), static_cast<CGFloat
>(nY
) }, { static_cast<CGFloat
>(nNewWidth
), static_cast<CGFloat
>(nNewHeight
) } };
786 xCroppedImage
= CGImageCreateWithImageInRect( mxCachedImage
, aCropRect
);
787 CG_TRACE( "CGImageCreateWithImageInRect(" << mxCachedImage
<< "," << aCropRect
<< ") = " << xCroppedImage
);
790 return xCroppedImage
;
793 static void CFRTLFree(void* /*info*/, const void* data
, size_t /*size*/)
795 rtl_freeMemory( const_cast<void*>(data
) );
798 CGImageRef
QuartzSalBitmap::CreateWithMask( const QuartzSalBitmap
& rMask
,
799 int nX
, int nY
, int nWidth
, int nHeight
) const
801 CGImageRef
xImage( CreateCroppedImage( nX
, nY
, nWidth
, nHeight
) );
805 CGImageRef xMask
= rMask
.CreateCroppedImage( nX
, nY
, nWidth
, nHeight
);
809 // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
810 // TODO: isolate in an extra method?
811 DBG_WRITE_IMAGE(xMask
, "xMask");
812 if( !CGImageIsMask(xMask
) || rMask
.GetBitCount() != 8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
814 const CGRect xImageRect
=CGRectMake( 0, 0, nWidth
, nHeight
);//the rect has no offset
816 // create the alpha mask image fitting our image
817 // TODO: is caching the full mask or the subimage mask worth it?
818 int nMaskBytesPerRow
= ((nWidth
+ 3) & ~3);
819 void* pMaskMem
= rtl_allocateMemory( nMaskBytesPerRow
* nHeight
);
820 CGContextRef xMaskContext
= CGBitmapContextCreate( pMaskMem
,
821 nWidth
, nHeight
, 8, nMaskBytesPerRow
, GetSalData()->mxGraySpace
, kCGImageAlphaNone
);
822 CG_TRACE( "CGBitmapContextCreate(" << nWidth
<< "x" << nHeight
<< "x8," << nMaskBytesPerRow
<< ") = " << xMaskContext
);
823 CG_TRACE( "CGContextDrawImage(" << xMaskContext
<< "," << xImageRect
<< "," << xMask
<< ")" );
824 CGContextDrawImage( xMaskContext
, xImageRect
, xMask
);
825 CG_TRACE( "CFRelease(" << xMask
<< ")" );
827 CGDataProviderRef
xDataProvider( CGDataProviderCreateWithData( NULL
,
828 pMaskMem
, nHeight
* nMaskBytesPerRow
, &CFRTLFree
) );
829 static const CGFloat
* pDecode
= NULL
;
830 xMask
= CGImageMaskCreate( nWidth
, nHeight
, 8, 8, nMaskBytesPerRow
, xDataProvider
, pDecode
, false );
831 CG_TRACE( "CGImageMaskCreate(" << nWidth
<< "," << nHeight
<< ",8,8) = " << xMask
);
832 CFRelease( xDataProvider
);
833 CG_TRACE( "CFRelease(" << xMaskContext
<< ")" );
834 CFRelease( xMaskContext
);
840 // combine image and alpha mask
841 CGImageRef xMaskedImage
= CGImageCreateWithMask( xImage
, xMask
);
842 CG_TRACE( "CGImageCreateWithMask(" << xImage
<< "," << xMask
<< ") = " << xMaskedImage
);
843 DBG_WRITE_IMAGE(xImage
, "xImage");
844 DBG_WRITE_IMAGE(xMaskedImage
, "xMaskedImage");
845 CG_TRACE( "CFRelease(" << xMask
<< ")" );
847 CG_TRACE( "CFRelease(" << xImage
<< ")" );
852 /** creates an image from the given rectangle, replacing all black pixels with nMaskColor and make all other full transparent */
853 CGImageRef
QuartzSalBitmap::CreateColorMask( int nX
, int nY
, int nWidth
, int nHeight
, SalColor nMaskColor
) const
855 CGImageRef xMask
= 0;
856 if( maUserBuffer
.get() && (nX
+ nWidth
<= mnWidth
) && (nY
+ nHeight
<= mnHeight
) )
858 const sal_uInt32 nDestBytesPerRow
= nWidth
<< 2;
859 sal_uInt32
* pMaskBuffer
= static_cast<sal_uInt32
*>( rtl_allocateMemory( nHeight
* nDestBytesPerRow
) );
860 sal_uInt32
* pDest
= pMaskBuffer
;
862 ImplPixelFormat
* pSourcePixels
= ImplPixelFormat::GetFormat( mnBits
, maPalette
);
864 if( pMaskBuffer
&& pSourcePixels
)
867 reinterpret_cast<sal_uInt8
*>(&nColor
)[0] = 0xff;
868 reinterpret_cast<sal_uInt8
*>(&nColor
)[1] = SALCOLOR_RED( nMaskColor
);
869 reinterpret_cast<sal_uInt8
*>(&nColor
)[2] = SALCOLOR_GREEN( nMaskColor
);
870 reinterpret_cast<sal_uInt8
*>(&nColor
)[3] = SALCOLOR_BLUE( nMaskColor
);
872 sal_uInt8
* pSource
= maUserBuffer
.get();
874 pSource
+= nY
* mnBytesPerRow
;
879 pSourcePixels
->StartLine( pSource
);
880 pSourcePixels
->SkipPixel(nX
);
881 sal_uInt32 x
= nWidth
;
884 *pDest
++ = ( pSourcePixels
->ReadPixel() == 0 ) ? nColor
: 0;
886 pSource
+= mnBytesPerRow
;
889 CGDataProviderRef
xDataProvider( CGDataProviderCreateWithData(NULL
, pMaskBuffer
, nHeight
* nDestBytesPerRow
, &CFRTLFree
) );
890 xMask
= CGImageCreate(nWidth
, nHeight
, 8, 32, nDestBytesPerRow
, GetSalData()->mxRGBSpace
, kCGImageAlphaPremultipliedFirst
, xDataProvider
, NULL
, true, kCGRenderingIntentDefault
);
891 CG_TRACE( "CGImageCreate(" << nWidth
<< "x" << nHeight
<< "x8) = " << xMask
);
892 CFRelease(xDataProvider
);
899 delete pSourcePixels
;
904 /** QuartzSalBitmap::GetSystemData Get platform native image data from existing image
906 * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx
907 * @return true if successful
909 bool QuartzSalBitmap::GetSystemData( BitmapSystemData
& rData
)
913 if( !mxGraphicContext
)
916 if ( mxGraphicContext
)
920 if ((CGBitmapContextGetBitsPerPixel(mxGraphicContext
) == 32) &&
921 (CGBitmapContextGetBitmapInfo(mxGraphicContext
) & kCGBitmapByteOrderMask
) != kCGBitmapByteOrder32Host
) {
923 * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it.
925 OSL_TRACE("QuartzSalBitmap::%s(): kCGBitmapByteOrder32Host not found => inserting it.",__func__
);
927 CGImageRef xImage
= CGBitmapContextCreateImage (mxGraphicContext
);
928 CG_TRACE( "CGBitmapContextCreateImage(" << mxGraphicContext
<< ") = " << xImage
);
930 // re-create the context with single change: include kCGBitmapByteOrder32Host flag.
931 CGContextRef mxGraphicContextNew
= CGBitmapContextCreate( CGBitmapContextGetData(mxGraphicContext
),
932 CGBitmapContextGetWidth(mxGraphicContext
),
933 CGBitmapContextGetHeight(mxGraphicContext
),
934 CGBitmapContextGetBitsPerComponent(mxGraphicContext
),
935 CGBitmapContextGetBytesPerRow(mxGraphicContext
),
936 CGBitmapContextGetColorSpace(mxGraphicContext
),
937 CGBitmapContextGetBitmapInfo(mxGraphicContext
) | kCGBitmapByteOrder32Host
);
938 CG_TRACE( "CGBitmapContextCreate(" << CGBitmapContextGetWidth(mxGraphicContext
) << "x" << CGBitmapContextGetHeight(mxGraphicContext
) << "x" << CGBitmapContextGetBitsPerComponent(mxGraphicContext
) << ") = " << mxGraphicContextNew
);
940 CG_TRACE( "CFRelease(" << mxGraphicContext
<< ")" );
941 CFRelease(mxGraphicContext
);
943 // Needs to be flipped
944 CG_TRACE( "CGContextSaveGState(" << mxGraphicContextNew
<< ")" );
945 CGContextSaveGState( mxGraphicContextNew
);
946 CG_TRACE( "CGContextTranslateCTM(" << mxGraphicContextNew
<< ",0," << CGBitmapContextGetHeight(mxGraphicContextNew
) << ")" );
947 CGContextTranslateCTM (mxGraphicContextNew
, 0, CGBitmapContextGetHeight(mxGraphicContextNew
));
948 CG_TRACE( "CGContextScaleCTM(" << mxGraphicContextNew
<< ",1,-1)" );
949 CGContextScaleCTM (mxGraphicContextNew
, 1.0, -1.0);
951 CG_TRACE( "CGContextDrawImage(" << mxGraphicContextNew
<< "," << CGRectMake(0, 0, CGImageGetWidth(xImage
), CGImageGetHeight(xImage
)) << "," << xImage
<< ")" );
952 CGContextDrawImage(mxGraphicContextNew
, CGRectMake( 0, 0, CGImageGetWidth(xImage
), CGImageGetHeight(xImage
)), xImage
);
955 CG_TRACE( "CGContextRestoreGState(" << mxGraphicContextNew
<< ")" );
956 CGContextRestoreGState( mxGraphicContextNew
);
958 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
959 CGImageRelease( xImage
);
960 mxGraphicContext
= mxGraphicContextNew
;
963 rData
.rImageContext
= (void *) mxGraphicContext
;
964 rData
.mnWidth
= mnWidth
;
965 rData
.mnHeight
= mnHeight
;
971 bool QuartzSalBitmap::Crop( const Rectangle
& /*rRectPixel*/ )
976 bool QuartzSalBitmap::Erase( const ::Color
& /*rFillColor*/ )
981 bool QuartzSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag
/*nScaleFlag*/ )
986 bool QuartzSalBitmap::Replace( const Color
& /*rSearchColor*/, const Color
& /*rReplaceColor*/, sal_uLong
/*nTol*/ )
991 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */