Font test.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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