diff options
Diffstat (limited to 'canvas/source/opengl/ogl_spritedevicehelper.cxx')
-rw-r--r-- | canvas/source/opengl/ogl_spritedevicehelper.cxx | 1249 |
1 files changed, 1249 insertions, 0 deletions
diff --git a/canvas/source/opengl/ogl_spritedevicehelper.cxx b/canvas/source/opengl/ogl_spritedevicehelper.cxx new file mode 100644 index 000000000000..cb96f2e6cf94 --- /dev/null +++ b/canvas/source/opengl/ogl_spritedevicehelper.cxx @@ -0,0 +1,1249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_spritedevicehelper.hxx" +#include "ogl_spritecanvas.hxx" +#include "ogl_canvasbitmap.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_canvascustomsprite.hxx" +#include "ogl_texturecache.hxx" + +#include <canvas/verbosetrace.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/tools/unopolypolygon.hxx> + +#include <osl/mutex.hxx> +#include <rtl/instance.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/rendering/XColorSpace.hpp> +#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp> + +#include <vcl/sysdata.hxx> +#include <vcl/syschild.hxx> +#include <vcl/canvastools.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#define GL_GLEXT_PROTOTYPES +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glext.h> + +namespace unx +{ + #include <X11/keysym.h> + #include <X11/X.h> + #include <GL/glx.h> + #include <GL/glxext.h> +} + + +using namespace ::com::sun::star; + +static bool lcl_bErrorTriggered=false; +static int lcl_XErrorHandler( unx::Display*, unx::XErrorEvent* ) +{ + lcl_bErrorTriggered = true; + return 0; +} + +/** Dummy vertex processing. Simply uses default pipeline for vertex + transformation, and forwards texture coodinates to fragment shader + */ +static const char dummyVertexShader[] = +{ + "varying vec2 v_textureCoords2d; " + "void main(void) " + "{ " + " gl_Position = ftransform(); " + " v_textureCoords2d = gl_MultiTexCoord0.st; " + "} " +}; + +/** Two-color linear gradient + */ +static const char linearTwoColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform vec4 v_startColor4d; " + "uniform vec4 v_endColor4d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "void main(void) " + "{ " + " gl_FragColor = mix(v_startColor4d, " + " v_endColor4d, " + " clamp( " + " (m_transform * vec3(v_textureCoords2d,1)).s, " + " 0.0, 1.0)); " + "} " +}; + +/** N-color linear gradient + */ +static const char linearMultiColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform int i_nColors; " + "uniform sampler1D t_colorArray4d; " + "uniform sampler1D t_stopArray1d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + " " + "int findBucket(float t) " + "{ " + " int nMinBucket=0; " + " while( nMinBucket < i_nColors && " + " texture1D(t_stopArray1d, nMinBucket).s < t ) " + " ++nMinBucket; " + " return max(nMinBucket-1,0); " + "} " + " " + "void main(void) " + "{ " + " const float fAlpha = " + " clamp( (m_transform * vec3(v_textureCoords2d,1)).s, " + " 0.0, 1.0 ); " + " " + " const int nMinBucket=findBucket( fAlpha ); " + " " + " const float fLerp = " + " (fAlpha-texture1D(t_stopArray1d, nMinBucket).s) / " + " (texture1D(t_stopArray1d, nMinBucket+1).s - " + " texture1D(t_stopArray1d, nMinBucket).s); " + " " + " gl_FragColor = mix(texture1D(t_colorArray4d, nMinBucket), " + " texture1D(t_colorArray4d, nMinBucket+1), " + " fLerp); " + "} " +}; + +/** Two-color radial gradient + */ +static const char radialTwoColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform vec4 v_startColor4d; " + "uniform vec4 v_endColor4d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "const vec2 v_center2d = vec2(0,0); " + "void main(void) " + "{ " + " gl_FragColor = mix(v_startColor4d, " + " v_endColor4d, " + " 1.0 - distance( " + " vec2( " + " m_transform * vec3(v_textureCoords2d,1)), " + " v_center2d)); " + "} " +}; + +/** Multi-color radial gradient + */ +static const char radialMultiColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform int i_nColors; " + "uniform sampler1D t_colorArray4d; " + "uniform sampler1D t_stopArray1d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "const vec2 v_center2d = vec2(0,0); " + " " + "int findBucket(float t) " + "{ " + " int nMinBucket=0; " + " while( nMinBucket < i_nColors && " + " texture1D(t_stopArray1d, nMinBucket).s < t ) " + " ++nMinBucket; " + " return max(nMinBucket-1,0); " + "} " + " " + "void main(void) " + "{ " + " const float fAlpha = " + " clamp( 1.0 - distance( " + " vec2( m_transform * vec3(v_textureCoords2d,1)), " + " v_center2d), " + " 0.0, 1.0 ); " + " " + " const int nMinBucket=findBucket( fAlpha ); " + " " + " const float fLerp = " + " (fAlpha-texture1D(t_stopArray1d, nMinBucket).s) / " + " (texture1D(t_stopArray1d, nMinBucket+1).s - " + " texture1D(t_stopArray1d, nMinBucket).s); " + " " + " gl_FragColor = mix(texture1D(t_colorArray4d, nMinBucket), " + " texture1D(t_colorArray4d, nMinBucket+1), " + " fLerp); " + "} " +}; + +/** Two-color rectangular gradient + */ +static const char rectangularTwoColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform vec4 v_startColor4d; " + "uniform vec4 v_endColor4d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "void main(void) " + "{ " + " const vec2 v = abs( vec2(m_transform * vec3(v_textureCoords2d,1)) ); " + " const float t = max(v.x, v.y); " + " gl_FragColor = mix(v_startColor4d, " + " v_endColor4d, " + " 1.0-t); " + "} " +}; + +/** Multi-color rectangular gradient + */ +static const char rectangularMultiColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform int i_nColors; " + "uniform sampler1D t_colorArray4d; " + "uniform sampler1D t_stopArray1d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + " " + "int findBucket(float t) " + "{ " + " int nMinBucket=0; " + " while( nMinBucket < i_nColors && " + " texture1D(t_stopArray1d, nMinBucket).s < t ) " + " ++nMinBucket; " + " return max(nMinBucket-1,0); " + "} " + " " + "void main(void) " + "{ " + " const vec2 v = abs( vec2(m_transform * vec3(v_textureCoords2d,1)) ); " + " const float fAlpha = 1 - max(v.x, v.y); " + " " + " const int nMinBucket=findBucket( fAlpha ); " + " " + " const float fLerp = " + " (fAlpha-texture1D(t_stopArray1d, nMinBucket).s) / " + " (texture1D(t_stopArray1d, nMinBucket+1).s - " + " texture1D(t_stopArray1d, nMinBucket).s); " + " " + " gl_FragColor = mix(texture1D(t_colorArray4d, nMinBucket), " + " texture1D(t_colorArray4d, nMinBucket+1), " + " fLerp); " + "} " +}; + +static void initContext() +{ + // need the backside for mirror effects + glDisable(GL_CULL_FACE); + + // no perspective, we're 2D + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // misc preferences + glEnable(GL_POINT_SMOOTH); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT,GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST); + glShadeModel(GL_FLAT); +} + +static void initTransformation(const ::Size& rSize, bool bMirror=false) +{ + // use whole window + glViewport( 0,0, + (GLsizei)rSize.Width(), + (GLsizei)rSize.Height() ); + + // model coordinate system is already in device pixel + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslated(-1.0, (bMirror ? -1.0 : 1.0), 0.0); + glScaled( 2.0 / rSize.Width(), + (bMirror ? 2.0 : -2.0) / rSize.Height(), + 1.0 ); + + // clear to black + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +static boost::shared_ptr<SystemChildWindow> createChildWindow( unx::XVisualInfo*& viWin, + unx::XVisualInfo*& viPB, + void*& fbConfig, + Window& rWindow, + unx::Display* pDisplay, + int nScreen ) +{ + // select appropriate visual + static int winAttrList3[] = + { + GLX_RGBA,//only TrueColor or DirectColor + //single buffered + GLX_RED_SIZE,4,//use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,//use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,//use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,0,//no depth buffer + None + }; + static int pBufAttrList3[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,//use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,//use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,//use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,0,//no depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int winAttrList2[] = + { + GLX_RGBA,//only TrueColor or DirectColor + /// single buffered + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,1,/// use the maximum depth bits, making sure there is a depth buffer + None + }; + static int pBufAttrList2[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,1,/// use the maximum depth bits, making sure there is a depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int winAttrList1[] = + { + GLX_RGBA,//only TrueColor or DirectColor + GLX_DOUBLEBUFFER,/// only double buffer + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,0,/// no depth buffer + None + }; + static int pBufAttrList1[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,0,/// no depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int winAttrList0[] = + { + GLX_RGBA,//only TrueColor or DirectColor + GLX_DOUBLEBUFFER,// only double buffer + GLX_RED_SIZE,4,// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,// use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,1,// use the maximum depth bits, making sure there is a depth buffer + None + }; + static int pBufAttrList0[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,// use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,1,// use the maximum depth bits, making sure there is a depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int* winAttrTable[] = + { + winAttrList0, + winAttrList1, + winAttrList2, + winAttrList3, + NULL + }; + static int* pBufAttrTable[] = + { + pBufAttrList0, + pBufAttrList1, + pBufAttrList2, + pBufAttrList3, + NULL + }; + int** pWinAttributeTable = winAttrTable; + int** pBufAttributeTable = pBufAttrTable; + + boost::shared_ptr<SystemChildWindow> pResult; + unx::GLXFBConfig* fbConfigs=NULL; + int nConfigs, nVal; + while( *pWinAttributeTable && *pBufAttributeTable ) + { + // try to find a window visual for the current set of + // attributes + viWin = unx::glXChooseVisual( pDisplay, + nScreen, + *pWinAttributeTable ); + if( viWin ) + { + // try to find a framebuffer config for the current set of + // attributes + fbConfigs = glXChooseFBConfig( pDisplay, + nScreen, + *pBufAttributeTable, + &nConfigs ); + // don't use glXGetFBConfigs, that does not list alpha-configs + // fbConfigs = unx::glXGetFBConfigs(pDisplay, nScreen, &nConfigs); + for(int i=0; i<nConfigs; i++) + { + viPB = glXGetVisualFromFBConfig(pDisplay, fbConfigs[i]); + if( viPB && viPB->visualid != viWin->visualid ) + { + glXGetFBConfigAttrib(pDisplay, + fbConfigs[i], + GLX_DRAWABLE_TYPE, + &nVal); + + if( (GLX_PBUFFER_BIT|GLX_WINDOW_BIT|GLX_PIXMAP_BIT) + == (nVal & (GLX_PBUFFER_BIT|GLX_WINDOW_BIT|GLX_PIXMAP_BIT)) ) + { + SystemWindowData winData; + winData.nSize = sizeof(winData); + SAL_INFO("canvas.ogl", "using VisualID " << viWin->visualid << " for OpenGL canvas"); + winData.pVisual = (void*)(viWin->visual); + pResult.reset( new SystemChildWindow(&rWindow, 0, &winData, false) ); + + if( pResult->GetSystemData() ) + { + fbConfig = &fbConfigs[i]; + return pResult; + } + + pResult.reset(); + } + + XFree(viPB); + } + } + + XFree(viWin); + } + + ++pWinAttributeTable; + ++pBufAttributeTable; + } + + return pResult; +} + + +namespace oglcanvas +{ + /** Compile shader program + + Code courtesy rodo + */ + void SpriteDeviceHelper::compileShader(unsigned int& o_rShaderHandle, + unsigned int eShaderType, + const char* pShaderSourceCode) + { + GLint nCompileStatus; + char log[1024]; + + o_rShaderHandle = glCreateShader( eShaderType ); + glShaderSource( o_rShaderHandle, 1, &pShaderSourceCode, NULL ); + glCompileShader( o_rShaderHandle ); + glGetShaderInfoLog( o_rShaderHandle, sizeof(log), NULL, log ); + SAL_INFO("canvas.ogl", "shader compile log: " << log); + + glGetShaderiv( o_rShaderHandle, GL_COMPILE_STATUS, &nCompileStatus ); + if( !nCompileStatus ) + { + glDeleteShader(o_rShaderHandle); + o_rShaderHandle=0; + } + } + + /** Link vertex & fragment shaders + + Code courtesy rodo + */ + void SpriteDeviceHelper::linkShaders(unsigned int& o_rProgramHandle, + unsigned int nVertexProgramId, + unsigned int nFragmentProgramId) + { + if( !nVertexProgramId || !nFragmentProgramId ) + return; + + o_rProgramHandle = glCreateProgram(); + glAttachShader( o_rProgramHandle, nVertexProgramId ); + glAttachShader( o_rProgramHandle, nFragmentProgramId ); + + char log[1024]; + GLint nProgramLinked; + + glLinkProgram( o_rProgramHandle ); + glGetProgramInfoLog( o_rProgramHandle, sizeof(log), NULL, log ); + SAL_INFO("canvas.ogl", "shader program link log: " << log); + glGetProgramiv( o_rProgramHandle, GL_LINK_STATUS, &nProgramLinked ); + + if( !nProgramLinked ) + { + glDeleteProgram(o_rProgramHandle); + o_rProgramHandle=0; + } + } + + SpriteDeviceHelper::SpriteDeviceHelper() : + mpDevice(NULL), + mpSpriteCanvas(NULL), + maActiveSprites(), + maLastUpdate(), + mpChildWindow(), + mpDisplay(NULL), + mpGLContext(NULL), + mpGLPBufContext(NULL), + mpFBConfig(NULL), + mpTextureCache(new TextureCache()), + mnDummyVertexProgram(0), + mnLinearTwoColorGradientFragmentProgram(0), + mnLinearMultiColorGradientFragmentProgram(0), + mnRadialTwoColorGradientFragmentProgram(0), + mnRadialMultiColorGradientFragmentProgram(0), + mnRectangularTwoColorGradientFragmentProgram(0), + mnRectangularMultiColorGradientFragmentProgram(0), + mnLinearTwoColorGradientProgram(0), + mnLinearMultiColorGradientProgram(0), + mnRadialTwoColorGradientProgram(0), + mnRadialMultiColorGradientProgram(0), + mnRectangularTwoColorGradientProgram(0), + mnRectangularMultiColorGradientProgram(0) + {} + + void SpriteDeviceHelper::init( Window& rWindow, + SpriteCanvas& rSpriteCanvas, + const awt::Rectangle& rViewArea ) + { + mpSpriteCanvas = &rSpriteCanvas; + + rSpriteCanvas.setWindow( + uno::Reference<awt::XWindow2>( + VCLUnoHelper::GetInterface(&rWindow), + uno::UNO_QUERY_THROW) ); + + // init OpenGL + const SystemEnvData* sysData(rWindow.GetSystemData()); + unx::Display* pDisplay=reinterpret_cast<unx::Display*>(sysData->pDisplay); + mpDisplay=pDisplay; + if( !unx::glXQueryExtension(pDisplay, NULL, NULL) ) + return; + + unx::Window xWindow = sysData->aWindow; + unx::XWindowAttributes xAttr; + unx::XGetWindowAttributes( pDisplay, xWindow, &xAttr ); + int nScreen = XScreenNumberOfScreen( xAttr.screen ); + + unx::Window childXWindow=0; + unx::XVisualInfo* viWin=NULL; + unx::XVisualInfo* viPB=NULL; + mpChildWindow=createChildWindow(viWin,viPB,mpFBConfig, + rWindow,pDisplay,nScreen); + + // tweak SysChild window to act as an input-transparent + // overlay + if( mpChildWindow ) + { + childXWindow=mpChildWindow->GetSystemData()->aWindow; + mpChildWindow->SetMouseTransparent(true); + mpChildWindow->SetParentClipMode( PARENTCLIPMODE_NOCLIP ); + mpChildWindow->EnableEraseBackground(false); + mpChildWindow->SetControlForeground(); + mpChildWindow->SetControlBackground(); + mpChildWindow->EnablePaint(false); + + unx::GLXContext pContext1 = + glXCreateContext(pDisplay, + viWin, + 0, + GL_TRUE); + mpGLContext = pContext1; + + unx::GLXContext pContext2 = + glXCreateContext( pDisplay, + viPB, + pContext1, + GL_TRUE ); + mpGLPBufContext = pContext2; + + XFree(viWin); + XFree(viPB); + + if( !glXMakeCurrent( pDisplay, + childXWindow, + pContext1) ) + { + glXDestroyContext(pDisplay, pContext1); + glXDestroyContext(pDisplay, pContext2); + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Could not select OpenGL context!") ),NULL); + } + + const GLubyte* extensions=glGetString( GL_EXTENSIONS ); + if( gluCheckExtension((const GLubyte*)"GLX_SGI_swap_control", extensions) ) + { + // try to enable vsync + typedef GLint (*glXSwapIntervalProc)(GLint); + glXSwapIntervalProc glXSwapInterval = + (glXSwapIntervalProc) unx::glXGetProcAddress((const GLubyte*)"glXSwapIntervalSGI"); + if( glXSwapInterval ) + { + int (*oldHandler)(unx::Display*, unx::XErrorEvent*); + + // synchronize on global mutex - no other ogl + // canvas instance permitted to enter here + { + ::osl::MutexGuard aGuard( *::osl::Mutex::getGlobalMutex() ); + + // replace error handler temporarily + oldHandler = unx::XSetErrorHandler( lcl_XErrorHandler ); + + lcl_bErrorTriggered = false; + + // Note: if this fails, so be it. Buggy + // drivers will then not have vsync. + glXSwapInterval(1); + + // sync so that we possibly get an XError + unx::glXWaitGL(); + XSync(pDisplay, false); + + unx::XSetErrorHandler( oldHandler ); + } + } + } + + // init window context + initContext(); + + // compile & link shaders - code courtesy rodo + compileShader(mnDummyVertexProgram, + GL_VERTEX_SHADER, + dummyVertexShader); + compileShader(mnLinearTwoColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + linearTwoColorGradientFragmentShader); + compileShader(mnLinearMultiColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + linearMultiColorGradientFragmentShader); + compileShader(mnRadialTwoColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + radialTwoColorGradientFragmentShader); + compileShader(mnRadialMultiColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + radialMultiColorGradientFragmentShader); + compileShader(mnRectangularTwoColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + rectangularTwoColorGradientFragmentShader); + compileShader(mnRectangularMultiColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + rectangularMultiColorGradientFragmentShader); + linkShaders(mnLinearTwoColorGradientProgram, + mnDummyVertexProgram, + mnLinearTwoColorGradientFragmentProgram); + linkShaders(mnLinearMultiColorGradientProgram, + mnDummyVertexProgram, + mnLinearMultiColorGradientFragmentProgram); + linkShaders(mnRadialTwoColorGradientProgram, + mnDummyVertexProgram, + mnRadialTwoColorGradientFragmentProgram); + linkShaders(mnRadialMultiColorGradientProgram, + mnDummyVertexProgram, + mnRadialMultiColorGradientFragmentProgram); + linkShaders(mnRectangularTwoColorGradientProgram, + mnDummyVertexProgram, + mnRectangularTwoColorGradientFragmentProgram); + linkShaders(mnRectangularMultiColorGradientProgram, + mnDummyVertexProgram, + mnRectangularMultiColorGradientFragmentProgram); + + glXMakeCurrent(pDisplay, None, NULL); + } + + if( !mpGLContext || glGetError() != GL_NO_ERROR ) + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Could not create OpenGL context, or an error occurred doing so!") ),NULL); + + notifySizeUpdate(rViewArea); + mpChildWindow->Show(); + // TODO(E3): check for GL_ARB_imaging extension + } + + void SpriteDeviceHelper::disposing() + { + // release all references + mpSpriteCanvas = NULL; + mpDevice = NULL; + mpTextureCache.reset(); + + if( mpGLContext ) + { + glDeleteProgram( mnRectangularTwoColorGradientProgram ); + glDeleteProgram( mnRectangularMultiColorGradientProgram ); + glDeleteProgram( mnRadialTwoColorGradientProgram ); + glDeleteProgram( mnRadialMultiColorGradientProgram ); + glDeleteProgram( mnLinearTwoColorGradientProgram ); + glDeleteProgram( mnLinearMultiColorGradientProgram ); + glDeleteShader( mnRectangularTwoColorGradientFragmentProgram ); + glDeleteShader( mnRectangularMultiColorGradientFragmentProgram ); + glDeleteShader( mnRadialTwoColorGradientFragmentProgram ); + glDeleteShader( mnRadialMultiColorGradientFragmentProgram ); + glDeleteShader( mnLinearTwoColorGradientFragmentProgram ); + glDeleteShader( mnLinearMultiColorGradientFragmentProgram ); + glDeleteShader( mnDummyVertexProgram ); + + glXDestroyContext(reinterpret_cast<unx::Display*>(mpDisplay), + reinterpret_cast<unx::GLXContext>(mpGLContext)); + } + + mpDisplay = NULL; + mpGLContext = NULL; + mpChildWindow.reset(); + } + + geometry::RealSize2D SpriteDeviceHelper::getPhysicalResolution() + { + if( !mpChildWindow ) + return ::canvas::tools::createInfiniteSize2D(); // we're disposed + + // Map a one-by-one millimeter box to pixel + const MapMode aOldMapMode( mpChildWindow->GetMapMode() ); + mpChildWindow->SetMapMode( MapMode(MAP_MM) ); + const Size aPixelSize( mpChildWindow->LogicToPixel(Size(1,1)) ); + mpChildWindow->SetMapMode( aOldMapMode ); + + return ::vcl::unotools::size2DFromSize( aPixelSize ); + } + + geometry::RealSize2D SpriteDeviceHelper::getPhysicalSize() + { + if( !mpChildWindow ) + return ::canvas::tools::createInfiniteSize2D(); // we're disposed + + // Map the pixel dimensions of the output window to millimeter + const MapMode aOldMapMode( mpChildWindow->GetMapMode() ); + mpChildWindow->SetMapMode( MapMode(MAP_MM) ); + const Size aLogSize( mpChildWindow->PixelToLogic(mpChildWindow->GetOutputSizePixel()) ); + mpChildWindow->SetMapMode( aOldMapMode ); + + return ::vcl::unotools::size2DFromSize( aLogSize ); + } + + uno::Reference< rendering::XLinePolyPolygon2D > SpriteDeviceHelper::createCompatibleLinePolyPolygon( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XLinePolyPolygon2D >(); // we're disposed + + return uno::Reference< rendering::XLinePolyPolygon2D >( + new ::basegfx::unotools::UnoPolyPolygon( + ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points ))); + } + + uno::Reference< rendering::XBezierPolyPolygon2D > SpriteDeviceHelper::createCompatibleBezierPolyPolygon( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XBezierPolyPolygon2D >(); // we're disposed + + return uno::Reference< rendering::XBezierPolyPolygon2D >( + new ::basegfx::unotools::UnoPolyPolygon( + ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points ) ) ); + } + + uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& size ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XBitmap >(); // we're disposed + + return uno::Reference< rendering::XBitmap >( + new CanvasBitmap( size, + mpSpriteCanvas, + *this, + false ) ); + } + + uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& /*size*/ ) + { + return uno::Reference< rendering::XVolatileBitmap >(); + } + + uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleAlphaBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& size ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XBitmap >(); // we're disposed + + return uno::Reference< rendering::XBitmap >( + new CanvasBitmap( size, + mpSpriteCanvas, + *this, + true ) ); + } + + uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileAlphaBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& /*size*/ ) + { + return uno::Reference< rendering::XVolatileBitmap >(); + } + + sal_Bool SpriteDeviceHelper::hasFullScreenMode() + { + // TODO(F3): offer fullscreen mode the XCanvas way + return false; + } + + sal_Bool SpriteDeviceHelper::enterFullScreenMode( sal_Bool /*bEnter*/ ) + { + // TODO(F3): offer fullscreen mode the XCanvas way + return false; + } + + ::sal_Int32 SpriteDeviceHelper::createBuffers( ::sal_Int32 /*nBuffers*/ ) + { + // TODO(F3): implement XBufferStrategy interface. For now, we + // _always_ will have exactly one backbuffer + return 1; + } + + void SpriteDeviceHelper::destroyBuffers() + { + // TODO(F3): implement XBufferStrategy interface. For now, we + // _always_ will have exactly one backbuffer + } + + namespace + { + /** Functor providing a StrictWeakOrdering for XSprites (over + priority) + */ + struct SpriteComparator + { + bool operator()( const ::rtl::Reference<CanvasCustomSprite>& rLHS, + const ::rtl::Reference<CanvasCustomSprite>& rRHS ) const + { + const double nPrioL( rLHS->getPriority() ); + const double nPrioR( rRHS->getPriority() ); + + // if prios are equal, tie-break on ptr value + return nPrioL == nPrioR ? rLHS.get() < rRHS.get() : nPrioL < nPrioR; + } + }; + } + + ::sal_Bool SpriteDeviceHelper::showBuffer( bool bIsVisible, ::sal_Bool /*bUpdateAll*/ ) + { + // hidden or disposed? + if( !bIsVisible || !mpChildWindow || !mpSpriteCanvas ) + return false; + + if( !activateWindowContext() ) + return false; + + const ::Size& rOutputSize=mpChildWindow->GetSizePixel(); + initTransformation(rOutputSize); + + // render the actual spritecanvas content + mpSpriteCanvas->renderRecordedActions(); + + // render all sprites (in order of priority) on top of that + std::vector< ::rtl::Reference<CanvasCustomSprite> > aSprites; + std::copy(maActiveSprites.begin(), + maActiveSprites.end(), + std::back_insert_iterator< + std::vector< ::rtl::Reference< CanvasCustomSprite > > >(aSprites)); + std::sort(aSprites.begin(), + aSprites.end(), + SpriteComparator()); + std::for_each(aSprites.begin(), + aSprites.end(), + boost::mem_fn(&CanvasCustomSprite::renderSprite)); + + + // frame counter, other info + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslated(-1.0, 1.0, 0.0); + glScaled( 2.0 / rOutputSize.Width(), + -2.0 / rOutputSize.Height(), + 1.0 ); + + const double denominator( maLastUpdate.getElapsedTime() ); + maLastUpdate.reset(); + + const double fps(denominator == 0.0 ? 100.0 : 1.0/denominator); + std::vector<double> aVec; aVec.push_back(fps); + aVec.push_back(maActiveSprites.size()); + aVec.push_back(mpTextureCache->getCacheSize()); + aVec.push_back(mpTextureCache->getCacheMissCount()); + aVec.push_back(mpTextureCache->getCacheHitCount()); + renderOSD( aVec, 20 ); + + // switch buffer, sync etc. + const unx::Window aXWindow=mpChildWindow->GetSystemData()->aWindow; + unx::glXSwapBuffers(reinterpret_cast<unx::Display*>(mpDisplay), + aXWindow); + mpChildWindow->Show(); + unx::glXWaitGL(); + XSync( reinterpret_cast<unx::Display*>(mpDisplay), false ); + + // flush texture cache, such that it does not build up + // indefinitely. + // TODO: have max cache size/LRU time in config, prune only on + // demand + mpTextureCache->prune(); + + return true; + } + + ::sal_Bool SpriteDeviceHelper::switchBuffer( bool bIsVisible, ::sal_Bool bUpdateAll ) + { + // no difference for VCL canvas + return showBuffer( bIsVisible, bUpdateAll ); + } + + uno::Any SpriteDeviceHelper::isAccelerated() const + { + return ::com::sun::star::uno::makeAny(false); + } + + uno::Any SpriteDeviceHelper::getDeviceHandle() const + { + return uno::Any(); + } + + uno::Any SpriteDeviceHelper::getSurfaceHandle() const + { + return uno::Any(); + } + + uno::Reference<rendering::XColorSpace> SpriteDeviceHelper::getColorSpace() const + { + // always the same + return uno::Reference<rendering::XColorSpace>( + ::canvas::tools::getStdColorSpace(), + uno::UNO_QUERY); + } + + void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds ) + { + if( mpChildWindow ) + mpChildWindow->setPosSizePixel( + 0,0,rBounds.Width,rBounds.Height); + } + + void SpriteDeviceHelper::dumpScreenContent() const + { + SAL_INFO("canvas.ogl", BOOST_CURRENT_FUNCTION ); + } + + void SpriteDeviceHelper::show( const ::rtl::Reference< CanvasCustomSprite >& xSprite ) + { + maActiveSprites.insert(xSprite); + } + + void SpriteDeviceHelper::hide( const ::rtl::Reference< CanvasCustomSprite >& xSprite ) + { + maActiveSprites.erase(xSprite); + } + + static void setupUniforms( unsigned int nProgramId, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + const GLint nTransformLocation = glGetUniformLocation(nProgramId, + "m_transform" ); + // OGL is column-major + float aTexTransform[] = + { + float(rTexTransform.get(0,0)), float(rTexTransform.get(1,0)), + float(rTexTransform.get(0,1)), float(rTexTransform.get(1,1)), + float(rTexTransform.get(0,2)), float(rTexTransform.get(1,2)) + }; + glUniformMatrix3x2fv(nTransformLocation,1,false,aTexTransform); + } + + static void setupUniforms( unsigned int nProgramId, + const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + glUseProgram(nProgramId); + + GLuint nColorsTexture; + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &nColorsTexture); + glBindTexture(GL_TEXTURE_1D, nColorsTexture); + + const sal_Int32 nColors=rStops.getLength(); + glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, nColors, 0, GL_RGBA, GL_DOUBLE, pColors ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + GLuint nStopsTexture; + glActiveTexture(GL_TEXTURE1); + glGenTextures(1, &nStopsTexture); + glBindTexture(GL_TEXTURE_1D, nStopsTexture); + + glTexImage1D( GL_TEXTURE_1D, 0, GL_ALPHA, nColors, 0, GL_ALPHA, GL_DOUBLE, rStops.getConstArray() ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + const GLint nColorArrayLocation = glGetUniformLocation(nProgramId, + "t_colorArray4d" ); + glUniform1i( nColorArrayLocation, 0 ); // unit 0 + + const GLint nStopArrayLocation = glGetUniformLocation(nProgramId, + "t_stopArray1d" ); + glUniform1i( nStopArrayLocation, 1 ); // unit 1 + + const GLint nNumColorLocation = glGetUniformLocation(nProgramId, + "i_nColors" ); + glUniform1i( nNumColorLocation, nColors-1 ); + + setupUniforms(nProgramId,rTexTransform); + } + + static void setupUniforms( unsigned int nProgramId, + const rendering::ARGBColor& rStartColor, + const rendering::ARGBColor& rEndColor, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + glUseProgram(nProgramId); + + const GLint nStartColorLocation = glGetUniformLocation(nProgramId, + "v_startColor4d" ); + glUniform4f(nStartColorLocation, + rStartColor.Red, + rStartColor.Green, + rStartColor.Blue, + rStartColor.Alpha); + + const GLint nEndColorLocation = glGetUniformLocation(nProgramId, + "v_endColor4d" ); + glUniform4f(nEndColorLocation, + rEndColor.Red, + rEndColor.Green, + rEndColor.Blue, + rEndColor.Alpha); + + setupUniforms(nProgramId,rTexTransform); + } + + void SpriteDeviceHelper::useLinearGradientShader( const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + if( rStops.getLength() > 2 ) + setupUniforms(mnLinearMultiColorGradientProgram, pColors, rStops, rTexTransform); + else + setupUniforms(mnLinearTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform); + } + + void SpriteDeviceHelper::useRadialGradientShader( const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + if( rStops.getLength() > 2 ) + setupUniforms(mnRadialMultiColorGradientProgram, pColors, rStops, rTexTransform); + else + setupUniforms(mnRadialTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform); + } + + void SpriteDeviceHelper::useRectangularGradientShader( const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + if( rStops.getLength() > 2 ) + setupUniforms(mnRectangularMultiColorGradientProgram, pColors, rStops, rTexTransform); + else + setupUniforms(mnRectangularTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform); + } + + bool SpriteDeviceHelper::activatePBufferContext(const ::basegfx::B2IVector& rSize, + unsigned int PBuffer) const + { + if( !glXMakeCurrent( reinterpret_cast<unx::Display*>(mpDisplay), + PBuffer, + reinterpret_cast<unx::GLXContext>(mpGLPBufContext)) ) + { + SAL_INFO("canvas.ogl", "SpriteDeviceHelper::activatePBufferContext(): cannot activate GL context"); + return false; + } + + initContext(); + initTransformation( + ::Size( + rSize.getX(), + rSize.getY()), + true); + + return true; + } + + bool SpriteDeviceHelper::activateWindowContext() const + { + const unx::Window aXWindow=mpChildWindow->GetSystemData()->aWindow; + if( !glXMakeCurrent( reinterpret_cast<unx::Display*>(mpDisplay), + aXWindow, + reinterpret_cast<unx::GLXContext>(mpGLContext)) ) + { + SAL_INFO("canvas.ogl", "SpriteDeviceHelper::activateWindowContext(): cannot activate GL context"); + return false; + } + + return true; + } + + bool SpriteDeviceHelper::updatePBufferTexture( const ::basegfx::B2IVector& rSize, + unsigned int nTextId ) const + { + glBindTexture( GL_TEXTURE_2D, nTextId ); + glEnable(GL_TEXTURE_2D); + glCopyTexSubImage2D( GL_TEXTURE_2D, + 0, 0, 0, 0, 0, + rSize.getX(), + rSize.getY() ); + glBindTexture(GL_TEXTURE_2D, 0); + + return true; + } + + namespace + { + class BufferContextImpl : public IBufferContext + { + ::basegfx::B2IVector maSize; + const SpriteDeviceHelper& mrDeviceHelper; + unx::GLXPbuffer mpPBuffer; + unx::Display* mpDisplay; + unsigned int mnTexture; + + virtual bool startBufferRendering() + { + return mrDeviceHelper.activatePBufferContext(maSize,mpPBuffer); + } + + virtual bool endBufferRendering() + { + mrDeviceHelper.updatePBufferTexture(maSize,mnTexture); + if( !mrDeviceHelper.activateWindowContext() ) + return false; + + glBindTexture( GL_TEXTURE_2D, mnTexture ); + + return true; + } + + public: + BufferContextImpl(const SpriteDeviceHelper& rDeviceHelper, + unx::GLXPbuffer pBuffer, + unx::Display* pDisplay, + const ::basegfx::B2IVector& rSize) : + maSize(rSize), + mrDeviceHelper(rDeviceHelper), + mpPBuffer(pBuffer), + mpDisplay(pDisplay), + mnTexture(0) + { + glGenTextures( 1, &mnTexture ); +#if 1 + glBindTexture( GL_TEXTURE_2D, mnTexture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, + maSize.getX(), maSize.getY(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, new int[maSize.getX()*maSize.getY()] ); +#endif + } + + ~BufferContextImpl() + { +#if 0 + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures( 1, &mnTexture ); + glXDestroyPbuffer( mpDisplay, mpPBuffer ); +#endif + } + }; + } + + IBufferContextSharedPtr SpriteDeviceHelper::createBufferContext(const ::basegfx::B2IVector& rSize) const + { + int pBufAttribs[] = + { + GLX_PBUFFER_WIDTH, rSize.getX(), + GLX_PBUFFER_HEIGHT, rSize.getY(), + GLX_LARGEST_PBUFFER, False, + None + }; + + unx::GLXPbuffer pBuffer; + pBuffer = unx::glXCreatePbuffer( reinterpret_cast<unx::Display*>(mpDisplay), + *reinterpret_cast<unx::GLXFBConfig*>(mpFBConfig), + pBufAttribs ); + + IBufferContextSharedPtr pRet; + if( pBuffer ) + pRet.reset(new BufferContextImpl( + *this, + pBuffer, + reinterpret_cast<unx::Display*>(mpDisplay), + rSize)); + + return pRet; + } + + TextureCache& SpriteDeviceHelper::getTextureCache() const + { + return *mpTextureCache; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |