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