/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_canvas.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spritehelper.hxx" using namespace ::com::sun::star; namespace vclcanvas { SpriteHelper::SpriteHelper() : mpBackBuffer(), mpBackBufferMask(), maContent(), mbShowSpriteBounds(false) { } void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize, const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas, const BackBufferSharedPtr& rBackBuffer, const BackBufferSharedPtr& rBackBufferMask, bool bShowSpriteBounds ) { ENSURE_OR_THROW( rOwningSpriteCanvas.get() && rBackBuffer && rBackBufferMask, "SpriteHelper::init(): Invalid sprite canvas or back buffer" ); mpBackBuffer = rBackBuffer; mpBackBufferMask = rBackBufferMask; mbShowSpriteBounds = bShowSpriteBounds; init( rSpriteSize, rOwningSpriteCanvas ); } void SpriteHelper::disposing() { mpBackBuffer.reset(); mpBackBufferMask.reset(); // forward to parent CanvasCustomSpriteHelper::disposing(); } void SpriteHelper::redraw( OutputDevice& rTargetSurface, const ::basegfx::B2DPoint& rPos, bool& io_bSurfacesDirty, bool bBufferedUpdate ) const { (void)bBufferedUpdate; // not used on every platform if( !mpBackBuffer || !mpBackBufferMask ) { return; // we're disposed } // log output pos in device pixel VERBOSE_TRACE( "SpriteHelper::redraw(): output pos is (%f, %f)", rPos.getX(), rPos.getY() ); const double fAlpha( getAlpha() ); if( isActive() && !::basegfx::fTools::equalZero( fAlpha ) ) { const Point aEmptyPoint; const ::basegfx::B2DVector& rOrigOutputSize( getSizePixel() ); // might get changed below (e.g. adapted for // transformations). IMPORTANT: both position and size are // rounded to integer values. From now on, only those // rounded values are used, to keep clip and content in // sync. ::Size aOutputSize( ::vcl::unotools::sizeFromB2DSize( rOrigOutputSize ) ); ::Point aOutPos( ::vcl::unotools::pointFromB2DPoint( rPos ) ); // TODO(F3): Support for alpha-VDev // Do we have to update our bitmaps (necessary if virdev // was painted to, or transformation changed)? const bool bNeedBitmapUpdate( io_bSurfacesDirty || hasTransformChanged() || maContent->IsEmpty() ); // updating content of sprite cache - surface is no // longer dirty in relation to our cache io_bSurfacesDirty = false; transformUpdated(); if( bNeedBitmapUpdate ) { Bitmap aBmp( mpBackBuffer->getOutDev().GetBitmap( aEmptyPoint, aOutputSize ) ); if( isContentFullyOpaque() ) { // optimized case: content canvas is fully // opaque. Note: since we retrieved aBmp directly // from an OutDev, it's already a 'display bitmap' // on windows. maContent = BitmapEx( aBmp ); } else { // sprite content might contain alpha, create // BmpEx, then. Bitmap aMask( mpBackBufferMask->getOutDev().GetBitmap( aEmptyPoint, aOutputSize ) ); // bitmasks are much faster than alphamasks on some platforms // so convert to bitmask if useful #if defined LINUX || defined FREEBSD || defined NETBSD || defined QUARTZ // #122485# allow more than 1bit masks for Linux and Mac, // but reduce to mono now aMask.MakeMono(255); #else // #122485# assert when mask uses more than 1bit and reduce // to mono if( aMask.GetBitCount() != 1 ) { OSL_ENSURE(false, "CanvasCustomSprite::redraw(): Mask bitmap is not " "monochrome (performance!)"); aMask.MakeMono(255); } #endif // Note: since we retrieved aBmp and aMask // directly from an OutDev, it's already a // 'display bitmap' on windows. maContent = BitmapEx( aBmp, aMask ); } } ::basegfx::B2DHomMatrix aTransform( getTransformation() ); // check whether matrix is "easy" to handle - pure // translations or scales are handled by OutputDevice // alone const bool bIdentityTransform( aTransform.isIdentity() ); // make transformation absolute (put sprite to final // output position). Need to happen here, as we also have // to translate the clip polygon aTransform.translate( aOutPos.X(), aOutPos.Y() ); if( !bIdentityTransform ) { if( !::basegfx::fTools::equalZero( aTransform.get(0,1) ) || !::basegfx::fTools::equalZero( aTransform.get(1,0) ) ) { // "complex" transformation, employ affine // transformator // modify output position, to account for the fact // that transformBitmap() always normalizes its output // bitmap into the smallest enclosing box. ::basegfx::B2DRectangle aDestRect; ::canvas::tools::calcTransformedRectBounds( aDestRect, ::basegfx::B2DRectangle(0, 0, rOrigOutputSize.getX(), rOrigOutputSize.getY()), aTransform ); aOutPos.X() = ::basegfx::fround( aDestRect.getMinX() ); aOutPos.Y() = ::basegfx::fround( aDestRect.getMinY() ); // TODO(P3): Use optimized bitmap transformation here. // actually re-create the bitmap ONLY if necessary if( bNeedBitmapUpdate ) maContent = tools::transformBitmap( *maContent, aTransform, uno::Sequence(), tools::MODULATE_NONE ); aOutputSize = maContent->GetSizePixel(); } else { // relatively 'simplistic' transformation - // retrieve scale and translational offset aOutputSize.setWidth ( ::basegfx::fround( rOrigOutputSize.getX() * aTransform.get(0,0) ) ); aOutputSize.setHeight( ::basegfx::fround( rOrigOutputSize.getY() * aTransform.get(1,1) ) ); aOutPos.X() = ::basegfx::fround( aTransform.get(0,2) ); aOutPos.Y() = ::basegfx::fround( aTransform.get(1,2) ); } } // transformBitmap() might return empty bitmaps, for tiny // scales. if( !!(*maContent) ) { // when true, fast path for slide transition has // already redrawn the sprite. bool bSpriteRedrawn( false ); rTargetSurface.Push( PUSH_CLIPREGION ); // apply clip (if any) if( getClip().is() ) { ::basegfx::B2DPolyPolygon aClipPoly( ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( getClip() )); if( aClipPoly.count() ) { // aTransform already contains the // translational component, moving the clip to // the final sprite output position. aClipPoly.transform( aTransform ); #if ! defined WNT && ! defined QUARTZ // non-Windows only - bAtLeastOnePolygon is // only used in non-WNT code below // check whether maybe the clip consists // solely out of rectangular polygons. If this // is the case, enforce using the triangle // clip region setup - non-optimized X11 // drivers tend to perform abyssmally on // XPolygonRegion, which is used internally, // when filling complex polypolygons. bool bAtLeastOnePolygon( false ); const sal_Int32 nPolygons( aClipPoly.count() ); for( sal_Int32 i=0; iIsTransparent() ) { // fast path for slide transitions // (buffered, no alpha, no mask (because // full slide is contained in the sprite)) // XOR bitmap onto backbuffer, clear area // that should be _visible_ with black, // XOR bitmap again on top of that - // result: XOR cancels out where no black // has been rendered, and yields the // original bitmap, where black is // underneath. rTargetSurface.Push( PUSH_RASTEROP ); rTargetSurface.SetRasterOp( ROP_XOR ); rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() ); rTargetSurface.SetLineColor(); rTargetSurface.SetFillColor( COL_BLACK ); rTargetSurface.SetRasterOp( ROP_0 ); rTargetSurface.DrawPolyPolygon(PolyPolygon(aClipPoly)); // #i76339# rTargetSurface.SetRasterOp( ROP_XOR ); rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() ); rTargetSurface.Pop(); bSpriteRedrawn = true; } else #endif { Region aClipRegion( aClipPoly ); rTargetSurface.SetClipRegion( aClipRegion ); } } } if( !bSpriteRedrawn ) { if( ::rtl::math::approxEqual(fAlpha, 1.0) ) { // no alpha modulation -> just copy to output if( maContent->IsTransparent() ) rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, *maContent ); else rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() ); } else { // TODO(P3): Switch to OutputDevice::DrawTransparent() // here // draw semi-transparent sal_uInt8 nColor( static_cast( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) ); AlphaMask aAlpha( maContent->GetSizePixel(), &nColor ); // mask out fully transparent areas if( maContent->IsTransparent() ) aAlpha.Replace( maContent->GetMask(), 255 ); // alpha-blend to output rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, BitmapEx( maContent->GetBitmap(), aAlpha ) ); } } rTargetSurface.Pop(); if( mbShowSpriteBounds ) { ::PolyPolygon aMarkerPoly( ::canvas::tools::getBoundMarksPolyPolygon( ::basegfx::B2DRectangle(aOutPos.X(), aOutPos.Y(), aOutPos.X() + aOutputSize.Width()-1, aOutPos.Y() + aOutputSize.Height()-1) ) ); // Paint little red sprite area markers rTargetSurface.SetLineColor( COL_RED ); rTargetSurface.SetFillColor(); for( int i=0; i 0 OSL_TRACE( "SpriteHelper::redraw(): sprite %X has prio %f\n", this, getPriority() ); #endif } } } } ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const { return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly ); } }