File: sdf_test.c

package info (click to toggle)
libstb 0.0~git20180212.15.e6afb9c-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 5,440 kB
  • sloc: ansic: 65,656; cpp: 1,020; makefile: 54
file content (152 lines) | stat: -rw-r--r-- 4,626 bytes parent folder | download | duplicates (5)
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;
}