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/.
10 #include <sal/config.h>
16 #include <basegfx/matrix/b2dhommatrix.hxx>
17 #include <basegfx/polygon/b2dpolypolygon.hxx>
18 #include <basegfx/polygon/b2dpolypolygontools.hxx>
19 #include <basegfx/utils/canvastools.hxx>
20 #include <com/sun/star/rendering/CompositeOperation.hpp>
22 #include <rtl/math.hxx>
23 #include <comphelper/diagnose_ex.hxx>
24 #include <vcl/font.hxx>
25 #include <vcl/kernarray.hxx>
26 #include <vcl/metric.hxx>
27 #include <vcl/virdev.hxx>
29 #include "ogl_canvasbitmap.hxx"
30 #include "ogl_canvasfont.hxx"
31 #include "ogl_canvastools.hxx"
32 #include "ogl_texturecache.hxx"
33 #include "ogl_tools.hxx"
35 #include "ogl_canvashelper.hxx"
37 using namespace ::com::sun::star
;
38 using namespace std::placeholders
;
45 This OpenGL canvas implementation tries to keep all render
46 output as high-level as possible, i.e. geometry data and
47 externally-provided bitmaps. Therefore, calls at the
48 XCanvas-interfaces are not immediately transformed into colored
49 pixel inside some GL buffer, but are retained simply with their
50 call parameters. Only after XSpriteCanvas::updateScreen() has
51 been called, this all gets transferred to the OpenGL subsystem
52 and converted to a visible scene. The big advantage is, this
53 makes sprite modifications practically zero-overhead, and saves
54 a lot on texture memory (compared to the directx canvas, which
55 immediately dumps every render call into a texture).
57 The drawback, of course, is that complex images churn a lot of
58 GPU cycles on every re-rendering.
60 For the while, I'll be using immediate mode, i.e. transfer data
61 over and over again to the OpenGL subsystem. Alternatively,
62 there are display lists, which at least keep the data on the
63 server, or even better, vertex buffers, which copy geometry
66 Next todo: put polygon geometry into vertex buffer (LRU cache
67 necessary?) - or, rather, buffer objects! prune entries older
68 than one updateScreen() call)
70 Text: http://www.opengl.org/resources/features/fontsurvey/
73 struct CanvasHelper::Action
75 ::basegfx::B2DHomMatrix maTransform
;
76 GLenum meSrcBlendMode
;
77 GLenum meDstBlendMode
;
78 rendering::ARGBColor maARGBColor
;
79 ::basegfx::B2DPolyPolygonVector maPolyPolys
;
83 const ::basegfx::B2DHomMatrix
&,
86 const rendering::ARGBColor
&,
87 const ::basegfx::B2DPolyPolygonVector
&)> maFunction
;
92 bool lcl_drawLine( const CanvasHelper
& /*rHelper*/,
93 const ::basegfx::B2DHomMatrix
& rTransform
,
96 const rendering::ARGBColor
& rColor
,
97 const geometry::RealPoint2D
& rStartPoint
,
98 const geometry::RealPoint2D
& rEndPoint
)
100 TransformationPreserver aPreserver
;
101 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
104 glVertex2d(rStartPoint
.X
, rStartPoint
.Y
);
105 glVertex2d(rEndPoint
.X
, rEndPoint
.Y
);
111 bool lcl_drawPolyPolygon( const CanvasHelper
& /*rHelper*/,
112 const ::basegfx::B2DHomMatrix
& rTransform
,
115 const rendering::ARGBColor
& rColor
,
116 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
118 TransformationPreserver aPreserver
;
119 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
121 for( const auto& rPoly
: rPolyPolygons
)
122 renderPolyPolygon( rPoly
);
127 bool lcl_fillPolyPolygon( const CanvasHelper
& /*rHelper*/,
128 const ::basegfx::B2DHomMatrix
& rTransform
,
131 const rendering::ARGBColor
& rColor
,
132 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
134 TransformationPreserver aPreserver
;
135 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
137 for( const auto& rPoly
: rPolyPolygons
)
139 glBegin( GL_TRIANGLES
);
140 renderComplexPolyPolygon( rPoly
);
147 bool lcl_fillGradientPolyPolygon( const CanvasHelper
& rHelper
,
148 const ::basegfx::B2DHomMatrix
& rTransform
,
151 const ::canvas::ParametricPolyPolygon::Values
& rValues
,
152 const rendering::Texture
& rTexture
,
153 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
155 TransformationPreserver aPreserver
;
156 setupState(rTransform
, eSrcBlend
, eDstBlend
, rendering::ARGBColor());
158 // convert to weird canvas textur coordinate system (not
159 // [0,1]^2, but path coordinate system)
160 ::basegfx::B2DHomMatrix aTextureTransform
;
161 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform
,
162 rTexture
.AffineTransform
);
163 ::basegfx::B2DRange aBounds
;
164 for( const auto& rPoly
: rPolyPolygons
)
165 aBounds
.expand( ::basegfx::utils::getRange( rPoly
) );
166 aTextureTransform
.translate(-aBounds
.getMinX(), -aBounds
.getMinY());
167 aTextureTransform
.scale(1/aBounds
.getWidth(), 1/aBounds
.getHeight());
169 const sal_Int32 nNumCols
=rValues
.maColors
.getLength();
170 uno::Sequence
< rendering::ARGBColor
> aColors(nNumCols
);
171 rendering::ARGBColor
* const pColors
=aColors
.getArray();
172 rendering::ARGBColor
* pCurrCol
=pColors
;
173 for( sal_Int32 i
=0; i
<nNumCols
; ++i
)
174 *pCurrCol
++ = rHelper
.getDevice()->getDeviceColorSpace()->convertToARGB(rValues
.maColors
[i
])[0];
176 OSL_ASSERT(nNumCols
== rValues
.maStops
.getLength());
178 switch( rValues
.meType
)
180 case ::canvas::ParametricPolyPolygon::GradientType::Linear
:
181 rHelper
.getDeviceHelper()->useLinearGradientShader(pColors
,
186 case ::canvas::ParametricPolyPolygon::GradientType::Elliptical
:
187 rHelper
.getDeviceHelper()->useRadialGradientShader(pColors
,
192 case ::canvas::ParametricPolyPolygon::GradientType::Rectangular
:
193 rHelper
.getDeviceHelper()->useRectangularGradientShader(pColors
,
199 ENSURE_OR_THROW( false,
200 "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" );
204 for( const auto& rPoly
: rPolyPolygons
)
206 glBegin(GL_TRIANGLES
);
207 renderComplexPolyPolygon( rPoly
);
213 glMatrixMode(GL_MODELVIEW
);
218 bool lcl_drawOwnBitmap( const CanvasHelper
& /*rHelper*/,
219 const ::basegfx::B2DHomMatrix
& rTransform
,
222 const rendering::ARGBColor
& rColor
,
223 const CanvasBitmap
& rBitmap
)
225 TransformationPreserver aPreserver
;
226 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
228 return rBitmap
.renderRecordedActions();
231 bool lcl_drawGenericBitmap( const CanvasHelper
& rHelper
,
232 const ::basegfx::B2DHomMatrix
& rTransform
,
235 const rendering::ARGBColor
& rColor
,
236 const geometry::IntegerSize2D
& rPixelSize
,
237 const uno::Sequence
<sal_Int8
>& rPixelData
,
238 sal_uInt32 nPixelCrc32
)
240 TransformationPreserver aPreserver
;
241 setupState(rTransform
, eSrcBlend
, eDstBlend
, rColor
);
243 const unsigned int nTexId
=rHelper
.getDeviceHelper()->getTextureCache().getTexture(
244 rPixelSize
, rPixelData
.getConstArray(), nPixelCrc32
);
246 glBindTexture(GL_TEXTURE_2D
, nTexId
);
247 glEnable(GL_TEXTURE_2D
);
248 glTexParameteri(GL_TEXTURE_2D
,
249 GL_TEXTURE_MIN_FILTER
,
251 glTexParameteri(GL_TEXTURE_2D
,
252 GL_TEXTURE_MAG_FILTER
,
255 glBlendFunc(GL_SRC_ALPHA
,
256 GL_ONE_MINUS_SRC_ALPHA
);
258 // blend against fixed vertex color; texture alpha is multiplied in
261 glBegin(GL_TRIANGLE_STRIP
);
262 glTexCoord2f(0,0); glVertex2d(0,0);
263 glTexCoord2f(0,1); glVertex2d(0, rPixelSize
.Height
);
264 glTexCoord2f(1,0); glVertex2d(rPixelSize
.Width
,0);
265 glTexCoord2f(1,1); glVertex2d(rPixelSize
.Width
,rPixelSize
.Height
);
268 glBindTexture(GL_TEXTURE_2D
, 0);
269 glDisable(GL_TEXTURE_2D
);
274 bool lcl_fillTexturedPolyPolygon( const CanvasHelper
& rHelper
,
275 const ::basegfx::B2DHomMatrix
& rTransform
,
278 const rendering::Texture
& rTexture
,
279 const geometry::IntegerSize2D
& rPixelSize
,
280 const uno::Sequence
<sal_Int8
>& rPixelData
,
281 sal_uInt32 nPixelCrc32
,
282 const ::basegfx::B2DPolyPolygonVector
& rPolyPolygons
)
284 TransformationPreserver aPreserver
;
285 setupState(rTransform
, eSrcBlend
, eDstBlend
, rendering::ARGBColor());
287 const unsigned int nTexId
=rHelper
.getDeviceHelper()->getTextureCache().getTexture(
288 rPixelSize
, rPixelData
.getConstArray(), nPixelCrc32
);
290 glBindTexture(GL_TEXTURE_2D
, nTexId
);
291 glEnable(GL_TEXTURE_2D
);
292 glTexParameteri(GL_TEXTURE_2D
,
293 GL_TEXTURE_MIN_FILTER
,
295 glTexParameteri(GL_TEXTURE_2D
,
296 GL_TEXTURE_MAG_FILTER
,
299 glBlendFunc(GL_SRC_ALPHA
,
300 GL_ONE_MINUS_SRC_ALPHA
);
302 // convert to weird canvas textur coordinate system (not
303 // [0,1]^2, but path coordinate system)
304 ::basegfx::B2DHomMatrix aTextureTransform
;
305 ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform
,
306 rTexture
.AffineTransform
);
307 ::basegfx::B2DRange aBounds
;
308 for( const auto& rPolyPolygon
: rPolyPolygons
)
309 aBounds
.expand( ::basegfx::utils::getRange( rPolyPolygon
) );
310 aTextureTransform
.translate(-aBounds
.getMinX(), -aBounds
.getMinY());
311 aTextureTransform
.scale(1/aBounds
.getWidth(), 1/aBounds
.getHeight());
312 aTextureTransform
.invert();
314 glMatrixMode(GL_TEXTURE
);
315 double aTexTransform
[] =
317 aTextureTransform
.get(0,0), aTextureTransform
.get(1,0), 0, 0,
318 aTextureTransform
.get(0,1), aTextureTransform
.get(1,1), 0, 0,
320 aTextureTransform
.get(0,2), aTextureTransform
.get(1,2), 0, 1
322 glLoadMatrixd(aTexTransform
);
324 // blend against fixed vertex color; texture alpha is multiplied in
325 glColor4f(1,1,1,rTexture
.Alpha
);
327 for( const auto& rPolyPolygon
: rPolyPolygons
)
329 glBegin(GL_TRIANGLES
);
330 renderComplexPolyPolygon( rPolyPolygon
);
335 glMatrixMode(GL_MODELVIEW
);
337 glBindTexture(GL_TEXTURE_2D
, 0);
338 glDisable(GL_TEXTURE_2D
);
344 CanvasHelper::CanvasHelper() :
346 mpDeviceHelper( nullptr )
349 CanvasHelper::~CanvasHelper()
352 CanvasHelper
& CanvasHelper::operator=( const CanvasHelper
& rSrc
)
354 mpDevice
= rSrc
.mpDevice
;
355 mpDeviceHelper
= rSrc
.mpDeviceHelper
;
356 mpRecordedActions
= rSrc
.mpRecordedActions
;
360 void CanvasHelper::disposing()
362 RecordVectorT aThrowaway
;
363 mpRecordedActions
.swap( aThrowaway
);
365 mpDeviceHelper
= nullptr;
368 void CanvasHelper::init( rendering::XGraphicDevice
& rDevice
,
369 SpriteDeviceHelper
& rDeviceHelper
)
372 mpDeviceHelper
= &rDeviceHelper
;
375 void CanvasHelper::clear()
377 mpRecordedActions
->clear();
380 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
381 const geometry::RealPoint2D
& aStartPoint
,
382 const geometry::RealPoint2D
& aEndPoint
,
383 const rendering::ViewState
& viewState
,
384 const rendering::RenderState
& renderState
)
388 mpRecordedActions
->push_back( Action() );
389 Action
& rAct
=mpRecordedActions
->back();
391 setupGraphicsState( rAct
, viewState
, renderState
);
392 rAct
.maFunction
= std::bind(&lcl_drawLine
,
393 std::placeholders::_1
, std::placeholders::_2
, std::placeholders::_3
, std::placeholders::_4
, std::placeholders::_5
,
394 aStartPoint
, aEndPoint
);
398 void CanvasHelper::drawBezier( const rendering::XCanvas
* /*pCanvas*/,
399 const geometry::RealBezierSegment2D
& aBezierSegment
,
400 const geometry::RealPoint2D
& aEndPoint
,
401 const rendering::ViewState
& viewState
,
402 const rendering::RenderState
& renderState
)
407 mpRecordedActions
->push_back( Action() );
408 Action
& rAct
=mpRecordedActions
->back();
410 setupGraphicsState( rAct
, viewState
, renderState
);
412 // TODO(F2): subdivide&render whole curve
413 rAct
.maFunction
= std::bind(&lcl_drawLine
,
414 std::placeholders::_1
, std::placeholders::_2
, std::placeholders::_3
, std::placeholders::_4
, std::placeholders::_5
,
415 geometry::RealPoint2D(
421 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
422 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
423 const rendering::ViewState
& viewState
,
424 const rendering::RenderState
& renderState
)
426 ENSURE_OR_THROW( xPolyPolygon
.is(),
427 "CanvasHelper::drawPolyPolygon: polygon is NULL");
431 mpRecordedActions
->push_back( Action() );
432 Action
& rAct
=mpRecordedActions
->back();
434 setupGraphicsState( rAct
, viewState
, renderState
);
435 rAct
.maPolyPolys
.push_back(
436 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
437 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
439 rAct
.maFunction
= &lcl_drawPolyPolygon
;
442 // TODO(P1): Provide caching here.
443 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
446 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
447 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
448 const rendering::ViewState
& viewState
,
449 const rendering::RenderState
& renderState
,
450 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
452 ENSURE_OR_THROW( xPolyPolygon
.is(),
453 "CanvasHelper::strokePolyPolygon: polygon is NULL");
457 mpRecordedActions
->push_back( Action() );
458 Action
& rAct
=mpRecordedActions
->back();
460 setupGraphicsState( rAct
, viewState
, renderState
);
461 rAct
.maPolyPolys
.push_back(
462 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
463 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
465 // TODO(F3): fallback to drawPolyPolygon currently
466 rAct
.maFunction
= &lcl_drawPolyPolygon
;
469 // TODO(P1): Provide caching here.
470 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
473 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
474 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
475 const rendering::ViewState
& /*viewState*/,
476 const rendering::RenderState
& /*renderState*/,
477 const uno::Sequence
< rendering::Texture
>& /*textures*/,
478 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
481 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
484 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
485 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
486 const rendering::ViewState
& /*viewState*/,
487 const rendering::RenderState
& /*renderState*/,
488 const uno::Sequence
< rendering::Texture
>& /*textures*/,
489 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
490 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
493 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
496 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* /*pCanvas*/,
497 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
498 const rendering::ViewState
& /*viewState*/,
499 const rendering::RenderState
& /*renderState*/,
500 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
503 return uno::Reference
< rendering::XPolyPolygon2D
>(nullptr);
506 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
507 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
508 const rendering::ViewState
& viewState
,
509 const rendering::RenderState
& renderState
)
511 ENSURE_OR_THROW( xPolyPolygon
.is(),
512 "CanvasHelper::fillPolyPolygon: polygon is NULL");
516 mpRecordedActions
->push_back( Action() );
517 Action
& rAct
=mpRecordedActions
->back();
519 setupGraphicsState( rAct
, viewState
, renderState
);
520 rAct
.maPolyPolys
.push_back(
521 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
522 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
524 rAct
.maFunction
= &lcl_fillPolyPolygon
;
527 // TODO(P1): Provide caching here.
528 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
531 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
532 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
533 const rendering::ViewState
& viewState
,
534 const rendering::RenderState
& renderState
,
535 const uno::Sequence
< rendering::Texture
>& textures
)
537 ENSURE_OR_THROW( xPolyPolygon
.is(),
538 "CanvasHelper::fillPolyPolygon: polygon is NULL");
542 mpRecordedActions
->push_back( Action() );
543 Action
& rAct
=mpRecordedActions
->back();
545 setupGraphicsState( rAct
, viewState
, renderState
);
546 rAct
.maPolyPolys
.push_back(
547 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
));
548 rAct
.maPolyPolys
.back().makeUnique(); // own copy, for thread safety
550 // TODO(F1): Multi-texturing
551 if( textures
[0].Gradient
.is() )
553 // try to cast XParametricPolyPolygon2D reference to
554 // our implementation class.
555 ::canvas::ParametricPolyPolygon
* pGradient
=
556 dynamic_cast< ::canvas::ParametricPolyPolygon
* >( textures
[0].Gradient
.get() );
560 // copy state from Gradient polypoly locally
561 // (given object might change!)
562 const ::canvas::ParametricPolyPolygon::Values
aValues(
563 pGradient
->getValues() );
565 rAct
.maFunction
= std::bind(&lcl_fillGradientPolyPolygon
,
566 std::placeholders::_1
, std::placeholders::_2
, std::placeholders::_3
, std::placeholders::_4
,
569 std::placeholders::_6
);
573 // TODO(F1): The generic case is missing here
574 ENSURE_OR_THROW( false,
575 "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
578 else if( textures
[0].Bitmap
.is() )
581 CanvasBitmap
* pOwnBitmap
=dynamic_cast<CanvasBitmap
*>(textures
[0].Bitmap
.get());
584 // TODO(F2): own texture bitmap
588 // TODO(P3): Highly inefficient - simply copies pixel data
590 uno::Reference
< rendering::XIntegerReadOnlyBitmap
> xIntegerBitmap(
593 if( xIntegerBitmap
.is() )
595 const geometry::IntegerSize2D aSize
=xIntegerBitmap
->getSize();
596 rendering::IntegerBitmapLayout aLayout
;
597 uno::Sequence
<sal_Int8
> aPixelData
=
598 xIntegerBitmap
->getData(
600 geometry::IntegerRectangle2D(0,0,aSize
.Width
,aSize
.Height
));
602 // force-convert color to ARGB8888 int color space
603 uno::Sequence
<sal_Int8
> aARGBBytes(
604 aLayout
.ColorSpace
->convertToIntegerColorSpace(
606 canvas::tools::getStdColorSpace()));
608 rAct
.maFunction
= std::bind(&lcl_fillTexturedPolyPolygon
,
609 std::placeholders::_1
, std::placeholders::_2
, std::placeholders::_3
, std::placeholders::_4
,
614 aARGBBytes
.getConstArray(),
615 aARGBBytes
.getLength()),
616 std::placeholders::_6
);
618 // TODO(F1): handle non-integer case
623 // TODO(P1): Provide caching here.
624 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
627 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
628 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
629 const rendering::ViewState
& /*viewState*/,
630 const rendering::RenderState
& /*renderState*/,
631 const uno::Sequence
< rendering::Texture
>& /*textures*/,
632 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
635 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
638 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* /*pCanvas*/,
639 const rendering::FontRequest
& fontRequest
,
640 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
641 const geometry::Matrix2D
& fontMatrix
)
644 return uno::Reference
< rendering::XCanvasFont
>(
645 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
) );
647 return uno::Reference
< rendering::XCanvasFont
>();
650 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* /*pCanvas*/,
651 const rendering::FontInfo
& /*aFilter*/,
652 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
655 return uno::Sequence
< rendering::FontInfo
>();
658 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* /*pCanvas*/,
659 const rendering::StringContext
& /*text*/,
660 const uno::Reference
< rendering::XCanvasFont
>& /*xFont*/,
661 const rendering::ViewState
& /*viewState*/,
662 const rendering::RenderState
& /*renderState*/,
663 sal_Int8
/*textDirection*/ )
665 // TODO - but not used from slideshow
666 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
669 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* /*pCanvas*/,
670 const uno::Reference
< rendering::XTextLayout
>& xLayoutetText
,
671 const rendering::ViewState
& viewState
,
672 const rendering::RenderState
& renderState
)
674 ENSURE_OR_THROW( xLayoutetText
.is(),
675 "CanvasHelper::drawTextLayout: text is NULL");
679 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
680 pVDev
->EnableOutput(false);
682 auto pLayoutFont
= xLayoutetText
->getFont();
683 CanvasFont
* pFont
=dynamic_cast<CanvasFont
*>(pLayoutFont
.get());
684 const rendering::StringContext aTxt
=xLayoutetText
->getText();
685 if( pFont
&& aTxt
.Length
)
688 const rendering::FontRequest aFontRequest
= pFont
->getFontRequest();
689 const geometry::Matrix2D
& rFontMatrix
= pFont
->getFontMatrix();
691 aFontRequest
.FontDescription
.FamilyName
,
692 aFontRequest
.FontDescription
.StyleName
,
693 Size( 0, ::basegfx::fround
<tools::Long
>(aFontRequest
.CellSize
)));
695 aFont
.SetAlignment( ALIGN_BASELINE
);
696 aFont
.SetCharSet( (aFontRequest
.FontDescription
.IsSymbolFont
==util::TriState_YES
) ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
697 aFont
.SetVertical( aFontRequest
.FontDescription
.IsVertical
==util::TriState_YES
);
698 aFont
.SetWeight( static_cast<FontWeight
>(aFontRequest
.FontDescription
.FontDescription
.Weight
) );
699 aFont
.SetItalic( (aFontRequest
.FontDescription
.FontDescription
.Letterform
<=8) ? ITALIC_NONE
: ITALIC_NORMAL
);
701 if (pFont
->getEmphasisMark())
702 aFont
.SetEmphasisMark(FontEmphasisMark(pFont
->getEmphasisMark()));
704 // adjust to stretched font
705 if(!::rtl::math::approxEqual(rFontMatrix
.m00
, rFontMatrix
.m11
))
707 const Size aSize
= pVDev
->GetFontMetric( aFont
).GetFontSize();
708 const double fDividend( rFontMatrix
.m10
+ rFontMatrix
.m11
);
709 double fStretch
= rFontMatrix
.m00
+ rFontMatrix
.m01
;
711 if( !::basegfx::fTools::equalZero( fDividend
) )
712 fStretch
/= fDividend
;
714 const sal_Int32 nNewWidth
= ::basegfx::fround( aSize
.Width() * fStretch
);
716 aFont
.SetAverageFontWidth( nNewWidth
);
720 pVDev
->SetFont(aFont
);
722 mpRecordedActions
->push_back( Action() );
723 Action
& rAct
=mpRecordedActions
->back();
725 setupGraphicsState( rAct
, viewState
, renderState
);
727 // handle custom spacing, if there
728 uno::Sequence
<double> aLogicalAdvancements
=xLayoutetText
->queryLogicalAdvancements();
729 if( aLogicalAdvancements
.hasElements() )
731 KernArraySpan
aDXArray(aLogicalAdvancements
.getConstArray(), aLogicalAdvancements
.getLength());
733 uno::Sequence
<sal_Bool
> aKashidaPositions
=xLayoutetText
->queryKashidaPositions();
734 std::span
<const sal_Bool
> aKashidaArray(aKashidaPositions
.getConstArray(), aKashidaPositions
.getLength());
737 pVDev
->GetTextOutlines(rAct
.maPolyPolys
,
749 pVDev
->GetTextOutlines(rAct
.maPolyPolys
,
756 // own copy, for thread safety
757 for( auto& rPoly
: rAct
.maPolyPolys
)
760 rAct
.maFunction
= &lcl_fillPolyPolygon
;
765 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
768 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* /*pCanvas*/,
769 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
770 const rendering::ViewState
& viewState
,
771 const rendering::RenderState
& renderState
)
773 ENSURE_OR_THROW( xBitmap
.is(),
774 "CanvasHelper::drawBitmap: bitmap is NULL");
779 CanvasBitmap
* pOwnBitmap
=dynamic_cast<CanvasBitmap
*>(xBitmap
.get());
782 // insert as transformed copy of bitmap action vector -
783 // during rendering, this gets rendered into a temporary
784 // buffer, and then composited to the front
785 mpRecordedActions
->push_back( Action() );
786 Action
& rAct
=mpRecordedActions
->back();
788 setupGraphicsState( rAct
, viewState
, renderState
);
789 rAct
.maFunction
= std::bind(&lcl_drawOwnBitmap
,
790 std::placeholders::_1
, std::placeholders::_2
, std::placeholders::_3
, std::placeholders::_4
, std::placeholders::_5
,
795 // TODO(P3): Highly inefficient - simply copies pixel data
797 uno::Reference
< rendering::XIntegerReadOnlyBitmap
> xIntegerBitmap(
798 xBitmap
, uno::UNO_QUERY
);
799 if( xIntegerBitmap
.is() )
801 const geometry::IntegerSize2D aSize
=xBitmap
->getSize();
802 rendering::IntegerBitmapLayout aLayout
;
803 uno::Sequence
<sal_Int8
> aPixelData
=
804 xIntegerBitmap
->getData(
806 geometry::IntegerRectangle2D(0,0,aSize
.Width
,aSize
.Height
));
808 // force-convert color to ARGB8888 int color space
809 uno::Sequence
<sal_Int8
> aARGBBytes(
810 aLayout
.ColorSpace
->convertToIntegerColorSpace(
812 canvas::tools::getStdColorSpace()));
814 mpRecordedActions
->push_back( Action() );
815 Action
& rAct
=mpRecordedActions
->back();
817 setupGraphicsState( rAct
, viewState
, renderState
);
818 rAct
.maFunction
= std::bind(&lcl_drawGenericBitmap
,
819 std::placeholders::_1
, std::placeholders::_2
, std::placeholders::_3
, std::placeholders::_4
, std::placeholders::_5
,
822 aARGBBytes
.getConstArray(),
823 aARGBBytes
.getLength()));
825 // TODO(F1): handle non-integer case
829 // TODO(P1): Provide caching here.
830 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
833 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
834 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
835 const rendering::ViewState
& viewState
,
836 const rendering::RenderState
& renderState
)
838 // TODO(F3): remove this wart altogether
839 return drawBitmap(pCanvas
, xBitmap
, viewState
, renderState
);
843 void CanvasHelper::setupGraphicsState( Action
& o_action
,
844 const rendering::ViewState
& viewState
,
845 const rendering::RenderState
& renderState
)
847 ENSURE_OR_THROW( mpDevice
,
848 "CanvasHelper::setupGraphicsState: reference device invalid" );
850 // TODO(F3): clipping
851 // TODO(P2): think about caching transformations between canvas calls
853 // setup overall transform only now. View clip above was
854 // relative to view transform
855 ::canvas::tools::mergeViewAndRenderTransform(o_action
.maTransform
,
858 // setup compositing - mapping courtesy David Reveman
859 // (glitz_operator.c)
860 switch( renderState
.CompositeOperation
)
862 case rendering::CompositeOperation::OVER
:
863 o_action
.meSrcBlendMode
=GL_ONE
;
864 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
866 case rendering::CompositeOperation::CLEAR
:
867 o_action
.meSrcBlendMode
=GL_ZERO
;
868 o_action
.meDstBlendMode
=GL_ZERO
;
870 case rendering::CompositeOperation::SOURCE
:
871 o_action
.meSrcBlendMode
=GL_ONE
;
872 o_action
.meDstBlendMode
=GL_ZERO
;
874 case rendering::CompositeOperation::UNDER
:
875 case rendering::CompositeOperation::DESTINATION
:
876 o_action
.meSrcBlendMode
=GL_ZERO
;
877 o_action
.meDstBlendMode
=GL_ONE
;
879 case rendering::CompositeOperation::INSIDE
:
880 o_action
.meSrcBlendMode
=GL_DST_ALPHA
;
881 o_action
.meDstBlendMode
=GL_ZERO
;
883 case rendering::CompositeOperation::INSIDE_REVERSE
:
884 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
885 o_action
.meDstBlendMode
=GL_ZERO
;
887 case rendering::CompositeOperation::OUTSIDE
:
888 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
889 o_action
.meDstBlendMode
=GL_ONE
;
891 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
892 o_action
.meSrcBlendMode
=GL_ZERO
;
893 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
895 case rendering::CompositeOperation::ATOP
:
896 o_action
.meSrcBlendMode
=GL_DST_ALPHA
;
897 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
899 case rendering::CompositeOperation::ATOP_REVERSE
:
900 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
901 o_action
.meDstBlendMode
=GL_SRC_ALPHA
;
903 case rendering::CompositeOperation::XOR
:
904 o_action
.meSrcBlendMode
=GL_ONE_MINUS_DST_ALPHA
;
905 o_action
.meDstBlendMode
=GL_ONE_MINUS_SRC_ALPHA
;
907 case rendering::CompositeOperation::ADD
:
908 o_action
.meSrcBlendMode
=GL_ONE
;
909 o_action
.meDstBlendMode
=GL_ONE
;
911 case rendering::CompositeOperation::SATURATE
:
912 o_action
.meSrcBlendMode
=GL_SRC_ALPHA_SATURATE
;
913 o_action
.meDstBlendMode
=GL_SRC_ALPHA_SATURATE
;
917 ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
921 if (renderState
.DeviceColor
.hasElements())
922 o_action
.maARGBColor
=
923 mpDevice
->getDeviceColorSpace()->convertToARGB(renderState
.DeviceColor
)[0];
926 bool CanvasHelper::renderRecordedActions() const
928 for( const auto& rRecordedAction
: *mpRecordedActions
)
930 if( !rRecordedAction
.maFunction( *this,
931 rRecordedAction
.maTransform
,
932 rRecordedAction
.meSrcBlendMode
,
933 rRecordedAction
.meDstBlendMode
,
934 rRecordedAction
.maARGBColor
,
935 rRecordedAction
.maPolyPolys
) )
942 size_t CanvasHelper::getRecordedActionCount() const
944 return mpRecordedActions
->size();
948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */