build fix
[LibreOffice.git] / slideshow / source / engine / shapes / viewshape.cxx
blob7ea7bcc5e98a81c26db16859162d72d0de9ae5b2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <tools/diagnose_ex.h>
23 #include <math.h>
25 #include <rtl/math.hxx>
27 #include <com/sun/star/rendering/XCanvas.hpp>
28 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
29 #include <com/sun/star/rendering/PanoseLetterForm.hpp>
30 #include <com/sun/star/awt/FontSlant.hpp>
32 #include <cppuhelper/exc_hlp.hxx>
33 #include <comphelper/anytostring.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include <basegfx/numeric/ftools.hxx>
37 #include <basegfx/matrix/b2dhommatrix.hxx>
38 #include <basegfx/matrix/b2dhommatrixtools.hxx>
40 #include <canvas/canvastools.hxx>
41 #include <cppcanvas/vclfactory.hxx>
42 #include <cppcanvas/basegfxfactory.hxx>
44 #include "viewshape.hxx"
45 #include "tools.hxx"
47 using namespace ::com::sun::star;
49 namespace slideshow
51 namespace internal
54 // TODO(F2): Provide sensible setup for mtf-related attributes (fill mode,
55 // char rotation etc.). Do that via mtf argument at this object
57 bool ViewShape::prefetch( RendererCacheEntry& io_rCacheEntry,
58 const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
59 const GDIMetaFileSharedPtr& rMtf,
60 const ShapeAttributeLayerSharedPtr& rAttr )
62 ENSURE_OR_RETURN_FALSE( rMtf,
63 "ViewShape::prefetch(): no valid metafile!" );
65 if( rMtf != io_rCacheEntry.mpMtf ||
66 rDestinationCanvas != io_rCacheEntry.getDestinationCanvas() )
68 // buffered renderer invalid, re-create
69 ::cppcanvas::Renderer::Parameters aParms;
71 // rendering attribute override parameter struct. For
72 // every valid attribute, the corresponding struct
73 // member is filled, which in the metafile renderer
74 // forces rendering with the given attribute.
75 if( rAttr )
77 if( rAttr->isFillColorValid() )
79 // convert RGBColor to RGBA32 integer. Note
80 // that getIntegerColor() also truncates
81 // out-of-range values appropriately
82 aParms.maFillColor =
83 rAttr->getFillColor().getIntegerColor();
85 if( rAttr->isLineColorValid() )
87 // convert RGBColor to RGBA32 integer. Note
88 // that getIntegerColor() also truncates
89 // out-of-range values appropriately
90 aParms.maLineColor =
91 rAttr->getLineColor().getIntegerColor();
93 if( rAttr->isCharColorValid() )
95 // convert RGBColor to RGBA32 integer. Note
96 // that getIntegerColor() also truncates
97 // out-of-range values appropriately
98 aParms.maTextColor =
99 rAttr->getCharColor().getIntegerColor();
101 if( rAttr->isDimColorValid() )
103 // convert RGBColor to RGBA32 integer. Note
104 // that getIntegerColor() also truncates
105 // out-of-range values appropriately
107 // dim color overrides all other colors
108 aParms.maFillColor =
109 aParms.maLineColor =
110 aParms.maTextColor =
111 rAttr->getDimColor().getIntegerColor();
113 if( rAttr->isFontFamilyValid() )
115 aParms.maFontName =
116 rAttr->getFontFamily();
118 if( rAttr->isCharScaleValid() )
120 ::basegfx::B2DHomMatrix aMatrix;
122 // enlarge text by given scale factor. Do that
123 // with the middle of the shape as the center
124 // of scaling.
125 aMatrix.translate( -0.5, -0.5 );
126 aMatrix.scale( rAttr->getCharScale(),
127 rAttr->getCharScale() );
128 aMatrix.translate( 0.5, 0.5 );
130 aParms.maTextTransformation = aMatrix;
132 if( rAttr->isCharWeightValid() )
134 aParms.maFontWeight =
135 static_cast< sal_Int8 >(
136 ::basegfx::fround(
137 ::std::max( 0.0,
138 ::std::min( 11.0,
139 rAttr->getCharWeight() / 20.0 ) ) ) );
141 if( rAttr->isCharPostureValid() )
143 aParms.maFontLetterForm =
144 rAttr->getCharPosture() == awt::FontSlant_NONE ?
145 rendering::PanoseLetterForm::ANYTHING :
146 rendering::PanoseLetterForm::OBLIQUE_CONTACT;
148 if( rAttr->isUnderlineModeValid() )
150 aParms.maFontUnderline =
151 rAttr->getUnderlineMode();
155 io_rCacheEntry.mpRenderer = ::cppcanvas::VCLFactory::createRenderer( rDestinationCanvas,
156 *rMtf.get(),
157 aParms );
159 io_rCacheEntry.mpMtf = rMtf;
160 io_rCacheEntry.mpDestinationCanvas = rDestinationCanvas;
162 // also invalidate alpha compositing bitmap (created
163 // new renderer, which possibly generates different
164 // output). Do NOT invalidate, if we're incidentally
165 // rendering INTO it.
166 if( rDestinationCanvas != io_rCacheEntry.mpLastBitmapCanvas )
168 io_rCacheEntry.mpLastBitmapCanvas.reset();
169 io_rCacheEntry.mpLastBitmap.reset();
173 return static_cast< bool >(io_rCacheEntry.mpRenderer);
176 bool ViewShape::draw( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
177 const GDIMetaFileSharedPtr& rMtf,
178 const ShapeAttributeLayerSharedPtr& rAttr,
179 const ::basegfx::B2DHomMatrix& rTransform,
180 const ::basegfx::B2DPolyPolygon* pClip,
181 const VectorOfDocTreeNodes& rSubsets ) const
183 ::cppcanvas::RendererSharedPtr pRenderer(
184 getRenderer( rDestinationCanvas, rMtf, rAttr ) );
186 ENSURE_OR_RETURN_FALSE( pRenderer, "ViewShape::draw(): Invalid renderer" );
188 pRenderer->setTransformation( rTransform );
189 #if OSL_DEBUG_LEVEL >= 2
190 rendering::RenderState aRenderState;
191 ::canvas::tools::initRenderState(aRenderState);
192 ::canvas::tools::setRenderStateTransform(aRenderState,
193 rTransform);
194 aRenderState.DeviceColor.realloc(4);
195 aRenderState.DeviceColor[0] = 1.0;
196 aRenderState.DeviceColor[1] = 0.0;
197 aRenderState.DeviceColor[2] = 0.0;
198 aRenderState.DeviceColor[3] = 1.0;
202 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(0.0,0.0),
203 geometry::RealPoint2D(1.0,1.0),
204 rDestinationCanvas->getViewState(),
205 aRenderState );
206 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0),
207 geometry::RealPoint2D(0.0,1.0),
208 rDestinationCanvas->getViewState(),
209 aRenderState );
211 catch( uno::Exception& )
213 DBG_UNHANDLED_EXCEPTION();
215 #endif
216 if( pClip )
217 pRenderer->setClip( *pClip );
218 else
219 pRenderer->setClip();
221 if( rSubsets.empty() )
223 return pRenderer->draw();
225 else
227 // render subsets of whole metafile
230 bool bRet(true);
231 for( const auto& rSubset : rSubsets )
233 if( !pRenderer->drawSubset( rSubset.getStartIndex(),
234 rSubset.getEndIndex() ) )
235 bRet = false;
238 return bRet;
242 namespace
244 /// Convert untransformed shape update area to device pixel.
245 ::basegfx::B2DRectangle shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix& rCanvasTransformation,
246 const ::basegfx::B2DRectangle& rUntransformedArea )
248 // convert area to pixel, and add anti-aliasing border
250 // TODO(P1): Should the view transform some
251 // day contain rotation/shear, transforming
252 // the original bounds with the total
253 // transformation might result in smaller
254 // overall bounds.
256 ::basegfx::B2DRectangle aBoundsPixel;
257 ::canvas::tools::calcTransformedRectBounds( aBoundsPixel,
258 rUntransformedArea,
259 rCanvasTransformation );
261 // add antialiasing border around the shape (AA
262 // touches pixel _outside_ the nominal bound rect)
263 aBoundsPixel.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
265 return aBoundsPixel;
268 /// Convert shape unit rect to device pixel.
269 ::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& rUnitBounds,
270 const ::basegfx::B2DHomMatrix& rShapeTransformation,
271 const ::basegfx::B2DHomMatrix& rCanvasTransformation,
272 const ShapeAttributeLayerSharedPtr& pAttr )
274 // calc update area for whole shape (including
275 // character scaling)
276 return shapeArea2AreaPixel( rCanvasTransformation,
277 getShapeUpdateArea( rUnitBounds,
278 rShapeTransformation,
279 pAttr ) );
283 bool ViewShape::renderSprite( const ViewLayerSharedPtr& rViewLayer,
284 const GDIMetaFileSharedPtr& rMtf,
285 const ::basegfx::B2DRectangle& rOrigBounds,
286 const ::basegfx::B2DRectangle& rBounds,
287 const ::basegfx::B2DRectangle& rUnitBounds,
288 int nUpdateFlags,
289 const ShapeAttributeLayerSharedPtr& pAttr,
290 const VectorOfDocTreeNodes& rSubsets,
291 double nPrio,
292 bool bIsVisible ) const
294 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
295 // in that all the common setup steps here are refactored to Shape (would then
296 // have to be performed only _once_ per Shape paint).
298 if( !bIsVisible ||
299 rUnitBounds.isEmpty() ||
300 rOrigBounds.isEmpty() ||
301 rBounds.isEmpty() )
303 // shape is invisible or has zero size, no need to
304 // update anything.
305 if( mpSprite )
306 mpSprite->hide();
308 return true;
312 // calc sprite position, size and content transformation
313 // =====================================================
315 // the shape transformation for a sprite is always a
316 // simple scale-up to the nominal shape size. Everything
317 // else is handled via the sprite transformation
318 ::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation;
319 aNonTranslationalShapeTransformation.scale( rOrigBounds.getWidth(),
320 rOrigBounds.getHeight() );
321 ::basegfx::B2DHomMatrix aShapeTransformation( aNonTranslationalShapeTransformation );
322 aShapeTransformation.translate( rOrigBounds.getMinX(),
323 rOrigBounds.getMinY() );
325 const ::basegfx::B2DHomMatrix& rCanvasTransform(
326 rViewLayer->getSpriteTransformation() );
328 // area actually needed for the sprite
329 const ::basegfx::B2DRectangle& rSpriteBoundsPixel(
330 calcUpdateAreaPixel( rUnitBounds,
331 aShapeTransformation,
332 rCanvasTransform,
333 pAttr ) );
335 // actual area for the shape (without subsetting, but
336 // including char scaling)
337 const ::basegfx::B2DRectangle& rShapeBoundsPixel(
338 calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
339 aShapeTransformation,
340 rCanvasTransform,
341 pAttr ) );
343 // nominal area for the shape (without subsetting, without
344 // char scaling). NOTE: to cancel the shape translation,
345 // contained in rSpriteBoundsPixel, this is _without_ any
346 // translational component.
347 ::basegfx::B2DRectangle aLogShapeBounds;
348 const ::basegfx::B2DRectangle& rNominalShapeBoundsPixel(
349 shapeArea2AreaPixel( rCanvasTransform,
350 ::canvas::tools::calcTransformedRectBounds(
351 aLogShapeBounds,
352 ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
353 aNonTranslationalShapeTransformation ) ) );
355 // create (or resize) sprite with sprite's pixel size, if
356 // not done already
357 const ::basegfx::B2DSize& rSpriteSizePixel(rSpriteBoundsPixel.getRange());
358 if( !mpSprite )
360 mpSprite.reset(
361 new AnimatedSprite( mpViewLayer,
362 rSpriteSizePixel,
363 nPrio ));
365 else
367 // TODO(F2): when the sprite _actually_ gets resized,
368 // content needs a repaint!
369 mpSprite->resize( rSpriteSizePixel );
372 ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" );
374 SAL_INFO("slideshow", "ViewShape::renderSprite(): Rendering sprite " <<
375 mpSprite.get() );
378 // always show the sprite (might have been hidden before)
379 mpSprite->show();
381 // determine center of sprite output position in pixel
382 // (assumption here: all shape transformations have the
383 // shape center as the pivot point). From that, subtract
384 // distance of rSpriteBoundsPixel's left, top edge from
385 // rShapeBoundsPixel's center. This moves the sprite at
386 // the appropriate output position within the virtual
387 // rShapeBoundsPixel area.
388 ::basegfx::B2DPoint aSpritePosPixel( rBounds.getCenter() );
389 aSpritePosPixel *= rCanvasTransform;
390 aSpritePosPixel -= rShapeBoundsPixel.getCenter() - rSpriteBoundsPixel.getMinimum();
392 // the difference between rShapeBoundsPixel and
393 // rSpriteBoundsPixel upper, left corner is: the offset we
394 // have to move sprite output to the right, top (to make
395 // the desired subset content visible at all)
396 const ::basegfx::B2DSize& rSpriteCorrectionOffset(
397 rSpriteBoundsPixel.getMinimum() - rNominalShapeBoundsPixel.getMinimum() );
399 // offset added top, left for anti-aliasing (otherwise,
400 // shapes fully filling the sprite will have anti-aliased
401 // pixel cut off)
402 const ::basegfx::B2DSize aAAOffset(
403 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE,
404 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
406 // set pixel output offset to sprite: we always leave
407 // ANTIALIASING_EXTRA_SIZE room atop and to the left, and,
408 // what's more, for subsetted shapes, we _have_ to cancel
409 // the effect of the shape renderer outputting the subset
410 // at its absolute position inside the shape, instead of
411 // at the origin.
412 // NOTE: As for now, sprites are always positioned on
413 // integer pixel positions on screen, have to round to
414 // nearest integer here, too
415 mpSprite->setPixelOffset(
416 aAAOffset - ::basegfx::B2DSize(
417 ::basegfx::fround( rSpriteCorrectionOffset.getX() ),
418 ::basegfx::fround( rSpriteCorrectionOffset.getY() ) ) );
420 // always set sprite position and transformation, since
421 // they do not relate directly to the update flags
422 // (e.g. sprite position changes when sprite size changes)
423 mpSprite->movePixel( aSpritePosPixel );
424 mpSprite->transform( getSpriteTransformation( rSpriteSizePixel,
425 rOrigBounds.getRange(),
426 pAttr ) );
429 // process flags
430 // =============
432 bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & FORCE) );
434 if( mbForceUpdate || (nUpdateFlags & ALPHA) )
436 mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ?
437 ::basegfx::clamp(pAttr->getAlpha(),
438 0.0,
439 1.0) :
440 1.0 );
442 if( mbForceUpdate || (nUpdateFlags & CLIP) )
444 if( pAttr && pAttr->isClipValid() )
446 ::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() );
448 // extract linear part of canvas view transformation
449 // (linear means: without translational components)
450 ::basegfx::B2DHomMatrix aViewTransform(
451 mpViewLayer->getTransformation() );
452 aViewTransform.set( 0, 2, 0.0 );
453 aViewTransform.set( 1, 2, 0.0 );
455 // make the clip 2*ANTIALIASING_EXTRA_SIZE larger
456 // such that it's again centered over the sprite.
457 aViewTransform.scale(rSpriteSizePixel.getX()/
458 (rSpriteSizePixel.getX()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE),
459 rSpriteSizePixel.getY()/
460 (rSpriteSizePixel.getY()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE));
462 // transform clip polygon from view to device
463 // coordinate space
464 aClipPoly.transform( aViewTransform );
466 mpSprite->clip( aClipPoly );
468 else
469 mpSprite->clip();
471 if( mbForceUpdate || (nUpdateFlags & CONTENT) )
473 bRedrawRequired = true;
475 // TODO(P1): maybe provide some appearance change methods at
476 // the Renderer interface
478 // force the renderer to be regenerated below, for the
479 // different attributes to take effect
480 invalidateRenderer();
483 mbForceUpdate = false;
485 if( !bRedrawRequired )
486 return true;
489 // sprite needs repaint - output to sprite canvas
490 // ==============================================
492 ::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() );
494 return draw( pContentCanvas,
495 rMtf,
496 pAttr,
497 aShapeTransformation,
498 nullptr, // clipping is done via Sprite::clip()
499 rSubsets );
502 bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
503 const GDIMetaFileSharedPtr& rMtf,
504 const ::basegfx::B2DRectangle& rBounds,
505 const ::basegfx::B2DRectangle& rUpdateBounds,
506 int nUpdateFlags,
507 const ShapeAttributeLayerSharedPtr& pAttr,
508 const VectorOfDocTreeNodes& rSubsets,
509 bool bIsVisible ) const
511 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
512 // in that all the common setup steps here are refactored to Shape (would then
513 // have to be performed only _once_ per Shape paint).
515 if( !bIsVisible )
517 SAL_INFO("slideshow", "ViewShape::render(): skipping shape " << this );
519 // shape is invisible, no need to update anything.
520 return true;
523 // since we have no sprite here, _any_ update request
524 // translates into a required redraw.
525 bool bRedrawRequired( mbForceUpdate || nUpdateFlags != 0 );
527 if( (nUpdateFlags & CONTENT) )
529 // TODO(P1): maybe provide some appearance change methods at
530 // the Renderer interface
532 // force the renderer to be regenerated below, for the
533 // different attributes to take effect
534 invalidateRenderer();
537 mbForceUpdate = false;
539 if( !bRedrawRequired )
540 return true;
542 SAL_INFO( "slideshow", "ViewShape::render(): rendering shape " <<
543 this <<
544 " at position (" <<
545 rBounds.getMinX() << "," <<
546 rBounds.getMinY() << ")" );
549 // shape needs repaint - setup all that's needed
552 boost::optional<basegfx::B2DPolyPolygon> aClip;
554 if( pAttr )
556 // setup clip poly
557 if( pAttr->isClipValid() )
558 aClip.reset( pAttr->getClip() );
560 // emulate global shape alpha by first rendering into
561 // a temp bitmap, and then to screen (this would have
562 // been much easier if we'd be currently a sprite -
563 // see above)
564 if( pAttr->isAlphaValid() )
566 const double nAlpha( pAttr->getAlpha() );
568 if( !::basegfx::fTools::equalZero( nAlpha ) &&
569 !::rtl::math::approxEqual(nAlpha, 1.0) )
571 // render with global alpha - have to prepare
572 // a bitmap, and render that with modulated
573 // alpha
576 const ::basegfx::B2DHomMatrix aTransform(
577 getShapeTransformation( rBounds,
578 pAttr ) );
580 // TODO(P1): Should the view transform some
581 // day contain rotation/shear, transforming
582 // the original bounds with the total
583 // transformation might result in smaller
584 // overall bounds.
586 // determine output rect of _shape update
587 // area_ in device pixel
588 const ::basegfx::B2DHomMatrix aCanvasTransform(
589 rDestinationCanvas->getTransformation() );
590 ::basegfx::B2DRectangle aTmpRect;
591 ::canvas::tools::calcTransformedRectBounds( aTmpRect,
592 rUpdateBounds,
593 aCanvasTransform );
595 // pixel size of cache bitmap: round up to
596 // nearest int
597 const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1,
598 static_cast<sal_Int32>( aTmpRect.getHeight() )+1 );
600 // try to fetch temporary surface for alpha
601 // compositing (to achieve the global alpha
602 // blend effect, have to first render shape as
603 // a whole, then blit that surface with global
604 // alpha to the destination)
605 const RendererCacheVector::iterator aCompositingSurface(
606 getCacheEntry( rDestinationCanvas ) );
608 if( !aCompositingSurface->mpLastBitmapCanvas ||
609 aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize )
611 // create a bitmap of appropriate size
612 ::cppcanvas::BitmapSharedPtr pBitmap(
613 ::cppcanvas::BaseGfxFactory::createAlphaBitmap(
614 rDestinationCanvas,
615 aBmpSize ) );
617 ENSURE_OR_THROW(pBitmap,
618 "ViewShape::render(): Could not create compositing surface");
620 aCompositingSurface->mpDestinationCanvas = rDestinationCanvas;
621 aCompositingSurface->mpLastBitmap = pBitmap;
622 aCompositingSurface->mpLastBitmapCanvas = pBitmap->getBitmapCanvas();
625 // buffer aCompositingSurface iterator content
626 // - said one might get invalidated during
627 // draw() below.
628 ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas(
629 aCompositingSurface->mpLastBitmapCanvas );
631 ::cppcanvas::BitmapSharedPtr pBitmap(
632 aCompositingSurface->mpLastBitmap);
634 // setup bitmap canvas transformation -
635 // which happens to be the destination
636 // canvas transformation without any
637 // translational components.
639 // But then, the render transformation as
640 // calculated by getShapeTransformation()
641 // above outputs the shape at its real
642 // destination position. Thus, we have to
643 // offset the output back to the origin,
644 // for which we simply plug in the
645 // negative position of the left, top edge
646 // of the shape's bound rect in device
647 // pixel into aLinearTransform below.
648 ::basegfx::B2DHomMatrix aAdjustedCanvasTransform( aCanvasTransform );
649 aAdjustedCanvasTransform.translate( -aTmpRect.getMinX(),
650 -aTmpRect.getMinY() );
652 pBitmapCanvas->setTransformation( aAdjustedCanvasTransform );
654 // TODO(P2): If no update flags, or only
655 // alpha_update is set, we can save us the
656 // rendering into the bitmap (uh, it's not
657 // _that_ easy - for a forced redraw,
658 // e.g. when ending an animation, we always
659 // get UPDATE_FORCE here).
661 // render into this bitmap
662 if( !draw( pBitmapCanvas,
663 rMtf,
664 pAttr,
665 aTransform,
666 !aClip ? nullptr : &(*aClip),
667 rSubsets ) )
669 return false;
672 // render bitmap to screen, with given global
673 // alpha. Since the bitmap already contains
674 // pixel-equivalent output, we have to use the
675 // inverse view transformation, adjusted with
676 // the final shape output position (note:
677 // cannot simply change the view
678 // transformation here, as that would affect a
679 // possibly set clip!)
680 ::basegfx::B2DHomMatrix aBitmapTransform( aCanvasTransform );
681 OSL_ENSURE( aBitmapTransform.isInvertible(),
682 "ViewShape::render(): View transformation is singular!" );
684 aBitmapTransform.invert();
686 const basegfx::B2DHomMatrix aTranslation(basegfx::tools::createTranslateB2DHomMatrix(
687 aTmpRect.getMinX(), aTmpRect.getMinY()));
689 aBitmapTransform = aBitmapTransform * aTranslation;
690 pBitmap->setTransformation( aBitmapTransform );
692 // finally, render bitmap alpha-modulated
693 pBitmap->drawAlphaModulated( nAlpha );
695 return true;
700 // retrieve shape transformation, _with_ shape translation
701 // to actual page position.
702 const ::basegfx::B2DHomMatrix aTransform(
703 getShapeTransformation( rBounds,
704 pAttr ) );
706 return draw( rDestinationCanvas,
707 rMtf,
708 pAttr,
709 aTransform,
710 !aClip ? nullptr : &(*aClip),
711 rSubsets );
715 ViewShape::ViewShape( const ViewLayerSharedPtr& rViewLayer ) :
716 mpViewLayer( rViewLayer ),
717 maRenderers(),
718 mpSprite(),
719 mbAnimationMode( false ),
720 mbForceUpdate( true )
722 ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" );
725 const ViewLayerSharedPtr& ViewShape::getViewLayer() const
727 return mpViewLayer;
730 ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const
732 // lookup destination canvas - is there already a renderer
733 // created for that target?
734 RendererCacheVector::iterator aIter;
735 const RendererCacheVector::iterator aEnd( maRenderers.end() );
737 // already there?
738 if( (aIter=::std::find_if( maRenderers.begin(),
739 aEnd,
740 [&rDestinationCanvas]( const RendererCacheEntry& rCacheEntry )
741 { return rDestinationCanvas == rCacheEntry.getDestinationCanvas(); } ) ) == aEnd )
743 if( maRenderers.size() >= MAX_RENDER_CACHE_ENTRIES )
745 // cache size exceeded - prune entries. For now,
746 // simply remove the first one, which of course
747 // breaks for more complex access schemes. But in
748 // general, this leads to most recently used
749 // entries to reside at the end of the vector.
750 maRenderers.erase( maRenderers.begin() );
752 // ATTENTION: after this, both aIter and aEnd are
753 // invalid!
756 // not yet in cache - add default-constructed cache
757 // entry, to have something to return
758 maRenderers.push_back( RendererCacheEntry() );
759 aIter = maRenderers.end()-1;
762 return aIter;
765 ::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
766 const GDIMetaFileSharedPtr& rMtf,
767 const ShapeAttributeLayerSharedPtr& rAttr ) const
769 // lookup destination canvas - is there already a renderer
770 // created for that target?
771 const RendererCacheVector::iterator aIter(
772 getCacheEntry( rDestinationCanvas ) );
774 // now we have a valid entry, either way. call prefetch()
775 // on it, nevertheless - maybe the metafile changed, and
776 // the renderer still needs an update (prefetch() will
777 // detect that)
778 if( prefetch( *aIter,
779 rDestinationCanvas,
780 rMtf,
781 rAttr ) )
783 return aIter->mpRenderer;
785 else
787 // prefetch failed - renderer is invalid
788 return ::cppcanvas::RendererSharedPtr();
792 void ViewShape::invalidateRenderer() const
794 // simply clear the cache. Subsequent getRenderer() calls
795 // will regenerate the Renderers.
796 maRenderers.clear();
799 ::basegfx::B2DSize ViewShape::getAntialiasingBorder() const
801 ENSURE_OR_THROW( mpViewLayer->getCanvas(),
802 "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" );
804 const ::basegfx::B2DHomMatrix& rViewTransform(
805 mpViewLayer->getTransformation() );
807 // TODO(F1): As a quick shortcut (did not want to invert
808 // whole matrix here), taking only scale components of
809 // view transformation matrix. This will be wrong when
810 // e.g. shearing is involved.
811 const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(0,0) );
812 const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(1,1) );
814 return ::basegfx::B2DSize( nXBorder,
815 nYBorder );
818 bool ViewShape::enterAnimationMode()
820 mbForceUpdate = true;
821 mbAnimationMode = true;
823 return true;
826 void ViewShape::leaveAnimationMode()
828 mpSprite.reset();
829 mbAnimationMode = false;
830 mbForceUpdate = true;
833 bool ViewShape::update( const GDIMetaFileSharedPtr& rMtf,
834 const RenderArgs& rArgs,
835 int nUpdateFlags,
836 bool bIsVisible ) const
838 ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" );
840 // Shall we render to a sprite, or to a plain canvas?
841 if( mbAnimationMode )
842 return renderSprite( mpViewLayer,
843 rMtf,
844 rArgs.maOrigBounds,
845 rArgs.maBounds,
846 rArgs.maUnitBounds,
847 nUpdateFlags,
848 rArgs.mrAttr,
849 rArgs.mrSubsets,
850 rArgs.mnShapePriority,
851 bIsVisible );
852 else
853 return render( mpViewLayer->getCanvas(),
854 rMtf,
855 rArgs.maBounds,
856 rArgs.maUpdateBounds,
857 nUpdateFlags,
858 rArgs.mrAttr,
859 rArgs.mrSubsets,
860 bIsVisible );
866 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */