| /************************************************************** |
| * |
| * 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 ); |
| } |
| |
| } |
| } |