Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / vcl / impltools.cxx
blobcc25ea084bc9e23d04704c263fb53b8531729411
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <canvas/debug.hxx>
22 #include <tools/diagnose_ex.h>
24 #include <rtl/math.hxx>
26 #include <com/sun/star/geometry/RealSize2D.hpp>
27 #include <com/sun/star/geometry/RealPoint2D.hpp>
28 #include <com/sun/star/geometry/RealRectangle2D.hpp>
29 #include <com/sun/star/rendering/RenderState.hpp>
30 #include <com/sun/star/rendering/XCanvas.hpp>
31 #include <com/sun/star/rendering/XBitmap.hpp>
32 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
33 #include <com/sun/star/geometry/RealBezierSegment2D.hpp>
34 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
36 #include <vcl/salbtype.hxx>
37 #include <vcl/bmpacc.hxx>
38 #include <vcl/bitmapex.hxx>
39 #include <vcl/metric.hxx>
40 #include <vcl/canvastools.hxx>
42 #include <basegfx/point/b2dpoint.hxx>
43 #include <basegfx/tuple/b2dtuple.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <basegfx/range/b2drectangle.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/tools/canvastools.hxx>
48 #include <basegfx/numeric/ftools.hxx>
50 #include <canvas/canvastools.hxx>
52 #include "impltools.hxx"
53 #include "canvasbitmap.hxx"
55 #include <numeric>
58 using namespace ::com::sun::star;
60 namespace vclcanvas
62 namespace tools
64 ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
66 // TODO(F3): CanvasCustomSprite should also be tunnelled
67 // through (also implements XIntegerBitmap interface)
68 CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
70 if( pBitmapImpl )
72 return pBitmapImpl->getBitmap();
74 else
76 SpriteCanvas* pCanvasImpl = dynamic_cast< SpriteCanvas* >( xBitmap.get() );
77 if( pCanvasImpl && pCanvasImpl->getBackBuffer() )
79 // TODO(F3): mind the plain Canvas impl. Consolidate with CWS canvas05
80 const ::OutputDevice& rDev( pCanvasImpl->getBackBuffer()->getOutDev() );
81 const ::Point aEmptyPoint;
82 return rDev.GetBitmapEx( aEmptyPoint,
83 rDev.GetOutputSizePixel() );
86 // TODO(F2): add support for floating point bitmap formats
87 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
88 xBitmap, uno::UNO_QUERY_THROW );
90 ::BitmapEx aBmpEx = vcl::unotools::bitmapExFromXBitmap( xIntBmp );
91 if( !!aBmpEx )
92 return aBmpEx;
94 // TODO(F1): extract pixel from XBitmap interface
95 ENSURE_OR_THROW( false,
96 "bitmapExFromXBitmap(): could not extract bitmap" );
99 return ::BitmapEx();
102 bool setupFontTransform( ::Point& o_rPoint,
103 vcl::Font& io_rVCLFont,
104 const rendering::ViewState& rViewState,
105 const rendering::RenderState& rRenderState,
106 ::OutputDevice& rOutDev )
108 ::basegfx::B2DHomMatrix aMatrix;
110 ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
111 rViewState,
112 rRenderState);
114 ::basegfx::B2DTuple aScale;
115 ::basegfx::B2DTuple aTranslate;
116 double nRotate, nShearX;
118 aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
120 // query font metric _before_ tampering with width and height
121 if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
123 // retrieve true font width
124 const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetWidth() );
126 const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
128 if( !nScaledFontWidth )
130 // scale is smaller than one pixel - disable text
131 // output altogether
132 return false;
135 io_rVCLFont.SetWidth( nScaledFontWidth );
138 if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
140 const sal_Int32 nFontHeight( io_rVCLFont.GetHeight() );
141 io_rVCLFont.SetHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
144 io_rVCLFont.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate, 2*M_PI)*(1800.0/M_PI)) ) );
146 // TODO(F2): Missing functionality in VCL: shearing
147 o_rPoint.X() = ::basegfx::fround(aTranslate.getX());
148 o_rPoint.Y() = ::basegfx::fround(aTranslate.getY());
150 return true;
153 bool isRectangle( const ::tools::PolyPolygon& rPolyPoly )
155 // exclude some cheap cases first
156 if( rPolyPoly.Count() != 1 )
157 return false;
159 const ::Polygon& rPoly( rPolyPoly[0] );
161 sal_uInt16 nCount( rPoly.GetSize() );
162 if( nCount < 4 )
163 return false;
165 // delegate to basegfx
166 return ::basegfx::tools::isRectangle( rPoly.getB2DPolygon() );
170 // VCL-Canvas related
173 ::Point mapRealPoint2D( const geometry::RealPoint2D& rPoint,
174 const rendering::ViewState& rViewState,
175 const rendering::RenderState& rRenderState )
177 ::basegfx::B2DPoint aPoint( ::basegfx::unotools::b2DPointFromRealPoint2D(rPoint) );
179 ::basegfx::B2DHomMatrix aMatrix;
180 aPoint *= ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
181 rViewState,
182 rRenderState);
184 return vcl::unotools::pointFromB2DPoint( aPoint );
187 ::tools::PolyPolygon mapPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly,
188 const rendering::ViewState& rViewState,
189 const rendering::RenderState& rRenderState )
191 ::basegfx::B2DHomMatrix aMatrix;
192 ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
193 rViewState,
194 rRenderState);
196 ::basegfx::B2DPolyPolygon aTemp( rPoly );
198 aTemp.transform( aMatrix );
200 return ::tools::PolyPolygon( aTemp );
203 ::BitmapEx transformBitmap( const BitmapEx& rBitmap,
204 const ::basegfx::B2DHomMatrix& rTransform,
205 const uno::Sequence< double >& rDeviceColor,
206 ModulationMode eModulationMode )
208 SAL_INFO( "canvas.vcl", "::vclcanvas::tools::transformBitmap()" );
209 SAL_INFO( "canvas.vcl", "::vclcanvas::tools::transformBitmap: 0x" << std::hex << &rBitmap );
211 // calc transformation and size of bitmap to be
212 // generated. Note, that the translational components are
213 // deleted from the transformation; this can be handled by
214 // an offset when painting the bitmap
215 const Size aBmpSize( rBitmap.GetSizePixel() );
216 ::basegfx::B2DRectangle aDestRect;
218 bool bCopyBack( false );
220 // calc effective transformation for bitmap
221 const ::basegfx::B2DRectangle aSrcRect( 0, 0,
222 aBmpSize.Width(),
223 aBmpSize.Height() );
224 ::canvas::tools::calcTransformedRectBounds( aDestRect,
225 aSrcRect,
226 rTransform );
228 // re-center bitmap, such that it's left, top border is
229 // aligned with (0,0). The method takes the given
230 // rectangle, and calculates a transformation that maps
231 // this rectangle unscaled to the origin.
232 ::basegfx::B2DHomMatrix aLocalTransform;
233 ::canvas::tools::calcRectToOriginTransform( aLocalTransform,
234 aSrcRect,
235 rTransform );
237 const bool bModulateColors( eModulationMode == MODULATE_WITH_DEVICECOLOR &&
238 rDeviceColor.getLength() > 2 );
239 const double nRedModulation( bModulateColors ? rDeviceColor[0] : 1.0 );
240 const double nGreenModulation( bModulateColors ? rDeviceColor[1] : 1.0 );
241 const double nBlueModulation( bModulateColors ? rDeviceColor[2] : 1.0 );
242 const double nAlphaModulation( bModulateColors && rDeviceColor.getLength() > 3 ?
243 rDeviceColor[3] : 1.0 );
245 Bitmap aSrcBitmap( rBitmap.GetBitmap() );
246 Bitmap aSrcAlpha;
248 // differentiate mask and alpha channel (on-off
249 // vs. multi-level transparency)
250 if( rBitmap.IsTransparent() )
252 if( rBitmap.IsAlpha() )
253 aSrcAlpha = rBitmap.GetAlpha().GetBitmap();
254 else
255 aSrcAlpha = rBitmap.GetMask();
258 Bitmap::ScopedReadAccess pReadAccess( aSrcBitmap );
259 Bitmap::ScopedReadAccess pAlphaReadAccess( rBitmap.IsTransparent() ?
260 aSrcAlpha.AcquireReadAccess() :
261 (BitmapReadAccess*)NULL,
262 aSrcAlpha );
264 if( pReadAccess.get() == NULL ||
265 (pAlphaReadAccess.get() == NULL && rBitmap.IsTransparent()) )
267 // TODO(E2): Error handling!
268 ENSURE_OR_THROW( false,
269 "transformBitmap(): could not access source bitmap" );
272 // mapping table, to translate pAlphaReadAccess' pixel
273 // values into destination alpha values (needed e.g. for
274 // paletted 1-bit masks).
275 sal_uInt8 aAlphaMap[256];
277 if( rBitmap.IsTransparent() )
279 if( rBitmap.IsAlpha() )
281 // source already has alpha channel - 1:1 mapping,
282 // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
283 sal_uInt8 val=0;
284 sal_uInt8* pCur=aAlphaMap;
285 sal_uInt8* const pEnd=&aAlphaMap[256];
286 while(pCur != pEnd)
287 *pCur++ = val++;
289 else
291 // mask transparency - determine used palette colors
292 const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) );
293 const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) );
295 // shortcut for true luminance calculation
296 // (assumes that palette is grey-level)
297 aAlphaMap[0] = rCol0.GetRed();
298 aAlphaMap[1] = rCol1.GetRed();
301 // else: mapping table is not used
303 const Size aDestBmpSize( ::basegfx::fround( aDestRect.getWidth() ),
304 ::basegfx::fround( aDestRect.getHeight() ) );
306 if( aDestBmpSize.Width() == 0 || aDestBmpSize.Height() == 0 )
307 return BitmapEx();
309 Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() );
310 Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
313 // just to be on the safe side: let the
314 // ScopedAccessors get destructed before
315 // copy-constructing the resulting bitmap. This will
316 // rule out the possibility that cached accessor data
317 // is not yet written back.
318 Bitmap::ScopedWriteAccess pWriteAccess( aDstBitmap );
319 Bitmap::ScopedWriteAccess pAlphaWriteAccess( aDstAlpha );
322 if( pWriteAccess.get() != NULL &&
323 pAlphaWriteAccess.get() != NULL &&
324 rTransform.isInvertible() )
326 // we're doing inverse mapping here, i.e. mapping
327 // points from the destination bitmap back to the
328 // source
329 ::basegfx::B2DHomMatrix aTransform( aLocalTransform );
330 aTransform.invert();
332 // for the time being, always read as ARGB
333 for( int y=0; y<aDestBmpSize.Height(); ++y )
335 if( bModulateColors )
337 // TODO(P2): Have different branches for
338 // alpha-only modulation (color
339 // modulations eq. 1.0)
341 // modulate all color channels with given
342 // values
344 // differentiate mask and alpha channel (on-off
345 // vs. multi-level transparency)
346 if( rBitmap.IsTransparent() )
348 // Handling alpha and mask just the same...
349 for( int x=0; x<aDestBmpSize.Width(); ++x )
351 ::basegfx::B2DPoint aPoint(x,y);
352 aPoint *= aTransform;
354 const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
355 const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
356 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
357 nSrcY < 0 || nSrcY >= aBmpSize.Height() )
359 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
361 else
363 // modulate alpha with
364 // nAlphaModulation. This is a
365 // little bit verbose, formula
366 // is 255 - (255-pixAlpha)*nAlphaModulation
367 // (invert 'alpha' pixel value,
368 // to get the standard alpha
369 // channel behaviour)
370 const sal_uInt8 cMappedAlphaIdx = aAlphaMap[ pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX ) ];
371 const sal_uInt8 cModulatedAlphaIdx = 255U - static_cast<sal_uInt8>( nAlphaModulation* (255U - cMappedAlphaIdx) + .5 );
372 pAlphaWriteAccess->SetPixelIndex( y, x, cModulatedAlphaIdx );
373 BitmapColor aColor( pReadAccess->GetPixel( nSrcY, nSrcX ) );
375 aColor.SetRed(
376 static_cast<sal_uInt8>(
377 nRedModulation *
378 aColor.GetRed() + .5 ));
379 aColor.SetGreen(
380 static_cast<sal_uInt8>(
381 nGreenModulation *
382 aColor.GetGreen() + .5 ));
383 aColor.SetBlue(
384 static_cast<sal_uInt8>(
385 nBlueModulation *
386 aColor.GetBlue() + .5 ));
388 pWriteAccess->SetPixel( y, x,
389 aColor );
393 else
395 for( int x=0; x<aDestBmpSize.Width(); ++x )
397 ::basegfx::B2DPoint aPoint(x,y);
398 aPoint *= aTransform;
400 const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
401 const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
402 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
403 nSrcY < 0 || nSrcY >= aBmpSize.Height() )
405 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
407 else
409 // modulate alpha with
410 // nAlphaModulation. This is a
411 // little bit verbose, formula
412 // is 255 - 255*nAlphaModulation
413 // (invert 'alpha' pixel value,
414 // to get the standard alpha
415 // channel behaviour)
416 pAlphaWriteAccess->SetPixel( y, x,
417 BitmapColor(
418 255U -
419 static_cast<sal_uInt8>(
420 nAlphaModulation*255.0
421 + .5 ) ) );
423 BitmapColor aColor( pReadAccess->GetPixel( nSrcY,
424 nSrcX ) );
426 aColor.SetRed(
427 static_cast<sal_uInt8>(
428 nRedModulation *
429 aColor.GetRed() + .5 ));
430 aColor.SetGreen(
431 static_cast<sal_uInt8>(
432 nGreenModulation *
433 aColor.GetGreen() + .5 ));
434 aColor.SetBlue(
435 static_cast<sal_uInt8>(
436 nBlueModulation *
437 aColor.GetBlue() + .5 ));
439 pWriteAccess->SetPixel( y, x,
440 aColor );
445 else
447 // differentiate mask and alpha channel (on-off
448 // vs. multi-level transparency)
449 if( rBitmap.IsTransparent() )
451 // Handling alpha and mask just the same...
452 for( int x=0; x<aDestBmpSize.Width(); ++x )
454 ::basegfx::B2DPoint aPoint(x,y);
455 aPoint *= aTransform;
457 const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
458 const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
459 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
460 nSrcY < 0 || nSrcY >= aBmpSize.Height() )
462 pAlphaWriteAccess->SetPixelIndex( y, x, 255 );
464 else
466 const sal_uInt8 cAlphaIdx = pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX );
467 pAlphaWriteAccess->SetPixelIndex( y, x, aAlphaMap[ cAlphaIdx ] );
468 pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY, nSrcX ) );
472 else
474 for( int x=0; x<aDestBmpSize.Width(); ++x )
476 ::basegfx::B2DPoint aPoint(x,y);
477 aPoint *= aTransform;
479 const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
480 const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
481 if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
482 nSrcY < 0 || nSrcY >= aBmpSize.Height() )
484 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
486 else
488 pAlphaWriteAccess->SetPixel( y, x, BitmapColor(0) );
489 pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY,
490 nSrcX ) );
497 bCopyBack = true;
499 else
501 // TODO(E2): Error handling!
502 ENSURE_OR_THROW( false,
503 "transformBitmap(): could not access bitmap" );
507 if( bCopyBack )
508 return BitmapEx( aDstBitmap, AlphaMask( aDstAlpha ) );
509 else
510 return BitmapEx();
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */