Handmade Hero»Forums»Code
107 posts
stbtt_GetPackedQuad doesn't modify the yOffset??
Edited by C_Worm on
Hey!

Im using the stb_truetype.h for rendering text, and more specifically this API

1
2
3
4
5
6
7
//   Improved 3D API (more shippable):
//           #include "stb_rect_pack.h"           -- optional, but you really want it
//           stbtt_PackBegin()
//           stbtt_PackSetOversampling()          -- for improved quality on small fonts
//           stbtt_PackFontRanges()               -- pack and renders
//           stbtt_PackEnd()
//           stbtt_GetPackedQuad()



However, if i render a sentence like this
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
        char line[50] = {};
        strcpy(line, "get Outt'a heregj!");
        
        for(i32 i = 0; i < strlen(line); i++)
        {
            
            adv = xp / (float)win32.win_w;
            stbtt_GetPackedQuad(packed_char, 1024, 1024, line[i] - ' ', &xp, &yp, &glyph_quad, 0);
            
            
            t_pos_x = glyph_quad.s0;
            t_pos_y = 1.0f - glyph_quad.t1;
            t_sz_x = glyph_quad.s1 - glyph_quad.s0;
            t_sz_y = (1.0f - glyph_quad.t0) - (1.0f - glyph_quad.t1);
            
            c_w = (glyph_quad.x1 - glyph_quad.x0) / (float)win32.win_w;
            c_h = (glyph_quad.y1 - glyph_quad.y0) / (float)win32.win_h;
            
            CreateQuad(vBuff, 5 + i, v2f(-0.5f + adv, 0.3f), v2f(c_w, c_h), v4f(1.0f, 1.0f, 1.0f, 1.0f), v2f(t_pos_x, t_pos_y), v2f(t_sz_x, t_sz_y));
            
        } 
       




the xp advances the cursor (to the right) to where the next character in the sentence should be, however the yp doesn't get modified even if the character beeing sent to stbtt_GetPackedQuad() is a 'g' or 'j', so the 'g' or 'j' doesn't get rendered with respect to the baseline.

I went through the stbtt_GetPackedQuad() defintion, and at the end only the *xpos get's modified and not the *ypos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
{
   float ipw = 1.0f / pw, iph = 1.0f / ph;
   const stbtt_packedchar *b = chardata + char_index;

   if (align_to_integer) {
      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
      q->x0 = x;
      q->y0 = y;
      q->x1 = x + b->xoff2 - b->xoff;
      q->y1 = y + b->yoff2 - b->yoff;
   } else {
      q->x0 = *xpos + b->xoff;
      q->y0 = *ypos + b->yoff;
      q->x1 = *xpos + b->xoff2;
      q->y1 = *ypos + b->yoff2;
   }

   q->s0 = b->x0 * ipw;
   q->t0 = b->y0 * iph;
   q->s1 = b->x1 * ipw;
   q->t1 = b->y1 * iph;

   *xpos += b->xadvance;
}


Any thoughts on this?


Simon Anciaux
1337 posts
stbtt_GetPackedQuad doesn't modify the yOffset??
Edited by Simon Anciaux on
I believe, xpos and ypos are used as state for the function to track were it is in the rendering of the sentence and should not be use for rendering. ypos will never change and it's probably passed as a pointer only to be similar in usage to xpos.

The coordinates of the quad are the values from the stbtt_aligned_quad argument, x0, y0, x1, y1.
107 posts
stbtt_GetPackedQuad doesn't modify the yOffset??
I tried using the x0, y0, x1, y1 from stbtt_aligned_quad returned from stbtt_GetPackedQuad()

However since i invert the buffer to not get it upside down, the stbtt_GetPackedQuad() returns aligned_quads relative to
the non-inverted bitmap.

Which in turn makes the positioning of the characters somewhat wrong, since i guess the baseline is now upside down.

I can't find a way around this, since i don't know where to get the baseline from...

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
    bool success = 0;
    const u32 pxlw = 1024;
    const u32 pxlh = 1024;
    const u32 fontf_sz = pxlw*pxlh;
    unsigned char font_file[fontf_sz] = {};
    unsigned char pixels[fontf_sz] = {};
    stbtt_packedchar packed_char[126] = {};
    stbtt_pack_context pack_c = {};
    
    size_t bytes_read = 0;
    bytes_read = fread(font_file, 1, fontf_sz, fopen("C:/windows/fonts/arial.ttf", "rb"));
    
    // init pixel_bitmap and packing context
    success = stbtt_PackBegin(&pack_c, pixels, pxlw, pxlh, 0, 1, 0);
    
    // set detail level of characters in font which requires the pixel_bitmap to be bigger
    stbtt_PackSetOversampling(&pack_c, 2, 2);
    
    // Set what characters in the pixel_map you want STBTT_POINT_SIZE makes the charcter size in pixels
    stbtt_PackFontRange(&pack_c, font_file, 0, 45, ' ', 126,  packed_char);
    
    // Invert font texture becuase stb give's it to us upside down ( y increases downward )
    InvertBuffer(pixels, pxlw,  pxlh);
    
    // End the packing context
    stbtt_PackEnd(&pack_c);
    
    unsigned char myBuffer[512 * 512] = {};
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, pxlw, pxlh, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
    stbi_image_free(image);
    
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    bool wglSwap = wglSwapIntervalEXT(1);
    
    // Initialise game code
    InitGame(vBuff);
    
    while(running)
    {
        
        
        // start timer
        win32.dts = (float)(win32.end_time.QuadPart - win32.start_time.QuadPart) / (float)win32.perf_frequency.QuadPart;
        win32.dts *= 1000; // Miliseconds
        QueryPerformanceCounter(&win32.start_time);
        
        if(KeyPressed(VK_ESCAPE)) running = false;
        
        Win32_MessageHandler(&win32);
        Win32_GetWinSize(&win32);
        
        glViewport(0, 0, win32.win_w, win32.win_h);
        
        char letter = 'A';
        
        if(KeyPressed('D'))  letter++;
        if(KeyPressed('A')) letter--;
        
        
        // Gets a renderable character
        stbtt_aligned_quad glyph_quad = {};
        float xp = 0.0f;
        float yp = 0.024f;
        
        
        
        float t_pos_x = glyph_quad.s0;
        float t_pos_y = 1.0f - glyph_quad.t1;
        float t_sz_x = glyph_quad.s1 - glyph_quad.s0;
        float t_sz_y = (1.0f - glyph_quad.t0) - (1.0f - glyph_quad.t1);
        float adv = xp / (float)win32.win_w;
        
        float c_w = (glyph_quad.x1 - glyph_quad.x0) / (float)win32.win_w;
        float c_h = (glyph_quad.y1 - glyph_quad.y0) / (float)win32.win_h;
        
        char line[50] = {};
        strcpy(line, "get Outt,a hegj?!");
        
        for(i32 i = 0; i < strlen(line); i++)
        {
            
            adv = xp / (float)win32.win_w;
            float advy = yp / (float)win32.win_h;
            stbtt_GetPackedQuad(packed_char, 1024, 1024, line[i] - ' ', &xp, &yp, &glyph_quad, 0);
            
            t_pos_x = glyph_quad.s0;
            t_pos_y = 1.0f - glyph_quad.t1;
            t_sz_x = glyph_quad.s1 - glyph_quad.s0;
            t_sz_y = (1.0f - glyph_quad.t0) - (1.0f - glyph_quad.t1);
            
            // The glyph_quad.x0 & glyph_quad.y0 should be the the advancing positions in x/y 
            float c_x = -1.0f + ((-1.0f + glyph_quad.x0) / (float)win32.win_w);
            float c_y = 0.9f + ((glyph_quad.y0)  / (float)win32.win_h);
            
            c_w = (glyph_quad.x1 - glyph_quad.x0) / (float)win32.win_w;
            c_h = (glyph_quad.y1 - glyph_quad.y0) / (float)win32.win_h;
            
            CreateQuad(vBuff, 5 + i, v2f(c_x, c_y), v2f(c_w, c_h), v4f(0.0f, 1.0f, 1.0f, 1.0f), v2f(t_pos_x, t_pos_y), v2f(t_sz_x, t_sz_y));
            
        } 
        
Simon Anciaux
1337 posts
stbtt_GetPackedQuad doesn't modify the yOffset??
ypos is the position of the baseline. The function uses top left origin for the coordinates, if you pass N in ypos, the top of the character (y0) will be less then N and the bottom of the character (y1) will be more then N. So, assuming you want to use a bottom left origin, you need to find the difference between ypos (the baseline) and the result of GetPackedQuad, inverse it and add it to the baseline.

You don't need to change the x and s values, you only need to change the y and t. Note that I haven't tested the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for ( uintptr_t i = 0; i < string_length; i++ ) {

        int index = ...;
        stbtt_aligned_quad quad = { 0 };
        stbtt_GetPackedQuad( packed_chars, atlas_width, atlas_height, index, &xpos, &ypos, &quad, 0 );
        float x0 = quad.x0;
        float x1 = quad.x1;
        float u0 = quad.s0;
        float u1 = quad.s1;
        
        /* Adjust position */
        float y0 = ypos + ( ypos - quad.y1 ); /* using y1 for y0 */
        float y1 = ypos + ( ypos - quad.y0 ); /* using y0 for y1 */

        /* Adjust texture coordinates */
        float v0 = 1.0f - quad.t1; /* using t1 for v0 */
        float v1 = 1.0f - quad.t0; /* using t0 for v1 */
        
        /* Create your vertex using:
           x0, y0, u0, v0 for the bottom left
           x1, y1, u1, v1 for the top right
        */
    }


Another way to do it is to write your equivalent of GetPackedQuad, as the packed_chars contain the offsets.
1
2
3
4
x0 = xpos + packed_char->xoff;
y0 = ypos - packed_char->yoff2;
x1 = xpos + packed_char->xoff2;
y1 = ypos - packed_char->yoff;