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