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