tdf#154285 Check upper bound of arguments in SbRtl_Minute function
[LibreOffice.git] / canvas / source / opengl / ogl_canvashelper.cxx
blob114f98be19034df44dcdabc5519e8d09ca723dc4
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/.
8 */
10 #include <sal/config.h>
12 #include <memory>
13 #include <functional>
14 #include <epoxy/gl.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>
21 #include <rtl/crc.h>
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;
40 namespace oglcanvas
42 /* Concepts:
43 =========
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
64 data over en bloc.
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;
81 std::function< bool (
82 const CanvasHelper&,
83 const ::basegfx::B2DHomMatrix&,
84 GLenum,
85 GLenum,
86 const rendering::ARGBColor&,
87 const ::basegfx::B2DPolyPolygonVector&)> maFunction;
90 namespace
92 bool lcl_drawLine( const CanvasHelper& /*rHelper*/,
93 const ::basegfx::B2DHomMatrix& rTransform,
94 GLenum eSrcBlend,
95 GLenum eDstBlend,
96 const rendering::ARGBColor& rColor,
97 const geometry::RealPoint2D& rStartPoint,
98 const geometry::RealPoint2D& rEndPoint )
100 TransformationPreserver aPreserver;
101 setupState(rTransform, eSrcBlend, eDstBlend, rColor);
103 glBegin(GL_LINES);
104 glVertex2d(rStartPoint.X, rStartPoint.Y);
105 glVertex2d(rEndPoint.X, rEndPoint.Y);
106 glEnd();
108 return true;
111 bool lcl_drawPolyPolygon( const CanvasHelper& /*rHelper*/,
112 const ::basegfx::B2DHomMatrix& rTransform,
113 GLenum eSrcBlend,
114 GLenum eDstBlend,
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 );
124 return true;
127 bool lcl_fillPolyPolygon( const CanvasHelper& /*rHelper*/,
128 const ::basegfx::B2DHomMatrix& rTransform,
129 GLenum eSrcBlend,
130 GLenum eDstBlend,
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 );
141 glEnd();
144 return true;
147 bool lcl_fillGradientPolyPolygon( const CanvasHelper& rHelper,
148 const ::basegfx::B2DHomMatrix& rTransform,
149 GLenum eSrcBlend,
150 GLenum eDstBlend,
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,
182 rValues.maStops,
183 aTextureTransform);
184 break;
186 case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
187 rHelper.getDeviceHelper()->useRadialGradientShader(pColors,
188 rValues.maStops,
189 aTextureTransform);
190 break;
192 case ::canvas::ParametricPolyPolygon::GradientType::Rectangular:
193 rHelper.getDeviceHelper()->useRectangularGradientShader(pColors,
194 rValues.maStops,
195 aTextureTransform);
196 break;
198 default:
199 ENSURE_OR_THROW( false,
200 "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" );
204 for( const auto& rPoly : rPolyPolygons )
206 glBegin(GL_TRIANGLES);
207 renderComplexPolyPolygon( rPoly );
208 glEnd();
211 glUseProgram(0);
212 glLoadIdentity();
213 glMatrixMode(GL_MODELVIEW);
215 return true;
218 bool lcl_drawOwnBitmap( const CanvasHelper& /*rHelper*/,
219 const ::basegfx::B2DHomMatrix& rTransform,
220 GLenum eSrcBlend,
221 GLenum eDstBlend,
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,
233 GLenum eSrcBlend,
234 GLenum eDstBlend,
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,
250 GL_NEAREST);
251 glTexParameteri(GL_TEXTURE_2D,
252 GL_TEXTURE_MAG_FILTER,
253 GL_NEAREST);
254 glEnable(GL_BLEND);
255 glBlendFunc(GL_SRC_ALPHA,
256 GL_ONE_MINUS_SRC_ALPHA);
258 // blend against fixed vertex color; texture alpha is multiplied in
259 glColor4f(1,1,1,1);
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);
266 glEnd();
268 glBindTexture(GL_TEXTURE_2D, 0);
269 glDisable(GL_TEXTURE_2D);
271 return true;
274 bool lcl_fillTexturedPolyPolygon( const CanvasHelper& rHelper,
275 const ::basegfx::B2DHomMatrix& rTransform,
276 GLenum eSrcBlend,
277 GLenum eDstBlend,
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,
294 GL_NEAREST);
295 glTexParameteri(GL_TEXTURE_2D,
296 GL_TEXTURE_MAG_FILTER,
297 GL_NEAREST);
298 glEnable(GL_BLEND);
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,
319 0, 0, 1, 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 );
331 glEnd();
334 glLoadIdentity();
335 glMatrixMode(GL_MODELVIEW);
337 glBindTexture(GL_TEXTURE_2D, 0);
338 glDisable(GL_TEXTURE_2D);
340 return true;
344 CanvasHelper::CanvasHelper() :
345 mpDevice( nullptr ),
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;
357 return *this;
360 void CanvasHelper::disposing()
362 RecordVectorT aThrowaway;
363 mpRecordedActions.swap( aThrowaway );
364 mpDevice = nullptr;
365 mpDeviceHelper = nullptr;
368 void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
369 SpriteDeviceHelper& rDeviceHelper )
371 mpDevice = &rDevice;
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 )
386 if( mpDevice )
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 )
404 if( !mpDevice )
405 return;
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(
416 aBezierSegment.Px,
417 aBezierSegment.Py),
418 aEndPoint);
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");
429 if( mpDevice )
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");
455 if( mpDevice )
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*/ )
480 // TODO
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*/ )
492 // TODO
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*/ )
502 // TODO
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");
514 if( mpDevice )
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");
540 if( mpDevice )
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() );
558 if( pGradient )
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,
567 aValues,
568 textures[0],
569 std::placeholders::_6);
571 else
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() )
580 // own bitmap?
581 CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(textures[0].Bitmap.get());
582 if( pOwnBitmap )
584 // TODO(F2): own texture bitmap
586 else
588 // TODO(P3): Highly inefficient - simply copies pixel data
590 uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
591 textures[0].Bitmap,
592 uno::UNO_QUERY);
593 if( xIntegerBitmap.is() )
595 const geometry::IntegerSize2D aSize=xIntegerBitmap->getSize();
596 rendering::IntegerBitmapLayout aLayout;
597 uno::Sequence<sal_Int8> aPixelData=
598 xIntegerBitmap->getData(
599 aLayout,
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(
605 aPixelData,
606 canvas::tools::getStdColorSpace()));
608 rAct.maFunction = std::bind(&lcl_fillTexturedPolyPolygon,
609 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
610 textures[0],
611 aSize,
612 aARGBBytes,
613 rtl_crc32(0,
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*/ )
634 // TODO
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 )
643 if( mpDevice )
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*/ )
654 // TODO
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");
677 if( mpDevice )
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 )
687 // create the font
688 const rendering::FontRequest aFontRequest = pFont->getFontRequest();
689 const geometry::Matrix2D& rFontMatrix = pFont->getFontMatrix();
690 vcl::Font aFont(
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 );
719 // set font
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());
736 // get the glyphs
737 pVDev->GetTextOutlines(rAct.maPolyPolys,
738 aTxt.Text,
740 aTxt.StartPosition,
741 aTxt.Length,
743 aDXArray,
744 aKashidaArray);
746 else
748 // get the glyphs
749 pVDev->GetTextOutlines(rAct.maPolyPolys,
750 aTxt.Text,
752 aTxt.StartPosition,
753 aTxt.Length );
756 // own copy, for thread safety
757 for( auto& rPoly : rAct.maPolyPolys )
758 rPoly.makeUnique();
760 rAct.maFunction = &lcl_fillPolyPolygon;
764 // TODO
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");
776 if( mpDevice )
778 // own bitmap?
779 CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get());
780 if( pOwnBitmap )
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,
791 *pOwnBitmap);
793 else
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(
805 aLayout,
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(
811 aPixelData,
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,
820 aSize, aARGBBytes,
821 rtl_crc32(0,
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,
856 viewState,
857 renderState);
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;
865 break;
866 case rendering::CompositeOperation::CLEAR:
867 o_action.meSrcBlendMode=GL_ZERO;
868 o_action.meDstBlendMode=GL_ZERO;
869 break;
870 case rendering::CompositeOperation::SOURCE:
871 o_action.meSrcBlendMode=GL_ONE;
872 o_action.meDstBlendMode=GL_ZERO;
873 break;
874 case rendering::CompositeOperation::UNDER:
875 case rendering::CompositeOperation::DESTINATION:
876 o_action.meSrcBlendMode=GL_ZERO;
877 o_action.meDstBlendMode=GL_ONE;
878 break;
879 case rendering::CompositeOperation::INSIDE:
880 o_action.meSrcBlendMode=GL_DST_ALPHA;
881 o_action.meDstBlendMode=GL_ZERO;
882 break;
883 case rendering::CompositeOperation::INSIDE_REVERSE:
884 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
885 o_action.meDstBlendMode=GL_ZERO;
886 break;
887 case rendering::CompositeOperation::OUTSIDE:
888 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
889 o_action.meDstBlendMode=GL_ONE;
890 break;
891 case rendering::CompositeOperation::OUTSIDE_REVERSE:
892 o_action.meSrcBlendMode=GL_ZERO;
893 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
894 break;
895 case rendering::CompositeOperation::ATOP:
896 o_action.meSrcBlendMode=GL_DST_ALPHA;
897 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
898 break;
899 case rendering::CompositeOperation::ATOP_REVERSE:
900 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
901 o_action.meDstBlendMode=GL_SRC_ALPHA;
902 break;
903 case rendering::CompositeOperation::XOR:
904 o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
905 o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
906 break;
907 case rendering::CompositeOperation::ADD:
908 o_action.meSrcBlendMode=GL_ONE;
909 o_action.meDstBlendMode=GL_ONE;
910 break;
911 case rendering::CompositeOperation::SATURATE:
912 o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE;
913 o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE;
914 break;
916 default:
917 ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
918 break;
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 ) )
936 return false;
939 return true;
942 size_t CanvasHelper::getRecordedActionCount() const
944 return mpRecordedActions->size();
948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */