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