xref: /trunk/main/canvas/source/vcl/impltools.cxx (revision 87bc88d3)
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     {
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 
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 
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 
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 
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 
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                     ::std::iota( aAlphaMap, &aAlphaMap[256], 0 );
301                 }
302                 else
303                 {
304                     // mask transparency - determine used palette colors
305                     const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) );
306                     const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) );
307 
308                     // shortcut for true luminance calculation
309                     // (assumes that palette is grey-level)
310                     aAlphaMap[0] = rCol0.GetRed();
311                     aAlphaMap[1] = rCol1.GetRed();
312                 }
313             }
314             // else: mapping table is not used
315 
316             const Size aDestBmpSize( ::basegfx::fround( aDestRect.getWidth() ),
317                                      ::basegfx::fround( aDestRect.getHeight() ) );
318 
319             if( aDestBmpSize.Width() == 0 || aDestBmpSize.Height() == 0 )
320                 return BitmapEx();
321 
322             Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() );
323             Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() );
324 
325             {
326                 // just to be on the safe side: let the
327                 // ScopedAccessors get destructed before
328                 // copy-constructing the resulting bitmap. This will
329                 // rule out the possibility that cached accessor data
330                 // is not yet written back.
331                 ScopedBitmapWriteAccess pWriteAccess( aDstBitmap.AcquireWriteAccess(),
332                                                       aDstBitmap );
333                 ScopedBitmapWriteAccess pAlphaWriteAccess( aDstAlpha.AcquireWriteAccess(),
334                                                            aDstAlpha );
335 
336 
337                 if( pWriteAccess.get() != NULL &&
338                     pAlphaWriteAccess.get() != NULL &&
339                     rTransform.isInvertible() )
340                 {
341                     // we're doing inverse mapping here, i.e. mapping
342                     // points from the destination bitmap back to the
343                     // source
344                     ::basegfx::B2DHomMatrix aTransform( aLocalTransform );
345                     aTransform.invert();
346 
347                     // for the time being, always read as ARGB
348                     for( int y=0; y<aDestBmpSize.Height(); ++y )
349                     {
350                         if( bModulateColors )
351                         {
352                             // TODO(P2): Have different branches for
353                             // alpha-only modulation (color
354                             // modulations eq. 1.0)
355 
356                             // modulate all color channels with given
357                             // values
358 
359                             // differentiate mask and alpha channel (on-off
360                             // vs. multi-level transparency)
361                             if( rBitmap.IsTransparent() )
362                             {
363                                 // Handling alpha and mask just the same...
364                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
365                                 {
366                                     ::basegfx::B2DPoint aPoint(x,y);
367                                     aPoint *= aTransform;
368 
369                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
370                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
371                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
372                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
373                                     {
374                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
375                                     }
376                                     else
377                                     {
378                                         // modulate alpha with
379                                         // nAlphaModulation. This is a
380                                         // little bit verbose, formula
381                                         // is 255 - (255-pixAlpha)*nAlphaModulation
382                                         // (invert 'alpha' pixel value,
383                                         // to get the standard alpha
384                                         // channel behaviour)
385                                         const sal_uInt8 cMappedAlphaIdx = aAlphaMap[ pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX ) ];
386                                         const sal_uInt8 cModulatedAlphaIdx = 255U - static_cast<sal_uInt8>( nAlphaModulation* (255U - cMappedAlphaIdx) + .5 );
387                                         pAlphaWriteAccess->SetPixelIndex( y, x, cModulatedAlphaIdx );
388                                         BitmapColor aColor( pReadAccess->GetPixel( nSrcY, nSrcX ) );
389 
390                                         aColor.SetRed(
391                                             static_cast<sal_uInt8>(
392                                                 nRedModulation *
393                                                 aColor.GetRed() + .5 ));
394                                         aColor.SetGreen(
395                                             static_cast<sal_uInt8>(
396                                                 nGreenModulation *
397                                                 aColor.GetGreen() + .5 ));
398                                         aColor.SetBlue(
399                                             static_cast<sal_uInt8>(
400                                                 nBlueModulation *
401                                                 aColor.GetBlue() + .5 ));
402 
403                                         pWriteAccess->SetPixel( y, x,
404                                                                 aColor );
405                                     }
406                                 }
407                             }
408                             else
409                             {
410                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
411                                 {
412                                     ::basegfx::B2DPoint aPoint(x,y);
413                                     aPoint *= aTransform;
414 
415                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
416                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
417                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
418                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
419                                     {
420                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
421                                     }
422                                     else
423                                     {
424                                         // modulate alpha with
425                                         // nAlphaModulation. This is a
426                                         // little bit verbose, formula
427                                         // is 255 - 255*nAlphaModulation
428                                         // (invert 'alpha' pixel value,
429                                         // to get the standard alpha
430                                         // channel behaviour)
431                                         pAlphaWriteAccess->SetPixel( y, x,
432                                                                      BitmapColor(
433                                                                          255U -
434                                                                          static_cast<sal_uInt8>(
435                                                                              nAlphaModulation*255.0
436                                                                              + .5 ) ) );
437 
438                                         BitmapColor aColor( pReadAccess->GetPixel( nSrcY,
439                                                                                    nSrcX ) );
440 
441                                         aColor.SetRed(
442                                             static_cast<sal_uInt8>(
443                                                 nRedModulation *
444                                                 aColor.GetRed() + .5 ));
445                                         aColor.SetGreen(
446                                             static_cast<sal_uInt8>(
447                                                 nGreenModulation *
448                                                 aColor.GetGreen() + .5 ));
449                                         aColor.SetBlue(
450                                             static_cast<sal_uInt8>(
451                                                 nBlueModulation *
452                                                 aColor.GetBlue() + .5 ));
453 
454                                         pWriteAccess->SetPixel( y, x,
455                                                                 aColor );
456                                     }
457                                 }
458                             }
459                         }
460                         else
461                         {
462                             // differentiate mask and alpha channel (on-off
463                             // vs. multi-level transparency)
464                             if( rBitmap.IsTransparent() )
465                             {
466                                 // Handling alpha and mask just the same...
467                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
468                                 {
469                                     ::basegfx::B2DPoint aPoint(x,y);
470                                     aPoint *= aTransform;
471 
472                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
473                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
474                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
475                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
476                                     {
477                                         pAlphaWriteAccess->SetPixelIndex( y, x, 255 );
478                                     }
479                                     else
480                                     {
481                                         const sal_uInt8 cAlphaIdx = pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX );
482                                         pAlphaWriteAccess->SetPixelIndex( y, x, aAlphaMap[ cAlphaIdx ] );
483                                         pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY, nSrcX ) );
484                                     }
485                                 }
486                             }
487                             else
488                             {
489                                 for( int x=0; x<aDestBmpSize.Width(); ++x )
490                                 {
491                                     ::basegfx::B2DPoint aPoint(x,y);
492                                     aPoint *= aTransform;
493 
494                                     const int nSrcX( ::basegfx::fround( aPoint.getX() ) );
495                                     const int nSrcY( ::basegfx::fround( aPoint.getY() ) );
496                                     if( nSrcX < 0 || nSrcX >= aBmpSize.Width() ||
497                                         nSrcY < 0 || nSrcY >= aBmpSize.Height() )
498                                     {
499                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(255) );
500                                     }
501                                     else
502                                     {
503                                         pAlphaWriteAccess->SetPixel( y, x, BitmapColor(0) );
504                                         pWriteAccess->SetPixel( y, x, pReadAccess->GetPixel( nSrcY,
505                                                                                              nSrcX ) );
506                                     }
507                                 }
508                             }
509                         }
510                     }
511 
512                     bCopyBack = true;
513                 }
514                 else
515                 {
516                     // TODO(E2): Error handling!
517                     ENSURE_OR_THROW( false,
518                                       "transformBitmap(): could not access bitmap" );
519                 }
520             }
521 
522             if( bCopyBack )
523                 return BitmapEx( aDstBitmap, AlphaMask( aDstAlpha ) );
524             else
525                 return BitmapEx();
526         }
527     }
528 }
529