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_canvas.hxx" 30 31 #include <canvas/debug.hxx> 32 #include <tools/diagnose_ex.h> 33 34 #include <com/sun/star/util/Endianness.hpp> 35 36 #include <rtl/logfile.hxx> 37 #include <rtl/math.hxx> 38 39 #include <tools/poly.hxx> 40 #include <vcl/window.hxx> 41 #include <vcl/bitmapex.hxx> 42 #include <vcl/bmpacc.hxx> 43 #include <vcl/canvastools.hxx> 44 45 #include <basegfx/matrix/b2dhommatrix.hxx> 46 #include <basegfx/point/b2dpoint.hxx> 47 #include <basegfx/tools/canvastools.hxx> 48 #include <basegfx/numeric/ftools.hxx> 49 50 #include <canvas/canvastools.hxx> 51 52 #include "canvasbitmap.hxx" 53 #include "canvasbitmaphelper.hxx" 54 55 56 using namespace ::com::sun::star; 57 58 namespace vclcanvas 59 { 60 CanvasBitmapHelper::CanvasBitmapHelper() : 61 mpBackBuffer(), 62 mpOutDevReference() 63 { 64 } 65 66 void CanvasBitmapHelper::setBitmap( const BitmapEx& rBitmap ) 67 { 68 ENSURE_OR_THROW( mpOutDev, 69 "Invalid reference device" ); 70 71 mpBackBuffer.reset( new BitmapBackBuffer( rBitmap, 72 mpOutDev->getOutDev() ) ); 73 74 // tell canvas helper about the new target OutDev (don't 75 // protect state, it's our own VirDev, anyways) 76 setOutDev( mpBackBuffer, false ); 77 } 78 79 void CanvasBitmapHelper::init( const BitmapEx& rBitmap, 80 rendering::XGraphicDevice& rDevice, 81 const OutDevProviderSharedPtr& rOutDevReference ) 82 { 83 mpOutDevReference = rOutDevReference; 84 mpBackBuffer.reset( new BitmapBackBuffer( rBitmap, rOutDevReference->getOutDev() )); 85 86 // forward new settings to base class (ref device, output 87 // surface, no protection (own backbuffer), alpha depends on 88 // whether BmpEx is transparent or not) 89 CanvasHelper::init( rDevice, 90 mpBackBuffer, 91 false, 92 rBitmap.IsTransparent() ); 93 } 94 95 void CanvasBitmapHelper::disposing() 96 { 97 mpBackBuffer.reset(); 98 mpOutDevReference.reset(); 99 100 // forward to base class 101 CanvasHelper::disposing(); 102 } 103 104 geometry::IntegerSize2D CanvasBitmapHelper::getSize() 105 { 106 if( !mpBackBuffer ) 107 return geometry::IntegerSize2D(); 108 109 return ::vcl::unotools::integerSize2DFromSize( mpBackBuffer->getBitmapSizePixel() ); 110 } 111 112 void CanvasBitmapHelper::clear() 113 { 114 // are we disposed? 115 if( mpBackBuffer ) 116 mpBackBuffer->clear(); // alpha vdev needs special treatment 117 } 118 119 uno::Reference< rendering::XBitmap > CanvasBitmapHelper::getScaledBitmap( const geometry::RealSize2D& newSize, 120 sal_Bool beFast ) 121 { 122 ENSURE_OR_THROW( mpDevice, 123 "disposed CanvasHelper" ); 124 125 RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::getScaledBitmap()" ); 126 127 if( !mpBackBuffer || mpDevice ) 128 return uno::Reference< rendering::XBitmap >(); // we're disposed 129 130 BitmapEx aRes( mpBackBuffer->getBitmapReference() ); 131 132 aRes.Scale( ::vcl::unotools::sizeFromRealSize2D(newSize), 133 beFast ? BMP_SCALE_FAST : BMP_SCALE_INTERPOLATE ); 134 135 return uno::Reference< rendering::XBitmap >( 136 new CanvasBitmap( aRes, *mpDevice, mpOutDevReference ) ); 137 } 138 139 uno::Sequence< sal_Int8 > CanvasBitmapHelper::getData( rendering::IntegerBitmapLayout& rLayout, 140 const geometry::IntegerRectangle2D& rect ) 141 { 142 RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::getData()" ); 143 144 if( !mpBackBuffer ) 145 return uno::Sequence< sal_Int8 >(); // we're disposed 146 147 rLayout = getMemoryLayout(); 148 Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() ); 149 Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() ); 150 151 ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(), 152 aBitmap ); 153 ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.IsEmpty() ? 154 (BitmapReadAccess*)NULL : aAlpha.AcquireReadAccess(), 155 aAlpha ); 156 157 ENSURE_OR_THROW( pReadAccess.get() != NULL, 158 "Could not acquire read access to bitmap" ); 159 160 // TODO(F1): Support more formats. 161 const Size aBmpSize( aBitmap.GetSizePixel() ); 162 163 rLayout.ScanLines = aBmpSize.Height(); 164 rLayout.ScanLineBytes = aBmpSize.Width()*4; 165 rLayout.ScanLineStride = rLayout.ScanLineBytes; 166 167 // for the time being, always return as BGRA 168 uno::Sequence< sal_Int8 > aRes( 4*aBmpSize.Width()*aBmpSize.Height() ); 169 sal_Int8* pRes = aRes.getArray(); 170 171 int nCurrPos(0); 172 for( int y=rect.Y1; 173 y<aBmpSize.Height() && y<rect.Y2; 174 ++y ) 175 { 176 if( pAlphaReadAccess.get() != NULL ) 177 { 178 for( int x=rect.X1; 179 x<aBmpSize.Width() && x<rect.X2; 180 ++x ) 181 { 182 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); 183 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); 184 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); 185 pRes[ nCurrPos++ ] = pAlphaReadAccess->GetPixel( y, x ).GetIndex(); 186 } 187 } 188 else 189 { 190 for( int x=rect.X1; 191 x<aBmpSize.Width() && x<rect.X2; 192 ++x ) 193 { 194 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); 195 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); 196 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); 197 pRes[ nCurrPos++ ] = sal_uInt8(255); 198 } 199 } 200 } 201 202 return aRes; 203 } 204 205 void CanvasBitmapHelper::setData( const uno::Sequence< sal_Int8 >& data, 206 const rendering::IntegerBitmapLayout& rLayout, 207 const geometry::IntegerRectangle2D& rect ) 208 { 209 RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::setData()" ); 210 211 if( !mpBackBuffer ) 212 return; // we're disposed 213 214 const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() ); 215 ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride || 216 aRefLayout.ColorSpace != rLayout.ColorSpace || 217 aRefLayout.Palette != rLayout.Palette || 218 aRefLayout.IsMsbFirst != rLayout.IsMsbFirst, 219 "Mismatching memory layout" ); 220 221 // retrieve local copies from the BitmapEx, which are later 222 // stored back. Unfortunately, the BitmapEx does not permit 223 // in-place modifications, as they are necessary here. 224 Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() ); 225 Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() ); 226 227 bool bCopyBack( false ); // only copy something back, if we 228 // actually changed a pixel 229 230 { 231 ScopedBitmapWriteAccess pWriteAccess( aBitmap.AcquireWriteAccess(), 232 aBitmap ); 233 ScopedBitmapWriteAccess pAlphaWriteAccess( aAlpha.IsEmpty() ? 234 (BitmapWriteAccess*)NULL : aAlpha.AcquireWriteAccess(), 235 aAlpha ); 236 237 if( pAlphaWriteAccess.get() ) 238 { 239 DBG_ASSERT( pAlphaWriteAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL || 240 pAlphaWriteAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK, 241 "non-8bit alpha not supported!" ); 242 } 243 244 ENSURE_OR_THROW( pWriteAccess.get() != NULL, 245 "Could not acquire write access to bitmap" ); 246 247 // TODO(F1): Support more formats. 248 const Size aBmpSize( aBitmap.GetSizePixel() ); 249 250 // for the time being, always read as BGRA 251 int x, y, nCurrPos(0); 252 for( y=rect.Y1; 253 y<aBmpSize.Height() && y<rect.Y2; 254 ++y ) 255 { 256 if( pAlphaWriteAccess.get() != NULL ) 257 { 258 switch( pWriteAccess->GetScanlineFormat() ) 259 { 260 case BMP_FORMAT_8BIT_PAL: 261 { 262 Scanline pScan = pWriteAccess->GetScanline( y ); 263 Scanline pAScan = pAlphaWriteAccess->GetScanline( y ); 264 265 for( x=rect.X1; 266 x<aBmpSize.Width() && x<rect.X2; 267 ++x ) 268 { 269 *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex( 270 BitmapColor( data[ nCurrPos ], 271 data[ nCurrPos+1 ], 272 data[ nCurrPos+2 ] ) ); 273 274 nCurrPos += 3; 275 276 // cast to unsigned byte, for correct subtraction result 277 *pAScan++ = static_cast<sal_uInt8>(255 - 278 static_cast<sal_uInt8>(data[ nCurrPos++ ])); 279 } 280 } 281 break; 282 283 case BMP_FORMAT_24BIT_TC_BGR: 284 { 285 Scanline pScan = pWriteAccess->GetScanline( y ); 286 Scanline pAScan = pAlphaWriteAccess->GetScanline( y ); 287 288 for( x=rect.X1; 289 x<aBmpSize.Width() && x<rect.X2; 290 ++x ) 291 { 292 *pScan++ = data[ nCurrPos+2 ]; 293 *pScan++ = data[ nCurrPos+1 ]; 294 *pScan++ = data[ nCurrPos ]; 295 296 nCurrPos += 3; 297 298 // cast to unsigned byte, for correct subtraction result 299 *pAScan++ = static_cast<sal_uInt8>(255 - 300 static_cast<sal_uInt8>(data[ nCurrPos++ ])); 301 } 302 } 303 break; 304 305 case BMP_FORMAT_24BIT_TC_RGB: 306 { 307 Scanline pScan = pWriteAccess->GetScanline( y ); 308 Scanline pAScan = pAlphaWriteAccess->GetScanline( y ); 309 310 for( x=rect.X1; 311 x<aBmpSize.Width() && x<rect.X2; 312 ++x ) 313 { 314 *pScan++ = data[ nCurrPos ]; 315 *pScan++ = data[ nCurrPos+1 ]; 316 *pScan++ = data[ nCurrPos+2 ]; 317 318 nCurrPos += 3; 319 320 // cast to unsigned byte, for correct subtraction result 321 *pAScan++ = static_cast<sal_uInt8>(255 - 322 static_cast<sal_uInt8>(data[ nCurrPos++ ])); 323 } 324 } 325 break; 326 327 default: 328 { 329 for( x=rect.X1; 330 x<aBmpSize.Width() && x<rect.X2; 331 ++x ) 332 { 333 pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos ], 334 data[ nCurrPos+1 ], 335 data[ nCurrPos+2 ] ) ); 336 nCurrPos += 3; 337 338 // cast to unsigned byte, for correct subtraction result 339 pAlphaWriteAccess->SetPixel( y, x, 340 BitmapColor( 341 static_cast<sal_uInt8>(255 - 342 static_cast<sal_uInt8>(data[ nCurrPos++ ])) ) ); 343 } 344 } 345 break; 346 } 347 } 348 else 349 { 350 // TODO(Q3): This is copy'n'pasted from 351 // canvashelper.cxx, unify! 352 switch( pWriteAccess->GetScanlineFormat() ) 353 { 354 case BMP_FORMAT_8BIT_PAL: 355 { 356 Scanline pScan = pWriteAccess->GetScanline( y ); 357 358 for( x=rect.X1; 359 x<aBmpSize.Width() && x<rect.X2; 360 ++x ) 361 { 362 *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex( 363 BitmapColor( data[ nCurrPos ], 364 data[ nCurrPos+1 ], 365 data[ nCurrPos+2 ] ) ); 366 367 nCurrPos += 4; // skip three colors, _plus_ alpha 368 } 369 } 370 break; 371 372 case BMP_FORMAT_24BIT_TC_BGR: 373 { 374 Scanline pScan = pWriteAccess->GetScanline( y ); 375 376 for( x=rect.X1; 377 x<aBmpSize.Width() && x<rect.X2; 378 ++x ) 379 { 380 *pScan++ = data[ nCurrPos+2 ]; 381 *pScan++ = data[ nCurrPos+1 ]; 382 *pScan++ = data[ nCurrPos ]; 383 384 nCurrPos += 4; // skip three colors, _plus_ alpha 385 } 386 } 387 break; 388 389 case BMP_FORMAT_24BIT_TC_RGB: 390 { 391 Scanline pScan = pWriteAccess->GetScanline( y ); 392 393 for( x=rect.X1; 394 x<aBmpSize.Width() && x<rect.X2; 395 ++x ) 396 { 397 *pScan++ = data[ nCurrPos ]; 398 *pScan++ = data[ nCurrPos+1 ]; 399 *pScan++ = data[ nCurrPos+2 ]; 400 401 nCurrPos += 4; // skip three colors, _plus_ alpha 402 } 403 } 404 break; 405 406 default: 407 { 408 for( x=rect.X1; 409 x<aBmpSize.Width() && x<rect.X2; 410 ++x ) 411 { 412 pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos ], 413 data[ nCurrPos+1 ], 414 data[ nCurrPos+2 ] ) ); 415 nCurrPos += 4; // skip three colors, _plus_ alpha 416 } 417 } 418 break; 419 } 420 } 421 422 bCopyBack = true; 423 } 424 } 425 426 // copy back only here, since the BitmapAccessors must be 427 // destroyed beforehand 428 if( bCopyBack ) 429 { 430 if( aAlpha.IsEmpty() ) 431 setBitmap( BitmapEx( aBitmap ) ); 432 else 433 setBitmap( BitmapEx( aBitmap, 434 AlphaMask( aAlpha ) ) ); 435 } 436 } 437 438 void CanvasBitmapHelper::setPixel( const uno::Sequence< sal_Int8 >& color, 439 const rendering::IntegerBitmapLayout& rLayout, 440 const geometry::IntegerPoint2D& pos ) 441 { 442 RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::setPixel()" ); 443 444 if( !mpBackBuffer ) 445 return; // we're disposed 446 447 const Size aBmpSize( mpBackBuffer->getBitmapReference().GetSizePixel() ); 448 449 ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(), 450 "X coordinate out of bounds" ); 451 ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(), 452 "Y coordinate out of bounds" ); 453 ENSURE_ARG_OR_THROW( color.getLength() > 3, 454 "not enough color components" ); 455 456 const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() ); 457 ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride || 458 aRefLayout.ColorSpace != rLayout.ColorSpace || 459 aRefLayout.Palette != rLayout.Palette || 460 aRefLayout.IsMsbFirst != rLayout.IsMsbFirst, 461 "Mismatching memory layout" ); 462 463 // retrieve local copies from the BitmapEx, which are later 464 // stored back. Unfortunately, the BitmapEx does not permit 465 // in-place modifications, as they are necessary here. 466 Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() ); 467 Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() ); 468 469 bool bCopyBack( false ); // only copy something back, if we 470 // actually changed a pixel 471 472 { 473 ScopedBitmapWriteAccess pWriteAccess( aBitmap.AcquireWriteAccess(), 474 aBitmap ); 475 ScopedBitmapWriteAccess pAlphaWriteAccess( aAlpha.IsEmpty() ? 476 (BitmapWriteAccess*)NULL : aAlpha.AcquireWriteAccess(), 477 aAlpha ); 478 479 ENSURE_OR_THROW( pWriteAccess.get() != NULL, 480 "Could not acquire write access to bitmap" ); 481 482 pWriteAccess->SetPixel( pos.Y, pos.X, BitmapColor( color[ 0 ], 483 color[ 1 ], 484 color[ 2 ] ) ); 485 486 if( pAlphaWriteAccess.get() != NULL ) 487 pAlphaWriteAccess->SetPixel( pos.Y, pos.X, BitmapColor( 255 - color[ 3 ] ) ); 488 489 bCopyBack = true; 490 } 491 492 // copy back only here, since the BitmapAccessors must be 493 // destroyed beforehand 494 if( bCopyBack ) 495 { 496 if( aAlpha.IsEmpty() ) 497 setBitmap( BitmapEx( aBitmap ) ); 498 else 499 setBitmap( BitmapEx( aBitmap, 500 AlphaMask( aAlpha ) ) ); 501 } 502 } 503 504 uno::Sequence< sal_Int8 > CanvasBitmapHelper::getPixel( rendering::IntegerBitmapLayout& rLayout, 505 const geometry::IntegerPoint2D& pos ) 506 { 507 RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::getPixel()" ); 508 509 if( !mpBackBuffer ) 510 return uno::Sequence< sal_Int8 >(); // we're disposed 511 512 rLayout = getMemoryLayout(); 513 rLayout.ScanLines = 1; 514 rLayout.ScanLineBytes = 4; 515 rLayout.ScanLineStride = rLayout.ScanLineBytes; 516 517 const Size aBmpSize( mpBackBuffer->getBitmapReference().GetSizePixel() ); 518 519 ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(), 520 "X coordinate out of bounds" ); 521 ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(), 522 "Y coordinate out of bounds" ); 523 524 Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() ); 525 Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() ); 526 527 ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(), 528 aBitmap ); 529 ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.IsEmpty() ? 530 (BitmapReadAccess*)NULL : aAlpha.AcquireReadAccess(), 531 aAlpha ); 532 ENSURE_OR_THROW( pReadAccess.get() != NULL, 533 "Could not acquire read access to bitmap" ); 534 535 uno::Sequence< sal_Int8 > aRes( 4 ); 536 sal_Int8* pRes = aRes.getArray(); 537 538 const BitmapColor aColor( pReadAccess->GetColor( pos.Y, pos.X ) ); 539 pRes[ 0 ] = aColor.GetRed(); 540 pRes[ 1 ] = aColor.GetGreen(); 541 pRes[ 2 ] = aColor.GetBlue(); 542 543 if( pAlphaReadAccess.get() != NULL ) 544 pRes[ 3 ] = pAlphaReadAccess->GetPixel( pos.Y, pos.X ).GetIndex(); 545 else 546 pRes[ 3 ] = sal_uInt8(255); 547 548 return aRes; 549 } 550 551 rendering::IntegerBitmapLayout CanvasBitmapHelper::getMemoryLayout() 552 { 553 if( !mpOutDev.get() ) 554 return rendering::IntegerBitmapLayout(); // we're disposed 555 556 return ::canvas::tools::getStdMemoryLayout(getSize()); 557 } 558 559 BitmapEx CanvasBitmapHelper::getBitmap() const 560 { 561 if( !mpBackBuffer ) 562 return BitmapEx(); // we're disposed 563 else 564 return mpBackBuffer->getBitmapReference(); 565 } 566 567 } 568