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