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: impltools.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>
37 #include <rtl/math.hxx>
38 #include <rtl/logfile.hxx>
40 #include <com/sun/star/geometry/RealSize2D.hpp>
41 #include <com/sun/star/geometry/RealPoint2D.hpp>
42 #include <com/sun/star/geometry/RealRectangle2D.hpp>
43 #include <com/sun/star/rendering/RenderState.hpp>
44 #include <com/sun/star/rendering/XCanvas.hpp>
45 #include <com/sun/star/rendering/XBitmap.hpp>
46 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
47 #include <com/sun/star/geometry/RealBezierSegment2D.hpp>
48 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
50 #include <vcl/salbtype.hxx>
51 #include <vcl/bmpacc.hxx>
52 #include <vcl/bitmapex.hxx>
53 #include <vcl/metric.hxx>
54 #include <vcl/canvastools.hxx>
56 #include <basegfx/point/b2dpoint.hxx>
57 #include <basegfx/tuple/b2dtuple.hxx>
58 #include <basegfx/polygon/b2dpolygontools.hxx>
59 #include <basegfx/range/b2drectangle.hxx>
60 #include <basegfx/matrix/b2dhommatrix.hxx>
61 #include <basegfx/tools/canvastools.hxx>
62 #include <basegfx/numeric/ftools.hxx>
64 #include <canvas/canvastools.hxx>
66 #include "impltools.hxx"
67 #include "canvasbitmap.hxx"
72 using namespace ::com::sun::star
;
78 ::BitmapEx
bitmapExFromXBitmap( const uno::Reference
< rendering::XBitmap
>& xBitmap
)
80 // TODO(F3): CanvasCustomSprite should also be tunnelled
81 // through (also implements XIntegerBitmap interface)
82 CanvasBitmap
* pBitmapImpl
= dynamic_cast< CanvasBitmap
* >( xBitmap
.get() );
86 return pBitmapImpl
->getBitmap();
90 SpriteCanvas
* pCanvasImpl
= dynamic_cast< SpriteCanvas
* >( xBitmap
.get() );
91 if( pCanvasImpl
&& pCanvasImpl
->getBackBuffer() )
93 // TODO(F3): mind the plain Canvas impl. Consolidate with CWS canvas05
94 const ::OutputDevice
& rDev( pCanvasImpl
->getBackBuffer()->getOutDev() );
95 const ::Point aEmptyPoint
;
96 return rDev
.GetBitmapEx( aEmptyPoint
,
97 rDev
.GetOutputSizePixel() );
100 // TODO(F2): add support for floating point bitmap formats
101 uno::Reference
< rendering::XIntegerReadOnlyBitmap
> xIntBmp(
102 xBitmap
, uno::UNO_QUERY_THROW
);
104 ::BitmapEx aBmpEx
= ::vcl::unotools::bitmapExFromXBitmap( xIntBmp
);
108 // TODO(F1): extract pixel from XBitmap interface
109 ENSURE_OR_THROW( false,
110 "bitmapExFromXBitmap(): could not extract bitmap" );
116 bool setupFontTransform( ::Point
& o_rPoint
,
118 const rendering::ViewState
& rViewState
,
119 const rendering::RenderState
& rRenderState
,
120 ::OutputDevice
& rOutDev
)
122 ::basegfx::B2DHomMatrix aMatrix
;
124 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
128 ::basegfx::B2DTuple aScale
;
129 ::basegfx::B2DTuple aTranslate
;
130 double nRotate
, nShearX
;
132 aMatrix
.decompose( aScale
, aTranslate
, nRotate
, nShearX
);
134 // #i72417# detecting the 180 degree rotation case manually here.
135 if( aScale
.getX() < 0.0 &&
136 aScale
.getY() < 0.0 &&
137 basegfx::fTools::equalZero(nRotate
) )
143 // query font metric _before_ tampering with width and height
144 if( !::rtl::math::approxEqual(aScale
.getX(), aScale
.getY()) )
146 // retrieve true font width
147 const sal_Int32
nFontWidth( rOutDev
.GetFontMetric( io_rVCLFont
).GetWidth() );
149 const sal_Int32
nScaledFontWidth( ::basegfx::fround(nFontWidth
* aScale
.getX()) );
151 if( !nScaledFontWidth
)
153 // scale is smaller than one pixel - disable text
158 io_rVCLFont
.SetWidth( nScaledFontWidth
);
161 if( !::rtl::math::approxEqual(aScale
.getY(), 1.0) )
163 const sal_Int32
nFontHeight( io_rVCLFont
.GetHeight() );
164 io_rVCLFont
.SetHeight( ::basegfx::fround(nFontHeight
* aScale
.getY()) );
167 io_rVCLFont
.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate
, 2*M_PI
)*(1800.0/M_PI
)) ) );
169 // TODO(F2): Missing functionality in VCL: shearing
170 o_rPoint
.X() = ::basegfx::fround(aTranslate
.getX());
171 o_rPoint
.Y() = ::basegfx::fround(aTranslate
.getY());
176 bool isRectangle( const PolyPolygon
& rPolyPoly
)
178 // exclude some cheap cases first
179 if( rPolyPoly
.Count() != 1 )
182 const ::Polygon
& rPoly( rPolyPoly
[0] );
184 USHORT
nCount( rPoly
.GetSize() );
188 // delegate to basegfx
189 return ::basegfx::tools::isRectangle( rPoly
.getB2DPolygon() );
193 // VCL-Canvas related
194 //---------------------------------------------------------------------
196 ::Point
mapRealPoint2D( const geometry::RealPoint2D
& rPoint
,
197 const rendering::ViewState
& rViewState
,
198 const rendering::RenderState
& rRenderState
)
200 ::basegfx::B2DPoint
aPoint( ::basegfx::unotools::b2DPointFromRealPoint2D(rPoint
) );
202 ::basegfx::B2DHomMatrix aMatrix
;
203 aPoint
*= ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
207 return ::vcl::unotools::pointFromB2DPoint( aPoint
);
210 ::PolyPolygon
mapPolyPolygon( const ::basegfx::B2DPolyPolygon
& rPoly
,
211 const rendering::ViewState
& rViewState
,
212 const rendering::RenderState
& rRenderState
)
214 ::basegfx::B2DHomMatrix aMatrix
;
215 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
,
219 ::basegfx::B2DPolyPolygon
aTemp( rPoly
);
221 aTemp
.transform( aMatrix
);
223 return ::PolyPolygon( aTemp
);
226 ::BitmapEx
transformBitmap( const BitmapEx
& rBitmap
,
227 const ::basegfx::B2DHomMatrix
& rTransform
,
228 const uno::Sequence
< double >& rDeviceColor
,
229 ModulationMode eModulationMode
)
231 RTL_LOGFILE_CONTEXT( aLog
, "::vclcanvas::tools::transformBitmap()" );
232 RTL_LOGFILE_CONTEXT_TRACE1( aLog
, "::vclcanvas::tools::transformBitmap: 0x%X", &rBitmap
);
234 // calc transformation and size of bitmap to be
235 // generated. Note, that the translational components are
236 // deleted from the transformation; this can be handled by
237 // an offset when painting the bitmap
238 const Size
aBmpSize( rBitmap
.GetSizePixel() );
239 ::basegfx::B2DRectangle aDestRect
;
241 bool bCopyBack( false );
243 // calc effective transformation for bitmap
244 const ::basegfx::B2DRectangle
aSrcRect( 0, 0,
247 ::canvas::tools::calcTransformedRectBounds( aDestRect
,
251 // re-center bitmap, such that it's left, top border is
252 // aligned with (0,0). The method takes the given
253 // rectangle, and calculates a transformation that maps
254 // this rectangle unscaled to the origin.
255 ::basegfx::B2DHomMatrix aLocalTransform
;
256 ::canvas::tools::calcRectToOriginTransform( aLocalTransform
,
260 const bool bModulateColors( eModulationMode
== MODULATE_WITH_DEVICECOLOR
&&
261 rDeviceColor
.getLength() > 2 );
262 const double nRedModulation( bModulateColors
? rDeviceColor
[0] : 1.0 );
263 const double nGreenModulation( bModulateColors
? rDeviceColor
[1] : 1.0 );
264 const double nBlueModulation( bModulateColors
? rDeviceColor
[2] : 1.0 );
265 const double nAlphaModulation( bModulateColors
&& rDeviceColor
.getLength() > 3 ?
266 rDeviceColor
[3] : 1.0 );
268 Bitmap
aSrcBitmap( rBitmap
.GetBitmap() );
271 // differentiate mask and alpha channel (on-off
272 // vs. multi-level transparency)
273 if( rBitmap
.IsTransparent() )
275 if( rBitmap
.IsAlpha() )
276 aSrcAlpha
= rBitmap
.GetAlpha().GetBitmap();
278 aSrcAlpha
= rBitmap
.GetMask();
281 ScopedBitmapReadAccess
pReadAccess( aSrcBitmap
.AcquireReadAccess(),
283 ScopedBitmapReadAccess
pAlphaReadAccess( rBitmap
.IsTransparent() ?
284 aSrcAlpha
.AcquireReadAccess() :
285 (BitmapReadAccess
*)NULL
,
288 if( pReadAccess
.get() == NULL
||
289 (pAlphaReadAccess
.get() == NULL
&& rBitmap
.IsTransparent()) )
291 // TODO(E2): Error handling!
292 ENSURE_OR_THROW( false,
293 "transformBitmap(): could not access source bitmap" );
296 // mapping table, to translate pAlphaReadAccess' pixel
297 // values into destination alpha values (needed e.g. for
298 // paletted 1-bit masks).
299 sal_uInt8 aAlphaMap
[256];
301 if( rBitmap
.IsTransparent() )
303 if( rBitmap
.IsAlpha() )
305 // source already has alpha channel - 1:1 mapping,
306 // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
307 ::std::iota( aAlphaMap
, &aAlphaMap
[256], 0 );
311 // mask transparency - determine used palette colors
312 const BitmapColor
& rCol0( pAlphaReadAccess
->GetPaletteColor( 0 ) );
313 const BitmapColor
& rCol1( pAlphaReadAccess
->GetPaletteColor( 1 ) );
315 // shortcut for true luminance calculation
316 // (assumes that palette is grey-level)
317 aAlphaMap
[0] = rCol0
.GetRed();
318 aAlphaMap
[1] = rCol1
.GetRed();
321 // else: mapping table is not used
323 const Size
aDestBmpSize( ::basegfx::fround( aDestRect
.getWidth() ),
324 ::basegfx::fround( aDestRect
.getHeight() ) );
326 if( aDestBmpSize
.Width() == 0 || aDestBmpSize
.Height() == 0 )
329 Bitmap
aDstBitmap( aDestBmpSize
, aSrcBitmap
.GetBitCount(), &pReadAccess
->GetPalette() );
330 Bitmap
aDstAlpha( AlphaMask( aDestBmpSize
).GetBitmap() );
333 // just to be on the safe side: let the
334 // ScopedAccessors get destructed before
335 // copy-constructing the resulting bitmap. This will
336 // rule out the possibility that cached accessor data
337 // is not yet written back.
338 ScopedBitmapWriteAccess
pWriteAccess( aDstBitmap
.AcquireWriteAccess(),
340 ScopedBitmapWriteAccess
pAlphaWriteAccess( aDstAlpha
.AcquireWriteAccess(),
344 if( pWriteAccess
.get() != NULL
&&
345 pAlphaWriteAccess
.get() != NULL
&&
346 rTransform
.isInvertible() )
348 // we're doing inverse mapping here, i.e. mapping
349 // points from the destination bitmap back to the
351 ::basegfx::B2DHomMatrix
aTransform( aLocalTransform
);
354 // for the time being, always read as ARGB
355 for( int y
=0; y
<aDestBmpSize
.Height(); ++y
)
357 if( bModulateColors
)
359 // TODO(P2): Have different branches for
360 // alpha-only modulation (color
361 // modulations eq. 1.0)
363 // modulate all color channels with given
366 // differentiate mask and alpha channel (on-off
367 // vs. multi-level transparency)
368 if( rBitmap
.IsTransparent() )
370 // Handling alpha and mask just the same...
371 for( int x
=0; x
<aDestBmpSize
.Width(); ++x
)
373 ::basegfx::B2DPoint
aPoint(x
,y
);
374 aPoint
*= aTransform
;
376 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
377 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
378 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
379 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
381 pAlphaWriteAccess
->SetPixel( y
, x
, BitmapColor(255) );
385 // modulate alpha with
386 // nAlphaModulation. This is a
387 // little bit verbose, formula
388 // is 255 - (255-pixAlpha)*nAlphaModulation
389 // (invert 'alpha' pixel value,
390 // to get the standard alpha
391 // channel behaviour)
392 pAlphaWriteAccess
->SetPixel( y
, x
,
398 - aAlphaMap
[ pAlphaReadAccess
->GetPixel(
400 nSrcX
).GetIndex() ] ) + .5 ) ) );
402 BitmapColor
aColor( pReadAccess
->GetPixel( nSrcY
,
408 aColor
.GetRed() + .5 ));
412 aColor
.GetGreen() + .5 ));
416 aColor
.GetBlue() + .5 ));
418 pWriteAccess
->SetPixel( y
, x
,
425 for( int x
=0; x
<aDestBmpSize
.Width(); ++x
)
427 ::basegfx::B2DPoint
aPoint(x
,y
);
428 aPoint
*= aTransform
;
430 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
431 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
432 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
433 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
435 pAlphaWriteAccess
->SetPixel( y
, x
, BitmapColor(255) );
439 // modulate alpha with
440 // nAlphaModulation. This is a
441 // little bit verbose, formula
442 // is 255 - 255*nAlphaModulation
443 // (invert 'alpha' pixel value,
444 // to get the standard alpha
445 // channel behaviour)
446 pAlphaWriteAccess
->SetPixel( y
, x
,
450 nAlphaModulation
*255.0
453 BitmapColor
aColor( pReadAccess
->GetPixel( nSrcY
,
459 aColor
.GetRed() + .5 ));
463 aColor
.GetGreen() + .5 ));
467 aColor
.GetBlue() + .5 ));
469 pWriteAccess
->SetPixel( y
, x
,
477 // differentiate mask and alpha channel (on-off
478 // vs. multi-level transparency)
479 if( rBitmap
.IsTransparent() )
481 // Handling alpha and mask just the same...
482 for( int x
=0; x
<aDestBmpSize
.Width(); ++x
)
484 ::basegfx::B2DPoint
aPoint(x
,y
);
485 aPoint
*= aTransform
;
487 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
488 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
489 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
490 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
492 pAlphaWriteAccess
->SetPixel( y
, x
, BitmapColor(255) );
496 pAlphaWriteAccess
->SetPixel( y
, x
,
498 pAlphaReadAccess
->GetPixel( nSrcY
,
501 pWriteAccess
->SetPixel( y
, x
, pReadAccess
->GetPixel( nSrcY
,
508 for( int x
=0; x
<aDestBmpSize
.Width(); ++x
)
510 ::basegfx::B2DPoint
aPoint(x
,y
);
511 aPoint
*= aTransform
;
513 const int nSrcX( ::basegfx::fround( aPoint
.getX() ) );
514 const int nSrcY( ::basegfx::fround( aPoint
.getY() ) );
515 if( nSrcX
< 0 || nSrcX
>= aBmpSize
.Width() ||
516 nSrcY
< 0 || nSrcY
>= aBmpSize
.Height() )
518 pAlphaWriteAccess
->SetPixel( y
, x
, BitmapColor(255) );
522 pAlphaWriteAccess
->SetPixel( y
, x
, BitmapColor(0) );
523 pWriteAccess
->SetPixel( y
, x
, pReadAccess
->GetPixel( nSrcY
,
535 // TODO(E2): Error handling!
536 ENSURE_OR_THROW( false,
537 "transformBitmap(): could not access bitmap" );
542 return BitmapEx( aDstBitmap
, AlphaMask( aDstAlpha
) );