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