1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_slideshow.hxx"
26 
27 // must be first
28 #include <canvas/debug.hxx>
29 #include <tools/diagnose_ex.h>
30 
31 #include <math.h>
32 
33 #include <rtl/logfile.hxx>
34 #include <rtl/math.hxx>
35 
36 #include <com/sun/star/rendering/XCanvas.hpp>
37 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
38 #include <com/sun/star/rendering/PanoseLetterForm.hpp>
39 #include <com/sun/star/awt/FontSlant.hpp>
40 
41 #include <cppuhelper/exc_hlp.hxx>
42 #include <comphelper/anytostring.hxx>
43 
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <basegfx/numeric/ftools.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/matrix/b2dhommatrixtools.hxx>
48 
49 #include <canvas/verbosetrace.hxx>
50 #include <canvas/canvastools.hxx>
51 #include <cppcanvas/vclfactory.hxx>
52 #include <cppcanvas/basegfxfactory.hxx>
53 
54 #include "viewshape.hxx"
55 #include "tools.hxx"
56 
57 #include <boost/bind.hpp>
58 
59 
60 using namespace ::com::sun::star;
61 
62 namespace slideshow
63 {
64     namespace internal
65     {
66 
67         // TODO(F2): Provide sensible setup for mtf-related attributes (fill mode,
68         // char rotation etc.). Do that via mtf argument at this object
69 
70         bool ViewShape::prefetch( RendererCacheEntry&					io_rCacheEntry,
71                                   const ::cppcanvas::CanvasSharedPtr&	rDestinationCanvas,
72                                   const GDIMetaFileSharedPtr&			rMtf,
73                                   const ShapeAttributeLayerSharedPtr&	rAttr ) const
74         {
75             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::prefetch()" );
76             ENSURE_OR_RETURN_FALSE( rMtf,
77                                "ViewShape::prefetch(): no valid metafile!" );
78 
79             if( rMtf != io_rCacheEntry.mpMtf ||
80                 rDestinationCanvas != io_rCacheEntry.getDestinationCanvas() )
81             {
82                 // buffered renderer invalid, re-create
83                 ::cppcanvas::Renderer::Parameters aParms;
84 
85                 // rendering attribute override parameter struct.  For
86                 // every valid attribute, the corresponding struct
87                 // member is filled, which in the metafile renderer
88                 // forces rendering with the given attribute.
89                 if( rAttr )
90                 {
91                     if( rAttr->isFillColorValid() )
92                     {
93                         // convert RGBColor to RGBA32 integer. Note
94                         // that getIntegerColor() also truncates
95                         // out-of-range values appropriately
96                         aParms.maFillColor =
97                             rAttr->getFillColor().getIntegerColor();
98                     }
99                     if( rAttr->isLineColorValid() )
100                     {
101                         // convert RGBColor to RGBA32 integer. Note
102                         // that getIntegerColor() also truncates
103                         // out-of-range values appropriately
104                         aParms.maLineColor =
105                             rAttr->getLineColor().getIntegerColor();
106                     }
107                     if( rAttr->isCharColorValid() )
108                     {
109                         // convert RGBColor to RGBA32 integer. Note
110                         // that getIntegerColor() also truncates
111                         // out-of-range values appropriately
112                         aParms.maTextColor =
113                             rAttr->getCharColor().getIntegerColor();
114                     }
115                     if( rAttr->isDimColorValid() )
116                     {
117                         // convert RGBColor to RGBA32 integer. Note
118                         // that getIntegerColor() also truncates
119                         // out-of-range values appropriately
120 
121                         // dim color overrides all other colors
122                         aParms.maFillColor =
123                         aParms.maLineColor =
124                         aParms.maTextColor =
125                             rAttr->getDimColor().getIntegerColor();
126                     }
127                     if( rAttr->isFontFamilyValid() )
128                     {
129                         aParms.maFontName =
130                             rAttr->getFontFamily();
131                     }
132                     if( rAttr->isCharScaleValid() )
133                     {
134                         ::basegfx::B2DHomMatrix aMatrix;
135 
136                         // enlarge text by given scale factor. Do that
137                         // with the middle of the shape as the center
138                         // of scaling.
139                         aMatrix.translate( -0.5, -0.5 );
140                         aMatrix.scale( rAttr->getCharScale(),
141                                        rAttr->getCharScale() );
142                         aMatrix.translate( 0.5, 0.5 );
143 
144                         aParms.maTextTransformation = aMatrix;
145                     }
146                     if( rAttr->isCharWeightValid() )
147                     {
148                         aParms.maFontWeight =
149                             static_cast< sal_Int8 >(
150                                 ::basegfx::fround(
151                                     ::std::max( 0.0,
152                                                 ::std::min( 11.0,
153                                                             rAttr->getCharWeight() / 20.0 ) ) ) );
154                     }
155                     if( rAttr->isCharPostureValid() )
156                     {
157                         aParms.maFontLetterForm =
158                             rAttr->getCharPosture() == awt::FontSlant_NONE ?
159                             rendering::PanoseLetterForm::ANYTHING :
160                             rendering::PanoseLetterForm::OBLIQUE_CONTACT;
161                     }
162                     if( rAttr->isUnderlineModeValid() )
163                     {
164                         aParms.maFontUnderline =
165                             rAttr->getUnderlineMode();
166                     }
167                 }
168 
169                 io_rCacheEntry.mpRenderer = ::cppcanvas::VCLFactory::getInstance().createRenderer( rDestinationCanvas,
170                                                                                                    *rMtf.get(),
171                                                                                                    aParms );
172 
173                 io_rCacheEntry.mpMtf      		   = rMtf;
174                 io_rCacheEntry.mpDestinationCanvas = rDestinationCanvas;
175 
176                 // also invalidate alpha compositing bitmap (created
177                 // new renderer, which possibly generates different
178                 // output). Do NOT invalidate, if we're incidentally
179                 // rendering INTO it.
180                 if( rDestinationCanvas != io_rCacheEntry.mpLastBitmapCanvas )
181                 {
182                     io_rCacheEntry.mpLastBitmapCanvas.reset();
183                     io_rCacheEntry.mpLastBitmap.reset();
184                 }
185             }
186 
187             return io_rCacheEntry.mpRenderer;
188         }
189 
190         bool ViewShape::draw( const ::cppcanvas::CanvasSharedPtr&	rDestinationCanvas,
191                               const GDIMetaFileSharedPtr&			rMtf,
192                               const ShapeAttributeLayerSharedPtr&	rAttr,
193                               const ::basegfx::B2DHomMatrix&		rTransform,
194                               const ::basegfx::B2DPolyPolygon*		pClip,
195                               const VectorOfDocTreeNodes&			rSubsets ) const
196         {
197             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::draw()" );
198 
199             ::cppcanvas::RendererSharedPtr pRenderer(
200                 getRenderer( rDestinationCanvas, rMtf, rAttr ) );
201 
202             ENSURE_OR_RETURN_FALSE( pRenderer, "ViewShape::draw(): Invalid renderer" );
203 
204             pRenderer->setTransformation( rTransform );
205 #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0
206             rendering::RenderState aRenderState;
207             ::canvas::tools::initRenderState(aRenderState);
208             ::canvas::tools::setRenderStateTransform(aRenderState,
209                                                      rTransform);
210             aRenderState.DeviceColor.realloc(4);
211             aRenderState.DeviceColor[0] = 1.0;
212             aRenderState.DeviceColor[1] = 0.0;
213             aRenderState.DeviceColor[2] = 0.0;
214             aRenderState.DeviceColor[3] = 1.0;
215 
216             try
217             {
218                 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(0.0,0.0),
219                                                               geometry::RealPoint2D(1.0,1.0),
220                                                               rDestinationCanvas->getViewState(),
221                                                               aRenderState );
222                 rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0),
223                                                               geometry::RealPoint2D(0.0,1.0),
224                                                               rDestinationCanvas->getViewState(),
225                                                               aRenderState );
226             }
227             catch( uno::Exception& )
228             {
229                 DBG_UNHANDLED_EXCEPTION();
230             }
231 #endif
232             if( pClip )
233                 pRenderer->setClip( *pClip );
234             else
235                 pRenderer->setClip();
236 
237             if( rSubsets.empty() )
238             {
239                 return pRenderer->draw();
240             }
241             else
242             {
243                 // render subsets of whole metafile
244                 // --------------------------------
245 
246                 bool bRet(true);
247                 VectorOfDocTreeNodes::const_iterator 		aIter( rSubsets.begin() );
248                 const VectorOfDocTreeNodes::const_iterator	aEnd ( rSubsets.end() );
249                 while( aIter != aEnd )
250                 {
251                     if( !pRenderer->drawSubset( aIter->getStartIndex(),
252                                                 aIter->getEndIndex() ) )
253                         bRet = false;
254 
255                     ++aIter;
256                 }
257 
258                 return bRet;
259             }
260         }
261 
262         namespace
263         {
264             /// Convert untransformed shape update area to device pixel.
265             ::basegfx::B2DRectangle shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix&	rCanvasTransformation,
266                                                          const ::basegfx::B2DRectangle&	rUntransformedArea		)
267             {
268                 // convert area to pixel, and add anti-aliasing border
269 
270                 // TODO(P1): Should the view transform some
271                 // day contain rotation/shear, transforming
272                 // the original bounds with the total
273                 // transformation might result in smaller
274                 // overall bounds.
275 
276                 ::basegfx::B2DRectangle aBoundsPixel;
277                 ::canvas::tools::calcTransformedRectBounds( aBoundsPixel,
278                                                             rUntransformedArea,
279                                                             rCanvasTransformation );
280 
281                 // add antialiasing border around the shape (AA
282                 // touches pixel _outside_ the nominal bound rect)
283                 aBoundsPixel.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
284 
285                 return aBoundsPixel;
286             }
287 
288             /// Convert shape unit rect to device pixel.
289             ::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& 		rUnitBounds,
290                                                          const ::basegfx::B2DHomMatrix& 		rShapeTransformation,
291                                                          const ::basegfx::B2DHomMatrix&			rCanvasTransformation,
292                                                          const ShapeAttributeLayerSharedPtr&	pAttr					)
293             {
294                 // calc update area for whole shape (including
295                 // character scaling)
296                 return shapeArea2AreaPixel( rCanvasTransformation,
297                                             getShapeUpdateArea( rUnitBounds,
298                                                                 rShapeTransformation,
299                                                                 pAttr ) );
300             }
301         }
302 
303         bool ViewShape::renderSprite( const ViewLayerSharedPtr&             rViewLayer,
304                                       const GDIMetaFileSharedPtr&			rMtf,
305                                       const ::basegfx::B2DRectangle&		rOrigBounds,
306                                       const ::basegfx::B2DRectangle&		rBounds,
307                                       const ::basegfx::B2DRectangle&		rUnitBounds,
308                                       int									nUpdateFlags,
309                                       const ShapeAttributeLayerSharedPtr&	pAttr,
310                                       const VectorOfDocTreeNodes&			rSubsets,
311                                       double                                nPrio,
312                                       bool 									bIsVisible ) const
313         {
314             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::renderSprite()" );
315 
316             // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
317             // in that all the common setup steps here are refactored to Shape (would then
318             // have to be performed only _once_ per Shape paint).
319 
320             if( !bIsVisible ||
321                 rUnitBounds.isEmpty() ||
322                 rOrigBounds.isEmpty() ||
323                 rBounds.isEmpty() )
324             {
325                 // shape is invisible or has zero size, no need to
326                 // update anything.
327                 if( mpSprite )
328                     mpSprite->hide();
329 
330                 return true;
331             }
332 
333 
334             // calc sprite position, size and content transformation
335             // =====================================================
336 
337             // the shape transformation for a sprite is always a
338             // simple scale-up to the nominal shape size. Everything
339             // else is handled via the sprite transformation
340             ::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation;
341             aNonTranslationalShapeTransformation.scale( rOrigBounds.getWidth(),
342                                                         rOrigBounds.getHeight() );
343             ::basegfx::B2DHomMatrix aShapeTransformation( aNonTranslationalShapeTransformation );
344             aShapeTransformation.translate( rOrigBounds.getMinX(),
345                                             rOrigBounds.getMinY() );
346 
347             const ::basegfx::B2DHomMatrix& rCanvasTransform(
348                 rViewLayer->getSpriteTransformation() );
349 
350             // area actually needed for the sprite
351             const ::basegfx::B2DRectangle& rSpriteBoundsPixel(
352                 calcUpdateAreaPixel( rUnitBounds,
353                                      aShapeTransformation,
354                                      rCanvasTransform,
355                                      pAttr ) );
356 
357             // actual area for the shape (without subsetting, but
358             // including char scaling)
359             const ::basegfx::B2DRectangle& rShapeBoundsPixel(
360                 calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
361                                      aShapeTransformation,
362                                      rCanvasTransform,
363                                      pAttr ) );
364 
365             // nominal area for the shape (without subsetting, without
366             // char scaling). NOTE: to cancel the shape translation,
367             // contained in rSpriteBoundsPixel, this is _without_ any
368             // translational component (fixed along with #121921#).
369             ::basegfx::B2DRectangle		   aLogShapeBounds;
370             const ::basegfx::B2DRectangle& rNominalShapeBoundsPixel(
371                 shapeArea2AreaPixel( rCanvasTransform,
372                                      ::canvas::tools::calcTransformedRectBounds(
373                                          aLogShapeBounds,
374                                          ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0),
375                                          aNonTranslationalShapeTransformation ) ) );
376 
377             // create (or resize) sprite with sprite's pixel size, if
378             // not done already
379             const ::basegfx::B2DSize& rSpriteSizePixel(rSpriteBoundsPixel.getRange());
380             if( !mpSprite )
381             {
382                 mpSprite.reset(
383                     new AnimatedSprite( mpViewLayer,
384                                         rSpriteSizePixel,
385                                         nPrio ));
386             }
387             else
388             {
389                 // TODO(F2): when the sprite _actually_ gets resized,
390                 // content needs a repaint!
391                 mpSprite->resize( rSpriteSizePixel );
392             }
393 
394             ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" );
395 
396             VERBOSE_TRACE( "ViewShape::renderSprite(): Rendering sprite 0x%X",
397                            mpSprite.get() );
398 
399 
400             // always show the sprite (might have been hidden before)
401             mpSprite->show();
402 
403             // determine center of sprite output position in pixel
404             // (assumption here: all shape transformations have the
405             // shape center as the pivot point). From that, subtract
406             // distance of rSpriteBoundsPixel's left, top edge from
407             // rShapeBoundsPixel's center. This moves the sprite at
408             // the appropriate output position within the virtual
409             // rShapeBoundsPixel area.
410             ::basegfx::B2DPoint aSpritePosPixel( rBounds.getCenter() );
411             aSpritePosPixel *= rCanvasTransform;
412             aSpritePosPixel -= rShapeBoundsPixel.getCenter() - rSpriteBoundsPixel.getMinimum();
413 
414             // the difference between rShapeBoundsPixel and
415             // rSpriteBoundsPixel upper, left corner is: the offset we
416             // have to move sprite output to the right, top (to make
417             // the desired subset content visible at all)
418             const ::basegfx::B2DSize& rSpriteCorrectionOffset(
419                 rSpriteBoundsPixel.getMinimum() - rNominalShapeBoundsPixel.getMinimum() );
420 
421             // offset added top, left for anti-aliasing (otherwise,
422             // shapes fully filling the sprite will have anti-aliased
423             // pixel cut off)
424             const ::basegfx::B2DSize aAAOffset(
425                 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE,
426                 ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE );
427 
428             // set pixel output offset to sprite: we always leave
429             // ANTIALIASING_EXTRA_SIZE room atop and to the left, and,
430             // what's more, for subsetted shapes, we _have_ to cancel
431             // the effect of the shape renderer outputting the subset
432             // at its absolute position inside the shape, instead of
433             // at the origin.
434             // NOTE: As for now, sprites are always positioned on
435             // integer pixel positions on screen, have to round to
436             // nearest integer here, too (fixed along with #121921#)
437             mpSprite->setPixelOffset(
438                 aAAOffset - ::basegfx::B2DSize(
439                     ::basegfx::fround( rSpriteCorrectionOffset.getX() ),
440                     ::basegfx::fround( rSpriteCorrectionOffset.getY() ) ) );
441 
442             // always set sprite position and transformation, since
443             // they do not relate directly to the update flags
444             // (e.g. sprite position changes when sprite size changes)
445             mpSprite->movePixel( aSpritePosPixel );
446             mpSprite->transform( getSpriteTransformation( rSpriteSizePixel,
447                                                           rOrigBounds.getRange(),
448                                                           pAttr ) );
449 
450 
451             // process flags
452             // =============
453 
454             bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & FORCE) );
455 
456             if( mbForceUpdate || (nUpdateFlags & ALPHA) )
457             {
458                 mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ?
459                                     ::basegfx::clamp(pAttr->getAlpha(),
460                                                      0.0,
461                                                      1.0) :
462                                     1.0 );
463             }
464             if( mbForceUpdate || (nUpdateFlags & CLIP) )
465             {
466                 if( pAttr && pAttr->isClipValid() )
467                 {
468                     ::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() );
469 
470                     // extract linear part of canvas view transformation
471                     // (linear means: without translational components)
472                     ::basegfx::B2DHomMatrix aViewTransform(
473                         mpViewLayer->getTransformation() );
474                     aViewTransform.set( 0, 2, 0.0 );
475                     aViewTransform.set( 1, 2, 0.0 );
476 
477                     // make the clip 2*ANTIALIASING_EXTRA_SIZE larger
478                     // such that it's again centered over the sprite.
479                     aViewTransform.scale(rSpriteSizePixel.getX()/
480                                          (rSpriteSizePixel.getX()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE),
481                                          rSpriteSizePixel.getY()/
482                                          (rSpriteSizePixel.getY()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE));
483 
484                     // transform clip polygon from view to device
485                     // coordinate space
486                     aClipPoly.transform( aViewTransform );
487 
488                     mpSprite->clip( aClipPoly );
489                 }
490                 else
491                     mpSprite->clip();
492             }
493             if( mbForceUpdate || (nUpdateFlags & CONTENT) )
494             {
495                 bRedrawRequired = true;
496 
497                 // TODO(P1): maybe provide some appearance change methods at
498                 // the Renderer interface
499 
500                 // force the renderer to be regenerated below, for the
501                 // different attributes to take effect
502                 invalidateRenderer();
503             }
504 
505             mbForceUpdate = false;
506 
507             if( !bRedrawRequired )
508                 return true;
509 
510 
511             // sprite needs repaint - output to sprite canvas
512             // ==============================================
513 
514             ::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() );
515 
516             return draw( pContentCanvas,
517                          rMtf,
518                          pAttr,
519                          aShapeTransformation,
520                          NULL, // clipping is done via Sprite::clip()
521                          rSubsets );
522         }
523 
524         bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr&	rDestinationCanvas,
525                                 const GDIMetaFileSharedPtr&			rMtf,
526                                 const ::basegfx::B2DRectangle&		rBounds,
527                                 const ::basegfx::B2DRectangle&		rUpdateBounds,
528                                 int									nUpdateFlags,
529                                 const ShapeAttributeLayerSharedPtr&	pAttr,
530                                 const VectorOfDocTreeNodes&			rSubsets,
531                                 bool 								bIsVisible ) const
532         {
533             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::render()" );
534 
535             // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape,
536             // in that all the common setup steps here are refactored to Shape (would then
537             // have to be performed only _once_ per Shape paint).
538 
539             if( !bIsVisible )
540             {
541                 VERBOSE_TRACE( "ViewShape::render(): skipping shape %X", this );
542 
543                 // shape is invisible, no need to update anything.
544                 return true;
545             }
546 
547             // since we have no sprite here, _any_ update request
548             // translates into a required redraw.
549             bool bRedrawRequired( mbForceUpdate || nUpdateFlags != 0 );
550 
551             if( (nUpdateFlags & CONTENT) )
552             {
553                 // TODO(P1): maybe provide some appearance change methods at
554                 // the Renderer interface
555 
556                 // force the renderer to be regenerated below, for the
557                 // different attributes to take effect
558                 invalidateRenderer();
559             }
560 
561             mbForceUpdate = false;
562 
563             if( !bRedrawRequired )
564                 return true;
565 
566             VERBOSE_TRACE( "ViewShape::render(): rendering shape %X at position (%f,%f)",
567                            this,
568                            rBounds.getMinX(),
569                            rBounds.getMinY() );
570 
571 
572             // shape needs repaint - setup all that's needed
573             // ---------------------------------------------
574 
575             boost::optional<basegfx::B2DPolyPolygon> aClip;
576 
577             if( pAttr )
578             {
579                 // setup clip poly
580                 if( pAttr->isClipValid() )
581                     aClip.reset( pAttr->getClip() );
582 
583                 // emulate global shape alpha by first rendering into
584                 // a temp bitmap, and then to screen (this would have
585                 // been much easier if we'd be currently a sprite -
586                 // see above)
587                 if( pAttr->isAlphaValid() )
588                 {
589                     const double nAlpha( pAttr->getAlpha() );
590 
591                     if( !::basegfx::fTools::equalZero( nAlpha ) &&
592                         !::rtl::math::approxEqual(nAlpha, 1.0) )
593                     {
594                         // render with global alpha - have to prepare
595                         // a bitmap, and render that with modulated
596                         // alpha
597                         // -------------------------------------------
598 
599                         const ::basegfx::B2DHomMatrix aTransform(
600                             getShapeTransformation( rBounds,
601                                                     pAttr ) );
602 
603                         // TODO(P1): Should the view transform some
604                         // day contain rotation/shear, transforming
605                         // the original bounds with the total
606                         // transformation might result in smaller
607                         // overall bounds.
608 
609                         // determine output rect of _shape update
610                         // area_ in device pixel
611                         const ::basegfx::B2DHomMatrix aCanvasTransform(
612                             rDestinationCanvas->getTransformation() );
613                         ::basegfx::B2DRectangle aTmpRect;
614                         ::canvas::tools::calcTransformedRectBounds( aTmpRect,
615                                                                     rUpdateBounds,
616                                                                     aCanvasTransform );
617 
618                         // pixel size of cache bitmap: round up to
619                         // nearest int
620                         const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1,
621                                                            static_cast<sal_Int32>( aTmpRect.getHeight() )+1 );
622 
623                         // try to fetch temporary surface for alpha
624                         // compositing (to achieve the global alpha
625                         // blend effect, have to first render shape as
626                         // a whole, then blit that surface with global
627                         // alpha to the destination)
628                         const RendererCacheVector::iterator aCompositingSurface(
629                             getCacheEntry( rDestinationCanvas ) );
630 
631                         if( !aCompositingSurface->mpLastBitmapCanvas ||
632                             aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize )
633                         {
634                             // create a bitmap of appropriate size
635                             ::cppcanvas::BitmapSharedPtr pBitmap(
636                                 ::cppcanvas::BaseGfxFactory::getInstance().createAlphaBitmap(
637                                     rDestinationCanvas,
638                                     aBmpSize ) );
639 
640                             ENSURE_OR_THROW(pBitmap,
641                                              "ViewShape::render(): Could not create compositing surface");
642 
643                             aCompositingSurface->mpDestinationCanvas = rDestinationCanvas;
644                             aCompositingSurface->mpLastBitmap		 = pBitmap;
645                             aCompositingSurface->mpLastBitmapCanvas	 = pBitmap->getBitmapCanvas();
646                         }
647 
648                         // buffer aCompositingSurface iterator content
649                         // - said one might get invalidated during
650                         // draw() below.
651                         ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas(
652                             aCompositingSurface->mpLastBitmapCanvas );
653 
654                         ::cppcanvas::BitmapSharedPtr pBitmap(
655                             aCompositingSurface->mpLastBitmap);
656 
657                         // setup bitmap canvas transformation -
658                         // which happens to be the destination
659                         // canvas transformation without any
660                         // translational components.
661                         //
662                         // But then, the render transformation as
663                         // calculated by getShapeTransformation()
664                         // above outputs the shape at its real
665                         // destination position. Thus, we have to
666                         // offset the output back to the origin,
667                         // for which we simply plug in the
668                         // negative position of the left, top edge
669                         // of the shape's bound rect in device
670                         // pixel into aLinearTransform below.
671                         ::basegfx::B2DHomMatrix aAdjustedCanvasTransform( aCanvasTransform );
672                         aAdjustedCanvasTransform.translate( -aTmpRect.getMinX(),
673                                                             -aTmpRect.getMinY() );
674 
675                         pBitmapCanvas->setTransformation( aAdjustedCanvasTransform );
676 
677                         // TODO(P2): If no update flags, or only
678                         // alpha_update is set, we can save us the
679                         // rendering into the bitmap (uh, it's not
680                         // _that_ easy - for a forced redraw,
681                         // e.g. when ending an animation, we always
682                         // get UPDATE_FORCE here).
683 
684                         // render into this bitmap
685                         if( !draw( pBitmapCanvas,
686                                    rMtf,
687                                    pAttr,
688                                    aTransform,
689                                    !aClip ? NULL : &(*aClip),
690                                    rSubsets ) )
691                         {
692                             return false;
693                         }
694 
695                         // render bitmap to screen, with given global
696                         // alpha. Since the bitmap already contains
697                         // pixel-equivalent output, we have to use the
698                         // inverse view transformation, adjusted with
699                         // the final shape output position (note:
700                         // cannot simply change the view
701                         // transformation here, as that would affect a
702                         // possibly set clip!)
703                         ::basegfx::B2DHomMatrix aBitmapTransform( aCanvasTransform );
704                         OSL_ENSURE( aBitmapTransform.isInvertible(),
705                                     "ViewShape::render(): View transformation is singular!" );
706 
707                         aBitmapTransform.invert();
708 
709                         const basegfx::B2DHomMatrix aTranslation(basegfx::tools::createTranslateB2DHomMatrix(
710                             aTmpRect.getMinX(), aTmpRect.getMinY()));
711 
712                         aBitmapTransform = aBitmapTransform * aTranslation;
713                         pBitmap->setTransformation( aBitmapTransform );
714 
715                         // finally, render bitmap alpha-modulated
716                         pBitmap->drawAlphaModulated( nAlpha );
717 
718                         return true;
719                     }
720                 }
721             }
722 
723             // retrieve shape transformation, _with_ shape translation
724             // to actual page position.
725             const ::basegfx::B2DHomMatrix aTransform(
726                 getShapeTransformation( rBounds,
727                                         pAttr ) );
728 
729             return draw( rDestinationCanvas,
730                          rMtf,
731                          pAttr,
732                          aTransform,
733                          !aClip ? NULL : &(*aClip),
734                          rSubsets );
735         }
736 
737 
738         // -------------------------------------------------------------------------------------
739 
740         ViewShape::ViewShape( const ViewLayerSharedPtr& rViewLayer ) :
741             mpViewLayer( rViewLayer ),
742             maRenderers(),
743             mpSprite(),
744             mbAnimationMode( false ),
745             mbForceUpdate( true )
746         {
747             ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" );
748         }
749 
750         ViewLayerSharedPtr ViewShape::getViewLayer() const
751         {
752             return mpViewLayer;
753         }
754 
755         ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr&	rDestinationCanvas ) const
756         {
757             // lookup destination canvas - is there already a renderer
758             // created for that target?
759             RendererCacheVector::iterator 		aIter;
760             const RendererCacheVector::iterator aEnd( maRenderers.end() );
761 
762             // already there?
763             if( (aIter=::std::find_if( maRenderers.begin(),
764                                        aEnd,
765                                        ::boost::bind(
766                                            ::std::equal_to< ::cppcanvas::CanvasSharedPtr >(),
767                                            ::boost::cref( rDestinationCanvas ),
768                                            ::boost::bind(
769                                                &RendererCacheEntry::getDestinationCanvas,
770                                                _1 ) ) ) ) == aEnd )
771             {
772                 if( maRenderers.size() >= MAX_RENDER_CACHE_ENTRIES )
773                 {
774                     // cache size exceeded - prune entries. For now,
775                     // simply remove the first one, which of course
776                     // breaks for more complex access schemes. But in
777                     // general, this leads to most recently used
778                     // entries to reside at the end of the vector.
779                     maRenderers.erase( maRenderers.begin() );
780 
781                     // ATTENTION: after this, both aIter and aEnd are
782                     // invalid!
783                 }
784 
785                 // not yet in cache - add default-constructed cache
786                 // entry, to have something to return
787                 maRenderers.push_back( RendererCacheEntry() );
788                 aIter = maRenderers.end()-1;
789             }
790 
791             return aIter;
792         }
793 
794         ::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr&	rDestinationCanvas,
795                                                                const GDIMetaFileSharedPtr&			rMtf,
796                                                                const ShapeAttributeLayerSharedPtr&	rAttr ) const
797         {
798             // lookup destination canvas - is there already a renderer
799             // created for that target?
800             const RendererCacheVector::iterator aIter(
801                 getCacheEntry( rDestinationCanvas ) );
802 
803             // now we have a valid entry, either way. call prefetch()
804             // on it, nevertheless - maybe the metafile changed, and
805             // the renderer still needs an update (prefetch() will
806             // detect that)
807             if( prefetch( *aIter,
808                           rDestinationCanvas,
809                           rMtf,
810                           rAttr ) )
811             {
812                 return aIter->mpRenderer;
813             }
814             else
815             {
816                 // prefetch failed - renderer is invalid
817                 return ::cppcanvas::RendererSharedPtr();
818             }
819         }
820 
821         void ViewShape::invalidateRenderer() const
822         {
823             // simply clear the cache. Subsequent getRenderer() calls
824             // will regenerate the Renderers.
825             maRenderers.clear();
826         }
827 
828         ::basegfx::B2DSize ViewShape::getAntialiasingBorder() const
829         {
830             ENSURE_OR_THROW( mpViewLayer->getCanvas(),
831                               "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" );
832 
833             const ::basegfx::B2DHomMatrix& rViewTransform(
834                 mpViewLayer->getTransformation() );
835 
836             // TODO(F1): As a quick shortcut (did not want to invert
837             // whole matrix here), taking only scale components of
838             // view transformation matrix. This will be wrong when
839             // e.g. shearing is involved.
840             const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(0,0) );
841             const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(1,1) );
842 
843             return ::basegfx::B2DSize( nXBorder,
844                                        nYBorder );
845         }
846 
847         bool ViewShape::enterAnimationMode()
848         {
849             mbForceUpdate   = true;
850             mbAnimationMode = true;
851 
852             return true;
853         }
854 
855         void ViewShape::leaveAnimationMode()
856         {
857             mpSprite.reset();
858             mbAnimationMode = false;
859             mbForceUpdate   = true;
860         }
861 
862         bool ViewShape::update( const GDIMetaFileSharedPtr&	rMtf,
863                                 const RenderArgs&			rArgs,
864                                 int							nUpdateFlags,
865                                 bool						bIsVisible ) const
866         {
867             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::update()" );
868             ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" );
869 
870             // Shall we render to a sprite, or to a plain canvas?
871             if( isBackgroundDetached() )
872                 return renderSprite( mpViewLayer,
873                                      rMtf,
874                                      rArgs.maOrigBounds,
875                                      rArgs.maBounds,
876                                      rArgs.maUnitBounds,
877                                      nUpdateFlags,
878                                      rArgs.mrAttr,
879                                      rArgs.mrSubsets,
880                                      rArgs.mnShapePriority,
881                                      bIsVisible );
882             else
883                 return render( mpViewLayer->getCanvas(),
884                                rMtf,
885                                rArgs.maBounds,
886                                rArgs.maUpdateBounds,
887                                nUpdateFlags,
888                                rArgs.mrAttr,
889                                rArgs.mrSubsets,
890                                bIsVisible );
891         }
892 
893     }
894 }
895