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>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
27 #include <o3tl/make_shared.hxx>
28 #include <basegfx/vector/b2ivector.hxx>
29 #include <tools/color.hxx>
30 #include <vcl/bitmap.hxx>
31 #include <vcl/BitmapAccessMode.hxx>
32 #include <vcl/BitmapBuffer.hxx>
33 #include <vcl/BitmapColor.hxx>
34 #include <vcl/BitmapPalette.hxx>
35 #include <vcl/ColorMask.hxx>
36 #include <vcl/Scanline.hxx>
38 #include <bmpfast.hxx>
39 #include <quartz/salbmp.h>
40 #include <quartz/utils.h>
43 #include <osx/saldata.hxx>
45 #include "saldatabasic.hxx"
48 static const unsigned long k32BitRedColorMask
= 0x00ff0000;
49 static const unsigned long k32BitGreenColorMask
= 0x0000ff00;
50 static const unsigned long k32BitBlueColorMask
= 0x000000ff;
52 static bool isValidBitCount( sal_uInt16 nBitCount
)
54 return (nBitCount
== 1) || (nBitCount
== 4) || (nBitCount
== 8) ||
55 (nBitCount
== 24) || (nBitCount
== 32);
58 QuartzSalBitmap::QuartzSalBitmap()
59 : mxCachedImage( nullptr )
67 QuartzSalBitmap::~QuartzSalBitmap()
72 bool QuartzSalBitmap::Create(CGLayerHolder
const & rLayerHolder
, int nBitmapBits
, int nX
, int nY
, int nWidth
, int nHeight
, bool bFlipped
)
74 SAL_WARN_IF(!rLayerHolder
.isSet(), "vcl", "QuartzSalBitmap::Create() from non-layered context");
76 // sanitize input parameters
87 const CGSize aLayerSize
= CGLayerGetSize(rLayerHolder
.get());
89 if( nWidth
>= static_cast<int>(aLayerSize
.width
) - nX
)
90 nWidth
= static_cast<int>(aLayerSize
.width
) - nX
;
92 if( nHeight
>= static_cast<int>(aLayerSize
.height
) - nY
)
93 nHeight
= static_cast<int>(aLayerSize
.height
) - nY
;
95 if( (nWidth
< 0) || (nHeight
< 0) )
98 // initialize properties
101 mnBits
= nBitmapBits
? nBitmapBits
: 32;
103 // initialize drawing context
106 // copy layer content into the bitmap buffer
107 const CGPoint aSrcPoint
= { static_cast<CGFloat
>(-nX
), static_cast<CGFloat
>(-nY
) };
108 if (maGraphicContext
.isSet()) // remove warning
112 CGContextTranslateCTM( maGraphicContext
.get(), 0, +mnHeight
);
114 CGContextScaleCTM( maGraphicContext
.get(), +1, -1 );
117 CGContextDrawLayerAtPoint(maGraphicContext
.get(), aSrcPoint
, rLayerHolder
.get());
122 bool QuartzSalBitmap::Create( const Size
& rSize
, sal_uInt16 nBits
, const BitmapPalette
& rBitmapPalette
)
124 if( !isValidBitCount( nBits
) )
127 maPalette
= rBitmapPalette
;
129 mnWidth
= rSize
.Width();
130 mnHeight
= rSize
.Height();
131 return AllocateUserData();
134 bool QuartzSalBitmap::Create( const SalBitmap
& rSalBmp
)
136 return Create( rSalBmp
, rSalBmp
.GetBitCount() );
139 bool QuartzSalBitmap::Create( const SalBitmap
& rSalBmp
, SalGraphics
* pGraphics
)
141 return Create( rSalBmp
, pGraphics
? pGraphics
->GetBitCount() : rSalBmp
.GetBitCount() );
144 bool QuartzSalBitmap::Create( const SalBitmap
& rSalBmp
, sal_uInt16 nNewBitCount
)
146 const QuartzSalBitmap
& rSourceBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBmp
);
148 if (isValidBitCount(nNewBitCount
) && rSourceBitmap
.m_pUserBuffer
.get())
150 mnBits
= nNewBitCount
;
151 mnWidth
= rSourceBitmap
.mnWidth
;
152 mnHeight
= rSourceBitmap
.mnHeight
;
153 maPalette
= rSourceBitmap
.maPalette
;
155 if( AllocateUserData() )
157 ConvertBitmapData( mnWidth
, mnHeight
, mnBits
, mnBytesPerRow
, maPalette
,
158 m_pUserBuffer
.get(), rSourceBitmap
.mnBits
,
159 rSourceBitmap
.mnBytesPerRow
, rSourceBitmap
.maPalette
,
160 rSourceBitmap
.m_pUserBuffer
.get() );
167 bool QuartzSalBitmap::Create( const css::uno::Reference
< css::rendering::XBitmapCanvas
>& /*xBitmapCanvas*/,
168 Size
& /*rSize*/, bool /*bMask*/ )
173 void QuartzSalBitmap::Destroy()
178 void QuartzSalBitmap::doDestroy()
181 m_pUserBuffer
.reset();
184 void QuartzSalBitmap::DestroyContext()
188 CGImageRelease( mxCachedImage
);
189 mxCachedImage
= nullptr;
192 if (maGraphicContext
.isSet())
194 CGContextRelease(maGraphicContext
.get());
195 maGraphicContext
.set(nullptr);
196 m_pContextBuffer
.reset();
200 bool QuartzSalBitmap::CreateContext()
204 // prepare graphics context
205 // convert image from user input if available
206 const bool bSkipConversion
= !m_pUserBuffer
;
207 if( bSkipConversion
)
210 // default to RGBA color space
211 CGColorSpaceRef aCGColorSpace
= GetSalData()->mxRGBSpace
;
212 CGBitmapInfo aCGBmpInfo
= kCGImageAlphaNoneSkipFirst
;
214 // convert data into something accepted by CGBitmapContextCreate()
215 size_t bitsPerComponent
= 8;
216 sal_uInt32 nContextBytesPerRow
= mnBytesPerRow
;
219 // no conversion needed for truecolor
220 m_pContextBuffer
= m_pUserBuffer
;
222 else if( mnBits
== 8 && maPalette
.IsGreyPalette() )
224 // no conversion needed for grayscale
225 m_pContextBuffer
= m_pUserBuffer
;
226 aCGColorSpace
= GetSalData()->mxGraySpace
;
227 aCGBmpInfo
= kCGImageAlphaNone
;
228 bitsPerComponent
= mnBits
;
230 // TODO: is special handling for 1bit input buffers worth it?
233 // convert user data to 32 bit
234 nContextBytesPerRow
= mnWidth
<< 2;
237 m_pContextBuffer
= o3tl::make_shared_array
<sal_uInt8
>(mnHeight
* nContextBytesPerRow
);
239 if( !bSkipConversion
)
241 ConvertBitmapData( mnWidth
, mnHeight
,
242 32, nContextBytesPerRow
, maPalette
, m_pContextBuffer
.get(),
243 mnBits
, mnBytesPerRow
, maPalette
, m_pUserBuffer
.get() );
246 catch( const std::bad_alloc
& )
248 maGraphicContext
.set(nullptr);
252 if (m_pContextBuffer
.get())
254 maGraphicContext
.set(CGBitmapContextCreate(m_pContextBuffer
.get(), mnWidth
, mnHeight
,
255 bitsPerComponent
, nContextBytesPerRow
,
256 aCGColorSpace
, aCGBmpInfo
));
259 if (!maGraphicContext
.isSet())
260 m_pContextBuffer
.reset();
262 return maGraphicContext
.isSet();
265 bool QuartzSalBitmap::AllocateUserData()
269 if( mnWidth
&& mnHeight
)
275 case 1: mnBytesPerRow
= (mnWidth
+ 7) >> 3; break;
276 case 4: mnBytesPerRow
= (mnWidth
+ 1) >> 1; break;
277 case 8: mnBytesPerRow
= mnWidth
; break;
278 case 24: mnBytesPerRow
= (mnWidth
<< 1) + mnWidth
; break;
279 case 32: mnBytesPerRow
= mnWidth
<< 2; break;
281 assert(false && "vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
286 if (mnBytesPerRow
!= 0 &&
287 mnBytesPerRow
<= std::numeric_limits
<sal_uInt32
>::max() / mnHeight
)
291 m_pUserBuffer
= o3tl::make_shared_array
<sal_uInt8
>(mnBytesPerRow
* mnHeight
);
294 catch (std::bad_alloc
&) {}
298 SAL_WARN( "vcl.quartz", "bad_alloc: " << mnWidth
<< "x" << mnHeight
<< " (" << mnBytesPerRow
* mnHeight
<< " bytes)");
299 m_pUserBuffer
.reset();
303 return m_pUserBuffer
.get() != nullptr;
308 class ImplPixelFormat
311 static std::unique_ptr
<ImplPixelFormat
> GetFormat( sal_uInt16 nBits
, const BitmapPalette
& rPalette
);
313 virtual void StartLine( sal_uInt8
* pLine
) = 0;
314 virtual void SkipPixel( sal_uInt32 nPixel
) = 0;
315 virtual Color
ReadPixel() = 0;
316 virtual void WritePixel( Color nColor
) = 0;
317 virtual ~ImplPixelFormat() { }
320 class ImplPixelFormat32
: public ImplPixelFormat
321 // currently ARGB-format for 32bit depth
325 virtual void StartLine( sal_uInt8
* pLine
) override
{ pData
= pLine
; }
326 virtual void SkipPixel( sal_uInt32 nPixel
) override
328 pData
+= nPixel
<< 2;
330 virtual Color
ReadPixel() override
332 const Color
c( pData
[1], pData
[2], pData
[3] );
336 virtual void WritePixel( Color nColor
) override
339 *pData
++ = nColor
.GetRed();
340 *pData
++ = nColor
.GetGreen();
341 *pData
++ = nColor
.GetBlue();
345 class ImplPixelFormat24
: public ImplPixelFormat
346 // currently BGR-format for 24bit depth
350 virtual void StartLine( sal_uInt8
* pLine
) override
{ pData
= pLine
; }
351 virtual void SkipPixel( sal_uInt32 nPixel
) override
353 pData
+= (nPixel
<< 1) + nPixel
;
355 virtual Color
ReadPixel() override
357 const Color
c( pData
[2], pData
[1], pData
[0] );
361 virtual void WritePixel( Color nColor
) override
363 *pData
++ = nColor
.GetBlue();
364 *pData
++ = nColor
.GetGreen();
365 *pData
++ = nColor
.GetRed();
369 class ImplPixelFormat8
: public ImplPixelFormat
373 const BitmapPalette
& mrPalette
;
374 const sal_uInt16 mnPaletteCount
;
377 explicit ImplPixelFormat8( const BitmapPalette
& rPalette
)
379 , mrPalette(rPalette
)
380 , mnPaletteCount(rPalette
.GetEntryCount())
383 virtual void StartLine( sal_uInt8
* pLine
) override
{ pData
= pLine
; }
384 virtual void SkipPixel( sal_uInt32 nPixel
) override
388 virtual Color
ReadPixel() override
390 const sal_uInt8
nIndex(*pData
++);
392 // Caution(!) rPalette.GetEntryCount() may be != (depth^^2)-1 (!)
393 if(nIndex
< mnPaletteCount
)
394 return mrPalette
[nIndex
];
398 virtual void WritePixel( Color nColor
) override
400 *pData
++ = static_cast< sal_uInt8
>( mrPalette
.GetBestIndex( nColor
) );
404 class ImplPixelFormat4
: public ImplPixelFormat
408 const BitmapPalette
& mrPalette
;
409 const sal_uInt16 mnPaletteCount
;
414 explicit ImplPixelFormat4( const BitmapPalette
& rPalette
)
416 , mrPalette(rPalette
)
417 , mnPaletteCount(rPalette
.GetEntryCount())
422 virtual void SkipPixel( sal_uInt32 nPixel
) override
430 virtual void StartLine( sal_uInt8
* pLine
) override
436 virtual Color
ReadPixel() override
438 // Caution(!) rPalette.GetEntryCount() may be != (depth^^2)-1 (!)
439 const sal_uInt8
nIndex(( pData
[mnX
>> 1] >> mnShift
) & 0x0f);
443 if(nIndex
< mnPaletteCount
)
444 return mrPalette
[nIndex
];
448 virtual void WritePixel( Color nColor
) override
450 pData
[mnX
>>1] &= (0xf0 >> mnShift
);
451 pData
[mnX
>>1] |= (static_cast< sal_uInt8
>( mrPalette
.GetBestIndex( nColor
) ) & 0x0f);
457 class ImplPixelFormat1
: public ImplPixelFormat
461 const BitmapPalette
& mrPalette
;
462 const sal_uInt16 mnPaletteCount
;
466 explicit ImplPixelFormat1( const BitmapPalette
& rPalette
)
468 , mrPalette(rPalette
)
469 , mnPaletteCount(rPalette
.GetEntryCount())
473 virtual void SkipPixel( sal_uInt32 nPixel
) override
477 virtual void StartLine( sal_uInt8
* pLine
) override
482 virtual Color
ReadPixel() override
484 // Caution(!) rPalette.GetEntryCount() may be != (depth^^2)-1 (!)
485 const sal_uInt8
nIndex( (pData
[mnX
>> 3 ] >> ( 7 - ( mnX
& 7 ) )) & 1);
488 if(nIndex
< mnPaletteCount
)
489 return mrPalette
[nIndex
];
493 virtual void WritePixel( Color nColor
) override
495 if( mrPalette
.GetBestIndex( nColor
) & 1 )
497 pData
[ mnX
>> 3 ] |= 1 << ( 7 - ( mnX
& 7 ) );
501 pData
[ mnX
>> 3 ] &= ~( 1 << ( 7 - ( mnX
& 7 ) ) );
507 std::unique_ptr
<ImplPixelFormat
> ImplPixelFormat::GetFormat( sal_uInt16 nBits
, const BitmapPalette
& rPalette
)
511 case 1: return std::make_unique
<ImplPixelFormat1
>( rPalette
);
512 case 4: return std::make_unique
<ImplPixelFormat4
>( rPalette
);
513 case 8: return std::make_unique
<ImplPixelFormat8
>( rPalette
);
514 case 24: return std::make_unique
<ImplPixelFormat24
>();
515 case 32: return std::make_unique
<ImplPixelFormat32
>();
526 void QuartzSalBitmap::ConvertBitmapData( sal_uInt32 nWidth
, sal_uInt32 nHeight
,
527 sal_uInt16 nDestBits
, sal_uInt32 nDestBytesPerRow
,
528 const BitmapPalette
& rDestPalette
, sal_uInt8
* pDestData
,
529 sal_uInt16 nSrcBits
, sal_uInt32 nSrcBytesPerRow
,
530 const BitmapPalette
& rSrcPalette
, sal_uInt8
* pSrcData
)
533 if( (nDestBytesPerRow
== nSrcBytesPerRow
) &&
534 (nDestBits
== nSrcBits
) && ((nSrcBits
!= 8) || (rDestPalette
.operator==( rSrcPalette
))) )
536 // simple case, same format, so just copy
537 memcpy( pDestData
, pSrcData
, nHeight
* nDestBytesPerRow
);
541 // try accelerated conversion if possible
542 // TODO: are other truecolor conversions except BGR->ARGB worth it?
543 bool bConverted
= false;
544 if( (nSrcBits
== 24) && (nDestBits
== 32) )
546 // TODO: extend bmpfast.cxx with a method that can be directly used here
547 BitmapBuffer aSrcBuf
;
548 aSrcBuf
.mnFormat
= ScanlineFormat::N24BitTcBgr
;
549 aSrcBuf
.mpBits
= pSrcData
;
550 aSrcBuf
.mnBitCount
= nSrcBits
;
551 aSrcBuf
.mnScanlineSize
= nSrcBytesPerRow
;
552 BitmapBuffer aDstBuf
;
553 aDstBuf
.mnFormat
= ScanlineFormat::N32BitTcArgb
;
554 aDstBuf
.mpBits
= pDestData
;
555 aDstBuf
.mnBitCount
= nDestBits
;
556 aDstBuf
.mnScanlineSize
= nDestBytesPerRow
;
558 aSrcBuf
.mnWidth
= aDstBuf
.mnWidth
= nWidth
;
559 aSrcBuf
.mnHeight
= aDstBuf
.mnHeight
= nHeight
;
561 SalTwoRect
aTwoRects(0, 0, mnWidth
, mnHeight
, 0, 0, mnWidth
, mnHeight
);
562 bConverted
= ::ImplFastBitmapConversion( aDstBuf
, aSrcBuf
, aTwoRects
);
567 // TODO: this implementation is for clarity, not for speed
569 std::unique_ptr
<ImplPixelFormat
> pD
= ImplPixelFormat::GetFormat( nDestBits
, rDestPalette
);
570 std::unique_ptr
<ImplPixelFormat
> pS
= ImplPixelFormat::GetFormat( nSrcBits
, rSrcPalette
);
574 sal_uInt32 nY
= nHeight
;
577 pD
->StartLine( pDestData
);
578 pS
->StartLine( pSrcData
);
580 sal_uInt32 nX
= nWidth
;
583 pD
->WritePixel( pS
->ReadPixel() );
585 pSrcData
+= nSrcBytesPerRow
;
586 pDestData
+= nDestBytesPerRow
;
592 Size
QuartzSalBitmap::GetSize() const
594 return Size( mnWidth
, mnHeight
);
597 sal_uInt16
QuartzSalBitmap::GetBitCount() const
602 static struct pal_entry
608 const aImplSalSysPalEntryAry
[ 16 ] =
617 { 0x80, 0x80, 0x80 },
618 { 0xC0, 0xC0, 0xC0 },
628 static const BitmapPalette
& GetDefaultPalette( int mnBits
, bool bMonochrome
)
631 return Bitmap::GetGreyPalette( 1U << mnBits
);
633 // at this point we should provide some kind of default palette
634 // since all other platforms do so, too.
635 static bool bDefPalInit
= false;
636 static BitmapPalette aDefPalette256
;
637 static BitmapPalette aDefPalette16
;
638 static BitmapPalette aDefPalette2
;
642 aDefPalette256
.SetEntryCount( 256 );
643 aDefPalette16
.SetEntryCount( 16 );
644 aDefPalette2
.SetEntryCount( 2 );
648 for( i
= 0; i
< 16; i
++ )
651 aDefPalette256
[i
] = BitmapColor( aImplSalSysPalEntryAry
[i
].mnRed
,
652 aImplSalSysPalEntryAry
[i
].mnGreen
,
653 aImplSalSysPalEntryAry
[i
].mnBlue
);
656 aDefPalette2
[0] = BitmapColor( 0, 0, 0 );
657 aDefPalette2
[1] = BitmapColor( 0xff, 0xff, 0xff );
659 // own palette (6/6/6)
660 const int DITHER_PAL_STEPS
= 6;
661 const sal_uInt8 DITHER_PAL_DELTA
= 51;
663 sal_uInt8 nRed
, nGreen
, nBlue
;
664 for( nB
=0, nBlue
=0; nB
< DITHER_PAL_STEPS
; nB
++, nBlue
+= DITHER_PAL_DELTA
)
666 for( nG
=0, nGreen
=0; nG
< DITHER_PAL_STEPS
; nG
++, nGreen
+= DITHER_PAL_DELTA
)
668 for( nR
=0, nRed
=0; nR
< DITHER_PAL_STEPS
; nR
++, nRed
+= DITHER_PAL_DELTA
)
670 aDefPalette256
[ i
] = BitmapColor( nRed
, nGreen
, nBlue
);
677 // now fill in appropriate palette
680 case 1: return aDefPalette2
;
681 case 4: return aDefPalette16
;
682 case 8: return aDefPalette256
;
686 const static BitmapPalette aEmptyPalette
;
687 return aEmptyPalette
;
690 BitmapBuffer
* QuartzSalBitmap::AcquireBuffer( BitmapAccessMode
/*nMode*/ )
692 // TODO: AllocateUserData();
693 if (!m_pUserBuffer
.get())
696 BitmapBuffer
* pBuffer
= new BitmapBuffer
;
697 pBuffer
->mnWidth
= mnWidth
;
698 pBuffer
->mnHeight
= mnHeight
;
699 pBuffer
->maPalette
= maPalette
;
700 pBuffer
->mnScanlineSize
= mnBytesPerRow
;
701 pBuffer
->mpBits
= m_pUserBuffer
.get();
702 pBuffer
->mnBitCount
= mnBits
;
706 pBuffer
->mnFormat
= ScanlineFormat::N1BitMsbPal
;
709 pBuffer
->mnFormat
= ScanlineFormat::N4BitMsnPal
;
712 pBuffer
->mnFormat
= ScanlineFormat::N8BitPal
;
715 pBuffer
->mnFormat
= ScanlineFormat::N24BitTcBgr
;
719 pBuffer
->mnFormat
= ScanlineFormat::N32BitTcArgb
;
720 ColorMaskElement
aRedMask(k32BitRedColorMask
);
721 aRedMask
.CalcMaskShift();
722 ColorMaskElement
aGreenMask(k32BitGreenColorMask
);
723 aGreenMask
.CalcMaskShift();
724 ColorMaskElement
aBlueMask(k32BitBlueColorMask
);
725 aBlueMask
.CalcMaskShift();
726 pBuffer
->maColorMask
= ColorMask(aRedMask
, aGreenMask
, aBlueMask
);
729 default: assert(false);
732 // some BitmapBuffer users depend on a complete palette
733 if( (mnBits
<= 8) && !maPalette
)
734 pBuffer
->maPalette
= GetDefaultPalette( mnBits
, true );
739 void QuartzSalBitmap::ReleaseBuffer( BitmapBuffer
* pBuffer
, BitmapAccessMode nMode
)
741 // invalidate graphic context if we have different data
742 if( nMode
== BitmapAccessMode::Write
)
744 maPalette
= pBuffer
->maPalette
;
745 if (maGraphicContext
.isSet())
749 InvalidateChecksum();
755 CGImageRef
QuartzSalBitmap::CreateCroppedImage( int nX
, int nY
, int nNewWidth
, int nNewHeight
) const
759 if (!maGraphicContext
.isSet())
761 if( !const_cast<QuartzSalBitmap
*>(this)->CreateContext() )
766 mxCachedImage
= CGBitmapContextCreateImage(maGraphicContext
.get());
769 CGImageRef xCroppedImage
= nullptr;
770 // short circuit if there is nothing to crop
771 if( !nX
&& !nY
&& (mnWidth
== nNewWidth
) && (mnHeight
== nNewHeight
) )
773 xCroppedImage
= mxCachedImage
;
774 CFRetain( xCroppedImage
);
778 nY
= mnHeight
- (nY
+ nNewHeight
); // adjust for y-mirrored context
779 const CGRect aCropRect
= { { static_cast<CGFloat
>(nX
), static_cast<CGFloat
>(nY
) }, { static_cast<CGFloat
>(nNewWidth
), static_cast<CGFloat
>(nNewHeight
) } };
780 xCroppedImage
= CGImageCreateWithImageInRect( mxCachedImage
, aCropRect
);
783 return xCroppedImage
;
786 static void CFRTLFree(void* /*info*/, const void* data
, size_t /*size*/)
788 std::free( const_cast<void*>(data
) );
791 CGImageRef
QuartzSalBitmap::CreateWithMask( const QuartzSalBitmap
& rMask
,
792 int nX
, int nY
, int nWidth
, int nHeight
) const
794 CGImageRef
xImage( CreateCroppedImage( nX
, nY
, nWidth
, nHeight
) );
798 CGImageRef xMask
= rMask
.CreateCroppedImage( nX
, nY
, nWidth
, nHeight
);
802 // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
803 // TODO: isolate in an extra method?
804 if( !CGImageIsMask(xMask
) || rMask
.GetBitCount() != 8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
806 const CGRect xImageRect
=CGRectMake( 0, 0, nWidth
, nHeight
);//the rect has no offset
808 // create the alpha mask image fitting our image
809 // TODO: is caching the full mask or the subimage mask worth it?
810 int nMaskBytesPerRow
= ((nWidth
+ 3) & ~3);
811 void* pMaskMem
= std::malloc( nMaskBytesPerRow
* nHeight
);
812 CGContextRef xMaskContext
= CGBitmapContextCreate( pMaskMem
,
813 nWidth
, nHeight
, 8, nMaskBytesPerRow
, GetSalData()->mxGraySpace
, kCGImageAlphaNone
);
814 CGContextDrawImage( xMaskContext
, xImageRect
, xMask
);
816 CGDataProviderRef
xDataProvider( CGDataProviderCreateWithData( nullptr,
817 pMaskMem
, nHeight
* nMaskBytesPerRow
, &CFRTLFree
) );
819 static const CGFloat
* pDecode
= nullptr;
820 xMask
= CGImageMaskCreate( nWidth
, nHeight
, 8, 8, nMaskBytesPerRow
, xDataProvider
, pDecode
, false );
821 CFRelease( xDataProvider
);
822 CFRelease( xMaskContext
);
828 // combine image and alpha mask
829 CGImageRef xMaskedImage
= CGImageCreateWithMask( xImage
, xMask
);
835 /** creates an image from the given rectangle, replacing all black pixels
836 with nMaskColor and make all other full transparent */
837 CGImageRef
QuartzSalBitmap::CreateColorMask( int nX
, int nY
, int nWidth
,
838 int nHeight
, Color nMaskColor
) const
840 CGImageRef xMask
= nullptr;
841 if (m_pUserBuffer
.get() && (nX
+ nWidth
<= mnWidth
) && (nY
+ nHeight
<= mnHeight
))
843 const sal_uInt32 nDestBytesPerRow
= nWidth
<< 2;
844 std::unique_ptr
<sal_uInt32
[]> pMaskBuffer(new (std::nothrow
) sal_uInt32
[ nHeight
* nDestBytesPerRow
/ 4] );
845 sal_uInt32
* pDest
= pMaskBuffer
.get();
847 std::unique_ptr
<ImplPixelFormat
> pSourcePixels
= ImplPixelFormat::GetFormat( mnBits
, maPalette
);
849 if( pMaskBuffer
&& pSourcePixels
)
852 reinterpret_cast<sal_uInt8
*>(&nColor
)[0] = 0xff;
853 reinterpret_cast<sal_uInt8
*>(&nColor
)[1] = nMaskColor
.GetRed();
854 reinterpret_cast<sal_uInt8
*>(&nColor
)[2] = nMaskColor
.GetGreen();
855 reinterpret_cast<sal_uInt8
*>(&nColor
)[3] = nMaskColor
.GetBlue();
857 sal_uInt8
* pSource
= m_pUserBuffer
.get();
858 // First to nY on y-axis, as that is our starting point (sub-image)
860 pSource
+= nY
* mnBytesPerRow
;
865 pSourcePixels
->StartLine( pSource
);
866 pSourcePixels
->SkipPixel(nX
); // Skip on x axis to nX
867 sal_uInt32 x
= nWidth
;
870 *pDest
++ = ( pSourcePixels
->ReadPixel() == 0 ) ? nColor
: 0;
872 pSource
+= mnBytesPerRow
;
875 CGDataProviderRef
xDataProvider( CGDataProviderCreateWithData(nullptr, pMaskBuffer
.release(), nHeight
* nDestBytesPerRow
, &CFRTLFree
) );
876 xMask
= CGImageCreate(nWidth
, nHeight
, 8, 32, nDestBytesPerRow
, GetSalData()->mxRGBSpace
, kCGImageAlphaPremultipliedFirst
, xDataProvider
, nullptr, true, kCGRenderingIntentDefault
);
877 CFRelease(xDataProvider
);
883 /** QuartzSalBitmap::GetSystemData Get platform native image data from existing image
885 * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx
886 * @return true if successful
888 bool QuartzSalBitmap::GetSystemData( BitmapSystemData
& rData
)
892 if (!maGraphicContext
.isSet())
895 if (maGraphicContext
.isSet())
899 if ((CGBitmapContextGetBitsPerPixel(maGraphicContext
.get()) == 32) &&
900 (CGBitmapContextGetBitmapInfo(maGraphicContext
.get()) & kCGBitmapByteOrderMask
) != kCGBitmapByteOrder32Host
)
903 * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it.
905 * Not sure what the above comment means. We don't use Cairo on macOS or iOS.
907 * This whole if statement was originally (before 2011) inside #ifdef CAIRO. Did we use Cairo on Mac back then?
908 * Anyway, nowadays (since many years, I think) we don't, so should this if statement be dropped? Fun.
911 CGImageRef xImage
= CGBitmapContextCreateImage(maGraphicContext
.get());
913 // re-create the context with single change: include kCGBitmapByteOrder32Host flag.
914 CGContextHolder
maGraphicContextNew(CGBitmapContextCreate(CGBitmapContextGetData(maGraphicContext
.get()),
915 CGBitmapContextGetWidth(maGraphicContext
.get()),
916 CGBitmapContextGetHeight(maGraphicContext
.get()),
917 CGBitmapContextGetBitsPerComponent(maGraphicContext
.get()),
918 CGBitmapContextGetBytesPerRow(maGraphicContext
.get()),
919 CGBitmapContextGetColorSpace(maGraphicContext
.get()),
920 CGBitmapContextGetBitmapInfo(maGraphicContext
.get()) | kCGBitmapByteOrder32Host
));
921 CFRelease(maGraphicContext
.get());
923 // Needs to be flipped
924 maGraphicContextNew
.saveState();
925 CGContextTranslateCTM (maGraphicContextNew
.get(), 0, CGBitmapContextGetHeight(maGraphicContextNew
.get()));
926 CGContextScaleCTM (maGraphicContextNew
.get(), 1.0, -1.0);
928 CGContextDrawImage(maGraphicContextNew
.get(), CGRectMake( 0, 0, CGImageGetWidth(xImage
), CGImageGetHeight(xImage
)), xImage
);
931 CGContextRestoreGState( maGraphicContextNew
.get() );
932 CGImageRelease( xImage
);
933 maGraphicContext
= maGraphicContextNew
;
936 rData
.mnWidth
= mnWidth
;
937 rData
.mnHeight
= mnHeight
;
943 bool QuartzSalBitmap::ScalingSupported() const
948 bool QuartzSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag
/*nScaleFlag*/ )
953 bool QuartzSalBitmap::Replace( const Color
& /*rSearchColor*/, const Color
& /*rReplaceColor*/, sal_uInt8
/*nTol*/ )
958 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */