xref: /trunk/main/canvas/source/vcl/canvashelper.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 
36 #include <com/sun/star/rendering/CompositeOperation.hpp>
37 #include <com/sun/star/util/Endianness.hpp>
38 #include <com/sun/star/rendering/TextDirection.hpp>
39 #include <com/sun/star/rendering/TexturingMode.hpp>
40 #include <com/sun/star/rendering/PathCapType.hpp>
41 #include <com/sun/star/rendering/PathJoinType.hpp>
42 
43 #include <tools/poly.hxx>
44 #include <vcl/window.hxx>
45 #include <vcl/bitmapex.hxx>
46 #include <vcl/bmpacc.hxx>
47 #include <vcl/canvastools.hxx>
48 
49 #include <basegfx/matrix/b2dhommatrix.hxx>
50 #include <basegfx/range/b2drectangle.hxx>
51 #include <basegfx/point/b2dpoint.hxx>
52 #include <basegfx/vector/b2dsize.hxx>
53 #include <basegfx/polygon/b2dpolygon.hxx>
54 #include <basegfx/polygon/b2dpolygontools.hxx>
55 #include <basegfx/polygon/b2dpolypolygontools.hxx>
56 #include <basegfx/polygon/b2dlinegeometry.hxx>
57 #include <basegfx/tools/canvastools.hxx>
58 #include <basegfx/numeric/ftools.hxx>
59 
60 #include <utility>
61 
62 #include <comphelper/sequence.hxx>
63 #include <canvas/canvastools.hxx>
64 
65 #include "textlayout.hxx"
66 #include "canvashelper.hxx"
67 #include "canvasbitmap.hxx"
68 #include "impltools.hxx"
69 #include "canvasfont.hxx"
70 
71 
72 using namespace ::com::sun::star;
73 
74 namespace vclcanvas
75 {
76     namespace
77     {
78         basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType )
79         {
80             switch( nJoinType )
81             {
82                 case rendering::PathJoinType::NONE:
83                     return basegfx::B2DLINEJOIN_NONE;
84 
85                 case rendering::PathJoinType::MITER:
86                     return basegfx::B2DLINEJOIN_MITER;
87 
88                 case rendering::PathJoinType::ROUND:
89                     return basegfx::B2DLINEJOIN_ROUND;
90 
91                 case rendering::PathJoinType::BEVEL:
92                     return basegfx::B2DLINEJOIN_BEVEL;
93 
94                 default:
95                     ENSURE_OR_THROW( false,
96                                       "b2DJoineFromJoin(): Unexpected join type" );
97             }
98 
99             return basegfx::B2DLINEJOIN_NONE;
100         }
101     }
102 
103     CanvasHelper::CanvasHelper() :
104         mpDevice(),
105         mpProtectedOutDev(),
106         mpOutDev(),
107         mp2ndOutDev(),
108         mbHaveAlpha( false )
109     {
110     }
111 
112     void CanvasHelper::disposing()
113     {
114         mpDevice = NULL;
115         mpProtectedOutDev.reset();
116         mpOutDev.reset();
117         mp2ndOutDev.reset();
118     }
119 
120     void CanvasHelper::init( rendering::XGraphicDevice&     rDevice,
121                              const OutDevProviderSharedPtr&	rOutDev,
122                              bool 							bProtect,
123                              bool 							bHaveAlpha )
124     {
125         // cast away const, need to change refcount (as this is
126         // ~invisible to client code, still logically const)
127         mpDevice	= &rDevice;
128         mbHaveAlpha = bHaveAlpha;
129 
130         setOutDev( rOutDev, bProtect );
131     }
132 
133     void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev,
134                                   bool 							 bProtect )
135     {
136         if( bProtect )
137             mpProtectedOutDev = rOutDev;
138         else
139             mpProtectedOutDev.reset();
140 
141         mpOutDev = rOutDev;
142     }
143 
144     void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev )
145     {
146         mp2ndOutDev = rOutDev;
147         mp2ndOutDev->getOutDev().EnableMapMode( sal_False );
148     }
149 
150     void CanvasHelper::clear()
151     {
152         // are we disposed?
153         if( mpOutDev )
154         {
155             OutputDevice& rOutDev( mpOutDev->getOutDev() );
156             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
157 
158             rOutDev.EnableMapMode( sal_False );
159             rOutDev.SetLineColor( COL_WHITE );
160             rOutDev.SetFillColor( COL_WHITE );
161             rOutDev.DrawRect( Rectangle( Point(),
162                                          rOutDev.GetOutputSizePixel()) );
163 
164             if( mp2ndOutDev )
165             {
166                 OutputDevice& rOutDev2( mp2ndOutDev->getOutDev() );
167 
168                 rOutDev2.SetDrawMode( DRAWMODE_DEFAULT );
169                 rOutDev2.EnableMapMode( sal_False );
170                 rOutDev2.SetLineColor( COL_WHITE );
171                 rOutDev2.SetFillColor( COL_WHITE );
172                 rOutDev2.DrawRect( Rectangle( Point(),
173                                               rOutDev2.GetOutputSizePixel()) );
174                 rOutDev2.SetDrawMode( DRAWMODE_BLACKLINE | DRAWMODE_BLACKFILL | DRAWMODE_BLACKTEXT |
175                                       DRAWMODE_BLACKGRADIENT | DRAWMODE_BLACKBITMAP );
176             }
177         }
178     }
179 
180     void CanvasHelper::drawPoint( const rendering::XCanvas* 	,
181                                   const geometry::RealPoint2D& 	aPoint,
182                                   const rendering::ViewState& 	viewState,
183                                   const rendering::RenderState&	renderState )
184     {
185         // are we disposed?
186         if( mpOutDev )
187         {
188             // nope, render
189             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
190             setupOutDevState( viewState, renderState, LINE_COLOR );
191 
192             const Point aOutPoint( tools::mapRealPoint2D( aPoint,
193                                                           viewState, renderState ) );
194             // TODO(F1): alpha
195             mpOutDev->getOutDev().DrawPixel( aOutPoint );
196 
197             if( mp2ndOutDev )
198                 mp2ndOutDev->getOutDev().DrawPixel( aOutPoint );
199         }
200     }
201 
202     void CanvasHelper::drawLine( const rendering::XCanvas* 		,
203                                  const geometry::RealPoint2D& 	aStartRealPoint2D,
204                                  const geometry::RealPoint2D& 	aEndRealPoint2D,
205                                  const rendering::ViewState& 	viewState,
206                                  const rendering::RenderState& 	renderState )
207     {
208         // are we disposed?
209         if( mpOutDev )
210         {
211             // nope, render
212             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
213             setupOutDevState( viewState, renderState, LINE_COLOR );
214 
215             const Point aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D,
216                                                             viewState, renderState ) );
217             const Point aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D,
218                                                           viewState, renderState ) );
219             // TODO(F2): alpha
220             mpOutDev->getOutDev().DrawLine( aStartPoint, aEndPoint );
221 
222             if( mp2ndOutDev )
223                 mp2ndOutDev->getOutDev().DrawLine( aStartPoint, aEndPoint );
224         }
225     }
226 
227     void CanvasHelper::drawBezier( const rendering::XCanvas* 			,
228                                    const geometry::RealBezierSegment2D&	aBezierSegment,
229                                    const geometry::RealPoint2D& 		_aEndPoint,
230                                    const rendering::ViewState& 			viewState,
231                                    const rendering::RenderState& 		renderState )
232     {
233         if( mpOutDev )
234         {
235             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
236             setupOutDevState( viewState, renderState, LINE_COLOR );
237 
238             const Point& rStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px,
239                                                                                    aBezierSegment.Py),
240                                                             viewState, renderState ) );
241             const Point& rCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x,
242                                                                                    aBezierSegment.C1y),
243                                                             viewState, renderState ) );
244             const Point& rCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x,
245                                                                                    aBezierSegment.C2y),
246                                                              viewState, renderState ) );
247             const Point& rEndPoint( tools::mapRealPoint2D( _aEndPoint,
248                                                            viewState, renderState ) );
249 
250             ::Polygon aPoly(4);
251             aPoly.SetPoint( rStartPoint, 0 );
252             aPoly.SetFlags( 0, POLY_NORMAL );
253             aPoly.SetPoint( rCtrlPoint1, 1 );
254             aPoly.SetFlags( 1, POLY_CONTROL );
255             aPoly.SetPoint( rCtrlPoint2, 2 );
256             aPoly.SetFlags( 2, POLY_CONTROL );
257             aPoly.SetPoint( rEndPoint, 3 );
258             aPoly.SetFlags( 3, POLY_NORMAL );
259 
260             // TODO(F2): alpha
261             mpOutDev->getOutDev().DrawPolygon( aPoly );
262             if( mp2ndOutDev )
263                 mp2ndOutDev->getOutDev().DrawPolygon( aPoly );
264         }
265     }
266 
267     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* 							,
268                                                                                  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
269                                                                                  const rendering::ViewState& 						viewState,
270                                                                                  const rendering::RenderState& 						renderState )
271     {
272         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
273                          "polygon is NULL");
274 
275         if( mpOutDev )
276         {
277             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
278             setupOutDevState( viewState, renderState, LINE_COLOR );
279 
280             const ::basegfx::B2DPolyPolygon& rPolyPoly(
281                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
282             const PolyPolygon aPolyPoly( tools::mapPolyPolygon( rPolyPoly, viewState, renderState ) );
283 
284             if( rPolyPoly.isClosed() )
285             {
286                 mpOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
287 
288                 if( mp2ndOutDev )
289                     mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
290             }
291             else
292             {
293                 // mixed open/closed state. Cannot render open polygon
294                 // via DrawPolyPolygon(), since that implicitley
295                 // closed every polygon. OTOH, no need to distinguish
296                 // further and render closed polygons via
297                 // DrawPolygon(), and open ones via DrawPolyLine():
298                 // closed polygons will simply already contain the
299                 // closing segment.
300                 sal_uInt16 nSize( aPolyPoly.Count() );
301 
302                 for( sal_uInt16 i=0; i<nSize; ++i )
303                 {
304                     mpOutDev->getOutDev().DrawPolyLine( aPolyPoly[i] );
305 
306                     if( mp2ndOutDev )
307                         mp2ndOutDev->getOutDev().DrawPolyLine( aPolyPoly[i] );
308                 }
309             }
310         }
311 
312         // TODO(P1): Provide caching here.
313         return uno::Reference< rendering::XCachedPrimitive >(NULL);
314     }
315 
316     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* 							,
317                                                                                    const uno::Reference< rendering::XPolyPolygon2D >& 	xPolyPolygon,
318                                                                                    const rendering::ViewState& 							viewState,
319                                                                                    const rendering::RenderState& 						renderState,
320                                                                                    const rendering::StrokeAttributes& 					strokeAttributes )
321     {
322         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
323                          "polygon is NULL");
324 
325         if( mpOutDev )
326         {
327             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
328 
329             ::basegfx::B2DHomMatrix aMatrix;
330             ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
331 
332             ::basegfx::B2DSize aLinePixelSize(strokeAttributes.StrokeWidth,
333                                               strokeAttributes.StrokeWidth);
334             aLinePixelSize *= aMatrix;
335 
336             ::basegfx::B2DPolyPolygon aPolyPoly(
337                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
338 
339             if( aPolyPoly.areControlPointsUsed() )
340             {
341                 // AW: Not needed for ApplyLineDashing anymore; should be removed
342                 aPolyPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
343             }
344 
345             // apply dashing, if any
346             if( strokeAttributes.DashArray.getLength() )
347             {
348                 const ::std::vector<double>& aDashArray(
349                     ::comphelper::sequenceToContainer< ::std::vector<double> >(strokeAttributes.DashArray) );
350 
351                 ::basegfx::B2DPolyPolygon aDashedPolyPoly;
352 
353                 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
354                 {
355                     // AW: new interface; You may also get gaps in the same run now
356                     basegfx::tools::applyLineDashing(aPolyPoly.getB2DPolygon(i), aDashArray, &aDashedPolyPoly);
357                     //aDashedPolyPoly.append(
358                     //    ::basegfx::tools::applyLineDashing( aPolyPoly.getB2DPolygon(i),
359                     //                                        aDashArray ) );
360                 }
361 
362                 aPolyPoly = aDashedPolyPoly;
363             }
364 
365             ::basegfx::B2DPolyPolygon aStrokedPolyPoly;
366             if( aLinePixelSize.getLength() < 1.42 )
367             {
368                 // line width < 1.0 in device pixel, thus, output as a
369                 // simple hairline poly-polygon
370                 setupOutDevState( viewState, renderState, LINE_COLOR );
371 
372                 aStrokedPolyPoly = aPolyPoly;
373             }
374             else
375             {
376                 // render as a 'thick' line
377                 setupOutDevState( viewState, renderState, FILL_COLOR );
378 
379                 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
380                 {
381                     // TODO(F2): Use MiterLimit from StrokeAttributes,
382                     // need to convert it here to angle.
383 
384                     // TODO(F2): Also use Cap settings from
385                     // StrokeAttributes, the
386                     // createAreaGeometryForLineStartEnd() method does not
387                     // seem to fit very well here
388 
389                     // AW: New interface, will create bezier polygons now
390                     aStrokedPolyPoly.append(basegfx::tools::createAreaGeometry(
391                         aPolyPoly.getB2DPolygon(i), strokeAttributes.StrokeWidth*0.5, b2DJoineFromJoin(strokeAttributes.JoinType)));
392                     //aStrokedPolyPoly.append(
393                     //    ::basegfx::tools::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i),
394                     //                                                    strokeAttributes.StrokeWidth*0.5,
395                     //                                                    b2DJoineFromJoin(strokeAttributes.JoinType) ) );
396                 }
397             }
398 
399             // transform only _now_, all the StrokeAttributes are in
400             // user coordinates.
401             aStrokedPolyPoly.transform( aMatrix );
402 
403             const PolyPolygon aVCLPolyPoly( aStrokedPolyPoly );
404 
405             // TODO(F2): When using alpha here, must handle that via
406             // temporary surface or somesuch.
407 
408             // Note: the generated stroke poly-polygon is NOT free of
409             // self-intersections. Therefore, if we would render it
410             // via OutDev::DrawPolyPolygon(), on/off fill would
411             // generate off areas on those self-intersections.
412             sal_uInt16 nSize( aVCLPolyPoly.Count() );
413 
414             for( sal_uInt16 i=0; i<nSize; ++i )
415             {
416                 if( aStrokedPolyPoly.getB2DPolygon( i ).isClosed() ) {
417                     mpOutDev->getOutDev().DrawPolygon( aVCLPolyPoly[i] );
418                     if( mp2ndOutDev )
419                         mp2ndOutDev->getOutDev().DrawPolygon( aVCLPolyPoly[i] );
420                 } else {
421                     const sal_uInt16 nPolySize = aVCLPolyPoly[i].GetSize();
422                     if( nPolySize ) {
423                         Point rPrevPoint = aVCLPolyPoly[i].GetPoint( 0 );
424                         Point rPoint;
425 
426                         for( sal_uInt16 j=1; j<nPolySize; j++ ) {
427                             rPoint = aVCLPolyPoly[i].GetPoint( j );
428                             mpOutDev->getOutDev().DrawLine( rPrevPoint, rPoint );
429                             if( mp2ndOutDev )
430                                 mp2ndOutDev->getOutDev().DrawLine( rPrevPoint, rPoint );
431                             rPrevPoint = rPoint;
432                         }
433                     }
434                 }
435             }
436         }
437 
438         // TODO(P1): Provide caching here.
439         return uno::Reference< rendering::XCachedPrimitive >(NULL);
440     }
441 
442     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* 							,
443                                                                                            const uno::Reference< rendering::XPolyPolygon2D >& 	,
444                                                                                            const rendering::ViewState& 							,
445                                                                                            const rendering::RenderState& 						,
446                                                                                            const uno::Sequence< rendering::Texture >& 			,
447                                                                                            const rendering::StrokeAttributes& 					 )
448     {
449         return uno::Reference< rendering::XCachedPrimitive >(NULL);
450     }
451 
452     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* 							,
453                                                                                                 const uno::Reference< rendering::XPolyPolygon2D >& 	,
454                                                                                                 const rendering::ViewState& 						,
455                                                                                                 const rendering::RenderState& 						,
456                                                                                                 const uno::Sequence< rendering::Texture >& 			,
457                                                                                                 const uno::Reference< geometry::XMapping2D >& 		,
458                                                                                                 const rendering::StrokeAttributes& 					 )
459     {
460         return uno::Reference< rendering::XCachedPrimitive >(NULL);
461     }
462 
463     uno::Reference< rendering::XPolyPolygon2D >   CanvasHelper::queryStrokeShapes( const rendering::XCanvas* 							,
464                                                                                    const uno::Reference< rendering::XPolyPolygon2D >& 	,
465                                                                                    const rendering::ViewState& 							,
466                                                                                    const rendering::RenderState& 						,
467                                                                                    const rendering::StrokeAttributes& 					 )
468     {
469         return uno::Reference< rendering::XPolyPolygon2D >(NULL);
470     }
471 
472     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* 							,
473                                                                                  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
474                                                                                  const rendering::ViewState& 						viewState,
475                                                                                  const rendering::RenderState& 						renderState )
476     {
477         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
478                          "polygon is NULL");
479 
480         if( mpOutDev )
481         {
482             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
483 
484             const int nTransparency( setupOutDevState( viewState, renderState, FILL_COLOR ) );
485             const int nTransPercent( (nTransparency * 100 + 128) / 255 );  // normal rounding, no truncation here
486             ::basegfx::B2DPolyPolygon aB2DPolyPoly(
487                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
488             aB2DPolyPoly.setClosed(true); // ensure closed poly, otherwise VCL does not fill
489             const PolyPolygon aPolyPoly( tools::mapPolyPolygon(
490                                              aB2DPolyPoly,
491                                              viewState, renderState ) );
492             const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE );
493             if( !nTransparency || bSourceAlpha )
494             {
495                 mpOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
496             }
497             else
498             {
499                 mpOutDev->getOutDev().DrawTransparent( aPolyPoly, (sal_uInt16)nTransPercent );
500             }
501 
502             if( mp2ndOutDev )
503             {
504                 if( !nTransparency || bSourceAlpha )
505                 {
506                     // HACK. Normally, CanvasHelper does not care
507                     // about actually what mp2ndOutDev is...
508                     if( bSourceAlpha && nTransparency == 255 )
509                     {
510                         mp2ndOutDev->getOutDev().SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT |
511                                                               DRAWMODE_WHITEGRADIENT | DRAWMODE_WHITEBITMAP );
512                         mp2ndOutDev->getOutDev().SetFillColor( COL_WHITE );
513                         mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
514                         mp2ndOutDev->getOutDev().SetDrawMode( DRAWMODE_BLACKLINE | DRAWMODE_BLACKFILL | DRAWMODE_BLACKTEXT |
515                                                               DRAWMODE_BLACKGRADIENT | DRAWMODE_BLACKBITMAP );
516                     }
517                     else
518                     {
519                         mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly );
520                     }
521                 }
522                 else
523                 {
524                     mp2ndOutDev->getOutDev().DrawTransparent( aPolyPoly, (sal_uInt16)nTransPercent );
525                 }
526             }
527         }
528 
529         // TODO(P1): Provide caching here.
530         return uno::Reference< rendering::XCachedPrimitive >(NULL);
531     }
532 
533     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* 							,
534                                                                                               const uno::Reference< rendering::XPolyPolygon2D >& 	,
535                                                                                               const rendering::ViewState& 							,
536                                                                                               const rendering::RenderState& 						,
537                                                                                               const uno::Sequence< rendering::Texture >& 			,
538                                                                                               const uno::Reference< geometry::XMapping2D >& 			 )
539     {
540         return uno::Reference< rendering::XCachedPrimitive >(NULL);
541     }
542 
543     uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* 						,
544                                                                        const rendering::FontRequest& 					fontRequest,
545                                                                        const uno::Sequence< beans::PropertyValue >& 	extraFontProperties,
546                                                                        const geometry::Matrix2D& 						fontMatrix )
547     {
548         if( mpOutDev && mpDevice )
549         {
550             // TODO(F2): font properties and font matrix
551             return uno::Reference< rendering::XCanvasFont >(
552                     new CanvasFont(fontRequest, extraFontProperties, fontMatrix,
553                                    *mpDevice, mpOutDev) );
554         }
555 
556         return uno::Reference< rendering::XCanvasFont >();
557     }
558 
559     uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* 						,
560                                                                             const rendering::FontInfo& 						,
561                                                                             const uno::Sequence< beans::PropertyValue >& 	 )
562     {
563         // TODO(F2)
564         return uno::Sequence< rendering::FontInfo >();
565     }
566 
567     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* 						,
568                                                                           const rendering::StringContext& 					text,
569                                                                           const uno::Reference< rendering::XCanvasFont >& 	xFont,
570                                                                           const rendering::ViewState& 						viewState,
571                                                                           const rendering::RenderState& 					renderState,
572                                                                           sal_Int8				 							textDirection )
573     {
574         ENSURE_ARG_OR_THROW( xFont.is(),
575                          "font is NULL");
576 
577         if( mpOutDev )
578         {
579             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
580 
581             ::Point aOutpos;
582             if( !setupTextOutput( aOutpos, viewState, renderState, xFont ) )
583                 return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
584 
585             // change text direction and layout mode
586             sal_uIntPtr nLayoutMode(0);
587             switch( textDirection )
588             {
589                 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
590                     nLayoutMode |= TEXT_LAYOUT_BIDI_LTR;
591                     // FALLTHROUGH intended
592                 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
593                     nLayoutMode |= TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG;
594                     nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_LEFT;
595                     break;
596 
597                 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
598                     nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
599                     // FALLTHROUGH intended
600                 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
601                     nLayoutMode |= TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
602                     nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_RIGHT;
603                     break;
604             }
605 
606             // TODO(F2): alpha
607             mpOutDev->getOutDev().SetLayoutMode( nLayoutMode );
608             mpOutDev->getOutDev().DrawText( aOutpos,
609                                             text.Text,
610                                             ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
611                                             ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
612 
613             if( mp2ndOutDev )
614             {
615                 mp2ndOutDev->getOutDev().SetLayoutMode( nLayoutMode );
616                 mp2ndOutDev->getOutDev().DrawText( aOutpos,
617                                                    text.Text,
618                                                    ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
619                                                    ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
620             }
621         }
622 
623         return uno::Reference< rendering::XCachedPrimitive >(NULL);
624     }
625 
626     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* 						,
627                                                                                 const uno::Reference< rendering::XTextLayout >& xLayoutedText,
628                                                                                 const rendering::ViewState& 					viewState,
629                                                                                 const rendering::RenderState& 					renderState )
630     {
631         ENSURE_ARG_OR_THROW( xLayoutedText.is(),
632                          "layout is NULL");
633 
634         TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
635 
636         if( pTextLayout )
637         {
638             if( mpOutDev )
639             {
640                 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
641 
642                 // TODO(T3): Race condition. We're taking the font
643                 // from xLayoutedText, and then calling draw() at it,
644                 // without exclusive access. Move setupTextOutput(),
645                 // e.g. to impltools?
646 
647                 ::Point aOutpos;
648                 if( !setupTextOutput( aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
649                     return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
650 
651                 // TODO(F2): What about the offset scalings?
652                 // TODO(F2): alpha
653                 pTextLayout->draw( mpOutDev->getOutDev(), aOutpos, viewState, renderState );
654 
655                 if( mp2ndOutDev )
656                     pTextLayout->draw( mp2ndOutDev->getOutDev(), aOutpos, viewState, renderState );
657             }
658         }
659         else
660         {
661             ENSURE_ARG_OR_THROW( false,
662                                  "TextLayout not compatible with this canvas" );
663         }
664 
665         return uno::Reference< rendering::XCachedPrimitive >(NULL);
666     }
667 
668     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmap( const rendering::XCanvas* 					pCanvas,
669                                                                                 const uno::Reference< rendering::XBitmap >& xBitmap,
670                                                                                 const rendering::ViewState& 				viewState,
671                                                                                 const rendering::RenderState& 				renderState,
672                                                                                 bool 										bModulateColors )
673     {
674         ENSURE_ARG_OR_THROW( xBitmap.is(),
675                              "bitmap is NULL");
676 
677         ::canvas::tools::verifyInput( renderState,
678                                       BOOST_CURRENT_FUNCTION,
679                                       mpDevice,
680                                       4,
681                                       bModulateColors ? 3 : 0 );
682 
683         if( mpOutDev )
684         {
685             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
686             setupOutDevState( viewState, renderState, IGNORE_COLOR );
687 
688             ::basegfx::B2DHomMatrix aMatrix;
689             ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
690 
691             ::basegfx::B2DPoint aOutputPos( 0.0, 0.0 );
692             aOutputPos *= aMatrix;
693 
694             BitmapEx aBmpEx( tools::bitmapExFromXBitmap(xBitmap) );
695 
696             // TODO(F2): Implement modulation again for other color
697             // channels (currently, works only for alpha). Note: this
698             // is already implemented in transformBitmap()
699             if( bModulateColors &&
700                 renderState.DeviceColor.getLength() > 3 )
701             {
702                 // optimize away the case where alpha modulation value
703                 // is 1.0 - we then simply switch off modulation at all
704                 bModulateColors = !::rtl::math::approxEqual(
705                     renderState.DeviceColor[3], 1.0);
706             }
707 
708             // check whether we can render bitmap as-is: must not
709             // modulate colors, matrix must either be the identity
710             // transform (that's clear), _or_ contain only
711             // translational components.
712 			if( !bModulateColors &&
713                 (aMatrix.isIdentity() ||
714                  (::basegfx::fTools::equalZero( aMatrix.get(0,1) ) &&
715                   ::basegfx::fTools::equalZero( aMatrix.get(1,0) ) &&
716                   ::rtl::math::approxEqual(aMatrix.get(0,0), 1.0) &&
717                   ::rtl::math::approxEqual(aMatrix.get(1,1), 1.0)) ) )
718             {
719                 // optimized case: identity matrix, or only
720                 // translational components.
721                 mpOutDev->getOutDev().DrawBitmapEx( ::vcl::unotools::pointFromB2DPoint( aOutputPos ),
722                                                     aBmpEx );
723 
724                 if( mp2ndOutDev )
725                     mp2ndOutDev->getOutDev().DrawBitmapEx( ::vcl::unotools::pointFromB2DPoint( aOutputPos ),
726                                                            aBmpEx );
727 
728                 // Returning a cache object is not useful, the XBitmap
729                 // itself serves this purpose
730                 return uno::Reference< rendering::XCachedPrimitive >(NULL);
731             }
732             else
733             {
734                 // Matrix contains non-trivial transformation (or
735                 // color modulation is requested), decompose to check
736                 // whether GraphicObject suffices
737                 ::basegfx::B2DVector aScale;
738                 double				 nRotate;
739                 double				 nShearX;
740                 aMatrix.decompose( aScale, aOutputPos, nRotate, nShearX );
741 
742                 GraphicAttr 			aGrfAttr;
743                 GraphicObjectSharedPtr 	pGrfObj;
744 
745                 ::Size aBmpSize( aBmpEx.GetSizePixel() );
746 
747                 // setup alpha modulation
748                 if( bModulateColors )
749                 {
750                     const double nAlphaModulation( renderState.DeviceColor[3] );
751 
752                     // TODO(F1): Note that the GraphicManager has a
753                     // subtle difference in how it calculates the
754                     // resulting alpha value: it's using the inverse
755                     // alpha values (i.e. 'transparency'), and
756                     // calculates transOrig + transModulate, instead
757                     // of transOrig + transModulate -
758                     // transOrig*transModulate (which would be
759                     // equivalent to the origAlpha*modulateAlpha the
760                     // DX canvas performs)
761                     aGrfAttr.SetTransparency(
762                         static_cast< sal_uInt8 >(
763                             ::basegfx::fround( 255.0*( 1.0 - nAlphaModulation ) ) ) );
764                 }
765 
766                 if( ::basegfx::fTools::equalZero( nShearX ) )
767                 {
768                     // no shear, GraphicObject is enough (the
769                     // GraphicObject only supports scaling, rotation
770                     // and translation)
771 
772                     // #i75339# don't apply mirror flags, having
773                     // negative size values is enough to make
774                     // GraphicObject flip the bitmap
775 
776                     // The angle has to be mapped from radian to tenths of
777                     // degress with the orientation reversed: [0,2Pi) ->
778                     // (3600,0].  Note that the original angle may have
779                     // values outside the [0,2Pi) interval.
780                     const double nAngleInTenthOfDegrees (3600.0 - nRotate * 3600.0 / (2*M_PI));
781                     aGrfAttr.SetRotation( static_cast< sal_uInt16 >(::basegfx::fround(nAngleInTenthOfDegrees)) );
782 
783                     pGrfObj.reset( new GraphicObject( aBmpEx ) );
784                 }
785                 else
786                 {
787                     // modify output position, to account for the fact
788                     // that transformBitmap() always normalizes its output
789                     // bitmap into the smallest enclosing box.
790                     ::basegfx::B2DRectangle	aDestRect;
791                     ::canvas::tools::calcTransformedRectBounds( aDestRect,
792                                                                 ::basegfx::B2DRectangle(0,
793                                                                                         0,
794                                                                                         aBmpSize.Width(),
795                                                                                         aBmpSize.Height()),
796                                                                 aMatrix );
797 
798                     aOutputPos.setX( aDestRect.getMinX() );
799                     aOutputPos.setY( aDestRect.getMinY() );
800 
801                     // complex transformation, use generic affine bitmap
802                     // transformation
803                     aBmpEx = tools::transformBitmap( aBmpEx,
804                                                      aMatrix,
805                                                      renderState.DeviceColor,
806                                                      tools::MODULATE_NONE );
807 
808                     pGrfObj.reset( new GraphicObject( aBmpEx ) );
809 
810                     // clear scale values, generated bitmap already
811                     // contains scaling
812                     aScale.setX( 1.0 ); aScale.setY( 1.0 );
813 
814                     // update bitmap size, bitmap has changed above.
815                     aBmpSize = aBmpEx.GetSizePixel();
816                 }
817 
818                 // output GraphicObject
819                 const ::Point aPt( ::vcl::unotools::pointFromB2DPoint( aOutputPos ) );
820                 const ::Size  aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width() ),
821                                    ::basegfx::fround( aScale.getY() * aBmpSize.Height() ) );
822 
823                 pGrfObj->Draw( &mpOutDev->getOutDev(),
824                                aPt,
825                                aSz,
826                                &aGrfAttr );
827 
828                 if( mp2ndOutDev )
829                     pGrfObj->Draw( &mp2ndOutDev->getOutDev(),
830                                    aPt,
831                                    aSz,
832                                    &aGrfAttr );
833 
834                 // created GraphicObject, which possibly cached
835                 // display bitmap - return cache object, to retain
836                 // that information.
837                 return uno::Reference< rendering::XCachedPrimitive >(
838                     new CachedBitmap( pGrfObj,
839                                       aPt,
840                                       aSz,
841                                       aGrfAttr,
842                                       viewState,
843                                       renderState,
844                                       // cast away const, need to
845                                       // change refcount (as this is
846                                       // ~invisible to client code,
847                                       // still logically const)
848                                       const_cast< rendering::XCanvas* >(pCanvas)) );
849             }
850         }
851 
852         // Nothing rendered
853         return uno::Reference< rendering::XCachedPrimitive >(NULL);
854     }
855 
856     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* 					pCanvas,
857                                                                             const uno::Reference< rendering::XBitmap >& xBitmap,
858                                                                             const rendering::ViewState& 				viewState,
859                                                                             const rendering::RenderState& 				renderState )
860     {
861         return implDrawBitmap( pCanvas,
862                                xBitmap,
863                                viewState,
864                                renderState,
865                                false );
866     }
867 
868     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* 						pCanvas,
869                                                                                      const uno::Reference< rendering::XBitmap >& 	xBitmap,
870                                                                                      const rendering::ViewState& 					viewState,
871                                                                                      const rendering::RenderState& 					renderState )
872     {
873         return implDrawBitmap( pCanvas,
874                                xBitmap,
875                                viewState,
876                                renderState,
877                                true );
878     }
879 
880     uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
881     {
882         // cast away const, need to change refcount (as this is
883         // ~invisible to client code, still logically const)
884         return uno::Reference< rendering::XGraphicDevice >(mpDevice);
885     }
886 
887     void CanvasHelper::copyRect( const rendering::XCanvas* 							,
888                                  const uno::Reference< rendering::XBitmapCanvas >& 	,
889                                  const geometry::RealRectangle2D& 					,
890                                  const rendering::ViewState& 						,
891                                  const rendering::RenderState& 						,
892                                  const geometry::RealRectangle2D& 					,
893                                  const rendering::ViewState& 						,
894                                  const rendering::RenderState& 						 )
895     {
896         // TODO(F1)
897     }
898 
899     geometry::IntegerSize2D CanvasHelper::getSize()
900     {
901         if( !mpOutDev.get() )
902             return geometry::IntegerSize2D(); // we're disposed
903 
904         return ::vcl::unotools::integerSize2DFromSize( mpOutDev->getOutDev().GetOutputSizePixel() );
905     }
906 
907     uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
908                                                                         sal_Bool 					beFast )
909     {
910         if( !mpOutDev.get() || !mpDevice )
911             return uno::Reference< rendering::XBitmap >(); // we're disposed
912 
913         OutputDevice& rOutDev( mpOutDev->getOutDev() );
914 
915         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
916         rOutDev.EnableMapMode( sal_False );
917 
918         // TODO(F2): Support alpha vdev canvas here
919         const Point aEmptyPoint(0,0);
920         const Size  aBmpSize( rOutDev.GetOutputSizePixel() );
921 
922         Bitmap aBitmap( rOutDev.GetBitmap(aEmptyPoint, aBmpSize) );
923 
924         aBitmap.Scale( ::vcl::unotools::sizeFromRealSize2D(newSize),
925                        beFast ? BMP_SCALE_FAST : BMP_SCALE_INTERPOLATE );
926 
927         return uno::Reference< rendering::XBitmap >(
928             new CanvasBitmap( aBitmap, *mpDevice, mpOutDev ) );
929     }
930 
931     uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& 	 rLayout,
932                                                      const geometry::IntegerRectangle2D& rect )
933     {
934         if( !mpOutDev.get() )
935             return uno::Sequence< sal_Int8 >(); // we're disposed
936 
937         rLayout = getMemoryLayout();
938 
939         // TODO(F2): Support alpha canvas here
940         const Rectangle	aRect( ::vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
941 
942         OutputDevice& rOutDev( mpOutDev->getOutDev() );
943 
944         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
945         rOutDev.EnableMapMode( sal_False );
946 
947         Bitmap aBitmap( rOutDev.GetBitmap(aRect.TopLeft(),
948                                           aRect.GetSize()) );
949 
950         ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(),
951                                             aBitmap );
952 
953         ENSURE_OR_THROW( pReadAccess.get() != NULL,
954                          "Could not acquire read access to OutDev bitmap" );
955 
956         const sal_Int32 nWidth( rect.X2 - rect.X1 );
957         const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
958 
959         rLayout.ScanLines = nHeight;
960         rLayout.ScanLineBytes = nWidth*4;
961         rLayout.ScanLineStride = rLayout.ScanLineBytes;
962 
963         uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
964         sal_Int8* pRes = aRes.getArray();
965 
966         int nCurrPos(0);
967         for( int y=0; y<nHeight; ++y )
968         {
969             for( int x=0; x<nWidth; ++x )
970             {
971                 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
972                 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
973                 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
974                 pRes[ nCurrPos++ ] = -1;
975             }
976         }
977 
978         return aRes;
979     }
980 
981     void CanvasHelper::setData( const uno::Sequence< sal_Int8 >& 		data,
982                                 const rendering::IntegerBitmapLayout& 	aLayout,
983                                 const geometry::IntegerRectangle2D&		rect )
984     {
985         if( !mpOutDev.get() )
986             return; // we're disposed
987 
988         const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() );
989         ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != aLayout.PlaneStride ||
990                              aRefLayout.ColorSpace  != aLayout.ColorSpace ||
991                              aRefLayout.Palette     != aLayout.Palette ||
992                              aRefLayout.IsMsbFirst  != aLayout.IsMsbFirst,
993                              "Mismatching memory layout" );
994 
995         OutputDevice& rOutDev( mpOutDev->getOutDev() );
996 
997         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
998         rOutDev.EnableMapMode( sal_False );
999 
1000         const Rectangle aRect( ::vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
1001         const sal_uInt16 	nBitCount( ::std::min( (sal_uInt16)24U,
1002                                                (sal_uInt16)rOutDev.GetBitCount() ) );
1003         const BitmapPalette* pPalette = NULL;
1004 
1005         if( nBitCount <= 8 )
1006         {
1007             // TODO(Q1): Extract this to a common place, e.g. GraphicDevice
1008 
1009             // try to determine palette from output device (by
1010             // extracting a 1,1 bitmap, and querying it)
1011             const Point aEmptyPoint;
1012             const Size  aSize(1,1);
1013             Bitmap aTmpBitmap( rOutDev.GetBitmap( aEmptyPoint,
1014                                                   aSize ) );
1015 
1016             ScopedBitmapReadAccess pReadAccess( aTmpBitmap.AcquireReadAccess(),
1017                                                 aTmpBitmap );
1018 
1019             pPalette = &pReadAccess->GetPalette();
1020         }
1021 
1022         // TODO(F2): Support alpha canvas here
1023         Bitmap aBitmap( aRect.GetSize(), nBitCount, pPalette );
1024 
1025         bool bCopyBack( false ); // only copy something back, if we
1026                                  // actually changed some pixel
1027         {
1028             ScopedBitmapWriteAccess pWriteAccess( aBitmap.AcquireWriteAccess(),
1029                                                   aBitmap );
1030 
1031             ENSURE_OR_THROW( pWriteAccess.get() != NULL,
1032                              "Could not acquire write access to OutDev bitmap" );
1033 
1034             // for the time being, always read as RGB
1035             const sal_Int32 nWidth( rect.X2 - rect.X1 );
1036             const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
1037             int x, y, nCurrPos(0);
1038             for( y=0; y<nHeight; ++y )
1039             {
1040                 switch( pWriteAccess->GetScanlineFormat() )
1041                 {
1042                     case BMP_FORMAT_8BIT_PAL:
1043                     {
1044                         Scanline pScan = pWriteAccess->GetScanline( y );
1045 
1046                         for( x=0; x<nWidth; ++x )
1047                         {
1048                             *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex(
1049                                 BitmapColor( data[ nCurrPos ],
1050                                              data[ nCurrPos+1 ],
1051                                              data[ nCurrPos+2 ] ) );
1052 
1053                             nCurrPos += 4;
1054                         }
1055                     }
1056                     break;
1057 
1058                     case BMP_FORMAT_24BIT_TC_BGR:
1059                     {
1060                         Scanline pScan = pWriteAccess->GetScanline( y );
1061 
1062                         for( x=0; x<nWidth; ++x )
1063                         {
1064                             *pScan++ = data[ nCurrPos+2 ];
1065                             *pScan++ = data[ nCurrPos+1 ];
1066                             *pScan++ = data[ nCurrPos   ];
1067 
1068                             nCurrPos += 4;
1069                         }
1070                     }
1071                     break;
1072 
1073                     case BMP_FORMAT_24BIT_TC_RGB:
1074                     {
1075                         Scanline pScan = pWriteAccess->GetScanline( y );
1076 
1077                         for( x=0; x<nWidth; ++x )
1078                         {
1079                             *pScan++ = data[ nCurrPos   ];
1080                             *pScan++ = data[ nCurrPos+1 ];
1081                             *pScan++ = data[ nCurrPos+2 ];
1082 
1083                             nCurrPos += 4;
1084                         }
1085                     }
1086                     break;
1087 
1088                     default:
1089                     {
1090                         for( x=0; x<nWidth; ++x )
1091                         {
1092                             pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos   ],
1093                                                                        data[ nCurrPos+1 ],
1094                                                                        data[ nCurrPos+2 ] ) );
1095                             nCurrPos += 4;
1096                         }
1097                     }
1098                     break;
1099                 }
1100             }
1101 
1102             bCopyBack = true;
1103         }
1104 
1105         // copy back only here, since the BitmapAccessors must be
1106         // destroyed beforehand
1107         if( bCopyBack )
1108         {
1109             // TODO(F2): Support alpha canvas here
1110             rOutDev.DrawBitmap(aRect.TopLeft(), aBitmap);
1111         }
1112     }
1113 
1114     void CanvasHelper::setPixel( const uno::Sequence< sal_Int8 >& 		color,
1115                                  const rendering::IntegerBitmapLayout&	rLayout,
1116                                  const geometry::IntegerPoint2D& 		pos )
1117     {
1118         if( !mpOutDev.get() )
1119             return; // we're disposed
1120 
1121         OutputDevice& rOutDev( mpOutDev->getOutDev() );
1122 
1123         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1124         rOutDev.EnableMapMode( sal_False );
1125 
1126         const Size aBmpSize( rOutDev.GetOutputSizePixel() );
1127 
1128         ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
1129                              "X coordinate out of bounds" );
1130         ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
1131                              "Y coordinate out of bounds" );
1132         ENSURE_ARG_OR_THROW( color.getLength() > 3,
1133                              "not enough color components" );
1134 
1135         const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() );
1136         ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride ||
1137                              aRefLayout.ColorSpace  != rLayout.ColorSpace ||
1138                              aRefLayout.Palette     != rLayout.Palette ||
1139                              aRefLayout.IsMsbFirst  != rLayout.IsMsbFirst,
1140                              "Mismatching memory layout" );
1141 
1142         // TODO(F2): Support alpha canvas here
1143         rOutDev.DrawPixel( ::vcl::unotools::pointFromIntegerPoint2D( pos ),
1144                            ::canvas::tools::stdIntSequenceToColor( color ));
1145     }
1146 
1147     uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& rLayout,
1148                                                       const geometry::IntegerPoint2D& pos )
1149     {
1150         if( !mpOutDev.get() )
1151             return uno::Sequence< sal_Int8 >(); // we're disposed
1152 
1153         rLayout = getMemoryLayout();
1154         rLayout.ScanLines = 1;
1155         rLayout.ScanLineBytes = 4;
1156         rLayout.ScanLineStride = rLayout.ScanLineBytes;
1157 
1158         OutputDevice& rOutDev( mpOutDev->getOutDev() );
1159 
1160         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1161         rOutDev.EnableMapMode( sal_False );
1162 
1163         const Size aBmpSize( rOutDev.GetOutputSizePixel() );
1164 
1165         ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
1166                              "X coordinate out of bounds" );
1167         ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
1168                              "Y coordinate out of bounds" );
1169 
1170         // TODO(F2): Support alpha canvas here
1171         return ::canvas::tools::colorToStdIntSequence(
1172             rOutDev.GetPixel(
1173                 ::vcl::unotools::pointFromIntegerPoint2D( pos )));
1174     }
1175 
1176     rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1177     {
1178         if( !mpOutDev.get() )
1179             return rendering::IntegerBitmapLayout(); // we're disposed
1180 
1181         return ::canvas::tools::getStdMemoryLayout(getSize());
1182     }
1183 
1184     int CanvasHelper::setupOutDevState( const rendering::ViewState& 	viewState,
1185                                         const rendering::RenderState& 	renderState,
1186                                         ColorType						eColorType ) const
1187     {
1188         ENSURE_OR_THROW( mpOutDev.get(),
1189                          "outdev null. Are we disposed?" );
1190 
1191         ::canvas::tools::verifyInput( renderState,
1192                                       BOOST_CURRENT_FUNCTION,
1193                                       mpDevice,
1194                                       2,
1195                                       eColorType == IGNORE_COLOR ? 0 : 3 );
1196 
1197         OutputDevice& rOutDev( mpOutDev->getOutDev() );
1198         OutputDevice* p2ndOutDev = NULL;
1199 
1200         rOutDev.EnableMapMode( sal_False );
1201 
1202         if( mp2ndOutDev )
1203             p2ndOutDev = &mp2ndOutDev->getOutDev();
1204 
1205         int nTransparency(0);
1206 
1207         // TODO(P2): Don't change clipping all the time, maintain current clip
1208         // state and change only when update is necessary
1209 
1210         // accumulate non-empty clips into one region
1211         // ==========================================
1212 
1213         Region aClipRegion( REGION_NULL );
1214 
1215         if( viewState.Clip.is() )
1216         {
1217             ::basegfx::B2DPolyPolygon aClipPoly(
1218                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(viewState.Clip) );
1219 
1220             if( aClipPoly.count() )
1221             {
1222                 // setup non-empty clipping
1223                 ::basegfx::B2DHomMatrix aMatrix;
1224                 aClipPoly.transform(
1225                     ::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix,
1226                                                                     viewState.AffineTransform ) );
1227 
1228                 aClipRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) );
1229             }
1230             else
1231             {
1232                 // clip polygon is empty
1233                 aClipRegion.SetEmpty();
1234             }
1235         }
1236 
1237         if( renderState.Clip.is() )
1238         {
1239             ::basegfx::B2DPolyPolygon aClipPoly(
1240                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(renderState.Clip) );
1241 
1242             ::basegfx::B2DHomMatrix aMatrix;
1243             aClipPoly.transform(
1244                 ::canvas::tools::mergeViewAndRenderTransform( aMatrix,
1245                                                               viewState,
1246                                                               renderState ) );
1247 
1248             if( aClipPoly.count() )
1249             {
1250                 // setup non-empty clipping
1251                 Region aRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) );
1252                 aClipRegion.Intersect( aRegion );
1253             }
1254             else
1255             {
1256                 // clip polygon is empty
1257                 aClipRegion.SetEmpty();
1258             }
1259         }
1260 
1261         // setup accumulated clip region. Note that setting an
1262         // empty clip region denotes "clip everything" on the
1263         // OutputDevice (which is why we translate that into
1264         // SetClipRegion() here). When both view and render clip
1265         // are empty, aClipRegion remains default-constructed,
1266         // i.e. empty, too.
1267         if( aClipRegion.IsNull() )
1268         {
1269             rOutDev.SetClipRegion();
1270 
1271             if( p2ndOutDev )
1272                 p2ndOutDev->SetClipRegion();
1273         }
1274         else
1275         {
1276             rOutDev.SetClipRegion( aClipRegion );
1277 
1278             if( p2ndOutDev )
1279                 p2ndOutDev->SetClipRegion( aClipRegion );
1280         }
1281 
1282         if( eColorType != IGNORE_COLOR )
1283         {
1284             Color aColor( COL_WHITE );
1285 
1286             if( renderState.DeviceColor.getLength() > 2 )
1287             {
1288                 aColor = ::vcl::unotools::stdColorSpaceSequenceToColor(
1289                     renderState.DeviceColor );
1290             }
1291 
1292             // extract alpha, and make color opaque
1293             // afterwards. Otherwise, OutputDevice won't draw anything
1294             nTransparency = aColor.GetTransparency();
1295             aColor.SetTransparency(0);
1296 
1297             switch( eColorType )
1298             {
1299                 case LINE_COLOR:
1300                     rOutDev.SetLineColor( aColor );
1301                     rOutDev.SetFillColor();
1302 
1303                     if( p2ndOutDev )
1304                     {
1305                         p2ndOutDev->SetLineColor( aColor );
1306                         p2ndOutDev->SetFillColor();
1307                     }
1308                     break;
1309 
1310                 case FILL_COLOR:
1311                     rOutDev.SetFillColor( aColor );
1312                     rOutDev.SetLineColor();
1313 
1314                     if( p2ndOutDev )
1315                     {
1316                         p2ndOutDev->SetFillColor( aColor );
1317                         p2ndOutDev->SetLineColor();
1318                     }
1319                     break;
1320 
1321                 case TEXT_COLOR:
1322                     rOutDev.SetTextColor( aColor );
1323 
1324                     if( p2ndOutDev )
1325                         p2ndOutDev->SetTextColor( aColor );
1326                     break;
1327 
1328                 default:
1329                     ENSURE_OR_THROW( false,
1330                                      "Unexpected color type");
1331                     break;
1332             }
1333         }
1334 
1335         return nTransparency;
1336     }
1337 
1338     bool CanvasHelper::setupTextOutput( ::Point&										o_rOutPos,
1339                                         const rendering::ViewState& 					viewState,
1340                                         const rendering::RenderState& 					renderState,
1341                                         const uno::Reference< rendering::XCanvasFont >&	xFont	) const
1342     {
1343         ENSURE_OR_THROW( mpOutDev.get(),
1344                          "outdev null. Are we disposed?" );
1345 
1346         setupOutDevState( viewState, renderState, TEXT_COLOR );
1347 
1348         OutputDevice& rOutDev( mpOutDev->getOutDev() );
1349 
1350         ::Font aVCLFont;
1351 
1352         CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
1353 
1354         ENSURE_ARG_OR_THROW( pFont,
1355                              "Font not compatible with this canvas" );
1356 
1357         aVCLFont = pFont->getVCLFont();
1358 
1359         Color aColor( COL_BLACK );
1360 
1361         if( renderState.DeviceColor.getLength() > 2 )
1362         {
1363             aColor = ::vcl::unotools::stdColorSpaceSequenceToColor(
1364                 renderState.DeviceColor );
1365         }
1366 
1367         // setup font color
1368         aVCLFont.SetColor( aColor );
1369         aVCLFont.SetFillColor( aColor );
1370 
1371         // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
1372         if( !tools::setupFontTransform( o_rOutPos, aVCLFont, viewState, renderState, rOutDev ) )
1373             return false;
1374 
1375         rOutDev.SetFont( aVCLFont );
1376 
1377         if( mp2ndOutDev )
1378             mp2ndOutDev->getOutDev().SetFont( aVCLFont );
1379 
1380         return true;
1381     }
1382 
1383     bool CanvasHelper::repaint( const GraphicObjectSharedPtr&	rGrf,
1384                                 const rendering::ViewState&     viewState,
1385                                 const rendering::RenderState&   renderState,
1386                                 const ::Point& 					rPt,
1387                                 const ::Size& 					rSz,
1388                                 const GraphicAttr&				rAttr ) const
1389     {
1390         ENSURE_OR_RETURN_FALSE( rGrf,
1391                           "Invalid Graphic" );
1392 
1393         if( !mpOutDev )
1394             return false; // disposed
1395         else
1396         {
1397             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev );
1398             setupOutDevState( viewState, renderState, IGNORE_COLOR );
1399 
1400             if( !rGrf->Draw( &mpOutDev->getOutDev(), rPt, rSz, &rAttr ) )
1401                 return false;
1402 
1403             // #i80779# Redraw also into mask outdev
1404             if( mp2ndOutDev )
1405                 return rGrf->Draw( &mp2ndOutDev->getOutDev(), rPt, rSz, &rAttr );
1406 
1407             return true;
1408         }
1409     }
1410 
1411     void CanvasHelper::flush() const
1412     {
1413         if( mpOutDev && mpOutDev->getOutDev().GetOutDevType() == OUTDEV_WINDOW )
1414         {
1415             // TODO(Q3): Evil downcast. And what's more, Window::Flush is
1416             // not even const. Wah.
1417             static_cast<Window&>(mpOutDev->getOutDev()).Flush();
1418         }
1419 
1420         if( mp2ndOutDev && mp2ndOutDev->getOutDev().GetOutDevType() == OUTDEV_WINDOW )
1421         {
1422             // TODO(Q3): Evil downcast. And what's more, Window::Flush is
1423             // not even const. Wah.
1424             static_cast<Window&>(mp2ndOutDev->getOutDev()).Flush();
1425         }
1426     }
1427 
1428 }
1429