#include "lice_glbitmap.h" #include "lice_gl_ctx.h" #include "../plush2/plush.h" LICE_GLBitmap::LICE_GLBitmap() { m_bmp = 0; m_fbo = 0; m_tex = 0; m_bufloc = EMPTY; } // This is separate from the constructor for initialization order reasons void LICE_GLBitmap::Init(LICE_IBitmap* bmp, int w, int h) { m_bmp = bmp; if (w > 0 && h > 0) CreateFBO(w, h); } bool LICE_GLBitmap::CreateFBO(int w, int h) { if (LICE_GL_IsValid()) { if (m_fbo) ReleaseFBO(); glGenFramebuffersEXT(1, &m_fbo); // create a new empty FBO if (m_fbo) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); // bind this FBO so it is the rendering target glEnable(GL_TEXTURE_RECTANGLE_ARB); // enable texturing glGenTextures(1, &m_tex); // create a new texture to be the backing store if (m_tex) { glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_tex); // bind this texture so it is the texturing target glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // we won't be scaling it for this purpose glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // size the texture glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_tex, 0); // attach the texture as the backing store for the FBO } glDisable(GL_TEXTURE_RECTANGLE_ARB); // done texturing } return BindFBO(); // this tests the FBO for validity } return false; } LICE_GL_SysBitmap::LICE_GL_SysBitmap(int w, int h) : m_sysbmp(w, h) { Init(&m_sysbmp, w, h); } LICE_GL_MemBitmap::LICE_GL_MemBitmap(int w, int h) : m_membmp(w, h) { Init(&m_membmp, w, h); } HDC LICE_GL_SysBitmap::getDC() { // no known way to get a DC directly from an offscreen openGL render context, sadly FramebufferFromGPU(); OutputDebugString("GL to screen"); return m_sysbmp.getDC(); } LICE_GL_SubBitmap::LICE_GL_SubBitmap(LICE_IBitmap *parent, int x, int y, int w, int h) { m_parent = parent; m_x = x; m_y = y; m_w = w; m_h = h; } LICE_pixel* LICE_GL_SubBitmap::getBits() { if (!m_parent) return 0; return m_parent->getBits()+m_y*m_parent->getRowSpan()+m_x; } bool LICE_GL_SubBitmap::resize(int w, int h) { if (w == m_w && h == m_h) return false; m_w = w; m_h = h; return true; } INT_PTR LICE_GL_SubBitmap::Extended(int id, void* data) { if (!m_parent) return 0; if (id == LICE_EXT_SUPPORTS_ID) return m_parent->Extended(id, data); LICE_Ext_SetClip_data clipdata(m_x, m_y, m_w, m_h); if (!m_parent->Extended(LICE_EXT_SETCLIP, &clipdata)) return 0; INT_PTR ret = m_parent->Extended(id, data); m_parent->Extended(LICE_EXT_SETCLIP, 0); return ret; } bool LICE_GLBitmap::SetClip_ext(LICE_Ext_SetClip_data* p) { if (!FramebufferToGPU()) return false; if (p) { GLdouble c0[4] = { 1.0, 0.0, 0.0, -p->x }; GLdouble c1[4] = { -1.0, 0.0, 0.0, p->x+p->w }; GLdouble c2[4] = { 0.0, 1.0, 0.0, -p->y}; GLdouble c3[4] = { 0.0, -1.0, 0.0, p->y+p->h }; glClipPlane(GL_CLIP_PLANE0, c0); glClipPlane(GL_CLIP_PLANE1, c1); glClipPlane(GL_CLIP_PLANE2, c2); glClipPlane(GL_CLIP_PLANE3, c3); glEnable(GL_CLIP_PLANE0); glEnable(GL_CLIP_PLANE1); glEnable(GL_CLIP_PLANE2); glEnable(GL_CLIP_PLANE3); } else { glDisable(GL_CLIP_PLANE0); glDisable(GL_CLIP_PLANE1); glDisable(GL_CLIP_PLANE2); glDisable(GL_CLIP_PLANE3); } return true; } LICE_GLBitmap::~LICE_GLBitmap() { ReleaseFBO(); } int LICE_GLBitmap::getWidth() { return m_bmp->getWidth(); } int LICE_GLBitmap::getHeight() { return m_bmp->getHeight(); } int LICE_GLBitmap::getRowSpan() { return m_bmp->getRowSpan(); } LICE_pixel* LICE_GLBitmap::getBits() { FramebufferFromGPU(); return m_bmp->getBits(); } bool LICE_GLBitmap::resize(int w, int h) { int oldw = getWidth(); int oldh = getHeight(); if (w == oldw && h == oldh) return false; if (oldw == 0 && oldh == 0 && w > 0 && h > 0) { m_bmp->resize(w, h); CreateFBO(w, h); return true; } if (!m_fbo || !m_tex) return m_bmp->resize(w, h); OutputDebugString("GL resizing"); // the framebuffer/GPU transfer overhead is per-call, so it's important to be able // to move the entire bitmap back and forth in one call, which means keeping m_sysbmp width == rowspan int oldloc = m_bufloc; FramebufferFromGPU(); // copy out the GPU data (this binds the FBO so it is the rendering target) glEnable(GL_TEXTURE_RECTANGLE_ARB); // enable texturing glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_tex); // bind this texture so it is the texturing target glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // we won't be scaling it for this purpose glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // size the texture glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_tex, 0); // attach the texture as the backing store for the FBO glDisable(GL_TEXTURE_RECTANGLE_ARB); // done texturing if (!BindFBO()) return m_bmp->resize(w, h); // this tests the FBO for validity static LICE_MemBitmap tmpbmp; tmpbmp.resize(w, h); LICE_Blit(&tmpbmp, m_bmp, 0, 0, 0, 0, oldw, oldh, 1.0f, LICE_BLIT_MODE_COPY); m_bmp->resize(0, 0); // force it to resize down LICE_Copy(m_bmp, &tmpbmp); if (oldloc == EMPTY) m_bufloc = EMPTY; // else bufloc remains INMEM return true; } bool LICE_GLBitmap::BindFBO() { bool valid = false; if (m_fbo) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); // bind this FBO so it is the rendering target valid = (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT); if (valid) { int w = getWidth(); int h = getHeight(); glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, w, 0.0, h); } else { ReleaseFBO(); LICE_GL_CloseCtx(); // if we fail once we're done with GL } } return valid; } void LICE_GLBitmap::ReleaseFBO() { if (m_fbo) { glDeleteFramebuffersEXT(1, &m_fbo); m_fbo = 0; } if (m_tex) { glDeleteTextures(1, &m_tex); m_tex = 0; } } bool LICE_GLBitmap::FramebufferToGPU() { if (BindFBO()) { if (m_bufloc == INMEM) { OutputDebugString("GL to GPU"); glDisable(GL_BLEND); glRasterPos2i(0, 0); glDrawPixels(getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_bmp->getBits()); } m_bufloc = INGPU; } return (m_bufloc == INGPU); } void LICE_GLBitmap::FramebufferFromGPU() { if (m_bufloc == INGPU && BindFBO()) { OutputDebugString("GL to mem"); glFinish(); glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_bmp->getBits()); } m_bufloc = INMEM; } INT_PTR LICE_GLBitmap::Extended(int id, void* data) { if (id == LICE_EXT_SUPPORTS_ID) { int extid = (int) data; if (extid == LICE_EXT_LINE_ACCEL) return 1; if (extid == LICE_EXT_FILLRECT_ACCEL) return 1; if (extid == LICE_EXT_DRAWCBEZIER_ACCEL) return 1; if (extid == LICE_EXT_DRAWGLYPH_ACCEL) return 1; if (extid == LICE_EXT_BLIT_ACCEL) return 1; if (extid == LICE_EXT_SCALEDBLIT_ACCEL) return 1; if (extid == LICE_EXT_GETFBOTEX_ACCEL) return 1; if (extid == LICE_EXT_CLEAR_ACCEL) return 1; if (extid == LICE_EXT_DASHEDLINE_ACCEL) return 1; if (extid == LICE_EXT_GETPIXEL_ACCEL) return 1; if (extid == LICE_EXT_PUTPIXEL_ACCEL) return 1; if (extid == LICE_EXT_SETCLIP) return 1; if (extid == LICE_EXT_WINDOW_BLIT) return 1; if (extid == LICE_EXT_FORGET) return 1; if (extid == LICE_EXT_DRAWTRIANGLE_ACCEL) return 1; return 0; } if (id == LICE_EXT_CLEAR_ACCEL) return Clear_accel((LICE_pixel*)data); if (id == LICE_EXT_LINE_ACCEL) return Line_accel((LICE_Ext_Line_acceldata*)data); if (id == LICE_EXT_FILLRECT_ACCEL) return FillRect_accel((LICE_Ext_FillRect_acceldata*) data); if (id == LICE_EXT_DRAWCBEZIER_ACCEL) return DrawCBezier_accel((LICE_Ext_DrawCBezier_acceldata*)data); if (id == LICE_EXT_DRAWGLYPH_ACCEL) return DrawGlyph_accel((LICE_Ext_DrawGlyph_acceldata*)data); if (id == LICE_EXT_BLIT_ACCEL) return Blit_accel((LICE_Ext_Blit_acceldata*)data); if (id == LICE_EXT_SCALEDBLIT_ACCEL) return ScaledBlit_accel((LICE_Ext_ScaledBlit_acceldata*)data); if (id == LICE_EXT_DASHEDLINE_ACCEL) return DashedLine_accel((LICE_Ext_DashedLine_acceldata*)data); if (id == LICE_EXT_GETPIXEL_ACCEL) return GetPixel_accel((LICE_Ext_GetPixel_acceldata*)data); if (id == LICE_EXT_PUTPIXEL_ACCEL) return PutPixel_accel((LICE_Ext_PutPixel_acceldata*)data); if (id == LICE_EXT_SETCLIP) return SetClip_ext((LICE_Ext_SetClip_data*)data); if (id == LICE_EXT_DRAWTRIANGLE_ACCEL) return DrawTriangle_accel((LICE_Ext_DrawTriangle_acceldata*)data); if (id == LICE_EXT_WINDOW_BLIT) return WindowBlit((LICE_Ext_WindowBlit_data*)data); if (id == LICE_EXT_FORGET) { m_bufloc = EMPTY; return 1; } if (id == LICE_EXT_GETFBOTEX_ACCEL) { if (FramebufferToGPU()) return m_tex; return 0; } return 0; } static void SetGLAliasing(bool aa) { if (aa) { glEnable(GL_LINE_SMOOTH); } else { glDisable(GL_LINE_SMOOTH); } } static void SetGLColor(LICE_pixel color, float alpha, int licemode) { int a = 255; if (licemode&LICE_BLIT_USE_ALPHA) a = LICE_GETA(color); a = (float)a*alpha; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glColor4ub(LICE_GETB(color), LICE_GETG(color), LICE_GETR(color), a); //glColor4ub(LICE_GETR(color), LICE_GETG(color), LICE_GETB(color), a); } bool LICE_GLBitmap::Clear_accel(LICE_pixel* color) { if (!color) return false; LICE_pixel col = *color; if (!FramebufferToGPU()) return false; float r = (float)LICE_GETR(col)/255.0f; float g = (float)LICE_GETG(col)/255.0f; float b = (float)LICE_GETB(col)/255.0f; float a = (float)LICE_GETA(col)/255.0f; glClearColor(b, g, r, a); //glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT); return true; } bool LICE_GLBitmap::Line_accel(LICE_Ext_Line_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; SetGLColor(p->color, p->alpha, p->mode); SetGLAliasing(p->aa); glBegin(GL_LINES); glVertex2f(p->x1, p->y1); glVertex2f(p->x2, p->y2); glEnd(); return true; } bool LICE_GLBitmap::DashedLine_accel(LICE_Ext_DashedLine_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; SetGLColor(p->color, p->alpha, p->mode); SetGLAliasing(p->aa); glEnable(GL_LINE_STIPPLE); int n = (p->pxon+p->pxoff)/2; // todo properly when pxon != pxoff glLineStipple(n, 0xAAAA); glBegin(GL_LINES); glVertex2f(p->x1, p->y1); glVertex2f(p->x2, p->y2); glEnd(); glLineStipple(1, 65535); glDisable(GL_LINE_STIPPLE); return true; } bool LICE_GLBitmap::FillRect_accel(LICE_Ext_FillRect_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; SetGLColor(p->color, p->alpha, p->mode); glBegin(GL_POLYGON); glVertex2i(p->x, p->y); glVertex2i(p->x+p->w, p->y); glVertex2i(p->x+p->w, p->y+p->h); glVertex2i(p->x, p->y+p->h); glEnd(); return true; } bool LICE_GLBitmap::DrawCBezier_accel(LICE_Ext_DrawCBezier_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; GLUnurbsObj* nurbs = LICE_GL_GetNurbsObj(); if (!nurbs) return false; p->color = LICE_RGBA(255,255,255,255); // temp for easy ID of GL rendering SetGLColor(p->color, p->alpha, p->mode); SetGLAliasing(p->aa); float ctlpts[] = { p->xstart, p->ystart, 0.0f, p->xctl1, p->yctl1, 0.0f, p->xctl2, p->yctl2, 0.0f, p->xend, p->yend, 0.0f }; float knots[] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }; gluBeginCurve(nurbs); gluNurbsCurve(nurbs, 8, knots, 3, ctlpts, 4, GL_MAP1_VERTEX_3); gluEndCurve(nurbs); return true; } bool LICE_GLBitmap::DrawGlyph_accel(LICE_Ext_DrawGlyph_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; glEnable(GL_TEXTURE_RECTANGLE_ARB); int texID = LICE_GL_GetTexFromGlyph(p->alphas, p->glyph_w, p->glyph_h); if (!texID) return false; SetGLColor(p->color, p->alpha, p->mode); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texID); glBegin(GL_POLYGON); glTexCoord2i(0, 0); glVertex2i(p->x, p->y); glTexCoord2i(p->glyph_w, 0); glVertex2i(p->x+p->glyph_w, p->y); glTexCoord2i(p->glyph_w, p->glyph_h); glVertex2i(p->x+p->glyph_w, p->y+p->glyph_h); glTexCoord2i(0, p->glyph_h); glVertex2i(p->x, p->y+p->glyph_h); glEnd(); glDisable(GL_TEXTURE_RECTANGLE_ARB); return true; } bool LICE_GLBitmap::Blit_accel(LICE_Ext_Blit_acceldata* p) { if (!p || !p->src) return false; if (!FramebufferToGPU()) return false; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); GLuint src_tex = p->src->Extended(LICE_EXT_GETFBOTEX_ACCEL, 0); // this binds the src's FBO if (src_tex) { if (!BindFBO()) return false; // re-bind dest FBO glEnable(GL_TEXTURE_RECTANGLE_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, src_tex); glBegin(GL_POLYGON); glTexCoord2i(p->srcx, p->srcy); glVertex2i(p->dstx, p->dsty); glTexCoord2i(p->srcx+p->srcw, p->srcy); glVertex2i(p->dstx+p->srcw, p->dsty); glTexCoord2i(p->srcx+p->srcw, p->srcy+p->srch); glVertex2i(p->dstx+p->srcw, p->dsty+p->srch); glTexCoord2i(p->srcx, p->srcy+p->srch); glVertex2i(p->dstx, p->dsty+p->srch); glEnd(); glDisable(GL_TEXTURE_RECTANGLE_ARB); return true; } int srcspan = p->src->getRowSpan(); if (p->srcx == 0 && p->srcy == 0 && p->srcw == srcspan) { glRasterPos2i(p->dstx, p->dsty); glDrawPixels(p->srcw, p->srch, GL_RGBA, GL_UNSIGNED_BYTE, p->src->getBits()); return true; } LICE_pixel* srcrow = p->src->getBits()+p->srcy*srcspan+p->srcx; int y; for (y = 0; y < p->srch; ++y, srcrow += srcspan) { glRasterPos2i(p->dstx, p->dsty+y); glDrawPixels(p->srcw, 1, GL_RGBA, GL_UNSIGNED_BYTE, srcrow); } return true; } bool LICE_GLBitmap::ScaledBlit_accel(LICE_Ext_ScaledBlit_acceldata* p) { if (!p || !p->src) return false; if (!FramebufferToGPU()) return false; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_RECTANGLE_ARB); GLuint src_tex = p->src->Extended(LICE_EXT_GETFBOTEX_ACCEL, 0); // this binds the src's FBO bool src_has_tex = (src_tex > 0); if (src_has_tex) { if (!BindFBO()) return false; // re-bind dest FBO glBindTexture(GL_TEXTURE_RECTANGLE_ARB, src_tex); } else { // create texture from src bits glGenTextures(1, &src_tex); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, src_tex); int srcspan = p->src->getRowSpan(); if (p->srcx == 0 && p->srcy == 0 && srcspan == p->srcw) { glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, p->srcw, p->srch, 0, GL_RGBA, GL_UNSIGNED_BYTE, p->src->getBits()); } else { glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, p->srcw, p->srch, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // size the texture LICE_pixel* srcrow = p->src->getBits()+(int)p->srcy*srcspan+(int)p->srcx; int y; for (y = 0; y < p->srch; ++y, srcrow += srcspan) { glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, y, p->srcw, 1, GL_RGBA, GL_UNSIGNED_BYTE, srcrow); } } } if (!src_tex) return false; glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // filter glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBegin(GL_POLYGON); glTexCoord2i(p->srcx, p->srcy); glVertex2i(p->dstx, p->dsty); glTexCoord2i(p->srcx+p->srcw, p->srcy); glVertex2i(p->dstx+p->dstw, p->dsty); glTexCoord2i(p->srcx+p->srcw, p->srcy+p->srch); glVertex2i(p->dstx+p->dstw, p->dsty+p->dsth); glTexCoord2i(p->srcx, p->srcy+p->srch); glVertex2i(p->dstx, p->dsty+p->dsth); glEnd(); if (!src_has_tex) glDeleteTextures(1, &src_tex); glDisable(GL_TEXTURE_RECTANGLE_ARB); return true; } bool LICE_GLBitmap::GetPixel_accel(LICE_Ext_GetPixel_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; p->px = 0; glReadPixels(p->x, p->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &p->px); return true; } bool LICE_GLBitmap::PutPixel_accel(LICE_Ext_PutPixel_acceldata* p) { if (!p) return false; if (!FramebufferToGPU()) return false; SetGLColor(p->color, p->alpha, p->mode); glBegin(GL_POINTS); glVertex2i(p->x, p->y); glEnd(); return true; } bool LICE_GLBitmap::DrawTriangle_accel(LICE_Ext_DrawTriangle_acceldata *p) { if (!p) return false; if (!FramebufferToGPU()) return false; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); // todo: finish implementing zbuffering, (multi)texture mapping, alpha, etc glColor4f(p->VertexShades[0][0], p->VertexShades[0][1], p->VertexShades[0][2], p->mat->SolidOpacity); glBegin(GL_TRIANGLES); int x; for(x=0;x<3;x++) { glVertex2i(p->scrx[x], p->scry[x]); } glEnd(); return true; } bool LICE_GLBitmap::WindowBlit(LICE_Ext_WindowBlit_data* p) { if (!p) return false; if (!FramebufferToGPU()) return false; HWND hwnd = p->hwnd; if (hwnd != LICE_GL_GetWindow()) return 0; glFinish(); //HDC dc = GetDC(hwnd); //HGLRC rc = wglCreateContext(dc); //wglMakeCurrent(dc, rc); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); /* glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, w, 0.0, h); */ glEnable(GL_TEXTURE_RECTANGLE_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_tex); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // the src image is upside down // todo positioning glBegin(GL_POLYGON); glTexCoord2i(0, p->h); glVertex2i(0, 0); glTexCoord2i(p->w, p->h); glVertex2i(p->w, 0); glTexCoord2i(p->w, 0); glVertex2i(p->w-1, p->h); glTexCoord2i(0, 0); glVertex2i(0, p->h); glEnd(); glDisable(GL_TEXTURE_RECTANGLE_ARB); //wglMakeCurrent(0, 0); //wglDeleteContext(rc); // ReleaseDC(hwnd, dc); return true; }