Skip to content

Instantly share code, notes, and snippets.

@RJ-Infinity
Created April 25, 2025 22:23
Show Gist options
  • Select an option

  • Save RJ-Infinity/19a17dd915e60e90419e9aba07de9c3d to your computer and use it in GitHub Desktop.

Select an option

Save RJ-Infinity/19a17dd915e60e90419e9aba07de9c3d to your computer and use it in GitHub Desktop.
based of the example in the docs (https://freetype.org/freetype2/docs/tutorial/step1.html)
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <Windows.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
void show_image(unsigned char* image, int width, int height) {
unsigned char* img = calloc(width*height*4, 1);
// memset(img, 0xff, width*height*4);
int i, j;
for ( i = 0; i < height; i++ )
{
for ( j = 0; j < width; j++ ){
img[width*i*4 + j*4 + 0] = 0;//image[i * width + j];
img[width*i*4 + j*4 + 1] = 0;//image[i * width + j];
img[width*i*4 + j*4 + 2] = 0;//image[i * width + j];
img[width*i*4 + j*4 + 3] = image[i * width + j];
}
}
stbi_write_png("test.png", width, height, 4, img, width*4);
free(img);
}
// FT_Error error;
// unsigned char* text;
// double angle;
// int n, num_chars;
typedef struct{
int width;
int height;
int baseline;
} TextSize;
TextSize render_text(FT_Face face, unsigned char* text, unsigned char* image, TextSize size){
FT_GlyphSlot slot = face->glyph;
/* set up matrix */
FT_Matrix matrix;
matrix.xx = 0x10000L;
matrix.xy = 0;
matrix.yx = 0;
matrix.yy = 0x10000L;
FT_Vector pen = {0};
if (image){
pen.x = 0;
pen.y = size.baseline;
}
int min_pos = 0;
int max_pos = 0;
for (int n = 0; n < strlen(text); n++) {
/* set transformation */
FT_Set_Transform( face, &matrix, &pen );
FT_ULong utf8char;
if (text[n] >> 3 == 0b11110){
FT_ULong b1 = text[n];
n++;
FT_ULong b2 = text[n];
n++;
FT_ULong b3 = text[n];
n++;
FT_ULong b4 = text[n];
utf8char = (b1&0b111)<<18 | (b2&0b111111)<<12 | (b3&0b111111)<<6 | (b4&0b111111);
}else if (text[n] >> 4 == 0b1110){
FT_ULong b1 = text[n];
n++;
FT_ULong b2 = text[n];
n++;
FT_ULong b3 = text[n];
utf8char = (b1&0b1111)<<12 | (b2&0b111111)<<6 | (b3&0b111111);
}else if (text[n] >> 5 == 0b110){
FT_ULong b1 = text[n];
n++;
FT_ULong b2 = text[n];
utf8char = (b1&0b11111)<<6 | (b2&0b111111);
}else{
utf8char = text[n];
}
/* load glyph image into the slot (erase previous one) */
FT_Error error = FT_Load_Char(face, utf8char, image?FT_LOAD_RENDER:FT_LOAD_NO_BITMAP);
if ( error )
continue; /* ignore errors */
/* now, draw to our target surface (convert position) */
if (image){
FT_Bitmap* bitmap = &slot->bitmap;
FT_Int x = slot->bitmap_left;
FT_Int y = size.height - slot->bitmap_top;
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
/* for simplicity, we assume that `bitmap->pixel_mode' */
/* is `FT_PIXEL_MODE_GRAY' (i.e., not a bitmap font) */
assert(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY);
for ( i = x, p = 0; i < x_max; i++, p++ )
{
for ( j = y, q = 0; j < y_max; j++, q++ )
{
if (i < 0 || j < 0 || i >= size.width || j >= size.height){ continue; }
image[size.width * j + i] = (
image[size.width * j + i] + bitmap->buffer[q * bitmap->width + p]
) - (
image[size.width * j + i] * bitmap->buffer[q * bitmap->width + p]
);
// image[j][i] |= bitmap->buffer[q * bitmap->width + p];
// image[WIDTH*j*4 + i*4 + 0] |=bitmap->buffer[q * bitmap->width + p];
// image[WIDTH*j*4 + i*4 + 1] |=bitmap->buffer[q * bitmap->width + p];
// image[WIDTH*j*4 + i*4 + 2] |=bitmap->buffer[q * bitmap->width + p];
// image[WIDTH*j*4 + i*4 + 3] = 0xFF;
}
}
}
if (slot->metrics.horiBearingY > max_pos){max_pos = slot->metrics.horiBearingY;}
if (slot->metrics.horiBearingY-slot->metrics.height < min_pos){min_pos = slot->metrics.horiBearingY-slot->metrics.height;}
/* increment pen position */
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
return (TextSize){
pen.x/64,
(max_pos - min_pos)/64,
-min_pos
};
}
int main(int _argc, unsigned char** _argv) {
int argc;
LPWSTR* argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
unsigned char** argv = malloc(argc * sizeof(*argv));
for (size_t i = 0; i < argc; i++) {
int len = WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, NULL, 0, NULL, NULL);
argv[i] = malloc((len+1) * sizeof(*argv[i]));
argv[i][len] = 0; // assure its null terminated
WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, argv[i], len, NULL, NULL);
}
// leaking memory above. dosent matter as short run time
if (argc != 3) {
fprintf ( stderr, "usage: %s font sample-text\n", argv[0] );
exit( 1 );
}
unsigned char* filename = argv[1];
unsigned char* text = argv[2];
FT_Library library;
FT_Error error = FT_Init_FreeType( &library ); /* initialize library */
/* error handling omitted */
FT_Face face;
error = FT_New_Face( library, filename, 0, &face ); /* create face object */
/* error handling omitted */
/* use 50pt at 100dpi */
// error = FT_Set_Char_Size( face, 50 * 64, 0, 100, 0 ); /* set character size */
error = FT_Set_Pixel_Sizes(face, 51, 51);
/* error handling omitted */
/* cmap selection omitted; */
/* for simplicity we assume that the font contains a Unicode cmap */
TextSize size = render_text(face, text, NULL, (TextSize){});
unsigned char* image = calloc(size.width*size.height, 1);
render_text(face, text, image, size);
show_image(image, size.width, size.height);
FT_Done_Face(face);
FT_Done_FreeType(library);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment