blob: c54479b8a4b55c8b35b98b297f3c33a6792bb2fa [file] [log] [blame]
/**************************************************************
*
* 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_slideshow.hxx"
// must be first
#include <canvas/debug.hxx>
#include <tools/diagnose_ex.h>
#include <math.h>
#include <rtl/logfile.hxx>
#include <rtl/math.hxx>
#include <com/sun/star/rendering/XCanvas.hpp>
#include <com/sun/star/rendering/XIntegerBitmap.hpp>
#include <com/sun/star/rendering/PanoseLetterForm.hpp>
#include <com/sun/star/awt/FontSlant.hpp>
#include <cppuhelper/exc_hlp.hxx>
#include <comphelper/anytostring.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <canvas/verbosetrace.hxx>
#include <canvas/canvastools.hxx>
#include <cppcanvas/vclfactory.hxx>
#include <cppcanvas/basegfxfactory.hxx>
#include "viewshape.hxx"
#include "tools.hxx"
#include <boost/bind.hpp>
using namespace ::com::sun::star;
namespace slideshow
{
namespace internal
{
// TODO(F2): Provide sensible setup for mtf-related attributes (fill mode,
// char rotation etc.). Do that via mtf argument at this object
bool ViewShape::prefetch( RendererCacheEntry& io_rCacheEntry,
const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
const GDIMetaFileSharedPtr& rMtf,
const ShapeAttributeLayerSharedPtr& rAttr ) const
{
RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::prefetch()" );
ENSURE_OR_RETURN_FALSE( rMtf,
"ViewShape::prefetch(): no valid metafile!" );
if( rMtf != io_rCacheEntry.mpMtf ||
rDestinationCanvas != io_rCacheEntry.getDestinationCanvas() )
{
// buffered renderer invalid, re-create
::cppcanvas::Renderer::Parameters aParms;
// rendering attribute override parameter struct. For
// every valid attribute, the corresponding struct
// member is filled, which in the metafile renderer
// forces rendering with the given attribute.
if( rAttr )
{
if( rAttr->isFillColorValid() )
{
// convert RGBColor to RGBA32 integer. Note
// that getIntegerColor() also truncates
// out-of-range values appropriately
aParms.maFillColor =
rAttr->getFillColor().getIntegerColor();
}
if( rAttr->isLineColorValid() )
{
// convert RGBColor to RGBA32 integer. Note
// that getIntegerColor() also truncates
// out-of-range values appropriately
aParms.maLineColor =
rAttr->getLineColor().getIntegerColor();
}
if( rAttr->isCharColorValid() )
{
// convert RGBColor to RGBA32 integer. Note
// that getIntegerColor() also truncates
// out-of-range values appropriately
aParms.maTextColor =
rAttr->getCharColor().getIntegerColor();
}
if( rAttr->isDimColorValid() )
{
// convert RGBColor to RGBA32 integer. Note
// that getIntegerColor() also truncates
// out-of-range values appropriately
// dim color overrides all other colors
aParms.maFillColor =
aParms.maLineColor =
aParms.maTextColor =
rAttr->getDimColor().getIntegerColor();
}
if( rAttr->isFontFamilyValid() )
{
aParms.maFontName =
rAttr->getFontFamily();
}
if( rAttr->isCharScaleValid() )
{
::basegfx::B2DHomMatrix aMatrix;
// enlarge text by given scale factor. Do that
// with the middle of the shape as the center
// of scaling.
aMatrix.translate( -0.5, -0.5 );
aMatrix.scale( rAttr->getCharScale(),
rAttr->getCharScale() );
aMatrix.translate( 0.5, 0.5 );
aParms.maTextTransformation = aMatrix;
}
if( rAttr->isCharWeightValid() )
{
aParms.maFontWeight =
static_cast< sal_Int8 >(
::basegfx::fround(
::std::max( 0.0,
::std::min( 11.0,
rAttr->getCharWeight() / 20.0 ) ) ) );
}
if( rAttr->isCharPostureValid() )
{
aParms.maFontLetterForm =
rAttr->getCharPosture() == awt::FontSlant_NONE ?
rendering::PanoseLetterForm::ANYTHING :
rendering::PanoseLetterForm::OBLIQUE_CONTACT;
}
if( rAttr->isUnderlineModeValid() )
{
aParms.maFontUnderline =
rAttr->getUnderlineMode();
}
}
io_rCacheEntry.mpRenderer = ::cppcanvas::VCLFactory::getInstance().createRenderer( rDestinationCanvas,
*rMtf.get(),
aParms );
io_rCacheEntry.mpMtf = rMtf;
io_rCacheEntry.mpDestinationCanvas = rDestinationCanvas;
// also invalidate alpha compositing bitmap (created
// new renderer, which possibly generates different
// output). Do NOT invalidate, if we're incidentally
// rendering INTO it.
if( rDestinationCanvas != io_rCacheEntry.mpLastBitmapCanvas )
{
io_rCacheEntry.mpLastBitmapCanvas.reset();
io_rCacheEntry.mpLastBitmap.reset();
}
}
return (io_rCacheEntry.mpRenderer.get() != NULL);
}
bool ViewShape::draw( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
const GDIMetaFileSharedPtr& rMtf,
const ShapeAttributeLayerSharedPtr& rAttr,
const ::basegfx::B2DHomMatrix& rTransform,
const ::basegfx::B2DPolyPolygon* pClip,
const VectorOfDocTreeNodes& rSubsets ) const
{
RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::draw()" );
::cppcanvas::RendererSharedPtr pRenderer(
getRenderer( rDestinationCanvas, rMtf, rAttr ) );
ENSURE_OR_RETURN_FALSE( pRenderer, "ViewShape::draw(): Invalid renderer" );
pRenderer->setTransformation( rTransform );
#if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0
rendering::RenderState aRenderState;
::canvas::tools::initRenderState(aRenderState);
::canvas::tools::setRenderStateTransform(aRenderState,
rTransform);
aRenderState.DeviceColor.realloc(4);
aRenderState.DeviceColor[0] = 1.0;
aRenderState.DeviceColor[1] = 0.0;
aRenderState.DeviceColor[2] = 0.0;
aRenderState.DeviceColor[3] = 1.0;
try
{
rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(0.0,0.0),
geometry::RealPoint2D(1.0,1.0),
rDestinationCanvas->getViewState(),
aRenderState );
rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0),
geometry::RealPoint2D(0.0,1.0),
rDestinationCanvas->getViewState(),
aRenderState );
}
catch( uno::Exception& )
{
DBG_UNHANDLED_EXCEPTION();
}
#endif
if( pClip )
pRenderer->setClip( *pClip );
else
pRenderer->setClip();
if( rSubsets.empty() )
{
return pRenderer->draw();
}
else
{
// render subsets of whole metafile
// --------------------------------
bool bRet(true);
VectorOfDocTreeNodes::const_iterator aIter( rSubsets.begin() );
const VectorOfDocTreeNodes::const_iterator aEnd ( rSubsets.end() );
while( aIter != aEnd )
{
if( !pRenderer->drawSubset( aIter->getStartIndex(),
aIter->getEndIndex() ) )
bRet = false;
++aIter;
}
return bRet;
}
}
namespace
{
/// Convert untransformed shape update area to device pixel.
::basegfx::B2DRectangle shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix& rCanvasTransformation,
const ::basegfx::B2DRectangle& rUntransformedArea )
{
// convert area to pixel, and add anti-aliasing border
// TODO(P1): Should the view transform some
// day contain rotation/shear, transforming
// the original bounds with the total
// transformation might result in smaller
// overall bounds.
::basegfx::B2DRectangle aBoundsPixel;
::canvas::tools::calcTransformedRectBounds( aBoundsPixel,
rUntransformedArea,
rCanvasTransformation );
// add antialiasing border around the shape (AA
// touches pixel _outside_ the nominal bound rect)
aBoundsPixel.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
return aBoundsPixel;
}
/// Convert shape unit rect to device pixel.
::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& rUnitBounds,
const ::basegfx::B2DHomMatrix& rShapeTransformation,
const ::basegfx::B2DHomMatrix& rCanvasTransformation,
const ShapeAttributeLayerSharedPtr& pAttr )
{
// calc update area for whole shape (including
// character scaling)
return shapeArea2AreaPixel( rCanvasTransformation,
getShapeUpdateArea( rUnitBounds,
rShapeTransformation,
pAttr ) );
}
}
bool ViewShape::renderSprite( const ViewLayerSharedPtr& rViewLayer,
const GDIMetaFileSharedPtr& rMtf,
const ::basegfx::B2DRectangle& rOrigBounds,
const ::basegfx::B2DRectangle& rBounds,
const ::basegfx::B2DRectangle& rUnitBounds,
int nUpdateFlags,
const ShapeAttributeLayerSharedPtr& pAttr,
const VectorOfDocTreeNodes& rSubsets,
double nPrio,
bool bIsVisible ) const
{
RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::renderSprite()" );
// TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
// in that all the common setup steps here are refactored to Shape (would then
// have to be performed only _once_ per Shape paint).
if( !bIsVisible ||
rUnitBounds.isEmpty() ||
rOrigBounds.isEmpty() ||
rBounds.isEmpty() )
{
// shape is invisible or has zero size, no need to
// update anything.
if( mpSprite )
mpSprite->hide();
return true;
}
// calc sprite position, size and content transformation
// =====================================================
// the shape transformation for a sprite is always a
// simple scale-up to the nominal shape size. Everything
// else is handled via the sprite transformation
::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation;
aNonTranslationalShapeTransformation.scale( rOrigBounds.getWidth(),
rOrigBounds.getHeight() );
::basegfx::B2DHomMatrix aShapeTransformation( aNonTranslationalShapeTransformation );
aShapeTransformation.translate( rOrigBounds.getMinX(),
rOrigBounds.getMinY() );
const ::basegfx::B2DHomMatrix& rCanvasTransform(
rViewLayer->getSpriteTransformation() );
// area actually needed for the sprite
const ::basegfx::B2DRectangle& rSpriteBoundsPixel(
calcUpdateAreaPixel( rUnitBounds,
aShapeTransformation,
rCanvasTransform,
pAttr ) );
// actual area for the shape (without subsetting, but
// including char scaling)
const ::basegfx::B2DRectangle& rShapeBoundsPixel(
calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
aShapeTransformation,
rCanvasTransform,
pAttr ) );
// nominal area for the shape (without subsetting, without
// char scaling). NOTE: to cancel the shape translation,
// contained in rSpriteBoundsPixel, this is _without_ any
// translational component (fixed along with #121921#).
::basegfx::B2DRectangle aLogShapeBounds;
const ::basegfx::B2DRectangle& rNominalShapeBoundsPixel(
shapeArea2AreaPixel( rCanvasTransform,
::canvas::tools::calcTransformedRectBounds(
aLogShapeBounds,
::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
aNonTranslationalShapeTransformation ) ) );
// create (or resize) sprite with sprite's pixel size, if
// not done already
const ::basegfx::B2DSize& rSpriteSizePixel(rSpriteBoundsPixel.getRange());
if( !mpSprite )
{
mpSprite.reset(
new AnimatedSprite( mpViewLayer,
rSpriteSizePixel,
nPrio ));
}
else
{
// TODO(F2): when the sprite _actually_ gets resized,
// content needs a repaint!
mpSprite->resize( rSpriteSizePixel );
}
ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" );
VERBOSE_TRACE( "ViewShape::renderSprite(): Rendering sprite 0x%X",
mpSprite.get() );
// always show the sprite (might have been hidden before)
mpSprite->show();
// determine center of sprite output position in pixel
// (assumption here: all shape transformations have the
// shape center as the pivot point). From that, subtract
// distance of rSpriteBoundsPixel's left, top edge from
// rShapeBoundsPixel's center. This moves the sprite at
// the appropriate output position within the virtual
// rShapeBoundsPixel area.
::basegfx::B2DPoint aSpritePosPixel( rBounds.getCenter() );
aSpritePosPixel *= rCanvasTransform;
aSpritePosPixel -= rShapeBoundsPixel.getCenter() - rSpriteBoundsPixel.getMinimum();
// the difference between rShapeBoundsPixel and
// rSpriteBoundsPixel upper, left corner is: the offset we
// have to move sprite output to the right, top (to make
// the desired subset content visible at all)
const ::basegfx::B2DSize& rSpriteCorrectionOffset(
rSpriteBoundsPixel.getMinimum() - rNominalShapeBoundsPixel.getMinimum() );
// offset added top, left for anti-aliasing (otherwise,
// shapes fully filling the sprite will have anti-aliased
// pixel cut off)
const ::basegfx::B2DSize aAAOffset(
::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE,
::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
// set pixel output offset to sprite: we always leave
// ANTIALIASING_EXTRA_SIZE room atop and to the left, and,
// what's more, for subsetted shapes, we _have_ to cancel
// the effect of the shape renderer outputting the subset
// at its absolute position inside the shape, instead of
// at the origin.
// NOTE: As for now, sprites are always positioned on
// integer pixel positions on screen, have to round to
// nearest integer here, too (fixed along with #121921#)
mpSprite->setPixelOffset(
aAAOffset - ::basegfx::B2DSize(
::basegfx::fround( rSpriteCorrectionOffset.getX() ),
::basegfx::fround( rSpriteCorrectionOffset.getY() ) ) );
// always set sprite position and transformation, since
// they do not relate directly to the update flags
// (e.g. sprite position changes when sprite size changes)
mpSprite->movePixel( aSpritePosPixel );
mpSprite->transform( getSpriteTransformation( rSpriteSizePixel,
rOrigBounds.getRange(),
pAttr ) );
// process flags
// =============
bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & FORCE) );
if( mbForceUpdate || (nUpdateFlags & ALPHA) )
{
mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ?
::basegfx::clamp(pAttr->getAlpha(),
0.0,
1.0) :
1.0 );
}
if( mbForceUpdate || (nUpdateFlags & CLIP) )
{
if( pAttr && pAttr->isClipValid() )
{
::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() );
// extract linear part of canvas view transformation
// (linear means: without translational components)
::basegfx::B2DHomMatrix aViewTransform(
mpViewLayer->getTransformation() );
aViewTransform.set( 0, 2, 0.0 );
aViewTransform.set( 1, 2, 0.0 );
// make the clip 2*ANTIALIASING_EXTRA_SIZE larger
// such that it's again centered over the sprite.
aViewTransform.scale(rSpriteSizePixel.getX()/
(rSpriteSizePixel.getX()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE),
rSpriteSizePixel.getY()/
(rSpriteSizePixel.getY()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE));
// transform clip polygon from view to device
// coordinate space
aClipPoly.transform( aViewTransform );
mpSprite->clip( aClipPoly );
}
else
mpSprite->clip();
}
if( mbForceUpdate || (nUpdateFlags & CONTENT) )
{
bRedrawRequired = true;
// TODO(P1): maybe provide some appearance change methods at
// the Renderer interface
// force the renderer to be regenerated below, for the
// different attributes to take effect
invalidateRenderer();
}
mbForceUpdate = false;
if( !bRedrawRequired )
return true;
// sprite needs repaint - output to sprite canvas
// ==============================================
::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() );
return draw( pContentCanvas,
rMtf,
pAttr,
aShapeTransformation,
NULL, // clipping is done via Sprite::clip()
rSubsets );
}
bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
const GDIMetaFileSharedPtr& rMtf,
const ::basegfx::B2DRectangle& rBounds,
const ::basegfx::B2DRectangle& rUpdateBounds,
int nUpdateFlags,
const ShapeAttributeLayerSharedPtr& pAttr,
const VectorOfDocTreeNodes& rSubsets,
bool bIsVisible ) const
{
RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::render()" );
// TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
// in that all the common setup steps here are refactored to Shape (would then
// have to be performed only _once_ per Shape paint).
if( !bIsVisible )
{
VERBOSE_TRACE( "ViewShape::render(): skipping shape %X", this );
// shape is invisible, no need to update anything.
return true;
}
// since we have no sprite here, _any_ update request
// translates into a required redraw.
bool bRedrawRequired( mbForceUpdate || nUpdateFlags != 0 );
if( (nUpdateFlags & CONTENT) )
{
// TODO(P1): maybe provide some appearance change methods at
// the Renderer interface
// force the renderer to be regenerated below, for the
// different attributes to take effect
invalidateRenderer();
}
mbForceUpdate = false;
if( !bRedrawRequired )
return true;
VERBOSE_TRACE( "ViewShape::render(): rendering shape %X at position (%f,%f)",
this,
rBounds.getMinX(),
rBounds.getMinY() );
// shape needs repaint - setup all that's needed
// ---------------------------------------------
boost::optional<basegfx::B2DPolyPolygon> aClip;
if( pAttr )
{
// setup clip poly
if( pAttr->isClipValid() )
aClip.reset( pAttr->getClip() );
// emulate global shape alpha by first rendering into
// a temp bitmap, and then to screen (this would have
// been much easier if we'd be currently a sprite -
// see above)
if( pAttr->isAlphaValid() )
{
const double nAlpha( pAttr->getAlpha() );
if( !::basegfx::fTools::equalZero( nAlpha ) &&
!::rtl::math::approxEqual(nAlpha, 1.0) )
{
// render with global alpha - have to prepare
// a bitmap, and render that with modulated
// alpha
// -------------------------------------------
const ::basegfx::B2DHomMatrix aTransform(
getShapeTransformation( rBounds,
pAttr ) );
// TODO(P1): Should the view transform some
// day contain rotation/shear, transforming
// the original bounds with the total
// transformation might result in smaller
// overall bounds.
// determine output rect of _shape update
// area_ in device pixel
const ::basegfx::B2DHomMatrix aCanvasTransform(
rDestinationCanvas->getTransformation() );
::basegfx::B2DRectangle aTmpRect;
::canvas::tools::calcTransformedRectBounds( aTmpRect,
rUpdateBounds,
aCanvasTransform );
// pixel size of cache bitmap: round up to
// nearest int
const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1,
static_cast<sal_Int32>( aTmpRect.getHeight() )+1 );
// try to fetch temporary surface for alpha
// compositing (to achieve the global alpha
// blend effect, have to first render shape as
// a whole, then blit that surface with global
// alpha to the destination)
const RendererCacheVector::iterator aCompositingSurface(
getCacheEntry( rDestinationCanvas ) );
if( !aCompositingSurface->mpLastBitmapCanvas ||
aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize )
{
// create a bitmap of appropriate size
::cppcanvas::BitmapSharedPtr pBitmap(
::cppcanvas::BaseGfxFactory::getInstance().createAlphaBitmap(
rDestinationCanvas,
aBmpSize ) );
ENSURE_OR_THROW(pBitmap,
"ViewShape::render(): Could not create compositing surface");
aCompositingSurface->mpDestinationCanvas = rDestinationCanvas;
aCompositingSurface->mpLastBitmap = pBitmap;
aCompositingSurface->mpLastBitmapCanvas = pBitmap->getBitmapCanvas();
}
// buffer aCompositingSurface iterator content
// - said one might get invalidated during
// draw() below.
::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas(
aCompositingSurface->mpLastBitmapCanvas );
::cppcanvas::BitmapSharedPtr pBitmap(
aCompositingSurface->mpLastBitmap);
// setup bitmap canvas transformation -
// which happens to be the destination
// canvas transformation without any
// translational components.
//
// But then, the render transformation as
// calculated by getShapeTransformation()
// above outputs the shape at its real
// destination position. Thus, we have to
// offset the output back to the origin,
// for which we simply plug in the
// negative position of the left, top edge
// of the shape's bound rect in device
// pixel into aLinearTransform below.
::basegfx::B2DHomMatrix aAdjustedCanvasTransform( aCanvasTransform );
aAdjustedCanvasTransform.translate( -aTmpRect.getMinX(),
-aTmpRect.getMinY() );
pBitmapCanvas->setTransformation( aAdjustedCanvasTransform );
// TODO(P2): If no update flags, or only
// alpha_update is set, we can save us the
// rendering into the bitmap (uh, it's not
// _that_ easy - for a forced redraw,
// e.g. when ending an animation, we always
// get UPDATE_FORCE here).
// render into this bitmap
if( !draw( pBitmapCanvas,
rMtf,
pAttr,
aTransform,
!aClip ? NULL : &(*aClip),
rSubsets ) )
{
return false;
}
// render bitmap to screen, with given global
// alpha. Since the bitmap already contains
// pixel-equivalent output, we have to use the
// inverse view transformation, adjusted with
// the final shape output position (note:
// cannot simply change the view
// transformation here, as that would affect a
// possibly set clip!)
::basegfx::B2DHomMatrix aBitmapTransform( aCanvasTransform );
OSL_ENSURE( aBitmapTransform.isInvertible(),
"ViewShape::render(): View transformation is singular!" );
aBitmapTransform.invert();
const basegfx::B2DHomMatrix aTranslation(basegfx::tools::createTranslateB2DHomMatrix(
aTmpRect.getMinX(), aTmpRect.getMinY()));
aBitmapTransform = aBitmapTransform * aTranslation;
pBitmap->setTransformation( aBitmapTransform );
// finally, render bitmap alpha-modulated
pBitmap->drawAlphaModulated( nAlpha );
return true;
}
}
}
// retrieve shape transformation, _with_ shape translation
// to actual page position.
const ::basegfx::B2DHomMatrix aTransform(
getShapeTransformation( rBounds,
pAttr ) );
return draw( rDestinationCanvas,
rMtf,
pAttr,
aTransform,
!aClip ? NULL : &(*aClip),
rSubsets );
}
// -------------------------------------------------------------------------------------
ViewShape::ViewShape( const ViewLayerSharedPtr& rViewLayer ) :
mpViewLayer( rViewLayer ),
maRenderers(),
mpSprite(),
mbAnimationMode( false ),
mbForceUpdate( true )
{
ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" );
}
ViewLayerSharedPtr ViewShape::getViewLayer() const
{
return mpViewLayer;
}
ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const
{
// lookup destination canvas - is there already a renderer
// created for that target?
RendererCacheVector::iterator aIter;
const RendererCacheVector::iterator aEnd( maRenderers.end() );
// already there?
if( (aIter=::std::find_if( maRenderers.begin(),
aEnd,
::boost::bind(
::std::equal_to< ::cppcanvas::CanvasSharedPtr >(),
::boost::cref( rDestinationCanvas ),
::boost::bind(
&RendererCacheEntry::getDestinationCanvas,
_1 ) ) ) ) == aEnd )
{
if( maRenderers.size() >= MAX_RENDER_CACHE_ENTRIES )
{
// cache size exceeded - prune entries. For now,
// simply remove the first one, which of course
// breaks for more complex access schemes. But in
// general, this leads to most recently used
// entries to reside at the end of the vector.
maRenderers.erase( maRenderers.begin() );
// ATTENTION: after this, both aIter and aEnd are
// invalid!
}
// not yet in cache - add default-constructed cache
// entry, to have something to return
maRenderers.push_back( RendererCacheEntry() );
aIter = maRenderers.end()-1;
}
return aIter;
}
::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
const GDIMetaFileSharedPtr& rMtf,
const ShapeAttributeLayerSharedPtr& rAttr ) const
{
// lookup destination canvas - is there already a renderer
// created for that target?
const RendererCacheVector::iterator aIter(
getCacheEntry( rDestinationCanvas ) );
// now we have a valid entry, either way. call prefetch()
// on it, nevertheless - maybe the metafile changed, and
// the renderer still needs an update (prefetch() will
// detect that)
if( prefetch( *aIter,
rDestinationCanvas,
rMtf,
rAttr ) )
{
return aIter->mpRenderer;
}
else
{
// prefetch failed - renderer is invalid
return ::cppcanvas::RendererSharedPtr();
}
}
void ViewShape::invalidateRenderer() const
{
// simply clear the cache. Subsequent getRenderer() calls
// will regenerate the Renderers.
maRenderers.clear();
}
::basegfx::B2DSize ViewShape::getAntialiasingBorder() const
{
ENSURE_OR_THROW( mpViewLayer->getCanvas(),
"ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" );
const ::basegfx::B2DHomMatrix& rViewTransform(
mpViewLayer->getTransformation() );
// TODO(F1): As a quick shortcut (did not want to invert
// whole matrix here), taking only scale components of
// view transformation matrix. This will be wrong when
// e.g. shearing is involved.
const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(0,0) );
const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(1,1) );
return ::basegfx::B2DSize( nXBorder,
nYBorder );
}
bool ViewShape::enterAnimationMode()
{
mbForceUpdate = true;
mbAnimationMode = true;
return true;
}
void ViewShape::leaveAnimationMode()
{
mpSprite.reset();
mbAnimationMode = false;
mbForceUpdate = true;
}
bool ViewShape::update( const GDIMetaFileSharedPtr& rMtf,
const RenderArgs& rArgs,
int nUpdateFlags,
bool bIsVisible ) const
{
RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::update()" );
ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" );
// Shall we render to a sprite, or to a plain canvas?
if( isBackgroundDetached() )
return renderSprite( mpViewLayer,
rMtf,
rArgs.maOrigBounds,
rArgs.maBounds,
rArgs.maUnitBounds,
nUpdateFlags,
rArgs.mrAttr,
rArgs.mrSubsets,
rArgs.mnShapePriority,
bIsVisible );
else
return render( mpViewLayer->getCanvas(),
rMtf,
rArgs.maBounds,
rArgs.maUpdateBounds,
nUpdateFlags,
rArgs.mrAttr,
rArgs.mrSubsets,
bIsVisible );
}
}
}