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