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