xref: /AOO41X/main/vcl/aqua/source/gdi/salbmp.cxx (revision 24a22e85f267d4ef43b9f427d9a4f2a844842173)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <boost/bind.hpp>
28 
29 #include "basebmp/scanlineformats.hxx"
30 #include "basebmp/color.hxx"
31 
32 #include "basegfx/vector/b2ivector.hxx"
33 
34 #include "tools/color.hxx"
35 
36 #include "vcl/bitmap.hxx" // for BitmapSystemData
37 #include "vcl/salbtype.hxx"
38 
39 #include "aqua/salbmp.h"
40 #include "aqua/salinst.h"
41 
42 #include "bmpfast.hxx"
43 
44 // =======================================================================
45 
isValidBitCount(sal_uInt16 nBitCount)46 static bool isValidBitCount( sal_uInt16 nBitCount )
47 {
48     return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32);
49 }
50 
51 // =======================================================================
52 
AquaSalBitmap()53 AquaSalBitmap::AquaSalBitmap()
54 : mxGraphicContext( NULL )
55 , mxCachedImage( NULL )
56 , mnBits(0)
57 , mnWidth(0)
58 , mnHeight(0)
59 , mnBytesPerRow(0)
60 {
61 }
62 
63 // ------------------------------------------------------------------
64 
~AquaSalBitmap()65 AquaSalBitmap::~AquaSalBitmap()
66 {
67     Destroy();
68 }
69 
70 // ------------------------------------------------------------------
71 
Create(CGLayerRef xLayer,int nBitmapBits,int nX,int nY,int nWidth,int nHeight,bool)72 bool AquaSalBitmap::Create( CGLayerRef xLayer, int nBitmapBits,
73     int nX, int nY, int nWidth, int nHeight, bool /*bMirrorVert*/ )
74 {
75     DBG_ASSERT( xLayer, "AquaSalBitmap::Create() from non-layered context" );
76 
77     // sanitize input parameters
78     if( nX < 0 )
79         nWidth += nX, nX = 0;
80     if( nY < 0 )
81         nHeight += nY, nY = 0;
82     const CGSize aLayerSize = CGLayerGetSize( xLayer );
83     if( nWidth >= (int)aLayerSize.width - nX )
84         nWidth = (int)aLayerSize.width - nX;
85     if( nHeight >= (int)aLayerSize.height - nY )
86         nHeight = (int)aLayerSize.height - nY;
87     if( (nWidth < 0) || (nHeight < 0) )
88         nWidth = nHeight = 0;
89 
90     // initialize properties
91     mnWidth  = nWidth;
92     mnHeight = nHeight;
93     mnBits   = nBitmapBits ? nBitmapBits : 32;
94 
95     // initialize drawing context
96     CreateContext();
97 
98     // copy layer content into the bitmap buffer
99     const CGPoint aSrcPoint = CGPointMake( -nX, -nY);
100     ::CGContextDrawLayerAtPoint( mxGraphicContext, aSrcPoint, xLayer );
101     return true;
102 }
103 
104 // ------------------------------------------------------------------
105 
Create(const Size & rSize,sal_uInt16 nBits,const BitmapPalette & rBitmapPalette)106 bool AquaSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
107 {
108     if( !isValidBitCount( nBits ) )
109         return false;
110     maPalette = rBitmapPalette;
111     mnBits = nBits;
112     mnWidth = rSize.Width();
113     mnHeight = rSize.Height();
114     return AllocateUserData();
115 }
116 
117 // ------------------------------------------------------------------
118 
Create(const SalBitmap & rSalBmp)119 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp )
120 {
121     return Create( rSalBmp, rSalBmp.GetBitCount() );
122 }
123 
124 // ------------------------------------------------------------------
125 
Create(const SalBitmap & rSalBmp,SalGraphics * pGraphics)126 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
127 {
128     return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
129 }
130 
131 // ------------------------------------------------------------------
132 
Create(const SalBitmap & rSalBmp,sal_uInt16 nNewBitCount)133 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
134 {
135     const AquaSalBitmap& rSourceBitmap = static_cast<const AquaSalBitmap&>(rSalBmp);
136 
137     if( isValidBitCount( nNewBitCount ) &&  rSourceBitmap.maUserBuffer.get() )
138     {
139         mnBits = nNewBitCount;
140         mnWidth = rSourceBitmap.mnWidth;
141         mnHeight = rSourceBitmap.mnHeight;
142         maPalette = rSourceBitmap.maPalette;
143 
144         if( AllocateUserData() )
145         {
146             ConvertBitmapData( mnWidth, mnHeight, mnBits, mnBytesPerRow, maPalette, maUserBuffer.get(), rSourceBitmap.mnBits, rSourceBitmap.mnBytesPerRow, rSourceBitmap.maPalette, rSourceBitmap.maUserBuffer.get() );
147             return true;
148         }
149     }
150     return false;
151 }
152 
153 // ------------------------------------------------------------------
154 
Destroy()155 void AquaSalBitmap::Destroy()
156 {
157     DestroyContext();
158     maUserBuffer.reset();
159 }
160 
161 // ------------------------------------------------------------------
162 
DestroyContext()163 void AquaSalBitmap::DestroyContext()
164 {
165     CGImageRelease( mxCachedImage );
166     mxCachedImage = NULL;
167 
168     if( mxGraphicContext )
169     {
170         CGContextRelease( mxGraphicContext );
171         mxGraphicContext = NULL;
172         maContextBuffer.reset();
173     }
174 }
175 
176 // ------------------------------------------------------------------
177 
CreateContext()178 bool AquaSalBitmap::CreateContext()
179 {
180     DestroyContext();
181 
182     // prepare graphics context
183     // convert image from user input if available
184     const bool bSkipConversion = !maUserBuffer;
185     if( bSkipConversion )
186         AllocateUserData();
187 
188     // default to RGBA color space
189     CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
190     CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
191 
192     // convert data into something accepted by CGBitmapContextCreate()
193     size_t bitsPerComponent = (mnBits == 16) ? 5 : 8;
194     sal_uInt32 nContextBytesPerRow = mnBytesPerRow;
195     if( (mnBits == 16) || (mnBits == 32) )
196     {
197         // no conversion needed for truecolor
198         maContextBuffer = maUserBuffer;
199     }
200     else if( (mnBits == 8) && maPalette.IsGreyPalette() )
201     {
202         // no conversion needed for grayscale
203         maContextBuffer = maUserBuffer;
204         aCGColorSpace = GetSalData()->mxGraySpace;
205         aCGBmpInfo = kCGImageAlphaNone;
206         bitsPerComponent = mnBits;
207     }
208     // TODO: is special handling for 1bit input buffers worth it?
209     else
210     {
211         // convert user data to 32 bit
212         nContextBytesPerRow = mnWidth << 2;
213         try
214         {
215             maContextBuffer.reset( new sal_uInt8[ mnHeight * nContextBytesPerRow ] );
216 
217             if( !bSkipConversion )
218                 ConvertBitmapData( mnWidth, mnHeight,
219                                32, nContextBytesPerRow, maPalette, maContextBuffer.get(),
220                                mnBits, mnBytesPerRow, maPalette, maUserBuffer.get() );
221         }
222         catch( std::bad_alloc )
223         {
224             mxGraphicContext = 0;
225         }
226     }
227 
228     if( maContextBuffer.get() )
229     {
230         mxGraphicContext = ::CGBitmapContextCreate( maContextBuffer.get(), mnWidth, mnHeight,
231             bitsPerComponent, nContextBytesPerRow, aCGColorSpace, aCGBmpInfo );
232     }
233 
234     if( !mxGraphicContext )
235         maContextBuffer.reset();
236 
237     return mxGraphicContext != NULL;
238 }
239 
240 // ------------------------------------------------------------------
241 
AllocateUserData()242 bool AquaSalBitmap::AllocateUserData()
243 {
244     Destroy();
245 
246     if( mnWidth && mnHeight )
247     {
248         mnBytesPerRow =  0;
249 
250         switch( mnBits )
251         {
252         case 1:     mnBytesPerRow = (mnWidth + 7) >> 3; break;
253         case 4:     mnBytesPerRow = (mnWidth + 1) >> 1; break;
254         case 8:     mnBytesPerRow = mnWidth; break;
255         case 16:    mnBytesPerRow = mnWidth << 1; break;
256         case 24:    mnBytesPerRow = (mnWidth << 1) + mnWidth; break;
257         case 32:    mnBytesPerRow = mnWidth << 2; break;
258         default:
259             DBG_ERROR("vcl::AquaSalBitmap::AllocateUserData(), illegal bitcount!");
260         }
261     }
262 
263     try
264     {
265         if( mnBytesPerRow )
266             maUserBuffer.reset( new sal_uInt8[mnBytesPerRow * mnHeight] );
267     }
268     catch( const std::bad_alloc& )
269     {
270         DBG_ERROR( "vcl::AquaSalBitmap::AllocateUserData: bad alloc" );
271         maUserBuffer.reset();
272         mnBytesPerRow = 0;
273     }
274 
275     return maUserBuffer.get() != 0;
276 }
277 
278 // ------------------------------------------------------------------
279 
280 class ImplPixelFormat
281 {
282 protected:
283     sal_uInt8* pData;
284 public:
285     static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
286 
StartLine(sal_uInt8 * pLine)287     virtual void StartLine( sal_uInt8* pLine ) { pData = pLine; }
288     virtual void SkipPixel( sal_uInt32 nPixel ) = 0;
289     virtual ColorData ReadPixel() = 0;
290     virtual void WritePixel( ColorData nColor ) = 0;
291 };
292 
293 class ImplPixelFormat32 : public ImplPixelFormat
294 // currently ARGB-format for 32bit depth
295 {
296 public:
SkipPixel(sal_uInt32 nPixel)297     virtual void SkipPixel( sal_uInt32 nPixel )
298     {
299         pData += nPixel << 2;
300     }
ReadPixel()301     virtual ColorData ReadPixel()
302     {
303         const ColorData c = RGB_COLORDATA( pData[1], pData[2], pData[3] );
304         pData += 4;
305         return c;
306     }
WritePixel(ColorData nColor)307     virtual void WritePixel( ColorData nColor )
308     {
309         *pData++ = 0;
310         *pData++ = COLORDATA_RED( nColor );
311         *pData++ = COLORDATA_GREEN( nColor );
312         *pData++ = COLORDATA_BLUE( nColor );
313     }
314 };
315 
316 class ImplPixelFormat24 : public ImplPixelFormat
317 // currently BGR-format for 24bit depth
318 {
319 public:
SkipPixel(sal_uInt32 nPixel)320     virtual void SkipPixel( sal_uInt32 nPixel )
321     {
322         pData += (nPixel << 1) + nPixel;
323     }
ReadPixel()324     virtual ColorData ReadPixel()
325     {
326         const ColorData c = RGB_COLORDATA( pData[2], pData[1], pData[0] );
327         pData += 3;
328         return c;
329     }
WritePixel(ColorData nColor)330     virtual void WritePixel( ColorData nColor )
331     {
332         *pData++ = COLORDATA_BLUE( nColor );
333         *pData++ = COLORDATA_GREEN( nColor );
334         *pData++ = COLORDATA_RED( nColor );
335     }
336 };
337 
338 class ImplPixelFormat16 : public ImplPixelFormat
339 // currently R5G6B5-format for 16bit depth
340 {
341 protected:
342     sal_uInt16* pData16;
343 public:
344 
StartLine(sal_uInt8 * pLine)345     virtual void StartLine( sal_uInt8* pLine )
346     {
347         pData16 = (sal_uInt16*)pLine;
348     }
SkipPixel(sal_uInt32 nPixel)349     virtual void SkipPixel( sal_uInt32 nPixel )
350     {
351         pData += nPixel;
352     }
ReadPixel()353     virtual ColorData ReadPixel()
354     {
355         const ColorData c = RGB_COLORDATA( (*pData & 0x7c00) >> 7, (*pData & 0x03e0) >> 2 , (*pData & 0x001f) << 3 );
356         pData++;
357         return c;
358     }
WritePixel(ColorData nColor)359     virtual void WritePixel( ColorData nColor )
360     {
361         *pData++ =  ((COLORDATA_RED( nColor ) & 0xf8 ) << 7 ) ||
362                     ((COLORDATA_GREEN( nColor ) & 0xf8 ) << 2 ) ||
363                     ((COLORDATA_BLUE( nColor ) & 0xf8 ) >> 3 );
364     }
365 };
366 
367 class ImplPixelFormat8 : public ImplPixelFormat
368 {
369 private:
370     const BitmapPalette& mrPalette;
371 
372 public:
ImplPixelFormat8(const BitmapPalette & rPalette)373     ImplPixelFormat8( const BitmapPalette& rPalette )
374     : mrPalette( rPalette )
375     {
376     }
SkipPixel(sal_uInt32 nPixel)377     virtual void SkipPixel( sal_uInt32 nPixel )
378     {
379         pData += nPixel;
380     }
ReadPixel()381     virtual ColorData ReadPixel()
382     {
383         return mrPalette[ *pData++ ].operator Color().GetColor();
384     }
WritePixel(ColorData nColor)385     virtual void WritePixel( ColorData nColor )
386     {
387         const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
388         *pData++ = static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) );
389     }
390 };
391 
392 class ImplPixelFormat4 : public ImplPixelFormat
393 {
394 private:
395     const BitmapPalette& mrPalette;
396     sal_uInt32 mnX;
397     sal_uInt32 mnShift;
398 
399 public:
ImplPixelFormat4(const BitmapPalette & rPalette)400     ImplPixelFormat4( const BitmapPalette& rPalette )
401     : mrPalette( rPalette )
402     {
403     }
SkipPixel(sal_uInt32 nPixel)404     virtual void SkipPixel( sal_uInt32 nPixel )
405     {
406         mnX += nPixel;
407         if( (nPixel & 1) )
408             mnShift ^= 4;
409     }
StartLine(sal_uInt8 * pLine)410     virtual void StartLine( sal_uInt8* pLine )
411     {
412         pData = pLine;
413         mnX = 0;
414         mnShift = 4;
415     }
ReadPixel()416     virtual ColorData ReadPixel()
417     {
418         const BitmapColor& rColor = mrPalette[( pData[mnX >> 1] >> mnShift) & 0x0f];
419         mnX++;
420         mnShift ^= 4;
421         return rColor.operator Color().GetColor();
422     }
WritePixel(ColorData nColor)423     virtual void WritePixel( ColorData nColor )
424     {
425         const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
426         pData[mnX>>1] &= (0xf0 >> mnShift);
427         pData[mnX>>1] |= (static_cast< sal_uInt8 >( mrPalette.GetBestIndex( aColor ) ) & 0x0f);
428         mnX++;
429         mnShift ^= 4;
430     }
431 };
432 
433 class ImplPixelFormat1 : public ImplPixelFormat
434 {
435 private:
436     const BitmapPalette& mrPalette;
437     sal_uInt32 mnX;
438 
439 public:
ImplPixelFormat1(const BitmapPalette & rPalette)440     ImplPixelFormat1( const BitmapPalette& rPalette )
441     : mrPalette( rPalette )
442     {
443     }
SkipPixel(sal_uInt32 nPixel)444     virtual void SkipPixel( sal_uInt32 nPixel )
445     {
446         mnX += nPixel;
447     }
StartLine(sal_uInt8 * pLine)448     virtual void StartLine( sal_uInt8* pLine )
449     {
450         pData = pLine;
451         mnX = 0;
452     }
ReadPixel()453     virtual ColorData ReadPixel()
454     {
455         const BitmapColor& rColor = mrPalette[ (pData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
456         mnX++;
457         return rColor.operator Color().GetColor();
458     }
WritePixel(ColorData nColor)459     virtual void WritePixel( ColorData nColor )
460     {
461         const BitmapColor aColor( COLORDATA_RED( nColor ), COLORDATA_GREEN( nColor ), COLORDATA_BLUE( nColor ) );
462         if( mrPalette.GetBestIndex( aColor ) & 1 )
463             pData[ mnX >> 3 ] |= 1 << ( 7 - ( mnX & 7 ) );
464         else
465             pData[ mnX >> 3 ] &= ~( 1 << ( 7 - ( mnX & 7 ) ) );
466         mnX++;
467     }
468 };
469 
GetFormat(sal_uInt16 nBits,const BitmapPalette & rPalette)470 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
471 {
472     switch( nBits )
473     {
474     case 1: return new ImplPixelFormat1( rPalette );
475     case 4: return new ImplPixelFormat4( rPalette );
476     case 8: return new ImplPixelFormat8( rPalette );
477     case 16: return new ImplPixelFormat16;
478     case 24: return new ImplPixelFormat24;
479     case 32: return new ImplPixelFormat32;
480     }
481 
482     return 0;
483 }
484 
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)485 void AquaSalBitmap::ConvertBitmapData( sal_uInt32 nWidth, sal_uInt32 nHeight,
486                                        sal_uInt16 nDestBits, sal_uInt32 nDestBytesPerRow, const BitmapPalette& rDestPalette, sal_uInt8* pDestData,
487                                        sal_uInt16 nSrcBits, sal_uInt32 nSrcBytesPerRow, const BitmapPalette& rSrcPalette, sal_uInt8* pSrcData )
488 
489 {
490     if( (nDestBytesPerRow == nSrcBytesPerRow) && (nDestBits == nSrcBits) && ((nSrcBits != 8) || (rDestPalette.operator==( rSrcPalette ))) )
491     {
492         // simple case, same format, so just copy
493         memcpy( pDestData, pSrcData, nHeight * nDestBytesPerRow );
494         return;
495     }
496 
497     // try accelerated conversion if possible
498     // TODO: are other truecolor conversions except BGR->ARGB worth it?
499     bool bConverted = false;
500     if( (nSrcBits == 24) && (nDestBits == 32) )
501     {
502         // TODO: extend bmpfast.cxx with a method that can be directly used here
503         BitmapBuffer aSrcBuf;
504         aSrcBuf.mnFormat = BMP_FORMAT_24BIT_TC_BGR;
505         aSrcBuf.mpBits = pSrcData;
506         aSrcBuf.mnBitCount = nSrcBits;
507         aSrcBuf.mnScanlineSize = nSrcBytesPerRow;
508         BitmapBuffer aDstBuf;
509         aDstBuf.mnFormat = BMP_FORMAT_32BIT_TC_ARGB;
510         aDstBuf.mpBits = pDestData;
511         aSrcBuf.mnBitCount = nDestBits;
512         aDstBuf.mnScanlineSize = nDestBytesPerRow;
513 
514         aSrcBuf.mnWidth = aDstBuf.mnWidth = nWidth;
515         aSrcBuf.mnHeight = aDstBuf.mnHeight = nHeight;
516 
517         SalTwoRect aTwoRects;
518         aTwoRects.mnSrcX = aTwoRects.mnDestX = 0;
519         aTwoRects.mnSrcY = aTwoRects.mnDestY = 0;
520         aTwoRects.mnSrcWidth = aTwoRects.mnDestWidth = mnWidth;
521         aTwoRects.mnSrcHeight = aTwoRects.mnDestHeight = mnHeight;
522         bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );
523     }
524 
525     if( !bConverted )
526     {
527         // TODO: this implementation is for clarety, not for speed
528 
529         ImplPixelFormat* pD = ImplPixelFormat::GetFormat( nDestBits, rDestPalette );
530         ImplPixelFormat* pS = ImplPixelFormat::GetFormat( nSrcBits, rSrcPalette );
531 
532         if( pD && pS )
533         {
534             sal_uInt32 nY = nHeight;
535             while( nY-- )
536             {
537                 pD->StartLine( pDestData );
538                 pS->StartLine( pSrcData );
539 
540                 sal_uInt32 nX = nWidth;
541                 while( nX-- )
542                     pD->WritePixel( pS->ReadPixel() );
543 
544                 pSrcData += nSrcBytesPerRow;
545                 pDestData += nDestBytesPerRow;
546             }
547         }
548         delete pS;
549         delete pD;
550     }
551 }
552 
553 // ------------------------------------------------------------------
554 
GetSize() const555 Size AquaSalBitmap::GetSize() const
556 {
557     return Size( mnWidth, mnHeight );
558 }
559 
560 // ------------------------------------------------------------------
561 
GetBitCount() const562 sal_uInt16 AquaSalBitmap::GetBitCount() const
563 {
564     return mnBits;
565 }
566 
567 // ------------------------------------------------------------------
568 
569 static struct pal_entry
570 {
571     sal_uInt8 mnRed;
572     sal_uInt8 mnGreen;
573     sal_uInt8 mnBlue;
574 }
575 const aImplSalSysPalEntryAry[ 16 ] =
576 {
577 {    0,    0,    0 },
578 {    0,    0, 0x80 },
579 {    0, 0x80,    0 },
580 {    0, 0x80, 0x80 },
581 { 0x80,    0,    0 },
582 { 0x80,    0, 0x80 },
583 { 0x80, 0x80,    0 },
584 { 0x80, 0x80, 0x80 },
585 { 0xC0, 0xC0, 0xC0 },
586 {    0,    0, 0xFF },
587 {    0, 0xFF,    0 },
588 {    0, 0xFF, 0xFF },
589 { 0xFF,    0,    0 },
590 { 0xFF,    0, 0xFF },
591 { 0xFF, 0xFF,    0 },
592 { 0xFF, 0xFF, 0xFF }
593 };
594 
GetDefaultPalette(int mnBits,bool bMonochrome)595 const BitmapPalette& GetDefaultPalette( int mnBits, bool bMonochrome )
596 {
597     if( bMonochrome )
598         return Bitmap::GetGreyPalette( 1U << mnBits );
599 
600     // at this point we should provide some kind of default palette
601     // since all other platforms do so, too.
602     static bool bDefPalInit = false;
603     static BitmapPalette aDefPalette256;
604     static BitmapPalette aDefPalette16;
605     static BitmapPalette aDefPalette2;
606     if( ! bDefPalInit )
607     {
608         bDefPalInit = true;
609         aDefPalette256.SetEntryCount( 256 );
610         aDefPalette16.SetEntryCount( 16 );
611         aDefPalette2.SetEntryCount( 2 );
612 
613         // Standard colors
614         unsigned int i;
615         for( i = 0; i < 16; i++ )
616         {
617             aDefPalette16[i] =
618             aDefPalette256[i] = BitmapColor( aImplSalSysPalEntryAry[i].mnRed,
619                                              aImplSalSysPalEntryAry[i].mnGreen,
620                                              aImplSalSysPalEntryAry[i].mnBlue );
621             }
622 
623         aDefPalette2[0] = BitmapColor( 0, 0, 0 );
624         aDefPalette2[1] = BitmapColor( 0xff, 0xff, 0xff );
625 
626         // own palette (6/6/6)
627         const int DITHER_PAL_STEPS = 6;
628         const sal_uInt8 DITHER_PAL_DELTA = 51;
629         int nB, nG, nR;
630         sal_uInt8 nRed, nGreen, nBlue;
631         for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
632         {
633             for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
634             {
635                 for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
636                 {
637                     aDefPalette256[ i ] = BitmapColor( nRed, nGreen, nBlue );
638                     i++;
639                 }
640             }
641         }
642     }
643 
644     // now fill in appropriate palette
645     switch( mnBits )
646     {
647     case 1: return aDefPalette2;
648     case 4: return aDefPalette16;
649     case 8: return aDefPalette256;
650     default: break;
651     }
652 
653     const static BitmapPalette aEmptyPalette;
654     return aEmptyPalette;
655 }
656 
AcquireBuffer(bool)657 BitmapBuffer* AquaSalBitmap::AcquireBuffer( bool /*bReadOnly*/ )
658 {
659     if( !maUserBuffer.get() )
660 //  || maContextBuffer.get() && (maUserBuffer.get() != maContextBuffer.get()) )
661     {
662         fprintf(stderr,"ASB::Acq(%dx%d,d=%d)\n",mnWidth,mnHeight,mnBits);
663         // TODO: AllocateUserData();
664         return NULL;
665     }
666 
667     BitmapBuffer* pBuffer = new BitmapBuffer;
668     pBuffer->mnWidth = mnWidth;
669     pBuffer->mnHeight = mnHeight;
670     pBuffer->maPalette = maPalette;
671     pBuffer->mnScanlineSize = mnBytesPerRow;
672     pBuffer->mpBits = maUserBuffer.get();
673     pBuffer->mnBitCount = mnBits;
674     switch( mnBits )
675     {
676     case 1:     pBuffer->mnFormat = BMP_FORMAT_1BIT_MSB_PAL; break;
677     case 4:     pBuffer->mnFormat = BMP_FORMAT_4BIT_MSN_PAL; break;
678     case 8:     pBuffer->mnFormat = BMP_FORMAT_8BIT_PAL; break;
679     case 16:    pBuffer->mnFormat = BMP_FORMAT_16BIT_TC_MSB_MASK;
680                 pBuffer->maColorMask  = ColorMask( k16BitRedColorMask, k16BitGreenColorMask, k16BitBlueColorMask );
681                 break;
682     case 24:    pBuffer->mnFormat = BMP_FORMAT_24BIT_TC_BGR; break;
683     case 32:    pBuffer->mnFormat = BMP_FORMAT_32BIT_TC_ARGB;
684                 pBuffer->maColorMask  = ColorMask( k32BitRedColorMask, k32BitGreenColorMask, k32BitBlueColorMask );
685                 break;
686     }
687     pBuffer->mnFormat |= BMP_FORMAT_BOTTOM_UP;
688 
689     // some BitmapBuffer users depend on a complete palette
690     if( (mnBits <= 8) && !maPalette )
691         pBuffer->maPalette = GetDefaultPalette( mnBits, true );
692 
693     return pBuffer;
694 }
695 
696 // ------------------------------------------------------------------
697 
ReleaseBuffer(BitmapBuffer * pBuffer,bool bReadOnly)698 void AquaSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly )
699 {
700     // invalidate graphic context if we have different data
701     if( !bReadOnly )
702     {
703         maPalette = pBuffer->maPalette;
704         if( mxGraphicContext )
705             DestroyContext();
706     }
707 
708     delete pBuffer;
709 }
710 
711 // ------------------------------------------------------------------
712 
CreateCroppedImage(int nX,int nY,int nNewWidth,int nNewHeight) const713 CGImageRef AquaSalBitmap::CreateCroppedImage( int nX, int nY, int nNewWidth, int nNewHeight ) const
714 {
715     if( !mxCachedImage )
716     {
717         if( !mxGraphicContext )
718             if( !const_cast<AquaSalBitmap*>(this)->CreateContext() )
719                 return NULL;
720 
721         mxCachedImage = CGBitmapContextCreateImage( mxGraphicContext );
722     }
723 
724     CGImageRef xCroppedImage = NULL;
725     // short circuit if there is nothing to crop
726     if( !nX && !nY && (mnWidth == nNewWidth) && (mnHeight == nNewHeight) )
727     {
728           xCroppedImage = mxCachedImage;
729           CFRetain( xCroppedImage );
730     }
731     else
732     {
733         nY = mnHeight - (nY + nNewHeight); // adjust for y-mirrored context
734         const CGRect aCropRect = CGRectMake( nX, nY, nNewWidth, nNewHeight);
735         xCroppedImage = CGImageCreateWithImageInRect( mxCachedImage, aCropRect );
736     }
737 
738     return xCroppedImage;
739 }
740 
741 // ------------------------------------------------------------------
742 
CFRTLFree(void *,const void * data,size_t)743 static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
744 {
745     rtl_freeMemory( const_cast<void*>(data) );
746 }
747 
CreateWithMask(const AquaSalBitmap & rMask,int nX,int nY,int nWidth,int nHeight) const748 CGImageRef AquaSalBitmap::CreateWithMask( const AquaSalBitmap& rMask,
749     int nX, int nY, int nWidth, int nHeight ) const
750 {
751     CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) );
752     if( !xImage )
753         return NULL;
754 
755     CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
756     if( !xMask )
757         return xImage;
758 
759     // CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
760     // TODO: isolate in an extra method?
761     if( !CGImageIsMask(xMask) || (CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
762     {
763         const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset
764 
765         // create the alpha mask image fitting our image
766         // TODO: is caching the full mask or the subimage mask worth it?
767         int nMaskBytesPerRow = ((nWidth + 3) & ~3);
768         void* pMaskMem = rtl_allocateMemory( nMaskBytesPerRow * nHeight );
769         CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
770             nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone );
771         CGContextDrawImage( xMaskContext, xImageRect, xMask );
772         CFRelease( xMask );
773         CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( NULL,
774         pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
775         static const CGFloat* pDecode = NULL;
776         xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false );
777         CFRelease( xDataProvider );
778         CFRelease( xMaskContext );
779     }
780 
781     if( !xMask )
782         return xImage;
783 
784     // combine image and alpha mask
785     CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
786     CFRelease( xMask );
787     CFRelease( xImage );
788     return xMaskedImage;
789 }
790 
791 // ------------------------------------------------------------------
792 
793 /** creates an image from the given rectangle, replacing all black pixels with nMaskColor and make all other full transparent */
CreateColorMask(int nX,int nY,int nWidth,int nHeight,SalColor nMaskColor) const794 CGImageRef AquaSalBitmap::CreateColorMask( int nX, int nY, int nWidth, int nHeight, SalColor nMaskColor ) const
795 {
796     CGImageRef xMask = 0;
797     if( maUserBuffer.get() && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight) )
798     {
799         const sal_uInt32 nDestBytesPerRow = nWidth << 2;
800         sal_uInt32* pMaskBuffer = static_cast<sal_uInt32*>( rtl_allocateMemory( nHeight * nDestBytesPerRow ) );
801         sal_uInt32* pDest = pMaskBuffer;
802 
803         ImplPixelFormat* pSourcePixels = ImplPixelFormat::GetFormat( mnBits, maPalette );
804 
805         if( pMaskBuffer && pSourcePixels )
806         {
807             sal_uInt32 nColor;
808             reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff;
809             reinterpret_cast<sal_uInt8*>(&nColor)[1] = SALCOLOR_RED( nMaskColor );
810             reinterpret_cast<sal_uInt8*>(&nColor)[2] = SALCOLOR_GREEN( nMaskColor );
811             reinterpret_cast<sal_uInt8*>(&nColor)[3] = SALCOLOR_BLUE( nMaskColor );
812 
813             sal_uInt8* pSource = maUserBuffer.get();
814             if( nY )
815                 pSource += nY * mnBytesPerRow;
816 
817             int y = nHeight;
818             while( y-- )
819             {
820                 pSourcePixels->StartLine( pSource );
821                 pSourcePixels->SkipPixel(nX);
822                 sal_uInt32 x = nWidth;
823                 while( x-- )
824                 {
825                     *pDest++ = ( pSourcePixels->ReadPixel() == 0 ) ? nColor : 0;
826                 }
827                 pSource += mnBytesPerRow;
828             }
829 
830             CGDataProviderRef xDataProvider( CGDataProviderCreateWithData(NULL, pMaskBuffer, nHeight * nDestBytesPerRow, &CFRTLFree) );
831             xMask = CGImageCreate(nWidth, nHeight, 8, 32, nDestBytesPerRow, GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedFirst, xDataProvider, NULL, true, kCGRenderingIntentDefault);
832             CFRelease(xDataProvider);
833         }
834         else
835         {
836             free(pMaskBuffer);
837         }
838 
839         delete pSourcePixels;
840     }
841     return xMask;
842 }
843 
844 // =======================================================================
845 
846 /** AquaSalBitmap::GetSystemData Get platform native image data from existing image
847  *
848  *  @param rData struct BitmapSystemData, defined in vcl/inc/bitmap.hxx
849  *  @return true if successful
850 **/
GetSystemData(BitmapSystemData & rData)851 bool AquaSalBitmap::GetSystemData( BitmapSystemData& rData )
852 {
853     bool bRet = false;
854 
855     if( !mxGraphicContext )
856         CreateContext();
857 
858     if ( mxGraphicContext )
859     {
860         bRet = true;
861 
862 #ifdef CAIRO
863         if ((CGBitmapContextGetBitsPerPixel(mxGraphicContext) == 32) &&
864             (CGBitmapContextGetBitmapInfo(mxGraphicContext) & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host) {
865             /**
866              * We need to hack things because VCL does not use kCGBitmapByteOrder32Host, while Cairo requires it.
867              */
868             OSL_TRACE("AquaSalBitmap::%s(): kCGBitmapByteOrder32Host not found => inserting it.",__func__);
869 
870             CGImageRef xImage = CGBitmapContextCreateImage (mxGraphicContext);
871 
872             // re-create the context with single change: include kCGBitmapByteOrder32Host flag.
873             CGContextRef mxGraphicContextNew = CGBitmapContextCreate( CGBitmapContextGetData(mxGraphicContext),
874                                                                       CGBitmapContextGetWidth(mxGraphicContext),
875                                                                       CGBitmapContextGetHeight(mxGraphicContext),
876                                                                       CGBitmapContextGetBitsPerComponent(mxGraphicContext),
877                                                                       CGBitmapContextGetBytesPerRow(mxGraphicContext),
878                                                                       CGBitmapContextGetColorSpace(mxGraphicContext),
879                                                                       CGBitmapContextGetBitmapInfo(mxGraphicContext) | kCGBitmapByteOrder32Host);
880             CFRelease(mxGraphicContext);
881 
882             // Needs to be flipped
883             CGContextSaveGState( mxGraphicContextNew );
884             CGContextTranslateCTM (mxGraphicContextNew, 0, CGBitmapContextGetHeight(mxGraphicContextNew));
885             CGContextScaleCTM (mxGraphicContextNew, 1.0, -1.0);
886 
887             CGContextDrawImage(mxGraphicContextNew, CGRectMake( 0, 0, CGImageGetWidth(xImage), CGImageGetHeight(xImage)), xImage);
888 
889             // Flip back
890             CGContextRestoreGState( mxGraphicContextNew );
891 
892             CGImageRelease( xImage );
893             mxGraphicContext = mxGraphicContextNew;
894         }
895 #endif
896 
897         rData.rImageContext = (void *) mxGraphicContext;
898         rData.mnWidth = mnWidth;
899         rData.mnHeight = mnHeight;
900     }
901 
902     return bRet;
903 }
904