bump product version to 5.0.4.1
[LibreOffice.git] / vcl / quartz / salbmp.cxx
blob06b55ac786a964b093832bf3c6365d87c309cb70
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 .
20 #include <sal/config.h>
22 #include <cstddef>
23 #include <limits>
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"
38 #ifdef MACOSX
39 #include "osx/saldata.hxx"
40 #else
41 #include "saldatabasic.hxx"
42 #endif
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");
62 if (!bDoIt)
63 return;
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);
74 } else {
75 SAL_DEBUG("--- saved image " << baseName << " to " << [path UTF8String]);
78 CFRelease(destination);
81 #define DBG_WRITE_IMAGE(image, name) writeImageToFile(image, name)
83 #else
85 #define DBG_WRITE_IMAGE(image, name) /* empty */
87 #endif
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 )
97 , mnBits(0)
98 , mnWidth(0)
99 , mnHeight(0)
100 , mnBytesPerRow(0)
104 QuartzSalBitmap::~QuartzSalBitmap()
106 Destroy();
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
115 if( nX < 0 )
116 nWidth += nX, nX = 0;
117 if( nY < 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
129 mnWidth = nWidth;
130 mnHeight = nHeight;
131 mnBits = nBitmapBits ? nBitmapBits : 32;
133 // initialize drawing context
134 CreateContext();
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 );
143 return true;
146 bool QuartzSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
148 if( !isValidBitCount( nBits ) )
149 return false;
150 maPalette = rBitmapPalette;
151 mnBits = nBits;
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() );
181 return true;
184 return false;
187 bool QuartzSalBitmap::Create( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
189 return false;
192 void QuartzSalBitmap::Destroy()
194 DestroyContext();
195 maUserBuffer.reset();
198 void QuartzSalBitmap::DestroyContext()
200 if( mxCachedImage )
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()
218 DestroyContext();
220 // prepare graphics context
221 // convert image from user input if available
222 const bool bSkipConversion = !maUserBuffer;
223 if( bSkipConversion )
224 AllocateUserData();
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?
247 else
249 // convert user data to 32 bit
250 nContextBytesPerRow = mnWidth << 2;
253 maContextBuffer.reset( new sal_uInt8[ mnHeight * nContextBytesPerRow ] );
254 #ifdef DBG_UTIL
255 for (size_t i = 0; i < mnHeight * nContextBytesPerRow; i++)
256 maContextBuffer.get()[i] = (i & 0xFF);
257 #endif
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()
285 Destroy();
287 if( mnWidth && mnHeight )
289 mnBytesPerRow = 0;
291 switch( mnBits )
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;
299 default:
300 OSL_FAIL("vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
304 bool alloc = false;
305 if (mnBytesPerRow != 0
306 && mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
310 maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] );
311 alloc = true;
313 catch (std::bad_alloc &) {}
315 if (!alloc)
317 SAL_WARN(
318 "vcl.quartz", "bad alloc " << mnBytesPerRow << "x" << mnHeight);
319 maUserBuffer.reset( static_cast<sal_uInt8*>(NULL) );
320 mnBytesPerRow = 0;
322 #ifdef DBG_UTIL
323 else
325 for (size_t i = 0; i < mnBytesPerRow * mnHeight; i++)
326 maUserBuffer.get()[i] = (i & 0xFF);
328 #endif
330 return maUserBuffer.get() != 0;
333 namespace {
335 class ImplPixelFormat
337 public:
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
350 sal_uInt8* pData;
351 public:
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] );
360 pData += 4;
361 return c;
363 virtual void WritePixel( ColorData nColor ) SAL_OVERRIDE
365 *pData++ = 0;
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
375 sal_uInt8* pData;
376 public:
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] );
385 pData += 3;
386 return c;
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
399 sal_uInt16* pData;
400 public:
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
408 pData += nPixel;
410 virtual ColorData ReadPixel() SAL_OVERRIDE
412 const ColorData c = RGB_COLORDATA( (*pData & 0xf800) >> 8, (*pData & 0x07e0) >> 3 , (*pData & 0x001f) << 3 );
413 pData++;
414 return c;
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
426 private:
427 sal_uInt8* pData;
428 const BitmapPalette& mrPalette;
430 public:
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
438 pData += nPixel;
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
453 private:
454 sal_uInt8* pData;
455 const BitmapPalette& mrPalette;
456 sal_uInt32 mnX;
457 sal_uInt32 mnShift;
459 public:
460 ImplPixelFormat4( const BitmapPalette& rPalette )
461 : mrPalette( rPalette )
464 virtual void SkipPixel( sal_uInt32 nPixel ) SAL_OVERRIDE
466 mnX += nPixel;
467 if( (nPixel & 1) )
468 mnShift ^= 4;
470 virtual void StartLine( sal_uInt8* pLine ) SAL_OVERRIDE
472 pData = pLine;
473 mnX = 0;
474 mnShift = 4;
476 virtual ColorData ReadPixel() SAL_OVERRIDE
478 const BitmapColor& rColor = mrPalette[( pData[mnX >> 1] >> mnShift) & 0x0f];
479 mnX++;
480 mnShift ^= 4;
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);
488 mnX++;
489 mnShift ^= 4;
493 class ImplPixelFormat1 : public ImplPixelFormat
495 private:
496 sal_uInt8* pData;
497 const BitmapPalette& mrPalette;
498 sal_uInt32 mnX;
500 public:
501 ImplPixelFormat1( const BitmapPalette& rPalette )
502 : mrPalette( rPalette )
505 virtual void SkipPixel( sal_uInt32 nPixel ) SAL_OVERRIDE
507 mnX += nPixel;
509 virtual void StartLine( sal_uInt8* pLine ) SAL_OVERRIDE
511 pData = pLine;
512 mnX = 0;
514 virtual ColorData ReadPixel() SAL_OVERRIDE
516 const BitmapColor& rColor = mrPalette[ (pData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
517 mnX++;
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 ) );
525 else
526 pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) );
527 mnX++;
531 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
533 switch( nBits )
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;
543 return 0;
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 );
557 return;
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 );
584 if( !bConverted )
586 // TODO: this implementation is for clarety, not for speed
588 ImplPixelFormat* pD = ImplPixelFormat::GetFormat( nDestBits, rDestPalette );
589 ImplPixelFormat* pS = ImplPixelFormat::GetFormat( nSrcBits, rSrcPalette );
591 if( pD && pS )
593 sal_uInt32 nY = nHeight;
594 while( nY-- )
596 pD->StartLine( pDestData );
597 pS->StartLine( pSrcData );
599 sal_uInt32 nX = nWidth;
600 while( nX-- )
601 pD->WritePixel( pS->ReadPixel() );
603 pSrcData += nSrcBytesPerRow;
604 pDestData += nDestBytesPerRow;
607 delete pS;
608 delete pD;
612 Size QuartzSalBitmap::GetSize() const
614 return Size( mnWidth, mnHeight );
617 sal_uInt16 QuartzSalBitmap::GetBitCount() const
619 return mnBits;
622 static struct pal_entry
624 sal_uInt8 mnRed;
625 sal_uInt8 mnGreen;
626 sal_uInt8 mnBlue;
628 const aImplSalSysPalEntryAry[ 16 ] =
630 { 0, 0, 0 },
631 { 0, 0, 0x80 },
632 { 0, 0x80, 0 },
633 { 0, 0x80, 0x80 },
634 { 0x80, 0, 0 },
635 { 0x80, 0, 0x80 },
636 { 0x80, 0x80, 0 },
637 { 0x80, 0x80, 0x80 },
638 { 0xC0, 0xC0, 0xC0 },
639 { 0, 0, 0xFF },
640 { 0, 0xFF, 0 },
641 { 0, 0xFF, 0xFF },
642 { 0xFF, 0, 0 },
643 { 0xFF, 0, 0xFF },
644 { 0xFF, 0xFF, 0 },
645 { 0xFF, 0xFF, 0xFF }
648 const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome )
650 if( 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;
659 if( ! bDefPalInit )
661 bDefPalInit = true;
662 aDefPalette256.SetEntryCount( 256 );
663 aDefPalette16.SetEntryCount( 16 );
664 aDefPalette2.SetEntryCount( 2 );
666 // Standard colors
667 unsigned int i;
668 for( i = 0; i < 16; i++ )
670 aDefPalette16[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;
682 int nB, nG, nR;
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 );
691 i++;
697 // now fill in appropriate palette
698 switch( mnBits )
700 case 1: return aDefPalette2;
701 case 4: return aDefPalette16;
702 case 8: return aDefPalette256;
703 default: break;
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();
717 return NULL;
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;
727 switch( 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 );
734 break;
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 );
738 break;
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 );
746 return pBuffer;
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 )
756 DestroyContext();
759 delete pBuffer;
762 CGImageRef QuartzSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const
764 if( !mxCachedImage )
766 if( !mxGraphicContext )
767 if( !const_cast<QuartzSalBitmap*>(this)->CreateContext() )
768 return NULL;
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 );
782 else
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 ) );
802 if( !xImage )
803 return NULL;
805 CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
806 if( !xMask )
807 return xImage;
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 << ")" );
826 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 );
837 if( !xMask )
838 return xImage;
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 << ")" );
846 CFRelease( xMask );
847 CG_TRACE( "CFRelease(" << xImage << ")" );
848 CFRelease( xImage );
849 return xMaskedImage;
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 )
866 sal_uInt32 nColor;
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();
873 if( nY )
874 pSource += nY * mnBytesPerRow;
876 int y = nHeight;
877 while( y-- )
879 pSourcePixels->StartLine( pSource );
880 pSourcePixels->SkipPixel(nX);
881 sal_uInt32 x = nWidth;
882 while( x-- )
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);
894 else
896 free(pMaskBuffer);
899 delete pSourcePixels;
901 return xMask;
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 )
911 bool bRet = false;
913 if( !mxGraphicContext )
914 CreateContext();
916 if ( mxGraphicContext )
918 bRet = true;
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);
954 // Flip back
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;
968 return bRet;
971 bool QuartzSalBitmap::Crop( const Rectangle& /*rRectPixel*/ )
973 return false;
976 bool QuartzSalBitmap::Erase( const ::Color& /*rFillColor*/ )
978 return false;
981 bool QuartzSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ )
983 return false;
986 bool QuartzSalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uLong /*nTol*/ )
988 return false;
991 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */