1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
#define STB_DEFINE
#include "stb.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// used both to compute SDF and in 'shader'
float sdf_size = 32.0; // the larger this is, the better large font sizes look
float pixel_dist_scale = 64.0; // trades off precision w/ ability to handle *smaller* sizes
int onedge_value = 128;
int padding = 3; // not used in shader
typedef struct
{
float advance;
signed char xoff;
signed char yoff;
unsigned char w,h;
unsigned char *data;
} fontchar;
fontchar fdata[128];
#define BITMAP_W 1200
#define BITMAP_H 800
unsigned char bitmap[BITMAP_H][BITMAP_W][3];
char *sample = "This is goofy text, size %d!";
char *small_sample = "This is goofy text, size %d! Really needs in-shader supersampling to look good.";
void blend_pixel(int x, int y, int color, float alpha)
{
int i;
for (i=0; i < 3; ++i)
bitmap[y][x][i] = (unsigned char) (stb_lerp(alpha, bitmap[y][x][i], color)+0.5); // round
}
void draw_char(float px, float py, char c, float relative_scale)
{
int x,y;
fontchar *fc = &fdata[c];
float fx0 = px + fc->xoff*relative_scale;
float fy0 = py + fc->yoff*relative_scale;
float fx1 = fx0 + fc->w*relative_scale;
float fy1 = fy0 + fc->h*relative_scale;
int ix0 = (int) floor(fx0);
int iy0 = (int) floor(fy0);
int ix1 = (int) ceil(fx1);
int iy1 = (int) ceil(fy1);
// clamp to viewport
if (ix0 < 0) ix0 = 0;
if (iy0 < 0) iy0 = 0;
if (ix1 > BITMAP_W) ix1 = BITMAP_W;
if (iy1 > BITMAP_H) iy1 = BITMAP_H;
for (y=iy0; y < iy1; ++y) {
for (x=ix0; x < ix1; ++x) {
float sdf_dist, pix_dist;
float bmx = stb_linear_remap(x, fx0, fx1, 0, fc->w);
float bmy = stb_linear_remap(y, fy0, fy1, 0, fc->h);
int v00,v01,v10,v11;
float v0,v1,v;
int sx0 = (int) bmx;
int sx1 = sx0+1;
int sy0 = (int) bmy;
int sy1 = sy0+1;
// compute lerp weights
bmx = bmx - sx0;
bmy = bmy - sy0;
// clamp to edge
sx0 = stb_clamp(sx0, 0, fc->w-1);
sx1 = stb_clamp(sx1, 0, fc->w-1);
sy0 = stb_clamp(sy0, 0, fc->h-1);
sy1 = stb_clamp(sy1, 0, fc->h-1);
// bilinear texture sample
v00 = fc->data[sy0*fc->w+sx0];
v01 = fc->data[sy0*fc->w+sx1];
v10 = fc->data[sy1*fc->w+sx0];
v11 = fc->data[sy1*fc->w+sx1];
v0 = stb_lerp(bmx,v00,v01);
v1 = stb_lerp(bmx,v10,v11);
v = stb_lerp(bmy,v0 ,v1 );
#if 0
// non-anti-aliased
if (v > onedge_value)
blend_pixel(x,y,0,1.0);
#else
// Following math can be greatly simplified
// convert distance in SDF value to distance in SDF bitmap
sdf_dist = stb_linear_remap(v, onedge_value, onedge_value+pixel_dist_scale, 0, 1);
// convert distance in SDF bitmap to distance in output bitmap
pix_dist = sdf_dist * relative_scale;
// anti-alias by mapping 1/2 pixel around contour from 0..1 alpha
v = stb_linear_remap(pix_dist, -0.5f, 0.5f, 0, 1);
if (v > 1) v = 1;
if (v > 0)
blend_pixel(x,y,0,v);
#endif
}
}
}
void print_text(float x, float y, char *text, float scale)
{
int i;
for (i=0; text[i]; ++i) {
if (fdata[text[i]].data)
draw_char(x,y,text[i],scale);
x += fdata[text[i]].advance * scale;
}
}
int main(int argc, char **argv)
{
int ch;
float scale, ypos;
stbtt_fontinfo font;
void *data = stb_file("c:/windows/fonts/times.ttf", NULL);
stbtt_InitFont(&font, data, 0);
scale = stbtt_ScaleForPixelHeight(&font, sdf_size);
for (ch=32; ch < 127; ++ch) {
fontchar fc;
int xoff,yoff,w,h, advance;
fc.data = stbtt_GetCodepointSDF(&font, scale, ch, padding, onedge_value, pixel_dist_scale, &w, &h, &xoff, &yoff);
fc.xoff = xoff;
fc.yoff = yoff;
fc.w = w;
fc.h = h;
stbtt_GetCodepointHMetrics(&font, ch, &advance, NULL);
fc.advance = advance * scale;
fdata[ch] = fc;
}
ypos = 60;
memset(bitmap, 255, sizeof(bitmap));
print_text(400, ypos+30, stb_sprintf("sdf bitmap height %d", (int) sdf_size), 30/sdf_size);
ypos += 80;
for (scale = 8.0; scale < 120.0; scale *= 1.33f) {
print_text(80, ypos+scale, stb_sprintf(scale == 8.0 ? small_sample : sample, (int) scale), scale / sdf_size);
ypos += scale*1.05f + 20;
}
stbi_write_png("sdf_test.png", BITMAP_W, BITMAP_H, 3, bitmap, 0);
return 0;
}
|