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 #include <canvas/verbosetrace.hxx> 34 35 #include <rtl/math.hxx> 36 37 #include <vcl/outdev.hxx> 38 #include <vcl/bitmap.hxx> 39 #include <vcl/alpha.hxx> 40 #include <vcl/bitmapex.hxx> 41 #include <vcl/canvastools.hxx> 42 43 #include <basegfx/matrix/b2dhommatrix.hxx> 44 #include <basegfx/point/b2dpoint.hxx> 45 #include <basegfx/tools/canvastools.hxx> 46 #include <basegfx/polygon/b2dpolygon.hxx> 47 #include <basegfx/polygon/b2dpolygontools.hxx> 48 #include <basegfx/polygon/b2dpolypolygontools.hxx> 49 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx> 50 #include <basegfx/polygon/b2dpolygontriangulator.hxx> 51 #include <basegfx/polygon/b2dpolygonclipper.hxx> 52 #include <basegfx/numeric/ftools.hxx> 53 54 #include <canvas/canvastools.hxx> 55 56 #include "spritehelper.hxx" 57 58 using namespace ::com::sun::star; 59 60 61 namespace vclcanvas 62 { 63 SpriteHelper::SpriteHelper() : 64 mpBackBuffer(), 65 mpBackBufferMask(), 66 maContent(), 67 mbShowSpriteBounds(false) 68 { 69 } 70 71 void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize, 72 const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas, 73 const BackBufferSharedPtr& rBackBuffer, 74 const BackBufferSharedPtr& rBackBufferMask, 75 bool bShowSpriteBounds ) 76 { 77 ENSURE_OR_THROW( rOwningSpriteCanvas.get() && rBackBuffer && rBackBufferMask, 78 "SpriteHelper::init(): Invalid sprite canvas or back buffer" ); 79 80 mpBackBuffer = rBackBuffer; 81 mpBackBufferMask = rBackBufferMask; 82 mbShowSpriteBounds = bShowSpriteBounds; 83 84 init( rSpriteSize, rOwningSpriteCanvas ); 85 } 86 87 void SpriteHelper::disposing() 88 { 89 mpBackBuffer.reset(); 90 mpBackBufferMask.reset(); 91 92 // forward to parent 93 CanvasCustomSpriteHelper::disposing(); 94 } 95 96 void SpriteHelper::redraw( OutputDevice& rTargetSurface, 97 const ::basegfx::B2DPoint& rPos, 98 bool& io_bSurfacesDirty, 99 bool bBufferedUpdate ) const 100 { 101 (void)bBufferedUpdate; // not used on every platform 102 103 if( !mpBackBuffer || 104 !mpBackBufferMask ) 105 { 106 return; // we're disposed 107 } 108 109 // log output pos in device pixel 110 VERBOSE_TRACE( "SpriteHelper::redraw(): output pos is (%f, %f)", 111 rPos.getX(), 112 rPos.getY() ); 113 114 const double fAlpha( getAlpha() ); 115 116 if( isActive() && 117 !::basegfx::fTools::equalZero( fAlpha ) ) 118 { 119 const Point aEmptyPoint; 120 const ::basegfx::B2DVector& rOrigOutputSize( getSizePixel() ); 121 122 // might get changed below (e.g. adapted for 123 // transformations). IMPORTANT: both position and size are 124 // rounded to integer values. From now on, only those 125 // rounded values are used, to keep clip and content in 126 // sync. 127 ::Size aOutputSize( ::vcl::unotools::sizeFromB2DSize( rOrigOutputSize ) ); 128 ::Point aOutPos( ::vcl::unotools::pointFromB2DPoint( rPos ) ); 129 130 131 // TODO(F3): Support for alpha-VDev 132 133 // Do we have to update our bitmaps (necessary if virdev 134 // was painted to, or transformation changed)? 135 const bool bNeedBitmapUpdate( io_bSurfacesDirty || 136 hasTransformChanged() || 137 maContent->IsEmpty() ); 138 139 // updating content of sprite cache - surface is no 140 // longer dirty in relation to our cache 141 io_bSurfacesDirty = false; 142 transformUpdated(); 143 144 if( bNeedBitmapUpdate ) 145 { 146 Bitmap aBmp( mpBackBuffer->getOutDev().GetBitmap( aEmptyPoint, 147 aOutputSize ) ); 148 149 if( isContentFullyOpaque() ) 150 { 151 // optimized case: content canvas is fully 152 // opaque. Note: since we retrieved aBmp directly 153 // from an OutDev, it's already a 'display bitmap' 154 // on windows. 155 maContent = BitmapEx( aBmp ); 156 } 157 else 158 { 159 // sprite content might contain alpha, create 160 // BmpEx, then. 161 Bitmap aMask( mpBackBufferMask->getOutDev().GetBitmap( aEmptyPoint, 162 aOutputSize ) ); 163 164 // bitmasks are much faster than alphamasks on some platforms 165 // so convert to bitmask if useful 166 #ifndef QUARTZ 167 if( aMask.GetBitCount() != 1 ) 168 { 169 OSL_ENSURE(false, 170 "CanvasCustomSprite::redraw(): Mask bitmap is not " 171 "monochrome (performance!)"); 172 aMask.MakeMono(255); 173 } 174 #endif 175 176 // Note: since we retrieved aBmp and aMask 177 // directly from an OutDev, it's already a 178 // 'display bitmap' on windows. 179 maContent = BitmapEx( aBmp, aMask ); 180 } 181 } 182 183 ::basegfx::B2DHomMatrix aTransform( getTransformation() ); 184 185 // check whether matrix is "easy" to handle - pure 186 // translations or scales are handled by OutputDevice 187 // alone 188 const bool bIdentityTransform( aTransform.isIdentity() ); 189 190 // make transformation absolute (put sprite to final 191 // output position). Need to happen here, as we also have 192 // to translate the clip polygon 193 aTransform.translate( aOutPos.X(), 194 aOutPos.Y() ); 195 196 if( !bIdentityTransform ) 197 { 198 if( !::basegfx::fTools::equalZero( aTransform.get(0,1) ) || 199 !::basegfx::fTools::equalZero( aTransform.get(1,0) ) ) 200 { 201 // "complex" transformation, employ affine 202 // transformator 203 204 // modify output position, to account for the fact 205 // that transformBitmap() always normalizes its output 206 // bitmap into the smallest enclosing box. 207 ::basegfx::B2DRectangle aDestRect; 208 ::canvas::tools::calcTransformedRectBounds( aDestRect, 209 ::basegfx::B2DRectangle(0, 210 0, 211 rOrigOutputSize.getX(), 212 rOrigOutputSize.getY()), 213 aTransform ); 214 215 aOutPos.X() = ::basegfx::fround( aDestRect.getMinX() ); 216 aOutPos.Y() = ::basegfx::fround( aDestRect.getMinY() ); 217 218 // TODO(P3): Use optimized bitmap transformation here. 219 220 // actually re-create the bitmap ONLY if necessary 221 if( bNeedBitmapUpdate ) 222 maContent = tools::transformBitmap( *maContent, 223 aTransform, 224 uno::Sequence<double>(), 225 tools::MODULATE_NONE ); 226 227 aOutputSize = maContent->GetSizePixel(); 228 } 229 else 230 { 231 // relatively 'simplistic' transformation - 232 // retrieve scale and translational offset 233 aOutputSize.setWidth ( 234 ::basegfx::fround( rOrigOutputSize.getX() * aTransform.get(0,0) ) ); 235 aOutputSize.setHeight( 236 ::basegfx::fround( rOrigOutputSize.getY() * aTransform.get(1,1) ) ); 237 238 aOutPos.X() = ::basegfx::fround( aTransform.get(0,2) ); 239 aOutPos.Y() = ::basegfx::fround( aTransform.get(1,2) ); 240 } 241 } 242 243 // transformBitmap() might return empty bitmaps, for tiny 244 // scales. 245 if( !!(*maContent) ) 246 { 247 // when true, fast path for slide transition has 248 // already redrawn the sprite. 249 bool bSpriteRedrawn( false ); 250 251 rTargetSurface.Push( PUSH_CLIPREGION ); 252 253 // apply clip (if any) 254 if( getClip().is() ) 255 { 256 ::basegfx::B2DPolyPolygon aClipPoly( 257 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( 258 getClip() )); 259 260 if( aClipPoly.count() ) 261 { 262 // aTransform already contains the 263 // translational component, moving the clip to 264 // the final sprite output position. 265 aClipPoly.transform( aTransform ); 266 267 #if ! defined WNT && ! defined QUARTZ 268 // non-Windows only - bAtLeastOnePolygon is 269 // only used in non-WNT code below 270 271 // check whether maybe the clip consists 272 // solely out of rectangular polygons. If this 273 // is the case, enforce using the triangle 274 // clip region setup - non-optimized X11 275 // drivers tend to perform abyssmally on 276 // XPolygonRegion, which is used internally, 277 // when filling complex polypolygons. 278 bool bAtLeastOnePolygon( false ); 279 const sal_Int32 nPolygons( aClipPoly.count() ); 280 281 for( sal_Int32 i=0; i<nPolygons; ++i ) 282 { 283 if( !::basegfx::tools::isRectangle( 284 aClipPoly.getB2DPolygon(i)) ) 285 { 286 bAtLeastOnePolygon = true; 287 break; 288 } 289 } 290 #endif 291 292 if( mbShowSpriteBounds ) 293 { 294 // Paint green sprite clip area 295 rTargetSurface.SetLineColor( Color( 0,255,0 ) ); 296 rTargetSurface.SetFillColor(); 297 298 rTargetSurface.DrawPolyPolygon(PolyPolygon(aClipPoly)); // #i76339# 299 } 300 301 #if ! defined WNT && ! defined QUARTZ 302 // as a matter of fact, this fast path only 303 // performs well for X11 - under Windows, the 304 // clip via SetTriangleClipRegion is faster. 305 if( bAtLeastOnePolygon && 306 bBufferedUpdate && 307 ::rtl::math::approxEqual(fAlpha, 1.0) && 308 !maContent->IsTransparent() ) 309 { 310 // fast path for slide transitions 311 // (buffered, no alpha, no mask (because 312 // full slide is contained in the sprite)) 313 314 // XOR bitmap onto backbuffer, clear area 315 // that should be _visible_ with black, 316 // XOR bitmap again on top of that - 317 // result: XOR cancels out where no black 318 // has been rendered, and yields the 319 // original bitmap, where black is 320 // underneath. 321 rTargetSurface.Push( PUSH_RASTEROP ); 322 rTargetSurface.SetRasterOp( ROP_XOR ); 323 rTargetSurface.DrawBitmap( aOutPos, 324 aOutputSize, 325 maContent->GetBitmap() ); 326 327 rTargetSurface.SetLineColor(); 328 rTargetSurface.SetFillColor( COL_BLACK ); 329 rTargetSurface.SetRasterOp( ROP_0 ); 330 rTargetSurface.DrawPolyPolygon(PolyPolygon(aClipPoly)); // #i76339# 331 332 rTargetSurface.SetRasterOp( ROP_XOR ); 333 rTargetSurface.DrawBitmap( aOutPos, 334 aOutputSize, 335 maContent->GetBitmap() ); 336 337 rTargetSurface.Pop(); 338 339 bSpriteRedrawn = true; 340 } 341 else 342 #endif 343 { 344 Region aClipRegion( aClipPoly ); 345 rTargetSurface.SetClipRegion( aClipRegion ); 346 } 347 } 348 } 349 350 if( !bSpriteRedrawn ) 351 { 352 if( ::rtl::math::approxEqual(fAlpha, 1.0) ) 353 { 354 // no alpha modulation -> just copy to output 355 if( maContent->IsTransparent() ) 356 rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, *maContent ); 357 else 358 rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() ); 359 } 360 else 361 { 362 // TODO(P3): Switch to OutputDevice::DrawTransparent() 363 // here 364 365 // draw semi-transparent 366 sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) ); 367 AlphaMask aAlpha( maContent->GetSizePixel(), 368 &nColor ); 369 370 // mask out fully transparent areas 371 if( maContent->IsTransparent() ) 372 aAlpha.Replace( maContent->GetMask(), 255 ); 373 374 // alpha-blend to output 375 rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, 376 BitmapEx( maContent->GetBitmap(), 377 aAlpha ) ); 378 } 379 } 380 381 rTargetSurface.Pop(); 382 383 if( mbShowSpriteBounds ) 384 { 385 ::PolyPolygon aMarkerPoly( 386 ::canvas::tools::getBoundMarksPolyPolygon( 387 ::basegfx::B2DRectangle(aOutPos.X(), 388 aOutPos.Y(), 389 aOutPos.X() + aOutputSize.Width()-1, 390 aOutPos.Y() + aOutputSize.Height()-1) ) ); 391 392 // Paint little red sprite area markers 393 rTargetSurface.SetLineColor( COL_RED ); 394 rTargetSurface.SetFillColor(); 395 396 for( int i=0; i<aMarkerPoly.Count(); ++i ) 397 { 398 rTargetSurface.DrawPolyLine( aMarkerPoly.GetObject((sal_uInt16)i) ); 399 } 400 401 // paint sprite prio 402 Font aVCLFont; 403 aVCLFont.SetHeight( std::min(long(20),aOutputSize.Height()) ); 404 aVCLFont.SetColor( COL_RED ); 405 406 rTargetSurface.SetTextAlign(ALIGN_TOP); 407 rTargetSurface.SetTextColor( COL_RED ); 408 rTargetSurface.SetFont( aVCLFont ); 409 410 ::rtl::OUString text( ::rtl::math::doubleToUString( getPriority(), 411 rtl_math_StringFormat_F, 412 2,'.',NULL,' ') ); 413 414 rTargetSurface.DrawText( aOutPos+Point(2,2), text ); 415 416 #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 417 OSL_TRACE( "SpriteHelper::redraw(): sprite %X has prio %f\n", 418 this, getPriority() ); 419 #endif 420 } 421 } 422 } 423 } 424 425 ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const 426 { 427 return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly ); 428 } 429 430 } 431