Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include <canvas/debug.hxx>
31 : : #include <tools/diagnose_ex.h>
32 : : #include <canvas/verbosetrace.hxx>
33 : :
34 : : #include <rtl/math.hxx>
35 : :
36 : : #include <vcl/outdev.hxx>
37 : : #include <vcl/bitmap.hxx>
38 : : #include <vcl/alpha.hxx>
39 : : #include <vcl/bitmapex.hxx>
40 : : #include <vcl/canvastools.hxx>
41 : :
42 : : #include <basegfx/matrix/b2dhommatrix.hxx>
43 : : #include <basegfx/point/b2dpoint.hxx>
44 : : #include <basegfx/tools/canvastools.hxx>
45 : : #include <basegfx/polygon/b2dpolygon.hxx>
46 : : #include <basegfx/polygon/b2dpolygontools.hxx>
47 : : #include <basegfx/polygon/b2dpolypolygontools.hxx>
48 : : #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
49 : : #include <basegfx/polygon/b2dpolygontriangulator.hxx>
50 : : #include <basegfx/polygon/b2dpolygonclipper.hxx>
51 : : #include <basegfx/numeric/ftools.hxx>
52 : :
53 : : #include <canvas/canvastools.hxx>
54 : :
55 : : #include "spritehelper.hxx"
56 : :
57 : : using namespace ::com::sun::star;
58 : :
59 : :
60 : : namespace vclcanvas
61 : : {
62 : 0 : SpriteHelper::SpriteHelper() :
63 : : mpBackBuffer(),
64 : : mpBackBufferMask(),
65 : : maContent(),
66 : 0 : mbShowSpriteBounds(false)
67 : : {
68 : 0 : }
69 : :
70 : 0 : void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
71 : : const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
72 : : const BackBufferSharedPtr& rBackBuffer,
73 : : const BackBufferSharedPtr& rBackBufferMask,
74 : : bool bShowSpriteBounds )
75 : : {
76 : 0 : ENSURE_OR_THROW( rOwningSpriteCanvas.get() && rBackBuffer && rBackBufferMask,
77 : : "SpriteHelper::init(): Invalid sprite canvas or back buffer" );
78 : :
79 : 0 : mpBackBuffer = rBackBuffer;
80 : 0 : mpBackBufferMask = rBackBufferMask;
81 : 0 : mbShowSpriteBounds = bShowSpriteBounds;
82 : :
83 : 0 : init( rSpriteSize, rOwningSpriteCanvas );
84 : 0 : }
85 : :
86 : 0 : void SpriteHelper::disposing()
87 : : {
88 : 0 : mpBackBuffer.reset();
89 : 0 : mpBackBufferMask.reset();
90 : :
91 : : // forward to parent
92 : 0 : CanvasCustomSpriteHelper::disposing();
93 : 0 : }
94 : :
95 : 0 : void SpriteHelper::redraw( OutputDevice& rTargetSurface,
96 : : const ::basegfx::B2DPoint& rPos,
97 : : bool& io_bSurfacesDirty,
98 : : bool bBufferedUpdate ) const
99 : : {
100 : : (void)bBufferedUpdate; // not used on every platform
101 : :
102 : 0 : if( !mpBackBuffer ||
103 : 0 : !mpBackBufferMask )
104 : : {
105 : 0 : return; // we're disposed
106 : : }
107 : :
108 : : // log output pos in device pixel
109 : : VERBOSE_TRACE( "SpriteHelper::redraw(): output pos is (%f, %f)",
110 : : rPos.getX(),
111 : : rPos.getY() );
112 : :
113 : 0 : const double fAlpha( getAlpha() );
114 : :
115 : 0 : if( isActive() &&
116 : 0 : !::basegfx::fTools::equalZero( fAlpha ) )
117 : : {
118 : 0 : const Point aEmptyPoint;
119 : 0 : const ::basegfx::B2DVector& rOrigOutputSize( getSizePixel() );
120 : :
121 : : // might get changed below (e.g. adapted for
122 : : // transformations). IMPORTANT: both position and size are
123 : : // rounded to integer values. From now on, only those
124 : : // rounded values are used, to keep clip and content in
125 : : // sync.
126 : 0 : ::Size aOutputSize( ::vcl::unotools::sizeFromB2DSize( rOrigOutputSize ) );
127 : 0 : ::Point aOutPos( ::vcl::unotools::pointFromB2DPoint( rPos ) );
128 : :
129 : :
130 : : // TODO(F3): Support for alpha-VDev
131 : :
132 : : // Do we have to update our bitmaps (necessary if virdev
133 : : // was painted to, or transformation changed)?
134 : : const bool bNeedBitmapUpdate( io_bSurfacesDirty ||
135 : 0 : hasTransformChanged() ||
136 : 0 : maContent->IsEmpty() );
137 : :
138 : : // updating content of sprite cache - surface is no
139 : : // longer dirty in relation to our cache
140 : 0 : io_bSurfacesDirty = false;
141 : 0 : transformUpdated();
142 : :
143 : 0 : if( bNeedBitmapUpdate )
144 : : {
145 : 0 : Bitmap aBmp( mpBackBuffer->getOutDev().GetBitmap( aEmptyPoint,
146 : 0 : aOutputSize ) );
147 : :
148 : 0 : if( isContentFullyOpaque() )
149 : : {
150 : : // optimized case: content canvas is fully
151 : : // opaque. Note: since we retrieved aBmp directly
152 : : // from an OutDev, it's already a 'display bitmap'
153 : : // on windows.
154 : 0 : maContent = BitmapEx( aBmp );
155 : : }
156 : : else
157 : : {
158 : : // sprite content might contain alpha, create
159 : : // BmpEx, then.
160 : 0 : Bitmap aMask( mpBackBufferMask->getOutDev().GetBitmap( aEmptyPoint,
161 : 0 : aOutputSize ) );
162 : :
163 : : // bitmasks are much faster than alphamasks on some platforms
164 : : // so convert to bitmask if useful
165 : : #ifndef QUARTZ
166 : 0 : if( aMask.GetBitCount() != 1 )
167 : : {
168 : : OSL_FAIL("CanvasCustomSprite::redraw(): Mask bitmap is not "
169 : : "monochrome (performance!)");
170 : 0 : aMask.MakeMono(255);
171 : : }
172 : : #endif
173 : :
174 : : // Note: since we retrieved aBmp and aMask
175 : : // directly from an OutDev, it's already a
176 : : // 'display bitmap' on windows.
177 : 0 : maContent = BitmapEx( aBmp, aMask );
178 : 0 : }
179 : : }
180 : :
181 : 0 : ::basegfx::B2DHomMatrix aTransform( getTransformation() );
182 : :
183 : : // check whether matrix is "easy" to handle - pure
184 : : // translations or scales are handled by OutputDevice
185 : : // alone
186 : 0 : const bool bIdentityTransform( aTransform.isIdentity() );
187 : :
188 : : // make transformation absolute (put sprite to final
189 : : // output position). Need to happen here, as we also have
190 : : // to translate the clip polygon
191 : 0 : aTransform.translate( aOutPos.X(),
192 : 0 : aOutPos.Y() );
193 : :
194 : 0 : if( !bIdentityTransform )
195 : : {
196 : 0 : if( !::basegfx::fTools::equalZero( aTransform.get(0,1) ) ||
197 : 0 : !::basegfx::fTools::equalZero( aTransform.get(1,0) ) )
198 : : {
199 : : // "complex" transformation, employ affine
200 : : // transformator
201 : :
202 : : // modify output position, to account for the fact
203 : : // that transformBitmap() always normalizes its output
204 : : // bitmap into the smallest enclosing box.
205 : 0 : ::basegfx::B2DRectangle aDestRect;
206 : : ::canvas::tools::calcTransformedRectBounds( aDestRect,
207 : : ::basegfx::B2DRectangle(0,
208 : : 0,
209 : : rOrigOutputSize.getX(),
210 : : rOrigOutputSize.getY()),
211 : 0 : aTransform );
212 : :
213 : 0 : aOutPos.X() = ::basegfx::fround( aDestRect.getMinX() );
214 : 0 : aOutPos.Y() = ::basegfx::fround( aDestRect.getMinY() );
215 : :
216 : : // TODO(P3): Use optimized bitmap transformation here.
217 : :
218 : : // actually re-create the bitmap ONLY if necessary
219 : 0 : if( bNeedBitmapUpdate )
220 : 0 : maContent = tools::transformBitmap( *maContent,
221 : : aTransform,
222 : : uno::Sequence<double>(),
223 : 0 : tools::MODULATE_NONE );
224 : :
225 : 0 : aOutputSize = maContent->GetSizePixel();
226 : : }
227 : : else
228 : : {
229 : : // relatively 'simplistic' transformation -
230 : : // retrieve scale and translational offset
231 : : aOutputSize.setWidth (
232 : 0 : ::basegfx::fround( rOrigOutputSize.getX() * aTransform.get(0,0) ) );
233 : : aOutputSize.setHeight(
234 : 0 : ::basegfx::fround( rOrigOutputSize.getY() * aTransform.get(1,1) ) );
235 : :
236 : 0 : aOutPos.X() = ::basegfx::fround( aTransform.get(0,2) );
237 : 0 : aOutPos.Y() = ::basegfx::fround( aTransform.get(1,2) );
238 : : }
239 : : }
240 : :
241 : : // transformBitmap() might return empty bitmaps, for tiny
242 : : // scales.
243 : 0 : if( !!(*maContent) )
244 : : {
245 : : // when true, fast path for slide transition has
246 : : // already redrawn the sprite.
247 : 0 : bool bSpriteRedrawn( false );
248 : :
249 : 0 : rTargetSurface.Push( PUSH_CLIPREGION );
250 : :
251 : : // apply clip (if any)
252 : 0 : if( getClip().is() )
253 : : {
254 : : ::basegfx::B2DPolyPolygon aClipPoly(
255 : : ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
256 : 0 : getClip() ));
257 : :
258 : 0 : if( aClipPoly.count() )
259 : : {
260 : : // aTransform already contains the
261 : : // translational component, moving the clip to
262 : : // the final sprite output position.
263 : 0 : aClipPoly.transform( aTransform );
264 : :
265 : : #if ! defined WNT && ! defined QUARTZ
266 : : // non-Windows only - bAtLeastOnePolygon is
267 : : // only used in non-WNT code below
268 : :
269 : : // check whether maybe the clip consists
270 : : // solely out of rectangular polygons. If this
271 : : // is the case, enforce using the triangle
272 : : // clip region setup - non-optimized X11
273 : : // drivers tend to perform abyssmally on
274 : : // XPolygonRegion, which is used internally,
275 : : // when filling complex polypolygons.
276 : 0 : bool bAtLeastOnePolygon( false );
277 : 0 : const sal_Int32 nPolygons( aClipPoly.count() );
278 : :
279 : 0 : for( sal_Int32 i=0; i<nPolygons; ++i )
280 : : {
281 : 0 : if( !::basegfx::tools::isRectangle(
282 : 0 : aClipPoly.getB2DPolygon(i)) )
283 : : {
284 : 0 : bAtLeastOnePolygon = true;
285 : 0 : break;
286 : : }
287 : : }
288 : : #endif
289 : :
290 : 0 : if( mbShowSpriteBounds )
291 : : {
292 : : // Paint green sprite clip area
293 : 0 : rTargetSurface.SetLineColor( Color( 0,255,0 ) );
294 : 0 : rTargetSurface.SetFillColor();
295 : :
296 : 0 : rTargetSurface.DrawPolyPolygon(PolyPolygon(aClipPoly)); // #i76339#
297 : : }
298 : :
299 : : #if ! defined WNT && ! defined QUARTZ
300 : : // as a matter of fact, this fast path only
301 : : // performs well for X11 - under Windows, the
302 : : // clip via SetTriangleClipRegion is faster.
303 : 0 : if( bAtLeastOnePolygon &&
304 : : bBufferedUpdate &&
305 : 0 : ::rtl::math::approxEqual(fAlpha, 1.0) &&
306 : 0 : !maContent->IsTransparent() )
307 : : {
308 : : // fast path for slide transitions
309 : : // (buffered, no alpha, no mask (because
310 : : // full slide is contained in the sprite))
311 : :
312 : : // XOR bitmap onto backbuffer, clear area
313 : : // that should be _visible_ with black,
314 : : // XOR bitmap again on top of that -
315 : : // result: XOR cancels out where no black
316 : : // has been rendered, and yields the
317 : : // original bitmap, where black is
318 : : // underneath.
319 : 0 : rTargetSurface.Push( PUSH_RASTEROP );
320 : 0 : rTargetSurface.SetRasterOp( ROP_XOR );
321 : : rTargetSurface.DrawBitmap( aOutPos,
322 : : aOutputSize,
323 : 0 : maContent->GetBitmap() );
324 : :
325 : 0 : rTargetSurface.SetLineColor();
326 : 0 : rTargetSurface.SetFillColor( COL_BLACK );
327 : 0 : rTargetSurface.SetRasterOp( ROP_0 );
328 : 0 : rTargetSurface.DrawPolyPolygon(PolyPolygon(aClipPoly)); // #i76339#
329 : :
330 : 0 : rTargetSurface.SetRasterOp( ROP_XOR );
331 : : rTargetSurface.DrawBitmap( aOutPos,
332 : : aOutputSize,
333 : 0 : maContent->GetBitmap() );
334 : :
335 : 0 : rTargetSurface.Pop();
336 : :
337 : 0 : bSpriteRedrawn = true;
338 : : }
339 : : else
340 : : #endif
341 : : {
342 : 0 : Region aClipRegion( aClipPoly );
343 : 0 : rTargetSurface.SetClipRegion( aClipRegion );
344 : : }
345 : 0 : }
346 : : }
347 : :
348 : 0 : if( !bSpriteRedrawn )
349 : : {
350 : 0 : if( ::rtl::math::approxEqual(fAlpha, 1.0) )
351 : : {
352 : : // no alpha modulation -> just copy to output
353 : 0 : if( maContent->IsTransparent() )
354 : 0 : rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, *maContent );
355 : : else
356 : 0 : rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() );
357 : : }
358 : : else
359 : : {
360 : : // TODO(P3): Switch to OutputDevice::DrawTransparent()
361 : : // here
362 : :
363 : : // draw semi-transparent
364 : 0 : sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
365 : 0 : AlphaMask aAlpha( maContent->GetSizePixel(),
366 : 0 : &nColor );
367 : :
368 : : // mask out fully transparent areas
369 : 0 : if( maContent->IsTransparent() )
370 : 0 : aAlpha.Replace( maContent->GetMask(), 255 );
371 : :
372 : : // alpha-blend to output
373 : : rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize,
374 : : BitmapEx( maContent->GetBitmap(),
375 : 0 : aAlpha ) );
376 : : }
377 : : }
378 : :
379 : 0 : rTargetSurface.Pop();
380 : :
381 : 0 : if( mbShowSpriteBounds )
382 : : {
383 : : ::PolyPolygon aMarkerPoly(
384 : : ::canvas::tools::getBoundMarksPolyPolygon(
385 : 0 : ::basegfx::B2DRectangle(aOutPos.X(),
386 : 0 : aOutPos.Y(),
387 : 0 : aOutPos.X() + aOutputSize.Width()-1,
388 : 0 : aOutPos.Y() + aOutputSize.Height()-1) ) );
389 : :
390 : : // Paint little red sprite area markers
391 : 0 : rTargetSurface.SetLineColor( COL_RED );
392 : 0 : rTargetSurface.SetFillColor();
393 : :
394 : 0 : for( int i=0; i<aMarkerPoly.Count(); ++i )
395 : : {
396 : 0 : rTargetSurface.DrawPolyLine( aMarkerPoly.GetObject((sal_uInt16)i) );
397 : : }
398 : :
399 : : // paint sprite prio
400 : 0 : Font aVCLFont;
401 : 0 : aVCLFont.SetHeight( std::min(long(20),aOutputSize.Height()) );
402 : 0 : aVCLFont.SetColor( COL_RED );
403 : :
404 : 0 : rTargetSurface.SetTextAlign(ALIGN_TOP);
405 : 0 : rTargetSurface.SetTextColor( COL_RED );
406 : 0 : rTargetSurface.SetFont( aVCLFont );
407 : :
408 : : ::rtl::OUString text( ::rtl::math::doubleToUString( getPriority(),
409 : : rtl_math_StringFormat_F,
410 : 0 : 2,'.',NULL,' ') );
411 : :
412 : 0 : rTargetSurface.DrawText( aOutPos+Point(2,2), text );
413 : : VERBOSE_TRACE( "SpriteHelper::redraw(): sprite %X has prio %f\n",
414 : 0 : this, getPriority() );
415 : : }
416 : 0 : }
417 : : }
418 : : }
419 : :
420 : 0 : ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const
421 : : {
422 : 0 : return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly );
423 : : }
424 : :
425 : 0 : }
426 : :
427 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|