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>
11 #include <sal/log.hxx>
13 #include <basegfx/matrix/b2dhommatrix.hxx>
14 #include <basegfx/utils/canvastools.hxx>
15 #include <basegfx/utils/unopolypolygon.hxx>
16 #include <com/sun/star/awt/XTopWindow.hpp>
17 #include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
18 #include <com/sun/star/uno/Reference.hxx>
19 #include <toolkit/helper/vclunohelper.hxx>
20 #include <vcl/canvastools.hxx>
21 #include <vcl/opengl/OpenGLHelper.hxx>
22 #include <vcl/syschild.hxx>
24 #include "ogl_spritedevicehelper.hxx"
25 #include "ogl_spritecanvas.hxx"
26 #include "ogl_canvasbitmap.hxx"
27 #include "ogl_canvastools.hxx"
28 #include "ogl_canvascustomsprite.hxx"
29 #include "ogl_texturecache.hxx"
31 using namespace ::com::sun::star
;
33 static void initContext()
35 // need the backside for mirror effects
36 glDisable(GL_CULL_FACE
);
38 // no perspective, we're 2D
39 glMatrixMode(GL_PROJECTION
);
43 glEnable(GL_POINT_SMOOTH
);
44 glEnable(GL_LINE_SMOOTH
);
45 glEnable(GL_POLYGON_SMOOTH
);
46 glHint(GL_POINT_SMOOTH_HINT
,GL_NICEST
);
47 glHint(GL_LINE_SMOOTH_HINT
,GL_NICEST
);
48 glHint(GL_POLYGON_SMOOTH_HINT
,GL_NICEST
);
49 glShadeModel(GL_FLAT
);
52 static void initTransformation(const ::Size
& rSize
)
56 static_cast<GLsizei
>(rSize
.Width()),
57 static_cast<GLsizei
>(rSize
.Height()) );
59 // model coordinate system is already in device pixel
60 glMatrixMode(GL_MODELVIEW
);
62 glTranslated(-1.0, 1.0, 0.0);
63 glScaled( 2.0 / rSize
.Width(),
64 -2.0 / rSize
.Height(),
68 glClearColor(0,0,0,0);
69 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
75 SpriteDeviceHelper::SpriteDeviceHelper() :
76 mpSpriteCanvas(nullptr),
77 mpTextureCache(std::make_shared
<TextureCache
>()),
78 mnLinearTwoColorGradientProgram(0),
79 mnLinearMultiColorGradientProgram(0),
80 mnRadialTwoColorGradientProgram(0),
81 mnRadialMultiColorGradientProgram(0),
82 mnRectangularTwoColorGradientProgram(0),
83 mnRectangularMultiColorGradientProgram(0),
84 mxContext(OpenGLContext::Create())
87 SpriteDeviceHelper::~SpriteDeviceHelper()
88 { mxContext
->dispose(); }
90 void SpriteDeviceHelper::init( vcl::Window
& rWindow
,
91 SpriteCanvas
& rSpriteCanvas
,
92 const awt::Rectangle
& rViewArea
)
94 mpSpriteCanvas
= &rSpriteCanvas
;
96 rSpriteCanvas
.setWindow(
97 uno::Reference
<awt::XWindow2
>(
98 VCLUnoHelper::GetInterface(&rWindow
),
99 uno::UNO_QUERY_THROW
) );
101 mxContext
->requestLegacyContext();
102 mxContext
->init(&rWindow
);
103 // init window context
106 mnLinearMultiColorGradientProgram
=
107 OpenGLHelper::LoadShaders(u
"dummyVertexShader"_ustr
, u
"linearMultiColorGradientFragmentShader"_ustr
);
109 mnLinearTwoColorGradientProgram
=
110 OpenGLHelper::LoadShaders(u
"dummyVertexShader"_ustr
, u
"linearTwoColorGradientFragmentShader"_ustr
);
112 mnRadialMultiColorGradientProgram
=
113 OpenGLHelper::LoadShaders(u
"dummyVertexShader"_ustr
, u
"radialMultiColorGradientFragmentShader"_ustr
);
115 mnRadialTwoColorGradientProgram
=
116 OpenGLHelper::LoadShaders(u
"dummyVertexShader"_ustr
, u
"radialTwoColorGradientFragmentShader"_ustr
);
118 mnRectangularMultiColorGradientProgram
=
119 OpenGLHelper::LoadShaders(u
"dummyVertexShader"_ustr
, u
"rectangularMultiColorGradientFragmentShader"_ustr
);
121 mnRectangularTwoColorGradientProgram
=
122 OpenGLHelper::LoadShaders(u
"dummyVertexShader"_ustr
, u
"rectangularTwoColorGradientFragmentShader"_ustr
);
124 mxContext
->makeCurrent();
126 notifySizeUpdate(rViewArea
);
127 // TODO(E3): check for GL_ARB_imaging extension
130 void SpriteDeviceHelper::disposing()
132 // release all references
133 mpSpriteCanvas
= nullptr;
134 mpTextureCache
.reset();
136 if( mxContext
->isInitialized() )
138 glDeleteProgram( mnRectangularTwoColorGradientProgram
);
139 glDeleteProgram( mnRectangularMultiColorGradientProgram
);
140 glDeleteProgram( mnRadialTwoColorGradientProgram
);
141 glDeleteProgram( mnRadialMultiColorGradientProgram
);
142 glDeleteProgram( mnLinearTwoColorGradientProgram
);
143 glDeleteProgram( mnLinearMultiColorGradientProgram
);
147 geometry::RealSize2D
SpriteDeviceHelper::getPhysicalResolution()
149 if( !mxContext
->isInitialized() )
150 return ::canvas::tools::createInfiniteSize2D(); // we're disposed
152 // Map a one-by-one millimeter box to pixel
153 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
154 const MapMode
aOldMapMode( pChildWindow
->GetMapMode() );
155 pChildWindow
->SetMapMode( MapMode(MapUnit::MapMM
) );
156 const Size
aPixelSize( pChildWindow
->LogicToPixel(Size(1,1)) );
157 pChildWindow
->SetMapMode( aOldMapMode
);
159 return vcl::unotools::size2DFromSize( aPixelSize
);
162 geometry::RealSize2D
SpriteDeviceHelper::getPhysicalSize()
164 if( !mxContext
->isInitialized() )
165 return ::canvas::tools::createInfiniteSize2D(); // we're disposed
167 // Map the pixel dimensions of the output window to millimeter
168 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
169 const MapMode
aOldMapMode( pChildWindow
->GetMapMode() );
170 pChildWindow
->SetMapMode( MapMode(MapUnit::MapMM
) );
171 const Size
aLogSize( pChildWindow
->PixelToLogic(pChildWindow
->GetOutputSizePixel()) );
172 pChildWindow
->SetMapMode( aOldMapMode
);
174 return vcl::unotools::size2DFromSize( aLogSize
);
177 uno::Reference
< rendering::XLinePolyPolygon2D
> SpriteDeviceHelper::createCompatibleLinePolyPolygon(
178 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
179 const uno::Sequence
< uno::Sequence
< geometry::RealPoint2D
> >& points
)
182 if( !mpSpriteCanvas
)
183 return uno::Reference
< rendering::XLinePolyPolygon2D
>(); // we're disposed
185 return uno::Reference
< rendering::XLinePolyPolygon2D
>(
186 new ::basegfx::unotools::UnoPolyPolygon(
187 ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points
)));
190 uno::Reference
< rendering::XBezierPolyPolygon2D
> SpriteDeviceHelper::createCompatibleBezierPolyPolygon(
191 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
192 const uno::Sequence
< uno::Sequence
< geometry::RealBezierSegment2D
> >& points
)
195 if( !mpSpriteCanvas
)
196 return uno::Reference
< rendering::XBezierPolyPolygon2D
>(); // we're disposed
198 return uno::Reference
< rendering::XBezierPolyPolygon2D
>(
199 new ::basegfx::unotools::UnoPolyPolygon(
200 ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points
) ) );
203 uno::Reference
< rendering::XBitmap
> SpriteDeviceHelper::createCompatibleBitmap(
204 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
205 const geometry::IntegerSize2D
& size
)
208 if( !mpSpriteCanvas
)
209 return uno::Reference
< rendering::XBitmap
>(); // we're disposed
211 return uno::Reference
< rendering::XBitmap
>(
212 new CanvasBitmap( size
,
217 uno::Reference
< rendering::XVolatileBitmap
> SpriteDeviceHelper::createVolatileBitmap(
218 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
219 const geometry::IntegerSize2D
& /*size*/ )
221 return uno::Reference
< rendering::XVolatileBitmap
>();
224 uno::Reference
< rendering::XBitmap
> SpriteDeviceHelper::createCompatibleAlphaBitmap(
225 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
226 const geometry::IntegerSize2D
& size
)
229 if( !mpSpriteCanvas
)
230 return uno::Reference
< rendering::XBitmap
>(); // we're disposed
232 return uno::Reference
< rendering::XBitmap
>(
233 new CanvasBitmap( size
,
238 uno::Reference
< rendering::XVolatileBitmap
> SpriteDeviceHelper::createVolatileAlphaBitmap(
239 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
240 const geometry::IntegerSize2D
& /*size*/ )
242 return uno::Reference
< rendering::XVolatileBitmap
>();
247 /** Functor providing a StrictWeakOrdering for XSprites (over
250 struct SpriteComparator
252 bool operator()( const ::rtl::Reference
<CanvasCustomSprite
>& rLHS
,
253 const ::rtl::Reference
<CanvasCustomSprite
>& rRHS
) const
255 const double nPrioL( rLHS
->getPriority() );
256 const double nPrioR( rRHS
->getPriority() );
258 // if prios are equal, tie-break on ptr value
259 return nPrioL
== nPrioR
? rLHS
.get() < rRHS
.get() : nPrioL
< nPrioR
;
264 bool SpriteDeviceHelper::showBuffer( bool bIsVisible
, SAL_UNUSED_PARAMETER
bool /*bUpdateAll*/ )
266 // hidden or disposed?
267 if( !bIsVisible
|| !mxContext
->isInitialized() || !mpSpriteCanvas
)
270 mxContext
->makeCurrent();
272 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
273 const ::Size aOutputSize
= pChildWindow
->GetSizePixel();
274 initTransformation(aOutputSize
);
276 // render the actual spritecanvas content
277 mpSpriteCanvas
->renderRecordedActions();
279 // render all sprites (in order of priority) on top of that
280 std::vector
< ::rtl::Reference
<CanvasCustomSprite
> > aSprites(
281 maActiveSprites
.begin(),
282 maActiveSprites
.end());
283 std::sort(aSprites
.begin(),
286 for( const auto& rSprite
: aSprites
)
287 rSprite
->renderSprite();
290 // frame counter, other info
291 glMatrixMode(GL_MODELVIEW
);
293 glTranslated(-1.0, 1.0, 0.0);
294 glScaled( 2.0 / aOutputSize
.Width(),
295 -2.0 / aOutputSize
.Height(),
298 const double denominator( maLastUpdate
.getElapsedTime() );
299 maLastUpdate
.reset();
301 const double fps(denominator
== 0.0 ? 100.0 : 1.0/denominator
);
302 std::vector
<double> aVec
{ fps
, static_cast<double>(maActiveSprites
.size()),
303 static_cast<double>(mpTextureCache
->getCacheSize()),
304 static_cast<double>(mpTextureCache
->getCacheMissCount()),
305 static_cast<double>(mpTextureCache
->getCacheHitCount()) };
306 renderOSD( aVec
, 20 );
309 * TODO: moggi: fix it!
310 // switch buffer, sync etc.
311 const unx::Window aXWindow=pChildWindow->GetSystemData()->aWindow;
312 unx::glXSwapBuffers(reinterpret_cast<unx::Display*>(mpDisplay),
314 pChildWindow->Show();
316 XSync( reinterpret_cast<unx::Display*>(mpDisplay), false );
318 mxContext
->swapBuffers();
320 // flush texture cache, such that it does not build up
322 // TODO: have max cache size/LRU time in config, prune only on
324 mpTextureCache
->prune();
329 bool SpriteDeviceHelper::switchBuffer( bool bIsVisible
, bool bUpdateAll
)
331 // no difference for VCL canvas
332 return showBuffer( bIsVisible
, bUpdateAll
);
335 uno::Any
SpriteDeviceHelper::isAccelerated() const
337 return css::uno::Any(false);
340 uno::Any
SpriteDeviceHelper::getDeviceHandle() const
342 const SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
343 const OutputDevice
* pDevice
= pChildWindow
? pChildWindow
->GetOutDev() : nullptr;
344 return uno::Any(reinterpret_cast<sal_Int64
>(pDevice
));
347 uno::Any
SpriteDeviceHelper::getSurfaceHandle() const
352 uno::Reference
<rendering::XColorSpace
> SpriteDeviceHelper::getColorSpace() const
355 return ::canvas::tools::getStdColorSpace();
358 void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle
& rBounds
)
360 if( mxContext
->isInitialized() )
362 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
363 pChildWindow
->setPosSizePixel(
364 0,0,rBounds
.Width
,rBounds
.Height
);
368 void SpriteDeviceHelper::dumpScreenContent() const
370 SAL_INFO("canvas.ogl", __func__
);
373 void SpriteDeviceHelper::show( const ::rtl::Reference
< CanvasCustomSprite
>& xSprite
)
375 maActiveSprites
.insert(xSprite
);
378 void SpriteDeviceHelper::hide( const ::rtl::Reference
< CanvasCustomSprite
>& xSprite
)
380 maActiveSprites
.erase(xSprite
);
383 static void setupUniforms( unsigned int nProgramId
,
384 const ::basegfx::B2DHomMatrix
& rTexTransform
)
386 const GLint nTransformLocation
= glGetUniformLocation(nProgramId
,
388 // OGL is column-major
389 float aTexTransform
[] =
391 float(rTexTransform
.get(0,0)), float(rTexTransform
.get(1,0)),
392 float(rTexTransform
.get(0,1)), float(rTexTransform
.get(1,1)),
393 float(rTexTransform
.get(0,2)), float(rTexTransform
.get(1,2))
395 glUniformMatrix3x2fv(nTransformLocation
,1,false,aTexTransform
);
398 static void setupUniforms( unsigned int nProgramId
,
399 const rendering::ARGBColor
* pColors
,
400 const uno::Sequence
< double >& rStops
,
401 const ::basegfx::B2DHomMatrix
& rTexTransform
)
403 glUseProgram(nProgramId
);
405 GLuint nColorsTexture
;
406 glActiveTexture(GL_TEXTURE0
);
407 glGenTextures(1, &nColorsTexture
);
408 glBindTexture(GL_TEXTURE_1D
, nColorsTexture
);
410 const sal_Int32 nColors
=rStops
.getLength();
411 glTexImage1D( GL_TEXTURE_1D
, 0, GL_RGBA
, nColors
, 0, GL_RGBA
, GL_DOUBLE
, pColors
);
412 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
413 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
415 GLuint nStopsTexture
;
416 glActiveTexture(GL_TEXTURE1
);
417 glGenTextures(1, &nStopsTexture
);
418 glBindTexture(GL_TEXTURE_1D
, nStopsTexture
);
420 glTexImage1D( GL_TEXTURE_1D
, 0, GL_ALPHA
, nColors
, 0, GL_ALPHA
, GL_DOUBLE
, rStops
.getConstArray() );
421 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
422 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
424 const GLint nColorArrayLocation
= glGetUniformLocation(nProgramId
,
426 glUniform1i( nColorArrayLocation
, 0 ); // unit 0
428 const GLint nStopArrayLocation
= glGetUniformLocation(nProgramId
,
430 glUniform1i( nStopArrayLocation
, 1 ); // unit 1
432 const GLint nNumColorLocation
= glGetUniformLocation(nProgramId
,
434 glUniform1i( nNumColorLocation
, nColors
-1 );
436 setupUniforms(nProgramId
,rTexTransform
);
439 static void setupUniforms( unsigned int nProgramId
,
440 const rendering::ARGBColor
& rStartColor
,
441 const rendering::ARGBColor
& rEndColor
,
442 const ::basegfx::B2DHomMatrix
& rTexTransform
)
444 glUseProgram(nProgramId
);
446 const GLint nStartColorLocation
= glGetUniformLocation(nProgramId
,
448 glUniform4f(nStartColorLocation
,
454 const GLint nEndColorLocation
= glGetUniformLocation(nProgramId
,
456 glUniform4f(nEndColorLocation
,
462 setupUniforms(nProgramId
,rTexTransform
);
465 void SpriteDeviceHelper::useLinearGradientShader( const rendering::ARGBColor
* pColors
,
466 const uno::Sequence
< double >& rStops
,
467 const ::basegfx::B2DHomMatrix
& rTexTransform
)
469 if( rStops
.getLength() > 2 )
470 setupUniforms(mnLinearMultiColorGradientProgram
, pColors
, rStops
, rTexTransform
);
472 setupUniforms(mnLinearTwoColorGradientProgram
, pColors
[0], pColors
[1], rTexTransform
);
475 void SpriteDeviceHelper::useRadialGradientShader( const rendering::ARGBColor
* pColors
,
476 const uno::Sequence
< double >& rStops
,
477 const ::basegfx::B2DHomMatrix
& rTexTransform
)
479 if( rStops
.getLength() > 2 )
480 setupUniforms(mnRadialMultiColorGradientProgram
, pColors
, rStops
, rTexTransform
);
482 setupUniforms(mnRadialTwoColorGradientProgram
, pColors
[0], pColors
[1], rTexTransform
);
485 void SpriteDeviceHelper::useRectangularGradientShader( const rendering::ARGBColor
* pColors
,
486 const uno::Sequence
< double >& rStops
,
487 const ::basegfx::B2DHomMatrix
& rTexTransform
)
489 if( rStops
.getLength() > 2 )
490 setupUniforms(mnRectangularMultiColorGradientProgram
, pColors
, rStops
, rTexTransform
);
492 setupUniforms(mnRectangularTwoColorGradientProgram
, pColors
[0], pColors
[1], rTexTransform
);
498 class BufferContextImpl
: public IBufferContext
500 GLuint mnFramebufferId
;
504 virtual void startBufferRendering() override
506 glBindFramebuffer(GL_FRAMEBUFFER
, mnFramebufferId
);
509 virtual void endBufferRendering() override
511 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
514 virtual GLuint
getTextureId() override
520 explicit BufferContextImpl(const ::basegfx::B2IVector
& rSize
) :
525 OpenGLHelper::createFramebuffer(rSize
.getX(), rSize
.getY(), mnFramebufferId
,
526 mnDepthId
, mnTextureId
);
529 virtual ~BufferContextImpl() override
531 glDeleteTextures(1, &mnTextureId
);
532 glDeleteRenderbuffers(1, &mnDepthId
);
533 glDeleteFramebuffers(1, &mnFramebufferId
);
538 IBufferContextSharedPtr
SpriteDeviceHelper::createBufferContext(const ::basegfx::B2IVector
& rSize
) const
540 return std::make_shared
<BufferContextImpl
>(rSize
);
543 TextureCache
& SpriteDeviceHelper::getTextureCache() const
545 return *mpTextureCache
;
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */