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 <ctype.h> // don't ask. msdev breaks otherwise...
32 #include <basegfx/numeric/ftools.hxx>
33 
34 #include <canvas/debug.hxx>
35 #include <canvas/verbosetrace.hxx>
36 #include <tools/diagnose_ex.h>
37 
38 #include <com/sun/star/lang/XServiceInfo.hpp>
39 #include <com/sun/star/lang/XUnoTunnel.hpp>
40 #include <com/sun/star/geometry/RealPoint2D.hpp>
41 #include <com/sun/star/geometry/IntegerRectangle2D.hpp>
42 
43 #include <basegfx/matrix/b2dhommatrix.hxx>
44 #include <basegfx/range/b2irectangle.hxx>
45 #include <basegfx/range/b2drectangle.hxx>
46 #include <basegfx/polygon/b2dpolygon.hxx>
47 #include <basegfx/polygon/b2dpolypolygon.hxx>
48 #include <basegfx/tools/canvastools.hxx>
49 
50 #include <canvas/canvastools.hxx>
51 #include <canvas/verifyinput.hxx>
52 
53 #include "dx_impltools.hxx"
54 #include "dx_vcltools.hxx"
55 #include "dx_linepolypolygon.hxx"
56 #include "dx_canvasbitmap.hxx"
57 #include "dx_canvasfont.hxx"
58 #include "dx_canvas.hxx"
59 #include "dx_spritecanvas.hxx"
60 
61 #include <boost/scoped_array.hpp>
62 
63 #include <vector>
64 #include <algorithm>
65 
66 
67 using namespace ::com::sun::star;
68 
69 
70 namespace dxcanvas
71 {
72     namespace tools
73     {
74         ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D( const uno::Reference< rendering::XPolyPolygon2D >& xPoly )
75         {
76             LinePolyPolygon* pPolyImpl = dynamic_cast< LinePolyPolygon* >( xPoly.get() );
77 
78             if( pPolyImpl )
79             {
80                 return pPolyImpl->getPolyPolygon();
81             }
82             else
83             {
84                 const sal_Int32 nPolys( xPoly->getNumberOfPolygons() );
85 
86                 // not a known implementation object - try data source
87                 // interfaces
88                 uno::Reference< rendering::XBezierPolyPolygon2D > xBezierPoly(
89                     xPoly,
90                     uno::UNO_QUERY );
91 
92                 if( xBezierPoly.is() )
93                 {
94                     return ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence(
95                         xBezierPoly->getBezierSegments( 0,
96                                                         nPolys,
97                                                         0,
98                                                         -1 ) );
99                 }
100                 else
101                 {
102                     uno::Reference< rendering::XLinePolyPolygon2D > xLinePoly(
103                         xPoly,
104                         uno::UNO_QUERY );
105 
106                     // no implementation class and no data provider
107                     // found - contract violation.
108                     ENSURE_ARG_OR_THROW( xLinePoly.is(),
109                                      "VCLCanvas::polyPolygonFromXPolyPolygon2D(): Invalid input "
110                                      "poly-polygon, cannot retrieve vertex data" );
111 
112                     return ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence(
113                         xLinePoly->getPoints( 0,
114                                               nPolys,
115                                               0,
116                                               -1 ) );
117                 }
118             }
119         }
120 
121         void setupGraphics( Gdiplus::Graphics& rGraphics )
122         {
123             // setup graphics with (somewhat arbitrary) defaults
124             //rGraphics.SetCompositingQuality( Gdiplus::CompositingQualityHighQuality );
125             rGraphics.SetCompositingQuality( Gdiplus::CompositingQualityHighSpeed );
126             //rGraphics.SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear ); // with prefiltering for shrinks
127             rGraphics.SetInterpolationMode( Gdiplus::InterpolationModeBilinear );
128 
129             // #122683# Switched precedence of pixel offset
130             // mode. Seemingly, polygon stroking needs
131             // PixelOffsetModeNone to achieve visually pleasing
132             // results, whereas all other operations (e.g. polygon
133             // fills, bitmaps) look better with PixelOffsetModeHalf.
134             rGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeHalf ); // Pixel center at (0.5, 0.5) etc.
135             //rGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
136 
137             //rGraphics.SetSmoothingMode( Gdiplus::SmoothingModeHighSpeed ); // no line/curve antialiasing
138             //rGraphics.SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
139             rGraphics.SetSmoothingMode( Gdiplus::SmoothingModeAntiAlias );
140             //rGraphics.SetTextRenderingHint( Gdiplus::TextRenderingHintAntiAlias );
141             rGraphics.SetTextRenderingHint( Gdiplus::TextRenderingHintSystemDefault );
142 			rGraphics.SetPageUnit(Gdiplus::UnitPixel);
143         }
144 
145         Gdiplus::Graphics* createGraphicsFromHDC(HDC aHDC)
146         {
147             Gdiplus::Graphics* pRet = new Gdiplus::Graphics(aHDC);
148 			if( pRet )
149                 setupGraphics( *pRet );
150             return pRet;
151         }
152 
153         Gdiplus::Graphics* createGraphicsFromBitmap(const BitmapSharedPtr& rBitmap)
154         {
155             Gdiplus::Graphics* pRet = Gdiplus::Graphics::FromImage(rBitmap.get());
156             if( pRet )
157                 setupGraphics( *pRet );
158             return pRet;
159         }
160 
161         void gdiPlusMatrixFromB2DHomMatrix( Gdiplus::Matrix& rGdiplusMatrix, const ::basegfx::B2DHomMatrix& rMatrix )
162         {
163             rGdiplusMatrix.SetElements( static_cast<Gdiplus::REAL>(rMatrix.get(0,0)),
164                                         static_cast<Gdiplus::REAL>(rMatrix.get(1,0)),
165                                         static_cast<Gdiplus::REAL>(rMatrix.get(0,1)),
166                                         static_cast<Gdiplus::REAL>(rMatrix.get(1,1)),
167                                         static_cast<Gdiplus::REAL>(rMatrix.get(0,2)),
168                                         static_cast<Gdiplus::REAL>(rMatrix.get(1,2)) );
169         }
170 
171         void gdiPlusMatrixFromAffineMatrix2D( Gdiplus::Matrix& 					rGdiplusMatrix,
172                                               const geometry::AffineMatrix2D&	rMatrix )
173         {
174             rGdiplusMatrix.SetElements( static_cast<Gdiplus::REAL>(rMatrix.m00),
175                                         static_cast<Gdiplus::REAL>(rMatrix.m10),
176                                         static_cast<Gdiplus::REAL>(rMatrix.m01),
177                                         static_cast<Gdiplus::REAL>(rMatrix.m11),
178                                         static_cast<Gdiplus::REAL>(rMatrix.m02),
179                                         static_cast<Gdiplus::REAL>(rMatrix.m12) );
180         }
181 
182         namespace
183         {
184             // TODO(P2): Check whether this gets inlined. If not, make functor
185             // out of it
186             inline Gdiplus::PointF implGdiPlusPointFromRealPoint2D( const ::com::sun::star::geometry::RealPoint2D& rPoint )
187             {
188                 return Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.X),
189                                         static_cast<Gdiplus::REAL>(rPoint.Y) );
190             }
191 
192             void graphicsPathFromB2DPolygon( GraphicsPathSharedPtr&             rOutput,
193                                              ::std::vector< Gdiplus::PointF >&  rPoints,
194                                              const ::basegfx::B2DPolygon&       rPoly,
195                                              bool bNoLineJoin)
196             {
197                 const sal_uInt32 nPoints( rPoly.count() );
198 
199                 if( nPoints < 2 )
200                     return;
201 
202                 rOutput->StartFigure();
203 
204                 const bool bClosedPolygon( rPoly.isClosed() );
205 
206                 if( rPoly.areControlPointsUsed() )
207                 {
208                     // control points used -> for now, add all
209                     // segments as curves to GraphicsPath
210 
211                     // If the polygon is closed, we need to add the
212                     // first point, thus, one more (can't simply
213                     // GraphicsPath::CloseFigure() it, since the last
214                     // point cannot have any control points for GDI+)
215                     rPoints.resize( 3*nPoints + bClosedPolygon );
216 
217                     sal_uInt32 nCurrOutput=0;
218                     for( sal_uInt32 nCurrPoint=0; nCurrPoint<nPoints; ++nCurrPoint )
219                     {
220                         const ::basegfx::B2DPoint& rPoint( rPoly.getB2DPoint( nCurrPoint ) );
221                         rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.getX()),
222                                                                   static_cast<Gdiplus::REAL>(rPoint.getY()) );
223 
224                         const ::basegfx::B2DPoint& rControlPointA( rPoly.getNextControlPoint( nCurrPoint ) );
225                         rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rControlPointA.getX()),
226                                                                   static_cast<Gdiplus::REAL>(rControlPointA.getY()) );
227 
228                         const ::basegfx::B2DPoint& rControlPointB( rPoly.getPrevControlPoint( (nCurrPoint + 1) % nPoints) );
229                         rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rControlPointB.getX()),
230                                                                   static_cast<Gdiplus::REAL>(rControlPointB.getY()) );
231                     }
232 
233                     if( bClosedPolygon )
234                     {
235                         // add first point again (to be able to pass
236                         // control points for the last point, see
237                         // above)
238                         const ::basegfx::B2DPoint& rPoint( rPoly.getB2DPoint(0) );
239                         rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.getX()),
240                                                                   static_cast<Gdiplus::REAL>(rPoint.getY()) );
241 
242                         if(bNoLineJoin && nCurrOutput > 7)
243                         {
244                             for(sal_uInt32 a(3); a < nCurrOutput; a+=3)
245                             {
246         				        rOutput->StartFigure();
247                                 rOutput->AddBezier(rPoints[a - 3], rPoints[a - 2], rPoints[a - 1], rPoints[a]);
248                             }
249                         }
250                         else
251                         {
252                             rOutput->AddBeziers( &rPoints[0], nCurrOutput );
253                         }
254                     }
255                     else
256                     {
257                         // GraphicsPath expects 3(n-1)+1 points (i.e. the
258                         // last point must not have any trailing control
259                         // points after it).
260                         // Therefore, simply don't pass the last two
261                         // points here.
262                         if( nCurrOutput > 3 )
263                         {
264                             if(bNoLineJoin && nCurrOutput > 7)
265                             {
266                                 for(sal_uInt32 a(3); a < nCurrOutput; a+=3)
267                                 {
268         				            rOutput->StartFigure();
269                                     rOutput->AddBezier(rPoints[a - 3], rPoints[a - 2], rPoints[a - 1], rPoints[a]);
270                                 }
271                             }
272                             else
273                             {
274                                 rOutput->AddBeziers( &rPoints[0], nCurrOutput-2 );
275                             }
276                         }
277                     }
278                 }
279                 else
280                 {
281                     // no control points -> no curves, simply add
282                     // straigt lines to GraphicsPath
283                     rPoints.resize( nPoints );
284 
285                     for( sal_uInt32 nCurrPoint=0; nCurrPoint<nPoints; ++nCurrPoint )
286                     {
287                         const ::basegfx::B2DPoint& rPoint( rPoly.getB2DPoint( nCurrPoint ) );
288                         rPoints[nCurrPoint] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.getX()),
289                                                                static_cast<Gdiplus::REAL>(rPoint.getY()) );
290                     }
291 
292                     if(bNoLineJoin && nPoints > 2)
293                     {
294                         for(sal_uInt32 a(1); a < nPoints; a++)
295                         {
296         				    rOutput->StartFigure();
297                             rOutput->AddLine(rPoints[a - 1], rPoints[a]);
298                         }
299 
300                         if(bClosedPolygon)
301                         {
302         				    rOutput->StartFigure();
303                             rOutput->AddLine(rPoints[nPoints - 1], rPoints[0]);
304                         }
305                     }
306                     else
307                     {
308                         rOutput->AddLines( &rPoints[0], nPoints );
309                     }
310                 }
311 
312                 if( bClosedPolygon && !bNoLineJoin )
313                     rOutput->CloseFigure();
314             }
315         }
316 
317         Gdiplus::PointF gdiPlusPointFromRealPoint2D( const ::com::sun::star::geometry::RealPoint2D& rPoint )
318         {
319             return implGdiPlusPointFromRealPoint2D( rPoint );
320         }
321 
322         Gdiplus::Rect gdiPlusRectFromIntegerRectangle2D( const geometry::IntegerRectangle2D& rRect )
323         {
324             return Gdiplus::Rect( rRect.X1,
325                                   rRect.Y1,
326                                   rRect.X2 - rRect.X1,
327                                   rRect.Y2 - rRect.Y1 );
328         }
329 
330         Gdiplus::RectF gdiPlusRectFFromRectangle2D( const geometry::RealRectangle2D& rRect )
331         {
332             return Gdiplus::RectF( static_cast<Gdiplus::REAL>(rRect.X1),
333                                    static_cast<Gdiplus::REAL>(rRect.Y1),
334                                    static_cast<Gdiplus::REAL>(rRect.X2 - rRect.X1),
335                                    static_cast<Gdiplus::REAL>(rRect.Y2 - rRect.Y1) );
336         }
337 
338         RECT gdiRectFromB2IRect( const ::basegfx::B2IRange& rRect )
339         {
340             RECT aRect = {rRect.getMinX(),
341                           rRect.getMinY(),
342                           rRect.getMaxX(),
343                           rRect.getMaxY()};
344 
345             return aRect;
346         }
347 
348         geometry::RealPoint2D realPoint2DFromGdiPlusPointF( const Gdiplus::PointF& rPoint )
349         {
350             return geometry::RealPoint2D( rPoint.X, rPoint.Y );
351         }
352 
353         geometry::RealRectangle2D realRectangle2DFromGdiPlusRectF( const Gdiplus::RectF& rRect )
354         {
355             return geometry::RealRectangle2D( rRect.X, rRect.Y,
356                                               rRect.X + rRect.Width,
357                                               rRect.Y + rRect.Height );
358         }
359 
360         ::basegfx::B2DPoint	b2dPointFromGdiPlusPointF( const Gdiplus::PointF& rPoint )
361         {
362             return ::basegfx::B2DPoint( rPoint.X, rPoint.Y );
363         }
364 
365         ::basegfx::B2DRange	b2dRangeFromGdiPlusRectF( const Gdiplus::RectF& rRect )
366         {
367             return ::basegfx::B2DRange( rRect.X, rRect.Y,
368                                         rRect.X + rRect.Width,
369                                         rRect.Y + rRect.Height );
370         }
371 
372         uno::Sequence< double > argbToDoubleSequence( const Gdiplus::ARGB& rColor )
373         {
374             // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
375             uno::Sequence< double > aRet(4);
376 
377             aRet[0] = ((rColor >> 16) & 0xFF) / 255.0; 	// red
378             aRet[1] = ((rColor >> 8) & 0xFF) / 255.0;	// green
379             aRet[2] = (rColor & 0xFF) / 255.0;			// blue
380             aRet[3] = ((rColor >> 24) & 0xFF) / 255.0;	// alpha
381 
382             return aRet;
383         }
384 
385         uno::Sequence< sal_Int8 > argbToIntSequence( const Gdiplus::ARGB& rColor )
386         {
387             // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
388             uno::Sequence< sal_Int8 > aRet(4);
389 
390             aRet[0] = static_cast<sal_Int8>((rColor >> 16) & 0xFF);	// red
391             aRet[1] = static_cast<sal_Int8>((rColor >> 8) & 0xFF);	// green
392             aRet[2] = static_cast<sal_Int8>(rColor & 0xFF);			// blue
393             aRet[3] = static_cast<sal_Int8>((rColor >> 24) & 0xFF);	// alpha
394 
395             return aRet;
396         }
397 
398         Gdiplus::ARGB sequenceToArgb( const uno::Sequence< sal_Int8 >& rColor )
399         {
400             ENSURE_OR_THROW( rColor.getLength() > 2,
401                               "sequenceToArgb: need at least three channels" );
402 
403             // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
404             Gdiplus::ARGB aColor;
405 
406             aColor = (static_cast<sal_uInt8>(rColor[0]) << 16) | (static_cast<sal_uInt8>(rColor[1]) << 8) | static_cast<sal_uInt8>(rColor[2]);
407 
408             if( rColor.getLength() > 3 )
409                 aColor |= static_cast<sal_uInt8>(rColor[3]) << 24;
410 
411             return aColor;
412         }
413 
414         Gdiplus::ARGB sequenceToArgb( const uno::Sequence< double >& rColor )
415         {
416             ENSURE_OR_THROW( rColor.getLength() > 2,
417                               "sequenceToColor: need at least three channels" );
418 
419             // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
420             Gdiplus::ARGB aColor;
421 
422             ::canvas::tools::verifyRange(rColor[0],0.0,1.0);
423             ::canvas::tools::verifyRange(rColor[1],0.0,1.0);
424             ::canvas::tools::verifyRange(rColor[2],0.0,1.0);
425 
426             aColor =
427                 (static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[0] ) ) << 16) |
428                 (static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[1] ) ) << 8) |
429                 static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[2] ) );
430 
431             if( rColor.getLength() > 3 )
432             {
433                 ::canvas::tools::verifyRange(rColor[3],0.0,1.0);
434                 aColor |= static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[3] ) ) << 24;
435             }
436 
437             return aColor;
438         }
439 
440         GraphicsPathSharedPtr graphicsPathFromRealPoint2DSequence( const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points )
441         {
442             GraphicsPathSharedPtr pRes( new Gdiplus::GraphicsPath() );
443             ::std::vector< Gdiplus::PointF > aPoints;
444 
445             sal_Int32 nCurrPoly;
446             for( nCurrPoly=0; nCurrPoly<points.getLength(); ++nCurrPoly )
447             {
448                 const sal_Int32 nCurrSize( points[nCurrPoly].getLength() );
449                 if( nCurrSize )
450                 {
451                     aPoints.resize( nCurrSize );
452 
453                     // TODO(F1): Closed/open polygons
454 
455                     // convert from RealPoint2D array to Gdiplus::PointF array
456                     ::std::transform( const_cast< uno::Sequence< geometry::RealPoint2D >& >(points[nCurrPoly]).getArray(),
457                                       const_cast< uno::Sequence< geometry::RealPoint2D >& >(points[nCurrPoly]).getArray()+nCurrSize,
458                                       aPoints.begin(),
459                                       implGdiPlusPointFromRealPoint2D );
460 
461                     pRes->AddLines( &aPoints[0], nCurrSize );
462                 }
463             }
464 
465             return pRes;
466         }
467 
468         GraphicsPathSharedPtr graphicsPathFromB2DPolygon( const ::basegfx::B2DPolygon& rPoly, bool bNoLineJoin )
469         {
470             GraphicsPathSharedPtr 				pRes( new Gdiplus::GraphicsPath() );
471             ::std::vector< Gdiplus::PointF > 	aPoints;
472 
473             graphicsPathFromB2DPolygon( pRes, aPoints, rPoly, bNoLineJoin );
474 
475             return pRes;
476         }
477 
478         GraphicsPathSharedPtr graphicsPathFromB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly, bool bNoLineJoin )
479         {
480             GraphicsPathSharedPtr 				pRes( new Gdiplus::GraphicsPath() );
481             ::std::vector< Gdiplus::PointF > 	aPoints;
482 
483             const sal_uInt32 nPolies( rPoly.count() );
484             for( sal_uInt32 nCurrPoly=0; nCurrPoly<nPolies; ++nCurrPoly )
485             {
486                 graphicsPathFromB2DPolygon( pRes,
487                                             aPoints,
488                                             rPoly.getB2DPolygon( nCurrPoly ),
489                                             bNoLineJoin);
490             }
491 
492             return pRes;
493         }
494 
495         GraphicsPathSharedPtr graphicsPathFromXPolyPolygon2D( const uno::Reference< rendering::XPolyPolygon2D >& xPoly, bool bNoLineJoin )
496         {
497             LinePolyPolygon* pPolyImpl = dynamic_cast< LinePolyPolygon* >( xPoly.get() );
498 
499             if( pPolyImpl )
500             {
501                 return pPolyImpl->getGraphicsPath( bNoLineJoin );
502             }
503             else
504             {
505                 return tools::graphicsPathFromB2DPolyPolygon(
506                     polyPolygonFromXPolyPolygon2D( xPoly ), bNoLineJoin );
507             }
508         }
509 
510         bool drawGdiPlusBitmap( const GraphicsSharedPtr& rGraphics,
511                                 const BitmapSharedPtr&	 rBitmap )
512         {
513             Gdiplus::PointF	aPoint;
514             return (Gdiplus::Ok == rGraphics->DrawImage( rBitmap.get(),
515                                                          aPoint ) );
516         }
517 
518         bool drawDIBits( const GraphicsSharedPtr& rGraphics,
519                          const BITMAPINFO&		  rBI,
520                          const void*			  pBits )
521         {
522             BitmapSharedPtr pBitmap(
523                 Gdiplus::Bitmap::FromBITMAPINFO( &rBI,
524                                                  (void*)pBits ) );
525 
526             return drawGdiPlusBitmap( rGraphics,
527                                       pBitmap );
528         }
529 
530         bool drawRGBABits( const GraphicsSharedPtr& rGraphics,
531                            const RawRGBABitmap&		rRawRGBAData )
532         {
533             BitmapSharedPtr pBitmap( new Gdiplus::Bitmap( rRawRGBAData.mnWidth,
534                                                           rRawRGBAData.mnHeight,
535                                                           PixelFormat32bppARGB ) );
536 
537             Gdiplus::BitmapData aBmpData;
538             aBmpData.Width		 = rRawRGBAData.mnWidth;
539             aBmpData.Height		 = rRawRGBAData.mnHeight;
540             aBmpData.Stride 	 = 4*aBmpData.Width; // bottom-up format
541             aBmpData.PixelFormat = PixelFormat32bppARGB;
542             aBmpData.Scan0		 = rRawRGBAData.mpBitmapData.get();
543 
544             const Gdiplus::Rect aRect( 0,0,aBmpData.Width,aBmpData.Height );
545             if( Gdiplus::Ok != pBitmap->LockBits( &aRect,
546                                                   Gdiplus::ImageLockModeWrite | Gdiplus::ImageLockModeUserInputBuf,
547                                                   PixelFormat32bppARGB,
548                                                   &aBmpData ) )
549             {
550                 return false;
551             }
552 
553             // commit data to bitmap
554             pBitmap->UnlockBits( &aBmpData );
555 
556             return drawGdiPlusBitmap( rGraphics,
557                                       pBitmap );
558         }
559 
560         BitmapSharedPtr bitmapFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
561         {
562             BitmapProvider* pBitmapProvider = dynamic_cast< BitmapProvider* >(xBitmap.get());
563 
564             if( pBitmapProvider )
565             {
566 				IBitmapSharedPtr pBitmap( pBitmapProvider->getBitmap() );
567 				return pBitmap->getBitmap();
568             }
569             else
570             {
571                 // not a native CanvasBitmap, extract VCL bitmap and
572                 // render into GDI+ bitmap of similar size
573                 // =================================================
574 
575                 const geometry::IntegerSize2D aBmpSize( xBitmap->getSize() );
576                 BitmapSharedPtr 			  pBitmap;
577 
578                 if( xBitmap->hasAlpha() )
579                 {
580                     // TODO(P2): At least for the alpha bitmap case, it
581                     // would be possible to generate the corresponding
582                     // bitmap directly
583                     pBitmap.reset( new Gdiplus::Bitmap( aBmpSize.Width,
584                                                         aBmpSize.Height,
585                                                         PixelFormat32bppARGB ) );
586                 }
587                 else
588                 {
589                     // TODO(F2): Might be wise to create bitmap compatible
590                     // to the VCL bitmap. Also, check whether the VCL
591                     // bitmap's system handles can be used to create the
592                     // GDI+ bitmap (currently, it does not seem so).
593                     pBitmap.reset( new Gdiplus::Bitmap( aBmpSize.Width,
594                                                         aBmpSize.Height,
595                                                         PixelFormat24bppRGB ) );
596                 }
597 
598 				GraphicsSharedPtr pGraphics(createGraphicsFromBitmap(pBitmap));
599 		        tools::setupGraphics(*pGraphics);
600                 if( !drawVCLBitmapFromXBitmap(
601                         pGraphics,
602                         xBitmap) )
603                 {
604                     pBitmap.reset();
605                 }
606 
607                 return pBitmap;
608             }
609         }
610 
611         CanvasFont::ImplRef	canvasFontFromXFont( const uno::Reference< rendering::XCanvasFont >& xFont )
612         {
613             CanvasFont* pCanvasFont = dynamic_cast< CanvasFont* >(xFont.get());
614 
615             ENSURE_ARG_OR_THROW( pCanvasFont,
616                              "canvasFontFromXFont(): Invalid XFont (or incompatible font for this XCanvas)" );
617 
618             return CanvasFont::ImplRef( pCanvasFont );
619         }
620 
621         void setModulateImageAttributes( Gdiplus::ImageAttributes& o_rAttr,
622                                          double					   nRedModulation,
623                                          double					   nGreenModulation,
624                                          double					   nBlueModulation,
625                                          double					   nAlphaModulation )
626         {
627             // This gets rather verbose, but we have to setup a color
628             // transformation matrix, in order to incorporate the global
629             // alpha value mfAlpha into the bitmap rendering.
630             Gdiplus::ColorMatrix	 aColorMatrix;
631 
632             aColorMatrix.m[0][0] = static_cast<Gdiplus::REAL>(nRedModulation);
633             aColorMatrix.m[0][1] = 0.0;
634             aColorMatrix.m[0][2] = 0.0;
635             aColorMatrix.m[0][3] = 0.0;
636             aColorMatrix.m[0][4] = 0.0;
637 
638             aColorMatrix.m[1][0] = 0.0;
639             aColorMatrix.m[1][1] = static_cast<Gdiplus::REAL>(nGreenModulation);
640             aColorMatrix.m[1][2] = 0.0;
641             aColorMatrix.m[1][3] = 0.0;
642             aColorMatrix.m[1][4] = 0.0;
643 
644             aColorMatrix.m[2][0] = 0.0;
645             aColorMatrix.m[2][1] = 0.0;
646             aColorMatrix.m[2][2] = static_cast<Gdiplus::REAL>(nBlueModulation);
647             aColorMatrix.m[2][3] = 0.0;
648             aColorMatrix.m[2][4] = 0.0;
649 
650             aColorMatrix.m[3][0] = 0.0;
651             aColorMatrix.m[3][1] = 0.0;
652             aColorMatrix.m[3][2] = 0.0;
653             aColorMatrix.m[3][3] = static_cast<Gdiplus::REAL>(nAlphaModulation);
654             aColorMatrix.m[3][4] = 0.0;
655 
656             aColorMatrix.m[4][0] = 0.0;
657             aColorMatrix.m[4][1] = 0.0;
658             aColorMatrix.m[4][2] = 0.0;
659             aColorMatrix.m[4][3] = 0.0;
660             aColorMatrix.m[4][4] = 1.0;
661 
662             o_rAttr.SetColorMatrix( &aColorMatrix,
663                                     Gdiplus::ColorMatrixFlagsDefault,
664                                     Gdiplus::ColorAdjustTypeDefault );
665         }
666 
667     } // namespace tools
668 } // namespace dxcanvas
669