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