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