tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / slideshow / source / engine / shapes / viewshape.cxx
blobb29afe8e464236b8bed23a82c18b109db3019cfc
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 .
20 #include <sal/config.h>
22 #include <comphelper/diagnose_ex.hxx>
24 #include <algorithm>
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"
41 #include <tools.hxx>
42 #include <utility>
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.
70 if( rAttr )
72 if( rAttr->isFillColorValid() )
74 // convert RGBColor to RGBA32 integer. Note
75 // that getIntegerColor() also truncates
76 // out-of-range values appropriately
77 aParms.maFillColor =
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
85 aParms.maLineColor =
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
93 aParms.maTextColor =
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
103 aParms.maFillColor =
104 aParms.maLineColor =
105 aParms.maTextColor =
106 rAttr->getDimColor().getIntegerColor();
108 if( rAttr->isFontFamilyValid() )
110 aParms.maFontName =
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
119 // of scaling.
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 >(
131 ::basegfx::fround(
132 ::std::max( 0.0,
133 ::std::min( 11.0,
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,
151 *rMtf,
152 aParms );
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,
188 rTransform);
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(),
200 aRenderState );
201 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0),
202 geometry::RealPoint2D(0.0,1.0),
203 rDestinationCanvas->getViewState(),
204 aRenderState );
206 catch( uno::Exception& )
208 DBG_UNHANDLED_EXCEPTION("slideshow");
210 #endif
211 if( pClip )
212 pRenderer->setClip( *pClip );
213 else
214 pRenderer->setClip();
216 if( rSubsets.empty() )
218 return pRenderer->draw();
220 else
222 // render subsets of whole metafile
225 bool bRet(true);
226 for( const auto& rSubset : rSubsets )
228 if( !pRenderer->drawSubset( rSubset.getStartIndex(),
229 rSubset.getEndIndex() ) )
230 bRet = false;
233 return bRet;
237 namespace
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
249 // overall bounds.
251 ::basegfx::B2DRectangle aBoundsPixel = ::canvas::tools::calcTransformedRectBounds(
252 rUntransformedArea,
253 rCanvasTransformation );
255 // add antialiasing border around the shape (AA
256 // touches pixel _outside_ the nominal bound rect)
257 aBoundsPixel.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
259 return aBoundsPixel;
262 /// Convert shape unit rect to device pixel.
263 ::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& rUnitBounds,
264 const ::basegfx::B2DHomMatrix& rShapeTransformation,
265 const ::basegfx::B2DHomMatrix& rCanvasTransformation,
266 const ShapeAttributeLayerSharedPtr& pAttr )
268 // calc update area for whole shape (including
269 // character scaling)
270 return shapeArea2AreaPixel( rCanvasTransformation,
271 getShapeUpdateArea( rUnitBounds,
272 rShapeTransformation,
273 pAttr ) );
277 bool ViewShape::renderSprite( const ViewLayerSharedPtr& rViewLayer,
278 const GDIMetaFileSharedPtr& rMtf,
279 const ::basegfx::B2DRectangle& rOrigBounds,
280 const ::basegfx::B2DRectangle& rBounds,
281 const ::basegfx::B2DRectangle& rUnitBounds,
282 UpdateFlags nUpdateFlags,
283 const ShapeAttributeLayerSharedPtr& pAttr,
284 const VectorOfDocTreeNodes& rSubsets,
285 double nPrio,
286 bool bIsVisible ) const
288 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
289 // in that all the common setup steps here are refactored to Shape (would then
290 // have to be performed only _once_ per Shape paint).
292 if( !bIsVisible ||
293 rUnitBounds.isEmpty() ||
294 rOrigBounds.isEmpty() ||
295 rBounds.isEmpty() )
297 // shape is invisible or has zero size, no need to
298 // update anything.
299 if( mpSprite )
300 mpSprite->hide();
302 return true;
306 // calc sprite position, size and content transformation
307 // =====================================================
309 // the shape transformation for a sprite is always a
310 // simple scale-up to the nominal shape size. Everything
311 // else is handled via the sprite transformation
312 ::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation;
313 aNonTranslationalShapeTransformation.scale( rOrigBounds.getWidth(),
314 rOrigBounds.getHeight() );
315 ::basegfx::B2DHomMatrix aShapeTransformation( aNonTranslationalShapeTransformation );
316 aShapeTransformation.translate( rOrigBounds.getMinX(),
317 rOrigBounds.getMinY() );
319 const ::basegfx::B2DHomMatrix aCanvasTransform(
320 rViewLayer->getSpriteTransformation() );
322 // area actually needed for the sprite
323 const ::basegfx::B2DRectangle aSpriteBoundsPixel(
324 calcUpdateAreaPixel( rUnitBounds,
325 aShapeTransformation,
326 aCanvasTransform,
327 pAttr ) );
329 // actual area for the shape (without subsetting, but
330 // including char scaling)
331 const ::basegfx::B2DRectangle aShapeBoundsPixel(
332 calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
333 aShapeTransformation,
334 aCanvasTransform,
335 pAttr ) );
337 // nominal area for the shape (without subsetting, without
338 // char scaling). NOTE: to cancel the shape translation,
339 // contained in rSpriteBoundsPixel, this is _without_ any
340 // translational component.
341 const ::basegfx::B2DRectangle aNominalShapeBoundsPixel(
342 shapeArea2AreaPixel( aCanvasTransform,
343 ::canvas::tools::calcTransformedRectBounds(
344 ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
345 aNonTranslationalShapeTransformation ) ) );
347 // create (or resize) sprite with sprite's pixel size, if
348 // not done already
349 auto aRange = aSpriteBoundsPixel.getRange();
350 basegfx::B2DSize rSpriteSizePixel(aRange.getX(), aRange.getY());
351 if( !mpSprite )
353 mpSprite = std::make_shared<AnimatedSprite>( mpViewLayer,
354 rSpriteSizePixel,
355 nPrio );
357 else
359 // TODO(F2): when the sprite _actually_ gets resized,
360 // content needs a repaint!
361 mpSprite->resize( rSpriteSizePixel );
364 ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" );
366 SAL_INFO("slideshow", "ViewShape::renderSprite(): Rendering sprite " <<
367 mpSprite.get() );
370 // always show the sprite (might have been hidden before)
371 mpSprite->show();
373 // determine center of sprite output position in pixel
374 // (assumption here: all shape transformations have the
375 // shape center as the pivot point). From that, subtract
376 // distance of rSpriteBoundsPixel's left, top edge from
377 // rShapeBoundsPixel's center. This moves the sprite at
378 // the appropriate output position within the virtual
379 // rShapeBoundsPixel area.
380 ::basegfx::B2DPoint aSpritePosPixel( rBounds.getCenter() );
381 aSpritePosPixel *= aCanvasTransform;
382 aSpritePosPixel -= aShapeBoundsPixel.getCenter() - aSpriteBoundsPixel.getMinimum();
384 // the difference between rShapeBoundsPixel and
385 // rSpriteBoundsPixel upper, left corner is: the offset we
386 // have to move sprite output to the right, top (to make
387 // the desired subset content visible at all)
388 auto aDifference = aSpriteBoundsPixel.getMinimum() - aNominalShapeBoundsPixel.getMinimum();
389 const basegfx::B2DSize rSpriteCorrectionOffset(aDifference.getX(), aDifference.getY());
391 // offset added top, left for anti-aliasing (otherwise,
392 // shapes fully filling the sprite will have anti-aliased
393 // pixel cut off)
394 const ::basegfx::B2DSize aAAOffset(
395 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE,
396 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
398 // set pixel output offset to sprite: we always leave
399 // ANTIALIASING_EXTRA_SIZE room atop and to the left, and,
400 // what's more, for subsetted shapes, we _have_ to cancel
401 // the effect of the shape renderer outputting the subset
402 // at its absolute position inside the shape, instead of
403 // at the origin.
404 // NOTE: As for now, sprites are always positioned on
405 // integer pixel positions on screen, have to round to
406 // nearest integer here, too
407 mpSprite->setPixelOffset(
408 aAAOffset - ::basegfx::B2DSize(
409 ::basegfx::fround( rSpriteCorrectionOffset.getWidth() ),
410 ::basegfx::fround( rSpriteCorrectionOffset.getHeight() ) ) );
412 // always set sprite position and transformation, since
413 // they do not relate directly to the update flags
414 // (e.g. sprite position changes when sprite size changes)
415 mpSprite->movePixel( aSpritePosPixel );
416 mpSprite->transform( getSpriteTransformation( basegfx::B2DVector(rSpriteSizePixel.getWidth(), rSpriteSizePixel.getHeight()),
417 rOrigBounds.getRange(),
418 pAttr ) );
421 // process flags
422 // =============
424 bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & UpdateFlags::Force) );
426 if( mbForceUpdate || (nUpdateFlags & UpdateFlags::Alpha) )
428 mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ?
429 std::clamp(pAttr->getAlpha(),
430 0.0,
431 1.0) :
432 1.0 );
434 if( mbForceUpdate || (nUpdateFlags & UpdateFlags::Clip) )
436 if( pAttr && pAttr->isClipValid() )
438 ::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() );
440 // extract linear part of canvas view transformation
441 // (linear means: without translational components)
442 ::basegfx::B2DHomMatrix aViewTransform(
443 mpViewLayer->getTransformation() );
444 aViewTransform.set( 0, 2, 0.0 );
445 aViewTransform.set( 1, 2, 0.0 );
447 // make the clip 2*ANTIALIASING_EXTRA_SIZE larger
448 // such that it's again centered over the sprite.
449 aViewTransform.scale(rSpriteSizePixel.getWidth()/
450 (rSpriteSizePixel.getWidth()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE),
451 rSpriteSizePixel.getHeight()/
452 (rSpriteSizePixel.getHeight()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE));
454 // transform clip polygon from view to device
455 // coordinate space
456 aClipPoly.transform( aViewTransform );
458 mpSprite->clip( aClipPoly );
460 else
461 mpSprite->clip();
463 if( mbForceUpdate || (nUpdateFlags & UpdateFlags::Content) )
465 bRedrawRequired = true;
467 // TODO(P1): maybe provide some appearance change methods at
468 // the Renderer interface
470 // force the renderer to be regenerated below, for the
471 // different attributes to take effect
472 invalidateRenderer();
475 mbForceUpdate = false;
477 if( !bRedrawRequired )
478 return true;
481 // sprite needs repaint - output to sprite canvas
482 // ==============================================
484 ::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() );
486 return draw( pContentCanvas,
487 rMtf,
488 pAttr,
489 aShapeTransformation,
490 nullptr, // clipping is done via Sprite::clip()
491 rSubsets );
494 bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
495 const GDIMetaFileSharedPtr& rMtf,
496 const ::basegfx::B2DRectangle& rBounds,
497 const ::basegfx::B2DRectangle& rUpdateBounds,
498 UpdateFlags nUpdateFlags,
499 const ShapeAttributeLayerSharedPtr& pAttr,
500 const VectorOfDocTreeNodes& rSubsets,
501 bool bIsVisible ) const
503 // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
504 // in that all the common setup steps here are refactored to Shape (would then
505 // have to be performed only _once_ per Shape paint).
507 if( !bIsVisible )
509 SAL_INFO("slideshow", "ViewShape::render(): skipping shape " << this );
511 // shape is invisible, no need to update anything.
512 return true;
515 // since we have no sprite here, _any_ update request
516 // translates into a required redraw.
517 bool bRedrawRequired( mbForceUpdate || nUpdateFlags != UpdateFlags::NONE );
519 if( nUpdateFlags & UpdateFlags::Content )
521 // TODO(P1): maybe provide some appearance change methods at
522 // the Renderer interface
524 // force the renderer to be regenerated below, for the
525 // different attributes to take effect
526 invalidateRenderer();
529 mbForceUpdate = false;
531 if( !bRedrawRequired )
532 return true;
534 SAL_INFO( "slideshow", "ViewShape::render(): rendering shape " <<
535 this <<
536 " at position (" <<
537 rBounds.getMinX() << "," <<
538 rBounds.getMinY() << ")" );
541 // shape needs repaint - setup all that's needed
544 std::optional<basegfx::B2DPolyPolygon> aClip;
546 if( pAttr )
548 // setup clip poly
549 if( pAttr->isClipValid() )
550 aClip = pAttr->getClip();
552 // emulate global shape alpha by first rendering into
553 // a temp bitmap, and then to screen (this would have
554 // been much easier if we'd be currently a sprite -
555 // see above)
556 if( pAttr->isAlphaValid() )
558 const double nAlpha( pAttr->getAlpha() );
560 if( !::basegfx::fTools::equalZero( nAlpha ) &&
561 !::rtl::math::approxEqual(nAlpha, 1.0) )
563 // render with global alpha - have to prepare
564 // a bitmap, and render that with modulated
565 // alpha
568 const ::basegfx::B2DHomMatrix aTransform(
569 getShapeTransformation( rBounds,
570 pAttr ) );
572 // TODO(P1): Should the view transform some
573 // day contain rotation/shear, transforming
574 // the original bounds with the total
575 // transformation might result in smaller
576 // overall bounds.
578 // determine output rect of _shape update
579 // area_ in device pixel
580 const ::basegfx::B2DHomMatrix aCanvasTransform(
581 rDestinationCanvas->getTransformation() );
582 ::basegfx::B2DRectangle aTmpRect = ::canvas::tools::calcTransformedRectBounds(
583 rUpdateBounds,
584 aCanvasTransform );
586 // pixel size of cache bitmap: round up to
587 // nearest int
588 const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1,
589 static_cast<sal_Int32>( aTmpRect.getHeight() )+1 );
591 // try to fetch temporary surface for alpha
592 // compositing (to achieve the global alpha
593 // blend effect, have to first render shape as
594 // a whole, then blit that surface with global
595 // alpha to the destination)
596 const RendererCacheVector::iterator aCompositingSurface(
597 getCacheEntry( rDestinationCanvas ) );
599 if( !aCompositingSurface->mpLastBitmapCanvas ||
600 aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize )
602 // create a bitmap of appropriate size
603 ::cppcanvas::BitmapSharedPtr pBitmap(
604 ::cppcanvas::BaseGfxFactory::createAlphaBitmap(
605 rDestinationCanvas,
606 aBmpSize ) );
608 ENSURE_OR_THROW(pBitmap,
609 "ViewShape::render(): Could not create compositing surface");
611 aCompositingSurface->mpDestinationCanvas = rDestinationCanvas;
612 aCompositingSurface->mpLastBitmap = pBitmap;
613 aCompositingSurface->mpLastBitmapCanvas = pBitmap->getBitmapCanvas();
616 // buffer aCompositingSurface iterator content
617 // - said one might get invalidated during
618 // draw() below.
619 ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas(
620 aCompositingSurface->mpLastBitmapCanvas );
622 ::cppcanvas::BitmapSharedPtr pBitmap(
623 aCompositingSurface->mpLastBitmap);
625 // setup bitmap canvas transformation -
626 // which happens to be the destination
627 // canvas transformation without any
628 // translational components.
630 // But then, the render transformation as
631 // calculated by getShapeTransformation()
632 // above outputs the shape at its real
633 // destination position. Thus, we have to
634 // offset the output back to the origin,
635 // for which we simply plug in the
636 // negative position of the left, top edge
637 // of the shape's bound rect in device
638 // pixel into aLinearTransform below.
639 ::basegfx::B2DHomMatrix aAdjustedCanvasTransform( aCanvasTransform );
640 aAdjustedCanvasTransform.translate( -aTmpRect.getMinX(),
641 -aTmpRect.getMinY() );
643 pBitmapCanvas->setTransformation( aAdjustedCanvasTransform );
645 // TODO(P2): If no update flags, or only
646 // alpha_update is set, we can save us the
647 // rendering into the bitmap (uh, it's not
648 // _that_ easy - for a forced redraw,
649 // e.g. when ending an animation, we always
650 // get UPDATE_FORCE here).
652 // render into this bitmap
653 if( !draw( pBitmapCanvas,
654 rMtf,
655 pAttr,
656 aTransform,
657 !aClip ? nullptr : &(*aClip),
658 rSubsets ) )
660 return false;
663 // render bitmap to screen, with given global
664 // alpha. Since the bitmap already contains
665 // pixel-equivalent output, we have to use the
666 // inverse view transformation, adjusted with
667 // the final shape output position (note:
668 // cannot simply change the view
669 // transformation here, as that would affect a
670 // possibly set clip!)
671 ::basegfx::B2DHomMatrix aBitmapTransform( aCanvasTransform );
672 OSL_ENSURE( aBitmapTransform.isInvertible(),
673 "ViewShape::render(): View transformation is singular!" );
675 aBitmapTransform.invert();
677 const basegfx::B2DHomMatrix aTranslation(basegfx::utils::createTranslateB2DHomMatrix(
678 aTmpRect.getMinX(), aTmpRect.getMinY()));
680 aBitmapTransform = aBitmapTransform * aTranslation;
681 pBitmap->setTransformation( aBitmapTransform );
683 // finally, render bitmap alpha-modulated
684 pBitmap->drawAlphaModulated( nAlpha );
686 return true;
691 // retrieve shape transformation, _with_ shape translation
692 // to actual page position.
693 const ::basegfx::B2DHomMatrix aTransform(
694 getShapeTransformation( rBounds,
695 pAttr ) );
697 return draw( rDestinationCanvas,
698 rMtf,
699 pAttr,
700 aTransform,
701 !aClip ? nullptr : &(*aClip),
702 rSubsets );
706 ViewShape::ViewShape( ViewLayerSharedPtr xViewLayer ) :
707 mpViewLayer(std::move( xViewLayer )),
708 maRenderers(),
709 mpSprite(),
710 mbAnimationMode( false ),
711 mbForceUpdate( true )
713 ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" );
716 const ViewLayerSharedPtr& ViewShape::getViewLayer() const
718 return mpViewLayer;
721 ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const
723 // lookup destination canvas - is there already a renderer
724 // created for that target?
725 RendererCacheVector::iterator aIter;
726 const RendererCacheVector::iterator aEnd( maRenderers.end() );
728 // already there?
729 if( (aIter=::std::find_if( maRenderers.begin(),
730 aEnd,
731 [&rDestinationCanvas]( const RendererCacheEntry& rCacheEntry )
732 { return rDestinationCanvas == rCacheEntry.getDestinationCanvas(); } ) ) == aEnd )
734 if( maRenderers.size() >= MAX_RENDER_CACHE_ENTRIES )
736 // cache size exceeded - prune entries. For now,
737 // simply remove the first one, which of course
738 // breaks for more complex access schemes. But in
739 // general, this leads to most recently used
740 // entries to reside at the end of the vector.
741 maRenderers.erase( maRenderers.begin() );
743 // ATTENTION: after this, both aIter and aEnd are
744 // invalid!
747 // not yet in cache - add default-constructed cache
748 // entry, to have something to return
749 maRenderers.emplace_back( );
750 aIter = maRenderers.end()-1;
753 return aIter;
756 ::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
757 const GDIMetaFileSharedPtr& rMtf,
758 const ShapeAttributeLayerSharedPtr& rAttr ) const
760 // lookup destination canvas - is there already a renderer
761 // created for that target?
762 const RendererCacheVector::iterator aIter(
763 getCacheEntry( rDestinationCanvas ) );
765 // now we have a valid entry, either way. call prefetch()
766 // on it, nevertheless - maybe the metafile changed, and
767 // the renderer still needs an update (prefetch() will
768 // detect that)
769 if( prefetch( *aIter,
770 rDestinationCanvas,
771 rMtf,
772 rAttr ) )
774 return aIter->mpRenderer;
776 else
778 // prefetch failed - renderer is invalid
779 return ::cppcanvas::RendererSharedPtr();
783 void ViewShape::invalidateRenderer() const
785 // simply clear the cache. Subsequent getRenderer() calls
786 // will regenerate the Renderers.
787 maRenderers.clear();
790 ::basegfx::B2DSize ViewShape::getAntialiasingBorder() const
792 ENSURE_OR_THROW( mpViewLayer->getCanvas(),
793 "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" );
795 const ::basegfx::B2DHomMatrix aViewTransform(
796 mpViewLayer->getTransformation() );
798 // TODO(F1): As a quick shortcut (did not want to invert
799 // whole matrix here), taking only scale components of
800 // view transformation matrix. This will be wrong when
801 // e.g. shearing is involved.
802 const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / aViewTransform.get(0,0) );
803 const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / aViewTransform.get(1,1) );
805 return ::basegfx::B2DSize( nXBorder,
806 nYBorder );
809 void ViewShape::enterAnimationMode()
811 mbForceUpdate = true;
812 mbAnimationMode = true;
815 void ViewShape::leaveAnimationMode()
817 mpSprite.reset();
818 mbAnimationMode = false;
819 mbForceUpdate = true;
822 bool ViewShape::update( const GDIMetaFileSharedPtr& rMtf,
823 const RenderArgs& rArgs,
824 UpdateFlags nUpdateFlags,
825 bool bIsVisible ) const
827 ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" );
829 // Shall we render to a sprite, or to a plain canvas?
830 if( mbAnimationMode )
831 return renderSprite( mpViewLayer,
832 rMtf,
833 rArgs.maOrigBounds,
834 rArgs.maBounds,
835 rArgs.maUnitBounds,
836 nUpdateFlags,
837 rArgs.mrAttr,
838 rArgs.mrSubsets,
839 rArgs.mnShapePriority,
840 bIsVisible );
841 else
842 return render( mpViewLayer->getCanvas(),
843 rMtf,
844 rArgs.maBounds,
845 rArgs.maUpdateBounds,
846 nUpdateFlags,
847 rArgs.mrAttr,
848 rArgs.mrSubsets,
849 bIsVisible );
854 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */