1027 lines
38 KiB
C++
1027 lines
38 KiB
C++
/* -*- 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;
|
|
}
|
|
|
|
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()),
|
|
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("Could not select OpenGL context!");
|
|
}
|
|
|
|
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();
|
|
|
|
mnLinearMultiColorGradientProgram =
|
|
OpenGLHelper::LoadShaders("dummyVertexShader.glsl", "linearMultiColorGradientFragmentShader.glsl");
|
|
|
|
mnLinearTwoColorGradientProgram =
|
|
OpenGLHelper::LoadShaders("dummyVertexShader.glsl", "linearTwoColorGradientFragmentShader.glsl");
|
|
|
|
mnRadialMultiColorGradientProgram =
|
|
OpenGLHelper::LoadShaders("dummyVertexShader.glsl", "radialMultiColorGradientFragmentShader.glsl");
|
|
|
|
mnRadialTwoColorGradientProgram =
|
|
OpenGLHelper::LoadShaders("dummyVertexShader.glsl", "radialTwoColorGradientFragmentShader.glsl");
|
|
|
|
mnRectangularMultiColorGradientProgram =
|
|
OpenGLHelper::LoadShaders("dummyVertexShader.glsl", "rectangularMultiColorGradientFragmentShader.glsl");
|
|
|
|
mnRectangularTwoColorGradientProgram =
|
|
OpenGLHelper::LoadShaders("dummyVertexShader.glsl", "rectangularTwoColorGradientFragmentShader.glsl");
|
|
|
|
glXMakeCurrent(pDisplay, None, NULL);
|
|
}
|
|
|
|
if( !mpGLContext || glGetError() != GL_NO_ERROR )
|
|
throw lang::NoSupportException(
|
|
"Could not create OpenGL context, or an error occurred doing so!");
|
|
|
|
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 );
|
|
|
|
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 >();
|
|
}
|
|
|
|
bool SpriteDeviceHelper::hasFullScreenMode()
|
|
{
|
|
// TODO(F3): offer fullscreen mode the XCanvas way
|
|
return false;
|
|
}
|
|
|
|
bool SpriteDeviceHelper::enterFullScreenMode( 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;
|
|
}
|
|
};
|
|
}
|
|
|
|
bool SpriteDeviceHelper::showBuffer( bool bIsVisible, 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;
|
|
}
|
|
|
|
bool SpriteDeviceHelper::switchBuffer( bool bIsVisible, 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::makeAny( reinterpret_cast< sal_Int64 >(mpChildWindow.get()) );
|
|
}
|
|
|
|
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;
|
|
#if 0
|
|
unx::Display* mpDisplay;
|
|
#endif
|
|
unsigned int mnTexture;
|
|
|
|
virtual bool startBufferRendering() SAL_OVERRIDE
|
|
{
|
|
return mrDeviceHelper.activatePBufferContext(maSize,mpPBuffer);
|
|
}
|
|
|
|
virtual bool endBufferRendering() SAL_OVERRIDE
|
|
{
|
|
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*
|
|
#if 0
|
|
pDisplay
|
|
#endif
|
|
,
|
|
const ::basegfx::B2IVector& rSize) :
|
|
maSize(rSize),
|
|
mrDeviceHelper(rDeviceHelper),
|
|
mpPBuffer(pBuffer),
|
|
#if 0
|
|
mpDisplay(pDisplay),
|
|
#endif
|
|
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
|
|
}
|
|
|
|
virtual ~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: */
|