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 : #include <canvas/debug.hxx>
21 : #include <canvas/verbosetrace.hxx>
22 : #include <canvas/canvastools.hxx>
23 : #include <tools/diagnose_ex.h>
24 :
25 : #include <vcl/canvastools.hxx>
26 :
27 : #include <comphelper/scopeguard.hxx>
28 :
29 : #include <basegfx/range/b2drectangle.hxx>
30 : #include <basegfx/tools/canvastools.hxx>
31 :
32 : #include <boost/cast.hpp>
33 :
34 : #include "cairo_spritecanvashelper.hxx"
35 : #include "cairo_canvascustomsprite.hxx"
36 :
37 : using namespace ::cairo;
38 : using namespace ::com::sun::star;
39 :
40 : namespace cairocanvas
41 : {
42 : namespace
43 : {
44 : /** Sprite redraw at original position
45 :
46 : Used to repaint the whole canvas (background and all
47 : sprites)
48 : */
49 0 : void spriteRedraw( const CairoSharedPtr& pCairo,
50 : const ::canvas::Sprite::Reference& rSprite )
51 : {
52 : // downcast to derived cairocanvas::Sprite interface, which
53 : // provides the actual redraw methods.
54 0 : ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->redraw( pCairo, true);
55 0 : }
56 :
57 0 : void repaintBackground( const CairoSharedPtr& pCairo,
58 : const SurfaceSharedPtr& pBackgroundSurface,
59 : const ::basegfx::B2DRange& rArea )
60 : {
61 0 : cairo_save( pCairo.get() );
62 : cairo_rectangle( pCairo.get(), ceil( rArea.getMinX() ), ceil( rArea.getMinY() ),
63 0 : floor( rArea.getWidth() ), floor( rArea.getHeight() ) );
64 0 : cairo_clip( pCairo.get() );
65 0 : cairo_set_source_surface( pCairo.get(), pBackgroundSurface->getCairoSurface().get(), 0, 0 );
66 0 : cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
67 0 : cairo_paint( pCairo.get() );
68 0 : cairo_restore( pCairo.get() );
69 0 : }
70 :
71 0 : void opaqueUpdateSpriteArea( const ::canvas::Sprite::Reference& rSprite,
72 : const CairoSharedPtr& pCairo,
73 : const ::basegfx::B2IRange& rArea )
74 : {
75 : // clip output to actual update region (otherwise a)
76 : // wouldn't save much render time, and b) will clutter
77 : // scrolled sprite content outside this area)
78 0 : cairo_save( pCairo.get() );
79 0 : cairo_rectangle( pCairo.get(), rArea.getMinX(), rArea.getMinY(),
80 0 : sal::static_int_cast<sal_Int32>(rArea.getWidth()),
81 0 : sal::static_int_cast<sal_Int32>(rArea.getHeight()) );
82 0 : cairo_clip( pCairo.get() );
83 :
84 : // repaint affected sprite directly to output device (at
85 : // the actual screen output position)
86 : // rendering directly to device buffer
87 0 : ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw( pCairo, false );
88 :
89 0 : cairo_restore( pCairo.get() );
90 0 : }
91 :
92 : /** Repaint sprite at original position
93 :
94 : Used for opaque updates, which render directly to the
95 : device buffer.
96 : */
97 0 : void spriteRedrawStub( const CairoSharedPtr& pCairo,
98 : const ::canvas::Sprite::Reference& rSprite )
99 : {
100 0 : if( rSprite.is() )
101 : {
102 0 : ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw( pCairo, false );
103 : }
104 0 : }
105 :
106 : /** Repaint sprite at given position
107 :
108 : Used for generic update, which renders into device buffer.
109 : */
110 0 : void spriteRedrawStub2( const CairoSharedPtr& pCairo,
111 : const ::canvas::Sprite::Reference& rSprite )
112 : {
113 0 : if( rSprite.is() )
114 : {
115 0 : ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw( pCairo, true );
116 : }
117 0 : }
118 :
119 : /** Repaint sprite at original position
120 :
121 : Used for opaque updates from scrollUpdate(), which render
122 : directly to the front buffer.
123 : */
124 0 : void spriteRedrawStub3( const CairoSharedPtr& pCairo,
125 : const ::canvas::SpriteRedrawManager::AreaComponent& rComponent )
126 : {
127 0 : const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() );
128 :
129 0 : if( rSprite.is() )
130 0 : ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw( pCairo, false );
131 0 : }
132 : }
133 :
134 0 : SpriteCanvasHelper::SpriteCanvasHelper() :
135 : mpRedrawManager( NULL ),
136 : mpOwningSpriteCanvas( NULL ),
137 : mpCompositingSurface(),
138 : maCompositingSurfaceSize(),
139 0 : mbCompositingSurfaceDirty(true)
140 : {
141 0 : }
142 :
143 0 : void SpriteCanvasHelper::init( ::canvas::SpriteRedrawManager& rManager,
144 : SpriteCanvas& rDevice,
145 : const ::basegfx::B2ISize& rSize )
146 : {
147 0 : mpRedrawManager = &rManager;
148 0 : mpOwningSpriteCanvas = &rDevice;
149 :
150 0 : CanvasHelper::init( rSize, rDevice, &rDevice );
151 0 : }
152 :
153 0 : void SpriteCanvasHelper::disposing()
154 : {
155 0 : mpCompositingSurface.reset();
156 0 : mpOwningSpriteCanvas = NULL;
157 0 : mpRedrawManager = NULL;
158 :
159 : // forward to base
160 0 : CanvasHelper::disposing();
161 0 : }
162 :
163 0 : uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation(
164 : const uno::Reference< rendering::XAnimation >& )
165 : {
166 0 : return uno::Reference< rendering::XAnimatedSprite >();
167 : }
168 :
169 0 : uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps(
170 : const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/,
171 : sal_Int8 /*interpolationMode*/ )
172 : {
173 0 : return uno::Reference< rendering::XAnimatedSprite >();
174 : }
175 :
176 0 : uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize )
177 : {
178 0 : if( !mpRedrawManager )
179 0 : return uno::Reference< rendering::XCustomSprite >(); // we're disposed
180 :
181 : return uno::Reference< rendering::XCustomSprite >(
182 : new CanvasCustomSprite( spriteSize,
183 0 : mpOwningSpriteCanvas ) );
184 : }
185 :
186 0 : uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite(
187 : const uno::Reference< rendering::XSprite >& )
188 : {
189 0 : return uno::Reference< rendering::XSprite >();
190 : }
191 :
192 0 : bool SpriteCanvasHelper::updateScreen( const ::basegfx::B2IRange& /*rCurrArea*/,
193 : bool bUpdateAll,
194 : bool& io_bSurfaceDirty )
195 : {
196 0 : if( !mpRedrawManager ||
197 0 : !mpOwningSpriteCanvas ||
198 0 : !mpOwningSpriteCanvas->getWindowSurface() ||
199 0 : !mpOwningSpriteCanvas->getBufferSurface() )
200 : {
201 0 : return false; // disposed, or otherwise dysfunctional
202 : }
203 :
204 : SAL_INFO("canvas.cairo", "SpriteCanvasHelper::updateScreen called");
205 :
206 0 : const ::basegfx::B2ISize& rSize = mpOwningSpriteCanvas->getSizePixel();
207 :
208 : // force compositing surface to be available before using it
209 : // inside forEachSpriteArea
210 0 : SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rSize);
211 0 : SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
212 0 : CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
213 0 : CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
214 :
215 : // TODO(P1): Might be worthwile to track areas of background
216 : // changes, too.
217 0 : if( !bUpdateAll && !io_bSurfaceDirty && !mbCompositingSurfaceDirty )
218 : {
219 : // background has not changed, so we're free to optimize
220 : // repaint to areas where a sprite has changed
221 :
222 : // process each independent area of overlapping sprites
223 : // separately.
224 0 : mpRedrawManager->forEachSpriteArea( *this );
225 : }
226 : else
227 : {
228 : SAL_INFO("canvas.cairo", "SpriteCanvasHelper::updateScreen update ALL");
229 :
230 : // background has changed, so we currently have no choice
231 : // but repaint everything (or caller requested that)
232 :
233 0 : cairo_rectangle( pCompositingCairo.get(), 0, 0, rSize.getX(), rSize.getY() );
234 0 : cairo_clip( pCompositingCairo.get() );
235 0 : cairo_save( pCompositingCairo.get() );
236 : cairo_set_source_surface( pCompositingCairo.get(),
237 0 : mpOwningSpriteCanvas->getBufferSurface()->getCairoSurface().get(),
238 0 : 0, 0 );
239 0 : cairo_set_operator( pCompositingCairo.get(), CAIRO_OPERATOR_SOURCE );
240 0 : cairo_paint( pCompositingCairo.get() );
241 0 : cairo_restore( pCompositingCairo.get() );
242 :
243 : // repaint all active sprites on top of background into
244 : // VDev.
245 : mpRedrawManager->forEachSprite(
246 : ::boost::bind(
247 : &spriteRedraw,
248 : boost::cref(pCompositingCairo),
249 0 : _1 ) );
250 :
251 : // flush to screen
252 0 : cairo_rectangle( pWindowCairo.get(), 0, 0, rSize.getX(), rSize.getY() );
253 0 : cairo_clip( pWindowCairo.get() );
254 : cairo_set_source_surface( pWindowCairo.get(),
255 0 : pCompositingSurface->getCairoSurface().get(),
256 0 : 0, 0 );
257 0 : cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
258 0 : cairo_paint( pWindowCairo.get() );
259 : }
260 :
261 : // change record vector must be cleared, for the next turn of
262 : // rendering and sprite changing
263 0 : mpRedrawManager->clearChangeRecords();
264 :
265 0 : mbCompositingSurfaceDirty = false;
266 0 : io_bSurfaceDirty = false;
267 :
268 : // commit to screen
269 0 : mpOwningSpriteCanvas->flush();
270 :
271 0 : return true;
272 : }
273 :
274 0 : void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
275 : {
276 0 : if( mpOwningSpriteCanvas && mpCompositingSurface )
277 0 : repaintBackground( mpCompositingSurface->getCairo(),
278 : mpOwningSpriteCanvas->getBufferSurface(),
279 0 : rUpdateRect );
280 0 : }
281 :
282 0 : void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& rMoveStart,
283 : const ::basegfx::B2DRange& rMoveEnd,
284 : const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
285 : {
286 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
287 : mpOwningSpriteCanvas->getBufferSurface(),
288 : "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
289 :
290 : SAL_INFO("canvas.cairo", "SpriteCanvasHelper::scrollUpdate called");
291 :
292 0 : const ::basegfx::B2ISize& rSize = mpOwningSpriteCanvas->getSizePixel();
293 : const ::basegfx::B2IRange aOutputBounds( 0,0,
294 : rSize.getX(),
295 0 : rSize.getY() );
296 :
297 0 : SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rSize);
298 0 : SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
299 0 : CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
300 0 : CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
301 :
302 : // round rectangles to integer pixel. Note: have to be
303 : // extremely careful here, to avoid off-by-one errors for
304 : // the destination area: otherwise, the next scroll update
305 : // would copy pixel that are not supposed to be part of
306 : // the sprite.
307 : ::basegfx::B2IRange aSourceRect(
308 0 : ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) );
309 : const ::basegfx::B2IRange& rDestRect(
310 0 : ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
311 0 : ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() );
312 :
313 0 : ::std::vector< ::basegfx::B2IRange > aUnscrollableAreas;
314 :
315 : // TODO(E3): This is plain buggy (but copies the behaviour of
316 : // the old Impress slideshow) - the scrolled area might
317 : // actually lie _below_ another window!
318 :
319 : // clip to output bounds (cannot properly scroll stuff
320 : // _outside_ our screen area)
321 0 : if( !::canvas::tools::clipScrollArea( aSourceRect,
322 : aDestPos,
323 : aUnscrollableAreas,
324 0 : aOutputBounds ) )
325 : {
326 : // fully clipped scroll area: cannot simply scroll
327 : // then. Perform normal opaque update (can use that, since
328 : // one of the preconditions for scrollable update is
329 : // opaque sprite content)
330 :
331 : // repaint all affected sprites directly to output device
332 : ::std::for_each( rUpdateArea.maComponentList.begin(),
333 : rUpdateArea.maComponentList.end(),
334 : ::boost::bind(
335 : &spriteRedrawStub3,
336 : boost::cref(pCompositingCairo),
337 0 : _1 ) );
338 : }
339 : else
340 : {
341 0 : const ::basegfx::B2IVector aSourceUpperLeftPos( aSourceRect.getMinimum() );
342 :
343 : // clip dest area (which must be inside rDestBounds)
344 0 : ::basegfx::B2IRange aDestRect( rDestRect );
345 0 : aDestRect.intersect( aOutputBounds );
346 :
347 0 : ::basegfx::B2ISize aScrollSize( aDestRect.getWidth(), aDestRect.getHeight() );
348 0 : SurfaceSharedPtr pScrollSurface( getTemporarySurface() );
349 0 : CairoSharedPtr pScrollCairo( pScrollSurface->getCairo() );
350 :
351 0 : cairo_save( pScrollCairo.get() );
352 : // scroll the current content of the compositing surface (and,
353 : // thus, of the window) in temp. surface
354 : cairo_set_source_surface( pScrollCairo.get(),
355 0 : pCompositingSurface->getCairoSurface().get(),
356 0 : aDestPos.getX() - aSourceUpperLeftPos.getX(),
357 0 : aDestPos.getY() - aSourceUpperLeftPos.getY() );
358 : cairo_rectangle( pScrollCairo.get(),
359 0 : aDestPos.getX(), aDestPos.getY(),
360 0 : aScrollSize.getX(), aScrollSize.getY() );
361 0 : cairo_clip( pScrollCairo.get() );
362 0 : cairo_set_operator( pScrollCairo.get(), CAIRO_OPERATOR_SOURCE );
363 0 : cairo_paint( pScrollCairo.get() );
364 0 : cairo_restore( pScrollCairo.get() );
365 :
366 0 : cairo_save( pCompositingCairo.get() );
367 : // copy the scrolled area back onto the compositing surface
368 : cairo_set_source_surface( pCompositingCairo.get(),
369 0 : pScrollSurface->getCairoSurface().get(),
370 0 : 0, 0 );
371 : cairo_rectangle( pCompositingCairo.get(),
372 0 : aDestPos.getX(), aDestPos.getY(),
373 0 : aScrollSize.getX(), aScrollSize.getY() );
374 0 : cairo_clip( pCompositingCairo.get() );
375 0 : cairo_set_operator( pCompositingCairo.get(), CAIRO_OPERATOR_SOURCE );
376 0 : cairo_paint( pCompositingCairo.get() );
377 0 : cairo_restore( pCompositingCairo.get() );
378 :
379 : const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
380 0 : aFirst( rUpdateArea.maComponentList.begin() );
381 : ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
382 0 : aSecond( aFirst ); ++aSecond;
383 :
384 0 : ENSURE_OR_THROW( aFirst->second.getSprite().is(),
385 : "VCLCanvas::scrollUpdate(): no sprite" );
386 :
387 : // repaint uncovered areas from sprite. Need to actually
388 : // clip here, since we're only repainting _parts_ of the
389 : // sprite
390 : ::std::for_each( aUnscrollableAreas.begin(),
391 : aUnscrollableAreas.end(),
392 : ::boost::bind( &opaqueUpdateSpriteArea,
393 0 : ::boost::cref(aFirst->second.getSprite()),
394 : boost::cref(pCompositingCairo),
395 0 : _1 ) );
396 : }
397 :
398 : // repaint uncovered areas from backbuffer - take the
399 : // _rounded_ rectangles from above, to have the update
400 : // consistent with the scroll above.
401 0 : ::std::vector< ::basegfx::B2DRange > aUncoveredAreas;
402 : ::basegfx::computeSetDifference( aUncoveredAreas,
403 : rUpdateArea.maTotalBounds,
404 0 : ::basegfx::B2DRange( rDestRect ) );
405 0 : SurfaceSharedPtr surface(mpOwningSpriteCanvas->getBufferSurface());
406 : ::std::for_each( aUncoveredAreas.begin(),
407 : aUncoveredAreas.end(),
408 : ::boost::bind( &repaintBackground,
409 : boost::cref(pCompositingCairo),
410 : boost::cref(surface),
411 0 : _1 ) );
412 :
413 0 : cairo_rectangle( pWindowCairo.get(), 0, 0, rSize.getX(), rSize.getY() );
414 0 : cairo_clip( pWindowCairo.get() );
415 : cairo_set_source_surface( pWindowCairo.get(),
416 0 : pCompositingSurface->getCairoSurface().get(),
417 0 : 0, 0 );
418 0 : cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
419 0 : cairo_paint( pWindowCairo.get() );
420 0 : }
421 :
422 0 : void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea,
423 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
424 : {
425 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
426 : mpOwningSpriteCanvas->getBufferSurface(),
427 : "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
428 :
429 : SAL_INFO("canvas.cairo", "SpriteCanvasHelper::opaqueUpdate called");
430 :
431 0 : const ::basegfx::B2ISize& rDeviceSize = mpOwningSpriteCanvas->getSizePixel();
432 :
433 0 : SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rDeviceSize);
434 0 : SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
435 0 : CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
436 0 : CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
437 :
438 0 : cairo_rectangle( pCompositingCairo.get(), 0, 0, rDeviceSize.getX(), rDeviceSize.getY() );
439 0 : cairo_clip( pCompositingCairo.get() );
440 :
441 0 : ::basegfx::B2DVector aPos( ceil( rTotalArea.getMinX() ), ceil( rTotalArea.getMinY() ) );
442 0 : ::basegfx::B2DVector aSize( floor( rTotalArea.getMaxX() - aPos.getX() ), floor( rTotalArea.getMaxY() - aPos.getY() ) );
443 :
444 0 : cairo_rectangle( pCompositingCairo.get(), aPos.getX(), aPos.getY(), aSize.getX(), aSize.getY() );
445 0 : cairo_clip( pCompositingCairo.get() );
446 :
447 : // repaint all affected sprites directly to output device
448 : ::std::for_each( rSortedUpdateSprites.begin(),
449 : rSortedUpdateSprites.end(),
450 : ::boost::bind(
451 : &spriteRedrawStub,
452 : boost::cref(pCompositingCairo),
453 0 : _1 ) );
454 :
455 : // flush to screen
456 0 : cairo_rectangle( pWindowCairo.get(), 0, 0, rDeviceSize.getX(), rDeviceSize.getY() );
457 0 : cairo_clip( pWindowCairo.get() );
458 0 : cairo_rectangle( pWindowCairo.get(), aPos.getX(), aPos.getY(), aSize.getX(), aSize.getY() );
459 0 : cairo_clip( pWindowCairo.get() );
460 : cairo_set_source_surface( pWindowCairo.get(),
461 0 : pCompositingSurface->getCairoSurface().get(),
462 0 : 0, 0 );
463 0 : cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
464 0 : cairo_paint( pWindowCairo.get() );
465 0 : }
466 :
467 0 : void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea,
468 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
469 : {
470 : // TODO
471 : SAL_INFO("canvas.cairo", "SpriteCanvasHelper::genericUpdate called");
472 :
473 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
474 : mpOwningSpriteCanvas->getBufferSurface(),
475 : "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
476 :
477 : // limit size of update VDev to target outdev's size
478 0 : const ::basegfx::B2ISize& rSize = mpOwningSpriteCanvas->getSizePixel();
479 :
480 0 : SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rSize);
481 0 : SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
482 0 : CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
483 0 : CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
484 :
485 : // round output position towards zero. Don't want to truncate
486 : // a fraction of a sprite pixel... Clip position at origin,
487 : // otherwise, truncation of size below might leave visible
488 : // areas uncovered by VDev.
489 : const Point aOutputPosition(
490 : ::std::max( sal_Int32( 0 ),
491 0 : static_cast< sal_Int32 >(rRequestedArea.getMinX()) ),
492 : ::std::max( sal_Int32( 0 ),
493 0 : static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) );
494 : // round output size towards +infty. Don't want to truncate a
495 : // fraction of a sprite pixel... Limit size of VDev to output
496 : // device's area.
497 : const Size aOutputSize(
498 0 : ::std::min( rSize.getX(),
499 0 : ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X()) ),
500 0 : ::std::min( rSize.getY(),
501 0 : ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y()) ) );
502 :
503 0 : cairo_rectangle( pCompositingCairo.get(), aOutputPosition.X(), aOutputPosition.Y(), aOutputSize.Width(), aOutputSize.Height() );
504 0 : cairo_clip( pCompositingCairo.get() );
505 :
506 : // paint background
507 0 : cairo_save( pCompositingCairo.get() );
508 : cairo_set_source_surface( pCompositingCairo.get(),
509 0 : mpOwningSpriteCanvas->getBufferSurface()->getCairoSurface().get(),
510 0 : 0, 0 );
511 0 : cairo_set_operator( pCompositingCairo.get(), CAIRO_OPERATOR_SOURCE );
512 0 : cairo_paint( pCompositingCairo.get() );
513 0 : cairo_restore( pCompositingCairo.get() );
514 :
515 : // repaint all affected sprites on top of background into
516 : // VDev.
517 : ::std::for_each( rSortedUpdateSprites.begin(),
518 : rSortedUpdateSprites.end(),
519 : ::boost::bind( &spriteRedrawStub2,
520 : boost::cref(pCompositingCairo),
521 0 : _1 ) );
522 :
523 : // flush to screen
524 0 : cairo_rectangle( pWindowCairo.get(), aOutputPosition.X(), aOutputPosition.Y(), aOutputSize.Width(), aOutputSize.Height() );
525 0 : cairo_clip( pWindowCairo.get() );
526 : cairo_set_source_surface( pWindowCairo.get(),
527 0 : pCompositingSurface->getCairoSurface().get(),
528 0 : 0, 0 );
529 0 : cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
530 0 : cairo_paint( pWindowCairo.get() );
531 0 : }
532 :
533 0 : ::cairo::SurfaceSharedPtr SpriteCanvasHelper::getCompositingSurface( const ::basegfx::B2ISize& rNeededSize )
534 : {
535 0 : if( rNeededSize.getX() > maCompositingSurfaceSize.getX() ||
536 0 : rNeededSize.getY() > maCompositingSurfaceSize.getY() )
537 : {
538 : // need to give buffer more size
539 0 : mpCompositingSurface.reset();
540 : }
541 :
542 0 : if( !mpCompositingSurface )
543 : {
544 0 : mpCompositingSurface = createSurface( rNeededSize );
545 0 : maCompositingSurfaceSize = rNeededSize;
546 0 : mbCompositingSurfaceDirty = true;
547 0 : mpTemporarySurface.reset();
548 : }
549 :
550 0 : return mpCompositingSurface;
551 : }
552 :
553 0 : ::cairo::SurfaceSharedPtr SpriteCanvasHelper::getTemporarySurface()
554 : {
555 0 : if ( !mpTemporarySurface )
556 0 : mpTemporarySurface = createSurface( maCompositingSurfaceSize );
557 0 : return mpTemporarySurface;
558 : }
559 :
560 0 : ::cairo::SurfaceSharedPtr SpriteCanvasHelper::createSurface( const ::basegfx::B2ISize& rNeededSize ) const
561 : {
562 0 : return mpOwningSpriteCanvas->getWindowSurface()->getSimilar(
563 : CAIRO_CONTENT_COLOR,
564 0 : rNeededSize.getX(), rNeededSize.getY() );
565 : }
566 6 : }
567 :
568 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|