Branch data 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: */
|