1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <comphelper/diagnose_ex.hxx>
26 #include <rtl/math.hxx>
27 #include <sal/log.hxx>
29 #include <com/sun/star/rendering/PanoseLetterForm.hpp>
30 #include <com/sun/star/awt/FontSlant.hpp>
32 #include <basegfx/numeric/ftools.hxx>
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
36 #include <canvas/canvastools.hxx>
37 #include <cppcanvas/vclfactory.hxx>
38 #include <cppcanvas/basegfxfactory.hxx>
40 #include "viewshape.hxx"
44 using namespace ::com::sun::star
;
46 namespace slideshow::internal
49 // TODO(F2): Provide sensible setup for mtf-related attributes (fill mode,
50 // char rotation etc.). Do that via mtf argument at this object
52 bool ViewShape::prefetch( RendererCacheEntry
& io_rCacheEntry
,
53 const ::cppcanvas::CanvasSharedPtr
& rDestinationCanvas
,
54 const GDIMetaFileSharedPtr
& rMtf
,
55 const ShapeAttributeLayerSharedPtr
& rAttr
)
57 ENSURE_OR_RETURN_FALSE( rMtf
,
58 "ViewShape::prefetch(): no valid metafile!" );
60 if( rMtf
!= io_rCacheEntry
.mpMtf
||
61 rDestinationCanvas
!= io_rCacheEntry
.getDestinationCanvas() )
63 // buffered renderer invalid, re-create
64 ::cppcanvas::Renderer::Parameters aParms
;
66 // rendering attribute override parameter struct. For
67 // every valid attribute, the corresponding struct
68 // member is filled, which in the metafile renderer
69 // forces rendering with the given attribute.
72 if( rAttr
->isFillColorValid() )
74 // convert RGBColor to RGBA32 integer. Note
75 // that getIntegerColor() also truncates
76 // out-of-range values appropriately
78 rAttr
->getFillColor().getIntegerColor();
80 if( rAttr
->isLineColorValid() )
82 // convert RGBColor to RGBA32 integer. Note
83 // that getIntegerColor() also truncates
84 // out-of-range values appropriately
86 rAttr
->getLineColor().getIntegerColor();
88 if( rAttr
->isCharColorValid() )
90 // convert RGBColor to RGBA32 integer. Note
91 // that getIntegerColor() also truncates
92 // out-of-range values appropriately
94 rAttr
->getCharColor().getIntegerColor();
96 if( rAttr
->isDimColorValid() )
98 // convert RGBColor to RGBA32 integer. Note
99 // that getIntegerColor() also truncates
100 // out-of-range values appropriately
102 // dim color overrides all other colors
106 rAttr
->getDimColor().getIntegerColor();
108 if( rAttr
->isFontFamilyValid() )
111 rAttr
->getFontFamily();
113 if( rAttr
->isCharScaleValid() )
115 ::basegfx::B2DHomMatrix aMatrix
;
117 // enlarge text by given scale factor. Do that
118 // with the middle of the shape as the center
120 aMatrix
.translate( -0.5, -0.5 );
121 aMatrix
.scale( rAttr
->getCharScale(),
122 rAttr
->getCharScale() );
123 aMatrix
.translate( 0.5, 0.5 );
125 aParms
.maTextTransformation
= aMatrix
;
127 if( rAttr
->isCharWeightValid() )
129 aParms
.maFontWeight
=
130 static_cast< sal_Int8
>(
134 rAttr
->getCharWeight() / 20.0 ) ) ) );
136 if( rAttr
->isCharPostureValid() )
138 aParms
.maFontLetterForm
=
139 rAttr
->getCharPosture() == sal_Int16(awt::FontSlant_NONE
) ?
140 rendering::PanoseLetterForm::ANYTHING
:
141 rendering::PanoseLetterForm::OBLIQUE_CONTACT
;
143 if( rAttr
->isUnderlineModeValid() )
145 aParms
.maFontUnderline
=
146 rAttr
->getUnderlineMode();
150 io_rCacheEntry
.mpRenderer
= ::cppcanvas::VCLFactory::createRenderer( rDestinationCanvas
,
154 io_rCacheEntry
.mpMtf
= rMtf
;
155 io_rCacheEntry
.mpDestinationCanvas
= rDestinationCanvas
;
157 // also invalidate alpha compositing bitmap (created
158 // new renderer, which possibly generates different
159 // output). Do NOT invalidate, if we're incidentally
160 // rendering INTO it.
161 if( rDestinationCanvas
!= io_rCacheEntry
.mpLastBitmapCanvas
)
163 io_rCacheEntry
.mpLastBitmapCanvas
.reset();
164 io_rCacheEntry
.mpLastBitmap
.reset();
168 return static_cast< bool >(io_rCacheEntry
.mpRenderer
);
171 bool ViewShape::draw( const ::cppcanvas::CanvasSharedPtr
& rDestinationCanvas
,
172 const GDIMetaFileSharedPtr
& rMtf
,
173 const ShapeAttributeLayerSharedPtr
& rAttr
,
174 const ::basegfx::B2DHomMatrix
& rTransform
,
175 const ::basegfx::B2DPolyPolygon
* pClip
,
176 const VectorOfDocTreeNodes
& rSubsets
) const
178 ::cppcanvas::RendererSharedPtr
pRenderer(
179 getRenderer( rDestinationCanvas
, rMtf
, rAttr
) );
181 ENSURE_OR_RETURN_FALSE( pRenderer
, "ViewShape::draw(): Invalid renderer" );
183 pRenderer
->setTransformation( rTransform
);
184 #if OSL_DEBUG_LEVEL >= 2
185 rendering::RenderState aRenderState
;
186 ::canvas::tools::initRenderState(aRenderState
);
187 ::canvas::tools::setRenderStateTransform(aRenderState
,
189 aRenderState
.DeviceColor
.realloc(4);
190 aRenderState
.DeviceColor
[0] = 1.0;
191 aRenderState
.DeviceColor
[1] = 0.0;
192 aRenderState
.DeviceColor
[2] = 0.0;
193 aRenderState
.DeviceColor
[3] = 1.0;
197 rDestinationCanvas
->getUNOCanvas()->drawLine( geometry::RealPoint2D(0.0,0.0),
198 geometry::RealPoint2D(1.0,1.0),
199 rDestinationCanvas
->getViewState(),
201 rDestinationCanvas
->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0),
202 geometry::RealPoint2D(0.0,1.0),
203 rDestinationCanvas
->getViewState(),
206 catch( uno::Exception
& )
208 DBG_UNHANDLED_EXCEPTION("slideshow");
212 pRenderer
->setClip( *pClip
);
214 pRenderer
->setClip();
216 if( rSubsets
.empty() )
218 return pRenderer
->draw();
222 // render subsets of whole metafile
226 for( const auto& rSubset
: rSubsets
)
228 if( !pRenderer
->drawSubset( rSubset
.getStartIndex(),
229 rSubset
.getEndIndex() ) )
239 /// Convert untransformed shape update area to device pixel.
240 ::basegfx::B2DRectangle
shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix
& rCanvasTransformation
,
241 const ::basegfx::B2DRectangle
& rUntransformedArea
)
243 // convert area to pixel, and add anti-aliasing border
245 // TODO(P1): Should the view transform some
246 // day contain rotation/shear, transforming
247 // the original bounds with the total
248 // transformation might result in smaller
251 ::basegfx::B2DRectangle aBoundsPixel
;
252 ::canvas::tools::calcTransformedRectBounds( aBoundsPixel
,
254 rCanvasTransformation
);
256 // add antialiasing border around the shape (AA
257 // touches pixel _outside_ the nominal bound rect)
258 aBoundsPixel
.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
);
263 /// Convert shape unit rect to device pixel.
264 ::basegfx::B2DRectangle
calcUpdateAreaPixel( const ::basegfx::B2DRectangle
& rUnitBounds
,
265 const ::basegfx::B2DHomMatrix
& rShapeTransformation
,
266 const ::basegfx::B2DHomMatrix
& rCanvasTransformation
,
267 const ShapeAttributeLayerSharedPtr
& pAttr
)
269 // calc update area for whole shape (including
270 // character scaling)
271 return shapeArea2AreaPixel( rCanvasTransformation
,
272 getShapeUpdateArea( rUnitBounds
,
273 rShapeTransformation
,
278 bool ViewShape::renderSprite( const ViewLayerSharedPtr
& rViewLayer
,
279 const GDIMetaFileSharedPtr
& rMtf
,
280 const ::basegfx::B2DRectangle
& rOrigBounds
,
281 const ::basegfx::B2DRectangle
& rBounds
,
282 const ::basegfx::B2DRectangle
& rUnitBounds
,
283 UpdateFlags nUpdateFlags
,
284 const ShapeAttributeLayerSharedPtr
& pAttr
,
285 const VectorOfDocTreeNodes
& rSubsets
,
287 bool bIsVisible
) const
289 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
290 // in that all the common setup steps here are refactored to Shape (would then
291 // have to be performed only _once_ per Shape paint).
294 rUnitBounds
.isEmpty() ||
295 rOrigBounds
.isEmpty() ||
298 // shape is invisible or has zero size, no need to
307 // calc sprite position, size and content transformation
308 // =====================================================
310 // the shape transformation for a sprite is always a
311 // simple scale-up to the nominal shape size. Everything
312 // else is handled via the sprite transformation
313 ::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation
;
314 aNonTranslationalShapeTransformation
.scale( rOrigBounds
.getWidth(),
315 rOrigBounds
.getHeight() );
316 ::basegfx::B2DHomMatrix
aShapeTransformation( aNonTranslationalShapeTransformation
);
317 aShapeTransformation
.translate( rOrigBounds
.getMinX(),
318 rOrigBounds
.getMinY() );
320 const ::basegfx::B2DHomMatrix
& rCanvasTransform(
321 rViewLayer
->getSpriteTransformation() );
323 // area actually needed for the sprite
324 const ::basegfx::B2DRectangle
& rSpriteBoundsPixel(
325 calcUpdateAreaPixel( rUnitBounds
,
326 aShapeTransformation
,
330 // actual area for the shape (without subsetting, but
331 // including char scaling)
332 const ::basegfx::B2DRectangle
& rShapeBoundsPixel(
333 calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
334 aShapeTransformation
,
338 // nominal area for the shape (without subsetting, without
339 // char scaling). NOTE: to cancel the shape translation,
340 // contained in rSpriteBoundsPixel, this is _without_ any
341 // translational component.
342 ::basegfx::B2DRectangle aLogShapeBounds
;
343 const ::basegfx::B2DRectangle
& rNominalShapeBoundsPixel(
344 shapeArea2AreaPixel( rCanvasTransform
,
345 ::canvas::tools::calcTransformedRectBounds(
347 ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
348 aNonTranslationalShapeTransformation
) ) );
350 // create (or resize) sprite with sprite's pixel size, if
352 auto aRange
= rSpriteBoundsPixel
.getRange();
353 basegfx::B2DSize
rSpriteSizePixel(aRange
.getX(), aRange
.getY());
356 mpSprite
= std::make_shared
<AnimatedSprite
>( mpViewLayer
,
362 // TODO(F2): when the sprite _actually_ gets resized,
363 // content needs a repaint!
364 mpSprite
->resize( rSpriteSizePixel
);
367 ENSURE_OR_RETURN_FALSE( mpSprite
, "ViewShape::renderSprite(): No sprite" );
369 SAL_INFO("slideshow", "ViewShape::renderSprite(): Rendering sprite " <<
373 // always show the sprite (might have been hidden before)
376 // determine center of sprite output position in pixel
377 // (assumption here: all shape transformations have the
378 // shape center as the pivot point). From that, subtract
379 // distance of rSpriteBoundsPixel's left, top edge from
380 // rShapeBoundsPixel's center. This moves the sprite at
381 // the appropriate output position within the virtual
382 // rShapeBoundsPixel area.
383 ::basegfx::B2DPoint
aSpritePosPixel( rBounds
.getCenter() );
384 aSpritePosPixel
*= rCanvasTransform
;
385 aSpritePosPixel
-= rShapeBoundsPixel
.getCenter() - rSpriteBoundsPixel
.getMinimum();
387 // the difference between rShapeBoundsPixel and
388 // rSpriteBoundsPixel upper, left corner is: the offset we
389 // have to move sprite output to the right, top (to make
390 // the desired subset content visible at all)
391 auto aDifference
= rSpriteBoundsPixel
.getMinimum() - rNominalShapeBoundsPixel
.getMinimum();
392 const basegfx::B2DSize
rSpriteCorrectionOffset(aDifference
.getX(), aDifference
.getY());
394 // offset added top, left for anti-aliasing (otherwise,
395 // shapes fully filling the sprite will have anti-aliased
397 const ::basegfx::B2DSize
aAAOffset(
398 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
,
399 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
);
401 // set pixel output offset to sprite: we always leave
402 // ANTIALIASING_EXTRA_SIZE room atop and to the left, and,
403 // what's more, for subsetted shapes, we _have_ to cancel
404 // the effect of the shape renderer outputting the subset
405 // at its absolute position inside the shape, instead of
407 // NOTE: As for now, sprites are always positioned on
408 // integer pixel positions on screen, have to round to
409 // nearest integer here, too
410 mpSprite
->setPixelOffset(
411 aAAOffset
- ::basegfx::B2DSize(
412 ::basegfx::fround( rSpriteCorrectionOffset
.getWidth() ),
413 ::basegfx::fround( rSpriteCorrectionOffset
.getHeight() ) ) );
415 // always set sprite position and transformation, since
416 // they do not relate directly to the update flags
417 // (e.g. sprite position changes when sprite size changes)
418 mpSprite
->movePixel( aSpritePosPixel
);
419 mpSprite
->transform( getSpriteTransformation( basegfx::B2DVector(rSpriteSizePixel
.getWidth(), rSpriteSizePixel
.getHeight()),
420 rOrigBounds
.getRange(),
427 bool bRedrawRequired( mbForceUpdate
|| (nUpdateFlags
& UpdateFlags::Force
) );
429 if( mbForceUpdate
|| (nUpdateFlags
& UpdateFlags::Alpha
) )
431 mpSprite
->setAlpha( (pAttr
&& pAttr
->isAlphaValid()) ?
432 std::clamp(pAttr
->getAlpha(),
437 if( mbForceUpdate
|| (nUpdateFlags
& UpdateFlags::Clip
) )
439 if( pAttr
&& pAttr
->isClipValid() )
441 ::basegfx::B2DPolyPolygon
aClipPoly( pAttr
->getClip() );
443 // extract linear part of canvas view transformation
444 // (linear means: without translational components)
445 ::basegfx::B2DHomMatrix
aViewTransform(
446 mpViewLayer
->getTransformation() );
447 aViewTransform
.set( 0, 2, 0.0 );
448 aViewTransform
.set( 1, 2, 0.0 );
450 // make the clip 2*ANTIALIASING_EXTRA_SIZE larger
451 // such that it's again centered over the sprite.
452 aViewTransform
.scale(rSpriteSizePixel
.getWidth()/
453 (rSpriteSizePixel
.getWidth()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
),
454 rSpriteSizePixel
.getHeight()/
455 (rSpriteSizePixel
.getHeight()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
));
457 // transform clip polygon from view to device
459 aClipPoly
.transform( aViewTransform
);
461 mpSprite
->clip( aClipPoly
);
466 if( mbForceUpdate
|| (nUpdateFlags
& UpdateFlags::Content
) )
468 bRedrawRequired
= true;
470 // TODO(P1): maybe provide some appearance change methods at
471 // the Renderer interface
473 // force the renderer to be regenerated below, for the
474 // different attributes to take effect
475 invalidateRenderer();
478 mbForceUpdate
= false;
480 if( !bRedrawRequired
)
484 // sprite needs repaint - output to sprite canvas
485 // ==============================================
487 ::cppcanvas::CanvasSharedPtr
pContentCanvas( mpSprite
->getContentCanvas() );
489 return draw( pContentCanvas
,
492 aShapeTransformation
,
493 nullptr, // clipping is done via Sprite::clip()
497 bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr
& rDestinationCanvas
,
498 const GDIMetaFileSharedPtr
& rMtf
,
499 const ::basegfx::B2DRectangle
& rBounds
,
500 const ::basegfx::B2DRectangle
& rUpdateBounds
,
501 UpdateFlags nUpdateFlags
,
502 const ShapeAttributeLayerSharedPtr
& pAttr
,
503 const VectorOfDocTreeNodes
& rSubsets
,
504 bool bIsVisible
) const
506 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
507 // in that all the common setup steps here are refactored to Shape (would then
508 // have to be performed only _once_ per Shape paint).
512 SAL_INFO("slideshow", "ViewShape::render(): skipping shape " << this );
514 // shape is invisible, no need to update anything.
518 // since we have no sprite here, _any_ update request
519 // translates into a required redraw.
520 bool bRedrawRequired( mbForceUpdate
|| nUpdateFlags
!= UpdateFlags::NONE
);
522 if( nUpdateFlags
& UpdateFlags::Content
)
524 // TODO(P1): maybe provide some appearance change methods at
525 // the Renderer interface
527 // force the renderer to be regenerated below, for the
528 // different attributes to take effect
529 invalidateRenderer();
532 mbForceUpdate
= false;
534 if( !bRedrawRequired
)
537 SAL_INFO( "slideshow", "ViewShape::render(): rendering shape " <<
540 rBounds
.getMinX() << "," <<
541 rBounds
.getMinY() << ")" );
544 // shape needs repaint - setup all that's needed
547 std::optional
<basegfx::B2DPolyPolygon
> aClip
;
552 if( pAttr
->isClipValid() )
553 aClip
= pAttr
->getClip();
555 // emulate global shape alpha by first rendering into
556 // a temp bitmap, and then to screen (this would have
557 // been much easier if we'd be currently a sprite -
559 if( pAttr
->isAlphaValid() )
561 const double nAlpha( pAttr
->getAlpha() );
563 if( !::basegfx::fTools::equalZero( nAlpha
) &&
564 !::rtl::math::approxEqual(nAlpha
, 1.0) )
566 // render with global alpha - have to prepare
567 // a bitmap, and render that with modulated
571 const ::basegfx::B2DHomMatrix
aTransform(
572 getShapeTransformation( rBounds
,
575 // TODO(P1): Should the view transform some
576 // day contain rotation/shear, transforming
577 // the original bounds with the total
578 // transformation might result in smaller
581 // determine output rect of _shape update
582 // area_ in device pixel
583 const ::basegfx::B2DHomMatrix
aCanvasTransform(
584 rDestinationCanvas
->getTransformation() );
585 ::basegfx::B2DRectangle aTmpRect
;
586 ::canvas::tools::calcTransformedRectBounds( aTmpRect
,
590 // pixel size of cache bitmap: round up to
592 const ::basegfx::B2ISize
aBmpSize( static_cast<sal_Int32
>( aTmpRect
.getWidth() )+1,
593 static_cast<sal_Int32
>( aTmpRect
.getHeight() )+1 );
595 // try to fetch temporary surface for alpha
596 // compositing (to achieve the global alpha
597 // blend effect, have to first render shape as
598 // a whole, then blit that surface with global
599 // alpha to the destination)
600 const RendererCacheVector::iterator
aCompositingSurface(
601 getCacheEntry( rDestinationCanvas
) );
603 if( !aCompositingSurface
->mpLastBitmapCanvas
||
604 aCompositingSurface
->mpLastBitmapCanvas
->getSize() != aBmpSize
)
606 // create a bitmap of appropriate size
607 ::cppcanvas::BitmapSharedPtr
pBitmap(
608 ::cppcanvas::BaseGfxFactory::createAlphaBitmap(
612 ENSURE_OR_THROW(pBitmap
,
613 "ViewShape::render(): Could not create compositing surface");
615 aCompositingSurface
->mpDestinationCanvas
= rDestinationCanvas
;
616 aCompositingSurface
->mpLastBitmap
= pBitmap
;
617 aCompositingSurface
->mpLastBitmapCanvas
= pBitmap
->getBitmapCanvas();
620 // buffer aCompositingSurface iterator content
621 // - said one might get invalidated during
623 ::cppcanvas::BitmapCanvasSharedPtr
pBitmapCanvas(
624 aCompositingSurface
->mpLastBitmapCanvas
);
626 ::cppcanvas::BitmapSharedPtr
pBitmap(
627 aCompositingSurface
->mpLastBitmap
);
629 // setup bitmap canvas transformation -
630 // which happens to be the destination
631 // canvas transformation without any
632 // translational components.
634 // But then, the render transformation as
635 // calculated by getShapeTransformation()
636 // above outputs the shape at its real
637 // destination position. Thus, we have to
638 // offset the output back to the origin,
639 // for which we simply plug in the
640 // negative position of the left, top edge
641 // of the shape's bound rect in device
642 // pixel into aLinearTransform below.
643 ::basegfx::B2DHomMatrix
aAdjustedCanvasTransform( aCanvasTransform
);
644 aAdjustedCanvasTransform
.translate( -aTmpRect
.getMinX(),
645 -aTmpRect
.getMinY() );
647 pBitmapCanvas
->setTransformation( aAdjustedCanvasTransform
);
649 // TODO(P2): If no update flags, or only
650 // alpha_update is set, we can save us the
651 // rendering into the bitmap (uh, it's not
652 // _that_ easy - for a forced redraw,
653 // e.g. when ending an animation, we always
654 // get UPDATE_FORCE here).
656 // render into this bitmap
657 if( !draw( pBitmapCanvas
,
661 !aClip
? nullptr : &(*aClip
),
667 // render bitmap to screen, with given global
668 // alpha. Since the bitmap already contains
669 // pixel-equivalent output, we have to use the
670 // inverse view transformation, adjusted with
671 // the final shape output position (note:
672 // cannot simply change the view
673 // transformation here, as that would affect a
674 // possibly set clip!)
675 ::basegfx::B2DHomMatrix
aBitmapTransform( aCanvasTransform
);
676 OSL_ENSURE( aBitmapTransform
.isInvertible(),
677 "ViewShape::render(): View transformation is singular!" );
679 aBitmapTransform
.invert();
681 const basegfx::B2DHomMatrix
aTranslation(basegfx::utils::createTranslateB2DHomMatrix(
682 aTmpRect
.getMinX(), aTmpRect
.getMinY()));
684 aBitmapTransform
= aBitmapTransform
* aTranslation
;
685 pBitmap
->setTransformation( aBitmapTransform
);
687 // finally, render bitmap alpha-modulated
688 pBitmap
->drawAlphaModulated( nAlpha
);
695 // retrieve shape transformation, _with_ shape translation
696 // to actual page position.
697 const ::basegfx::B2DHomMatrix
aTransform(
698 getShapeTransformation( rBounds
,
701 return draw( rDestinationCanvas
,
705 !aClip
? nullptr : &(*aClip
),
710 ViewShape::ViewShape( ViewLayerSharedPtr xViewLayer
) :
711 mpViewLayer(std::move( xViewLayer
)),
714 mbAnimationMode( false ),
715 mbForceUpdate( true )
717 ENSURE_OR_THROW( mpViewLayer
, "ViewShape::ViewShape(): Invalid View" );
720 const ViewLayerSharedPtr
& ViewShape::getViewLayer() const
725 ViewShape::RendererCacheVector::iterator
ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr
& rDestinationCanvas
) const
727 // lookup destination canvas - is there already a renderer
728 // created for that target?
729 RendererCacheVector::iterator aIter
;
730 const RendererCacheVector::iterator
aEnd( maRenderers
.end() );
733 if( (aIter
=::std::find_if( maRenderers
.begin(),
735 [&rDestinationCanvas
]( const RendererCacheEntry
& rCacheEntry
)
736 { return rDestinationCanvas
== rCacheEntry
.getDestinationCanvas(); } ) ) == aEnd
)
738 if( maRenderers
.size() >= MAX_RENDER_CACHE_ENTRIES
)
740 // cache size exceeded - prune entries. For now,
741 // simply remove the first one, which of course
742 // breaks for more complex access schemes. But in
743 // general, this leads to most recently used
744 // entries to reside at the end of the vector.
745 maRenderers
.erase( maRenderers
.begin() );
747 // ATTENTION: after this, both aIter and aEnd are
751 // not yet in cache - add default-constructed cache
752 // entry, to have something to return
753 maRenderers
.emplace_back( );
754 aIter
= maRenderers
.end()-1;
760 ::cppcanvas::RendererSharedPtr
ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr
& rDestinationCanvas
,
761 const GDIMetaFileSharedPtr
& rMtf
,
762 const ShapeAttributeLayerSharedPtr
& rAttr
) const
764 // lookup destination canvas - is there already a renderer
765 // created for that target?
766 const RendererCacheVector::iterator
aIter(
767 getCacheEntry( rDestinationCanvas
) );
769 // now we have a valid entry, either way. call prefetch()
770 // on it, nevertheless - maybe the metafile changed, and
771 // the renderer still needs an update (prefetch() will
773 if( prefetch( *aIter
,
778 return aIter
->mpRenderer
;
782 // prefetch failed - renderer is invalid
783 return ::cppcanvas::RendererSharedPtr();
787 void ViewShape::invalidateRenderer() const
789 // simply clear the cache. Subsequent getRenderer() calls
790 // will regenerate the Renderers.
794 ::basegfx::B2DSize
ViewShape::getAntialiasingBorder() const
796 ENSURE_OR_THROW( mpViewLayer
->getCanvas(),
797 "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" );
799 const ::basegfx::B2DHomMatrix
& rViewTransform(
800 mpViewLayer
->getTransformation() );
802 // TODO(F1): As a quick shortcut (did not want to invert
803 // whole matrix here), taking only scale components of
804 // view transformation matrix. This will be wrong when
805 // e.g. shearing is involved.
806 const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
/ rViewTransform
.get(0,0) );
807 const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE
/ rViewTransform
.get(1,1) );
809 return ::basegfx::B2DSize( nXBorder
,
813 void ViewShape::enterAnimationMode()
815 mbForceUpdate
= true;
816 mbAnimationMode
= true;
819 void ViewShape::leaveAnimationMode()
822 mbAnimationMode
= false;
823 mbForceUpdate
= true;
826 bool ViewShape::update( const GDIMetaFileSharedPtr
& rMtf
,
827 const RenderArgs
& rArgs
,
828 UpdateFlags nUpdateFlags
,
829 bool bIsVisible
) const
831 ENSURE_OR_RETURN_FALSE( mpViewLayer
->getCanvas(), "ViewShape::update(): Invalid layer canvas" );
833 // Shall we render to a sprite, or to a plain canvas?
834 if( mbAnimationMode
)
835 return renderSprite( mpViewLayer
,
843 rArgs
.mnShapePriority
,
846 return render( mpViewLayer
->getCanvas(),
849 rArgs
.maUpdateBounds
,
858 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */