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_canvas.hxx" 26 27 #include <vcl/canvastools.hxx> 28 29 #include <vcl/bitmap.hxx> 30 #include <vcl/bitmapex.hxx> 31 #include <vcl/bmpacc.hxx> 32 #include <tools/diagnose_ex.h> 33 34 #include "dx_impltools.hxx" 35 #include <basegfx/numeric/ftools.hxx> 36 37 #include <canvas/debug.hxx> 38 #include <canvas/verbosetrace.hxx> 39 40 #include <com/sun/star/lang/XServiceInfo.hpp> 41 #include <com/sun/star/rendering/XIntegerBitmap.hpp> 42 43 #include <boost/scoped_array.hpp> 44 45 #include "dx_vcltools.hxx" 46 47 using namespace ::com::sun::star; 48 49 namespace dxcanvas 50 { 51 namespace tools 52 { 53 namespace 54 { 55 /// Calc number of colors in given BitmapInfoHeader 56 sal_Int32 calcDIBColorCount( const BITMAPINFOHEADER& rBIH ) 57 { 58 if( rBIH.biSize != sizeof( BITMAPCOREHEADER ) ) 59 { 60 if( rBIH.biBitCount <= 8 ) 61 { 62 if( rBIH.biClrUsed ) 63 return rBIH.biClrUsed; 64 else 65 return 1L << rBIH.biBitCount; 66 } 67 } 68 else 69 { 70 BITMAPCOREHEADER* pCoreHeader = (BITMAPCOREHEADER*)&rBIH; 71 72 if( pCoreHeader->bcBitCount <= 8 ) 73 return 1L << pCoreHeader->bcBitCount; 74 } 75 76 return 0; // nothing known 77 } 78 79 /// Draw DI bits to given Graphics 80 bool drawDIBits( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics, 81 const void* hDIB ) 82 { 83 bool bRet( false ); 84 BitmapSharedPtr pBitmap; 85 86 const BITMAPINFO* pBI = (BITMAPINFO*)GlobalLock( (HGLOBAL)hDIB ); 87 88 if( pBI ) 89 { 90 const BITMAPINFOHEADER* pBIH = (BITMAPINFOHEADER*)pBI; 91 const BYTE* pBits = (BYTE*) pBI + *(DWORD*)pBI + 92 calcDIBColorCount( *pBIH ) * sizeof( RGBQUAD ); 93 94 // forward to outsourced GDI+ rendering method 95 // (header clashes) 96 bRet = tools::drawDIBits( rGraphics, *pBI, (void*)pBits ); 97 98 GlobalUnlock( (HGLOBAL)hDIB ); 99 } 100 101 return bRet; 102 } 103 104 /** Draw VCL bitmap to given Graphics 105 106 @param rBmp 107 Reference to bitmap. Might get modified, in such a way 108 that it will hold a DIB after a successful function call. 109 */ 110 bool drawVCLBitmap( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics, 111 ::Bitmap& rBmp ) 112 { 113 BitmapSystemData aBmpSysData; 114 115 if( !rBmp.GetSystemData( aBmpSysData ) || 116 !aBmpSysData.pDIB ) 117 { 118 // first of all, ensure that Bitmap contains a DIB, by 119 // acquiring a read access 120 BitmapReadAccess* pReadAcc = rBmp.AcquireReadAccess(); 121 122 // TODO(P2): Acquiring a read access can actually 123 // force a read from VRAM, thus, avoiding this 124 // step somehow will increase performance 125 // here. 126 if( pReadAcc ) 127 { 128 // try again: now, WinSalBitmap must have 129 // generated a DIB 130 if( rBmp.GetSystemData( aBmpSysData ) && 131 aBmpSysData.pDIB ) 132 { 133 return drawDIBits( rGraphics, 134 aBmpSysData.pDIB ); 135 } 136 137 rBmp.ReleaseAccess( pReadAcc ); 138 } 139 } 140 else 141 { 142 return drawDIBits( rGraphics, 143 aBmpSysData.pDIB ); 144 } 145 146 // failed to generate DIBits from vcl bitmap 147 return false; 148 } 149 150 /** Create a chunk of raw RGBA data GDI+ Bitmap from VCL BbitmapEX 151 */ 152 RawRGBABitmap bitmapFromVCLBitmapEx( const ::BitmapEx& rBmpEx ) 153 { 154 // TODO(P2): Avoid temporary bitmap generation, maybe 155 // even ensure that created DIBs are copied back to 156 // BmpEx (currently, every AcquireReadAccess() will 157 // make the local bitmap copy unique, effectively 158 // duplicating the memory used) 159 160 ENSURE_OR_THROW( rBmpEx.IsTransparent(), 161 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 162 "BmpEx not transparent" ); 163 164 // convert transparent bitmap to 32bit RGBA 165 // ======================================== 166 167 const ::Size aBmpSize( rBmpEx.GetSizePixel() ); 168 169 RawRGBABitmap aBmpData; 170 aBmpData.mnWidth = aBmpSize.Width(); 171 aBmpData.mnHeight = aBmpSize.Height(); 172 aBmpData.mpBitmapData.reset( new sal_uInt8[ 4*aBmpData.mnWidth*aBmpData.mnHeight ] ); 173 174 Bitmap aBitmap( rBmpEx.GetBitmap() ); 175 176 ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(), 177 aBitmap ); 178 179 const sal_Int32 nWidth( aBmpSize.Width() ); 180 const sal_Int32 nHeight( aBmpSize.Height() ); 181 182 ENSURE_OR_THROW( pReadAccess.get() != NULL, 183 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 184 "Unable to acquire read acces to bitmap" ); 185 186 if( rBmpEx.IsAlpha() ) 187 { 188 Bitmap aAlpha( rBmpEx.GetAlpha().GetBitmap() ); 189 190 ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.AcquireReadAccess(), 191 aAlpha ); 192 193 // By convention, the access buffer always has 194 // one of the following formats: 195 // 196 // BMP_FORMAT_1BIT_MSB_PAL 197 // BMP_FORMAT_4BIT_MSN_PAL 198 // BMP_FORMAT_8BIT_PAL 199 // BMP_FORMAT_16BIT_TC_LSB_MASK 200 // BMP_FORMAT_24BIT_TC_BGR 201 // BMP_FORMAT_32BIT_TC_MASK 202 // 203 // and is always BMP_FORMAT_BOTTOM_UP 204 // 205 // This is the way 206 // WinSalBitmap::AcquireBuffer() sets up the 207 // buffer 208 209 ENSURE_OR_THROW( pAlphaReadAccess.get() != NULL, 210 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 211 "Unable to acquire read acces to alpha" ); 212 213 ENSURE_OR_THROW( pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL || 214 pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK, 215 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 216 "Unsupported alpha scanline format" ); 217 218 BitmapColor aCol; 219 const sal_Int32 nWidth( aBmpSize.Width() ); 220 const sal_Int32 nHeight( aBmpSize.Height() ); 221 sal_uInt8* pCurrOutput( aBmpData.mpBitmapData.get() ); 222 int x, y; 223 224 for( y=0; y<nHeight; ++y ) 225 { 226 switch( pReadAccess->GetScanlineFormat() ) 227 { 228 case BMP_FORMAT_8BIT_PAL: 229 { 230 Scanline pScan = pReadAccess->GetScanline( y ); 231 Scanline pAScan = pAlphaReadAccess->GetScanline( y ); 232 233 for( x=0; x<nWidth; ++x ) 234 { 235 aCol = pReadAccess->GetPaletteColor( *pScan++ ); 236 237 *pCurrOutput++ = aCol.GetBlue(); 238 *pCurrOutput++ = aCol.GetGreen(); 239 *pCurrOutput++ = aCol.GetRed(); 240 241 // out notion of alpha is 242 // different from the rest 243 // of the world's 244 *pCurrOutput++ = 255 - (BYTE)*pAScan++; 245 } 246 } 247 break; 248 249 case BMP_FORMAT_24BIT_TC_BGR: 250 { 251 Scanline pScan = pReadAccess->GetScanline( y ); 252 Scanline pAScan = pAlphaReadAccess->GetScanline( y ); 253 254 for( x=0; x<nWidth; ++x ) 255 { 256 // store as RGBA 257 *pCurrOutput++ = *pScan++; 258 *pCurrOutput++ = *pScan++; 259 *pCurrOutput++ = *pScan++; 260 261 // out notion of alpha is 262 // different from the rest 263 // of the world's 264 *pCurrOutput++ = 255 - (BYTE)*pAScan++; 265 } 266 } 267 break; 268 269 // TODO(P2): Might be advantageous 270 // to hand-formulate the following 271 // formats, too. 272 case BMP_FORMAT_1BIT_MSB_PAL: 273 // FALLTHROUGH intended 274 case BMP_FORMAT_4BIT_MSN_PAL: 275 // FALLTHROUGH intended 276 case BMP_FORMAT_16BIT_TC_LSB_MASK: 277 // FALLTHROUGH intended 278 case BMP_FORMAT_32BIT_TC_MASK: 279 { 280 Scanline pAScan = pAlphaReadAccess->GetScanline( y ); 281 282 // using fallback for those 283 // seldom formats 284 for( x=0; x<nWidth; ++x ) 285 { 286 // yes. x and y are swapped on Get/SetPixel 287 aCol = pReadAccess->GetColor(y,x); 288 289 *pCurrOutput++ = aCol.GetBlue(); 290 *pCurrOutput++ = aCol.GetGreen(); 291 *pCurrOutput++ = aCol.GetRed(); 292 293 // out notion of alpha is 294 // different from the rest 295 // of the world's 296 *pCurrOutput++ = 255 - (BYTE)*pAScan++; 297 } 298 } 299 break; 300 301 case BMP_FORMAT_1BIT_LSB_PAL: 302 // FALLTHROUGH intended 303 case BMP_FORMAT_4BIT_LSN_PAL: 304 // FALLTHROUGH intended 305 case BMP_FORMAT_8BIT_TC_MASK: 306 // FALLTHROUGH intended 307 case BMP_FORMAT_24BIT_TC_RGB: 308 // FALLTHROUGH intended 309 case BMP_FORMAT_24BIT_TC_MASK: 310 // FALLTHROUGH intended 311 case BMP_FORMAT_16BIT_TC_MSB_MASK: 312 // FALLTHROUGH intended 313 case BMP_FORMAT_32BIT_TC_ABGR: 314 // FALLTHROUGH intended 315 case BMP_FORMAT_32BIT_TC_ARGB: 316 // FALLTHROUGH intended 317 case BMP_FORMAT_32BIT_TC_BGRA: 318 // FALLTHROUGH intended 319 case BMP_FORMAT_32BIT_TC_RGBA: 320 // FALLTHROUGH intended 321 default: 322 ENSURE_OR_THROW( false, 323 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 324 "Unexpected scanline format - has " 325 "WinSalBitmap::AcquireBuffer() changed?" ); 326 } 327 } 328 } 329 else 330 { 331 Bitmap aMask( rBmpEx.GetMask() ); 332 333 ScopedBitmapReadAccess pMaskReadAccess( aMask.AcquireReadAccess(), 334 aMask ); 335 336 // By convention, the access buffer always has 337 // one of the following formats: 338 // 339 // BMP_FORMAT_1BIT_MSB_PAL 340 // BMP_FORMAT_4BIT_MSN_PAL 341 // BMP_FORMAT_8BIT_PAL 342 // BMP_FORMAT_16BIT_TC_LSB_MASK 343 // BMP_FORMAT_24BIT_TC_BGR 344 // BMP_FORMAT_32BIT_TC_MASK 345 // 346 // and is always BMP_FORMAT_BOTTOM_UP 347 // 348 // This is the way 349 // WinSalBitmap::AcquireBuffer() sets up the 350 // buffer 351 352 ENSURE_OR_THROW( pMaskReadAccess.get() != NULL, 353 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 354 "Unable to acquire read acces to mask" ); 355 356 ENSURE_OR_THROW( pMaskReadAccess->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL, 357 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 358 "Unsupported mask scanline format" ); 359 360 BitmapColor aCol; 361 int nCurrBit; 362 const int nMask( 1L ); 363 const int nInitialBit(7); 364 sal_uInt8* pCurrOutput( aBmpData.mpBitmapData.get() ); 365 int x, y; 366 367 // mapping table, to get from mask index color to 368 // alpha value (which depends on the mask's palette) 369 sal_uInt8 aColorMap[2]; 370 371 const BitmapColor& rCol0( pMaskReadAccess->GetPaletteColor( 0 ) ); 372 const BitmapColor& rCol1( pMaskReadAccess->GetPaletteColor( 1 ) ); 373 374 // shortcut for true luminance calculation 375 // (assumes that palette is grey-level). Note the 376 // swapped the indices here, to account for the 377 // fact that VCL's notion of alpha is inverted to 378 // the rest of the world's. 379 aColorMap[0] = rCol1.GetRed(); 380 aColorMap[1] = rCol0.GetRed(); 381 382 for( y=0; y<nHeight; ++y ) 383 { 384 switch( pReadAccess->GetScanlineFormat() ) 385 { 386 case BMP_FORMAT_8BIT_PAL: 387 { 388 Scanline pScan = pReadAccess->GetScanline( y ); 389 Scanline pMScan = pMaskReadAccess->GetScanline( y ); 390 391 for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x ) 392 { 393 aCol = pReadAccess->GetPaletteColor( *pScan++ ); 394 395 *pCurrOutput++ = aCol.GetBlue(); 396 *pCurrOutput++ = aCol.GetGreen(); 397 *pCurrOutput++ = aCol.GetRed(); 398 399 *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ]; 400 nCurrBit = ((nCurrBit - 1) % 8L) & 7L; 401 } 402 } 403 break; 404 405 case BMP_FORMAT_24BIT_TC_BGR: 406 { 407 Scanline pScan = pReadAccess->GetScanline( y ); 408 Scanline pMScan = pMaskReadAccess->GetScanline( y ); 409 410 for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x ) 411 { 412 // store as RGBA 413 *pCurrOutput++ = *pScan++; 414 *pCurrOutput++ = *pScan++; 415 *pCurrOutput++ = *pScan++; 416 417 *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ]; 418 nCurrBit = ((nCurrBit - 1) % 8L) & 7L; 419 } 420 } 421 break; 422 423 // TODO(P2): Might be advantageous 424 // to hand-formulate the following 425 // formats, too. 426 case BMP_FORMAT_1BIT_MSB_PAL: 427 // FALLTHROUGH intended 428 case BMP_FORMAT_4BIT_MSN_PAL: 429 // FALLTHROUGH intended 430 case BMP_FORMAT_16BIT_TC_LSB_MASK: 431 // FALLTHROUGH intended 432 case BMP_FORMAT_32BIT_TC_MASK: 433 { 434 Scanline pMScan = pMaskReadAccess->GetScanline( y ); 435 436 // using fallback for those 437 // seldom formats 438 for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x ) 439 { 440 // yes. x and y are swapped on Get/SetPixel 441 aCol = pReadAccess->GetColor(y,x); 442 443 // store as RGBA 444 *pCurrOutput++ = aCol.GetBlue(); 445 *pCurrOutput++ = aCol.GetGreen(); 446 *pCurrOutput++ = aCol.GetRed(); 447 448 *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ]; 449 nCurrBit = ((nCurrBit - 1) % 8L) & 7L; 450 } 451 } 452 break; 453 454 case BMP_FORMAT_1BIT_LSB_PAL: 455 // FALLTHROUGH intended 456 case BMP_FORMAT_4BIT_LSN_PAL: 457 // FALLTHROUGH intended 458 case BMP_FORMAT_8BIT_TC_MASK: 459 // FALLTHROUGH intended 460 case BMP_FORMAT_24BIT_TC_RGB: 461 // FALLTHROUGH intended 462 case BMP_FORMAT_24BIT_TC_MASK: 463 // FALLTHROUGH intended 464 case BMP_FORMAT_16BIT_TC_MSB_MASK: 465 // FALLTHROUGH intended 466 case BMP_FORMAT_32BIT_TC_ABGR: 467 // FALLTHROUGH intended 468 case BMP_FORMAT_32BIT_TC_ARGB: 469 // FALLTHROUGH intended 470 case BMP_FORMAT_32BIT_TC_BGRA: 471 // FALLTHROUGH intended 472 case BMP_FORMAT_32BIT_TC_RGBA: 473 // FALLTHROUGH intended 474 default: 475 ENSURE_OR_THROW( false, 476 "::dxcanvas::tools::bitmapFromVCLBitmapEx(): " 477 "Unexpected scanline format - has " 478 "WinSalBitmap::AcquireBuffer() changed?" ); 479 } 480 } 481 } 482 483 return aBmpData; 484 } 485 486 bool drawVCLBitmapEx( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics, 487 const ::BitmapEx& rBmpEx ) 488 { 489 if( !rBmpEx.IsTransparent() ) 490 { 491 Bitmap aBmp( rBmpEx.GetBitmap() ); 492 return drawVCLBitmap( rGraphics, aBmp ); 493 } 494 else 495 { 496 return drawRGBABits( rGraphics, 497 bitmapFromVCLBitmapEx( rBmpEx ) ); 498 } 499 } 500 } 501 502 bool drawVCLBitmapFromXBitmap( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics, 503 const uno::Reference< rendering::XBitmap >& xBitmap ) 504 { 505 // TODO(F2): add support for floating point bitmap formats 506 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp( 507 xBitmap, uno::UNO_QUERY ); 508 509 if( !xIntBmp.is() ) 510 return false; 511 512 ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap( xIntBmp ); 513 if( !aBmpEx ) 514 return false; 515 516 return drawVCLBitmapEx( rGraphics, aBmpEx ); 517 } 518 } 519 } 520