xref: /trunk/main/canvas/source/vcl/impltools.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 
34 #include <rtl/math.hxx>
35 #include <rtl/logfile.hxx>
36 
37 #include <com/sun/star/geometry/RealSize2D.hpp>
38 #include <com/sun/star/geometry/RealPoint2D.hpp>
39 #include <com/sun/star/geometry/RealRectangle2D.hpp>
40 #include <com/sun/star/rendering/RenderState.hpp>
41 #include <com/sun/star/rendering/XCanvas.hpp>
42 #include <com/sun/star/rendering/XBitmap.hpp>
43 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
44 #include <com/sun/star/geometry/RealBezierSegment2D.hpp>
45 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
46 
47 #include <vcl/salbtype.hxx>
48 #include <vcl/bmpacc.hxx>
49 #include <vcl/bitmapex.hxx>
50 #include <vcl/metric.hxx>
51 #include <vcl/canvastools.hxx>
52 
53 #include <basegfx/point/b2dpoint.hxx>
54 #include <basegfx/tuple/b2dtuple.hxx>
55 #include <basegfx/polygon/b2dpolygontools.hxx>
56 #include <basegfx/range/b2drectangle.hxx>
57 #include <basegfx/matrix/b2dhommatrix.hxx>
58 #include <basegfx/tools/canvastools.hxx>
59 #include <basegfx/numeric/ftools.hxx>
60 
61 #include <canvas/canvastools.hxx>
62 
63 #include "impltools.hxx"
64 #include "canvasbitmap.hxx"
65 
66 #include <numeric>
67 
68 
69 using namespace ::com::sun::star;
70 
71 namespace vclcanvas
72 {
73     namespace tools
74     {
75         ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
76         {
77             // TODO(F3): CanvasCustomSprite should also be tunnelled
78             // through (also implements XIntegerBitmap interface)
79             CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
80 
81             if( pBitmapImpl )
82             {
83                 return pBitmapImpl->getBitmap();
84             }
85             else
86             {
87                 SpriteCanvas* pCanvasImpl = dynamic_cast< SpriteCanvas* >( xBitmap.get() );
88                 if( pCanvasImpl && pCanvasImpl->getBackBuffer() )
89                 {
90                     // TODO(F3): mind the plain Canvas impl. Consolidate with CWS canvas05
91                     const ::OutputDevice& rDev( pCanvasImpl->getBackBuffer()->getOutDev() );
92                     const ::Point aEmptyPoint;
93                     return rDev.GetBitmapEx( aEmptyPoint,
94                                              rDev.GetOutputSizePixel() );
95                 }
96 
97                 // TODO(F2): add support for floating point bitmap formats
98                 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
99                     xBitmap, uno::UNO_QUERY_THROW );
100 
101                 ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap( xIntBmp );
102                 if( !!aBmpEx )
103                     return aBmpEx;
104 
105                 // TODO(F1): extract pixel from XBitmap interface
106                 ENSURE_OR_THROW( false,
107                                   "bitmapExFromXBitmap(): could not extract bitmap" );
108             }
109 
110             return ::BitmapEx();
111         }
112 
113         bool setupFontTransform( ::Point&						o_rPoint,
114                                  ::Font& 						io_rVCLFont,
115                                  const rendering::ViewState& 	rViewState,
116                                  const rendering::RenderState& 	rRenderState,
117                                  ::OutputDevice&				rOutDev )
118         {
119             ::basegfx::B2DHomMatrix aMatrix;
120 
121             ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
122                                                          rViewState,
123                                                          rRenderState);
124 
125             ::basegfx::B2DTuple aScale;
126             ::basegfx::B2DTuple aTranslate;
127             double nRotate, nShearX;
128 
129             aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
130 
131             // #i72417# detecting the 180 degree rotation case manually here.
132             if( aScale.getX() < 0.0 &&
133                 aScale.getY() < 0.0 &&
134                 basegfx::fTools::equalZero(nRotate) )
135             {
136                 aScale *= -1.0;
137                 nRotate += M_PI;
138             }
139 
140             // query font metric _before_ tampering with width and height
141             if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
142             {
143                 // retrieve true font width
144                 const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetWidth() );
145 
146                 const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
147 
148                 if( !nScaledFontWidth )
149                 {
150                     // scale is smaller than one pixel - disable text
151                     // output altogether
152                     return false;
153                 }
154 
155                 io_rVCLFont.SetWidth( nScaledFontWidth );
156             }
157 
158             if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
159             {
160                 const sal_Int32 nFontHeight( io_rVCLFont.GetHeight() );
161                 io_rVCLFont.SetHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
162             }
163 
164             io_rVCLFont.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate, 2*M_PI)*(1800.0/M_PI)) ) );
165 
166             // TODO(F2): Missing functionality in VCL: shearing
167             o_rPoint.X() = ::basegfx::fround(aTranslate.getX());
168             o_rPoint.Y() = ::basegfx::fround(aTranslate.getY());
169 
170             return true;
171         }
172 
173         bool isRectangle( const PolyPolygon& rPolyPoly )
174         {
175             // exclude some cheap cases first
176             if( rPolyPoly.Count() != 1 )
177                 return false;
178 
179             const ::Polygon& rPoly( rPolyPoly[0] );
180 
181             sal_uInt16 nCount( rPoly.GetSize() );
182             if( nCount < 4 )
183                 return false;
184 
185             // delegate to basegfx
186             return ::basegfx::tools::isRectangle( rPoly.getB2DPolygon() );
187         }
188 
189 
190         // VCL-Canvas related
191         //---------------------------------------------------------------------
192 
193         ::Point mapRealPoint2D( const geometry::RealPoint2D& 	rPoint,
194                                 const rendering::ViewState& 	rViewState,
195                                 const rendering::RenderState&	rRenderState )
196         {
197             ::basegfx::B2DPoint aPoint( ::basegfx::unotools::b2DPointFromRealPoint2D(rPoint) );
198 
199             ::basegfx::B2DHomMatrix aMatrix;
200             aPoint *= ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
201                                                                    rViewState,
202                                                                    rRenderState);
203 
204             return ::vcl::unotools::pointFromB2DPoint( aPoint );
205         }
206 
207         ::PolyPolygon mapPolyPolygon( const ::basegfx::B2DPolyPolygon& 	rPoly,
208                                       const rendering::ViewState& 		rViewState,
209                                       const rendering::RenderState&		rRenderState )
210         {
211             ::basegfx::B2DHomMatrix aMatrix;
212             ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
213                                                          rViewState,
214                                                          rRenderState);
215 
216             ::basegfx::B2DPolyPolygon aTemp( rPoly );
217 
218             aTemp.transform( aMatrix );
219 
220             return ::PolyPolygon( aTemp );
221         }
222 
223         ::BitmapEx transformBitmap( const BitmapEx& 				rBitmap,
224                                     const ::basegfx::B2DHomMatrix& 	rTransform,
225                                     const uno::Sequence< double >&	rDeviceColor,
226                                     ModulationMode					eModulationMode )
227         {
228             RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::tools::transformBitmap()" );
229             RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::vclcanvas::tools::transformBitmap: 0x%X", &rBitmap );
230 
231             // calc transformation and size of bitmap to be
232             // generated. Note, that the translational components are
233             // deleted from the transformation; this can be handled by
234             // an offset when painting the bitmap
235             const Size 					aBmpSize( rBitmap.GetSizePixel() );
236             ::basegfx::B2DRectangle		aDestRect;
237 
238             bool bCopyBack( false );
239 
240             // calc effective transformation for bitmap
241             const ::basegfx::B2DRectangle aSrcRect( 0, 0,
242                                                     aBmpSize.Width(),
243                                                     aBmpSize.Height() );
244             ::canvas::tools::calcTransformedRectBounds( aDestRect,
245                                                         aSrcRect,
246                                                         rTransform );
247 
248             // re-center bitmap, such that it's left, top border is
249             // aligned with (0,0). The method takes the given
250             // rectangle, and calculates a transformation that maps
251             // this rectangle unscaled to the origin.
252             ::basegfx::B2DHomMatrix aLocalTransform;
253             ::canvas::tools::calcRectToOriginTransform( aLocalTransform,
254                                                         aSrcRect,
255                                                         rTransform );
256 
257             const bool bModulateColors( eModulationMode == MODULATE_WITH_DEVICECOLOR &&
258                                         rDeviceColor.getLength() > 2 );
259             const double nRedModulation( bModulateColors ? rDeviceColor[0] : 1.0 );
260             const double nGreenModulation( bModulateColors ? rDeviceColor[1] : 1.0 );
261             const double nBlueModulation( bModulateColors ? rDeviceColor[2] : 1.0 );
262             const double nAlphaModulation( bModulateColors && rDeviceColor.getLength() > 3 ?
263                                            rDeviceColor[3] : 1.0 );
264 
265             Bitmap aSrcBitmap( rBitmap.GetBitmap() );
266             Bitmap aSrcAlpha;
267 
268             // differentiate mask and alpha channel (on-off
269             // vs. multi-level transparency)
270             if( rBitmap.IsTransparent() )
271             {
272                 if( rBitmap.IsAlpha() )
273                     aSrcAlpha = rBitmap.GetAlpha().GetBitmap();
274                 else
275                     aSrcAlpha = rBitmap.GetMask();
276             }
277 
278             ScopedBitmapReadAccess pReadAccess( aSrcBitmap.AcquireReadAccess(),
279                                                 aSrcBitmap );
280             ScopedBitmapReadAccess pAlphaReadAccess( rBitmap.IsTransparent() ?
281                                                      aSrcAlpha.AcquireReadAccess() :
282                                                      (BitmapReadAccess*)NULL,
283                                                      aSrcAlpha );
284 
285             if( pReadAccess.get() == NULL ||
286                 (pAlphaReadAccess.get() == NULL && rBitmap.IsTransparent()) )
287             {
288                 // TODO(E2): Error handling!
289                 ENSURE_OR_THROW( false,
290                                   "transformBitmap(): could not access source bitmap" );
291             }
292 
293             // mapping table, to translate pAlphaReadAccess' pixel
294             // values into destination alpha values (needed e.g. for
295             // paletted 1-bit masks).
296             sal_uInt8 aAlphaMap[256];
297 
298             if( rBitmap.IsTransparent() )
299             {
300                 if( rBitmap.IsAlpha() )
301                 {
302                     // source already has alpha channel - 1:1 mapping,
303                     // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255.
304                     ::std::iota( aAlphaMap, &aAlphaMap[256], 0 );
305                 }
306                 else
307                 {
308                     // mask transparency - determine used palette colors
309                     const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) );
310                     const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) );
311 
312                     // shortcut for true luminance calculation
313                     // (assumes that palette is grey-level)
314                     aAlphaMap[0] = rCol0.GetRed();
315                     aAlphaMap[1] = rCol1.GetRed();
316                 }
317             }
318             // else: mapping table is not used
319 
320             const Size aDestBmpSize( ::basegfx::fround( aDestRect.getWidth() ),
321                                      ::basegfx::fround( aDestRect.getHeight() ) );
322 
323             if( aDestBmpSize.Width() == 0 || aDestBmpSize.Height() == 0 )
324                 return BitmapEx();
325 
326             Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() );
327             Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
328 
329             {
330                 // just to be on the safe side: let the
331                 // ScopedAccessors get destructed before
332                 // copy-constructing the resulting bitmap. This will
333                 // rule out the possibility that cached accessor data
334                 // is not yet written back.
335                 ScopedBitmapWriteAccess pWriteAccess( aDstBitmap.AcquireWriteAccess(),
336                                                       aDstBitmap );
337                 ScopedBitmapWriteAccess pAlphaWriteAccess( aDstAlpha.AcquireWriteAccess(),
338                                                            aDstAlpha );
339 
340 
341                 if( pWriteAccess.get() != NULL &&
342                     pAlphaWriteAccess.get() != NULL &&
343                     rTransform.isInvertible() )
344                 {
345                     // we're doing inverse mapping here, i.e. mapping
346                     // points from the destination bitmap back to the
347                     // source
348                     ::basegfx::B2DHomMatrix aTransform( aLocalTransform );
349                     aTransform.invert();
350 
351                     // for the time being, always read as ARGB
352                     for( int y=0; y<aDestBmpSize.Height(); ++y )
353                     {
354                         if( bModulateColors )
355                         {
356                             // TODO(P2): Have different branches for
357                             // alpha-only modulation (color
358                             // modulations eq. 1.0)
359 
360                             // modulate all color channels with given
361                             // values
362 
363                             // differentiate mask and alpha channel (on-off
364                             // vs. multi-level transparency)
365                             if( rBitmap.IsTransparent() )
366                             {
367                                 // Handling alpha and mask just the same...
368                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
369                                 {
370                                     ::basegfx::B2DPoint aPoint(x,y);
371                                     aPoint *= aTransform;
372 
373                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
374                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
375                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
376                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
377                                     {
378                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
379                                     }
380                                     else
381                                     {
382                                         // modulate alpha with
383                                         // nAlphaModulation. This is a
384                                         // little bit verbose, formula
385                                         // is 255 - (255-pixAlpha)*nAlphaModulation
386                                         // (invert 'alpha' pixel value,
387                                         // to get the standard alpha
388                                         // channel behaviour)
389                                         pAlphaWriteAccess->SetPixel( y, x,
390                                                                      BitmapColor(
391                                                                          255U -
392                                                                          static_cast<sal_uInt8>(
393                                                                              nAlphaModulation*
394                                                                              (255U
395                                                                               - aAlphaMap[ pAlphaReadAccess->GetPixel(
396                                                                                                nSrcY,
397                                                                                                nSrcX ).GetIndex() ] ) + .5 ) ) );
398 
399                                         BitmapColor aColor( pReadAccess->GetPixel( nSrcY,
400                                                                                    nSrcX ) );
401 
402                                         aColor.SetRed(
403                                             static_cast<sal_uInt8>(
404                                                 nRedModulation *
405                                                 aColor.GetRed() + .5 ));
406                                         aColor.SetGreen(
407                                             static_cast<sal_uInt8>(
408                                                 nGreenModulation *
409                                                 aColor.GetGreen() + .5 ));
410                                         aColor.SetBlue(
411                                             static_cast<sal_uInt8>(
412                                                 nBlueModulation *
413                                                 aColor.GetBlue() + .5 ));
414 
415                                         pWriteAccess->SetPixel( y, x,
416                                                                 aColor );
417                                     }
418                                 }
419                             }
420                             else
421                             {
422                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
423                                 {
424                                     ::basegfx::B2DPoint aPoint(x,y);
425                                     aPoint *= aTransform;
426 
427                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
428                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
429                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
430                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
431                                     {
432                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
433                                     }
434                                     else
435                                     {
436                                         // modulate alpha with
437                                         // nAlphaModulation. This is a
438                                         // little bit verbose, formula
439                                         // is 255 - 255*nAlphaModulation
440                                         // (invert 'alpha' pixel value,
441                                         // to get the standard alpha
442                                         // channel behaviour)
443                                         pAlphaWriteAccess->SetPixel( y, x,
444                                                                      BitmapColor(
445                                                                          255U -
446                                                                          static_cast<sal_uInt8>(
447                                                                              nAlphaModulation*255.0
448                                                                              + .5 ) ) );
449 
450                                         BitmapColor aColor( pReadAccess->GetPixel( nSrcY,
451                                                                                    nSrcX ) );
452 
453                                         aColor.SetRed(
454                                             static_cast<sal_uInt8>(
455                                                 nRedModulation *
456                                                 aColor.GetRed() + .5 ));
457                                         aColor.SetGreen(
458                                             static_cast<sal_uInt8>(
459                                                 nGreenModulation *
460                                                 aColor.GetGreen() + .5 ));
461                                         aColor.SetBlue(
462                                             static_cast<sal_uInt8>(
463                                                 nBlueModulation *
464                                                 aColor.GetBlue() + .5 ));
465 
466                                         pWriteAccess->SetPixel( y, x,
467                                                                 aColor );
468                                     }
469                                 }
470                             }
471                         }
472                         else
473                         {
474                             // differentiate mask and alpha channel (on-off
475                             // vs. multi-level transparency)
476                             if( rBitmap.IsTransparent() )
477                             {
478                                 // Handling alpha and mask just the same...
479                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
480                                 {
481                                     ::basegfx::B2DPoint aPoint(x,y);
482                                     aPoint *= aTransform;
483 
484                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
485                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
486                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
487                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
488                                     {
489                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
490                                     }
491                                     else
492                                     {
493                                         pAlphaWriteAccess->SetPixel( y, x,
494                                                                      aAlphaMap[
495                                                                          pAlphaReadAccess->GetPixel( nSrcY,
496                                                                                                      nSrcX ) ] );
497 
498                                         pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY,
499                                                                                              nSrcX ) );
500                                     }
501                                 }
502                             }
503                             else
504                             {
505                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
506                                 {
507                                     ::basegfx::B2DPoint aPoint(x,y);
508                                     aPoint *= aTransform;
509 
510                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
511                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
512                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
513                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
514                                     {
515                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
516                                     }
517                                     else
518                                     {
519                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(0) );
520                                         pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY,
521                                                                                              nSrcX ) );
522                                     }
523                                 }
524                             }
525                         }
526                     }
527 
528                     bCopyBack = true;
529                 }
530                 else
531                 {
532                     // TODO(E2): Error handling!
533                     ENSURE_OR_THROW( false,
534                                       "transformBitmap(): could not access bitmap" );
535                 }
536             }
537 
538             if( bCopyBack )
539                 return BitmapEx( aDstBitmap, AlphaMask( aDstAlpha ) );
540             else
541                 return BitmapEx();
542         }
543     }
544 }
545