1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: spritehelper.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <canvas/verbosetrace.hxx>
38 #include <rtl/math.hxx>
40 #include <vcl/outdev.hxx>
41 #include <vcl/bitmap.hxx>
42 #include <vcl/alpha.hxx>
43 #include <vcl/bitmapex.hxx>
44 #include <vcl/canvastools.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/point/b2dpoint.hxx>
48 #include <basegfx/tools/canvastools.hxx>
49 #include <basegfx/polygon/b2dpolygon.hxx>
50 #include <basegfx/polygon/b2dpolygontools.hxx>
51 #include <basegfx/polygon/b2dpolypolygontools.hxx>
52 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
53 #include <basegfx/polygon/b2dpolygontriangulator.hxx>
54 #include <basegfx/polygon/b2dpolygonclipper.hxx>
55 #include <basegfx/numeric/ftools.hxx>
57 #include <canvas/canvastools.hxx>
59 #include "spritehelper.hxx"
61 using namespace ::com::sun::star
;
66 SpriteHelper::SpriteHelper() :
70 mbShowSpriteBounds(false)
74 void SpriteHelper::init( const geometry::RealSize2D
& rSpriteSize
,
75 const ::canvas::SpriteSurface::Reference
& rOwningSpriteCanvas
,
76 const BackBufferSharedPtr
& rBackBuffer
,
77 const BackBufferSharedPtr
& rBackBufferMask
,
78 bool bShowSpriteBounds
)
80 ENSURE_OR_THROW( rOwningSpriteCanvas
.get() && rBackBuffer
&& rBackBufferMask
,
81 "SpriteHelper::init(): Invalid sprite canvas or back buffer" );
83 mpBackBuffer
= rBackBuffer
;
84 mpBackBufferMask
= rBackBufferMask
;
85 mbShowSpriteBounds
= bShowSpriteBounds
;
87 init( rSpriteSize
, rOwningSpriteCanvas
);
90 void SpriteHelper::disposing()
93 mpBackBufferMask
.reset();
96 CanvasCustomSpriteHelper::disposing();
99 void SpriteHelper::redraw( OutputDevice
& rTargetSurface
,
100 const ::basegfx::B2DPoint
& rPos
,
101 bool& io_bSurfacesDirty
,
102 bool bBufferedUpdate
) const
104 (void)bBufferedUpdate
; // not used on every platform
109 return; // we're disposed
112 // log output pos in device pixel
113 VERBOSE_TRACE( "SpriteHelper::redraw(): output pos is (%f, %f)",
117 const double fAlpha( getAlpha() );
120 !::basegfx::fTools::equalZero( fAlpha
) )
122 const Point aEmptyPoint
;
123 const ::basegfx::B2DVector
& rOrigOutputSize( getSizePixel() );
125 // might get changed below (e.g. adapted for
126 // transformations). IMPORTANT: both position and size are
127 // rounded to integer values. From now on, only those
128 // rounded values are used, to keep clip and content in
130 ::Size
aOutputSize( ::vcl::unotools::sizeFromB2DSize( rOrigOutputSize
) );
131 ::Point
aOutPos( ::vcl::unotools::pointFromB2DPoint( rPos
) );
134 // TODO(F3): Support for alpha-VDev
136 // Do we have to update our bitmaps (necessary if virdev
137 // was painted to, or transformation changed)?
138 const bool bNeedBitmapUpdate( io_bSurfacesDirty
||
139 hasTransformChanged() ||
140 maContent
->IsEmpty() );
142 // updating content of sprite cache - surface is no
143 // longer dirty in relation to our cache
144 io_bSurfacesDirty
= false;
147 if( bNeedBitmapUpdate
)
149 Bitmap
aBmp( mpBackBuffer
->getOutDev().GetBitmap( aEmptyPoint
,
152 if( isContentFullyOpaque() )
154 // optimized case: content canvas is fully
155 // opaque. Note: since we retrieved aBmp directly
156 // from an OutDev, it's already a 'display bitmap'
158 maContent
= BitmapEx( aBmp
);
162 // sprite content might contain alpha, create
164 Bitmap
aMask( mpBackBufferMask
->getOutDev().GetBitmap( aEmptyPoint
,
167 // bitmasks are much faster than alphamasks on some platforms
168 // so convert to bitmask if useful
170 if( aMask
.GetBitCount() != 1 )
173 "CanvasCustomSprite::redraw(): Mask bitmap is not "
174 "monochrome (performance!)");
179 // Note: since we retrieved aBmp and aMask
180 // directly from an OutDev, it's already a
181 // 'display bitmap' on windows.
182 maContent
= BitmapEx( aBmp
, aMask
);
186 ::basegfx::B2DHomMatrix
aTransform( getTransformation() );
188 // check whether matrix is "easy" to handle - pure
189 // translations or scales are handled by OutputDevice
191 const bool bIdentityTransform( aTransform
.isIdentity() );
193 // make transformation absolute (put sprite to final
194 // output position). Need to happen here, as we also have
195 // to translate the clip polygon
196 aTransform
.translate( aOutPos
.X(),
199 if( !bIdentityTransform
)
201 if( !::basegfx::fTools::equalZero( aTransform
.get(0,1) ) ||
202 !::basegfx::fTools::equalZero( aTransform
.get(1,0) ) )
204 // "complex" transformation, employ affine
207 // modify output position, to account for the fact
208 // that transformBitmap() always normalizes its output
209 // bitmap into the smallest enclosing box.
210 ::basegfx::B2DRectangle aDestRect
;
211 ::canvas::tools::calcTransformedRectBounds( aDestRect
,
212 ::basegfx::B2DRectangle(0,
214 rOrigOutputSize
.getX(),
215 rOrigOutputSize
.getY()),
218 aOutPos
.X() = ::basegfx::fround( aDestRect
.getMinX() );
219 aOutPos
.Y() = ::basegfx::fround( aDestRect
.getMinY() );
221 // TODO(P3): Use optimized bitmap transformation here.
223 // actually re-create the bitmap ONLY if necessary
224 if( bNeedBitmapUpdate
)
225 maContent
= tools::transformBitmap( *maContent
,
227 uno::Sequence
<double>(),
228 tools::MODULATE_NONE
);
230 aOutputSize
= maContent
->GetSizePixel();
234 // relatively 'simplistic' transformation -
235 // retrieve scale and translational offset
236 aOutputSize
.setWidth (
237 ::basegfx::fround( rOrigOutputSize
.getX() * aTransform
.get(0,0) ) );
238 aOutputSize
.setHeight(
239 ::basegfx::fround( rOrigOutputSize
.getY() * aTransform
.get(1,1) ) );
241 aOutPos
.X() = ::basegfx::fround( aTransform
.get(0,2) );
242 aOutPos
.Y() = ::basegfx::fround( aTransform
.get(1,2) );
246 // transformBitmap() might return empty bitmaps, for tiny
250 // when true, fast path for slide transition has
251 // already redrawn the sprite.
252 bool bSpriteRedrawn( false );
254 rTargetSurface
.Push( PUSH_CLIPREGION
);
256 // apply clip (if any)
259 ::basegfx::B2DPolyPolygon
aClipPoly(
260 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
263 if( aClipPoly
.count() )
265 // aTransform already contains the
266 // translational component, moving the clip to
267 // the final sprite output position.
268 aClipPoly
.transform( aTransform
);
270 #if ! defined WNT && ! defined QUARTZ
271 // non-Windows only - bAtLeastOnePolygon is
272 // only used in non-WNT code below
274 // check whether maybe the clip consists
275 // solely out of rectangular polygons. If this
276 // is the case, enforce using the triangle
277 // clip region setup - non-optimized X11
278 // drivers tend to perform abyssmally on
279 // XPolygonRegion, which is used internally,
280 // when filling complex polypolygons.
281 bool bAtLeastOnePolygon( false );
282 const sal_Int32
nPolygons( aClipPoly
.count() );
284 for( sal_Int32 i
=0; i
<nPolygons
; ++i
)
286 if( !::basegfx::tools::isRectangle(
287 aClipPoly
.getB2DPolygon(i
)) )
289 bAtLeastOnePolygon
= true;
295 if( mbShowSpriteBounds
)
297 // Paint green sprite clip area
298 rTargetSurface
.SetLineColor( Color( 0,255,0 ) );
299 rTargetSurface
.SetFillColor();
301 rTargetSurface
.DrawPolyPolygon(PolyPolygon(aClipPoly
)); // #i76339#
304 #if ! defined WNT && ! defined QUARTZ
305 // as a matter of fact, this fast path only
306 // performs well for X11 - under Windows, the
307 // clip via SetTriangleClipRegion is faster.
308 if( bAtLeastOnePolygon
&&
310 ::rtl::math::approxEqual(fAlpha
, 1.0) &&
311 !maContent
->IsTransparent() )
313 // fast path for slide transitions
314 // (buffered, no alpha, no mask (because
315 // full slide is contained in the sprite))
317 // XOR bitmap onto backbuffer, clear area
318 // that should be _visible_ with black,
319 // XOR bitmap again on top of that -
320 // result: XOR cancels out where no black
321 // has been rendered, and yields the
322 // original bitmap, where black is
324 rTargetSurface
.Push( PUSH_RASTEROP
);
325 rTargetSurface
.SetRasterOp( ROP_XOR
);
326 rTargetSurface
.DrawBitmap( aOutPos
,
328 maContent
->GetBitmap() );
330 rTargetSurface
.SetLineColor();
331 rTargetSurface
.SetFillColor( COL_BLACK
);
332 rTargetSurface
.SetRasterOp( ROP_0
);
333 rTargetSurface
.DrawPolyPolygon(PolyPolygon(aClipPoly
)); // #i76339#
335 rTargetSurface
.SetRasterOp( ROP_XOR
);
336 rTargetSurface
.DrawBitmap( aOutPos
,
338 maContent
->GetBitmap() );
340 rTargetSurface
.Pop();
342 bSpriteRedrawn
= true;
347 // redraw is direcly on the front buffer,
348 // or using alpha blending - cannot use
349 // XOR, thus, employing the still somewhat
350 // speedier triangle clip method
351 ::basegfx::B2DPolygon
aTriangulatedClip(::basegfx::triangulator::triangulate(aClipPoly
));
353 // restrict the clipping area to the visible portion of the output device.
354 Size
aSize(rTargetSurface
.GetOutputSizePixel());
355 ::basegfx::B2DRange
aOutputRect(::basegfx::B2DPoint(0,0),::basegfx::B2DPoint(aSize
.Width(),aSize
.Height()));
356 ::basegfx::B2DPolygon
aClippedClip(::basegfx::tools::clipTriangleListOnRange(aTriangulatedClip
,aOutputRect
));
359 const Polygon
aPoly(aClippedClip
);
360 const PolyPolygon
aPolyPoly(aPoly
);
361 rTargetSurface
.SetTriangleClipRegion(aPolyPoly
);
366 if( !bSpriteRedrawn
)
368 if( ::rtl::math::approxEqual(fAlpha
, 1.0) )
370 // no alpha modulation -> just copy to output
371 if( maContent
->IsTransparent() )
372 rTargetSurface
.DrawBitmapEx( aOutPos
, aOutputSize
, *maContent
);
374 rTargetSurface
.DrawBitmap( aOutPos
, aOutputSize
, maContent
->GetBitmap() );
378 // TODO(P3): Switch to OutputDevice::DrawTransparent()
381 // draw semi-transparent
382 BYTE
nColor( static_cast<UINT8
>( ::basegfx::fround( 255.0*(1.0 - fAlpha
) + .5) ) );
383 AlphaMask
aAlpha( maContent
->GetSizePixel(),
386 // mask out fully transparent areas
387 if( maContent
->IsTransparent() )
388 aAlpha
.Replace( maContent
->GetMask(), 255 );
390 // alpha-blend to output
391 rTargetSurface
.DrawBitmapEx( aOutPos
, aOutputSize
,
392 BitmapEx( maContent
->GetBitmap(),
397 rTargetSurface
.Pop();
399 if( mbShowSpriteBounds
)
401 ::PolyPolygon
aMarkerPoly(
402 ::canvas::tools::getBoundMarksPolyPolygon(
403 ::basegfx::B2DRectangle(aOutPos
.X(),
405 aOutPos
.X() + aOutputSize
.Width()-1,
406 aOutPos
.Y() + aOutputSize
.Height()-1) ) );
408 // Paint little red sprite area markers
409 rTargetSurface
.SetLineColor( COL_RED
);
410 rTargetSurface
.SetFillColor();
412 for( int i
=0; i
<aMarkerPoly
.Count(); ++i
)
414 rTargetSurface
.DrawPolyLine( aMarkerPoly
.GetObject((USHORT
)i
) );
419 aVCLFont
.SetHeight( std::min(long(20),aOutputSize
.Height()) );
420 aVCLFont
.SetColor( COL_RED
);
422 rTargetSurface
.SetTextAlign(ALIGN_TOP
);
423 rTargetSurface
.SetTextColor( COL_RED
);
424 rTargetSurface
.SetFont( aVCLFont
);
426 ::rtl::OUString
text( ::rtl::math::doubleToUString( getPriority(),
427 rtl_math_StringFormat_F
,
430 rTargetSurface
.DrawText( aOutPos
+Point(2,2), text
);
432 #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0
433 OSL_TRACE( "SpriteHelper::redraw(): sprite %X has prio %f\n",
434 this, getPriority() );
441 ::basegfx::B2DPolyPolygon
SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference
< rendering::XPolyPolygon2D
>& xPoly
) const
443 return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly
);