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