ecere/gfx/NewFonts: Font outline support
authorAlexis Naveros <alexis@ecere.com>
Tue, 24 Nov 2015 11:13:57 +0000 (06:13 -0500)
committerJerome St-Louis <jerome@ecere.com>
Mon, 21 Nov 2016 14:18:40 +0000 (09:18 -0500)
ecere/src/gfx/newFonts/drawManager.ec
ecere/src/gfx/newFonts/fontManager.ec
ecere/src/gfx/newFonts/fontRenderer.ec

index 5759a45..0fca846 100644 (file)
@@ -1,4 +1,4 @@
-import "instance"
+import "OpenGLDisplayDriver"
 
 #include <stdio.h>
 #include <math.h>
@@ -124,7 +124,6 @@ import "instance"
 
 #define OFFSET(s, m) ((uint)(uintptr) (&((s *) 0)->m))
 
-import "fontManager"
 import "textureManager"
 
 
@@ -132,14 +131,20 @@ import "textureManager"
 
 ////
 
-static struct DMDrawVertex
+static struct DMDrawVertexFlat
 {
-  // TODO FIXME: switch to short
   float vertex[2];
   float texcoord0[2];
   uint32 color;
 } __attribute__((aligned(16)));
 
+static struct DMDrawVertex
+{
+  short vertex[2];
+  short texcoord0[2];
+  uint32 color;
+} __attribute__((aligned(16)));
+
 struct DMDrawBuffer
 {
    GLuint vbo;
@@ -282,6 +287,7 @@ define DM_LAYER_TOP = DMLayer::DM_LAYER_15_TOP;
 
 define DM_PROGRAM_NORMAL = 0;
 define DM_PROGRAM_ALPHABLEND = 1;
+define DM_PROGRAM_ALPHABLEND_INTENSITY = 2;
 
 #ifdef _DEBUG
 static inline void OpenGLErrorCheck( const char *file, int line )
@@ -296,8 +302,10 @@ static inline void OpenGLErrorCheck( const char *file, int line )
 #define ERRORCHECK()
 #endif
 
-define rotationNormFactor = 32767.0f;
+#define DM_IMAGE_ROTATION_NORMFACTOR (32767.0f)
 
+#define DM_VERTEX_VERTEX_NORMFACTOR (4.0f)
+#define DM_VERTEX_TEXCOORD_NORMFACTOR (32767.0)
 
 static GLuint dmCreateShader( GLenum type, const char *shadersource, const char *optionstring )
 {
@@ -414,7 +422,7 @@ const char *dmVertexShaderNormal =
 "void main()\n"
 "{\n"
 " \n"
-"  varTexcoord0 = inTexcoord0;\n"
+"  varTexcoord0 = inTexcoord0 * (1.0/" CC_STRINGIFY(DM_VERTEX_TEXCOORD_NORMFACTOR) ");\n"
 "  varColor = inColor;\n"
 "  gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n"
 "  return;\n"
@@ -447,7 +455,7 @@ const char *dmVertexShaderAlpha =
 "void main()\n"
 "{\n"
 " \n"
-"  varTexcoord0 = inTexcoord0;\n"
+"  varTexcoord0 = inTexcoord0 * (1.0/" CC_STRINGIFY(DM_VERTEX_TEXCOORD_NORMFACTOR) ");\n"
 "  varColor = inColor;\n"
 "  gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n"
 "  return;\n"
@@ -468,6 +476,40 @@ const char *dmFragmentShaderAlpha =
 "}\n"
 ;
 
+const char *dmVertexShaderAlphaIntensity =
+"#version 130\n"
+"uniform mat4 uniMatrix;\n"
+"in vec2 inVertex;\n"
+"in vec2 inTexcoord0;\n"
+"in vec4 inColor;\n"
+"out vec2 varTexcoord0;\n"
+"out vec4 varColor;\n"
+"void main()\n"
+"{\n"
+" \n"
+"  varTexcoord0 = inTexcoord0 * (1.0/" CC_STRINGIFY(DM_VERTEX_TEXCOORD_NORMFACTOR) ");\n"
+"  varColor = inColor;\n"
+"  gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n"
+"  return;\n"
+"}\n"
+;
+
+
+const char *dmFragmentShaderAlphaIntensity =
+"#version 130\n"
+"uniform sampler2D texBase;\n"
+"in vec2 varTexcoord0;\n"
+"in vec4 varColor;\n"
+"out vec4 gl_FragColor;\n"
+"void main()\n"
+"{\n"
+"  vec2 tex;\n"
+"  tex = texture2D( texBase, varTexcoord0 ).rg;\n"
+"  gl_FragColor = vec4( varColor.rgb * tex.g, varColor.a * tex.r );\n"
+"  return;\n"
+"}\n"
+;
+
 static void matrixOrtho( float *m, float left, float right, float bottom, float top, float nearval, float farval )
 {
   float x = 2.0f / ( right - left );
@@ -528,72 +570,12 @@ public class DrawManager
    int drawBarrierIndex;
    uint32 orderBarrierMask;
 
-   // Font manager
-   FontManager fm;
-
    // Counter to track program uniforms and such
    int64 updateCount;
 
    GLuint prevProgram;
 
-   static void flushRenderDrawBuffer( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount )
-   {
-      if( !program || flags.prehistoricOpenGL )
-      {
-         glEnable( GL_TEXTURE_2D );
-         glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
-         glColor3f( 1.0, 1.0, 1.0 );
-
-         glEnableClientState( GL_VERTEX_ARRAY );
-         glEnableClientState( GL_TEXTURE_COORD_ARRAY );
-         glEnableClientState( GL_COLOR_ARRAY );
-
-         glVertexPointer( 2, GL_FLOAT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) );
-         glTexCoordPointer( 2, GL_FLOAT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) );
-         glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) );
-
-         glDrawArrays( GL_TRIANGLES, 0, vertexCount );
-
-         glDisableClientState( GL_VERTEX_ARRAY );
-         glDisableClientState( GL_TEXTURE_COORD_ARRAY );
-         glDisableClientState( GL_COLOR_ARRAY );
-         glDisable( GL_TEXTURE_2D );
-     }
-     else
-     {
-         glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
-         if( program->vertexloc != -1 )
-         {
-            glEnableVertexAttribArray( program->vertexloc );
-            glVertexAttribPointer( program->vertexloc, 2, GL_FLOAT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) );
-         }
-         if( program->texcoord0loc != -1 )
-         {
-            glEnableVertexAttribArray( program->texcoord0loc );
-            glVertexAttribPointer( program->texcoord0loc, 2, GL_FLOAT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) );
-         }
-         if( program->colorloc != -1 )
-         {
-            glEnableVertexAttribArray( program->colorloc );
-            glVertexAttribPointer( program->colorloc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) );
-         }
-
-         glDrawArrays( GL_TRIANGLES, 0, vertexCount );
-
-         if( program->vertexloc != -1 )
-            glDisableVertexAttribArray( program->vertexloc );
-         if( program->texcoord0loc != -1 )
-            glDisableVertexAttribArray( program->texcoord0loc );
-         if( program->colorloc != -1 )
-            glDisableVertexAttribArray( program->colorloc );
-      }
-
-   #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER
-      glFlush();
-   #endif
-   }
-
-   static DMProgram *dmFlushUseProgram( int programIndex )
+   static DMProgram *flushUseProgram( int programIndex )
    {
       DMProgram *program;
 
@@ -615,165 +597,33 @@ public class DrawManager
       return program;
    }
 
-public:
-   bool init( FontManager fontManager, DrawManagerFlags flags )
-   {
-      int drawBufferIndex, programIndex;
-      DMDrawBuffer *drawBuffer;
-
-      imageBufferCount = 0;
-      imageBufferSize = 4096;
-      imageBuffer = new DMImageBuffer[imageBufferSize];
-      imageBufferTmp = new DMImageBuffer[imageBufferSize];
-
-      for( drawBufferIndex = 0 ; drawBufferIndex < DM_CONTEXT_DRAW_BUFFER_COUNT ; drawBufferIndex++ )
-      {
-         drawBuffer = &this.drawBuffer[drawBufferIndex];
-         drawBuffer->glType = GL_FLOAT;
-         drawBuffer->vertexCount = 0;
-         drawBuffer->vertexAlloc = DM_CONTEXT_DRAW_BUFFER_VERTEX_ALLOC;
-         glGenBuffers( 1, &drawBuffer->vbo );
-         glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
-         glBufferData( GL_ARRAY_BUFFER, drawBuffer->vertexAlloc * sizeof(DMDrawVertex), 0, GL_DYNAMIC_DRAW );
-         drawBuffer->vertexBuffer = new DMDrawVertex[drawBuffer->vertexAlloc];
-      }
-
-      fm = fontManager;
-      this.flags = flags;
-
-      if( !flags.prehistoricOpenGL )
-      {
-         DMProgram *program;
-         for( programIndex = 0 ; programIndex < DM_PROGRAM_COUNT ; programIndex++ )
-         {
-            program = &shaderprograms[ programIndex ];
-            program->flags = 0;
-            program->lastUpdateCount = -1;
-         }
-         program = &shaderprograms[ DM_PROGRAM_NORMAL ];
-         if( !( dmCreateProgram( program, dmVertexShaderNormal, dmFragmentShaderNormal, 0 ) ) )
-            return false;
-         program = &shaderprograms[ DM_PROGRAM_ALPHABLEND ];
-         if( !( dmCreateProgram( program, dmVertexShaderAlpha, dmFragmentShaderAlpha, 0 ) ) )
-            return false;
-         // glUseProgram( 0 );
-      }
-
-      updateCount = 0;
-
-      return true;
-   }
-
-
-   void end( )
-   {
-      int i;
-
-      for( i = 0 ; i < DM_CONTEXT_DRAW_BUFFER_COUNT ; i++ )
-      {
-         DMDrawBuffer *db = &drawBuffer[i];
-         glDeleteBuffers( 1, &db->vbo );
-         delete db->vertexBuffer;
-      }
-
-      // TODO: Destroy the shaders!
-      delete imageBuffer;
-      delete imageBufferTmp;
-   }
-
-   void ready( int viewportwidth, int viewportheight )
-   {
-      glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&prevProgram);
-      // while(glGetError());
-
-      // ERRORCHECK();
-
-      // Save OpenGL state
-      // FIXME: no glPushAttrib() in core profile
-//#ifndef SHADERS
-      glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS );
-      glPushAttrib( GL_ALL_ATTRIB_BITS );
-//#endif
-
-      // Prepare rendering pass
-      matrixOrtho( matrix, 0.0, (float)viewportwidth, (float)viewportheight, 0.0, -1.0f, 1.0 );
-      drawBarrierIndex = 0;
-      orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
-      orderBarrierMask = 0;
-
-      updateCount++;
-   }
-
-   void drawImage( DMImage *image, int offsetx, int offsety, int sizex, int sizey, uint32 color )
+   static void flushRenderDrawBufferArchaic( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount )
    {
-     DMImageBuffer *imageBuffer;
-
-     if( image->flags.empty || ( sizex <= 0 ) || ( sizey <= 0 ) )
-       return;
+      glEnable( GL_TEXTURE_2D );
+      glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+      glColor3f( 1.0, 1.0, 1.0 );
 
-     if( imageBufferCount >= imageBufferSize )
-     {
-       imageBufferSize <<= 1;
-       this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
-       imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
-     }
-
-     imageBuffer = &this.imageBuffer[ imageBufferCount ];
-     imageBuffer->image = image;
-     imageBuffer->offsetx = (short)offsetx;
-     imageBuffer->offsety = (short)offsety;
-     imageBuffer->sizex = (short)sizex;
-     imageBuffer->sizey = (short)sizey;
-   #if DM_ENABLE_IMAGE_ROTATION
-     imageBuffer->angsin = 0;
-     imageBuffer->angcos = (short)rotationNormFactor;
-   #endif
-     imageBuffer->color = color;
-     imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
-
-   #if DM_RENDER_IMAGE_DEBUG
-   printf( "  Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
-   #endif
+      glEnableClientState( GL_VERTEX_ARRAY );
+      glEnableClientState( GL_TEXTURE_COORD_ARRAY );
+      glEnableClientState( GL_COLOR_ARRAY );
 
-     imageBufferCount++;
-   }
-
-   void drawImageFloat( DMImage *image, float offsetx, float offsety, float sizex, float sizey, float angsin, float angcos, uint32 color )
-   {
-     DMImageBuffer *imageBuffer;
-
-     if( image->flags.empty || sizex <= 0 || sizey <= 0 )
-       return;
+      glVertexPointer( 2, GL_FLOAT, sizeof(DMDrawVertexFlat), (void *)OFFSET(DMDrawVertexFlat,vertex) );
+      glTexCoordPointer( 2, GL_FLOAT, sizeof(DMDrawVertexFlat), (void *)OFFSET(DMDrawVertexFlat,texcoord0) );
+      glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(DMDrawVertexFlat), (void *)OFFSET(DMDrawVertexFlat,color) );
 
-     if( imageBufferCount >= imageBufferSize )
-     {
-       imageBufferSize <<= 1;
-       this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
-       imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
-     }
+      glDrawArrays( GL_TRIANGLES, 0, vertexCount );
 
-     imageBuffer = &this.imageBuffer[ imageBufferCount ];
-     imageBuffer->image = image;
-     imageBuffer->offsetx = (short)offsetx;
-     imageBuffer->offsety = (short)offsety;
-     imageBuffer->sizex = (short)sizex;
-     imageBuffer->sizey = (short)sizey;
-   #if DM_ENABLE_IMAGE_ROTATION
-     imageBuffer->angsin = (short)roundf( angsin * rotationNormFactor );
-     imageBuffer->angcos = (short)roundf( angcos * rotationNormFactor );
-   #endif
-     imageBuffer->color = color;
-     imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
+      glDisableClientState( GL_VERTEX_ARRAY );
+      glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+      glDisableClientState( GL_COLOR_ARRAY );
+      glDisable( GL_TEXTURE_2D );
 
-   #if DM_RENDER_IMAGE_DEBUG
-   printf( "  Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
+   #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER
+      glFlush();
    #endif
-
-     imageBufferCount++;
    }
 
-
-   void flushImages( )
+   void flushDrawImagesArchaic( )
    {
      bool flushflag, stateBlend;
      int index, vertexCount, programIndex;
@@ -786,7 +636,7 @@ public:
      DMImage *image, *bindimage;
      Texture texture, bindTexture;
      DMDrawBuffer *drawBuffer;
-     DMDrawVertex *vboVertex;
+     DMDrawVertexFlat *vboVertex;
      DMProgram *program;
 
      ERRORCHECK();
@@ -795,10 +645,10 @@ public:
      this.orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
      if(imageBufferCount)
      {
-        /* Sort by image type and texture, minimize state changes */
+        // Sort by image type and texture, minimize state changes
         dmSortImages( this.imageBuffer, imageBufferTmp, imageBufferCount, (uint32)( (intptr_t)this.imageBuffer >> 4 ) );
 
-        /* Fill a drawBuffer, write vertex and texcoords */
+        // Fill a drawBuffer, write vertex and texcoords
         drawBuffer = &this.drawBuffer[drawBufferIndex];
         drawBufferIndex = ( drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
         glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
@@ -841,9 +691,9 @@ public:
             {
               glUnmapBuffer( GL_ARRAY_BUFFER );
               // Flush font manager texture updates
-              fm.flushUpdate( );
+              flush( );
               // Render buffered images
-              flushRenderDrawBuffer( drawBuffer, program, vertexCount );
+              flushRenderDrawBufferArchaic( drawBuffer, program, vertexCount );
               drawBuffer = &this.drawBuffer[drawBufferIndex];
               drawBufferIndex = ( drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
               glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
@@ -862,7 +712,7 @@ public:
             if( programIndex != image->programIndex )
             {
               programIndex = image->programIndex;
-              program = dmFlushUseProgram( programIndex );
+              program = flushUseProgram( programIndex );
             }
             if( texture != bindTexture )
             {
@@ -880,8 +730,8 @@ public:
       #endif
 
       #if DM_ENABLE_IMAGE_ROTATION
-          angsin = (float)imageBuffer->angsin * (1.0f/rotationNormFactor);
-          angcos = (float)imageBuffer->angcos * (1.0f/rotationNormFactor);
+          angsin = (float)imageBuffer->angsin * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
+          angcos = (float)imageBuffer->angcos * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
           sizex = (float)imageBuffer->sizex;
           sizey = (float)imageBuffer->sizey;
           vx0 = (float)( imageBuffer->offsetx );
@@ -945,10 +795,12 @@ public:
         }
 
         glUnmapBuffer( GL_ARRAY_BUFFER );
+
         // Flush font manager texture updates
-        fm.flushUpdate( );
+        flush();
+
         // Render buffered images
-        flushRenderDrawBuffer( drawBuffer, program, vertexCount );
+        flushRenderDrawBufferArchaic( drawBuffer, program, vertexCount );
         imageBufferCount = 0;
 
         ERRORCHECK();
@@ -969,6 +821,422 @@ public:
 //#endif
    }
 
+   void flushRenderDrawBuffer( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount )
+   {
+      glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+      if( program->vertexloc != -1 )
+      {
+         glEnableVertexAttribArray( program->vertexloc );
+         glVertexAttribPointer( program->vertexloc, 2, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) );
+      }
+      if( program->texcoord0loc != -1 )
+      {
+         glEnableVertexAttribArray( program->texcoord0loc );
+         glVertexAttribPointer( program->texcoord0loc, 2, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) );
+      }
+      if( program->colorloc != -1 )
+      {
+         glEnableVertexAttribArray( program->colorloc );
+         glVertexAttribPointer( program->colorloc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) );
+      }
+
+      glDrawArrays( GL_TRIANGLES, 0, vertexCount );
+
+      if( program->vertexloc != -1 )
+         glDisableVertexAttribArray( program->vertexloc );
+      if( program->texcoord0loc != -1 )
+         glDisableVertexAttribArray( program->texcoord0loc );
+      if( program->colorloc != -1 )
+         glDisableVertexAttribArray( program->colorloc );
+
+   #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER
+      glFlush();
+   #endif
+   }
+
+   void flushDrawImages( )
+   {
+      int index, stateblend, vertexcount, flushflag, programIndex;
+      float vx0, vx1, vx2, vx3, vy0, vy1, vy2, vy3;
+      #if DM_ENABLE_IMAGE_ROTATION
+      float angsin, angcos, sizex, sizey;
+      #endif
+      float tx0, tx1, ty0, ty1;
+      DMImageBuffer *imageBuffer;
+      DMImage *image, *bindimage;
+      Texture texture, bindtexture;
+      DMDrawBuffer *drawBuffer;
+      DMDrawVertex *vbovertex;
+      DMProgram *program;
+
+      ERRORCHECK();
+
+      this.drawBarrierIndex = 0;
+      this.orderBarrierMask = this.drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
+      if( imageBufferCount )
+      {
+         /* Sort by image type and texture, minimize state changes */
+         dmSortImages( this.imageBuffer, this.imageBufferTmp, imageBufferCount, (uint32)( (uintptr)this.imageBuffer >> 4 ) );
+
+         /* Fill a drawBuffer, write vertex and texcoords */
+         drawBuffer = &this.drawBuffer[this.drawBufferIndex];
+         this.drawBufferIndex = ( this.drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
+         glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+         vbovertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
+         vertexcount = 0;
+
+         glActiveTexture( GL_TEXTURE0 );
+         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+         glDisable( GL_BLEND );
+
+         #if DM_RENDER_IMAGE_DEBUG
+         printf( " Flush %d images\n", (int)imageBufferCount );
+         #endif
+
+         bindimage = 0;
+         bindtexture = 0;
+         stateblend = 0;
+         programIndex = -1;
+         program = 0;
+         imageBuffer = this.imageBuffer;
+         for( index = 0 ; index < imageBufferCount ; index++, imageBuffer++ )
+         {
+          image = imageBuffer->image;
+          texture = image->texture;
+
+          flushflag = 0;
+          if( image != bindimage )
+          {
+            if( stateblend != ( image->flags.blending ) )
+              flushflag = 1;
+            if( texture != bindtexture )
+              flushflag = 1;
+          }
+          if( vertexcount >= ( drawBuffer->vertexAlloc - 6 ) )
+            flushflag = 1;
+
+          if( flushflag )
+          {
+            if( vertexcount )
+            {
+              glUnmapBuffer( GL_ARRAY_BUFFER );
+              // Flush font manager texture updates
+              flush();
+
+              // Render buffered images
+              flushRenderDrawBuffer( drawBuffer, program, vertexcount );
+              drawBuffer = &this.drawBuffer[this.drawBufferIndex];
+              this.drawBufferIndex = ( this.drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
+              glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+              vbovertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
+              vertexcount = 0;
+            }
+
+            if( stateblend != ( image->flags.blending ) )
+            {
+              stateblend = image->flags.blending;
+              ( stateblend ? glEnable : glDisable )( GL_BLEND );
+            #if DM_RENDER_IMAGE_DEBUG
+               printf( "  Switch blending %d\n", ( stateblend != 0 ) );
+            #endif
+            }
+            if( programIndex != image->programIndex )
+            {
+              programIndex = image->programIndex;
+              program = flushUseProgram( programIndex );
+            }
+            if( texture != bindtexture )
+            {
+              bindtexture = texture;
+              glBindTexture( GL_TEXTURE_2D, bindtexture.glTex );
+      #if DM_RENDER_IMAGE_DEBUG
+              printf( "  Switch to texture 0x%x\n", (int)texture.ordermask );
+      #endif
+            }
+            bindimage = image;
+          }
+
+         #if DM_RENDER_IMAGE_DEBUG
+         printf( "   Render image at %d %d, order 0x%x, texture %p\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex, texture );
+         #endif
+
+      #if DM_ENABLE_IMAGE_ROTATION
+          /* FIXME TODO: Don't go through float, compute texcoord integers directly */
+          angsin = (float)imageBuffer->angsin * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
+          angcos = (float)imageBuffer->angcos * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
+          sizex = (float)imageBuffer->sizex;
+          sizey = (float)imageBuffer->sizey;
+          vx0 = (float)( imageBuffer->offsetx );
+          vy0 = (float)( imageBuffer->offsety );
+          vx1 = vx0 + ( angcos * sizex );
+          vy1 = vy0 + ( angsin * sizex );
+          vx2 = vx0 - ( angsin * sizey );
+          vy2 = vy0 + ( angcos * sizey );
+          vx3 = vx0 + ( angcos * sizex ) - ( angsin * sizey );
+          vy3 = vy0 + ( angsin * sizex ) + ( angcos * sizey );
+      #else
+          /* FIXME TODO: Don't go through float, compute texcoord integers directly */
+          vx0 = (float)( imageBuffer->offsetx );
+          vy0 = (float)( imageBuffer->offsety );
+          vx3 = vx0 + (float)( imageBuffer->sizex );
+          vy3 = vy0 + (float)( imageBuffer->sizey );
+          vx1 = vx3;
+          vy1 = vy0;
+          vx2 = vx0;
+          vy2 = vy3;
+      #endif
+
+          /* FIXME TODO: Don't go through float, compute texcoord integers directly */
+          tx0 = (float)( image->srcx ) * texture.widthinv;
+          ty0 = (float)( image->srcy ) * texture.heightinv;
+          tx1 = (float)( image->srcx + image->sizex ) * texture.widthinv;
+          ty1 = (float)( image->srcy + image->sizey ) * texture.heightinv;
+
+          /* Write data to VBO */
+          /* TODO: write vertex/texcoord all at once with SSE */
+          vbovertex[0].vertex[0] = (short)( vx3 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[0].vertex[1] = (short)( vy3 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[0].texcoord0[0] = (short)( tx1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[0].texcoord0[1] = (short)( ty1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[0].color = imageBuffer->color;
+          vbovertex[1].vertex[0] = (short)( vx1 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[1].vertex[1] = (short)( vy1 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[1].texcoord0[0] = (short)( tx1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[1].texcoord0[1] = (short)( ty0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[1].color = imageBuffer->color;
+          vbovertex[2].vertex[0] = (short)( vx2 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[2].vertex[1] = (short)( vy2 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[2].texcoord0[0] = (short)( tx0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[2].texcoord0[1] = (short)( ty1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[2].color = imageBuffer->color;
+          vbovertex[3].vertex[0] = (short)( vx0 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[3].vertex[1] = (short)( vy0 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[3].texcoord0[0] = (short)( tx0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[3].texcoord0[1] = (short)( ty0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[3].color = imageBuffer->color;
+          vbovertex[4].vertex[0] = (short)( vx2 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[4].vertex[1] = (short)( vy2 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[4].texcoord0[0] = (short)( tx0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[4].texcoord0[1] = (short)( ty1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[4].color = imageBuffer->color;
+          vbovertex[5].vertex[0] = (short)( vx1 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[5].vertex[1] = (short)( vy1 * DM_VERTEX_VERTEX_NORMFACTOR );
+          vbovertex[5].texcoord0[0] = (short)( tx1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[5].texcoord0[1] = (short)( ty0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+          vbovertex[5].color = imageBuffer->color;
+
+          vbovertex += 6;
+          vertexcount += 6;
+        }
+
+        glUnmapBuffer( GL_ARRAY_BUFFER );
+        // Flush font manager texture updates
+        flush();
+        // Render buffered images
+        flushRenderDrawBuffer( drawBuffer, program, vertexcount );
+        imageBufferCount = 0;
+
+        ERRORCHECK();
+
+      #if 1
+        glBindBuffer( GL_ARRAY_BUFFER, 0 );
+        glabCurArrayBuffer = 0;
+
+        glUseProgram( prevProgram );
+      #endif
+      }
+
+      // Restore OpenGL state
+      // FIXME: no glPushAttrib() in core profile
+//#ifndef SHADERS
+      glPopAttrib();
+      glPopClientAttrib();
+//#endif
+   }
+
+public:
+
+   virtual void flush();
+
+   bool init( DrawManagerFlags flags )
+   {
+      int drawBufferIndex, programIndex;
+      DMDrawBuffer *drawBuffer;
+      uint vertexSize;
+
+      imageBufferCount = 0;
+      imageBufferSize = 4096;
+      imageBuffer = new DMImageBuffer[imageBufferSize];
+      imageBufferTmp = new DMImageBuffer[imageBufferSize];
+
+      this.flags = flags;
+
+      if( flags.prehistoricOpenGL )
+         vertexSize = sizeof(DMDrawVertexFlat);
+      else
+      {
+         DMProgram *program;
+         for( programIndex = 0 ; programIndex < DM_PROGRAM_COUNT ; programIndex++ )
+         {
+            program = &shaderprograms[ programIndex ];
+            program->flags = 0;
+            program->lastUpdateCount = -1;
+         }
+         program = &shaderprograms[ DM_PROGRAM_NORMAL ];
+         if( !( dmCreateProgram( program, dmVertexShaderNormal, dmFragmentShaderNormal, 0 ) ) )
+            return false;
+         program = &shaderprograms[ DM_PROGRAM_ALPHABLEND ];
+         if( !( dmCreateProgram( program, dmVertexShaderAlpha, dmFragmentShaderAlpha, 0 ) ) )
+            return false;
+         program = &shaderprograms[ DM_PROGRAM_ALPHABLEND_INTENSITY ];
+         if( !( dmCreateProgram( program, dmVertexShaderAlphaIntensity, dmFragmentShaderAlphaIntensity, 0 ) ) )
+            return false;
+         // glUseProgram( 0 );
+         vertexSize = sizeof(DMDrawVertex);
+      }
+
+      for( drawBufferIndex = 0 ; drawBufferIndex < DM_CONTEXT_DRAW_BUFFER_COUNT ; drawBufferIndex++ )
+      {
+         drawBuffer = &this.drawBuffer[drawBufferIndex];
+         drawBuffer->glType = GL_FLOAT;
+         drawBuffer->vertexCount = 0;
+         drawBuffer->vertexAlloc = DM_CONTEXT_DRAW_BUFFER_VERTEX_ALLOC;
+         glGenBuffers( 1, &drawBuffer->vbo );
+         glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+         glBufferData( GL_ARRAY_BUFFER, drawBuffer->vertexAlloc * vertexSize, 0, GL_DYNAMIC_DRAW );
+         drawBuffer->vertexBuffer = new byte[drawBuffer->vertexAlloc * vertexSize];
+      }
+
+      updateCount = 0;
+
+      return true;
+   }
+
+
+   void end( )
+   {
+      int i;
+
+      for( i = 0 ; i < DM_CONTEXT_DRAW_BUFFER_COUNT ; i++ )
+      {
+         DMDrawBuffer *db = &drawBuffer[i];
+         glDeleteBuffers( 1, &db->vbo );
+         delete db->vertexBuffer;
+      }
+
+      // TODO: Destroy the shaders!
+      delete imageBuffer;
+      delete imageBufferTmp;
+   }
+
+   void ready( int viewportwidth, int viewportheight )
+   {
+      int mindex;
+      float norminv;
+      glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&prevProgram);
+      // while(glGetError());
+
+      // ERRORCHECK();
+
+      // Save OpenGL state
+      // FIXME: no glPushAttrib() in core profile
+//#ifndef SHADERS
+      glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS );
+      glPushAttrib( GL_ALL_ATTRIB_BITS );
+//#endif
+
+      // Prepare rendering pass
+      matrixOrtho( matrix, 0.0, (float)viewportwidth, (float)viewportheight, 0.0, -1.0f, 1.0 );
+      norminv = 1.0f / DM_VERTEX_VERTEX_NORMFACTOR;
+      for( mindex = 0 ; mindex < 12 ; mindex += 4 )
+      {
+        matrix[mindex+0] *= norminv;
+        matrix[mindex+1] *= norminv;
+        matrix[mindex+2] *= norminv;
+      }
+      drawBarrierIndex = 0;
+      orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
+      orderBarrierMask = 0;
+
+      updateCount++;
+   }
+
+   void drawImage( DMImage *image, int offsetx, int offsety, int sizex, int sizey, uint32 color )
+   {
+     DMImageBuffer *imageBuffer;
+
+     if( image->flags.empty || ( sizex <= 0 ) || ( sizey <= 0 ) )
+       return;
+
+     if( imageBufferCount >= imageBufferSize )
+     {
+       imageBufferSize <<= 1;
+       this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
+       imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
+     }
+
+     imageBuffer = &this.imageBuffer[ imageBufferCount ];
+     imageBuffer->image = image;
+     imageBuffer->offsetx = (short)offsetx;
+     imageBuffer->offsety = (short)offsety;
+     imageBuffer->sizex = (short)sizex;
+     imageBuffer->sizey = (short)sizey;
+   #if DM_ENABLE_IMAGE_ROTATION
+     imageBuffer->angsin = 0;
+     imageBuffer->angcos = (short)DM_IMAGE_ROTATION_NORMFACTOR;
+   #endif
+     imageBuffer->color = color;
+     imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
+
+   #if DM_RENDER_IMAGE_DEBUG
+   printf( "  Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
+   #endif
+
+     imageBufferCount++;
+   }
+
+   void drawImageFloat( DMImage *image, float offsetx, float offsety, float sizex, float sizey, float angsin, float angcos, uint32 color )
+   {
+     DMImageBuffer *imageBuffer;
+
+     if( image->flags.empty || sizex <= 0 || sizey <= 0 )
+       return;
+
+     if( imageBufferCount >= imageBufferSize )
+     {
+       imageBufferSize <<= 1;
+       this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
+       imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
+     }
+
+     imageBuffer = &this.imageBuffer[ imageBufferCount ];
+     imageBuffer->image = image;
+     imageBuffer->offsetx = (short)offsetx;
+     imageBuffer->offsety = (short)offsety;
+     imageBuffer->sizex = (short)sizex;
+     imageBuffer->sizey = (short)sizey;
+   #if DM_ENABLE_IMAGE_ROTATION
+     imageBuffer->angsin = (short)roundf( angsin * DM_IMAGE_ROTATION_NORMFACTOR );
+     imageBuffer->angcos = (short)roundf( angcos * DM_IMAGE_ROTATION_NORMFACTOR );
+   #endif
+     imageBuffer->color = color;
+     imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
+
+   #if DM_RENDER_IMAGE_DEBUG
+   printf( "  Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
+   #endif
+
+     imageBufferCount++;
+   }
+
+   void flushImages( )
+   {
+     if( flags.prehistoricOpenGL )
+       flushDrawImagesArchaic( );
+     else
+       flushDrawImages( );
+   }
+
    void drawBarrier( )
    {
       drawBarrierIndex++;
index ee30248..dce96e8 100644 (file)
@@ -7,6 +7,7 @@ import "LinkList"
 import "File"
 
 import "atlasBuilder"
+import "imgDistMap"
 
 #include <math.h>
 
@@ -242,7 +243,7 @@ public class FontManagerRenderer
 public:
    FontManager fm;
 
-   virtual bool init();
+   virtual bool init(int channelCount);
 
    // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
    virtual int createTexture( int width, int height );
@@ -288,12 +289,6 @@ public:
 
 #define FM_ENABLE_HINTING (1)
 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
-#if FM_SUPPORT_GLYPH_ROTATION
- #define FM_TEXTURE_PADDING (1)
-#else
- #define FM_TEXTURE_PADDING (0)
-#endif
-#define FM_DEBUG_WHITERECT (0)
 
 
 #define FM_HASH_TABLE_SIZE (4096)
@@ -373,30 +368,19 @@ struct FMFreeTypeFont
      return 1;
    }
 
-   static void copyGlyphBitmap( byte *dst, int glyphwidth, int glyphheight, int dststride, int glyphindex )
-   {
-     int x, y;
-     byte *dstrow, *src;
-     FT_GlyphSlot glyphslot;
-
-     glyphslot = face->glyph;
-     src = glyphslot->bitmap.buffer;
-     for( y = 0 ; y < glyphslot->bitmap.rows ; y++ )
-     {
-       dstrow = &dst[ y * dststride ];
-       for( x = 0 ; x < glyphslot->bitmap.width ; x++ )
-         dstrow[ x ] = src[ x ];
-       src += glyphslot->bitmap.width;
-     }
-     return;
-   }
-
    static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
    {
      FT_Vector ftKerning;
      FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
      return ftKerning.x;
    }
+
+   static inline byte *getGlyphBitmap( int glyphindex )
+   {
+     FT_GlyphSlot glyphslot;
+     glyphslot = face->glyph;
+     return glyphslot->bitmap.buffer;
+   }
 };
 
 #define FM_DEF_CODEPOINT_BITS (32)
@@ -468,6 +452,72 @@ public class FMFont : struct
    int glyphalloc;
    int glyphcount;
    int hashtable[FM_HASH_TABLE_SIZE];
+   int glyphPaddingWidth;
+
+   void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth );
+   void *processImageContext;
+
+   /*
+   public void setFontImageProcessing(
+      void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
+      void *opaquecontext )
+   {
+     this.processImage = processImage;
+     this.processImageContext = opaquecontext;
+   }
+   */
+
+   float outlineRadius;
+   float outlineAlphaFactor;
+   float outlineIntensityFactor;
+
+   static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth )
+   {
+     int x, y;
+     byte *src, *dst, *dstrow;
+     float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
+     float *distancemap, *dmap;
+
+     distancemap = new float[width * height];
+
+     src = &image[0];
+     imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
+
+     alphafactor = font.outlineAlphaFactor; //2.0f;
+     intensityfactor = font.outlineIntensityFactor; // 0.2f;
+     range = (float)font.outlineRadius;
+     rangeinv = 1.0f / range;
+
+     dmap = distancemap;
+     dst = &image[0];
+     for( y = 0 ; y < height ; y++ )
+     {
+       dstrow = dst;
+       for( x = 0 ; x < width ; x++ )
+       {
+         rangebase = ( range - dmap[ x ] ) * rangeinv;
+         alpha = alphafactor * rangebase;
+         intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase );
+         /* Alpha channel */
+         dstrow[0] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
+         /* Intensity channel */
+         dstrow[1] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
+         dstrow += bytesperpixel;
+       }
+       dst += bytesperline;
+       dmap += width;
+     }
+     delete distancemap;
+   }
+
+   public void setOutline(float intensityFactor, float alphaFactor, float radius)      // TODO: Figure out radius?
+   {
+      outlineIntensityFactor = intensityFactor;
+      outlineAlphaFactor = alphaFactor;
+      outlineRadius = radius;
+      processImage = outlineProcessGlyphImage;
+      processImageContext = this;
+   }
 
    ~FMFont()
    {
@@ -491,12 +541,13 @@ public class FMFont : struct
 
    static float getVertAlign( FMTextAlignment align, int size )
    {
+      float sizef = size;
      if( align.vertAlignment == top )
-       return ascender * size;
+       return ascender * sizef;
      else if( align.vertAlignment == middle )
-       return middleAlign * size;
+       return middleAlign * sizef;
      else if( align.vertAlignment == bottom )
-       return descender * (float)size;
+       return descender * sizef;
      return 0.0f;
    }
 
@@ -529,11 +580,50 @@ struct FMState
 
 static FT_Library ftLibrary2;
 
+static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
+{
+  int x, y;
+  for( y = 0 ; y < glyphheight ; y++ )
+  {
+    byte *dstrow = &dst[ y * dststride ];
+    for( x = 0 ; x < glyphwidth ; x++ )
+      dstrow[ x ] = src[ x ];
+    src += glyphwidth;
+  }
+}
+
+static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
+{
+  int x, y;
+  for( y = 0 ; y < glyphheight ; y++ )
+  {
+    byte *dstrow = &dst[ y * dststride ];
+    for( x = 0 ; x < glyphwidth ; x++ )
+      dstrow[ x << 1 ] = src[ x ];
+    src += glyphwidth;
+  }
+}
+
+static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
+{
+  int x, y;
+  for( y = 0 ; y < glyphheight ; y++ )
+  {
+    byte *dstrow = &dst[ y * dststride ];
+    for( x = 0 ; x < glyphwidth ; x++ )
+      dstrow[ x << 2 ] = src[ x ];
+    src += glyphwidth;
+  }
+}
+
 public class FontManager
 {
   FontManagerRenderer renderer;
   int width, height;
   float widthinv, heightinv;
+  int bytesperpixel;
+  int bytesperline;
+  int channelindex;
 
   AtlasBuilder atlas { };
   byte *texdata;
@@ -544,47 +634,24 @@ public class FontManager
   FMState states[FM_MAX_STATES];
   int nstates;
 
-   #if FM_DEBUG_WHITERECT
-   static void addWhiteRect( int w, int h )
-   {
-     int gx, gy;
-     if( atlas.addRect( w, h, &gx, &gy ) )
-     {
-        // Rasterize
-        byte * dst = &texdata[ gx + ( gy * width ) ];
-        int x, y;
-        for( y = 0 ; y < h ; y++)
-        {
-          for( x = 0 ; x < w ; x++ )
-            dst[x] = 0xff;
-          dst += width;
-        }
-
-        dirtyrect[0] = Min( dirtyrect[0], gx );
-        dirtyrect[1] = Min( dirtyrect[1], gy );
-        dirtyrect[2] = Max( dirtyrect[2], gx + w );
-        dirtyrect[3] = Max( dirtyrect[3], gy + h );
-     }
-   }
-   #endif
+   void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
+
 
    static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale )
    {
-     int i, glyphindex, advance, x0, y0, x1, y1, glyphwidth, glyphheight, gx, gy;
-   #if FM_TEXTURE_PADDING >= 1
-     int x, y;
-   #endif
+     int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
+     int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
      uint64 glyphdef;
      FMGlyph *glyph;
      uint32 hashindex;
      int padding, added;
      byte *bdst;
-     byte *dst;
+     byte *dst, *src;
 
      glyph = 0;
      if( size < 0.2 )
        return 0;
-     padding = blurradius + FM_TEXTURE_PADDING;
+     padding = blurradius + font.glyphPaddingWidth;
 
      /* Find code point and size. */
      glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale );
@@ -597,7 +664,7 @@ public class FontManager
        i = font.glyphs[i].listnext;
      }
 
-     /* Could not find glyph, create it. */
+     /* Could not find glyph, create it */
      if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
      {
        glyphindex = -1;
@@ -611,8 +678,7 @@ public class FontManager
    #endif
        y0 = -(int)ceilf( font.limitmaxy * (float)size );
        y1 = -(int)floorf( font.limitminy * (float)size );
-       glyphheight = y1 - y0;
-       i = ( glyphheight - size ) / 3;
+       i = ( (y1 - y0) - size ) / 3;
        y0 += i;
        y1 -= i;
      }
@@ -626,16 +692,18 @@ public class FontManager
          return 0;
        }
      }
-     glyphwidth = ( x1 - x0 ) + ( padding << 1 );
-     glyphheight = ( y1 - y0 ) + ( padding << 1 );
+     glyphwidth = ( x1 - x0 );
+     glyphheight = ( y1 - y0 );
+     glyphareawidth = glyphwidth + (padding << 1);
+     glyphareaheight = glyphheight + (padding << 1);
 
      // Find free spot for the rect in the atlas
-     added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
+     added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
      if( !( added ) && ( onAtlasFull ) )
      {
        /* Atlas is full, let the user to resize the atlas (or not), and try again. */
        onAtlasFull();
-       added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
+       added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
      }
      if( !( added ) )
        return 0;
@@ -646,50 +714,59 @@ public class FontManager
      glyph->glyphindex = glyphindex;
      glyph->x0 = (short)gx;
      glyph->y0 = (short)gy;
-     glyph->x1 = (short)( glyph->x0 + glyphwidth );
-     glyph->y1 = (short)( glyph->y0 + glyphheight );
+     glyph->x1 = (short)( glyph->x0 + glyphareawidth );
+     glyph->y1 = (short)( glyph->y0 + glyphareaheight );
      glyph->advance = (short)advance;
      glyph->offsetx = (short)( x0 - padding );
      glyph->offsety = (short)( y0 - padding );
      glyph->listnext = 0;
      glyph->imageIndex = -1;
      if( renderer.registerImage )
-       glyph->imageIndex = renderer.registerImage( gx, gy, glyphwidth, glyphheight );
+       glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight );
 
-     /* Add char to hash table */
+     // Add char to hash table
      glyph->listnext = font.hashtable[hashindex];
      font.hashtable[hashindex] = font.glyphcount - 1;
 
+     // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
+     dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
+     for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
+       memset( dst, 0, glyphareawidth * bytesperpixel );
+
      /* Rasterize */
-     dst = &texdata[ ( glyph->x0 + padding ) + ( ( glyph->y0 + padding ) * this.width ) ];
+     dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
      if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
-       buildCursorGlyph( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width );
+     {
+         src = new byte[ glyphwidth * glyphheight ];
+         buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
+         copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
+         delete src;
+     }
      else
-       font.ftFont.copyGlyphBitmap( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width, glyphindex );
+     {
+        src = font.ftFont.getGlyphBitmap(glyphindex);
+        copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
+     }
 
      /* Blur */
      if( blurradius > 0 )
      {
-       bdst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
+#if 1
+    printf( "Do NOT use the blur! This will be removed in favor of external image processing\n" );
+#endif
+       bdst = &texdata[ glyph->x0 * bytesperpixel + ( glyph->y0 * bytesperline ) ];
        if( blurscale == 1 )
-         blur(bdst, glyphwidth, glyphheight, this.width, blurradius );
+         blur(bdst, glyphareawidth, glyphareaheight, this.width, blurradius );
        else
-         blurScale(bdst, glyphwidth, glyphheight, this.width, blurradius, blurscale );
+         blurScale(bdst, glyphareawidth, glyphareaheight, this.width, blurradius, blurscale );
      }
 
-   #if FM_TEXTURE_PADDING >= 1
-     dst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
-     for( y = 0 ; y < glyphheight ; y++ )
-     {
-       dst[ y * this.width] = 0;
-       dst[ ( glyphwidth - 1 ) + ( y * this.width ) ] = 0;
-     }
-     for( x = 0 ; x < glyphwidth ; x++ )
+     // User custom font image processing
+     if(font.processImage)
      {
-       dst[ x ] = 0;
-       dst[ x + ( ( glyphheight - 1 ) * this.width ) ] = 0;
+        dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
+        font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth );
      }
-   #endif
 
      dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
      dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
@@ -737,19 +814,27 @@ public:
    virtual void (*onAtlasFull)();
 
    // Create and destroy font manager
-   bool create( int width, int height, FontManagerRenderer renderer)
+   bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
    {
       bool result = false;
 
+     if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
+       return 0;
+     if( ( width <= 0 ) || ( height <= 0 ) )
+       return 0;
+
       this.renderer = renderer;
       incref renderer;
-      renderer.init();
+      renderer.init(channelCount);
 
       // Initialize implementation library
       if(FMFreeTypeFont::init() )
       {
          this.width = width;
          this.height = height;
+         bytesperpixel = channelCount;
+         bytesperline = width * bytesperpixel;
+         this.channelindex = channelIndex;
          if(renderer.createTexture( width, height ))
          {
             if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
@@ -757,20 +842,22 @@ public:
                // Create texture for the cache.
                widthinv = 1.0f / width;
                heightinv = 1.0f / height;
-               texdata = new byte[width * height];
+               texdata = new byte[width * height * bytesperpixel];
                if(texdata)
                {
-                  memset( texdata, 0, width * height );
+                  memset( texdata, 0, height * bytesperline );
 
                   dirtyrect[0] = this.width;
                   dirtyrect[1] = this.height;
                   dirtyrect[2] = 0;
                   dirtyrect[3] = 0;
 
-               // Add white rect at 0,0 for debug drawing.
-               #if FM_DEBUG_WHITERECT
-                 fmAddWhiteRect(2, 2 );
-               #endif
+                  if( bytesperpixel == 1 )
+                    copyGlyphBitmap = copyGlyphBitmap1;
+                  else if( bytesperpixel == 2 )
+                    copyGlyphBitmap = copyGlyphBitmap2;
+                  else
+                    copyGlyphBitmap = copyGlyphBitmap4;
 
                  pushState();
                  clearState();
@@ -851,6 +938,11 @@ public:
      states[ nstates - 1 ].blurscale = (uint16)scale;
    }
 
+   // Set image manipuation callback
+   void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
+   {
+
+   }
 
    // State handling
    void pushState( )
@@ -892,7 +984,7 @@ public:
    }
 
    // Add font from file
-   FMFont addFont(const String path )
+   FMFont addFont(const String path, int glyphPaddingWidth )
    {
       FMFont font = null;
       File f = FileOpen(path, read);
@@ -904,7 +996,7 @@ public:
          if(data)
          {
             f.Read(data, 1, dataSize);
-            font = addFontData(data, dataSize );
+            font = addFontData(data, dataSize, glyphPaddingWidth);
             if(!font)
                delete data;
          }
@@ -914,12 +1006,13 @@ public:
    }
 
    // Add font from data ; do not free( data ), the font manager will do that when removing the font
-   FMFont addFontData( byte *data, int dataSize )
+   FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
    {
      FMFont font
      {
         glyphs = new FMGlyph[FM_INIT_GLYPHS];
         glyphalloc = FM_INIT_GLYPHS;
+        glyphPaddingWidth = glyphPaddingWidth;
      };
      if(font)
      {
@@ -1436,7 +1529,7 @@ public:
       }
    }
 
-   void fmGetFontLimits( int *retlimitminy, int *retlimitmaxy )
+   void getFontLimits( int *retlimitminy, int *retlimitmaxy )
    {
      FMFont font;
      FMState *state;
@@ -1525,18 +1618,18 @@ public:
          int i;
 
          // Copy old texture data over.
-         if( !( data = new byte[width * height] ) )
+         if( !( data = new byte[width * bytesperline] ) )
             return false;
          for( i = 0 ; i < this.height ; i++ )
          {
-            byte * dst = &data[ i * width ];
-            byte * src = &this.texdata[ i * this.width ];
-            memcpy( dst, src, this.width );
+            byte * dst = &data[ (i * width) * bytesperpixel ];
+            byte * src = &this.texdata[ i * this.bytesperline ];
+            memcpy( dst, src, bytesperline);
             if( width > this.width )
-               memset( dst+this.width, 0, width - this.width );
+               memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
          }
          if( height > this.height )
-            memset( &data[ this.height * width ], 0, ( height - this.height ) * width );
+            memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
 
          delete this.texdata;
          texdata = data;
@@ -1552,6 +1645,7 @@ public:
 
          this.width = width;
          this.height = height;
+         this.bytesperline = this.width * bytesperpixel;
          widthinv = 1.0f / this.width;
          heightinv = 1.0f / this.height;
 
@@ -1573,7 +1667,7 @@ public:
          atlas.reset( width, height );
 
          // Clear texture data.
-         texdata = renew texdata byte[width * height];
+         texdata = renew texdata byte[width * height * bytesperpixel];
          if(!texdata) return 0;
          memset( this.texdata, 0, width * height );
 
@@ -1595,14 +1689,10 @@ public:
 
          this.width = width;
          this.height = height;
+         this.bytesperline = width * bytesperpixel;
          this.widthinv = 1.0f / this.width;
          this.heightinv = 1.0f / this.height;
 
-         // Add white rect at 0,0 for debug drawing.
-      #if FM_DEBUG_WHITERECT
-         fmAddWhiteRect(2, 2 );
-      #endif
-
          return true;
       }
       return false;
index 72b4f96..f09e0e5 100644 (file)
@@ -34,6 +34,7 @@ public class FontRenderer : FontManagerRenderer
    DrawManager dm;
    Texture texture;
    int textureWidth, textureHeight;
+   int channelcount;
 
    int imagecount;
    int imageAlloc;
@@ -54,6 +55,12 @@ public:
 
    property DrawManager drawManager { set { dm = value; } }
 
+   bool init(int channelCount)
+   {
+      this.channelcount = channelCount;
+      return true;
+   }
+
    ~FontRenderer()
    {
       delete texture;
@@ -64,7 +71,7 @@ public:
    {
       IMGImage image
       {
-         format = { width = width, height = height, type = grayScale, bytesPerPixel = 1, bytesPerLine = width };
+         format = { width = width, height = height, type = grayScale, bytesPerPixel = channelcount, bytesPerLine = width };
       };
 
       delete texture;
@@ -95,9 +102,19 @@ public:
    {
      if(texture)
      {
+        int glformat;
         int w = rect[2] - rect[0];
         int h = rect[3] - rect[1];
 
+        if( channelcount == 1 )
+          glformat = GL_RED;
+        else if( channelcount == 2 )
+          glformat = GL_RG;
+        else if( channelcount == 3 )
+          glformat = GL_RGB;
+        else if( channelcount == 4 )
+          glformat = GL_RGBA;
+
         // FIXME: no glPushAttrib() in core profile
 //#ifndef SHADERS
         glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT );
@@ -108,7 +125,7 @@ public:
         glPixelStorei( GL_UNPACK_ROW_LENGTH, textureWidth );
         glPixelStorei( GL_UNPACK_SKIP_PIXELS, rect[0] );
         glPixelStorei( GL_UNPACK_SKIP_ROWS, rect[1] );
-        glTexSubImage2D( GL_TEXTURE_2D, 0, rect[0], rect[1], w, h, GL_RED, GL_UNSIGNED_BYTE, data );
+        glTexSubImage2D( GL_TEXTURE_2D, 0, rect[0], rect[1], w, h, glformat, GL_UNSIGNED_BYTE, data );
 //#ifndef SHADERS
         glPopAttrib();
         glPopClientAttrib();
@@ -118,8 +135,8 @@ public:
         IMGImage image;
         image.format.width = textureWidth;
         image.format.height = textureHeight;
-        image.format.type = IMG_FORMAT_TYPE_GRAYSCALE;
-        image.format.bytesPerPixel = 1;
+        image.format.type = channelcount == 4 ? IMG_FORMAT_TYPE_RGBA32 : IMG_FORMAT_TYPE_GRAYSCALE;
+        image.format.bytesPerPixel = channelcount;
         image.format.bytesPerLine = image.format.width * image.format.bytesPerPixel;
         image.data = data;
         imgWritePngFile( "zzz2.png", &image, 1.0 );
@@ -146,6 +163,8 @@ public:
 
       image = &imageList[ imageindex ];
    #if 1
+      image->defineImage( texture, offsetx, offsety, width, height, 1, DM_PROGRAM_ALPHABLEND_INTENSITY, stateLayer );
+   #elif 1
       image->defineImage( texture, offsetx, offsety, width, height, 1, DM_PROGRAM_ALPHABLEND, stateLayer );
    #else
       image->defineImage( texture, offsetx, offsety, width, height, 1, DM_PROGRAM_NORMAL, stateLayer );