265 lines
5.8 KiB
C++
265 lines
5.8 KiB
C++
#include "lice_gl_ctx.h"
|
|
|
|
#define MAX_CACHED_GLYPHS 4096
|
|
|
|
// create one hidden window per process to hold the openGL state,
|
|
// its GL render context stays current for the life of the process,
|
|
// we serve all framebuffers from the same render context
|
|
|
|
class LICE_GL_ctx
|
|
{
|
|
public:
|
|
|
|
LICE_GL_ctx();
|
|
~LICE_GL_ctx();
|
|
|
|
bool IsValid();
|
|
HWND GetWindow() { return m_hwnd; }
|
|
void Close();
|
|
|
|
GLUnurbsObj* GetNurbsObj(int linetol=8); // linetol = maximum number of straight-line pixels
|
|
|
|
int GetTexFromGlyph(const unsigned char* glyph, int glyph_w, int glyph_h);
|
|
void ClearTex();
|
|
|
|
struct GlyphCache
|
|
{
|
|
unsigned int tex;
|
|
unsigned char* glyph; // lives on the heap
|
|
int glyph_w, glyph_h;
|
|
};
|
|
|
|
private:
|
|
|
|
bool Init();
|
|
|
|
bool m_init_tried;
|
|
HINSTANCE m_gldll;
|
|
HWND m_hwnd;
|
|
HGLRC m_glrc;
|
|
|
|
GLUnurbsObj* m_nurbs; // keep this here for easy reuse
|
|
|
|
GlyphCache m_glyphCache[MAX_CACHED_GLYPHS];
|
|
int m_nCachedGlyphs;
|
|
};
|
|
|
|
LICE_GL_ctx::LICE_GL_ctx()
|
|
{
|
|
m_init_tried = false;
|
|
m_gldll = 0;
|
|
m_hwnd = 0;
|
|
m_glrc = 0;
|
|
m_nurbs = 0;
|
|
memset(m_glyphCache, 0, MAX_CACHED_GLYPHS*sizeof(GlyphCache));
|
|
m_nCachedGlyphs = 0;
|
|
}
|
|
|
|
LICE_GL_ctx::~LICE_GL_ctx()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
bool LICE_GL_ctx::Init()
|
|
{
|
|
m_init_tried = true;
|
|
|
|
m_gldll = LoadLibrary("opengl32.dll");
|
|
if (!m_gldll)
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
// create a minimal GL render context to serve FBOs out of
|
|
WNDCLASS wc;
|
|
memset(&wc, 0, sizeof(WNDCLASS));
|
|
wc.hInstance = GetModuleHandle(0);
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
wc.lpszClassName = "LICE_GL_ctx";
|
|
RegisterClass(&wc);
|
|
m_hwnd = CreateWindow("LICE_GL_ctx", "LICE_GL_ctx", 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
|
|
HDC dc = GetDC(m_hwnd);
|
|
if (!dc)
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
|
|
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = 24;
|
|
pfd.cAlphaBits = 8;
|
|
int pxfmt = ChoosePixelFormat(dc, &pfd);
|
|
if (!SetPixelFormat(dc, pxfmt, &pfd))
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
m_glrc = wglCreateContext(dc);
|
|
if (!wglMakeCurrent(dc, m_glrc)) // render context should stay current throughout
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
char *rendstr = (char*) glGetString(GL_RENDERER);
|
|
if (!rendstr || strstr(rendstr, "GDI"))
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
// check now for all the extension functions we will ever need
|
|
if (glewInit() != GLEW_OK ||
|
|
!glewIsSupported("GL_EXT_framebuffer_object") ||
|
|
!glewIsSupported("GL_ARB_texture_rectangle"))
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
// any one-time initialization goes here
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
ReleaseDC(m_hwnd, dc);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LICE_GL_ctx::IsValid()
|
|
{
|
|
if (m_gldll && m_glrc) return true;
|
|
if (!m_init_tried) return Init();
|
|
return false;
|
|
}
|
|
|
|
void LICE_GL_ctx::Close()
|
|
{
|
|
ClearTex();
|
|
if (m_nurbs)
|
|
{
|
|
gluDeleteNurbsRenderer(m_nurbs);
|
|
m_nurbs = 0;
|
|
}
|
|
if (m_glrc)
|
|
{
|
|
wglMakeCurrent(0, 0);
|
|
wglDeleteContext(m_glrc);
|
|
m_glrc = 0;
|
|
}
|
|
if (m_hwnd)
|
|
{
|
|
DestroyWindow(m_hwnd);
|
|
m_hwnd = 0;
|
|
}
|
|
if (m_gldll)
|
|
{
|
|
FreeLibrary(m_gldll);
|
|
m_gldll = 0;
|
|
}
|
|
}
|
|
|
|
GLUnurbsObj* LICE_GL_ctx::GetNurbsObj(int linetol)
|
|
{
|
|
if (!IsValid()) return 0;
|
|
if (!m_nurbs) m_nurbs = gluNewNurbsRenderer();
|
|
if (m_nurbs) gluNurbsProperty(m_nurbs, GLU_SAMPLING_TOLERANCE, (float)linetol);
|
|
return m_nurbs;
|
|
}
|
|
|
|
void LICE_GL_ctx::ClearTex()
|
|
{
|
|
int i;
|
|
for (i = 0; i < m_nCachedGlyphs; ++i)
|
|
{
|
|
glDeleteTextures(1, &m_glyphCache[i].tex);
|
|
free(m_glyphCache[i].glyph);
|
|
memset(&m_glyphCache[i], 0, sizeof(GlyphCache));
|
|
}
|
|
m_nCachedGlyphs = 0;
|
|
}
|
|
|
|
static int _glyphcmp(const void* p1, const void* p2)
|
|
{
|
|
LICE_GL_ctx::GlyphCache* gc1 = (LICE_GL_ctx::GlyphCache*) p1;
|
|
LICE_GL_ctx::GlyphCache* gc2 = (LICE_GL_ctx::GlyphCache*) p2;
|
|
|
|
if (gc1->glyph_w < gc2->glyph_w) return -1;
|
|
if (gc1->glyph_w > gc2->glyph_w) return 1;
|
|
if (gc1->glyph_h < gc2->glyph_h) return -1;
|
|
if (gc1->glyph_h > gc2->glyph_h) return 1;
|
|
return memcmp(gc1->glyph, gc2->glyph, gc1->glyph_w*gc1->glyph_h);
|
|
}
|
|
|
|
int LICE_GL_ctx::GetTexFromGlyph(const unsigned char* glyph, int glyph_w, int glyph_h)
|
|
{
|
|
if (!IsValid()) return false;
|
|
|
|
GlyphCache gc;
|
|
gc.tex = 0;
|
|
gc.glyph = (unsigned char *)glyph;
|
|
gc.glyph_w = glyph_w;
|
|
gc.glyph_h = glyph_h;
|
|
|
|
GlyphCache* p = (GlyphCache*) bsearch(&gc, m_glyphCache, m_nCachedGlyphs, sizeof(GlyphCache), _glyphcmp);
|
|
if (p) return p->tex;
|
|
|
|
glGenTextures(1, &gc.tex);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, gc.tex);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_ALPHA8, glyph_w, glyph_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, glyph);
|
|
|
|
if (m_nCachedGlyphs >= MAX_CACHED_GLYPHS) ClearTex(); // quick & dirty
|
|
|
|
gc.glyph = (unsigned char*) malloc(glyph_w*glyph_h);
|
|
memcpy(gc.glyph, glyph, glyph_w*glyph_h);
|
|
m_glyphCache[m_nCachedGlyphs++] = gc; // copy
|
|
qsort(m_glyphCache, m_nCachedGlyphs, sizeof(GlyphCache), _glyphcmp);
|
|
|
|
return gc.tex;
|
|
}
|
|
|
|
////////
|
|
|
|
static LICE_GL_ctx s_glctx; // one static opengl context object per process
|
|
|
|
|
|
bool LICE_GL_IsValid()
|
|
{
|
|
return s_glctx.IsValid();
|
|
}
|
|
|
|
HWND LICE_GL_GetWindow()
|
|
{
|
|
if (s_glctx.IsValid()) return s_glctx.GetWindow();
|
|
return 0;
|
|
}
|
|
|
|
void LICE_GL_CloseCtx()
|
|
{
|
|
s_glctx.Close();
|
|
}
|
|
|
|
GLUnurbsObj* LICE_GL_GetNurbsObj(int linetol) // linetol = maximum number of straight-line pixels
|
|
{
|
|
return s_glctx.GetNurbsObj(linetol);
|
|
}
|
|
|
|
GLuint LICE_GL_GetTexFromGlyph(const unsigned char* glyph, int glyph_w, int glyph_h)
|
|
{
|
|
return s_glctx.GetTexFromGlyph(glyph, glyph_w, glyph_h);
|
|
}
|
|
|
|
void LICE_GL_ClearTex()
|
|
{
|
|
s_glctx.ClearTex();
|
|
}
|
|
|