summaryrefslogtreecommitdiff
path: root/canvas/source/vcl/spritehelper.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'canvas/source/vcl/spritehelper.cxx')
-rw-r--r--canvas/source/vcl/spritehelper.cxx446
1 files changed, 446 insertions, 0 deletions
diff --git a/canvas/source/vcl/spritehelper.cxx b/canvas/source/vcl/spritehelper.cxx
new file mode 100644
index 000000000000..123fe1002332
--- /dev/null
+++ b/canvas/source/vcl/spritehelper.cxx
@@ -0,0 +1,446 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: spritehelper.cxx,v $
+ * $Revision: 1.13 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_canvas.hxx"
+
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <canvas/verbosetrace.hxx>
+
+#include <rtl/math.hxx>
+
+#include <vcl/outdev.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/alpha.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#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
+#ifndef QUARTZ
+ 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<double>(),
+ 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; i<nPolygons; ++i )
+ {
+ if( !::basegfx::tools::isRectangle(
+ aClipPoly.getB2DPolygon(i)) )
+ {
+ bAtLeastOnePolygon = true;
+ break;
+ }
+ }
+#endif
+
+ if( mbShowSpriteBounds )
+ {
+ // Paint green sprite clip area
+ rTargetSurface.SetLineColor( Color( 0,255,0 ) );
+ rTargetSurface.SetFillColor();
+
+ rTargetSurface.DrawPolyPolygon(PolyPolygon(aClipPoly)); // #i76339#
+ }
+
+#if ! defined WNT && ! defined QUARTZ
+ // as a matter of fact, this fast path only
+ // performs well for X11 - under Windows, the
+ // clip via SetTriangleClipRegion is faster.
+ if( bAtLeastOnePolygon &&
+ bBufferedUpdate &&
+ ::rtl::math::approxEqual(fAlpha, 1.0) &&
+ !maContent->IsTransparent() )
+ {
+ // 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
+ {
+ // redraw is direcly on the front buffer,
+ // or using alpha blending - cannot use
+ // XOR, thus, employing the still somewhat
+ // speedier triangle clip method
+ ::basegfx::B2DPolygon aTriangulatedClip(::basegfx::triangulator::triangulate(aClipPoly));
+
+ // restrict the clipping area to the visible portion of the output device.
+ Size aSize(rTargetSurface.GetOutputSizePixel());
+ ::basegfx::B2DRange aOutputRect(::basegfx::B2DPoint(0,0),::basegfx::B2DPoint(aSize.Width(),aSize.Height()));
+ ::basegfx::B2DPolygon aClippedClip(::basegfx::tools::clipTriangleListOnRange(aTriangulatedClip,aOutputRect));
+
+ // #i76339#
+ const Polygon aPoly(aClippedClip);
+ const PolyPolygon aPolyPoly(aPoly);
+ rTargetSurface.SetTriangleClipRegion(aPolyPoly);
+ }
+ }
+ }
+
+ 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
+ BYTE nColor( static_cast<UINT8>( ::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<aMarkerPoly.Count(); ++i )
+ {
+ rTargetSurface.DrawPolyLine( aMarkerPoly.GetObject((USHORT)i) );
+ }
+
+ // paint sprite prio
+ Font aVCLFont;
+ aVCLFont.SetHeight( std::min(long(20),aOutputSize.Height()) );
+ aVCLFont.SetColor( COL_RED );
+
+ rTargetSurface.SetTextAlign(ALIGN_TOP);
+ rTargetSurface.SetTextColor( COL_RED );
+ rTargetSurface.SetFont( aVCLFont );
+
+ ::rtl::OUString text( ::rtl::math::doubleToUString( getPriority(),
+ rtl_math_StringFormat_F,
+ 2,'.',NULL,' ') );
+
+ rTargetSurface.DrawText( aOutPos+Point(2,2), text );
+
+#if defined(VERBOSE) && OSL_DEBUG_LEVEL > 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 );
+ }
+
+}