Move setting of LD_LIBRARY_PATH closer to invocation of cppunittester
[LibreOffice.git] / canvas / source / vcl / canvashelper.cxx
bloba35a93bc1dba8561cb6437ff7300624c4eb04431
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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;
57 namespace vclcanvas
59 namespace
61 basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType )
63 switch( 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;
77 default:
78 ENSURE_OR_THROW( false,
79 "b2DJoineFromJoin(): Unexpected join type" );
82 return basegfx::B2DLineJoin::NONE;
85 drawing::LineCap unoCapeFromCap( sal_Int8 nCapType)
87 switch ( 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;
98 default:
99 ENSURE_OR_THROW( false,
100 "unoCapeFromCap(): Unexpected cap type" );
102 return drawing::LineCap_BUTT;
106 CanvasHelper::CanvasHelper() :
107 mpDevice(),
108 mbHaveAlpha( false )
112 void CanvasHelper::disposing()
114 mpDevice = nullptr;
115 mpProtectedOutDevProvider.reset();
116 mpOutDevProvider.reset();
117 mp2ndOutDevProvider.reset();
120 void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
121 const OutDevProviderSharedPtr& rOutDev,
122 bool bProtect,
123 bool bHaveAlpha )
125 // cast away const, need to change refcount (as this is
126 // ~invisible to client code, still logically const)
127 mpDevice = &rDevice;
128 mbHaveAlpha = bHaveAlpha;
130 setOutDev( rOutDev, bProtect );
133 void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev,
134 bool bProtect )
136 if( bProtect )
137 mpProtectedOutDevProvider = rOutDev;
138 else
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()
153 // are we disposed?
154 if( !mpOutDevProvider )
155 return;
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 )
169 return;
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 )
191 // are we disposed?
192 if( !mpOutDevProvider )
193 return;
195 // nope, render
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 ) );
203 // TODO(F2): alpha
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 )
217 return;
219 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
220 setupOutDevState( viewState, renderState, LINE_COLOR );
222 const Point aStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px,
223 aBezierSegment.Py),
224 viewState, renderState ) );
225 const Point aCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x,
226 aBezierSegment.C1y),
227 viewState, renderState ) );
228 const Point aCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x,
229 aBezierSegment.C2y),
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 );
244 // TODO(F2): alpha
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(),
256 "polygon is NULL");
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 );
274 else
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
282 // closing segment.
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(),
306 "polygon is NULL");
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),
332 // aDashArray ) );
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);
350 else
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;
362 else
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*/ ,
380 fMiterMinimumAngle
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
390 // user coordinates.
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 );
407 } else {
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(),
455 "polygon is NULL");
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(
466 aB2DPolyPoly,
467 viewState, renderState ) );
468 const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE );
469 if( nAlpha == 255 || bSourceAlpha )
471 mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
473 else
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
485 if( nAlpha > 2 )
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 >& )
527 // TODO(F2)
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(),
539 "font is NULL");
541 if( mpOutDevProvider )
543 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
545 ::Point aOutpos;
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;
557 break;
559 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
560 nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl;
561 [[fallthrough]];
562 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
563 nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
564 nLayoutMode |= vcl::text::ComplexTextLayoutFlags::TextOriginRight;
565 break;
568 // TODO(F2): alpha
569 mpOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode );
570 mpOutDevProvider->getOutDev().DrawText( aOutpos,
571 text.Text,
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,
579 text.Text,
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(),
594 "layout is NULL");
596 TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
598 if( pTextLayout )
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?
609 ::Point aOutpos;
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?
614 // TODO(F2): alpha
615 pTextLayout->draw( mpOutDevProvider->getOutDev(), aOutpos, viewState, renderState );
617 if( mp2ndOutDevProvider )
618 pTextLayout->draw( mp2ndOutDevProvider->getOutDev(), aOutpos, viewState, renderState );
621 else
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(),
637 "bitmap is NULL");
639 ::canvas::tools::verifyInput( renderState,
640 __func__,
641 mpDevice,
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 ),
684 aBmpEx );
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 ),
698 aBmpEx );
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() );
723 aAlpha.Invert();
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);
740 else
742 // Matrix contains non-trivial transformation (or
743 // color modulation is requested), decompose to check
744 // whether GraphicObject suffices
745 ::basegfx::B2DVector aScale;
746 double nRotate;
747 double nShearX;
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)
769 aGrfAttr.SetAlpha(
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
778 // and translation)
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 );
793 else
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,
801 aBmpSize.Width(),
802 aBmpSize.Height()),
803 aMatrix );
805 aOutputPos.setX( aDestRect.getMinX() );
806 aOutputPos.setY( aDestRect.getMinY() );
808 // complex transformation, use generic affine bitmap
809 // transformation
810 aBmpEx = tools::transformBitmap( aBmpEx,
811 aMatrix );
813 pGrfObj = std::make_shared<GraphicObject>( aBmpEx );
815 // clear scale values, generated bitmap already
816 // contains scaling
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(),
829 aPt,
830 aSz,
831 &aGrfAttr);
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() );
844 aAlpha.Invert();
845 BitmapEx a2ndBmpEx( aBmpEx.GetBitmap(), aAlpha );
846 p2ndGrfObj = std::make_shared<GraphicObject>( a2ndBmpEx );
849 p2ndGrfObj->Draw(mp2ndOutDevProvider->getOutDev(),
850 aPt,
851 aSz,
852 &aGrfAttr);
855 // created GraphicObject, which possibly cached
856 // display bitmap - return cache object, to retain
857 // that information.
858 return uno::Reference< rendering::XCachedPrimitive >(
859 new CachedBitmap( std::move(pGrfObj),
860 aPt,
861 aSz,
862 aGrfAttr,
863 viewState,
864 renderState,
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)) );
873 // Nothing rendered
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,
883 xBitmap,
884 viewState,
885 renderState,
886 false );
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,
895 xBitmap,
896 viewState,
897 renderState,
898 true );
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,
910 bool beFast )
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();
969 int nCurrPos(0);
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;
981 return aRes;
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(
1010 rOutDev.GetPixel(
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()) );
1020 if ( !mbHaveAlpha )
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,
1034 __func__,
1035 mpDevice,
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();
1048 int nAlpha(255);
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 )
1071 case LINE_COLOR:
1072 rOutDev.SetLineColor( aColor );
1073 rOutDev.SetFillColor();
1075 if( p2ndOutDev )
1077 p2ndOutDev->SetLineColor( aColor );
1078 p2ndOutDev->SetFillColor();
1080 break;
1082 case FILL_COLOR:
1083 rOutDev.SetFillColor( aColor );
1084 rOutDev.SetLineColor();
1086 if( p2ndOutDev )
1088 p2ndOutDev->SetFillColor( aColor );
1089 p2ndOutDev->SetLineColor();
1091 break;
1093 case TEXT_COLOR:
1094 rOutDev.SetTextColor( aColor );
1096 if( p2ndOutDev )
1097 p2ndOutDev->SetTextColor( aColor );
1098 break;
1100 default:
1101 ENSURE_OR_THROW( false,
1102 "Unexpected color type");
1103 break;
1107 return nAlpha;
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 );
1137 // setup font color
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 ) )
1143 return false;
1145 rOutDev.SetFont( aVCLFont );
1147 if( mp2ndOutDevProvider )
1148 mp2ndOutDevProvider->getOutDev().SetFont( aVCLFont );
1150 return true;
1153 bool CanvasHelper::repaint( const GraphicObjectSharedPtr& rGrf,
1154 const rendering::ViewState& viewState,
1155 const rendering::RenderState& renderState,
1156 const ::Point& rPt,
1157 const ::Size& rSz,
1158 const GraphicAttr& rAttr ) const
1160 ENSURE_OR_RETURN_FALSE( rGrf,
1161 "Invalid Graphic" );
1163 if( !mpOutDevProvider )
1164 return false; // disposed
1165 else
1167 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
1168 setupOutDevState( viewState, renderState, IGNORE_COLOR );
1170 if (!rGrf->Draw(mpOutDevProvider->getOutDev(), rPt, rSz, &rAttr))
1171 return false;
1173 // #i80779# Redraw also into mask outdev
1174 if (mp2ndOutDevProvider)
1175 return rGrf->Draw(mp2ndOutDevProvider->getOutDev(), rPt, rSz, &rAttr);
1177 return true;
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: */