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 "ogl_spritedevicehelper.hxx"
11 #include "ogl_spritecanvas.hxx"
12 #include "ogl_canvasbitmap.hxx"
13 #include "ogl_canvastools.hxx"
14 #include "ogl_canvascustomsprite.hxx"
15 #include "ogl_texturecache.hxx"
17 #include <canvas/verbosetrace.hxx>
18 #include <basegfx/tools/canvastools.hxx>
19 #include <basegfx/tools/unopolypolygon.hxx>
21 #include <osl/mutex.hxx>
22 #include <rtl/instance.hxx>
23 #include <com/sun/star/uno/Reference.hxx>
24 #include <com/sun/star/lang/NoSupportException.hpp>
25 #include <com/sun/star/rendering/XColorSpace.hpp>
26 #include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
28 #include <vcl/sysdata.hxx>
29 #include <vcl/syschild.hxx>
30 #include <vcl/canvastools.hxx>
31 #include <toolkit/helper/vclunohelper.hxx>
33 #include <vcl/opengl/OpenGLHelper.hxx>
35 using namespace ::com::sun::star
;
37 static void initContext()
39 // need the backside for mirror effects
40 glDisable(GL_CULL_FACE
);
42 // no perspective, we're 2D
43 glMatrixMode(GL_PROJECTION
);
47 glEnable(GL_POINT_SMOOTH
);
48 glEnable(GL_LINE_SMOOTH
);
49 glEnable(GL_POLYGON_SMOOTH
);
50 glHint(GL_POINT_SMOOTH_HINT
,GL_NICEST
);
51 glHint(GL_LINE_SMOOTH_HINT
,GL_NICEST
);
52 glHint(GL_POLYGON_SMOOTH_HINT
,GL_NICEST
);
53 glShadeModel(GL_FLAT
);
56 static void initTransformation(const ::Size
& rSize
, bool bMirror
=false)
60 (GLsizei
)rSize
.Width(),
61 (GLsizei
)rSize
.Height() );
63 // model coordinate system is already in device pixel
64 glMatrixMode(GL_MODELVIEW
);
66 glTranslated(-1.0, (bMirror
? -1.0 : 1.0), 0.0);
67 glScaled( 2.0 / rSize
.Width(),
68 (bMirror
? 2.0 : -2.0) / rSize
.Height(),
72 glClearColor(0,0,0,0);
73 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
79 SpriteDeviceHelper::SpriteDeviceHelper() :
84 mpTextureCache(new TextureCache()),
85 mnLinearTwoColorGradientProgram(0),
86 mnLinearMultiColorGradientProgram(0),
87 mnRadialTwoColorGradientProgram(0),
88 mnRadialMultiColorGradientProgram(0),
89 mnRectangularTwoColorGradientProgram(0),
90 mnRectangularMultiColorGradientProgram(0),
91 mxContext(OpenGLContext::Create())
94 SpriteDeviceHelper::~SpriteDeviceHelper()
95 { mxContext
->dispose(); }
97 void SpriteDeviceHelper::init( vcl::Window
& rWindow
,
98 SpriteCanvas
& rSpriteCanvas
,
99 const awt::Rectangle
& rViewArea
)
101 mpSpriteCanvas
= &rSpriteCanvas
;
103 rSpriteCanvas
.setWindow(
104 uno::Reference
<awt::XWindow2
>(
105 VCLUnoHelper::GetInterface(&rWindow
),
106 uno::UNO_QUERY_THROW
) );
108 mxContext
->requestLegacyContext();
109 mxContext
->init(&rWindow
);
110 // init window context
113 mnLinearMultiColorGradientProgram
=
114 OpenGLHelper::LoadShaders("dummyVertexShader", "linearMultiColorGradientFragmentShader");
116 mnLinearTwoColorGradientProgram
=
117 OpenGLHelper::LoadShaders("dummyVertexShader", "linearTwoColorGradientFragmentShader");
119 mnRadialMultiColorGradientProgram
=
120 OpenGLHelper::LoadShaders("dummyVertexShader", "radialMultiColorGradientFragmentShader");
122 mnRadialTwoColorGradientProgram
=
123 OpenGLHelper::LoadShaders("dummyVertexShader", "radialTwoColorGradientFragmentShader");
125 mnRectangularMultiColorGradientProgram
=
126 OpenGLHelper::LoadShaders("dummyVertexShader", "rectangularMultiColorGradientFragmentShader");
128 mnRectangularTwoColorGradientProgram
=
129 OpenGLHelper::LoadShaders("dummyVertexShader", "rectangularTwoColorGradientFragmentShader");
131 mxContext
->makeCurrent();
133 notifySizeUpdate(rViewArea
);
134 // TODO(E3): check for GL_ARB_imaging extension
137 void SpriteDeviceHelper::disposing()
139 // release all references
140 mpSpriteCanvas
= NULL
;
142 mpTextureCache
.reset();
144 if( mxContext
->isInitialized() )
146 glDeleteProgram( mnRectangularTwoColorGradientProgram
);
147 glDeleteProgram( mnRectangularMultiColorGradientProgram
);
148 glDeleteProgram( mnRadialTwoColorGradientProgram
);
149 glDeleteProgram( mnRadialMultiColorGradientProgram
);
150 glDeleteProgram( mnLinearTwoColorGradientProgram
);
151 glDeleteProgram( mnLinearMultiColorGradientProgram
);
155 geometry::RealSize2D
SpriteDeviceHelper::getPhysicalResolution()
157 if( !mxContext
->isInitialized() )
158 return ::canvas::tools::createInfiniteSize2D(); // we're disposed
160 // Map a one-by-one millimeter box to pixel
161 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
162 const MapMode
aOldMapMode( pChildWindow
->GetMapMode() );
163 pChildWindow
->SetMapMode( MapMode(MAP_MM
) );
164 const Size
aPixelSize( pChildWindow
->LogicToPixel(Size(1,1)) );
165 pChildWindow
->SetMapMode( aOldMapMode
);
167 return vcl::unotools::size2DFromSize( aPixelSize
);
170 geometry::RealSize2D
SpriteDeviceHelper::getPhysicalSize()
172 if( !mxContext
->isInitialized() )
173 return ::canvas::tools::createInfiniteSize2D(); // we're disposed
175 // Map the pixel dimensions of the output window to millimeter
176 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
177 const MapMode
aOldMapMode( pChildWindow
->GetMapMode() );
178 pChildWindow
->SetMapMode( MapMode(MAP_MM
) );
179 const Size
aLogSize( pChildWindow
->PixelToLogic(pChildWindow
->GetOutputSizePixel()) );
180 pChildWindow
->SetMapMode( aOldMapMode
);
182 return vcl::unotools::size2DFromSize( aLogSize
);
185 uno::Reference
< rendering::XLinePolyPolygon2D
> SpriteDeviceHelper::createCompatibleLinePolyPolygon(
186 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
187 const uno::Sequence
< uno::Sequence
< geometry::RealPoint2D
> >& points
)
190 if( !mpSpriteCanvas
)
191 return uno::Reference
< rendering::XLinePolyPolygon2D
>(); // we're disposed
193 return uno::Reference
< rendering::XLinePolyPolygon2D
>(
194 new ::basegfx::unotools::UnoPolyPolygon(
195 ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points
)));
198 uno::Reference
< rendering::XBezierPolyPolygon2D
> SpriteDeviceHelper::createCompatibleBezierPolyPolygon(
199 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
200 const uno::Sequence
< uno::Sequence
< geometry::RealBezierSegment2D
> >& points
)
203 if( !mpSpriteCanvas
)
204 return uno::Reference
< rendering::XBezierPolyPolygon2D
>(); // we're disposed
206 return uno::Reference
< rendering::XBezierPolyPolygon2D
>(
207 new ::basegfx::unotools::UnoPolyPolygon(
208 ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points
) ) );
211 uno::Reference
< rendering::XBitmap
> SpriteDeviceHelper::createCompatibleBitmap(
212 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
213 const geometry::IntegerSize2D
& size
)
216 if( !mpSpriteCanvas
)
217 return uno::Reference
< rendering::XBitmap
>(); // we're disposed
219 return uno::Reference
< rendering::XBitmap
>(
220 new CanvasBitmap( size
,
226 uno::Reference
< rendering::XVolatileBitmap
> SpriteDeviceHelper::createVolatileBitmap(
227 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
228 const geometry::IntegerSize2D
& /*size*/ )
230 return uno::Reference
< rendering::XVolatileBitmap
>();
233 uno::Reference
< rendering::XBitmap
> SpriteDeviceHelper::createCompatibleAlphaBitmap(
234 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
235 const geometry::IntegerSize2D
& size
)
238 if( !mpSpriteCanvas
)
239 return uno::Reference
< rendering::XBitmap
>(); // we're disposed
241 return uno::Reference
< rendering::XBitmap
>(
242 new CanvasBitmap( size
,
248 uno::Reference
< rendering::XVolatileBitmap
> SpriteDeviceHelper::createVolatileAlphaBitmap(
249 const uno::Reference
< rendering::XGraphicDevice
>& /*rDevice*/,
250 const geometry::IntegerSize2D
& /*size*/ )
252 return uno::Reference
< rendering::XVolatileBitmap
>();
257 /** Functor providing a StrictWeakOrdering for XSprites (over
260 struct SpriteComparator
262 bool operator()( const ::rtl::Reference
<CanvasCustomSprite
>& rLHS
,
263 const ::rtl::Reference
<CanvasCustomSprite
>& rRHS
) const
265 const double nPrioL( rLHS
->getPriority() );
266 const double nPrioR( rRHS
->getPriority() );
268 // if prios are equal, tie-break on ptr value
269 return nPrioL
== nPrioR
? rLHS
.get() < rRHS
.get() : nPrioL
< nPrioR
;
274 bool SpriteDeviceHelper::showBuffer( bool bIsVisible
, bool /*bUpdateAll*/ )
276 // hidden or disposed?
277 if( !bIsVisible
|| !mxContext
->isInitialized() || !mpSpriteCanvas
)
280 if( !activateWindowContext() )
283 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
284 const ::Size
& rOutputSize
= pChildWindow
->GetSizePixel();
285 initTransformation(rOutputSize
);
287 // render the actual spritecanvas content
288 mpSpriteCanvas
->renderRecordedActions();
290 // render all sprites (in order of priority) on top of that
291 std::vector
< ::rtl::Reference
<CanvasCustomSprite
> > aSprites
;
292 std::copy(maActiveSprites
.begin(),
293 maActiveSprites
.end(),
294 std::back_insert_iterator
<
295 std::vector
< ::rtl::Reference
< CanvasCustomSprite
> > >(aSprites
));
296 std::sort(aSprites
.begin(),
299 std::for_each(aSprites
.begin(),
301 boost::mem_fn(&CanvasCustomSprite::renderSprite
));
304 // frame counter, other info
305 glMatrixMode(GL_MODELVIEW
);
307 glTranslated(-1.0, 1.0, 0.0);
308 glScaled( 2.0 / rOutputSize
.Width(),
309 -2.0 / rOutputSize
.Height(),
312 const double denominator( maLastUpdate
.getElapsedTime() );
313 maLastUpdate
.reset();
315 const double fps(denominator
== 0.0 ? 100.0 : 1.0/denominator
);
316 std::vector
<double> aVec
; aVec
.push_back(fps
);
317 aVec
.push_back(maActiveSprites
.size());
318 aVec
.push_back(mpTextureCache
->getCacheSize());
319 aVec
.push_back(mpTextureCache
->getCacheMissCount());
320 aVec
.push_back(mpTextureCache
->getCacheHitCount());
321 renderOSD( aVec
, 20 );
324 * TODO: moggi: fix it!
325 // switch buffer, sync etc.
326 const unx::Window aXWindow=pChildWindow->GetSystemData()->aWindow;
327 unx::glXSwapBuffers(reinterpret_cast<unx::Display*>(mpDisplay),
329 pChildWindow->Show();
331 XSync( reinterpret_cast<unx::Display*>(mpDisplay), false );
333 mxContext
->swapBuffers();
335 // flush texture cache, such that it does not build up
337 // TODO: have max cache size/LRU time in config, prune only on
339 mpTextureCache
->prune();
344 bool SpriteDeviceHelper::switchBuffer( bool bIsVisible
, bool bUpdateAll
)
346 // no difference for VCL canvas
347 return showBuffer( bIsVisible
, bUpdateAll
);
350 uno::Any
SpriteDeviceHelper::isAccelerated() const
352 return ::com::sun::star::uno::makeAny(false);
355 uno::Any
SpriteDeviceHelper::getDeviceHandle() const
357 const SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
358 return uno::makeAny( reinterpret_cast< sal_Int64
>(pChildWindow
) );
361 uno::Any
SpriteDeviceHelper::getSurfaceHandle() const
366 uno::Reference
<rendering::XColorSpace
> SpriteDeviceHelper::getColorSpace() const
369 return uno::Reference
<rendering::XColorSpace
>(
370 ::canvas::tools::getStdColorSpace(),
374 void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle
& rBounds
)
376 if( mxContext
->isInitialized() )
378 SystemChildWindow
* pChildWindow
= mxContext
->getChildWindow();
379 pChildWindow
->setPosSizePixel(
380 0,0,rBounds
.Width
,rBounds
.Height
);
384 void SpriteDeviceHelper::dumpScreenContent() const
386 SAL_INFO("canvas.ogl", BOOST_CURRENT_FUNCTION
);
389 void SpriteDeviceHelper::show( const ::rtl::Reference
< CanvasCustomSprite
>& xSprite
)
391 maActiveSprites
.insert(xSprite
);
394 void SpriteDeviceHelper::hide( const ::rtl::Reference
< CanvasCustomSprite
>& xSprite
)
396 maActiveSprites
.erase(xSprite
);
399 static void setupUniforms( unsigned int nProgramId
,
400 const ::basegfx::B2DHomMatrix
& rTexTransform
)
402 const GLint nTransformLocation
= glGetUniformLocation(nProgramId
,
404 // OGL is column-major
405 float aTexTransform
[] =
407 float(rTexTransform
.get(0,0)), float(rTexTransform
.get(1,0)),
408 float(rTexTransform
.get(0,1)), float(rTexTransform
.get(1,1)),
409 float(rTexTransform
.get(0,2)), float(rTexTransform
.get(1,2))
411 glUniformMatrix3x2fv(nTransformLocation
,1,false,aTexTransform
);
414 static void setupUniforms( unsigned int nProgramId
,
415 const rendering::ARGBColor
* pColors
,
416 const uno::Sequence
< double >& rStops
,
417 const ::basegfx::B2DHomMatrix
& rTexTransform
)
419 glUseProgram(nProgramId
);
421 GLuint nColorsTexture
;
422 glActiveTexture(GL_TEXTURE0
);
423 glGenTextures(1, &nColorsTexture
);
424 glBindTexture(GL_TEXTURE_1D
, nColorsTexture
);
426 const sal_Int32 nColors
=rStops
.getLength();
427 glTexImage1D( GL_TEXTURE_1D
, 0, GL_RGBA
, nColors
, 0, GL_RGBA
, GL_DOUBLE
, pColors
);
428 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
429 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
431 GLuint nStopsTexture
;
432 glActiveTexture(GL_TEXTURE1
);
433 glGenTextures(1, &nStopsTexture
);
434 glBindTexture(GL_TEXTURE_1D
, nStopsTexture
);
436 glTexImage1D( GL_TEXTURE_1D
, 0, GL_ALPHA
, nColors
, 0, GL_ALPHA
, GL_DOUBLE
, rStops
.getConstArray() );
437 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
438 glTexParameteri( GL_TEXTURE_1D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
440 const GLint nColorArrayLocation
= glGetUniformLocation(nProgramId
,
442 glUniform1i( nColorArrayLocation
, 0 ); // unit 0
444 const GLint nStopArrayLocation
= glGetUniformLocation(nProgramId
,
446 glUniform1i( nStopArrayLocation
, 1 ); // unit 1
448 const GLint nNumColorLocation
= glGetUniformLocation(nProgramId
,
450 glUniform1i( nNumColorLocation
, nColors
-1 );
452 setupUniforms(nProgramId
,rTexTransform
);
455 static void setupUniforms( unsigned int nProgramId
,
456 const rendering::ARGBColor
& rStartColor
,
457 const rendering::ARGBColor
& rEndColor
,
458 const ::basegfx::B2DHomMatrix
& rTexTransform
)
460 glUseProgram(nProgramId
);
462 const GLint nStartColorLocation
= glGetUniformLocation(nProgramId
,
464 glUniform4f(nStartColorLocation
,
470 const GLint nEndColorLocation
= glGetUniformLocation(nProgramId
,
472 glUniform4f(nEndColorLocation
,
478 setupUniforms(nProgramId
,rTexTransform
);
481 void SpriteDeviceHelper::useLinearGradientShader( const rendering::ARGBColor
* pColors
,
482 const uno::Sequence
< double >& rStops
,
483 const ::basegfx::B2DHomMatrix
& rTexTransform
)
485 if( rStops
.getLength() > 2 )
486 setupUniforms(mnLinearMultiColorGradientProgram
, pColors
, rStops
, rTexTransform
);
488 setupUniforms(mnLinearTwoColorGradientProgram
, pColors
[0], pColors
[1], rTexTransform
);
491 void SpriteDeviceHelper::useRadialGradientShader( const rendering::ARGBColor
* pColors
,
492 const uno::Sequence
< double >& rStops
,
493 const ::basegfx::B2DHomMatrix
& rTexTransform
)
495 if( rStops
.getLength() > 2 )
496 setupUniforms(mnRadialMultiColorGradientProgram
, pColors
, rStops
, rTexTransform
);
498 setupUniforms(mnRadialTwoColorGradientProgram
, pColors
[0], pColors
[1], rTexTransform
);
501 void SpriteDeviceHelper::useRectangularGradientShader( const rendering::ARGBColor
* pColors
,
502 const uno::Sequence
< double >& rStops
,
503 const ::basegfx::B2DHomMatrix
& rTexTransform
)
505 if( rStops
.getLength() > 2 )
506 setupUniforms(mnRectangularMultiColorGradientProgram
, pColors
, rStops
, rTexTransform
);
508 setupUniforms(mnRectangularTwoColorGradientProgram
, pColors
[0], pColors
[1], rTexTransform
);
511 bool SpriteDeviceHelper::activateWindowContext()
513 mxContext
->makeCurrent();
520 class BufferContextImpl
: public IBufferContext
522 ::basegfx::B2IVector maSize
;
523 GLuint mnFrambufferId
;
527 virtual bool startBufferRendering() SAL_OVERRIDE
529 glBindFramebuffer(GL_FRAMEBUFFER
, mnFrambufferId
);
533 virtual bool endBufferRendering() SAL_OVERRIDE
535 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
539 virtual GLuint
getTextureId() SAL_OVERRIDE
545 BufferContextImpl(const ::basegfx::B2IVector
& rSize
) :
551 OpenGLHelper::createFramebuffer(maSize
.getX(), maSize
.getY(), mnFrambufferId
,
552 mnDepthId
, mnTextureId
, false);
555 virtual ~BufferContextImpl()
557 glDeleteTextures(1, &mnTextureId
);
558 glDeleteRenderbuffers(1, &mnDepthId
);
559 glDeleteFramebuffers(1, &mnFrambufferId
);
564 IBufferContextSharedPtr
SpriteDeviceHelper::createBufferContext(const ::basegfx::B2IVector
& rSize
) const
566 return IBufferContextSharedPtr(new BufferContextImpl(rSize
));
569 TextureCache
& SpriteDeviceHelper::getTextureCache() const
571 return *mpTextureCache
;
575 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */