Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / quartz / salbmp.cxx
blob117c038c844ed94d175181e2b0ac7172babc00ba
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>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
24 #include <cstddef>
25 #include <limits>
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>
42 #ifdef MACOSX
43 #include <osx/saldata.hxx>
44 #else
45 #include "saldatabasic.hxx"
46 #endif
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 )
60 , mnBits(0)
61 , mnWidth(0)
62 , mnHeight(0)
63 , mnBytesPerRow(0)
67 QuartzSalBitmap::~QuartzSalBitmap()
69 doDestroy();
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
77 if( nX < 0 ) {
78 nWidth += nX;
79 nX = 0;
82 if( nY < 0 ) {
83 nHeight += nY;
84 nY = 0;
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) )
96 nWidth = nHeight = 0;
98 // initialize properties
99 mnWidth = nWidth;
100 mnHeight = nHeight;
101 mnBits = nBitmapBits ? nBitmapBits : 32;
103 // initialize drawing context
104 CreateContext();
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
110 if( bFlipped )
112 CGContextTranslateCTM( maGraphicContext.get(), 0, +mnHeight );
114 CGContextScaleCTM( maGraphicContext.get(), +1, -1 );
117 CGContextDrawLayerAtPoint(maGraphicContext.get(), aSrcPoint, rLayerHolder.get());
119 return true;
122 bool QuartzSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
124 if( !isValidBitCount( nBits ) )
125 return false;
127 maPalette = rBitmapPalette;
128 mnBits = nBits;
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() );
161 return true;
164 return false;
167 bool QuartzSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/,
168 Size& /*rSize*/, bool /*bMask*/ )
170 return false;
173 void QuartzSalBitmap::Destroy()
175 doDestroy();
178 void QuartzSalBitmap::doDestroy()
180 DestroyContext();
181 m_pUserBuffer.reset();
184 void QuartzSalBitmap::DestroyContext()
186 if( mxCachedImage )
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()
202 DestroyContext();
204 // prepare graphics context
205 // convert image from user input if available
206 const bool bSkipConversion = !m_pUserBuffer;
207 if( bSkipConversion )
208 AllocateUserData();
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;
217 if( mnBits == 32 )
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?
231 else
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()
267 Destroy();
269 if( mnWidth && mnHeight )
271 mnBytesPerRow = 0;
273 switch( mnBits )
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;
280 default:
281 assert(false && "vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
285 bool alloc = false;
286 if (mnBytesPerRow != 0 &&
287 mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
291 m_pUserBuffer = o3tl::make_shared_array<sal_uInt8>(mnBytesPerRow * mnHeight);
292 alloc = true;
294 catch (std::bad_alloc &) {}
296 if (!alloc)
298 SAL_WARN( "vcl.quartz", "bad_alloc: " << mnWidth << "x" << mnHeight << " (" << mnBytesPerRow * mnHeight << " bytes)");
299 m_pUserBuffer.reset();
300 mnBytesPerRow = 0;
303 return m_pUserBuffer.get() != nullptr;
306 namespace {
308 class ImplPixelFormat
310 public:
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
323 sal_uInt8* pData;
324 public:
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] );
333 pData += 4;
334 return c;
336 virtual void WritePixel( Color nColor ) override
338 *pData++ = 0;
339 *pData++ = nColor.GetRed();
340 *pData++ = nColor.GetGreen();
341 *pData++ = nColor.GetBlue();
345 class ImplPixelFormat24 : public ImplPixelFormat
346 // currently BGR-format for 24bit depth
348 sal_uInt8* pData;
349 public:
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] );
358 pData += 3;
359 return c;
361 virtual void WritePixel( Color nColor ) override
363 *pData++ = nColor.GetBlue();
364 *pData++ = nColor.GetGreen();
365 *pData++ = nColor.GetRed();
369 class ImplPixelFormat8 : public ImplPixelFormat
371 private:
372 sal_uInt8* pData;
373 const BitmapPalette& mrPalette;
374 const sal_uInt16 mnPaletteCount;
376 public:
377 explicit ImplPixelFormat8( const BitmapPalette& rPalette )
378 : pData(nullptr)
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
386 pData += nPixel;
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];
395 else
396 return COL_BLACK;
398 virtual void WritePixel( Color nColor ) override
400 *pData++ = static_cast< sal_uInt8 >( mrPalette.GetBestIndex( nColor ) );
404 class ImplPixelFormat4 : public ImplPixelFormat
406 private:
407 sal_uInt8* pData;
408 const BitmapPalette& mrPalette;
409 const sal_uInt16 mnPaletteCount;
410 sal_uInt32 mnX;
411 sal_uInt32 mnShift;
413 public:
414 explicit ImplPixelFormat4( const BitmapPalette& rPalette )
415 : pData(nullptr)
416 , mrPalette(rPalette)
417 , mnPaletteCount(rPalette.GetEntryCount())
418 , mnX(0)
419 , mnShift(0)
422 virtual void SkipPixel( sal_uInt32 nPixel ) override
424 mnX += nPixel;
425 if( nPixel & 1 )
427 mnShift ^= 4;
430 virtual void StartLine( sal_uInt8* pLine ) override
432 pData = pLine;
433 mnX = 0;
434 mnShift = 4;
436 virtual Color ReadPixel() override
438 // Caution(!) rPalette.GetEntryCount() may be != (depth^^2)-1 (!)
439 const sal_uInt8 nIndex(( pData[mnX >> 1] >> mnShift) & 0x0f);
440 mnX++;
441 mnShift ^= 4;
443 if(nIndex < mnPaletteCount)
444 return mrPalette[nIndex];
445 else
446 return COL_BLACK;
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);
452 mnX++;
453 mnShift ^= 4;
457 class ImplPixelFormat1 : public ImplPixelFormat
459 private:
460 sal_uInt8* pData;
461 const BitmapPalette& mrPalette;
462 const sal_uInt16 mnPaletteCount;
463 sal_uInt32 mnX;
465 public:
466 explicit ImplPixelFormat1( const BitmapPalette& rPalette )
467 : pData(nullptr)
468 , mrPalette(rPalette)
469 , mnPaletteCount(rPalette.GetEntryCount())
470 , mnX(0)
473 virtual void SkipPixel( sal_uInt32 nPixel ) override
475 mnX += nPixel;
477 virtual void StartLine( sal_uInt8* pLine ) override
479 pData = pLine;
480 mnX = 0;
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);
486 mnX++;
488 if(nIndex < mnPaletteCount)
489 return mrPalette[nIndex];
490 else
491 return COL_BLACK;
493 virtual void WritePixel( Color nColor ) override
495 if( mrPalette.GetBestIndex( nColor ) & 1 )
497 pData[ mnX >> 3 ] |= 1 << ( 7 - ( mnX & 7 ) );
499 else
501 pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) );
503 mnX++;
507 std::unique_ptr<ImplPixelFormat> ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
509 switch( nBits )
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>();
516 default:
517 assert(false);
518 return nullptr;
521 return nullptr;
524 } // namespace
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 );
538 return;
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 );
565 if( !bConverted )
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 );
572 if( pD && pS )
574 sal_uInt32 nY = nHeight;
575 while( nY-- )
577 pD->StartLine( pDestData );
578 pS->StartLine( pSrcData );
580 sal_uInt32 nX = nWidth;
581 while( nX-- )
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
599 return mnBits;
602 static struct pal_entry
604 sal_uInt8 mnRed;
605 sal_uInt8 mnGreen;
606 sal_uInt8 mnBlue;
608 const aImplSalSysPalEntryAry[ 16 ] =
610 { 0, 0, 0 },
611 { 0, 0, 0x80 },
612 { 0, 0x80, 0 },
613 { 0, 0x80, 0x80 },
614 { 0x80, 0, 0 },
615 { 0x80, 0, 0x80 },
616 { 0x80, 0x80, 0 },
617 { 0x80, 0x80, 0x80 },
618 { 0xC0, 0xC0, 0xC0 },
619 { 0, 0, 0xFF },
620 { 0, 0xFF, 0 },
621 { 0, 0xFF, 0xFF },
622 { 0xFF, 0, 0 },
623 { 0xFF, 0, 0xFF },
624 { 0xFF, 0xFF, 0 },
625 { 0xFF, 0xFF, 0xFF }
628 static const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome )
630 if( 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;
639 if( ! bDefPalInit )
641 bDefPalInit = true;
642 aDefPalette256.SetEntryCount( 256 );
643 aDefPalette16.SetEntryCount( 16 );
644 aDefPalette2.SetEntryCount( 2 );
646 // Standard colors
647 unsigned int i;
648 for( i = 0; i < 16; i++ )
650 aDefPalette16[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;
662 int nB, nG, nR;
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 );
671 i++;
677 // now fill in appropriate palette
678 switch( mnBits )
680 case 1: return aDefPalette2;
681 case 4: return aDefPalette16;
682 case 8: return aDefPalette256;
683 default: break;
686 const static BitmapPalette aEmptyPalette;
687 return aEmptyPalette;
690 BitmapBuffer* QuartzSalBitmap::AcquireBuffer( BitmapAccessMode /*nMode*/ )
692 // TODO: AllocateUserData();
693 if (!m_pUserBuffer.get())
694 return nullptr;
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;
703 switch( mnBits )
705 case 1:
706 pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal;
707 break;
708 case 4:
709 pBuffer->mnFormat = ScanlineFormat::N4BitMsnPal;
710 break;
711 case 8:
712 pBuffer->mnFormat = ScanlineFormat::N8BitPal;
713 break;
714 case 24:
715 pBuffer->mnFormat = ScanlineFormat::N24BitTcBgr;
716 break;
717 case 32:
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);
727 break;
729 default: assert(false);
732 // some BitmapBuffer users depend on a complete palette
733 if( (mnBits <= 8) && !maPalette )
734 pBuffer->maPalette = GetDefaultPalette( mnBits, true );
736 return pBuffer;
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())
747 DestroyContext();
749 InvalidateChecksum();
752 delete pBuffer;
755 CGImageRef QuartzSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const
757 if( !mxCachedImage )
759 if (!maGraphicContext.isSet())
761 if( !const_cast<QuartzSalBitmap*>(this)->CreateContext() )
763 return nullptr;
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 );
776 else
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 ) );
795 if( !xImage )
796 return nullptr;
798 CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
799 if( !xMask )
800 return xImage;
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 );
815 CFRelease( 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 );
825 if( !xMask )
826 return xImage;
828 // combine image and alpha mask
829 CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
830 CFRelease( xMask );
831 CFRelease( xImage );
832 return xMaskedImage;
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 )
851 sal_uInt32 nColor;
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)
859 if( nY )
860 pSource += nY * mnBytesPerRow;
862 int y = nHeight;
863 while( y-- )
865 pSourcePixels->StartLine( pSource );
866 pSourcePixels->SkipPixel(nX); // Skip on x axis to nX
867 sal_uInt32 x = nWidth;
868 while( x-- )
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);
880 return xMask;
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 )
890 bool bRet = false;
892 if (!maGraphicContext.isSet())
893 CreateContext();
895 if (maGraphicContext.isSet())
897 bRet = true;
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);
930 // Flip back
931 CGContextRestoreGState( maGraphicContextNew.get() );
932 CGImageRelease( xImage );
933 maGraphicContext = maGraphicContextNew;
936 rData.mnWidth = mnWidth;
937 rData.mnHeight = mnHeight;
940 return bRet;
943 bool QuartzSalBitmap::ScalingSupported() const
945 return false;
948 bool QuartzSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ )
950 return false;
953 bool QuartzSalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uInt8 /*nTol*/ )
955 return false;
958 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */