1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
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/range/b2drectangle.hxx>
29 #include <basegfx/utils/canvastools.hxx>
30 #include <basegfx/vector/b2dsize.hxx>
31 #include <com/sun/star/drawing/LineCap.hpp>
32 #include <com/sun/star/rendering/CompositeOperation.hpp>
33 #include <com/sun/star/rendering/PathCapType.hpp>
34 #include <com/sun/star/rendering/PathJoinType.hpp>
35 #include <com/sun/star/rendering/TextDirection.hpp>
36 #include <comphelper/sequence.hxx>
37 #include <rtl/math.hxx>
38 #include <comphelper/diagnose_ex.hxx>
39 #include <tools/poly.hxx>
40 #include <vcl/bitmapex.hxx>
41 #include <vcl/BitmapReadAccess.hxx>
42 #include <vcl/canvastools.hxx>
43 #include <vcl/bitmap/BitmapAlphaClampFilter.hxx>
44 #include <vcl/skia/SkiaHelper.hxx>
46 #include <canvas/canvastools.hxx>
48 #include "canvasbitmap.hxx"
49 #include "canvasfont.hxx"
50 #include "canvashelper.hxx"
51 #include "impltools.hxx"
52 #include "textlayout.hxx"
55 using namespace ::com::sun::star
;
61 basegfx::B2DLineJoin
b2DJoineFromJoin( sal_Int8 nJoinType
)
65 case rendering::PathJoinType::NONE
:
66 return basegfx::B2DLineJoin::NONE
;
68 case rendering::PathJoinType::MITER
:
69 return basegfx::B2DLineJoin::Miter
;
71 case rendering::PathJoinType::ROUND
:
72 return basegfx::B2DLineJoin::Round
;
74 case rendering::PathJoinType::BEVEL
:
75 return basegfx::B2DLineJoin::Bevel
;
78 ENSURE_OR_THROW( false,
79 "b2DJoineFromJoin(): Unexpected join type" );
82 return basegfx::B2DLineJoin::NONE
;
85 drawing::LineCap
unoCapeFromCap( sal_Int8 nCapType
)
89 case rendering::PathCapType::BUTT
:
90 return drawing::LineCap_BUTT
;
92 case rendering::PathCapType::ROUND
:
93 return drawing::LineCap_ROUND
;
95 case rendering::PathCapType::SQUARE
:
96 return drawing::LineCap_SQUARE
;
99 ENSURE_OR_THROW( false,
100 "unoCapeFromCap(): Unexpected cap type" );
102 return drawing::LineCap_BUTT
;
106 CanvasHelper::CanvasHelper() :
112 void CanvasHelper::disposing()
115 mpProtectedOutDevProvider
.reset();
116 mpOutDevProvider
.reset();
117 mp2ndOutDevProvider
.reset();
120 void CanvasHelper::init( rendering::XGraphicDevice
& rDevice
,
121 const OutDevProviderSharedPtr
& rOutDev
,
125 // cast away const, need to change refcount (as this is
126 // ~invisible to client code, still logically const)
128 mbHaveAlpha
= bHaveAlpha
;
130 setOutDev( rOutDev
, bProtect
);
133 void CanvasHelper::setOutDev( const OutDevProviderSharedPtr
& rOutDev
,
137 mpProtectedOutDevProvider
= rOutDev
;
139 mpProtectedOutDevProvider
.reset();
141 mpOutDevProvider
= rOutDev
;
144 void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr
& rOutDev
)
146 mp2ndOutDevProvider
= rOutDev
;
147 mp2ndOutDevProvider
->getOutDev().EnableMapMode( false );
148 mp2ndOutDevProvider
->getOutDev().SetAntialiasing( AntialiasingFlags::Enable
);
151 void CanvasHelper::clear()
154 if( !mpOutDevProvider
)
157 OutputDevice
& rOutDev( mpOutDevProvider
->getOutDev() );
158 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
160 rOutDev
.EnableMapMode( false );
161 rOutDev
.SetAntialiasing( AntialiasingFlags::Enable
);
162 rOutDev
.SetLineColor( COL_WHITE
);
163 rOutDev
.SetFillColor( COL_WHITE
);
164 rOutDev
.SetClipRegion();
165 rOutDev
.DrawRect( ::tools::Rectangle( Point(),
166 rOutDev
.GetOutputSizePixel()) );
168 if( !mp2ndOutDevProvider
)
171 OutputDevice
& rOutDev2( mp2ndOutDevProvider
->getOutDev() );
173 rOutDev2
.SetDrawMode( DrawModeFlags::Default
);
174 rOutDev2
.EnableMapMode( false );
175 rOutDev2
.SetAntialiasing( AntialiasingFlags::Enable
);
176 rOutDev2
.SetLineColor( COL_WHITE
);
177 rOutDev2
.SetFillColor( COL_WHITE
);
178 rOutDev2
.SetClipRegion();
179 rOutDev2
.DrawRect( ::tools::Rectangle( Point(),
180 rOutDev2
.GetOutputSizePixel()) );
181 rOutDev2
.SetDrawMode( DrawModeFlags::BlackLine
| DrawModeFlags::BlackFill
| DrawModeFlags::BlackText
|
182 DrawModeFlags::BlackGradient
| DrawModeFlags::BlackBitmap
);
185 void CanvasHelper::drawLine( const rendering::XCanvas
* ,
186 const geometry::RealPoint2D
& aStartRealPoint2D
,
187 const geometry::RealPoint2D
& aEndRealPoint2D
,
188 const rendering::ViewState
& viewState
,
189 const rendering::RenderState
& renderState
)
192 if( !mpOutDevProvider
)
196 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
197 setupOutDevState( viewState
, renderState
, LINE_COLOR
);
199 const Point
aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D
,
200 viewState
, renderState
) );
201 const Point
aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D
,
202 viewState
, renderState
) );
204 mpOutDevProvider
->getOutDev().DrawLine( aStartPoint
, aEndPoint
);
206 if( mp2ndOutDevProvider
)
207 mp2ndOutDevProvider
->getOutDev().DrawLine( aStartPoint
, aEndPoint
);
210 void CanvasHelper::drawBezier( const rendering::XCanvas
* ,
211 const geometry::RealBezierSegment2D
& aBezierSegment
,
212 const geometry::RealPoint2D
& _aEndPoint
,
213 const rendering::ViewState
& viewState
,
214 const rendering::RenderState
& renderState
)
216 if( !mpOutDevProvider
)
219 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
220 setupOutDevState( viewState
, renderState
, LINE_COLOR
);
222 const Point
aStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment
.Px
,
224 viewState
, renderState
) );
225 const Point
aCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment
.C1x
,
227 viewState
, renderState
) );
228 const Point
aCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment
.C2x
,
230 viewState
, renderState
) );
231 const Point
aEndPoint( tools::mapRealPoint2D( _aEndPoint
,
232 viewState
, renderState
) );
234 ::tools::Polygon
aPoly(4);
235 aPoly
.SetPoint( aStartPoint
, 0 );
236 aPoly
.SetFlags( 0, PolyFlags::Normal
);
237 aPoly
.SetPoint( aCtrlPoint1
, 1 );
238 aPoly
.SetFlags( 1, PolyFlags::Control
);
239 aPoly
.SetPoint( aCtrlPoint2
, 2 );
240 aPoly
.SetFlags( 2, PolyFlags::Control
);
241 aPoly
.SetPoint( aEndPoint
, 3 );
242 aPoly
.SetFlags( 3, PolyFlags::Normal
);
245 mpOutDevProvider
->getOutDev().DrawPolygon( aPoly
);
246 if( mp2ndOutDevProvider
)
247 mp2ndOutDevProvider
->getOutDev().DrawPolygon( aPoly
);
250 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* ,
251 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
252 const rendering::ViewState
& viewState
,
253 const rendering::RenderState
& renderState
)
255 ENSURE_ARG_OR_THROW( xPolyPolygon
.is(),
258 if( mpOutDevProvider
)
260 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
261 setupOutDevState( viewState
, renderState
, LINE_COLOR
);
263 const ::basegfx::B2DPolyPolygon
aBasegfxPolyPoly(
264 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
) );
265 const ::tools::PolyPolygon
aPolyPoly( tools::mapPolyPolygon( aBasegfxPolyPoly
, viewState
, renderState
) );
267 if( aBasegfxPolyPoly
.isClosed() )
269 mpOutDevProvider
->getOutDev().DrawPolyPolygon( aPolyPoly
);
271 if( mp2ndOutDevProvider
)
272 mp2ndOutDevProvider
->getOutDev().DrawPolyPolygon( aPolyPoly
);
276 // mixed open/closed state. Cannot render open polygon
277 // via DrawPolyPolygon(), since that implicitly
278 // closed every polygon. OTOH, no need to distinguish
279 // further and render closed polygons via
280 // DrawPolygon(), and open ones via DrawPolyLine():
281 // closed polygons will simply already contain the
283 sal_uInt16
nSize( aPolyPoly
.Count() );
285 for( sal_uInt16 i
=0; i
<nSize
; ++i
)
287 mpOutDevProvider
->getOutDev().DrawPolyLine( aPolyPoly
[i
] );
289 if( mp2ndOutDevProvider
)
290 mp2ndOutDevProvider
->getOutDev().DrawPolyLine( aPolyPoly
[i
] );
295 // TODO(P1): Provide caching here.
296 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
299 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* ,
300 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
301 const rendering::ViewState
& viewState
,
302 const rendering::RenderState
& renderState
,
303 const rendering::StrokeAttributes
& strokeAttributes
)
305 ENSURE_ARG_OR_THROW( xPolyPolygon
.is(),
308 if( mpOutDevProvider
)
310 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
312 ::basegfx::B2DHomMatrix aMatrix
;
313 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
, viewState
, renderState
);
315 ::basegfx::B2DPolyPolygon
aPolyPoly(
316 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
) );
318 // apply dashing, if any
319 if( strokeAttributes
.DashArray
.hasElements() )
321 const std::vector
<double> aDashArray(
322 ::comphelper::sequenceToContainer
< std::vector
<double> >(strokeAttributes
.DashArray
) );
324 ::basegfx::B2DPolyPolygon aDashedPolyPoly
;
326 for( sal_uInt32 i
=0; i
<aPolyPoly
.count(); ++i
)
328 // AW: new interface; You may also get gaps in the same run now
329 basegfx::utils::applyLineDashing(aPolyPoly
.getB2DPolygon(i
), aDashArray
, &aDashedPolyPoly
);
330 //aDashedPolyPoly.append(
331 // ::basegfx::utils::applyLineDashing( aPolyPoly.getB2DPolygon(i),
335 aPolyPoly
= std::move(aDashedPolyPoly
);
338 ::basegfx::B2DSize
aLinePixelSize(strokeAttributes
.StrokeWidth
,
339 strokeAttributes
.StrokeWidth
);
340 aLinePixelSize
*= aMatrix
;
341 ::basegfx::B2DPolyPolygon aStrokedPolyPoly
;
342 if( aLinePixelSize
.getLength() < 1.42 )
344 // line width < 1.0 in device pixel, thus, output as a
345 // simple hairline poly-polygon
346 setupOutDevState( viewState
, renderState
, LINE_COLOR
);
348 aStrokedPolyPoly
= std::move(aPolyPoly
);
352 // render as a 'thick' line
353 setupOutDevState( viewState
, renderState
, FILL_COLOR
);
355 for( sal_uInt32 i
=0; i
<aPolyPoly
.count(); ++i
)
357 double fMiterMinimumAngle
;
358 if (strokeAttributes
.MiterLimit
<= 1.0)
360 fMiterMinimumAngle
= M_PI_2
;
364 fMiterMinimumAngle
= 2.0 * asin(1.0/strokeAttributes
.MiterLimit
);
367 // TODO(F2): Also use Cap settings from
368 // StrokeAttributes, the
369 // createAreaGeometryForLineStartEnd() method does not
370 // seem to fit very well here
372 // AW: New interface, will create bezier polygons now
373 aStrokedPolyPoly
.append(basegfx::utils::createAreaGeometry(
374 aPolyPoly
.getB2DPolygon(i
),
375 strokeAttributes
.StrokeWidth
*0.5,
376 b2DJoineFromJoin(strokeAttributes
.JoinType
),
377 unoCapeFromCap(strokeAttributes
.StartCapType
),
378 basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/ ,
379 0.4 /* default fMaxPartOfEdge*/ ,
382 //aStrokedPolyPoly.append(
383 // ::basegfx::utils::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i),
384 // strokeAttributes.StrokeWidth*0.5,
385 // b2DJoineFromJoin(strokeAttributes.JoinType) ) );
389 // transform only _now_, all the StrokeAttributes are in
391 aStrokedPolyPoly
.transform( aMatrix
);
393 // TODO(F2): When using alpha here, must handle that via
394 // temporary surface or somesuch.
396 // Note: the generated stroke poly-polygon is NOT free of
397 // self-intersections. Therefore, if we would render it
398 // via OutDev::DrawPolyPolygon(), on/off fill would
399 // generate off areas on those self-intersections.
400 for( sal_uInt32 i
=0; i
<aStrokedPolyPoly
.count(); ++i
)
402 const basegfx::B2DPolygon
& polygon
= aStrokedPolyPoly
.getB2DPolygon( i
);
403 if( polygon
.isClosed()) {
404 mpOutDevProvider
->getOutDev().DrawPolygon( polygon
);
405 if( mp2ndOutDevProvider
)
406 mp2ndOutDevProvider
->getOutDev().DrawPolygon( polygon
);
408 mpOutDevProvider
->getOutDev().DrawPolyLine( polygon
);
409 if( mp2ndOutDevProvider
)
410 mp2ndOutDevProvider
->getOutDev().DrawPolyLine( polygon
);
415 // TODO(P1): Provide caching here.
416 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
419 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* ,
420 const uno::Reference
< rendering::XPolyPolygon2D
>& ,
421 const rendering::ViewState
& ,
422 const rendering::RenderState
& ,
423 const uno::Sequence
< rendering::Texture
>& ,
424 const rendering::StrokeAttributes
& )
426 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
429 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* ,
430 const uno::Reference
< rendering::XPolyPolygon2D
>& ,
431 const rendering::ViewState
& ,
432 const rendering::RenderState
& ,
433 const uno::Sequence
< rendering::Texture
>& ,
434 const uno::Reference
< geometry::XMapping2D
>& ,
435 const rendering::StrokeAttributes
& )
437 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
440 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* ,
441 const uno::Reference
< rendering::XPolyPolygon2D
>& ,
442 const rendering::ViewState
& ,
443 const rendering::RenderState
& ,
444 const rendering::StrokeAttributes
& )
446 return uno::Reference
< rendering::XPolyPolygon2D
>(nullptr);
449 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* ,
450 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
451 const rendering::ViewState
& viewState
,
452 const rendering::RenderState
& renderState
)
454 ENSURE_ARG_OR_THROW( xPolyPolygon
.is(),
457 if( mpOutDevProvider
)
459 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
461 const int nAlpha( setupOutDevState( viewState
, renderState
, FILL_COLOR
) );
462 ::basegfx::B2DPolyPolygon
aB2DPolyPoly(
463 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
464 aB2DPolyPoly
.setClosed(true); // ensure closed poly, otherwise VCL does not fill
465 const ::tools::PolyPolygon
aPolyPoly( tools::mapPolyPolygon(
467 viewState
, renderState
) );
468 const bool bSourceAlpha( renderState
.CompositeOperation
== rendering::CompositeOperation::SOURCE
);
469 if( nAlpha
== 255 || bSourceAlpha
)
471 mpOutDevProvider
->getOutDev().DrawPolyPolygon( aPolyPoly
);
475 const int nTransPercent( ((255 - nAlpha
) * 100 + 128) / 255 ); // normal rounding, no truncation here
476 mpOutDevProvider
->getOutDev().DrawTransparent( aPolyPoly
, static_cast<sal_uInt16
>(nTransPercent
) );
479 if( mp2ndOutDevProvider
)
481 // HACK. Normally, CanvasHelper does not care about
482 // actually what mp2ndOutDev is... well, here we do &
483 // assume a 1bpp target - everything beyond 97%
484 // transparency is fully transparent
487 mp2ndOutDevProvider
->getOutDev().SetFillColor( COL_BLACK
);
488 mp2ndOutDevProvider
->getOutDev().DrawPolyPolygon( aPolyPoly
);
493 // TODO(P1): Provide caching here.
494 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
497 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* ,
498 const uno::Reference
< rendering::XPolyPolygon2D
>& ,
499 const rendering::ViewState
& ,
500 const rendering::RenderState
& ,
501 const uno::Sequence
< rendering::Texture
>& ,
502 const uno::Reference
< geometry::XMapping2D
>& )
504 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
507 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* ,
508 const rendering::FontRequest
& fontRequest
,
509 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
510 const geometry::Matrix2D
& fontMatrix
)
512 if( mpOutDevProvider
&& mpDevice
)
514 // TODO(F2): font properties and font matrix
515 return uno::Reference
< rendering::XCanvasFont
>(
516 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
,
517 *mpDevice
, mpOutDevProvider
) );
520 return uno::Reference
< rendering::XCanvasFont
>();
523 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* ,
524 const rendering::FontInfo
& ,
525 const uno::Sequence
< beans::PropertyValue
>& )
528 return uno::Sequence
< rendering::FontInfo
>();
531 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* ,
532 const rendering::StringContext
& text
,
533 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
534 const rendering::ViewState
& viewState
,
535 const rendering::RenderState
& renderState
,
536 sal_Int8 textDirection
)
538 ENSURE_ARG_OR_THROW( xFont
.is(),
541 if( mpOutDevProvider
)
543 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
546 if( !setupTextOutput( aOutpos
, viewState
, renderState
, xFont
) )
547 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr); // no output necessary
549 // change text direction and layout mode
550 vcl::text::ComplexTextLayoutFlags
nLayoutMode(vcl::text::ComplexTextLayoutFlags::Default
);
551 switch( textDirection
)
553 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT
:
554 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT
:
555 nLayoutMode
|= vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
556 nLayoutMode
|= vcl::text::ComplexTextLayoutFlags::TextOriginLeft
;
559 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT
:
560 nLayoutMode
|= vcl::text::ComplexTextLayoutFlags::BiDiRtl
;
562 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT
:
563 nLayoutMode
|= vcl::text::ComplexTextLayoutFlags::BiDiRtl
| vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
564 nLayoutMode
|= vcl::text::ComplexTextLayoutFlags::TextOriginRight
;
569 mpOutDevProvider
->getOutDev().SetLayoutMode( nLayoutMode
);
570 mpOutDevProvider
->getOutDev().DrawText( aOutpos
,
572 ::canvas::tools::numeric_cast
<sal_uInt16
>(text
.StartPosition
),
573 ::canvas::tools::numeric_cast
<sal_uInt16
>(text
.Length
) );
575 if( mp2ndOutDevProvider
)
577 mp2ndOutDevProvider
->getOutDev().SetLayoutMode( nLayoutMode
);
578 mp2ndOutDevProvider
->getOutDev().DrawText( aOutpos
,
580 ::canvas::tools::numeric_cast
<sal_uInt16
>(text
.StartPosition
),
581 ::canvas::tools::numeric_cast
<sal_uInt16
>(text
.Length
) );
585 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
588 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* ,
589 const uno::Reference
< rendering::XTextLayout
>& xLayoutedText
,
590 const rendering::ViewState
& viewState
,
591 const rendering::RenderState
& renderState
)
593 ENSURE_ARG_OR_THROW( xLayoutedText
.is(),
596 TextLayout
* pTextLayout
= dynamic_cast< TextLayout
* >( xLayoutedText
.get() );
600 if( mpOutDevProvider
)
602 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
604 // TODO(T3): Race condition. We're taking the font
605 // from xLayoutedText, and then calling draw() at it,
606 // without exclusive access. Move setupTextOutput(),
607 // e.g. to impltools?
610 if( !setupTextOutput( aOutpos
, viewState
, renderState
, xLayoutedText
->getFont() ) )
611 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr); // no output necessary
613 // TODO(F2): What about the offset scalings?
615 pTextLayout
->draw( mpOutDevProvider
->getOutDev(), aOutpos
, viewState
, renderState
);
617 if( mp2ndOutDevProvider
)
618 pTextLayout
->draw( mp2ndOutDevProvider
->getOutDev(), aOutpos
, viewState
, renderState
);
623 ENSURE_ARG_OR_THROW( false,
624 "TextLayout not compatible with this canvas" );
627 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
630 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::implDrawBitmap( const rendering::XCanvas
* pCanvas
,
631 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
632 const rendering::ViewState
& viewState
,
633 const rendering::RenderState
& renderState
,
634 bool bModulateColors
)
636 ENSURE_ARG_OR_THROW( xBitmap
.is(),
639 ::canvas::tools::verifyInput( renderState
,
643 bModulateColors
? 3 : 0 );
645 if( mpOutDevProvider
)
647 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
648 setupOutDevState( viewState
, renderState
, IGNORE_COLOR
);
650 ::basegfx::B2DHomMatrix aMatrix
;
651 ::canvas::tools::mergeViewAndRenderTransform(aMatrix
, viewState
, renderState
);
653 ::basegfx::B2DPoint
aOutputPos( 0.0, 0.0 );
654 aOutputPos
*= aMatrix
;
656 BitmapEx
aBmpEx( tools::bitmapExFromXBitmap(xBitmap
) );
658 // TODO(F2): Implement modulation again for other color
659 // channels (currently, works only for alpha). Note: this
660 // is already implemented in transformBitmap()
661 if( bModulateColors
&&
662 renderState
.DeviceColor
.getLength() > 3 )
664 // optimize away the case where alpha modulation value
665 // is 1.0 - we then simply switch off modulation at all
666 bModulateColors
= !::rtl::math::approxEqual(
667 renderState
.DeviceColor
[3], 1.0);
670 // check whether we can render bitmap as-is: must not
671 // modulate colors, matrix must either be the identity
672 // transform (that's clear), _or_ contain only
673 // translational components.
674 if( !bModulateColors
&&
675 (aMatrix
.isIdentity() ||
676 (::basegfx::fTools::equalZero( aMatrix
.get(0,1) ) &&
677 ::basegfx::fTools::equalZero( aMatrix
.get(1,0) ) &&
678 ::rtl::math::approxEqual(aMatrix
.get(0,0), 1.0) &&
679 ::rtl::math::approxEqual(aMatrix
.get(1,1), 1.0)) ) )
681 // optimized case: identity matrix, or only
682 // translational components.
683 mpOutDevProvider
->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos
),
686 if( mp2ndOutDevProvider
)
688 // HACK. Normally, CanvasHelper does not care about
689 // actually what mp2ndOutDev is... well, here we do &
690 // assume a 1bpp target - everything beyond 97%
691 // transparency is fully transparent
692 if( aBmpEx
.IsAlpha() && !SkiaHelper::isVCLSkiaEnabled())
694 BitmapFilter::Filter(aBmpEx
, BitmapAlphaClampFilter(253));
697 mp2ndOutDevProvider
->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos
),
701 // Returning a cache object is not useful, the XBitmap
702 // itself serves this purpose
703 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
705 else if( mpOutDevProvider
->getOutDev().HasFastDrawTransformedBitmap())
707 ::basegfx::B2DHomMatrix aSizeTransform
;
708 aSizeTransform
.scale( aBmpEx
.GetSizePixel().Width(), aBmpEx
.GetSizePixel().Height() );
709 aMatrix
= aMatrix
* aSizeTransform
;
710 const double fAlpha
= bModulateColors
? renderState
.DeviceColor
[3] : 1.0;
712 mpOutDevProvider
->getOutDev().DrawTransformedBitmapEx( aMatrix
, aBmpEx
, fAlpha
);
713 if( mp2ndOutDevProvider
)
715 if( aBmpEx
.IsAlpha() )
717 // tdf#157790 invert alpha mask
718 // Due to commit 81994cb2b8b32453a92bcb011830fcb884f22ff3,
719 // the alpha mask needs to be inverted. Note: when
720 // testing tdf#157790, this code only gets executed
721 // when Skia is enabled.
722 AlphaMask
aAlpha( aBmpEx
.GetAlphaMask() );
724 aBmpEx
= BitmapEx( aBmpEx
.GetBitmap(), aAlpha
);
726 // HACK. Normally, CanvasHelper does not care about
727 // actually what mp2ndOutDev is... well, here we do &
728 // assume a 1bpp target - everything beyond 97%
729 // transparency is fully transparent
730 if( !SkiaHelper::isVCLSkiaEnabled())
732 BitmapFilter::Filter(aBmpEx
, BitmapAlphaClampFilter(253));
736 mp2ndOutDevProvider
->getOutDev().DrawTransformedBitmapEx( aMatrix
, aBmpEx
);
738 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
742 // Matrix contains non-trivial transformation (or
743 // color modulation is requested), decompose to check
744 // whether GraphicObject suffices
745 ::basegfx::B2DVector aScale
;
748 aMatrix
.decompose( aScale
, aOutputPos
, nRotate
, nShearX
);
750 GraphicAttr aGrfAttr
;
751 GraphicObjectSharedPtr pGrfObj
;
753 ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
755 // setup alpha modulation
756 if( bModulateColors
)
758 const double nAlphaModulation( renderState
.DeviceColor
[3] );
760 // TODO(F1): Note that the GraphicManager has a
761 // subtle difference in how it calculates the
762 // resulting alpha value: it's using the inverse
763 // alpha values (i.e. 'transparency'), and
764 // calculates transOrig + transModulate, instead
765 // of transOrig + transModulate -
766 // transOrig*transModulate (which would be
767 // equivalent to the origAlpha*modulateAlpha the
768 // DX canvas performs)
770 static_cast< sal_uInt8
>(
771 ::basegfx::fround( 255.0 * nAlphaModulation
) ) );
774 if( ::basegfx::fTools::equalZero( nShearX
) )
776 // no shear, GraphicObject is enough (the
777 // GraphicObject only supports scaling, rotation
780 // #i75339# don't apply mirror flags, having
781 // negative size values is enough to make
782 // GraphicObject flip the bitmap
784 // The angle has to be mapped from radian to tenths of
785 // degrees with the orientation reversed: [0,2Pi) ->
786 // (3600,0]. Note that the original angle may have
787 // values outside the [0,2Pi) interval.
788 const double nAngleInTenthOfDegrees (3600.0 - basegfx::rad2deg
<10>(nRotate
));
789 aGrfAttr
.SetRotation( Degree10(::basegfx::fround(nAngleInTenthOfDegrees
)) );
791 pGrfObj
= std::make_shared
<GraphicObject
>( aBmpEx
);
795 // modify output position, to account for the fact
796 // that transformBitmap() always normalizes its output
797 // bitmap into the smallest enclosing box.
798 ::basegfx::B2DRectangle aDestRect
= ::canvas::tools::calcTransformedRectBounds(
799 ::basegfx::B2DRectangle(0,
805 aOutputPos
.setX( aDestRect
.getMinX() );
806 aOutputPos
.setY( aDestRect
.getMinY() );
808 // complex transformation, use generic affine bitmap
810 aBmpEx
= tools::transformBitmap( aBmpEx
,
813 pGrfObj
= std::make_shared
<GraphicObject
>( aBmpEx
);
815 // clear scale values, generated bitmap already
817 aScale
.setX( 1.0 ); aScale
.setY( 1.0 );
819 // update bitmap size, bitmap has changed above.
820 aBmpSize
= aBmpEx
.GetSizePixel();
823 // output GraphicObject
824 const ::Point
aPt( vcl::unotools::pointFromB2DPoint( aOutputPos
) );
825 const ::Size
aSz( ::basegfx::fround
<::tools::Long
>( aScale
.getX() * aBmpSize
.Width() ),
826 ::basegfx::fround
<::tools::Long
>( aScale
.getY() * aBmpSize
.Height() ) );
828 pGrfObj
->Draw(mpOutDevProvider
->getOutDev(),
833 if( mp2ndOutDevProvider
)
835 GraphicObjectSharedPtr p2ndGrfObj
= pGrfObj
;
836 if( aBmpEx
.IsAlpha() )
838 // tdf#157790 invert alpha mask
839 // Due to commit 81994cb2b8b32453a92bcb011830fcb884f22ff3,
840 // the alpha mask needs to be inverted. Note: when
841 // testing tdf#157790, this code only gets executed
842 // when Skia is disabled.
843 AlphaMask
aAlpha( aBmpEx
.GetAlphaMask() );
845 BitmapEx
a2ndBmpEx( aBmpEx
.GetBitmap(), aAlpha
);
846 p2ndGrfObj
= std::make_shared
<GraphicObject
>( a2ndBmpEx
);
849 p2ndGrfObj
->Draw(mp2ndOutDevProvider
->getOutDev(),
855 // created GraphicObject, which possibly cached
856 // display bitmap - return cache object, to retain
858 return uno::Reference
< rendering::XCachedPrimitive
>(
859 new CachedBitmap( std::move(pGrfObj
),
865 // cast away const, need to
866 // change refcount (as this is
867 // ~invisible to client code,
868 // still logically const)
869 const_cast< rendering::XCanvas
* >(pCanvas
)) );
874 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
877 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* pCanvas
,
878 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
879 const rendering::ViewState
& viewState
,
880 const rendering::RenderState
& renderState
)
882 return implDrawBitmap( pCanvas
,
889 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
890 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
891 const rendering::ViewState
& viewState
,
892 const rendering::RenderState
& renderState
)
894 return implDrawBitmap( pCanvas
,
901 geometry::IntegerSize2D
CanvasHelper::getSize()
903 if( !mpOutDevProvider
)
904 return geometry::IntegerSize2D(); // we're disposed
906 return vcl::unotools::integerSize2DFromSize( mpOutDevProvider
->getOutDev().GetOutputSizePixel() );
909 uno::Reference
< rendering::XBitmap
> CanvasHelper::getScaledBitmap( const geometry::RealSize2D
& newSize
,
912 if( !mpOutDevProvider
|| !mpDevice
)
913 return uno::Reference
< rendering::XBitmap
>(); // we're disposed
915 OutputDevice
& rOutDev( mpOutDevProvider
->getOutDev() );
917 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
918 rOutDev
.EnableMapMode( false );
919 rOutDev
.SetAntialiasing( AntialiasingFlags::Enable
);
921 // TODO(F2): Support alpha vdev canvas here
922 const Point
aEmptyPoint(0,0);
923 const Size
aBmpSize( rOutDev
.GetOutputSizePixel() );
925 BitmapEx
aBitmap( rOutDev
.GetBitmapEx(aEmptyPoint
, aBmpSize
) );
927 aBitmap
.Scale( vcl::unotools::sizeFromRealSize2D(newSize
),
928 beFast
? BmpScaleFlag::Default
: BmpScaleFlag::BestQuality
);
930 return uno::Reference
< rendering::XBitmap
>(
931 new CanvasBitmap( aBitmap
, *mpDevice
, mpOutDevProvider
) );
934 uno::Sequence
< sal_Int8
> CanvasHelper::getData( rendering::IntegerBitmapLayout
& rLayout
,
935 const geometry::IntegerRectangle2D
& rect
)
937 if( !mpOutDevProvider
)
938 return uno::Sequence
< sal_Int8
>(); // we're disposed
940 rLayout
= getMemoryLayout();
942 // TODO(F2): Support alpha canvas here
943 const ::tools::Rectangle
aRect( vcl::unotools::rectangleFromIntegerRectangle2D(rect
) );
945 OutputDevice
& rOutDev( mpOutDevProvider
->getOutDev() );
947 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
948 rOutDev
.EnableMapMode( false );
949 rOutDev
.SetAntialiasing( AntialiasingFlags::Enable
);
951 Bitmap
aBitmap( rOutDev
.GetBitmapEx(aRect
.TopLeft(),
952 aRect
.GetSize()).GetBitmap() );
954 BitmapScopedReadAccess
pReadAccess( aBitmap
);
956 ENSURE_OR_THROW( pReadAccess
.get() != nullptr,
957 "Could not acquire read access to OutDev bitmap" );
959 const sal_Int32
nWidth( rect
.X2
- rect
.X1
);
960 const sal_Int32
nHeight( rect
.Y2
- rect
.Y1
);
962 rLayout
.ScanLines
= nHeight
;
963 rLayout
.ScanLineBytes
= nWidth
*4;
964 rLayout
.ScanLineStride
= rLayout
.ScanLineBytes
;
966 uno::Sequence
< sal_Int8
> aRes( 4*nWidth
*nHeight
);
967 sal_Int8
* pRes
= aRes
.getArray();
970 for( int y
=0; y
<nHeight
; ++y
)
972 for( int x
=0; x
<nWidth
; ++x
)
974 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetRed();
975 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetGreen();
976 pRes
[ nCurrPos
++ ] = pReadAccess
->GetColor( y
, x
).GetBlue();
977 pRes
[ nCurrPos
++ ] = -1;
984 uno::Sequence
< sal_Int8
> CanvasHelper::getPixel( rendering::IntegerBitmapLayout
& rLayout
,
985 const geometry::IntegerPoint2D
& pos
)
987 if( !mpOutDevProvider
)
988 return uno::Sequence
< sal_Int8
>(); // we're disposed
990 rLayout
= getMemoryLayout();
991 rLayout
.ScanLines
= 1;
992 rLayout
.ScanLineBytes
= 4;
993 rLayout
.ScanLineStride
= rLayout
.ScanLineBytes
;
995 OutputDevice
& rOutDev( mpOutDevProvider
->getOutDev() );
997 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
998 rOutDev
.EnableMapMode( false );
999 rOutDev
.SetAntialiasing( AntialiasingFlags::Enable
);
1001 const Size
aBmpSize( rOutDev
.GetOutputSizePixel() );
1003 ENSURE_ARG_OR_THROW( pos
.X
>= 0 && pos
.X
< aBmpSize
.Width(),
1004 "X coordinate out of bounds" );
1005 ENSURE_ARG_OR_THROW( pos
.Y
>= 0 && pos
.Y
< aBmpSize
.Height(),
1006 "Y coordinate out of bounds" );
1008 // TODO(F2): Support alpha canvas here
1009 return ::canvas::tools::colorToStdIntSequence(
1011 vcl::unotools::pointFromIntegerPoint2D( pos
)));
1014 rendering::IntegerBitmapLayout
CanvasHelper::getMemoryLayout()
1016 if( !mpOutDevProvider
)
1017 return rendering::IntegerBitmapLayout(); // we're disposed
1019 rendering::IntegerBitmapLayout
aBitmapLayout( ::canvas::tools::getStdMemoryLayout(getSize()) );
1021 aBitmapLayout
.ColorSpace
= canvas::tools::getStdColorSpaceWithoutAlpha();
1023 return aBitmapLayout
;
1026 int CanvasHelper::setupOutDevState( const rendering::ViewState
& viewState
,
1027 const rendering::RenderState
& renderState
,
1028 ColorType eColorType
) const
1030 ENSURE_OR_THROW( mpOutDevProvider
,
1031 "outdev null. Are we disposed?" );
1033 ::canvas::tools::verifyInput( renderState
,
1037 eColorType
== IGNORE_COLOR
? 0 : 3 );
1039 OutputDevice
& rOutDev( mpOutDevProvider
->getOutDev() );
1040 OutputDevice
* p2ndOutDev
= nullptr;
1042 rOutDev
.EnableMapMode( false );
1043 rOutDev
.SetAntialiasing( AntialiasingFlags::Enable
);
1045 if( mp2ndOutDevProvider
)
1046 p2ndOutDev
= &mp2ndOutDevProvider
->getOutDev();
1050 // TODO(P2): Don't change clipping all the time, maintain current clip
1051 // state and change only when update is necessary
1052 ::canvas::tools::clipOutDev(viewState
, renderState
, rOutDev
, p2ndOutDev
);
1054 Color
aColor( COL_WHITE
);
1056 if( renderState
.DeviceColor
.getLength() > 2 )
1058 aColor
= vcl::unotools::stdColorSpaceSequenceToColor(
1059 renderState
.DeviceColor
);
1062 // extract alpha, and make color opaque
1063 // afterwards. Otherwise, OutputDevice won't draw anything
1064 nAlpha
= aColor
.GetAlpha();
1065 aColor
.SetAlpha(255);
1067 if( eColorType
!= IGNORE_COLOR
)
1069 switch( eColorType
)
1072 rOutDev
.SetLineColor( aColor
);
1073 rOutDev
.SetFillColor();
1077 p2ndOutDev
->SetLineColor( aColor
);
1078 p2ndOutDev
->SetFillColor();
1083 rOutDev
.SetFillColor( aColor
);
1084 rOutDev
.SetLineColor();
1088 p2ndOutDev
->SetFillColor( aColor
);
1089 p2ndOutDev
->SetLineColor();
1094 rOutDev
.SetTextColor( aColor
);
1097 p2ndOutDev
->SetTextColor( aColor
);
1101 ENSURE_OR_THROW( false,
1102 "Unexpected color type");
1110 bool CanvasHelper::setupTextOutput( ::Point
& o_rOutPos
,
1111 const rendering::ViewState
& viewState
,
1112 const rendering::RenderState
& renderState
,
1113 const uno::Reference
< rendering::XCanvasFont
>& xFont
) const
1115 ENSURE_OR_THROW( mpOutDevProvider
,
1116 "outdev null. Are we disposed?" );
1118 OutputDevice
& rOutDev( mpOutDevProvider
->getOutDev() );
1120 setupOutDevState( viewState
, renderState
, TEXT_COLOR
);
1122 CanvasFont
* pFont
= dynamic_cast< CanvasFont
* >( xFont
.get() );
1124 ENSURE_ARG_OR_THROW( pFont
,
1125 "Font not compatible with this canvas" );
1127 vcl::Font aVCLFont
= pFont
->getVCLFont();
1129 Color
aColor( COL_BLACK
);
1131 if( renderState
.DeviceColor
.getLength() > 2 )
1133 aColor
= vcl::unotools::stdColorSpaceSequenceToColor(
1134 renderState
.DeviceColor
);
1138 aVCLFont
.SetColor( aColor
);
1139 aVCLFont
.SetFillColor( aColor
);
1141 // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
1142 if( !tools::setupFontTransform( o_rOutPos
, aVCLFont
, viewState
, renderState
, rOutDev
) )
1145 rOutDev
.SetFont( aVCLFont
);
1147 if( mp2ndOutDevProvider
)
1148 mp2ndOutDevProvider
->getOutDev().SetFont( aVCLFont
);
1153 bool CanvasHelper::repaint( const GraphicObjectSharedPtr
& rGrf
,
1154 const rendering::ViewState
& viewState
,
1155 const rendering::RenderState
& renderState
,
1158 const GraphicAttr
& rAttr
) const
1160 ENSURE_OR_RETURN_FALSE( rGrf
,
1161 "Invalid Graphic" );
1163 if( !mpOutDevProvider
)
1164 return false; // disposed
1167 tools::OutDevStateKeeper
aStateKeeper( mpProtectedOutDevProvider
);
1168 setupOutDevState( viewState
, renderState
, IGNORE_COLOR
);
1170 if (!rGrf
->Draw(mpOutDevProvider
->getOutDev(), rPt
, rSz
, &rAttr
))
1173 // #i80779# Redraw also into mask outdev
1174 if (mp2ndOutDevProvider
)
1175 return rGrf
->Draw(mp2ndOutDevProvider
->getOutDev(), rPt
, rSz
, &rAttr
);
1181 void CanvasHelper::flush() const
1183 if (mpOutDevProvider
)
1184 mpOutDevProvider
->getOutDev().Flush();
1186 if (mp2ndOutDevProvider
)
1187 mp2ndOutDevProvider
->getOutDev().Flush();
1192 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */