From 342c19690e48f50ff5d4180a3f47d31452d4b78d Mon Sep 17 00:00:00 2001 From: Jonathan Lambrechts <jonathan.lambrechts@uclouvain.be> Date: Tue, 4 Jun 2013 10:19:34 +0000 Subject: [PATCH] cairo string now faster than native (at least on my laptop : 35x) --- Fltk/drawContextFltkCairo.cpp | 227 ++++++++++++++++++++-------------- Fltk/drawContextFltkCairo.h | 5 +- Fltk/openglWindow.cpp | 1 + Graphics/drawContext.h | 1 + 4 files changed, 140 insertions(+), 94 deletions(-) diff --git a/Fltk/drawContextFltkCairo.cpp b/Fltk/drawContextFltkCairo.cpp index e6dc4ba5e9..0cde498d75 100644 --- a/Fltk/drawContextFltkCairo.cpp +++ b/Fltk/drawContextFltkCairo.cpp @@ -10,61 +10,127 @@ #if defined(HAVE_CAIRO) #include <cairo/cairo.h> -//mostly borrowed from fltk function gl_texture_fifo::display_texture -static void _data2gl (int width, int height, unsigned char *data, - int Lx, int Ly, unsigned int _textureId) -{ - //setup matrices - GLint matrixMode; - glGetIntegerv (GL_MATRIX_MODE, &matrixMode); - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - glLoadIdentity (); - glMatrixMode (GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity (); - float winw = Fl_Window::current()->w(); - float winh = Fl_Window::current()->h(); - glScalef (2.0f / winw, 2.0f / winh, 1.0f); - glTranslatef (-winw / 2.0f, -winh / 2.0f, 0.0f); - //write the texture on screen - GLfloat pos[4]; - glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); +class drawContextFltkCairo::queueString { + public : + typedef struct { + std::string text; + GLfloat x, y, z; + GLfloat r, g, b, alpha; + int fontSize; + cairo_font_face_t *fontFace; + cairo_text_extents_t extent; + } element; + + private: + std::vector<element> _elements; + double _totalWidth, _maxHeight; + + public: + queueString() + { + _totalWidth = 0; + _maxHeight = 0; + } + + ~queueString() + { + for(std::vector<element>::iterator it = _elements.begin(); it != _elements.end(); ++it) { + cairo_font_face_destroy(it->fontFace); + } + } - glEnable (GL_TEXTURE_RECTANGLE_ARB); - glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); - glDisable(GL_LIGHTING); - glDisable (GL_DEPTH_TEST); - glEnable (GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glBindTexture (GL_TEXTURE_RECTANGLE_ARB, _textureId); - glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_ALPHA, width, height, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, data); - glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_SRC0_ALPHA); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glTranslatef(pos[0] /*+ rect->x_bearing*/, pos[1] /*+ rect->y_bearing*/, pos[2]); - - glBegin (GL_QUADS); - glTexCoord2f (0, 0); - glVertex2f (0.0f, Ly); - glTexCoord2f (Lx, 0); - glVertex2f (Lx, Ly); - glTexCoord2f (Lx, Ly); - glVertex2f (Lx, 0.0f); - glTexCoord2f (0, Ly); - glVertex2f (0.0f, 0.0f); - glEnd (); - - glPopAttrib(); - - // reset original matrices - glPopMatrix(); // GL_MODELVIEW - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - glMatrixMode (matrixMode); -} + void append(const element &elem) + { + if (_totalWidth + elem.extent.width > 1000) + flush(); + _elements.push_back(elem); + _totalWidth += elem.extent.width; + _maxHeight = std::max(_maxHeight, elem.extent.height); + } + + void flush() + { + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_A8, _totalWidth, _maxHeight); + cairo_t *cr = cairo_create(surface); + float pos = 0; + cairo_set_source_rgba (cr, 0., 0., 0., 0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba(cr, 1, 1, 1, 1); + for(std::vector<element>::iterator it = _elements.begin(); it != _elements.end(); ++it) { + cairo_move_to(cr, pos - it->extent.x_bearing, -it->extent.y_bearing); + cairo_set_font_size(cr, it->fontSize); + cairo_set_font_face(cr, it->fontFace); + cairo_show_text(cr, it->text.c_str()); + cairo_font_face_destroy(it->fontFace); + pos += it->extent.width; + } + cairo_destroy(cr); + //setup matrices + GLint matrixMode; + GLuint textureId; + glGetIntegerv (GL_MATRIX_MODE, &matrixMode); + glMatrixMode (GL_PROJECTION); + glPushMatrix(); + glLoadIdentity (); + glMatrixMode (GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity (); + float winw = Fl_Window::current()->w(); + float winh = Fl_Window::current()->h(); + glScalef (2.0f / winw, 2.0f / winh, 1.0f); + glTranslatef (-winw / 2.0f, -winh / 2.0f, 0.0f); + //write the texture on screen + glEnable (GL_TEXTURE_RECTANGLE_ARB); + glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); + glDisable(GL_LIGHTING); + glDisable (GL_DEPTH_TEST); + glEnable (GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glGenTextures (1, &textureId); + glBindTexture (GL_TEXTURE_RECTANGLE_ARB, textureId); + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_ALPHA, cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface), 0, + GL_ALPHA, GL_UNSIGNED_BYTE, cairo_image_surface_get_data(surface)); + //glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_SRC0_ALPHA); + //printf("error %i %s\n", __LINE__, gluErrorString(glGetError())); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + pos = 0; + for(std::vector<element>::iterator it = _elements.begin(); it != _elements.end(); ++it) { + glTranslatef(it->x, it->y, it->z); + glColor4f(it->r, it->g, it->b, it->alpha); + float Lx = it->extent.width; + float Ly = it->extent.height; + + glBegin (GL_QUADS); + glTexCoord2f (pos, 0); + glVertex2f (0.0f, Ly); + glTexCoord2f (pos + Lx, 0); + glVertex2f (Lx, Ly); + glTexCoord2f (pos + Lx, Ly); + glVertex2f (Lx, 0.0f); + glTexCoord2f (pos, Ly); + glVertex2f (0.0f, 0.0f); + glEnd (); + pos += Lx; + glTranslatef(-it->x, -it->y, -it->z); + } + glDeleteTextures(1, &textureId); + + glPopAttrib(); + + // reset original matrices + glPopMatrix(); // GL_MODELVIEW + glMatrixMode (GL_PROJECTION); + glPopMatrix(); + glMatrixMode (matrixMode); + _elements.clear(); + _maxHeight = 0; + _totalWidth = 0; + cairo_surface_destroy(surface); + } +}; double drawContextFltkCairo::getStringWidth(const char *str) { @@ -78,48 +144,24 @@ void drawContextFltkCairo::draw() drawContextFltk::draw(); } -//ensure the surface is large enough -void drawContextFltkCairo::_resizeSurface(int w, int h) +void drawContextFltkCairo::flushString() { - if (w > cairo_image_surface_get_width(_surface) || - h > cairo_image_surface_get_height(_surface)) { - cairo_font_face_t *face = cairo_get_font_face(_cr); - cairo_matrix_t matrix; - cairo_get_font_matrix(_cr, &matrix); - cairo_destroy(_cr); - cairo_surface_destroy(_surface); - _surface = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h); - _cr = cairo_create(_surface); - cairo_set_font_face(_cr, face); - cairo_set_font_matrix(_cr, &matrix); - } + _queue->flush(); } +//ensure the surface is large enough void drawContextFltkCairo::drawString(const char *str) { + GLfloat pos[4], color[4]; + glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); + glGetFloatv(GL_CURRENT_COLOR, color); + cairo_set_font_size(_cr, _currentFontSize); cairo_text_extents_t extent; cairo_text_extents(_cr, str, &extent); - _resizeSurface(extent.width + 2, extent.height + 2); - cairo_surface_t *surface = _surface; - cairo_t *cr = _cr; - - cairo_set_source_rgba (cr, 0., 0., 0., 0); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_paint(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - extent.width +=1; - extent.height +=1; - cairo_move_to(cr, 1-extent.x_bearing, 1-extent.y_bearing); - cairo_set_source_rgba(cr, 1, 1, 1, 1); - cairo_show_text(cr, str); - - if(!_textureId) glGenTextures (1, &_textureId); - - _data2gl(cairo_image_surface_get_width(surface), - cairo_image_surface_get_height(surface), - cairo_image_surface_get_data(surface), - extent.width + 1, extent.height + 1, _textureId); - + queueString::element elem = {str, pos[0], pos[1], pos[2], color[0], color[1], color[2], color[3], + _currentFontSize, cairo_get_font_face(_cr), extent}; + cairo_font_face_reference(elem.fontFace); + _queue->append(elem); // fltk version (fl_read_image is too slow) /*Fl_Offscreen offscreen = fl_create_offscreen(100, 100); fl_begin_offscreen(offscreen); @@ -144,14 +186,14 @@ drawContextFltkCairo::~drawContextFltkCairo() { cairo_destroy(_cr); cairo_surface_destroy(_surface); - if(_textureId) glDeleteTextures(1, &_textureId); + delete _queue; } drawContextFltkCairo::drawContextFltkCairo() { - _surface = cairo_image_surface_create(CAIRO_FORMAT_A8, 100, 100); + _surface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1); + _queue = new queueString; _cr = cairo_create(_surface); - _textureId = 0; _currentFontId = -1; } @@ -189,6 +231,7 @@ void drawContextFltkCairo::setFont(int fontid, int fontsize) _currentFontId = fontid; } cairo_set_font_size(_cr, fontsize); + _currentFontSize = fontsize; } #endif diff --git a/Fltk/drawContextFltkCairo.h b/Fltk/drawContextFltkCairo.h index ec257f705b..24c49bcbfa 100644 --- a/Fltk/drawContextFltkCairo.h +++ b/Fltk/drawContextFltkCairo.h @@ -17,12 +17,12 @@ typedef struct _cairo_surface cairo_surface_t; typedef struct _cairo cairo_t; class drawContextFltkCairo : public drawContextFltk { + class queueString; + queueString *_queue; cairo_surface_t *_surface; cairo_t *_cr; - unsigned int _textureId; int _currentFontId; int _currentFontSize; - void _resizeSurface(int w, int h); public: void draw(); drawContextFltkCairo(); @@ -30,6 +30,7 @@ class drawContextFltkCairo : public drawContextFltk { double getStringWidth(const char *str); //int getStringHeight(); //int getStringDescent(); + void flushString(); void drawString(const char *str); void setFont(int fontid, int fontsize); std::string getName(){ return "Cairo"; } diff --git a/Fltk/openglWindow.cpp b/Fltk/openglWindow.cpp index 50aa13c22d..34061b8c77 100644 --- a/Fltk/openglWindow.cpp +++ b/Fltk/openglWindow.cpp @@ -312,6 +312,7 @@ void openglWindow::draw() _drawBorder(); } } + drawContext::global()->flushString(); _lock = false; } diff --git a/Graphics/drawContext.h b/Graphics/drawContext.h index 8bbaef2f2d..1afd7b01a1 100644 --- a/Graphics/drawContext.h +++ b/Graphics/drawContext.h @@ -98,6 +98,7 @@ class drawContextGlobal { virtual int getStringDescent(){ return 3; } virtual void drawString(const char *str){} virtual void resetFontTextures(){} + virtual void flushString(){} virtual std::string getName(){ return "None"; } }; -- GitLab