1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <basegfx/matrix/b2dhommatrix.hxx>
23 #include <basegfx/numeric/ftools.hxx>
24 #include <basegfx/point/b2dpoint.hxx>
25 #include <basegfx/polygon/b2dlinegeometry.hxx>
26 #include <basegfx/polygon/b2dpolygon.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/polygon/b2dpolypolygontools.hxx>
29 #include <basegfx/range/b2drectangle.hxx>
30 #include <basegfx/utils/canvastools.hxx>
31 #include <basegfx/vector/b2dsize.hxx>
32 #include <com/sun/star/drawing/LineCap.hpp>
33 #include <com/sun/star/rendering/CompositeOperation.hpp>
34 #include <com/sun/star/rendering/PathCapType.hpp>
35 #include <com/sun/star/rendering/PathJoinType.hpp>
36 #include <com/sun/star/rendering/TextDirection.hpp>
37 #include <comphelper/sequence.hxx>
38 #include <rtl/math.hxx>
39 #include <tools/diagnose_ex.h>
40 #include <tools/poly.hxx>
41 #include <vcl/bitmapex.hxx>
42 #include <vcl/bitmapaccess.hxx>
43 #include <vcl/canvastools.hxx>
44 #include <vcl/BitmapAlphaClampFilter.hxx>
45 
46 #include <canvas/canvastools.hxx>
47 
48 #include "canvasbitmap.hxx"
49 #include "canvasfont.hxx"
50 #include "canvashelper.hxx"
51 #include "impltools.hxx"
52 #include "textlayout.hxx"
53 
54 
55 using namespace ::com::sun::star;
56 
57 namespace vclcanvas
58 {
59     namespace
60     {
b2DJoineFromJoin(sal_Int8 nJoinType)61         basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType )
62         {
63             switch( nJoinType )
64             {
65                 case rendering::PathJoinType::NONE:
66                     return basegfx::B2DLineJoin::NONE;
67 
68                 case rendering::PathJoinType::MITER:
69                     return basegfx::B2DLineJoin::Miter;
70 
71                 case rendering::PathJoinType::ROUND:
72                     return basegfx::B2DLineJoin::Round;
73 
74                 case rendering::PathJoinType::BEVEL:
75                     return basegfx::B2DLineJoin::Bevel;
76 
77                 default:
78                     ENSURE_OR_THROW( false,
79                                       "b2DJoineFromJoin(): Unexpected join type" );
80             }
81 
82             return basegfx::B2DLineJoin::NONE;
83         }
84 
unoCapeFromCap(sal_Int8 nCapType)85         drawing::LineCap unoCapeFromCap( sal_Int8 nCapType)
86         {
87             switch ( nCapType)
88             {
89                 case rendering::PathCapType::BUTT:
90                     return drawing::LineCap_BUTT;
91 
92                 case rendering::PathCapType::ROUND:
93                     return drawing::LineCap_ROUND;
94 
95                 case rendering::PathCapType::SQUARE:
96                     return drawing::LineCap_SQUARE;
97 
98                 default:
99                     ENSURE_OR_THROW( false,
100                                       "unoCapeFromCap(): Unexpected cap type" );
101             }
102             return drawing::LineCap_BUTT;
103         }
104     }
105 
CanvasHelper()106     CanvasHelper::CanvasHelper() :
107         mpDevice(),
108         mpProtectedOutDevProvider(),
109         mpOutDevProvider(),
110         mp2ndOutDevProvider(),
111         mbHaveAlpha( false )
112     {
113     }
114 
disposing()115     void CanvasHelper::disposing()
116     {
117         mpDevice = nullptr;
118         mpProtectedOutDevProvider.reset();
119         mpOutDevProvider.reset();
120         mp2ndOutDevProvider.reset();
121     }
122 
init(rendering::XGraphicDevice & rDevice,const OutDevProviderSharedPtr & rOutDev,bool bProtect,bool bHaveAlpha)123     void CanvasHelper::init( rendering::XGraphicDevice&     rDevice,
124                              const OutDevProviderSharedPtr& rOutDev,
125                              bool                           bProtect,
126                              bool                           bHaveAlpha )
127     {
128         // cast away const, need to change refcount (as this is
129         // ~invisible to client code, still logically const)
130         mpDevice    = &rDevice;
131         mbHaveAlpha = bHaveAlpha;
132 
133         setOutDev( rOutDev, bProtect );
134     }
135 
setOutDev(const OutDevProviderSharedPtr & rOutDev,bool bProtect)136     void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev,
137                                   bool                           bProtect )
138     {
139         if( bProtect )
140             mpProtectedOutDevProvider = rOutDev;
141         else
142             mpProtectedOutDevProvider.reset();
143 
144         mpOutDevProvider = rOutDev;
145     }
146 
setBackgroundOutDev(const OutDevProviderSharedPtr & rOutDev)147     void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev )
148     {
149         mp2ndOutDevProvider = rOutDev;
150         mp2ndOutDevProvider->getOutDev().EnableMapMode( false );
151         mp2ndOutDevProvider->getOutDev().SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
152     }
153 
clear()154     void CanvasHelper::clear()
155     {
156         // are we disposed?
157         if( mpOutDevProvider )
158         {
159             OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
160             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
161 
162             rOutDev.EnableMapMode( false );
163             rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
164             rOutDev.SetLineColor( COL_WHITE );
165             rOutDev.SetFillColor( COL_WHITE );
166             rOutDev.SetClipRegion();
167             rOutDev.DrawRect( ::tools::Rectangle( Point(),
168                                          rOutDev.GetOutputSizePixel()) );
169 
170             if( mp2ndOutDevProvider )
171             {
172                 OutputDevice& rOutDev2( mp2ndOutDevProvider->getOutDev() );
173 
174                 rOutDev2.SetDrawMode( DrawModeFlags::Default );
175                 rOutDev2.EnableMapMode( false );
176                 rOutDev2.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
177                 rOutDev2.SetLineColor( COL_WHITE );
178                 rOutDev2.SetFillColor( COL_WHITE );
179                 rOutDev2.SetClipRegion();
180                 rOutDev2.DrawRect( ::tools::Rectangle( Point(),
181                                               rOutDev2.GetOutputSizePixel()) );
182                 rOutDev2.SetDrawMode( DrawModeFlags::BlackLine | DrawModeFlags::BlackFill | DrawModeFlags::BlackText |
183                                       DrawModeFlags::BlackGradient | DrawModeFlags::BlackBitmap );
184             }
185         }
186     }
187 
drawLine(const rendering::XCanvas *,const geometry::RealPoint2D & aStartRealPoint2D,const geometry::RealPoint2D & aEndRealPoint2D,const rendering::ViewState & viewState,const rendering::RenderState & renderState)188     void CanvasHelper::drawLine( const rendering::XCanvas*      ,
189                                  const geometry::RealPoint2D&   aStartRealPoint2D,
190                                  const geometry::RealPoint2D&   aEndRealPoint2D,
191                                  const rendering::ViewState&    viewState,
192                                  const rendering::RenderState&  renderState )
193     {
194         // are we disposed?
195         if( mpOutDevProvider )
196         {
197             // nope, render
198             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
199             setupOutDevState( viewState, renderState, LINE_COLOR );
200 
201             const Point aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D,
202                                                             viewState, renderState ) );
203             const Point aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D,
204                                                           viewState, renderState ) );
205             // TODO(F2): alpha
206             mpOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint );
207 
208             if( mp2ndOutDevProvider )
209                 mp2ndOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint );
210         }
211     }
212 
drawBezier(const rendering::XCanvas *,const geometry::RealBezierSegment2D & aBezierSegment,const geometry::RealPoint2D & _aEndPoint,const rendering::ViewState & viewState,const rendering::RenderState & renderState)213     void CanvasHelper::drawBezier( const rendering::XCanvas*            ,
214                                    const geometry::RealBezierSegment2D& aBezierSegment,
215                                    const geometry::RealPoint2D&         _aEndPoint,
216                                    const rendering::ViewState&          viewState,
217                                    const rendering::RenderState&        renderState )
218     {
219         if( mpOutDevProvider )
220         {
221             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
222             setupOutDevState( viewState, renderState, LINE_COLOR );
223 
224             const Point& rStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px,
225                                                                                    aBezierSegment.Py),
226                                                             viewState, renderState ) );
227             const Point& rCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x,
228                                                                                    aBezierSegment.C1y),
229                                                             viewState, renderState ) );
230             const Point& rCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x,
231                                                                                    aBezierSegment.C2y),
232                                                              viewState, renderState ) );
233             const Point& rEndPoint( tools::mapRealPoint2D( _aEndPoint,
234                                                            viewState, renderState ) );
235 
236             ::tools::Polygon aPoly(4);
237             aPoly.SetPoint( rStartPoint, 0 );
238             aPoly.SetFlags( 0, PolyFlags::Normal );
239             aPoly.SetPoint( rCtrlPoint1, 1 );
240             aPoly.SetFlags( 1, PolyFlags::Control );
241             aPoly.SetPoint( rCtrlPoint2, 2 );
242             aPoly.SetFlags( 2, PolyFlags::Control );
243             aPoly.SetPoint( rEndPoint, 3 );
244             aPoly.SetFlags( 3, PolyFlags::Normal );
245 
246             // TODO(F2): alpha
247             mpOutDevProvider->getOutDev().DrawPolygon( aPoly );
248             if( mp2ndOutDevProvider )
249                 mp2ndOutDevProvider->getOutDev().DrawPolygon( aPoly );
250         }
251     }
252 
drawPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState)253     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas*                          ,
254                                                                                  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
255                                                                                  const rendering::ViewState&                        viewState,
256                                                                                  const rendering::RenderState&                      renderState )
257     {
258         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
259                          "polygon is NULL");
260 
261         if( mpOutDevProvider )
262         {
263             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
264             setupOutDevState( viewState, renderState, LINE_COLOR );
265 
266             const ::basegfx::B2DPolyPolygon& rPolyPoly(
267                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
268             const ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon( rPolyPoly, viewState, renderState ) );
269 
270             if( rPolyPoly.isClosed() )
271             {
272                 mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
273 
274                 if( mp2ndOutDevProvider )
275                     mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
276             }
277             else
278             {
279                 // mixed open/closed state. Cannot render open polygon
280                 // via DrawPolyPolygon(), since that implicitly
281                 // closed every polygon. OTOH, no need to distinguish
282                 // further and render closed polygons via
283                 // DrawPolygon(), and open ones via DrawPolyLine():
284                 // closed polygons will simply already contain the
285                 // closing segment.
286                 sal_uInt16 nSize( aPolyPoly.Count() );
287 
288                 for( sal_uInt16 i=0; i<nSize; ++i )
289                 {
290                     mpOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] );
291 
292                     if( mp2ndOutDevProvider )
293                         mp2ndOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] );
294                 }
295             }
296         }
297 
298         // TODO(P1): Provide caching here.
299         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
300     }
301 
strokePolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const rendering::StrokeAttributes & strokeAttributes)302     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas*                            ,
303                                                                                    const uno::Reference< rendering::XPolyPolygon2D >&   xPolyPolygon,
304                                                                                    const rendering::ViewState&                          viewState,
305                                                                                    const rendering::RenderState&                        renderState,
306                                                                                    const rendering::StrokeAttributes&                   strokeAttributes )
307     {
308         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
309                          "polygon is NULL");
310 
311         if( mpOutDevProvider )
312         {
313             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
314 
315             ::basegfx::B2DHomMatrix aMatrix;
316             ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
317 
318             ::basegfx::B2DSize aLinePixelSize(strokeAttributes.StrokeWidth,
319                                               strokeAttributes.StrokeWidth);
320             aLinePixelSize *= aMatrix;
321 
322             ::basegfx::B2DPolyPolygon aPolyPoly(
323                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
324 
325             if( aPolyPoly.areControlPointsUsed() )
326             {
327                 // AW: Not needed for ApplyLineDashing anymore; should be removed
328                 aPolyPoly = ::basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly);
329             }
330 
331             // apply dashing, if any
332             if( strokeAttributes.DashArray.hasElements() )
333             {
334                 const std::vector<double>& aDashArray(
335                     ::comphelper::sequenceToContainer< std::vector<double> >(strokeAttributes.DashArray) );
336 
337                 ::basegfx::B2DPolyPolygon aDashedPolyPoly;
338 
339                 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
340                 {
341                     // AW: new interface; You may also get gaps in the same run now
342                     basegfx::utils::applyLineDashing(aPolyPoly.getB2DPolygon(i), aDashArray, &aDashedPolyPoly);
343                     //aDashedPolyPoly.append(
344                     //    ::basegfx::utils::applyLineDashing( aPolyPoly.getB2DPolygon(i),
345                     //                                        aDashArray ) );
346                 }
347 
348                 aPolyPoly = aDashedPolyPoly;
349             }
350 
351             ::basegfx::B2DPolyPolygon aStrokedPolyPoly;
352             if( aLinePixelSize.getLength() < 1.42 )
353             {
354                 // line width < 1.0 in device pixel, thus, output as a
355                 // simple hairline poly-polygon
356                 setupOutDevState( viewState, renderState, LINE_COLOR );
357 
358                 aStrokedPolyPoly = aPolyPoly;
359             }
360             else
361             {
362                 // render as a 'thick' line
363                 setupOutDevState( viewState, renderState, FILL_COLOR );
364 
365                 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
366                 {
367                     double fMiterMinimumAngle;
368                     if (strokeAttributes.MiterLimit <= 1.0)
369                     {
370                         fMiterMinimumAngle = F_PI2;
371                     }
372                     else
373                     {
374                         fMiterMinimumAngle = 2.0 * asin(1.0/strokeAttributes.MiterLimit);
375                     }
376 
377                     // TODO(F2): Also use Cap settings from
378                     // StrokeAttributes, the
379                     // createAreaGeometryForLineStartEnd() method does not
380                     // seem to fit very well here
381 
382                     // AW: New interface, will create bezier polygons now
383                     aStrokedPolyPoly.append(basegfx::utils::createAreaGeometry(
384                         aPolyPoly.getB2DPolygon(i),
385                         strokeAttributes.StrokeWidth*0.5,
386                         b2DJoineFromJoin(strokeAttributes.JoinType),
387                         unoCapeFromCap(strokeAttributes.StartCapType),
388                         basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/ ,
389                         0.4 /* default fMaxPartOfEdge*/ ,
390                         fMiterMinimumAngle
391                         ));
392                     //aStrokedPolyPoly.append(
393                     //    ::basegfx::utils::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i),
394                     //                                                    strokeAttributes.StrokeWidth*0.5,
395                     //                                                    b2DJoineFromJoin(strokeAttributes.JoinType) ) );
396                 }
397             }
398 
399             // transform only _now_, all the StrokeAttributes are in
400             // user coordinates.
401             aStrokedPolyPoly.transform( aMatrix );
402 
403             const ::tools::PolyPolygon aVCLPolyPoly( aStrokedPolyPoly );
404 
405             // TODO(F2): When using alpha here, must handle that via
406             // temporary surface or somesuch.
407 
408             // Note: the generated stroke poly-polygon is NOT free of
409             // self-intersections. Therefore, if we would render it
410             // via OutDev::DrawPolyPolygon(), on/off fill would
411             // generate off areas on those self-intersections.
412             sal_uInt16 nSize( aVCLPolyPoly.Count() );
413 
414             for( sal_uInt16 i=0; i<nSize; ++i )
415             {
416                 if( aStrokedPolyPoly.getB2DPolygon( i ).isClosed() ) {
417                     mpOutDevProvider->getOutDev().DrawPolygon( aVCLPolyPoly[i] );
418                     if( mp2ndOutDevProvider )
419                         mp2ndOutDevProvider->getOutDev().DrawPolygon( aVCLPolyPoly[i] );
420                 } else {
421                     const sal_uInt16 nPolySize = aVCLPolyPoly[i].GetSize();
422                     if( nPolySize ) {
423                         Point rPrevPoint = aVCLPolyPoly[i].GetPoint( 0 );
424                         Point rPoint;
425 
426                         for( sal_uInt16 j=1; j<nPolySize; j++ ) {
427                             rPoint = aVCLPolyPoly[i].GetPoint( j );
428                             mpOutDevProvider->getOutDev().DrawLine( rPrevPoint, rPoint );
429                             if( mp2ndOutDevProvider )
430                                 mp2ndOutDevProvider->getOutDev().DrawLine( rPrevPoint, rPoint );
431                             rPrevPoint = rPoint;
432                         }
433                     }
434                 }
435             }
436         }
437 
438         // TODO(P1): Provide caching here.
439         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
440     }
441 
strokeTexturedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const rendering::StrokeAttributes &)442     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas*                            ,
443                                                                                            const uno::Reference< rendering::XPolyPolygon2D >&   ,
444                                                                                            const rendering::ViewState&                          ,
445                                                                                            const rendering::RenderState&                        ,
446                                                                                            const uno::Sequence< rendering::Texture >&           ,
447                                                                                            const rendering::StrokeAttributes&                    )
448     {
449         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
450     }
451 
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 &)452     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas*                           ,
453                                                                                                 const uno::Reference< rendering::XPolyPolygon2D >&  ,
454                                                                                                 const rendering::ViewState&                         ,
455                                                                                                 const rendering::RenderState&                       ,
456                                                                                                 const uno::Sequence< rendering::Texture >&          ,
457                                                                                                 const uno::Reference< geometry::XMapping2D >&       ,
458                                                                                                 const rendering::StrokeAttributes&                   )
459     {
460         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
461     }
462 
queryStrokeShapes(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const rendering::StrokeAttributes &)463     uno::Reference< rendering::XPolyPolygon2D >   CanvasHelper::queryStrokeShapes( const rendering::XCanvas*                            ,
464                                                                                    const uno::Reference< rendering::XPolyPolygon2D >&   ,
465                                                                                    const rendering::ViewState&                          ,
466                                                                                    const rendering::RenderState&                        ,
467                                                                                    const rendering::StrokeAttributes&                    )
468     {
469         return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
470     }
471 
fillPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState)472     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas*                          ,
473                                                                                  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
474                                                                                  const rendering::ViewState&                        viewState,
475                                                                                  const rendering::RenderState&                      renderState )
476     {
477         ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
478                          "polygon is NULL");
479 
480         if( mpOutDevProvider )
481         {
482             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
483 
484             const int nTransparency( setupOutDevState( viewState, renderState, FILL_COLOR ) );
485             ::basegfx::B2DPolyPolygon aB2DPolyPoly(
486                 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
487             aB2DPolyPoly.setClosed(true); // ensure closed poly, otherwise VCL does not fill
488             const ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon(
489                                              aB2DPolyPoly,
490                                              viewState, renderState ) );
491             const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE );
492             if( !nTransparency || bSourceAlpha )
493             {
494                 mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
495             }
496             else
497             {
498                 const int nTransPercent( (nTransparency * 100 + 128) / 255 );  // normal rounding, no truncation here
499                 mpOutDevProvider->getOutDev().DrawTransparent( aPolyPoly, static_cast<sal_uInt16>(nTransPercent) );
500             }
501 
502             if( mp2ndOutDevProvider )
503             {
504                 // HACK. Normally, CanvasHelper does not care about
505                 // actually what mp2ndOutDev is...  well, here we do &
506                 // assume a 1bpp target - everything beyond 97%
507                 // transparency is fully transparent
508                 if( nTransparency < 253 )
509                 {
510                     mp2ndOutDevProvider->getOutDev().SetFillColor( COL_BLACK );
511                     mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
512                 }
513             }
514         }
515 
516         // TODO(P1): Provide caching here.
517         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
518     }
519 
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> &)520     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas*                             ,
521                                                                                               const uno::Reference< rendering::XPolyPolygon2D >&    ,
522                                                                                               const rendering::ViewState&                           ,
523                                                                                               const rendering::RenderState&                         ,
524                                                                                               const uno::Sequence< rendering::Texture >&            ,
525                                                                                               const uno::Reference< geometry::XMapping2D >&              )
526     {
527         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
528     }
529 
createFont(const rendering::XCanvas *,const rendering::FontRequest & fontRequest,const uno::Sequence<beans::PropertyValue> & extraFontProperties,const geometry::Matrix2D & fontMatrix)530     uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas*                        ,
531                                                                        const rendering::FontRequest&                    fontRequest,
532                                                                        const uno::Sequence< beans::PropertyValue >&     extraFontProperties,
533                                                                        const geometry::Matrix2D&                        fontMatrix )
534     {
535         if( mpOutDevProvider && mpDevice )
536         {
537             // TODO(F2): font properties and font matrix
538             return uno::Reference< rendering::XCanvasFont >(
539                     new CanvasFont(fontRequest, extraFontProperties, fontMatrix,
540                                    *mpDevice, mpOutDevProvider) );
541         }
542 
543         return uno::Reference< rendering::XCanvasFont >();
544     }
545 
queryAvailableFonts(const rendering::XCanvas *,const rendering::FontInfo &,const uno::Sequence<beans::PropertyValue> &)546     uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas*                       ,
547                                                                             const rendering::FontInfo&                      ,
548                                                                             const uno::Sequence< beans::PropertyValue >&     )
549     {
550         // TODO(F2)
551         return uno::Sequence< rendering::FontInfo >();
552     }
553 
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)554     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas*                         ,
555                                                                           const rendering::StringContext&                   text,
556                                                                           const uno::Reference< rendering::XCanvasFont >&   xFont,
557                                                                           const rendering::ViewState&                       viewState,
558                                                                           const rendering::RenderState&                     renderState,
559                                                                           sal_Int8                                          textDirection )
560     {
561         ENSURE_ARG_OR_THROW( xFont.is(),
562                          "font is NULL");
563 
564         if( mpOutDevProvider )
565         {
566             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
567 
568             ::Point aOutpos;
569             if( !setupTextOutput( aOutpos, viewState, renderState, xFont ) )
570                 return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
571 
572             // change text direction and layout mode
573             ComplexTextLayoutFlags nLayoutMode(ComplexTextLayoutFlags::Default);
574             switch( textDirection )
575             {
576                 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
577                 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
578                     nLayoutMode |= ComplexTextLayoutFlags::BiDiStrong;
579                     nLayoutMode |= ComplexTextLayoutFlags::TextOriginLeft;
580                     break;
581 
582                 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
583                     nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl;
584                     [[fallthrough]];
585                 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
586                     nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::BiDiStrong;
587                     nLayoutMode |= ComplexTextLayoutFlags::TextOriginRight;
588                     break;
589             }
590 
591             // TODO(F2): alpha
592             mpOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode );
593             mpOutDevProvider->getOutDev().DrawText( aOutpos,
594                                             text.Text,
595                                             ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
596                                             ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
597 
598             if( mp2ndOutDevProvider )
599             {
600                 mp2ndOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode );
601                 mp2ndOutDevProvider->getOutDev().DrawText( aOutpos,
602                                                    text.Text,
603                                                    ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
604                                                    ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
605             }
606         }
607 
608         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
609     }
610 
drawTextLayout(const rendering::XCanvas *,const uno::Reference<rendering::XTextLayout> & xLayoutedText,const rendering::ViewState & viewState,const rendering::RenderState & renderState)611     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas*                       ,
612                                                                                 const uno::Reference< rendering::XTextLayout >& xLayoutedText,
613                                                                                 const rendering::ViewState&                     viewState,
614                                                                                 const rendering::RenderState&                   renderState )
615     {
616         ENSURE_ARG_OR_THROW( xLayoutedText.is(),
617                          "layout is NULL");
618 
619         TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
620 
621         if( pTextLayout )
622         {
623             if( mpOutDevProvider )
624             {
625                 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
626 
627                 // TODO(T3): Race condition. We're taking the font
628                 // from xLayoutedText, and then calling draw() at it,
629                 // without exclusive access. Move setupTextOutput(),
630                 // e.g. to impltools?
631 
632                 ::Point aOutpos;
633                 if( !setupTextOutput( aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
634                     return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
635 
636                 // TODO(F2): What about the offset scalings?
637                 // TODO(F2): alpha
638                 pTextLayout->draw( mpOutDevProvider->getOutDev(), aOutpos, viewState, renderState );
639 
640                 if( mp2ndOutDevProvider )
641                     pTextLayout->draw( mp2ndOutDevProvider->getOutDev(), aOutpos, viewState, renderState );
642             }
643         }
644         else
645         {
646             ENSURE_ARG_OR_THROW( false,
647                                  "TextLayout not compatible with this canvas" );
648         }
649 
650         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
651     }
652 
implDrawBitmap(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState,bool bModulateColors)653     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmap( const rendering::XCanvas*                   pCanvas,
654                                                                                 const uno::Reference< rendering::XBitmap >& xBitmap,
655                                                                                 const rendering::ViewState&                 viewState,
656                                                                                 const rendering::RenderState&               renderState,
657                                                                                 bool                                        bModulateColors )
658     {
659         ENSURE_ARG_OR_THROW( xBitmap.is(),
660                              "bitmap is NULL");
661 
662         ::canvas::tools::verifyInput( renderState,
663                                       OSL_THIS_FUNC,
664                                       mpDevice,
665                                       4,
666                                       bModulateColors ? 3 : 0 );
667 
668         if( mpOutDevProvider )
669         {
670             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
671             setupOutDevState( viewState, renderState, IGNORE_COLOR );
672 
673             ::basegfx::B2DHomMatrix aMatrix;
674             ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
675 
676             ::basegfx::B2DPoint aOutputPos( 0.0, 0.0 );
677             aOutputPos *= aMatrix;
678 
679             BitmapEx aBmpEx( tools::bitmapExFromXBitmap(xBitmap) );
680 
681             // TODO(F2): Implement modulation again for other color
682             // channels (currently, works only for alpha). Note: this
683             // is already implemented in transformBitmap()
684             if( bModulateColors &&
685                 renderState.DeviceColor.getLength() > 3 )
686             {
687                 // optimize away the case where alpha modulation value
688                 // is 1.0 - we then simply switch off modulation at all
689                 bModulateColors = !::rtl::math::approxEqual(
690                     renderState.DeviceColor[3], 1.0);
691             }
692 
693             // check whether we can render bitmap as-is: must not
694             // modulate colors, matrix must either be the identity
695             // transform (that's clear), _or_ contain only
696             // translational components.
697             if( !bModulateColors &&
698                 (aMatrix.isIdentity() ||
699                  (::basegfx::fTools::equalZero( aMatrix.get(0,1) ) &&
700                   ::basegfx::fTools::equalZero( aMatrix.get(1,0) ) &&
701                   ::rtl::math::approxEqual(aMatrix.get(0,0), 1.0) &&
702                   ::rtl::math::approxEqual(aMatrix.get(1,1), 1.0)) ) )
703             {
704                 // optimized case: identity matrix, or only
705                 // translational components.
706                 mpOutDevProvider->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos ),
707                                                     aBmpEx );
708 
709                 if( mp2ndOutDevProvider )
710                 {
711                     // HACK. Normally, CanvasHelper does not care about
712                     // actually what mp2ndOutDev is...  well, here we do &
713                     // assume a 1bpp target - everything beyond 97%
714                     // transparency is fully transparent
715                     if( aBmpEx.IsAlpha() )
716                     {
717                         BitmapFilter::Filter(aBmpEx, BitmapAlphaClampFilter(253));
718                     }
719 
720                     mp2ndOutDevProvider->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos ),
721                                                            aBmpEx );
722                 }
723 
724                 // Returning a cache object is not useful, the XBitmap
725                 // itself serves this purpose
726                 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
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 
802                     pGrfObj.reset( new GraphicObject( aBmpEx ) );
803 
804                     // clear scale values, generated bitmap already
805                     // contains scaling
806                     aScale.setX( 1.0 ); aScale.setY( 1.0 );
807 
808                     // update bitmap size, bitmap has changed above.
809                     aBmpSize = aBmpEx.GetSizePixel();
810                 }
811 
812                 // output GraphicObject
813                 const ::Point aPt( vcl::unotools::pointFromB2DPoint( aOutputPos ) );
814                 const ::Size  aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width() ),
815                                    ::basegfx::fround( aScale.getY() * aBmpSize.Height() ) );
816 
817                 pGrfObj->Draw( &mpOutDevProvider->getOutDev(),
818                                aPt,
819                                aSz,
820                                &aGrfAttr );
821 
822                 if( mp2ndOutDevProvider )
823                     pGrfObj->Draw( &mp2ndOutDevProvider->getOutDev(),
824                                    aPt,
825                                    aSz,
826                                    &aGrfAttr );
827 
828                 // created GraphicObject, which possibly cached
829                 // display bitmap - return cache object, to retain
830                 // that information.
831                 return uno::Reference< rendering::XCachedPrimitive >(
832                     new CachedBitmap( pGrfObj,
833                                       aPt,
834                                       aSz,
835                                       aGrfAttr,
836                                       viewState,
837                                       renderState,
838                                       // cast away const, need to
839                                       // change refcount (as this is
840                                       // ~invisible to client code,
841                                       // still logically const)
842                                       const_cast< rendering::XCanvas* >(pCanvas)) );
843             }
844         }
845 
846         // Nothing rendered
847         return uno::Reference< rendering::XCachedPrimitive >(nullptr);
848     }
849 
drawBitmap(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState)850     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas*                   pCanvas,
851                                                                             const uno::Reference< rendering::XBitmap >& xBitmap,
852                                                                             const rendering::ViewState&                 viewState,
853                                                                             const rendering::RenderState&               renderState )
854     {
855         return implDrawBitmap( pCanvas,
856                                xBitmap,
857                                viewState,
858                                renderState,
859                                false );
860     }
861 
drawBitmapModulated(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState)862     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas*                      pCanvas,
863                                                                                      const uno::Reference< rendering::XBitmap >&    xBitmap,
864                                                                                      const rendering::ViewState&                    viewState,
865                                                                                      const rendering::RenderState&                  renderState )
866     {
867         return implDrawBitmap( pCanvas,
868                                xBitmap,
869                                viewState,
870                                renderState,
871                                true );
872     }
873 
getSize()874     geometry::IntegerSize2D CanvasHelper::getSize()
875     {
876         if( !mpOutDevProvider.get() )
877             return geometry::IntegerSize2D(); // we're disposed
878 
879         return vcl::unotools::integerSize2DFromSize( mpOutDevProvider->getOutDev().GetOutputSizePixel() );
880     }
881 
getScaledBitmap(const geometry::RealSize2D & newSize,bool beFast)882     uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
883                                                                         bool                        beFast )
884     {
885         if( !mpOutDevProvider.get() || !mpDevice )
886             return uno::Reference< rendering::XBitmap >(); // we're disposed
887 
888         OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
889 
890         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
891         rOutDev.EnableMapMode( false );
892         rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
893 
894         // TODO(F2): Support alpha vdev canvas here
895         const Point aEmptyPoint(0,0);
896         const Size  aBmpSize( rOutDev.GetOutputSizePixel() );
897 
898         BitmapEx aBitmap( rOutDev.GetBitmapEx(aEmptyPoint, aBmpSize) );
899 
900         aBitmap.Scale( vcl::unotools::sizeFromRealSize2D(newSize),
901                        beFast ? BmpScaleFlag::Default : BmpScaleFlag::BestQuality );
902 
903         return uno::Reference< rendering::XBitmap >(
904             new CanvasBitmap( aBitmap, *mpDevice, mpOutDevProvider ) );
905     }
906 
getData(rendering::IntegerBitmapLayout & rLayout,const geometry::IntegerRectangle2D & rect)907     uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout&     rLayout,
908                                                      const geometry::IntegerRectangle2D& rect )
909     {
910         if( !mpOutDevProvider.get() )
911             return uno::Sequence< sal_Int8 >(); // we're disposed
912 
913         rLayout = getMemoryLayout();
914 
915         // TODO(F2): Support alpha canvas here
916         const ::tools::Rectangle aRect( vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
917 
918         OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
919 
920         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
921         rOutDev.EnableMapMode( false );
922         rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
923 
924         Bitmap aBitmap( rOutDev.GetBitmapEx(aRect.TopLeft(),
925                                           aRect.GetSize()).GetBitmap() );
926 
927         Bitmap::ScopedReadAccess pReadAccess( aBitmap );
928 
929         ENSURE_OR_THROW( pReadAccess.get() != nullptr,
930                          "Could not acquire read access to OutDev bitmap" );
931 
932         const sal_Int32 nWidth( rect.X2 - rect.X1 );
933         const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
934 
935         rLayout.ScanLines = nHeight;
936         rLayout.ScanLineBytes = nWidth*4;
937         rLayout.ScanLineStride = rLayout.ScanLineBytes;
938 
939         uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
940         sal_Int8* pRes = aRes.getArray();
941 
942         int nCurrPos(0);
943         for( int y=0; y<nHeight; ++y )
944         {
945             for( int x=0; x<nWidth; ++x )
946             {
947                 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
948                 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
949                 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
950                 pRes[ nCurrPos++ ] = -1;
951             }
952         }
953 
954         return aRes;
955     }
956 
getPixel(rendering::IntegerBitmapLayout & rLayout,const geometry::IntegerPoint2D & pos)957     uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& rLayout,
958                                                       const geometry::IntegerPoint2D& pos )
959     {
960         if( !mpOutDevProvider.get() )
961             return uno::Sequence< sal_Int8 >(); // we're disposed
962 
963         rLayout = getMemoryLayout();
964         rLayout.ScanLines = 1;
965         rLayout.ScanLineBytes = 4;
966         rLayout.ScanLineStride = rLayout.ScanLineBytes;
967 
968         OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
969 
970         tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
971         rOutDev.EnableMapMode( false );
972         rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
973 
974         const Size aBmpSize( rOutDev.GetOutputSizePixel() );
975 
976         ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
977                              "X coordinate out of bounds" );
978         ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
979                              "Y coordinate out of bounds" );
980 
981         // TODO(F2): Support alpha canvas here
982         return ::canvas::tools::colorToStdIntSequence(
983             rOutDev.GetPixel(
984                 vcl::unotools::pointFromIntegerPoint2D( pos )));
985     }
986 
getMemoryLayout()987     rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
988     {
989         if( !mpOutDevProvider.get() )
990             return rendering::IntegerBitmapLayout(); // we're disposed
991 
992         rendering::IntegerBitmapLayout aBitmapLayout( ::canvas::tools::getStdMemoryLayout(getSize()) );
993         if ( !mbHaveAlpha )
994             aBitmapLayout.ColorSpace = canvas::tools::getStdColorSpaceWithoutAlpha();
995 
996         return aBitmapLayout;
997     }
998 
setupOutDevState(const rendering::ViewState & viewState,const rendering::RenderState & renderState,ColorType eColorType) const999     int CanvasHelper::setupOutDevState( const rendering::ViewState&     viewState,
1000                                         const rendering::RenderState&   renderState,
1001                                         ColorType                       eColorType ) const
1002     {
1003         ENSURE_OR_THROW( mpOutDevProvider.get(),
1004                          "outdev null. Are we disposed?" );
1005 
1006         ::canvas::tools::verifyInput( renderState,
1007                                       OSL_THIS_FUNC,
1008                                       mpDevice,
1009                                       2,
1010                                       eColorType == IGNORE_COLOR ? 0 : 3 );
1011 
1012         OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
1013         OutputDevice* p2ndOutDev = nullptr;
1014 
1015         rOutDev.EnableMapMode( false );
1016         rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
1017 
1018         if( mp2ndOutDevProvider )
1019             p2ndOutDev = &mp2ndOutDevProvider->getOutDev();
1020 
1021         int nTransparency(0);
1022 
1023         // TODO(P2): Don't change clipping all the time, maintain current clip
1024         // state and change only when update is necessary
1025         ::canvas::tools::clipOutDev(viewState, renderState, rOutDev, p2ndOutDev);
1026 
1027         Color aColor( COL_WHITE );
1028 
1029         if( renderState.DeviceColor.getLength() > 2 )
1030         {
1031             aColor = vcl::unotools::stdColorSpaceSequenceToColor(
1032                 renderState.DeviceColor );
1033         }
1034 
1035         // extract alpha, and make color opaque
1036         // afterwards. Otherwise, OutputDevice won't draw anything
1037         nTransparency = aColor.GetTransparency();
1038         aColor.SetTransparency(0);
1039 
1040         if( eColorType != IGNORE_COLOR )
1041         {
1042             switch( eColorType )
1043             {
1044                 case LINE_COLOR:
1045                     rOutDev.SetLineColor( aColor );
1046                     rOutDev.SetFillColor();
1047 
1048                     if( p2ndOutDev )
1049                     {
1050                         p2ndOutDev->SetLineColor( aColor );
1051                         p2ndOutDev->SetFillColor();
1052                     }
1053                     break;
1054 
1055                 case FILL_COLOR:
1056                     rOutDev.SetFillColor( aColor );
1057                     rOutDev.SetLineColor();
1058 
1059                     if( p2ndOutDev )
1060                     {
1061                         p2ndOutDev->SetFillColor( aColor );
1062                         p2ndOutDev->SetLineColor();
1063                     }
1064                     break;
1065 
1066                 case TEXT_COLOR:
1067                     rOutDev.SetTextColor( aColor );
1068 
1069                     if( p2ndOutDev )
1070                         p2ndOutDev->SetTextColor( aColor );
1071                     break;
1072 
1073                 default:
1074                     ENSURE_OR_THROW( false,
1075                                      "Unexpected color type");
1076                     break;
1077             }
1078         }
1079 
1080         return nTransparency;
1081     }
1082 
setupTextOutput(::Point & o_rOutPos,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const uno::Reference<rendering::XCanvasFont> & xFont) const1083     bool CanvasHelper::setupTextOutput( ::Point&                                        o_rOutPos,
1084                                         const rendering::ViewState&                     viewState,
1085                                         const rendering::RenderState&                   renderState,
1086                                         const uno::Reference< rendering::XCanvasFont >& xFont   ) const
1087     {
1088         ENSURE_OR_THROW( mpOutDevProvider.get(),
1089                          "outdev null. Are we disposed?" );
1090 
1091         OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
1092 
1093         setupOutDevState( viewState, renderState, TEXT_COLOR );
1094 
1095         CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
1096 
1097         ENSURE_ARG_OR_THROW( pFont,
1098                              "Font not compatible with this canvas" );
1099 
1100         vcl::Font aVCLFont = pFont->getVCLFont();
1101 
1102         Color aColor( COL_BLACK );
1103 
1104         if( renderState.DeviceColor.getLength() > 2 )
1105         {
1106             aColor = vcl::unotools::stdColorSpaceSequenceToColor(
1107                 renderState.DeviceColor );
1108         }
1109 
1110         // setup font color
1111         aVCLFont.SetColor( aColor );
1112         aVCLFont.SetFillColor( aColor );
1113 
1114         // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
1115         if( !tools::setupFontTransform( o_rOutPos, aVCLFont, viewState, renderState, rOutDev ) )
1116             return false;
1117 
1118         rOutDev.SetFont( aVCLFont );
1119 
1120         if( mp2ndOutDevProvider )
1121             mp2ndOutDevProvider->getOutDev().SetFont( aVCLFont );
1122 
1123         return true;
1124     }
1125 
repaint(const GraphicObjectSharedPtr & rGrf,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const::Point & rPt,const::Size & rSz,const GraphicAttr & rAttr) const1126     bool CanvasHelper::repaint( const GraphicObjectSharedPtr&   rGrf,
1127                                 const rendering::ViewState&     viewState,
1128                                 const rendering::RenderState&   renderState,
1129                                 const ::Point&                  rPt,
1130                                 const ::Size&                   rSz,
1131                                 const GraphicAttr&              rAttr ) const
1132     {
1133         ENSURE_OR_RETURN_FALSE( rGrf,
1134                           "Invalid Graphic" );
1135 
1136         if( !mpOutDevProvider )
1137             return false; // disposed
1138         else
1139         {
1140             tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
1141             setupOutDevState( viewState, renderState, IGNORE_COLOR );
1142 
1143             if( !rGrf->Draw( &mpOutDevProvider->getOutDev(), rPt, rSz, &rAttr ) )
1144                 return false;
1145 
1146             // #i80779# Redraw also into mask outdev
1147             if( mp2ndOutDevProvider )
1148                 return rGrf->Draw( &mp2ndOutDevProvider->getOutDev(), rPt, rSz, &rAttr );
1149 
1150             return true;
1151         }
1152     }
1153 
flush() const1154     void CanvasHelper::flush() const
1155     {
1156         if (mpOutDevProvider)
1157             mpOutDevProvider->getOutDev().Flush();
1158 
1159         if  (mp2ndOutDevProvider)
1160             mp2ndOutDevProvider->getOutDev().Flush();
1161     }
1162 
1163 }
1164 
1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1166