'RGB pixel format in FreeType

I'm having troubles understanding how the pixels are stored under FreeType, the core part of my problem is how to extract the RGB value after rendering the glyph.

typedef uint32_t Tindex;
//
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LCD);
//
FT_Bitmap bitmap = face->glyph->bitmap;
FT_Glyph_Metrics metrics = face->glyph->metrics;
//
Tindex colStartPos = metrics.horiBearingX >> 6;
Tindex rowStartPos = metrics.horiBearingY >> 6;
//
for (Tindex y = 0; y < bitmap.rows; y = y + 3)
{
    Tindex row = rowStartPos + y;
    for (Tindex x = 0; x < bitmap.width; x = x + 3)
    {
        Tindex col = colStartPos + x;
        uint8_t r = bitmap.buffer[y * bitmap.width + x];
        uint8_t g = bitmap.buffer[y * bitmap.width + x + 1];
        uint8_t b = bitmap.buffer[y * bitmap.width + x + 2];
        img(col, row) = pixel{r, g, b, 255};
        // img and pixel are placeholders to simplify the code
    }
}

The official documentation doesn't specify how the pixels are stored, it simply says that the format is RGB, also my compiler says that the type for the values retrieved from bitmap.buffer is float but this can be the result of some type punning technique.

I did notice that when rendering in FT_RENDER_MODE_LCD the number of iterations is being triplicated so the math checks out, but I don't understand what I'm doing wrong and this snippet is just one of the many tries I have done.

Also, note that bitmap.pitch / bitmap.rows is equal to 1 in my case, I would expect it to be 3.

Someone could explain how I'm supposed to read pixel values? It's possible to switch to an RGBA format?

From the docs, it looks like the format is interleaved, but it doesn't look like that


This piece of code is using boost, libpng and FreeType, it reproduces the problem that I'm experiencing and it's self-contained ( do not forget to customize the path to the TTF font )

#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
extern "C" {
#define FT_CONFIG_OPTION_SUBPIXEL_RENDERING
#include <ft2build.h>
#include FT_FREETYPE_H
#include <ftlcdfil.h>
}
#include <cstdint>
#define FONTFILE "/tmp/DroidSans.ttf"
#define IMGSIZE 400
typedef uint32_t Tindex;
int main()
{
    FT_Library library = 0;
    FT_Face face = 0;
    FT_Init_FreeType(&library);
    FT_Library_SetLcdFilter(library, FT_LCD_FILTER_LIGHT);
    FT_New_Face(library, FONTFILE, 0, &face);
    FT_Set_Char_Size(face, 0, 24 * 64, 300, 300);
    FT_ULong charcode = 0x003f;
    FT_UInt glyph_index = 0;
    glyph_index = FT_Get_Char_Index(face, charcode);
    FT_Load_Glyph(face, glyph_index, FT_LOAD_TARGET_NORMAL);
    FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LCD);
    FT_Bitmap bitmap = face->glyph->bitmap;
    FT_Glyph_Metrics metrics = face->glyph->metrics;
    Tindex colStartPos = metrics.horiBearingX / 64;
    Tindex rowStartPos = metrics.horiBearingY / 64;
    boost::gil::rgba8_image_t img{IMGSIZE, IMGSIZE};
    boost::gil::rgba8_image_t::view_t imgView{boost::gil::view(img)};
    for (Tindex y = 0; y < bitmap.rows; y++)
    {
        Tindex row = rowStartPos + y;
        for (Tindex x = 0; x < bitmap.width; x++)
        {
            Tindex col = colStartPos + x;
            uint8_t r = bitmap.buffer[y * bitmap.pitch + x * 3];
            uint8_t g = bitmap.buffer[y * bitmap.pitch + x * 3 + 1];
            uint8_t b = bitmap.buffer[y * bitmap.pitch + x * 3 + 2];
            imgView(col, row) = boost::gil::rgba8_pixel_t{r, g, b, 255};
        }
    }
    boost::gil::png_write_view("img.png", imgView);
}

I compile this with

g++ -std=c++11 $(freetype-config --cflags) main.cpp -lpng $(freetype-config --libs)


Solution 1:[1]

AFAIU from the docs (http://www.freetype.org/freetype2/docs/reference/ft2-basic_types.html#FT_Bitmap) your loop should be something like that:

for (Tindex y = 0; y < bitmap.rows; y++)
{
    Tindex row = rowStartPos + y;
    for (Tindex x = 0; x < bitmap.width; x++)
    {
        Tindex col = colStartPos + x;
        uint8_t r = bitmap.buffer[y * bitmap.pitch + x*3];
        uint8_t g = bitmap.buffer[y * bitmap.pitch + x*3 + 1];
        uint8_t b = bitmap.buffer[y * bitmap.pitch + x*3 + 2];
        img(col, row) = pixel{r, g, b, 255};
        // img and pixel are placeholders to simplify the code
    }
}

Solution 2:[2]

The documentation says that with the default FT_PIXEL_MODE_GRAY each pixel is stored in 1 byte representing a shade of gray. You can convert it manually to RGBA or perhaps you could use some Freetype features, like FT_PIXEL_MODE_LCD or FT_Bitmap_Convert(). I haven't tried this myself though.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ashalynd
Solution 2 Anton Jebrak