LibreOffice Module slideshow (master) 1
viewshape.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
23
24#include <algorithm>
25
26#include <rtl/math.hxx>
27#include <sal/log.hxx>
28
29#include <com/sun/star/rendering/PanoseLetterForm.hpp>
30#include <com/sun/star/awt/FontSlant.hpp>
31
35
39
40#include "viewshape.hxx"
41#include <tools.hxx>
42#include <utility>
43
44using namespace ::com::sun::star;
45
46namespace slideshow::internal
47{
48
49 // TODO(F2): Provide sensible setup for mtf-related attributes (fill mode,
50 // char rotation etc.). Do that via mtf argument at this object
51
53 const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
54 const GDIMetaFileSharedPtr& rMtf,
55 const ShapeAttributeLayerSharedPtr& rAttr )
56 {
58 "ViewShape::prefetch(): no valid metafile!" );
59
60 if( rMtf != io_rCacheEntry.mpMtf ||
61 rDestinationCanvas != io_rCacheEntry.getDestinationCanvas() )
62 {
63 // buffered renderer invalid, re-create
65
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 )
71 {
72 if( rAttr->isFillColorValid() )
73 {
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();
79 }
80 if( rAttr->isLineColorValid() )
81 {
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();
87 }
88 if( rAttr->isCharColorValid() )
89 {
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();
95 }
96 if( rAttr->isDimColorValid() )
97 {
98 // convert RGBColor to RGBA32 integer. Note
99 // that getIntegerColor() also truncates
100 // out-of-range values appropriately
101
102 // dim color overrides all other colors
103 aParms.maFillColor =
104 aParms.maLineColor =
105 aParms.maTextColor =
106 rAttr->getDimColor().getIntegerColor();
107 }
108 if( rAttr->isFontFamilyValid() )
109 {
110 aParms.maFontName =
111 rAttr->getFontFamily();
112 }
113 if( rAttr->isCharScaleValid() )
114 {
116
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 );
124
125 aParms.maTextTransformation = aMatrix;
126 }
127 if( rAttr->isCharWeightValid() )
128 {
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 ) ) ) );
135 }
136 if( rAttr->isCharPostureValid() )
137 {
138 aParms.maFontLetterForm =
139 rAttr->getCharPosture() == sal_Int16(awt::FontSlant_NONE) ?
140 rendering::PanoseLetterForm::ANYTHING :
141 rendering::PanoseLetterForm::OBLIQUE_CONTACT;
142 }
143 if( rAttr->isUnderlineModeValid() )
144 {
145 aParms.maFontUnderline =
146 rAttr->getUnderlineMode();
147 }
148 }
149
150 io_rCacheEntry.mpRenderer = ::cppcanvas::VCLFactory::createRenderer( rDestinationCanvas,
151 *rMtf,
152 aParms );
153
154 io_rCacheEntry.mpMtf = rMtf;
155 io_rCacheEntry.mpDestinationCanvas = rDestinationCanvas;
156
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 )
162 {
163 io_rCacheEntry.mpLastBitmapCanvas.reset();
164 io_rCacheEntry.mpLastBitmap.reset();
165 }
166 }
167
168 return static_cast< bool >(io_rCacheEntry.mpRenderer);
169 }
170
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
177 {
179 getRenderer( rDestinationCanvas, rMtf, rAttr ) );
180
181 ENSURE_OR_RETURN_FALSE( pRenderer, "ViewShape::draw(): Invalid renderer" );
182
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;
194
195 try
196 {
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 );
205 }
206 catch( uno::Exception& )
207 {
208 DBG_UNHANDLED_EXCEPTION("slideshow");
209 }
210#endif
211 if( pClip )
212 pRenderer->setClip( *pClip );
213 else
214 pRenderer->setClip();
215
216 if( rSubsets.empty() )
217 {
218 return pRenderer->draw();
219 }
220 else
221 {
222 // render subsets of whole metafile
223
224
225 bool bRet(true);
226 for( const auto& rSubset : rSubsets )
227 {
228 if( !pRenderer->drawSubset( rSubset.getStartIndex(),
229 rSubset.getEndIndex() ) )
230 bRet = false;
231 }
232
233 return bRet;
234 }
235 }
236
237 namespace
238 {
240 ::basegfx::B2DRectangle shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix& rCanvasTransformation,
241 const ::basegfx::B2DRectangle& rUntransformedArea )
242 {
243 // convert area to pixel, and add anti-aliasing border
244
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.
250
251 ::basegfx::B2DRectangle aBoundsPixel;
252 ::canvas::tools::calcTransformedRectBounds( aBoundsPixel,
253 rUntransformedArea,
254 rCanvasTransformation );
255
256 // add antialiasing border around the shape (AA
257 // touches pixel _outside_ the nominal bound rect)
259
260 return aBoundsPixel;
261 }
262
264 ::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& rUnitBounds,
265 const ::basegfx::B2DHomMatrix& rShapeTransformation,
266 const ::basegfx::B2DHomMatrix& rCanvasTransformation,
267 const ShapeAttributeLayerSharedPtr& pAttr )
268 {
269 // calc update area for whole shape (including
270 // character scaling)
271 return shapeArea2AreaPixel( rCanvasTransformation,
272 getShapeUpdateArea( rUnitBounds,
273 rShapeTransformation,
274 pAttr ) );
275 }
276 }
277
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,
286 double nPrio,
287 bool bIsVisible ) const
288 {
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).
292
293 if( !bIsVisible ||
294 rUnitBounds.isEmpty() ||
295 rOrigBounds.isEmpty() ||
296 rBounds.isEmpty() )
297 {
298 // shape is invisible or has zero size, no need to
299 // update anything.
300 if( mpSprite )
301 mpSprite->hide();
302
303 return true;
304 }
305
306
307 // calc sprite position, size and content transformation
308 // =====================================================
309
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() );
319
320 const ::basegfx::B2DHomMatrix& rCanvasTransform(
321 rViewLayer->getSpriteTransformation() );
322
323 // area actually needed for the sprite
324 const ::basegfx::B2DRectangle& rSpriteBoundsPixel(
325 calcUpdateAreaPixel( rUnitBounds,
326 aShapeTransformation,
327 rCanvasTransform,
328 pAttr ) );
329
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,
335 rCanvasTransform,
336 pAttr ) );
337
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(
346 aLogShapeBounds,
347 ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
348 aNonTranslationalShapeTransformation ) ) );
349
350 // create (or resize) sprite with sprite's pixel size, if
351 // not done already
352 auto aRange = rSpriteBoundsPixel.getRange();
353 basegfx::B2DSize rSpriteSizePixel(aRange.getX(), aRange.getY());
354 if( !mpSprite )
355 {
356 mpSprite = std::make_shared<AnimatedSprite>( mpViewLayer,
357 rSpriteSizePixel,
358 nPrio );
359 }
360 else
361 {
362 // TODO(F2): when the sprite _actually_ gets resized,
363 // content needs a repaint!
364 mpSprite->resize( rSpriteSizePixel );
365 }
366
367 ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" );
368
369 SAL_INFO("slideshow", "ViewShape::renderSprite(): Rendering sprite " <<
370 mpSprite.get() );
371
372
373 // always show the sprite (might have been hidden before)
374 mpSprite->show();
375
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();
386
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());
393
394 // offset added top, left for anti-aliasing (otherwise,
395 // shapes fully filling the sprite will have anti-aliased
396 // pixel cut off)
397 const ::basegfx::B2DSize aAAOffset(
400
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
406 // at the origin.
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() ) ) );
414
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(),
421 pAttr ) );
422
423
424 // process flags
425 // =============
426
427 bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & UpdateFlags::Force) );
428
429 if( mbForceUpdate || (nUpdateFlags & UpdateFlags::Alpha) )
430 {
431 mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ?
432 std::clamp(pAttr->getAlpha(),
433 0.0,
434 1.0) :
435 1.0 );
436 }
437 if( mbForceUpdate || (nUpdateFlags & UpdateFlags::Clip) )
438 {
439 if( pAttr && pAttr->isClipValid() )
440 {
441 ::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() );
442
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 );
449
450 // make the clip 2*ANTIALIASING_EXTRA_SIZE larger
451 // such that it's again centered over the sprite.
452 aViewTransform.scale(rSpriteSizePixel.getWidth()/
454 rSpriteSizePixel.getHeight()/
456
457 // transform clip polygon from view to device
458 // coordinate space
459 aClipPoly.transform( aViewTransform );
460
461 mpSprite->clip( aClipPoly );
462 }
463 else
464 mpSprite->clip();
465 }
466 if( mbForceUpdate || (nUpdateFlags & UpdateFlags::Content) )
467 {
468 bRedrawRequired = true;
469
470 // TODO(P1): maybe provide some appearance change methods at
471 // the Renderer interface
472
473 // force the renderer to be regenerated below, for the
474 // different attributes to take effect
476 }
477
478 mbForceUpdate = false;
479
480 if( !bRedrawRequired )
481 return true;
482
483
484 // sprite needs repaint - output to sprite canvas
485 // ==============================================
486
487 ::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() );
488
489 return draw( pContentCanvas,
490 rMtf,
491 pAttr,
492 aShapeTransformation,
493 nullptr, // clipping is done via Sprite::clip()
494 rSubsets );
495 }
496
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
505 {
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).
509
510 if( !bIsVisible )
511 {
512 SAL_INFO("slideshow", "ViewShape::render(): skipping shape " << this );
513
514 // shape is invisible, no need to update anything.
515 return true;
516 }
517
518 // since we have no sprite here, _any_ update request
519 // translates into a required redraw.
520 bool bRedrawRequired( mbForceUpdate || nUpdateFlags != UpdateFlags::NONE );
521
522 if( nUpdateFlags & UpdateFlags::Content )
523 {
524 // TODO(P1): maybe provide some appearance change methods at
525 // the Renderer interface
526
527 // force the renderer to be regenerated below, for the
528 // different attributes to take effect
530 }
531
532 mbForceUpdate = false;
533
534 if( !bRedrawRequired )
535 return true;
536
537 SAL_INFO( "slideshow", "ViewShape::render(): rendering shape " <<
538 this <<
539 " at position (" <<
540 rBounds.getMinX() << "," <<
541 rBounds.getMinY() << ")" );
542
543
544 // shape needs repaint - setup all that's needed
545
546
547 std::optional<basegfx::B2DPolyPolygon> aClip;
548
549 if( pAttr )
550 {
551 // setup clip poly
552 if( pAttr->isClipValid() )
553 aClip = pAttr->getClip();
554
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 -
558 // see above)
559 if( pAttr->isAlphaValid() )
560 {
561 const double nAlpha( pAttr->getAlpha() );
562
563 if( !::basegfx::fTools::equalZero( nAlpha ) &&
564 !::rtl::math::approxEqual(nAlpha, 1.0) )
565 {
566 // render with global alpha - have to prepare
567 // a bitmap, and render that with modulated
568 // alpha
569
570
571 const ::basegfx::B2DHomMatrix aTransform(
572 getShapeTransformation( rBounds,
573 pAttr ) );
574
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
579 // overall bounds.
580
581 // determine output rect of _shape update
582 // area_ in device pixel
583 const ::basegfx::B2DHomMatrix aCanvasTransform(
584 rDestinationCanvas->getTransformation() );
586 ::canvas::tools::calcTransformedRectBounds( aTmpRect,
587 rUpdateBounds,
588 aCanvasTransform );
589
590 // pixel size of cache bitmap: round up to
591 // nearest int
592 const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1,
593 static_cast<sal_Int32>( aTmpRect.getHeight() )+1 );
594
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 ) );
602
603 if( !aCompositingSurface->mpLastBitmapCanvas ||
604 aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize )
605 {
606 // create a bitmap of appropriate size
609 rDestinationCanvas,
610 aBmpSize ) );
611
612 ENSURE_OR_THROW(pBitmap,
613 "ViewShape::render(): Could not create compositing surface");
614
615 aCompositingSurface->mpDestinationCanvas = rDestinationCanvas;
616 aCompositingSurface->mpLastBitmap = pBitmap;
617 aCompositingSurface->mpLastBitmapCanvas = pBitmap->getBitmapCanvas();
618 }
619
620 // buffer aCompositingSurface iterator content
621 // - said one might get invalidated during
622 // draw() below.
624 aCompositingSurface->mpLastBitmapCanvas );
625
627 aCompositingSurface->mpLastBitmap);
628
629 // setup bitmap canvas transformation -
630 // which happens to be the destination
631 // canvas transformation without any
632 // translational components.
633
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() );
646
647 pBitmapCanvas->setTransformation( aAdjustedCanvasTransform );
648
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).
655
656 // render into this bitmap
657 if( !draw( pBitmapCanvas,
658 rMtf,
659 pAttr,
660 aTransform,
661 !aClip ? nullptr : &(*aClip),
662 rSubsets ) )
663 {
664 return false;
665 }
666
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!" );
678
679 aBitmapTransform.invert();
680
682 aTmpRect.getMinX(), aTmpRect.getMinY()));
683
684 aBitmapTransform = aBitmapTransform * aTranslation;
685 pBitmap->setTransformation( aBitmapTransform );
686
687 // finally, render bitmap alpha-modulated
688 pBitmap->drawAlphaModulated( nAlpha );
689
690 return true;
691 }
692 }
693 }
694
695 // retrieve shape transformation, _with_ shape translation
696 // to actual page position.
697 const ::basegfx::B2DHomMatrix aTransform(
698 getShapeTransformation( rBounds,
699 pAttr ) );
700
701 return draw( rDestinationCanvas,
702 rMtf,
703 pAttr,
704 aTransform,
705 !aClip ? nullptr : &(*aClip),
706 rSubsets );
707 }
708
709
711 mpViewLayer(std::move( xViewLayer )),
712 maRenderers(),
713 mpSprite(),
714 mbAnimationMode( false ),
715 mbForceUpdate( true )
716 {
717 ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" );
718 }
719
721 {
722 return mpViewLayer;
723 }
724
725 ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const
726 {
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() );
731
732 // already there?
733 if( (aIter=::std::find_if( maRenderers.begin(),
734 aEnd,
735 [&rDestinationCanvas]( const RendererCacheEntry& rCacheEntry )
736 { return rDestinationCanvas == rCacheEntry.getDestinationCanvas(); } ) ) == aEnd )
737 {
739 {
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() );
746
747 // ATTENTION: after this, both aIter and aEnd are
748 // invalid!
749 }
750
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;
755 }
756
757 return aIter;
758 }
759
760 ::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas,
761 const GDIMetaFileSharedPtr& rMtf,
762 const ShapeAttributeLayerSharedPtr& rAttr ) const
763 {
764 // lookup destination canvas - is there already a renderer
765 // created for that target?
766 const RendererCacheVector::iterator aIter(
767 getCacheEntry( rDestinationCanvas ) );
768
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
772 // detect that)
773 if( prefetch( *aIter,
774 rDestinationCanvas,
775 rMtf,
776 rAttr ) )
777 {
778 return aIter->mpRenderer;
779 }
780 else
781 {
782 // prefetch failed - renderer is invalid
783 return ::cppcanvas::RendererSharedPtr();
784 }
785 }
786
788 {
789 // simply clear the cache. Subsequent getRenderer() calls
790 // will regenerate the Renderers.
791 maRenderers.clear();
792 }
793
795 {
796 ENSURE_OR_THROW( mpViewLayer->getCanvas(),
797 "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" );
798
799 const ::basegfx::B2DHomMatrix& rViewTransform(
800 mpViewLayer->getTransformation() );
801
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) );
808
809 return ::basegfx::B2DSize( nXBorder,
810 nYBorder );
811 }
812
814 {
815 mbForceUpdate = true;
816 mbAnimationMode = true;
817 }
818
820 {
821 mpSprite.reset();
822 mbAnimationMode = false;
823 mbForceUpdate = true;
824 }
825
827 const RenderArgs& rArgs,
828 UpdateFlags nUpdateFlags,
829 bool bIsVisible ) const
830 {
831 ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" );
832
833 // Shall we render to a sprite, or to a plain canvas?
834 if( mbAnimationMode )
836 rMtf,
837 rArgs.maOrigBounds,
838 rArgs.maBounds,
839 rArgs.maUnitBounds,
840 nUpdateFlags,
841 rArgs.mrAttr,
842 rArgs.mrSubsets,
843 rArgs.mnShapePriority,
844 bIsVisible );
845 else
846 return render( mpViewLayer->getCanvas(),
847 rMtf,
848 rArgs.maBounds,
849 rArgs.maUpdateBounds,
850 nUpdateFlags,
851 rArgs.mrAttr,
852 rArgs.mrSubsets,
853 bIsVisible );
854 }
855
856}
857
858/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static T clamp(const T &rIn)
bool isInvertible() const
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void translate(double fX, double fY)
void scale(double fX, double fY)
void transform(const basegfx::B2DHomMatrix &rMatrix)
void grow(TYPE fValue)
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getHeight() const
TYPE getWidth() const
TYPE getHeight() const
static BitmapSharedPtr createAlphaBitmap(const CanvasSharedPtr &, const ::basegfx::B2ISize &rSize)
static constexpr auto ANTIALIASING_EXTRA_SIZE
static RendererSharedPtr createRenderer(const CanvasSharedPtr &rCanvas, const ::GDIMetaFile &rMtf, const Renderer::Parameters &rParms)
bool update(const GDIMetaFileSharedPtr &rMtf, const RenderArgs &rArgs, UpdateFlags nUpdateFlags, bool bIsVisible) const
Update the ViewShape.
Definition: viewshape.cxx:826
::cppcanvas::RendererSharedPtr getRenderer(const ::cppcanvas::CanvasSharedPtr &rDestinationCanvas, const GDIMetaFileSharedPtr &rMtf, const ShapeAttributeLayerSharedPtr &rAttr) const
Retrieve renderer for given canvas and metafile.
Definition: viewshape.cxx:760
::basegfx::B2DSize getAntialiasingBorder() const
Query dimension of a safety border around the shape for AA.
Definition: viewshape.cxx:794
bool render(const ::cppcanvas::CanvasSharedPtr &rDestinationCanvas, const GDIMetaFileSharedPtr &rMtf, const ::basegfx::B2DRectangle &rBounds, const ::basegfx::B2DRectangle &rUpdateBounds, UpdateFlags nUpdateFlags, const ShapeAttributeLayerSharedPtr &pAttr, const VectorOfDocTreeNodes &rSubsets, bool bIsVisible) const
Render shape to given canvas.
Definition: viewshape.cxx:497
RendererCacheVector maRenderers
A set of cached mtf/canvas combinations.
Definition: viewshape.hxx:301
bool renderSprite(const ViewLayerSharedPtr &rViewLayer, const GDIMetaFileSharedPtr &rMtf, const ::basegfx::B2DRectangle &rOrigBounds, const ::basegfx::B2DRectangle &rBounds, const ::basegfx::B2DRectangle &rUnitBounds, UpdateFlags nUpdateFlags, const ShapeAttributeLayerSharedPtr &pAttr, const VectorOfDocTreeNodes &rSubsets, double nPrio, bool bIsVisible) const
Render shape to an active sprite.
Definition: viewshape.cxx:278
const ViewLayerSharedPtr & getViewLayer() const
Query the associated view layer of this shape.
Definition: viewshape.cxx:720
void enterAnimationMode()
Notify the ViewShape that an animation starts now.
Definition: viewshape.cxx:813
AnimatedSpriteSharedPtr mpSprite
The sprite object.
Definition: viewshape.hxx:304
bool mbAnimationMode
If true, render() calls go to the sprite.
Definition: viewshape.hxx:307
bool draw(const ::cppcanvas::CanvasSharedPtr &rDestinationCanvas, const GDIMetaFileSharedPtr &rMtf, const ShapeAttributeLayerSharedPtr &rAttr, const ::basegfx::B2DHomMatrix &rTransform, const ::basegfx::B2DPolyPolygon *pClip, const VectorOfDocTreeNodes &rSubsets) const
Draw with prefetched Renderer to stored canvas.
Definition: viewshape.cxx:171
static bool prefetch(RendererCacheEntry &io_rCacheEntry, const ::cppcanvas::CanvasSharedPtr &rDestinationCanvas, const GDIMetaFileSharedPtr &rMtf, const ShapeAttributeLayerSharedPtr &rAttr)
Prefetch Renderer for given canvas.
Definition: viewshape.cxx:52
RendererCacheVector::iterator getCacheEntry(const ::cppcanvas::CanvasSharedPtr &rDestinationCanvas) const
Retrieve a valid iterator to renderer cache entry.
Definition: viewshape.cxx:725
ViewLayerSharedPtr mpViewLayer
The view layer this object is part of.
Definition: viewshape.hxx:298
void leaveAnimationMode()
Notify the ViewShape that it is no longer animated.
Definition: viewshape.cxx:819
ViewShape(ViewLayerSharedPtr xViewLayer)
Create a ViewShape for the given View.
Definition: viewshape.cxx:710
bool mbForceUpdate
If true, shape needs full repaint (and the sprite a setup, if any)
Definition: viewshape.hxx:310
#define ENSURE_OR_RETURN_FALSE(c, m)
#define ENSURE_OR_THROW(c, m)
#define DBG_UNHANDLED_EXCEPTION(...)
#define SAL_INFO(area, stream)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
std::shared_ptr< ::cppcanvas::Renderer > RendererSharedPtr
std::shared_ptr< BitmapCanvas > BitmapCanvasSharedPtr
std::shared_ptr< ::cppcanvas::Bitmap > BitmapSharedPtr
std::shared_ptr< Canvas > CanvasSharedPtr
::basegfx::B2DRectangle getShapeUpdateArea(const ::basegfx::B2DRectangle &rUnitBounds, const ::basegfx::B2DHomMatrix &rShapeTransform, const ShapeAttributeLayerSharedPtr &pAttr)
Definition: tools.cxx:541
::std::shared_ptr< ShapeAttributeLayer > ShapeAttributeLayerSharedPtr
::std::vector< DocTreeNode > VectorOfDocTreeNodes
std::shared_ptr< ViewLayer > ViewLayerSharedPtr
Definition: viewlayer.hxx:168
::basegfx::B2DHomMatrix getShapeTransformation(const ::basegfx::B2DRectangle &rShapeBounds, const ShapeAttributeLayerSharedPtr &pAttr)
Definition: tools.cxx:456
std::shared_ptr< GDIMetaFile > GDIMetaFileSharedPtr
Definition: tools.hxx:63
::basegfx::B2DHomMatrix getSpriteTransformation(const ::basegfx::B2DVector &rPixelSize, const ::basegfx::B2DVector &rOrigSize, const ShapeAttributeLayerSharedPtr &pAttr)
Definition: tools.cxx:474
std::weak_ptr< cppcanvas::CustomSprite > mpSprite
output surface (necessarily a sprite, won't otherwise be able to display anything before other sprite...
Definition: slideview.cxx:85
::std::optional< IntSRGBA > maFillColor
::std::optional< IntSRGBA > maTextColor
::std::optional< ::basegfx::B2DHomMatrix > maTextTransformation
::std::optional< IntSRGBA > maLineColor
::std::optional< OUString > maFontName
::std::optional< sal_Int8 > maFontWeight
::std::optional< bool > maFontUnderline
::std::optional< sal_Int8 > maFontLetterForm
const ::basegfx::B2DRectangle maUpdateBounds
Definition: viewshape.hxx:157
const ::basegfx::B2DRectangle maOrigBounds
Definition: viewshape.hxx:156
const ::basegfx::B2DRectangle maBounds
Definition: viewshape.hxx:158
const VectorOfDocTreeNodes & mrSubsets
Definition: viewshape.hxx:161
const ::basegfx::B2DRectangle maUnitBounds
Definition: viewshape.hxx:159
const ShapeAttributeLayerSharedPtr & mrAttr
Definition: viewshape.hxx:160
::cppcanvas::RendererSharedPtr mpRenderer
Definition: viewshape.hxx:224
::cppcanvas::BitmapCanvasSharedPtr mpLastBitmapCanvas
Definition: viewshape.hxx:227
const ::cppcanvas::CanvasSharedPtr & getDestinationCanvas() const
Definition: viewshape.hxx:218
::cppcanvas::CanvasSharedPtr mpDestinationCanvas
Definition: viewshape.hxx:223
signed char sal_Int8
UpdateFlags
Definition: viewshape.hxx:39