1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
23
24 #include <cstddef>
25 #include <limits>
26
27 #include <o3tl/make_shared.hxx>
28 #include <tools/color.hxx>
29 #include <vcl/bitmap.hxx>
30 #include <vcl/BitmapAccessMode.hxx>
31 #include <vcl/BitmapBuffer.hxx>
32 #include <vcl/BitmapColor.hxx>
33 #include <vcl/BitmapPalette.hxx>
34 #include <vcl/Scanline.hxx>
35
36 #include <bitmap/bmpfast.hxx>
37 #include <quartz/cgutils.h>
38 #include <quartz/salbmp.h>
39 #include <quartz/utils.h>
40 #include <bitmap/ScanlineTools.hxx>
41
42 #ifdef MACOSX
43 #include <osx/saldata.hxx>
44 #else
45 #include <ios/iosinst.hxx>
46 #endif
47
QuartzSalBitmap()48 QuartzSalBitmap::QuartzSalBitmap()
49 : mxCachedImage( nullptr )
50 , mnBits(0)
51 , mnWidth(0)
52 , mnHeight(0)
53 , mnBytesPerRow(0)
54 {
55 }
56
~QuartzSalBitmap()57 QuartzSalBitmap::~QuartzSalBitmap()
58 {
59 doDestroy();
60 }
61
Create(const Size & rSize,vcl::PixelFormat ePixelFormat,const BitmapPalette & rBitmapPalette)62 bool QuartzSalBitmap::Create( const Size& rSize, vcl::PixelFormat ePixelFormat, const BitmapPalette& rBitmapPalette )
63 {
64 if (ePixelFormat == vcl::PixelFormat::INVALID)
65 return false;
66
67 maPalette = rBitmapPalette;
68 mnBits = vcl::pixelFormatBitCount(ePixelFormat);
69 mnWidth = rSize.Width();
70 mnHeight = rSize.Height();
71 return AllocateUserData();
72 }
73
Create(const SalBitmap & rSalBmp)74 bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp )
75 {
76 vcl::PixelFormat ePixelFormat = vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount());
77 return Create( rSalBmp, ePixelFormat);
78 }
79
Create(const SalBitmap & rSalBmp,SalGraphics * pGraphics)80 bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
81 {
82 vcl::PixelFormat ePixelFormat = vcl::PixelFormat::INVALID;
83 if (pGraphics)
84 ePixelFormat = vcl::bitDepthToPixelFormat(pGraphics->GetBitCount());
85 else
86 ePixelFormat = vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount());
87
88 return Create( rSalBmp, ePixelFormat);
89 }
90
Create(const SalBitmap & rSalBmp,vcl::PixelFormat eNewPixelFormat)91 bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, vcl::PixelFormat eNewPixelFormat )
92 {
93 const QuartzSalBitmap& rSourceBitmap = static_cast<const QuartzSalBitmap&>(rSalBmp);
94
95 if (eNewPixelFormat != vcl::PixelFormat::INVALID && rSourceBitmap.m_pUserBuffer)
96 {
97 mnBits = vcl::pixelFormatBitCount(eNewPixelFormat);
98 mnWidth = rSourceBitmap.mnWidth;
99 mnHeight = rSourceBitmap.mnHeight;
100 maPalette = rSourceBitmap.maPalette;
101
102 if( AllocateUserData() )
103 {
104 ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette,
105 m_pUserBuffer.get(), rSourceBitmap.mnBits,
106 rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette,
107 rSourceBitmap.m_pUserBuffer.get() );
108 return true;
109 }
110 }
111 return false;
112 }
113
Create(const css::uno::Reference<css::rendering::XBitmapCanvas> &,Size &)114 bool QuartzSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/,
115 Size& /*rSize*/ )
116 {
117 return false;
118 }
119
Destroy()120 void QuartzSalBitmap::Destroy()
121 {
122 doDestroy();
123 }
124
doDestroy()125 void QuartzSalBitmap::doDestroy()
126 {
127 DestroyContext();
128 m_pUserBuffer.reset();
129 }
130
DestroyContext()131 void QuartzSalBitmap::DestroyContext()
132 {
133 if( mxCachedImage )
134 {
135 CGImageRelease( mxCachedImage );
136 mxCachedImage = nullptr;
137 }
138
139 if (maGraphicContext.isSet())
140 {
141 CGContextRelease(maGraphicContext.get());
142 maGraphicContext.set(nullptr);
143 m_pContextBuffer.reset();
144 }
145 }
146
CreateContext()147 bool QuartzSalBitmap::CreateContext()
148 {
149 DestroyContext();
150
151 // prepare graphics context
152 // convert image from user input if available
153 const bool bSkipConversion = !m_pUserBuffer;
154 if( bSkipConversion )
155 AllocateUserData();
156
157 // default to RGBA color space
158 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
159 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
160
161 // convert data into something accepted by CGBitmapContextCreate()
162 size_t bitsPerComponent = 8;
163 sal_uInt32 nContextBytesPerRow = mnBytesPerRow;
164 if( mnBits == 32 )
165 {
166 // no conversion needed for truecolor
167 m_pContextBuffer = m_pUserBuffer;
168 }
169 else if( mnBits == 8 && maPalette.IsGreyPalette8Bit() )
170 {
171 // no conversion needed for grayscale
172 m_pContextBuffer = m_pUserBuffer;
173 aCGColorSpace = GetSalData()->mxGraySpace;
174 aCGBmpInfo = kCGImageAlphaNone;
175 bitsPerComponent = mnBits;
176 }
177 // TODO: is special handling for 1bit input buffers worth it?
178 else
179 {
180 // convert user data to 32 bit
181 nContextBytesPerRow = mnWidth << 2;
182 try
183 {
184 m_pContextBuffer = o3tl::make_shared_array<sal_uInt8>(mnHeight * nContextBytesPerRow);
185
186 if( !bSkipConversion )
187 {
188 ConvertBitmapData( mnWidth, mnHeight,
189 32, nContextBytesPerRow, maPalette, m_pContextBuffer.get(),
190 mnBits, mnBytesPerRow, maPalette, m_pUserBuffer.get() );
191 }
192 }
193 catch( const std::bad_alloc& )
194 {
195 maGraphicContext.set(nullptr);
196 }
197 }
198
199 if (m_pContextBuffer)
200 {
201 maGraphicContext.set(CGBitmapContextCreate(m_pContextBuffer.get(), mnWidth, mnHeight,
202 bitsPerComponent, nContextBytesPerRow,
203 aCGColorSpace, aCGBmpInfo));
204 }
205
206 if (!maGraphicContext.isSet())
207 m_pContextBuffer.reset();
208
209 return maGraphicContext.isSet();
210 }
211
AllocateUserData()212 bool QuartzSalBitmap::AllocateUserData()
213 {
214 Destroy();
215
216 if( mnWidth && mnHeight )
217 {
218 mnBytesPerRow = 0;
219
220 switch( mnBits )
221 {
222 case 1: mnBytesPerRow = (mnWidth + 7) >> 3; break;
223 case 8: mnBytesPerRow = mnWidth; break;
224 case 24: mnBytesPerRow = (mnWidth << 1) + mnWidth; break;
225 case 32: mnBytesPerRow = mnWidth << 2; break;
226 default:
227 assert(false && "vcl::QuartzSalBitmap::AllocateUserData(), illegal bitcount!");
228 }
229 }
230
231 bool alloc = false;
232 if (mnBytesPerRow != 0 &&
233 mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
234 {
235 try
236 {
237 m_pUserBuffer = o3tl::make_shared_array<sal_uInt8>(mnBytesPerRow * mnHeight);
238 alloc = true;
239 }
240 catch (std::bad_alloc &) {}
241 }
242 if (!alloc)
243 {
244 SAL_WARN( "vcl.quartz", "bad_alloc: " << mnWidth << "x" << mnHeight << " (" << mnBytesPerRow * mnHeight << " bytes)");
245 m_pUserBuffer.reset();
246 mnBytesPerRow = 0;
247 }
248
249 return bool(m_pUserBuffer);
250 }
251
ConvertBitmapData(sal_uInt32 nWidth,sal_uInt32 nHeight,sal_uInt16 nDestBits,sal_uInt32 nDestBytesPerRow,const BitmapPalette & rDestPalette,sal_uInt8 * pDestData,sal_uInt16 nSrcBits,sal_uInt32 nSrcBytesPerRow,const BitmapPalette & rSrcPalette,sal_uInt8 * pSrcData)252 void QuartzSalBitmap::ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight,
253 sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow,
254 const BitmapPalette& rDestPalette, sal_uInt8* pDestData,
255 sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow,
256 const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData )
257
258 {
259 if( (nDestBytesPerRow == nSrcBytesPerRow) &&
260 (nDestBits == nSrcBits) && ((nSrcBits != 8) || (rDestPalette.operator==( rSrcPalette ))) )
261 {
262 // simple case, same format, so just copy
263 memcpy( pDestData, pSrcData, nHeight * nDestBytesPerRow );
264 return;
265 }
266
267 // try accelerated conversion if possible
268 // TODO: are other truecolor conversions except BGR->ARGB worth it?
269 bool bConverted = false;
270 if( (nSrcBits == 24) && (nDestBits == 32) )
271 {
272 // TODO: extend bmpfast.cxx with a method that can be directly used here
273 BitmapBuffer aSrcBuf;
274 aSrcBuf.meFormat = ScanlineFormat::N24BitTcBgr;
275 aSrcBuf.mpBits = pSrcData;
276 aSrcBuf.mnBitCount = nSrcBits;
277 aSrcBuf.mnScanlineSize = nSrcBytesPerRow;
278 BitmapBuffer aDstBuf;
279 aDstBuf.meFormat = ScanlineFormat::N32BitTcArgb;
280 aDstBuf.mpBits = pDestData;
281 aDstBuf.mnBitCount = nDestBits;
282 aDstBuf.mnScanlineSize = nDestBytesPerRow;
283
284 aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth;
285 aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight;
286
287 SalTwoRect aTwoRects(0, 0, mnWidth, mnHeight, 0, 0, mnWidth, mnHeight);
288 bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );
289 }
290
291 if( !bConverted )
292 {
293 // TODO: this implementation is for clarity, not for speed
294
295 auto pTarget = vcl::bitmap::getScanlineTransformer(nDestBits, rDestPalette);
296 auto pSource = vcl::bitmap::getScanlineTransformer(nSrcBits, rSrcPalette);
297
298 if (pTarget && pSource)
299 {
300 sal_uInt32 nY = nHeight;
301 while( nY-- )
302 {
303 pTarget->startLine(pDestData);
304 pSource->startLine(pSrcData);
305
306 sal_uInt32 nX = nWidth;
307 while( nX-- )
308 {
309 pTarget->writePixel(pSource->readPixel());
310 }
311 pSrcData += nSrcBytesPerRow;
312 pDestData += nDestBytesPerRow;
313 }
314 }
315 }
316 }
317
GetSize() const318 Size QuartzSalBitmap::GetSize() const
319 {
320 return Size( mnWidth, mnHeight );
321 }
322
GetBitCount() const323 sal_uInt16 QuartzSalBitmap::GetBitCount() const
324 {
325 return mnBits;
326 }
327
328 namespace {
329
330 struct pal_entry
331 {
332 sal_uInt8 mnRed;
333 sal_uInt8 mnGreen;
334 sal_uInt8 mnBlue;
335 };
336
337 }
338
339 pal_entry const aImplSalSysPalEntryAry[ 16 ] =
340 {
341 { 0, 0, 0 },
342 { 0, 0, 0x80 },
343 { 0, 0x80, 0 },
344 { 0, 0x80, 0x80 },
345 { 0x80, 0, 0 },
346 { 0x80, 0, 0x80 },
347 { 0x80, 0x80, 0 },
348 { 0x80, 0x80, 0x80 },
349 { 0xC0, 0xC0, 0xC0 },
350 { 0, 0, 0xFF },
351 { 0, 0xFF, 0 },
352 { 0, 0xFF, 0xFF },
353 { 0xFF, 0, 0 },
354 { 0xFF, 0, 0xFF },
355 { 0xFF, 0xFF, 0 },
356 { 0xFF, 0xFF, 0xFF }
357 };
358
GetDefaultPalette(int mnBits,bool bMonochrome)359 static const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome )
360 {
361 if( bMonochrome )
362 return Bitmap::GetGreyPalette( 1U << mnBits );
363
364 // at this point we should provide some kind of default palette
365 // since all other platforms do so, too.
366 static bool bDefPalInit = false;
367 static BitmapPalette aDefPalette256;
368 static BitmapPalette aDefPalette2;
369 if( ! bDefPalInit )
370 {
371 bDefPalInit = true;
372 aDefPalette256.SetEntryCount( 256 );
373 aDefPalette2.SetEntryCount( 2 );
374
375 // Standard colors
376 unsigned int i;
377 for( i = 0; i < 16; i++ )
378 {
379 aDefPalette256[i] = BitmapColor( aImplSalSysPalEntryAry[i].mnRed,
380 aImplSalSysPalEntryAry[i].mnGreen,
381 aImplSalSysPalEntryAry[i].mnBlue );
382 }
383
384 aDefPalette2[0] = BitmapColor( 0, 0, 0 );
385 aDefPalette2[1] = BitmapColor( 0xff, 0xff, 0xff );
386
387 // own palette (6/6/6)
388 const int DITHER_PAL_STEPS = 6;
389 const sal_uInt8 DITHER_PAL_DELTA = 51;
390 int nB, nG, nR;
391 sal_uInt8 nRed, nGreen, nBlue;
392 for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
393 {
394 for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
395 {
396 for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
397 {
398 aDefPalette256[ i ] = BitmapColor( nRed, nGreen, nBlue );
399 i++;
400 }
401 }
402 }
403 }
404
405 // now fill in appropriate palette
406 switch( mnBits )
407 {
408 case 1: return aDefPalette2;
409 case 8: return aDefPalette256;
410 default: break;
411 }
412
413 const static BitmapPalette aEmptyPalette;
414 return aEmptyPalette;
415 }
416
AcquireBuffer(BitmapAccessMode)417 BitmapBuffer* QuartzSalBitmap::AcquireBuffer( BitmapAccessMode /*nMode*/ )
418 {
419 // TODO: AllocateUserData();
420 if (!m_pUserBuffer)
421 return nullptr;
422
423 BitmapBuffer* pBuffer = new BitmapBuffer;
424 pBuffer->mnWidth = mnWidth;
425 pBuffer->mnHeight = mnHeight;
426 pBuffer->maPalette = maPalette;
427 pBuffer->mnScanlineSize = mnBytesPerRow;
428 pBuffer->mpBits = m_pUserBuffer.get();
429 pBuffer->mnBitCount = mnBits;
430 switch( mnBits )
431 {
432 case 1:
433 pBuffer->meFormat = ScanlineFormat::N1BitMsbPal;
434 break;
435 case 8:
436 pBuffer->meFormat = ScanlineFormat::N8BitPal;
437 break;
438 case 24:
439 pBuffer->meFormat = ScanlineFormat::N24BitTcBgr;
440 break;
441 case 32:
442 pBuffer->meFormat = ScanlineFormat::N32BitTcArgb;
443 break;
444 default: assert(false);
445 }
446
447 // some BitmapBuffer users depend on a complete palette
448 if( (mnBits <= 8) && !maPalette )
449 pBuffer->maPalette = GetDefaultPalette( mnBits, true );
450
451 return pBuffer;
452 }
453
ReleaseBuffer(BitmapBuffer * pBuffer,BitmapAccessMode nMode)454 void QuartzSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
455 {
456 // invalidate graphic context if we have different data
457 if( nMode == BitmapAccessMode::Write )
458 {
459 maPalette = pBuffer->maPalette;
460 if (maGraphicContext.isSet())
461 {
462 DestroyContext();
463 }
464 InvalidateChecksum();
465 }
466
467 delete pBuffer;
468 }
469
CreateCroppedImage(int nX,int nY,int nNewWidth,int nNewHeight) const470 CGImageRef QuartzSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const
471 {
472 if( !mxCachedImage )
473 {
474 if (!maGraphicContext.isSet())
475 {
476 if( !const_cast<QuartzSalBitmap*>(this)->CreateContext() )
477 {
478 return nullptr;
479 }
480 }
481 mxCachedImage = CGBitmapContextCreateImage(maGraphicContext.get());
482 }
483
484 CGImageRef xCroppedImage = nullptr;
485 // short circuit if there is nothing to crop
486 if( !nX && !nY && (mnWidth == nNewWidth) && (mnHeight == nNewHeight) )
487 {
488 xCroppedImage = mxCachedImage;
489 CFRetain( xCroppedImage );
490 }
491 else
492 {
493 nY = mnHeight - (nY + nNewHeight); // adjust for y-mirrored context
494 const CGRect aCropRect = { { static_cast<CGFloat>(nX), static_cast<CGFloat>(nY) }, { static_cast<CGFloat>(nNewWidth), static_cast<CGFloat>(nNewHeight) } };
495 xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect );
496 }
497
498 return xCroppedImage;
499 }
500
CFRTLFree(void *,const void * data,size_t)501 static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
502 {
503 std::free( const_cast<void*>(data) );
504 }
505
CreateWithMask(const SalBitmap & rMask,int nX,int nY,int nWidth,int nHeight) const506 CGImageRef QuartzSalBitmap::CreateWithMask( const SalBitmap& rMask,
507 int nX, int nY, int nWidth, int nHeight ) const
508 {
509 return CreateWithSalBitmapAndMask( *this, rMask, nX, nY, nWidth, nHeight );
510 }
511
512 /** creates an image from the given rectangle, replacing all black pixels
513 with nMaskColor and make all other full transparent */
CreateColorMask(int nX,int nY,int nWidth,int nHeight,Color nMaskColor) const514 CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int nY, int nWidth,
515 int nHeight, Color nMaskColor ) const
516 {
517 CGImageRef xMask = nullptr;
518 if (m_pUserBuffer && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight))
519 {
520 auto pSourcePixels = vcl::bitmap::getScanlineTransformer(mnBits, maPalette);
521 // Don't allocate destination buffer if there is no scanline transformer
522 if( !pSourcePixels )
523 return xMask;
524
525 const sal_uInt32 nDestBytesPerRow = nWidth << 2;
526 std::unique_ptr<sal_uInt32[]> pMaskBuffer(new (std::nothrow) sal_uInt32[ nHeight * nDestBytesPerRow / 4] );
527 if( pMaskBuffer )
528 {
529 sal_uInt32 nColor;
530 reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff;
531 reinterpret_cast<sal_uInt8*>(&nColor)[1] = nMaskColor.GetRed();
532 reinterpret_cast<sal_uInt8*>(&nColor)[2] = nMaskColor.GetGreen();
533 reinterpret_cast<sal_uInt8*>(&nColor)[3] = nMaskColor.GetBlue();
534
535 sal_uInt8* pSource = m_pUserBuffer.get();
536 sal_uInt32* pDest = pMaskBuffer.get();
537 // First to nY on y-axis, as that is our starting point (sub-image)
538 if( nY )
539 pSource += nY * mnBytesPerRow;
540
541 int y = nHeight;
542 while( y-- )
543 {
544 pSourcePixels->startLine( pSource );
545 pSourcePixels->skipPixel(nX); // Skip on x axis to nX
546 sal_uInt32 x = nWidth;
547 while( x-- )
548 {
549 // Fix failure to generate the correct color mask
550 // OutputDevice::ImplDrawRotateText() draws black text but
551 // that will generate gray pixels due to antialiasing so
552 // count dark gray the same as black, light gray the same
553 // as white, and the rest as medium gray.
554 // The results are not smooth since LibreOffice appears to
555 // redraw these semi-transparent masks repeatedly without
556 // clearing the background so the semi-transparent pixels
557 // will grow darker with repeatedly redraws due to
558 // cumulative blending. But it is now better than before.
559 sal_uInt8 nAlpha = 255 - pSourcePixels->readPixel().GetRed();
560 sal_uInt32 nPremultColor = nColor;
561 if ( nAlpha < 192 )
562 {
563 if ( nAlpha < 64 )
564 {
565 nPremultColor = 0;
566 }
567 else
568 {
569 reinterpret_cast<sal_uInt8*>(&nPremultColor)[0] /= 2;
570 reinterpret_cast<sal_uInt8*>(&nPremultColor)[1] /= 2;
571 reinterpret_cast<sal_uInt8*>(&nPremultColor)[2] /= 2;
572 reinterpret_cast<sal_uInt8*>(&nPremultColor)[3] /= 2;
573 }
574 }
575 *pDest++ = nPremultColor;
576 }
577 pSource += mnBytesPerRow;
578 }
579
580 CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(nullptr, pMaskBuffer.release(), nHeight * nDestBytesPerRow, &CFRTLFree) );
581 xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, nullptr, true, kCGRenderingIntentDefault);
582 CFRelease(xDataProvider);
583 }
584 }
585 return xMask;
586 }
587
588 /** QuartzSalBitmap::GetSystemData Get platform native image data from existing image
589 *
590 * @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx
591 * @return true if successful
592 **/
GetSystemData(BitmapSystemData & rData)593 bool QuartzSalBitmap::GetSystemData( BitmapSystemData& rData )
594 {
595 bool bRet = false;
596
597 if (!maGraphicContext.isSet())
598 CreateContext();
599
600 if (maGraphicContext.isSet())
601 {
602 bRet = true;
603
604 if ((CGBitmapContextGetBitsPerPixel(maGraphicContext.get()) == 32) &&
605 (CGBitmapContextGetBitmapInfo(maGraphicContext.get()) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host)
606 {
607 /**
608 * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it.
609 *
610 * Not sure what the above comment means. We don't use Cairo on macOS or iOS.
611 *
612 * This whole if statement was originally (before 2011) inside #ifdef CAIRO. Did we use Cairo on Mac back then?
613 * Anyway, nowadays (since many years, I think) we don't, so should this if statement be dropped? Fun.
614 */
615
616 CGImageRef xImage = CGBitmapContextCreateImage(maGraphicContext.get());
617
618 // re-create the context with single change: include kCGBitmapByteOrder32Host flag.
619 CGContextHolder aGraphicContextNew(CGBitmapContextCreate(CGBitmapContextGetData(maGraphicContext.get()),
620 CGBitmapContextGetWidth(maGraphicContext.get()),
621 CGBitmapContextGetHeight(maGraphicContext.get()),
622 CGBitmapContextGetBitsPerComponent(maGraphicContext.get()),
623 CGBitmapContextGetBytesPerRow(maGraphicContext.get()),
624 CGBitmapContextGetColorSpace(maGraphicContext.get()),
625 CGBitmapContextGetBitmapInfo(maGraphicContext.get()) | kCGBitmapByteOrder32Host));
626 CFRelease(maGraphicContext.get());
627
628 // Needs to be flipped
629 aGraphicContextNew.saveState();
630 CGContextTranslateCTM (aGraphicContextNew.get(), 0, CGBitmapContextGetHeight(aGraphicContextNew.get()));
631 CGContextScaleCTM (aGraphicContextNew.get(), 1.0, -1.0);
632
633 CGContextDrawImage(aGraphicContextNew.get(), CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage);
634
635 // Flip back
636 CGContextRestoreGState( aGraphicContextNew.get() );
637 CGImageRelease( xImage );
638 maGraphicContext = aGraphicContextNew;
639 }
640
641 rData.mnWidth = mnWidth;
642 rData.mnHeight = mnHeight;
643 }
644
645 return bRet;
646 }
647
ScalingSupported() const648 bool QuartzSalBitmap::ScalingSupported() const
649 {
650 return false;
651 }
652
Scale(const double &,const double &,BmpScaleFlag)653 bool QuartzSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ )
654 {
655 return false;
656 }
657
Replace(const Color &,const Color &,sal_uInt8)658 bool QuartzSalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uInt8 /*nTol*/ )
659 {
660 return false;
661 }
662
663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
664