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 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 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 65 AquaSalBitmap::~AquaSalBitmap() 66 { 67 Destroy(); 68 } 69 70 // ------------------------------------------------------------------ 71 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 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 119 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp ) 120 { 121 return Create( rSalBmp, rSalBmp.GetBitCount() ); 122 } 123 124 // ------------------------------------------------------------------ 125 126 bool AquaSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) 127 { 128 return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() ); 129 } 130 131 // ------------------------------------------------------------------ 132 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 155 void AquaSalBitmap::Destroy() 156 { 157 DestroyContext(); 158 maUserBuffer.reset(); 159 } 160 161 // ------------------------------------------------------------------ 162 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 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 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( NULL ); 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 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: 297 virtual void SkipPixel( sal_uInt32 nPixel ) 298 { 299 pData += nPixel << 2; 300 } 301 virtual ColorData ReadPixel() 302 { 303 const ColorData c = RGB_COLORDATA( pData[1], pData[2], pData[3] ); 304 pData += 4; 305 return c; 306 } 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: 320 virtual void SkipPixel( sal_uInt32 nPixel ) 321 { 322 pData += (nPixel << 1) + nPixel; 323 } 324 virtual ColorData ReadPixel() 325 { 326 const ColorData c = RGB_COLORDATA( pData[2], pData[1], pData[0] ); 327 pData += 3; 328 return c; 329 } 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 345 virtual void StartLine( sal_uInt8* pLine ) 346 { 347 pData16 = (sal_uInt16*)pLine; 348 } 349 virtual void SkipPixel( sal_uInt32 nPixel ) 350 { 351 pData += nPixel; 352 } 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 } 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: 373 ImplPixelFormat8( const BitmapPalette& rPalette ) 374 : mrPalette( rPalette ) 375 { 376 } 377 virtual void SkipPixel( sal_uInt32 nPixel ) 378 { 379 pData += nPixel; 380 } 381 virtual ColorData ReadPixel() 382 { 383 return mrPalette[ *pData++ ].operator Color().GetColor(); 384 } 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: 400 ImplPixelFormat4( const BitmapPalette& rPalette ) 401 : mrPalette( rPalette ) 402 { 403 } 404 virtual void SkipPixel( sal_uInt32 nPixel ) 405 { 406 mnX += nPixel; 407 if( (nPixel & 1) ) 408 mnShift ^= 4; 409 } 410 virtual void StartLine( sal_uInt8* pLine ) 411 { 412 pData = pLine; 413 mnX = 0; 414 mnShift = 4; 415 } 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 } 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: 440 ImplPixelFormat1( const BitmapPalette& rPalette ) 441 : mrPalette( rPalette ) 442 { 443 } 444 virtual void SkipPixel( sal_uInt32 nPixel ) 445 { 446 mnX += nPixel; 447 } 448 virtual void StartLine( sal_uInt8* pLine ) 449 { 450 pData = pLine; 451 mnX = 0; 452 } 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 } 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 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 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 555 Size AquaSalBitmap::GetSize() const 556 { 557 return Size( mnWidth, mnHeight ); 558 } 559 560 // ------------------------------------------------------------------ 561 562 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 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 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 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 713 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 743 static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/) 744 { 745 rtl_freeMemory( const_cast<void*>(data) ); 746 } 747 748 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 float* 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 */ 794 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 **/ 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