Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / opengl / ogl_spritedevicehelper.cxx
blobe76b86e3f67534355d19786231607c382a26e48a
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 "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);
44 glLoadIdentity();
46 // misc preferences
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)
58 // use whole window
59 glViewport( 0,0,
60 (GLsizei)rSize.Width(),
61 (GLsizei)rSize.Height() );
63 // model coordinate system is already in device pixel
64 glMatrixMode(GL_MODELVIEW);
65 glLoadIdentity();
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(),
69 1.0 );
71 // clear to black
72 glClearColor(0,0,0,0);
73 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
76 namespace oglcanvas
79 SpriteDeviceHelper::SpriteDeviceHelper() :
80 mpDevice(NULL),
81 mpSpriteCanvas(NULL),
82 maActiveSprites(),
83 maLastUpdate(),
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
111 initContext();
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;
141 mpDevice = 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 )
189 // disposed?
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 )
202 // disposed?
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 )
215 // disposed?
216 if( !mpSpriteCanvas )
217 return uno::Reference< rendering::XBitmap >(); // we're disposed
219 return uno::Reference< rendering::XBitmap >(
220 new CanvasBitmap( size,
221 mpSpriteCanvas,
222 *this,
223 false ) );
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 )
237 // disposed?
238 if( !mpSpriteCanvas )
239 return uno::Reference< rendering::XBitmap >(); // we're disposed
241 return uno::Reference< rendering::XBitmap >(
242 new CanvasBitmap( size,
243 mpSpriteCanvas,
244 *this,
245 true ) );
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 >();
255 namespace
257 /** Functor providing a StrictWeakOrdering for XSprites (over
258 priority)
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 )
278 return false;
280 if( !activateWindowContext() )
281 return false;
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(),
297 aSprites.end(),
298 SpriteComparator());
299 std::for_each(aSprites.begin(),
300 aSprites.end(),
301 boost::mem_fn(&CanvasCustomSprite::renderSprite));
304 // frame counter, other info
305 glMatrixMode(GL_MODELVIEW);
306 glLoadIdentity();
307 glTranslated(-1.0, 1.0, 0.0);
308 glScaled( 2.0 / rOutputSize.Width(),
309 -2.0 / rOutputSize.Height(),
310 1.0 );
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),
328 aXWindow);
329 pChildWindow->Show();
330 unx::glXWaitGL();
331 XSync( reinterpret_cast<unx::Display*>(mpDisplay), false );
333 mxContext->swapBuffers();
335 // flush texture cache, such that it does not build up
336 // indefinitely.
337 // TODO: have max cache size/LRU time in config, prune only on
338 // demand
339 mpTextureCache->prune();
341 return true;
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
363 return uno::Any();
366 uno::Reference<rendering::XColorSpace> SpriteDeviceHelper::getColorSpace() const
368 // always the same
369 return uno::Reference<rendering::XColorSpace>(
370 ::canvas::tools::getStdColorSpace(),
371 uno::UNO_QUERY);
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,
403 "m_transform" );
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,
441 "t_colorArray4d" );
442 glUniform1i( nColorArrayLocation, 0 ); // unit 0
444 const GLint nStopArrayLocation = glGetUniformLocation(nProgramId,
445 "t_stopArray1d" );
446 glUniform1i( nStopArrayLocation, 1 ); // unit 1
448 const GLint nNumColorLocation = glGetUniformLocation(nProgramId,
449 "i_nColors" );
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,
463 "v_startColor4d" );
464 glUniform4f(nStartColorLocation,
465 rStartColor.Red,
466 rStartColor.Green,
467 rStartColor.Blue,
468 rStartColor.Alpha);
470 const GLint nEndColorLocation = glGetUniformLocation(nProgramId,
471 "v_endColor4d" );
472 glUniform4f(nEndColorLocation,
473 rEndColor.Red,
474 rEndColor.Green,
475 rEndColor.Blue,
476 rEndColor.Alpha);
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);
487 else
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);
497 else
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);
507 else
508 setupUniforms(mnRectangularTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform);
511 bool SpriteDeviceHelper::activateWindowContext()
513 mxContext->makeCurrent();
514 return true;
517 namespace
520 class BufferContextImpl : public IBufferContext
522 ::basegfx::B2IVector maSize;
523 GLuint mnFrambufferId;
524 GLuint mnDepthId;
525 GLuint mnTextureId;
527 virtual bool startBufferRendering() SAL_OVERRIDE
529 glBindFramebuffer(GL_FRAMEBUFFER, mnFrambufferId);
530 return true;
533 virtual bool endBufferRendering() SAL_OVERRIDE
535 glBindFramebuffer(GL_FRAMEBUFFER, 0);
536 return true;
539 virtual GLuint getTextureId() SAL_OVERRIDE
541 return mnTextureId;
544 public:
545 BufferContextImpl(const ::basegfx::B2IVector& rSize) :
546 maSize(rSize),
547 mnFrambufferId(0),
548 mnDepthId(0),
549 mnTextureId(0)
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: */