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 <rtl/math.hxx> 35 #include <rtl/logfile.hxx> 36 37 #include <com/sun/star/geometry/RealSize2D.hpp> 38 #include <com/sun/star/geometry/RealPoint2D.hpp> 39 #include <com/sun/star/geometry/RealRectangle2D.hpp> 40 #include <com/sun/star/rendering/RenderState.hpp> 41 #include <com/sun/star/rendering/XCanvas.hpp> 42 #include <com/sun/star/rendering/XBitmap.hpp> 43 #include <com/sun/star/rendering/XPolyPolygon2D.hpp> 44 #include <com/sun/star/geometry/RealBezierSegment2D.hpp> 45 #include <com/sun/star/rendering/XIntegerBitmap.hpp> 46 47 #include <vcl/salbtype.hxx> 48 #include <vcl/bmpacc.hxx> 49 #include <vcl/bitmapex.hxx> 50 #include <vcl/metric.hxx> 51 #include <vcl/canvastools.hxx> 52 53 #include <basegfx/point/b2dpoint.hxx> 54 #include <basegfx/tuple/b2dtuple.hxx> 55 #include <basegfx/polygon/b2dpolygontools.hxx> 56 #include <basegfx/range/b2drectangle.hxx> 57 #include <basegfx/matrix/b2dhommatrix.hxx> 58 #include <basegfx/tools/canvastools.hxx> 59 #include <basegfx/numeric/ftools.hxx> 60 61 #include <canvas/canvastools.hxx> 62 63 #include "impltools.hxx" 64 #include "canvasbitmap.hxx" 65 66 #include <numeric> 67 68 69 using namespace ::com::sun::star; 70 71 namespace vclcanvas 72 { 73 namespace tools 74 { 75 ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap ) 76 { 77 // TODO(F3): CanvasCustomSprite should also be tunnelled 78 // through (also implements XIntegerBitmap interface) 79 CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() ); 80 81 if( pBitmapImpl ) 82 { 83 return pBitmapImpl->getBitmap(); 84 } 85 else 86 { 87 SpriteCanvas* pCanvasImpl = dynamic_cast< SpriteCanvas* >( xBitmap.get() ); 88 if( pCanvasImpl && pCanvasImpl->getBackBuffer() ) 89 { 90 // TODO(F3): mind the plain Canvas impl. Consolidate with CWS canvas05 91 const ::OutputDevice& rDev( pCanvasImpl->getBackBuffer()->getOutDev() ); 92 const ::Point aEmptyPoint; 93 return rDev.GetBitmapEx( aEmptyPoint, 94 rDev.GetOutputSizePixel() ); 95 } 96 97 // TODO(F2): add support for floating point bitmap formats 98 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp( 99 xBitmap, uno::UNO_QUERY_THROW ); 100 101 ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap( xIntBmp ); 102 if( !!aBmpEx ) 103 return aBmpEx; 104 105 // TODO(F1): extract pixel from XBitmap interface 106 ENSURE_OR_THROW( false, 107 "bitmapExFromXBitmap(): could not extract bitmap" ); 108 } 109 110 return ::BitmapEx(); 111 } 112 113 bool setupFontTransform( ::Point& o_rPoint, 114 ::Font& io_rVCLFont, 115 const rendering::ViewState& rViewState, 116 const rendering::RenderState& rRenderState, 117 ::OutputDevice& rOutDev ) 118 { 119 ::basegfx::B2DHomMatrix aMatrix; 120 121 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, 122 rViewState, 123 rRenderState); 124 125 ::basegfx::B2DTuple aScale; 126 ::basegfx::B2DTuple aTranslate; 127 double nRotate, nShearX; 128 129 aMatrix.decompose( aScale, aTranslate, nRotate, nShearX ); 130 131 // #i72417# detecting the 180 degree rotation case manually here. 132 if( aScale.getX() < 0.0 && 133 aScale.getY() < 0.0 && 134 basegfx::fTools::equalZero(nRotate) ) 135 { 136 aScale *= -1.0; 137 nRotate += M_PI; 138 } 139 140 // query font metric _before_ tampering with width and height 141 if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) ) 142 { 143 // retrieve true font width 144 const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetWidth() ); 145 146 const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) ); 147 148 if( !nScaledFontWidth ) 149 { 150 // scale is smaller than one pixel - disable text 151 // output altogether 152 return false; 153 } 154 155 io_rVCLFont.SetWidth( nScaledFontWidth ); 156 } 157 158 if( !::rtl::math::approxEqual(aScale.getY(), 1.0) ) 159 { 160 const sal_Int32 nFontHeight( io_rVCLFont.GetHeight() ); 161 io_rVCLFont.SetHeight( ::basegfx::fround(nFontHeight * aScale.getY()) ); 162 } 163 164 io_rVCLFont.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate, 2*M_PI)*(1800.0/M_PI)) ) ); 165 166 // TODO(F2): Missing functionality in VCL: shearing 167 o_rPoint.X() = ::basegfx::fround(aTranslate.getX()); 168 o_rPoint.Y() = ::basegfx::fround(aTranslate.getY()); 169 170 return true; 171 } 172 173 bool isRectangle( const PolyPolygon& rPolyPoly ) 174 { 175 // exclude some cheap cases first 176 if( rPolyPoly.Count() != 1 ) 177 return false; 178 179 const ::Polygon& rPoly( rPolyPoly[0] ); 180 181 sal_uInt16 nCount( rPoly.GetSize() ); 182 if( nCount < 4 ) 183 return false; 184 185 // delegate to basegfx 186 return ::basegfx::tools::isRectangle( rPoly.getB2DPolygon() ); 187 } 188 189 190 // VCL-Canvas related 191 //--------------------------------------------------------------------- 192 193 ::Point mapRealPoint2D( const geometry::RealPoint2D& rPoint, 194 const rendering::ViewState& rViewState, 195 const rendering::RenderState& rRenderState ) 196 { 197 ::basegfx::B2DPoint aPoint( ::basegfx::unotools::b2DPointFromRealPoint2D(rPoint) ); 198 199 ::basegfx::B2DHomMatrix aMatrix; 200 aPoint *= ::canvas::tools::mergeViewAndRenderTransform(aMatrix, 201 rViewState, 202 rRenderState); 203 204 return ::vcl::unotools::pointFromB2DPoint( aPoint ); 205 } 206 207 ::PolyPolygon mapPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly, 208 const rendering::ViewState& rViewState, 209 const rendering::RenderState& rRenderState ) 210 { 211 ::basegfx::B2DHomMatrix aMatrix; 212 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, 213 rViewState, 214 rRenderState); 215 216 ::basegfx::B2DPolyPolygon aTemp( rPoly ); 217 218 aTemp.transform( aMatrix ); 219 220 return ::PolyPolygon( aTemp ); 221 } 222 223 ::BitmapEx transformBitmap( const BitmapEx& rBitmap, 224 const ::basegfx::B2DHomMatrix& rTransform, 225 const uno::Sequence< double >& rDeviceColor, 226 ModulationMode eModulationMode ) 227 { 228 RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::tools::transformBitmap()" ); 229 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::vclcanvas::tools::transformBitmap: 0x%X", &rBitmap ); 230 231 // calc transformation and size of bitmap to be 232 // generated. Note, that the translational components are 233 // deleted from the transformation; this can be handled by 234 // an offset when painting the bitmap 235 const Size aBmpSize( rBitmap.GetSizePixel() ); 236 ::basegfx::B2DRectangle aDestRect; 237 238 bool bCopyBack( false ); 239 240 // calc effective transformation for bitmap 241 const ::basegfx::B2DRectangle aSrcRect( 0, 0, 242 aBmpSize.Width(), 243 aBmpSize.Height() ); 244 ::canvas::tools::calcTransformedRectBounds( aDestRect, 245 aSrcRect, 246 rTransform ); 247 248 // re-center bitmap, such that it's left, top border is 249 // aligned with (0,0). The method takes the given 250 // rectangle, and calculates a transformation that maps 251 // this rectangle unscaled to the origin. 252 ::basegfx::B2DHomMatrix aLocalTransform; 253 ::canvas::tools::calcRectToOriginTransform( aLocalTransform, 254 aSrcRect, 255 rTransform ); 256 257 const bool bModulateColors( eModulationMode == MODULATE_WITH_DEVICECOLOR && 258 rDeviceColor.getLength() > 2 ); 259 const double nRedModulation( bModulateColors ? rDeviceColor[0] : 1.0 ); 260 const double nGreenModulation( bModulateColors ? rDeviceColor[1] : 1.0 ); 261 const double nBlueModulation( bModulateColors ? rDeviceColor[2] : 1.0 ); 262 const double nAlphaModulation( bModulateColors && rDeviceColor.getLength() > 3 ? 263 rDeviceColor[3] : 1.0 ); 264 265 Bitmap aSrcBitmap( rBitmap.GetBitmap() ); 266 Bitmap aSrcAlpha; 267 268 // differentiate mask and alpha channel (on-off 269 // vs. multi-level transparency) 270 if( rBitmap.IsTransparent() ) 271 { 272 if( rBitmap.IsAlpha() ) 273 aSrcAlpha = rBitmap.GetAlpha().GetBitmap(); 274 else 275 aSrcAlpha = rBitmap.GetMask(); 276 } 277 278 ScopedBitmapReadAccess pReadAccess( aSrcBitmap.AcquireReadAccess(), 279 aSrcBitmap ); 280 ScopedBitmapReadAccess pAlphaReadAccess( rBitmap.IsTransparent() ? 281 aSrcAlpha.AcquireReadAccess() : 282 (BitmapReadAccess*)NULL, 283 aSrcAlpha ); 284 285 if( pReadAccess.get() == NULL || 286 (pAlphaReadAccess.get() == NULL && rBitmap.IsTransparent()) ) 287 { 288 // TODO(E2): Error handling! 289 ENSURE_OR_THROW( false, 290 "transformBitmap(): could not access source bitmap" ); 291 } 292 293 // mapping table, to translate pAlphaReadAccess' pixel 294 // values into destination alpha values (needed e.g. for 295 // paletted 1-bit masks). 296 sal_uInt8 aAlphaMap[256]; 297 298 if( rBitmap.IsTransparent() ) 299 { 300 if( rBitmap.IsAlpha() ) 301 { 302 // source already has alpha channel - 1:1 mapping, 303 // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255. 304 ::std::iota( aAlphaMap, &aAlphaMap[256], 0 ); 305 } 306 else 307 { 308 // mask transparency - determine used palette colors 309 const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) ); 310 const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) ); 311 312 // shortcut for true luminance calculation 313 // (assumes that palette is grey-level) 314 aAlphaMap[0] = rCol0.GetRed(); 315 aAlphaMap[1] = rCol1.GetRed(); 316 } 317 } 318 // else: mapping table is not used 319 320 const Size aDestBmpSize( ::basegfx::fround( aDestRect.getWidth() ), 321 ::basegfx::fround( aDestRect.getHeight() ) ); 322 323 if( aDestBmpSize.Width() == 0 || aDestBmpSize.Height() == 0 ) 324 return BitmapEx(); 325 326 Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() ); 327 Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() ); 328 329 { 330 // just to be on the safe side: let the 331 // ScopedAccessors get destructed before 332 // copy-constructing the resulting bitmap. This will 333 // rule out the possibility that cached accessor data 334 // is not yet written back. 335 ScopedBitmapWriteAccess pWriteAccess( aDstBitmap.AcquireWriteAccess(), 336 aDstBitmap ); 337 ScopedBitmapWriteAccess pAlphaWriteAccess( aDstAlpha.AcquireWriteAccess(), 338 aDstAlpha ); 339 340 341 if( pWriteAccess.get() != NULL && 342 pAlphaWriteAccess.get() != NULL && 343 rTransform.isInvertible() ) 344 { 345 // we're doing inverse mapping here, i.e. mapping 346 // points from the destination bitmap back to the 347 // source 348 ::basegfx::B2DHomMatrix aTransform( aLocalTransform ); 349 aTransform.invert(); 350 351 // for the time being, always read as ARGB 352 for( int y=0; y<aDestBmpSize.Height(); ++y ) 353 { 354 if( bModulateColors ) 355 { 356 // TODO(P2): Have different branches for 357 // alpha-only modulation (color 358 // modulations eq. 1.0) 359 360 // modulate all color channels with given 361 // values 362 363 // differentiate mask and alpha channel (on-off 364 // vs. multi-level transparency) 365 if( rBitmap.IsTransparent() ) 366 { 367 // Handling alpha and mask just the same... 368 for( int x=0; x<aDestBmpSize.Width(); ++x ) 369 { 370 ::basegfx::B2DPoint aPoint(x,y); 371 aPoint *= aTransform; 372 373 const int nSrcX( ::basegfx::fround( aPoint.getX() ) ); 374 const int nSrcY( ::basegfx::fround( aPoint.getY() ) ); 375 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || 376 nSrcY < 0 || nSrcY >= aBmpSize.Height() ) 377 { 378 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) ); 379 } 380 else 381 { 382 // modulate alpha with 383 // nAlphaModulation. This is a 384 // little bit verbose, formula 385 // is 255 - (255-pixAlpha)*nAlphaModulation 386 // (invert 'alpha' pixel value, 387 // to get the standard alpha 388 // channel behaviour) 389 pAlphaWriteAccess->SetPixel( y, x, 390 BitmapColor( 391 255U - 392 static_cast<sal_uInt8>( 393 nAlphaModulation* 394 (255U 395 - aAlphaMap[ pAlphaReadAccess->GetPixel( 396 nSrcY, 397 nSrcX ).GetIndex() ] ) + .5 ) ) ); 398 399 BitmapColor aColor( pReadAccess->GetPixel( nSrcY, 400 nSrcX ) ); 401 402 aColor.SetRed( 403 static_cast<sal_uInt8>( 404 nRedModulation * 405 aColor.GetRed() + .5 )); 406 aColor.SetGreen( 407 static_cast<sal_uInt8>( 408 nGreenModulation * 409 aColor.GetGreen() + .5 )); 410 aColor.SetBlue( 411 static_cast<sal_uInt8>( 412 nBlueModulation * 413 aColor.GetBlue() + .5 )); 414 415 pWriteAccess->SetPixel( y, x, 416 aColor ); 417 } 418 } 419 } 420 else 421 { 422 for( int x=0; x<aDestBmpSize.Width(); ++x ) 423 { 424 ::basegfx::B2DPoint aPoint(x,y); 425 aPoint *= aTransform; 426 427 const int nSrcX( ::basegfx::fround( aPoint.getX() ) ); 428 const int nSrcY( ::basegfx::fround( aPoint.getY() ) ); 429 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || 430 nSrcY < 0 || nSrcY >= aBmpSize.Height() ) 431 { 432 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) ); 433 } 434 else 435 { 436 // modulate alpha with 437 // nAlphaModulation. This is a 438 // little bit verbose, formula 439 // is 255 - 255*nAlphaModulation 440 // (invert 'alpha' pixel value, 441 // to get the standard alpha 442 // channel behaviour) 443 pAlphaWriteAccess->SetPixel( y, x, 444 BitmapColor( 445 255U - 446 static_cast<sal_uInt8>( 447 nAlphaModulation*255.0 448 + .5 ) ) ); 449 450 BitmapColor aColor( pReadAccess->GetPixel( nSrcY, 451 nSrcX ) ); 452 453 aColor.SetRed( 454 static_cast<sal_uInt8>( 455 nRedModulation * 456 aColor.GetRed() + .5 )); 457 aColor.SetGreen( 458 static_cast<sal_uInt8>( 459 nGreenModulation * 460 aColor.GetGreen() + .5 )); 461 aColor.SetBlue( 462 static_cast<sal_uInt8>( 463 nBlueModulation * 464 aColor.GetBlue() + .5 )); 465 466 pWriteAccess->SetPixel( y, x, 467 aColor ); 468 } 469 } 470 } 471 } 472 else 473 { 474 // differentiate mask and alpha channel (on-off 475 // vs. multi-level transparency) 476 if( rBitmap.IsTransparent() ) 477 { 478 // Handling alpha and mask just the same... 479 for( int x=0; x<aDestBmpSize.Width(); ++x ) 480 { 481 ::basegfx::B2DPoint aPoint(x,y); 482 aPoint *= aTransform; 483 484 const int nSrcX( ::basegfx::fround( aPoint.getX() ) ); 485 const int nSrcY( ::basegfx::fround( aPoint.getY() ) ); 486 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || 487 nSrcY < 0 || nSrcY >= aBmpSize.Height() ) 488 { 489 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) ); 490 } 491 else 492 { 493 pAlphaWriteAccess->SetPixel( y, x, 494 aAlphaMap[ 495 pAlphaReadAccess->GetPixel( nSrcY, 496 nSrcX ) ] ); 497 498 pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY, 499 nSrcX ) ); 500 } 501 } 502 } 503 else 504 { 505 for( int x=0; x<aDestBmpSize.Width(); ++x ) 506 { 507 ::basegfx::B2DPoint aPoint(x,y); 508 aPoint *= aTransform; 509 510 const int nSrcX( ::basegfx::fround( aPoint.getX() ) ); 511 const int nSrcY( ::basegfx::fround( aPoint.getY() ) ); 512 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || 513 nSrcY < 0 || nSrcY >= aBmpSize.Height() ) 514 { 515 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) ); 516 } 517 else 518 { 519 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(0) ); 520 pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY, 521 nSrcX ) ); 522 } 523 } 524 } 525 } 526 } 527 528 bCopyBack = true; 529 } 530 else 531 { 532 // TODO(E2): Error handling! 533 ENSURE_OR_THROW( false, 534 "transformBitmap(): could not access bitmap" ); 535 } 536 } 537 538 if( bCopyBack ) 539 return BitmapEx( aDstBitmap, AlphaMask( aDstAlpha ) ); 540 else 541 return BitmapEx(); 542 } 543 } 544 } 545