   Freetype 2 skewed characters -- what am I missing?
I've written a function to take a TTF and write it out into a texture atlas. However, the output is a bit weird as shown above. In every case, the mangled characters are characters which have a width that is not a multiple of 4. slot->bitmap->width % 4 != 0. Something tells me this has something to do with 32-bit boundaries or some such, but heck if I can make sense of it.Here is the code I am using, it's based pretty much on the tutorial at the freetype website. I'm using opengl for easy subimage mapping, but this occurs even if I just pick one of the mangled characters and dump the buffer directly to a TGA or some other bitmap format.FT_Library library;FT_Face face;FT_Init_FreeType( &library );FT_New_Face( library, "res/fonts/alphastreet.ttf", 0, &face );char const * str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!?0123456789";size_t image_width = 1024;size_t image_height = 1024;size_t tile_width = image_width / 16;size_t tile_height = image_height / 16;FT_Set_Pixel_Size( face, tile_width, tile_height );FT_GlyphSlot slot = face->glyph;GLubyte * img = new GLubyte[image_width * image_height * 4];memset( img, 0, sizeof(GLubyte) * image_width * image_height * 4 );// create grid lines (temp)for( size_t y = 0; y < 16; y++ )    memset( img + y * image_width * tile_width * 4, 255, image_width * 4 );for( size_t x = 0; x < 16; x++ )    for( size_t y = 0; y < image_width; y++ )   memset( img + y * image_width * 4 + x * tile_width * 4, 255, 4 );GLuint texId;glGenTextures( 1, &texId );glBindTexture( GL_TEXTURE_2D, texId );glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img );delete[] img;int i = 0;while( str != 0 ){    // Unicode A = 0x0041.. good enough for now    FT_Load_Char( face, 0x0041 + (str - 'A'), FT_LOAD_RENDER );    // For convenience    FT_Bitmap * b = &slot->bitmap;    // 16x16 cell position for character    int x = i % 16;    int y = 16 - (i - x) / 16 - 1;    // centre the glyph.. for now  GLint x_off = (tile_width - b->width) / 2;    GLint y_off = (tile_height - b->rows) / 2;    // Flip the character vertically    GLubyte * charimg = new GLubyte[b->rows * b->width];    for( int cy = 0; cy < b->rows; cy++ )   memcpy( charimg + cy * b->width, b->buffer + (b->rows - cy - 1) * b->pitch, b->width );    // Copy the character into the grid    glTexSubImage2D( GL_TEXTURE_2D, 0, x * tile_width + x_off, y * tile_height + y_off, b->width, b->rows, GL_LUMINANCE, GL_UNSIGNED_BYTE, charimg );    delete[] charimg;    i++;}glBindTexture( GL_TEXTURE_2D, 0 );FT_Done_Face( face );FT_Done_FreeType( library );The further it is off from a multiple of 4, the more skewed the character is.. i.e. a character with a width of, say, 19, is skewed by 1 pixel per row. 18 it is skewed by 2 pixels per row. My understanding is that b->pitch should be taking care of this, but in every case b->pitch is = b->width. I've tried forcing pitch to the next multiple of 4 up and while that kind-of corrects it and causes the character to not be skewed, it's often offset by some amount and there is garbage at the top and bottom of the bitmap. Correcting for this offset and I still see garbage in the bitmap.I've tested this on Windows with a source-compiled version of FreeType 2.3.12 as well as on linux with whatever version of libfreetype is included with Ubuntu 10.4. I've also tried various fonts -- Alpha Street (from dafont.com) as int he example, Arial, Times New Roman, Verdana, Tahoma, Courier New.. in all cases, exactly the same results.   
  I think your problem is that you are trying to store characters with a different width than tile_width into a tile. When you call FT_Set_Pixel_Size(), you are only setting the nominal pixel size of the character. Glyphs can be larger or smaller than the requested pixel size, and often are.Try adding the following asserts and see if they trip:    // For convenience    FT_Bitmap * b = &slot->bitmap;    assert(b->width <= tile_width );    assert(b->rows <= tile_height);;  Yeah, some code I excluded was a text dump of all the slot->bitmap parameters (width, rows, pitch, left, top, etc.). In no case is bitmap->width or bitmap->rows > tile_width with this font. There might be a case or two of bitmap->width or bitmap->rows == tile_width. Also the code is set up for variable image sizes.. above it's using 1024x1024. For the image I posted, I used 512x512. With different resolutions, then different characters become skewed.. but always following the same pattern:* In every case where bitmap->width % 4 == 0, the character renders perfectly and beautifully.* In every case where bitmap->width % 4 != 0, the character is skewed by exactly (4 - bitmap->width % 4) pixels per row.* bitmap->pitch is always = bitmap->width.Take a representative sample from the 512x512 version, the QRS above:tile_width = 32tile_height = 32Char: QSize: 24x24Offset: -1,24Pitch: 24Char: RSize: 22x25Offset: -1,24Pitch: 22Char: SSize: 23x24Offset: 0,24Pitch: 23Edit: To be clear, above Size: prints "bitmap->width x bitmap->rows" and Offset: is "slot->bitmap_left , slot->bitmap_top".Q bitmap->pitch % 4 = 0R bitmap->pitch % 4 = 2S bitmap->pitch % 4 = 3Tiles that are smaller than tile_width or tile_height are not a problem. I'm centering them in the cell and mapping them onto the texture. Even in cases where they are larger all that would causes is some bleed over into other cells and possibly a potential image buffer overflow which GL should handle anyway (i.e. I'd pass it a negative offset to TexSubImage2D, or the image would exceed the bounds of the image -- GL should crop in those cases).[Edited by - JimFromTexas on May 20, 2010 5:53:40 PM];  First thing to note, even though width and pitch are equal all times in this particular set up, it is wise to follow their function also for later on. Pitch is the width in bytes a row is (pitch * row is glyph dimension in memory), and width is the actual width in bytes of glyph data. So the memcpy must use in the source offset 'charimg + cy * b->pitch' and in the length parameter exactly what you have. Also the allocation should use pitch.Are you sure pitch and width are equal when (width % 4) != 0?Try printing out values for width and pitch and related variables. It looks like the rows are not wide enough for some characters. See what the difference is in values, what it should be and thus what variable to use where...Sorry bit vague, but that's how I solved it.EDIT: after your edit, I'm pretty sure this should fix it. Use pitch for the offsets and allocation (new GLubyte[b->rows * b->width]  becomes  new GLubyte[b->rows * b->pitch])) and use width ONLY with the size parameter for memcpy.EDIT2: See OP reply below; yes you are correct, I was mistaken.   ;Quote:Original post by DecriusFirst thing to note, even though width and pitch are equal all times in this particular set up, it is wise to follow their function also for later on. Pitch is the width in bytes a row is (pitch * row is glyph dimension in memory), and width is the actual width in bytes of glyph data. So the memcpy must use in the source offset 'charimg + cy * b->pitch' and in the length parameter exactly what you have. Also the allocation should use pitch.Thank you for the response. I disagree with this in that I am creating my own tile image which is width x rows in dimension. When I copy the rows, I advance bitmap->buffer by pitch while advancing charimg by width, then copying width bytes.. which I believe to be the correct way to do it even if pitch were different from width. That gives me a bitmap which is set up for OpenGL to map onto my main bitmap, with no stride between rows. I will give it a try though and see how it goes.Edit: Modifying the code to use pitch instead of width for the charimg initialization and subsequent memcpy (except for the size) makes no difference. :-/With regards your other point, see my previous response as I have added some text output of the variable dumps.[Edited by - JimFromTexas on May 20, 2010 5:17:18 PM];  Looks to me that R is twice as much skewed as S. And A and C are tree times as much skewed as S.Can you confirm that (pitch % 4) == 1 for A and C?If so, it looks like you need to pad the pitch by (4 -  (pitch % 4)) bytes, in other words make the pitch a multiple of 4...which I find weird since I don't do that at all.I think perhaps FT_Set_Pixel_Size() is the culprit, which calls http://freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Request_Size.   ;Quote:Original post by DecriusCan you confirm that (pitch % 4) == 1 for A and C?I can.A: width = 21, pitch = 21C: width = 21, pitch = 21Quote:Original post by DecriusIf so, it looks like you need to pad the pitch by (4 -  (pitch % 4)) bytes, in other words make the pitch a multiple of 4...which I find weird since I don't do that at all.Heh, yeah it is weird. I tried that and while it kind of un-skews the glyph there are offset and garbage problems in the bitmap as well.Quote:Original post by DecriusI think perhaps FT_Set_Pixel_Size() is the culprit, which calls http://freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Request_Size.I'll look at this closer. I could try to set it up using the more advanced rendering methods though if this is a bug in FT_Load_Char or FT_Set_Pixel_Size perhaps it is worth exploring further for filing a bug report.Here are the results if I add:if( b->pitch % 4 != 0 )    b->pitch += 4 - (b->pitch % 4);;  It looks like the offset to where is written is wrong, but it could also be the offset from where is read is wrong.Making width a multiple of 4 would skew it again, so perhaps try to make width multiple of 4 and revert pitch back to what it was?Sorry I can't give much less vague tips, it's hard without a shit load of sample data and a bunch of variables to tweak instantly (famous output-debugging)...   ;  You're on to something. Padding width got it very close. After some tweaking, I got this:if( b->width % 4 != 0 )    b->width += 4 - (b->width % 4);// Invert glyph verticallyGLubyte * charimg = new GLubyte[b->rows * b->width];memset( charimg, 0, b->rows * b->width );for( int cy = 0; cy < b->rows; cy++ )    memcpy( charimg + cy * b->width, b->buffer + (b->rows - cy - 1) * b->pitch, b->pitch );And this gives me what I'm looking for. Thanks! The task now is to understand why.. looking at it closer, I'm stumped. O.o It shouldn't be necessary to pad width at all, it's only affecting how large I make my temporary image. Is this actually a GL thing?   ;  Okay the final answer is that it's a GL thing.The solution would have been these two lines:glPixelStorei( GL_PACK_ALIGNMENT, 1 );glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );Those values default to 4.Hopefully this can help someone out there. :)[Edited by - JimFromTexas on May 20, 2010 8:43:30 PM]