Skip to content

Instantly share code, notes, and snippets.

@rygorous
Last active August 29, 2015 14:06
Embed
What would you like to do?
Font test.
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
static float srgb_to_linear(float f)
{
if (f <= 0.04045f)
return f / 12.92f;
else
return powf((f + 0.055f) / 1.055f, 2.4f);
}
static float linear_to_srgb(float f)
{
if (f <= 0.0031308f)
return f * 12.92f;
else
return 1.055f * powf(f, 1.0f / 2.4f) - 0.055f;
}
static float fracf(float f)
{
return f - floorf(f);
}
static uint8_t *read_file(char const *name)
{
FILE *f = fopen(name, "rb");
fseek(f, 0, SEEK_END);
int size = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *buf = new uint8_t[size];
fread(buf, 1, size, f);
fclose(f);
return buf;
}
struct Bitmap
{
Bitmap(int w, int h);
~Bitmap();
uint8_t *pixels;
int w, h;
};
Bitmap::Bitmap(int w, int h)
: w(w), h(h)
{
pixels = new uint8_t[w*h];
memset(pixels, 0, w*h);
}
Bitmap::~Bitmap()
{
delete[] pixels;
}
static void rect(Bitmap &bmp, int x0, int y0, int x1, int y1, uint8_t col)
{
for (int y = y0; y < y1; y++) {
uint8_t *row = bmp.pixels + y*bmp.w;
for (int x = x0; x < x1; x++)
row[x] = col;
}
}
static void mask(Bitmap &bmp, int pos_x, int pos_y, int w, int h, uint8_t const *mask, uint8_t col, bool gamma)
{
for (int y = 0; y < h; y++) {
uint8_t const *maskrow = mask + y*w;
uint8_t *outrow = bmp.pixels + (y + pos_y) * bmp.w + pos_x;
if (gamma) {
// values are sRGB, so we convert to linear before we blend (and convert the results back)
float col_lin = srgb_to_linear(col / 255.0f);
for (int x = 0; x < w; x++) {
float m = maskrow[x] / 255.0f;
float orig_lin = srgb_to_linear(outrow[x] / 255.0f);
float out_lin = orig_lin * (1.0f - m) + col_lin * m;
float out_srgb = linear_to_srgb(out_lin);
outrow[x] = (int) (out_srgb * 255.0f + 0.5f);
}
} else {
// blend as if the values are linear
for (int x = 0; x < w; x++) {
uint8_t m = maskrow[x];
outrow[x] = (outrow[x] * (255 - m) + col * m + 127) / 255;
}
}
}
}
static void text(Bitmap &bmp, float x, float y, char const *str, stbtt_fontinfo const *font, float scale, uint8_t col, bool gamma)
{
int ascent, descent, linegap;
stbtt_GetFontVMetrics(font, &ascent, &descent, &linegap);
y += scale * ascent;
while (*str) {
unsigned char ch = *str++;
int advance, lsb;
if (ch > ' ') {
int w, h, xoff, yoff;
// stb_truetype returns a (linear) coverage mask - 0 (0%) to 255 (100%).
uint8_t *msk = stbtt_GetCodepointBitmapSubpixel(font, scale, scale, fracf(x), fracf(y), ch, &w, &h, &xoff, &yoff);
mask(bmp, (int) (x + xoff), (int) (y + yoff), w, h, msk, col, gamma);
free(msk);
}
stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
x += scale * advance;
}
}
int main()
{
stbtt_fontinfo font;
unsigned char *fontdata = read_file("DejaVuSans.ttf");
if (!stbtt_InitFont(&font, fontdata, 0))
{
printf("couldn't read font.\n");
return 1;
}
int w = 512, h = 128;
Bitmap img(w, h);
rect(img, 0, 32, w, 96, 188);
rect(img, 0, 96, w, 128, 255);
#define MSG "The quick brown fox jumps over the lazy dog."
float scale = stbtt_ScaleForPixelHeight(&font, 14.0f);
text(img, 0.0f, 0.0f, MSG " (blend in sRGB space)", &font, scale, 255, false);
text(img, 0.0f, 16.0f, MSG " (blend in linear space)", &font, scale, 255, true);
text(img, 0.0f, 32.0f, MSG " (blend in sRGB space)", &font, scale, 255, false);
text(img, 0.0f, 48.0f, MSG " (blend in linear space)", &font, scale, 255, true);
text(img, 0.0f, 64.0f, MSG " (blend in sRGB space)", &font, scale, 0, false);
text(img, 0.0f, 80.0f, MSG " (blend in linear space)", &font, scale, 0, true);
text(img, 0.0f, 96.0f, MSG " (blend in sRGB space)", &font, scale, 0, false);
text(img, 0.0f, 112.0f, MSG " (blend in linear space)", &font, scale, 0, true);
stbi_write_png("out.png", img.w, img.h, 1, img.pixels, img.w);
delete[] fontdata;
return 0;
}
// vim:et:sw=4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment