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