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