Font Texture Alignment Issue

Started by
0 comments, last by Sik_the_hedgehog 14 years, 12 months ago
I've been working on font rendering in OpenGL and have run into a big of a snag. When copying the bitmap generated by the FreeType library into an OpenGL texture the bitmap is not rendered correctly. After many hours of debugging/traces I believe it is related to the format of the texture and possibly that I am not providing the data in the correct alignment; but I am unsure (I have not worked with OpenGL for a LONG time). I'm hoping a few more eyes here can help me pick up on my mistake. Thanks. Here's the example code I was working with:
#include <SDL.h>
#include <windows.h>
#include <GL\GL.h>
#include "ft2build.h"
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
#include FT_FREETYPE_H
#include <stdio.h>

typedef struct {
  int width;
  int height;
  int xoffset;
  int yoffset;
  float texels[4];
} Glyph;

int SDL_main(int argc, char *argA[]) {
  SDL_Event event = {0};
  int running = 1;
  GLuint texture = 0;
  GLubyte *empty_data = 0;
  int x = 50;
  int y = 50;
  int i, j;
  int ok = 0;
  const GLsizei TEXTURE_SIZE = 128;

  FT_Library lib;
  FT_Face face;
  FT_Bitmap *bitmap;
  FT_Glyph glyph;
  FT_BitmapGlyph bitmap_glyph;
  GLubyte* padded_bitmap;
  int bw = 0;
  int bh = 0;
  int texNext = 0;
  FT_UInt b_glyph = 0;
  FT_UInt A_glyph = 0;
  Glyph b, A;
  FT_Vector kerning = {0};
  FILE *test;

  ok = !FT_Init_FreeType(&lib);
  if (ok) {
    FT_New_Face(lib, "Verdana.ttf", 0, &face);
    FT_Set_Char_Size(face, 16*64, 16*64, 96, 96);
  }

  SDL_Init(SDL_INIT_VIDEO);
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
  SDL_SetVideoMode(800, 600, 24, SDL_OPENGL);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glEnable(GL_TEXTURE_2D);
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

  empty_data = calloc(2 * TEXTURE_SIZE * TEXTURE_SIZE, sizeof(GLubyte));
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, empty_data);
  free(empty_data);

  test = fopen("out.txt", "w");

  b_glyph = FT_Get_Char_Index(face, 'b');
  FT_Load_Char(face, 'b', FT_LOAD_DEFAULT);
  FT_Get_Glyph(face->glyph, &glyph);
  if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) {
    FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
  }
  bitmap_glyph = (FT_BitmapGlyph)glyph;
  bitmap = &bitmap_glyph->bitmap;
  bw = bitmap->width;
  bh = bitmap->rows;

  /* pad to add alpha */
  padded_bitmap = calloc(2 * bw * bh, sizeof(GLubyte));
  memset(padded_bitmap, 0, 2*bw*bh*sizeof(GLubyte));
  for(j = 0; j < bh; j++) {
    for(i = 0; i < bw; i++) {
      padded_bitmap[2 * (i + j * bw)] = 255;
      padded_bitmap[2 * (i + j * bw) + 1] = bitmap->buffer[(i + j * bw)];
      putc(bitmap->buffer[(i + j * bw)] > 128 ? ' ' : '.', test);
    }
    putc('\n', test);
  }
  putc('\n', test);

  b.width = bw; b.height = bh;
  b.texels[0] = 0.0;
  b.texels[1] = 0.0;
  b.xoffset = face->glyph->bitmap_left;
  b.yoffset = face->glyph->bitmap_top;
  b.texels[2] = b.texels[0] + b.width / (float)TEXTURE_SIZE;
  b.texels[3] = b.texels[1] + b.height / (float)TEXTURE_SIZE;

  texNext += bw;

  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->width, bitmap->rows, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, padded_bitmap);

  free(padded_bitmap);

  A_glyph = FT_Get_Char_Index(face, 'A');

  FT_Load_Char(face, 'A', FT_LOAD_DEFAULT);
  FT_Get_Glyph(face->glyph, &glyph);
  if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) {
    FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
  }
  bitmap_glyph = (FT_BitmapGlyph)glyph;
  bitmap = &bitmap_glyph->bitmap;
  bw = bitmap->width;
  bh = bitmap->rows;
  padded_bitmap = calloc(2 * bw * bh, sizeof(GLubyte));

  for(j = 0; j < bh; j++) {
    for(i = 0; i < bw; i++) {
      padded_bitmap[2 * (i + j * bw)] = 255;
      padded_bitmap[2 * (i + j * bw) + 1] = bitmap->buffer;
      putc(bitmap-&gt;buffer[(i + j * bw)] &gt; <span class="cpp-number">128</span> ? ' ' : '.', test);
    }
    putc('\n', test);
  }
  putc('\n', test);

  <span class="cpp-keyword">for</span>(j = <span class="cpp-number">0</span>; j &lt; bh; j++) {
    <span class="cpp-keyword">for</span>(i = <span class="cpp-number">0</span>; i &lt; bw; i++) {
      padded_bitmap[<span class="cpp-number">2</span> * (i + j * bw)] = <span class="cpp-number">255</span>;
      padded_bitmap[<span class="cpp-number">2</span> * (i + j * bw) + <span class="cpp-number">1</span>] = bitmap-&gt;buffer;
      putc(padded_bitmap[<span class="cpp-number">2</span> * (i + j * bw) + <span class="cpp-number">1</span>] &gt; <span class="cpp-number">128</span> ? ' ' : '.', test);
    }
    putc('\n', test);
  }
  fclose(test);

  A.width = bw; A.height = bh;
  A.xoffset = face-&gt;glyph-&gt;bitmap_left;
  A.yoffset = face-&gt;glyph-&gt;bitmap_top;
  A.texels[<span class="cpp-number">0</span>] = (<span class="cpp-keyword">float</span>)texNext / (<span class="cpp-keyword">float</span>)TEXTURE_SIZE;
  A.texels[<span class="cpp-number">1</span>] = (<span class="cpp-keyword">float</span>)<span class="cpp-number">0</span> / (<span class="cpp-keyword">float</span>)TEXTURE_SIZE;
  A.texels[<span class="cpp-number">2</span>] = A.texels[<span class="cpp-number">0</span>] + A.width / (<span class="cpp-keyword">float</span>)TEXTURE_SIZE;
  A.texels[<span class="cpp-number">3</span>] = A.texels[<span class="cpp-number">1</span>] + A.height / (<span class="cpp-keyword">float</span>)TEXTURE_SIZE;

  glTexSubImage2D(GL_TEXTURE_2D, <span class="cpp-number">0</span>, texNext, <span class="cpp-number">0</span>, bitmap-&gt;width, bitmap-&gt;rows, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, padded_bitmap);
  free(padded_bitmap);

  glMatrixMode(GL_PROJECTION);
  glOrtho(<span class="cpp-number">0</span>, <span class="cpp-number">800</span>, <span class="cpp-number">600</span>, <span class="cpp-number">0</span>, -<span class="cpp-number">1</span>, <span class="cpp-number">1</span>);
  glMatrixMode(GL_MODELVIEW);
  glTranslated(<span class="cpp-number">0</span>.<span class="cpp-number">00735</span>, <span class="cpp-number">0</span>.<span class="cpp-number">00735</span>, <span class="cpp-number">0</span>.<span class="cpp-number">0</span>);

  <span class="cpp-keyword">while</span> (running) {
    glClearColor(<span class="cpp-number">0</span>, <span class="cpp-number">0</span>, <span class="cpp-number">1</span>, <span class="cpp-number">0</span>);
    glClear(GL_COLOR_BUFFER_BIT);

    SDL_WaitEvent(&amp;event);
    <span class="cpp-keyword">switch</span> (event.type) {
      <span class="cpp-keyword">case</span> SDL_KEYDOWN:
        <span class="cpp-keyword">if</span> (event.key.keysym.sym == SDLK_ESCAPE) {
          running = <span class="cpp-number">0</span>;
          <span class="cpp-keyword">break</span>;
        }
      <span class="cpp-keyword">case</span> SDL_QUIT:
        running = <span class="cpp-number">0</span>;
        <span class="cpp-keyword">break</span>;
      <span class="cpp-keyword">default</span>:
        ;
    }
    FT_Get_Kerning(face, A_glyph, b_glyph, FT_KERNING_DEFAULT, &amp;kerning);

    glBegin(GL_QUADS);

    <span class="cpp-comment">/*b*/</span>
    glTexCoord2d(b.texels[<span class="cpp-number">0</span>], b.texels[<span class="cpp-number">1</span>]); glVertex2i(x, y-b.yoffset);
    glTexCoord2d(b.texels[<span class="cpp-number">0</span>], b.texels[<span class="cpp-number">3</span>]); glVertex2i(x, y+b.height-b.yoffset);
    glTexCoord2d(b.texels[<span class="cpp-number">2</span>], b.texels[<span class="cpp-number">3</span>]); glVertex2i(x+b.width, y+b.height-b.yoffset);
    glTexCoord2d(b.texels[<span class="cpp-number">2</span>], b.texels[<span class="cpp-number">1</span>]); glVertex2i(x+b.width, y-b.yoffset);

      <span class="cpp-comment">/*A*/</span>
    glTexCoord2d(A.texels[<span class="cpp-number">0</span>], A.texels[<span class="cpp-number">1</span>]); glVertex2i(texNext+(kerning.x&gt;&gt;<span class="cpp-number">6</span>)+x, y-A.yoffset);
    glTexCoord2d(A.texels[<span class="cpp-number">0</span>], A.texels[<span class="cpp-number">3</span>]); glVertex2i(texNext+(kerning.x&gt;&gt;<span class="cpp-number">6</span>)+x, y+A.height-A.yoffset);
    glTexCoord2d(A.texels[<span class="cpp-number">2</span>], A.texels[<span class="cpp-number">3</span>]); glVertex2i(texNext+(kerning.x&gt;&gt;<span class="cpp-number">6</span>)+x+A.width, y+A.height-A.yoffset);
    glTexCoord2d(A.texels[<span class="cpp-number">2</span>], A.texels[<span class="cpp-number">1</span>]); glVertex2i(texNext+(kerning.x&gt;&gt;<span class="cpp-number">6</span>)+x+A.width, y-A.yoffset);
    glEnd();
    SDL_GL_SwapBuffers();
  }

  glDeleteTextures(<span class="cpp-number">1</span>, &amp;texture);

  <span class="cpp-keyword">if</span> (ok) {
    FT_Done_Face(face);
    FT_Done_FreeType(lib);
  }

  SDL_Quit();
  <span class="cpp-keyword">return</span> <span class="cpp-number">0</span>;
}


</pre></div><!–ENDSCRIPT–>

And here's an ASCII rep of the output. Notice that the Freetype bitmaps are rendering correctly (also I have no idea why 'b' is so messed up):
<pre>
Freetype's returned bitmaps:
  ………
  ………
  ………
  ………
  ..    …
         ..
   ….  ..
  ……  .
  ……  .
  ……  .
  ……  .
  ……  .
  ……  .
  …..  ..
        …
       ….

……   ……
…..    ……
…..     …..
…..  .  …..
….  ..  …..
….  ..   ….
….  …  ….
…  ….  ….
…  ….   …
…  …..  …
..          …
..           ..
..  …….  ..
.  ……..  ..
.  ………  .
   ………  .

What ends up &#111;n the screen:
  ………  …
……  ……..
.  ………  ..
    …         
..   ….  ..  .
…..  .  ……
  .  ……  .  
……  .  …..
.  .  ……  . 
 …..  ..      
  …       ….
.  . … … …
 ……………
  …… . …..
…….. . . . .
…. …….. ..

……   …….
….    ……..
…     ……..
..  .  ………
  ..  ……… 
 ..   ……..  
…  …….  ..
..  …….  …
.   ……  ….
.  …..        
  …..         
  ….  ……. 
 …  ……..  
…  ………  
.   ………  .
…………    
</pre>
Advertisement
That looks like the stride has the wrong value. And that only judging from the output, not even paid attention at the code =P Check if you're setting the proper stride value for the texture data.
Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

This topic is closed to new replies.

Advertisement