|
@@ -398,6 +398,18 @@ int main(int arg, char **argv)
|
|
|
#ifndef STBTT_sqrt
|
|
|
#include <math.h>
|
|
|
#define STBTT_sqrt(x) sqrt(x)
|
|
|
+ #define STBTT_pow(x,y) pow(x,y)
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #ifndef STBTT_cos
|
|
|
+ #include <math.h>
|
|
|
+ #define STBTT_cos(x) cos(x)
|
|
|
+ #define STBTT_acos(x) acos(x)
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #ifndef STBTT_fabs
|
|
|
+ #include <math.h>
|
|
|
+ #define STBTT_fabs(x) fabs(x)
|
|
|
#endif
|
|
|
|
|
|
// #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
|
|
@@ -418,7 +430,7 @@ int main(int arg, char **argv)
|
|
|
#endif
|
|
|
|
|
|
#ifndef STBTT_memcpy
|
|
|
- #include <memory.h>
|
|
|
+ #include <string.h>
|
|
|
#define STBTT_memcpy memcpy
|
|
|
#define STBTT_memset memset
|
|
|
#endif
|
|
@@ -623,7 +635,7 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
|
|
|
|
|
|
// The following structure is defined publically so you can declare one on
|
|
|
// the stack or as a global or etc, but you should treat it as opaque.
|
|
|
-typedef struct stbtt_fontinfo
|
|
|
+struct stbtt_fontinfo
|
|
|
{
|
|
|
void * userdata;
|
|
|
unsigned char * data; // pointer to .ttf file
|
|
@@ -634,7 +646,7 @@ typedef struct stbtt_fontinfo
|
|
|
int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
|
|
|
int index_map; // a cmap mapping for our chosen character encoding
|
|
|
int indexToLocFormat; // format needed to map from glyph index to glyph
|
|
|
-} stbtt_fontinfo;
|
|
|
+};
|
|
|
|
|
|
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
|
|
|
// Given an offset into the file that defines a font, this function builds
|
|
@@ -774,6 +786,10 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns
|
|
|
// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
|
|
|
// shift for the character
|
|
|
|
|
|
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);
|
|
|
+// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
|
|
|
+// is performed (see stbtt_PackSetOversampling)
|
|
|
+
|
|
|
STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
|
|
|
// get the bbox of the bitmap centered around the glyph origin; so the
|
|
|
// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
|
|
@@ -791,6 +807,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float
|
|
|
STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
|
|
|
STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
|
|
|
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
|
|
|
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);
|
|
|
STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
|
|
|
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
|
|
|
|
|
@@ -804,6 +821,14 @@ typedef struct
|
|
|
|
|
|
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata);
|
|
|
|
|
|
+//////////////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+// Signed Distance Function rendering
|
|
|
+
|
|
|
+STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
|
|
|
+STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
|
|
|
+
|
|
|
+
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
//
|
|
|
// Finding the right font...
|
|
@@ -1974,7 +1999,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
|
|
}
|
|
|
y_crossing += dy * (x2 - (x1+1));
|
|
|
|
|
|
- STBTT_assert(fabs(area) <= 1.01f);
|
|
|
+ STBTT_assert(STBTT_fabs(area) <= 1.01f);
|
|
|
|
|
|
scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (y1-y_crossing);
|
|
|
|
|
@@ -2001,12 +2026,12 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
|
|
// that, we need to explicitly produce segments based on x positions.
|
|
|
|
|
|
// rename variables to clear pairs
|
|
|
- float y0 = y_top;
|
|
|
- float x1 = (float) (x);
|
|
|
- float x2 = (float) (x+1);
|
|
|
- float x3 = xb;
|
|
|
- float y3 = y_bottom;
|
|
|
- float y1,y2;
|
|
|
+ float x1,x2,x3,y3,y2;
|
|
|
+ y0 = y_top;
|
|
|
+ x1 = (float) (x);
|
|
|
+ x2 = (float) (x+1);
|
|
|
+ x3 = xb;
|
|
|
+ y3 = y_bottom;
|
|
|
|
|
|
// x = e->x + e->dx * (y-y_top)
|
|
|
// (y-y_top) = (x - e->x) / e->dx
|
|
@@ -2106,7 +2131,7 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
|
|
|
int m;
|
|
|
sum += scanline2[i];
|
|
|
k = scanline[i] + sum;
|
|
|
- k = (float) fabs(k)*255 + 0.5f;
|
|
|
+ k = (float) STBTT_fabs(k)*255 + 0.5f;
|
|
|
m = (int) k;
|
|
|
if (m > 255) m = 255;
|
|
|
result->pixels[j*result->stride + i] = (unsigned char) m;
|
|
@@ -2850,6 +2875,29 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon
|
|
|
return k;
|
|
|
}
|
|
|
|
|
|
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)
|
|
|
+{
|
|
|
+ stbtt_MakeGlyphBitmapSubpixel(info,
|
|
|
+ output,
|
|
|
+ out_w - (prefilter_x - 1),
|
|
|
+ out_h - (prefilter_y - 1),
|
|
|
+ out_stride,
|
|
|
+ scale_x,
|
|
|
+ scale_y,
|
|
|
+ shift_x,
|
|
|
+ shift_y,
|
|
|
+ glyph);
|
|
|
+
|
|
|
+ if (prefilter_x > 1)
|
|
|
+ stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);
|
|
|
+
|
|
|
+ if (prefilter_y > 1)
|
|
|
+ stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);
|
|
|
+
|
|
|
+ *sub_x = stbtt__oversample_shift(prefilter_x);
|
|
|
+ *sub_y = stbtt__oversample_shift(prefilter_y);
|
|
|
+}
|
|
|
+
|
|
|
// rects array must be big enough to accommodate all characters in the given ranges
|
|
|
STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
|
|
|
{
|
|
@@ -3012,6 +3060,382 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i
|
|
|
*xpos += b->xadvance;
|
|
|
}
|
|
|
|
|
|
+//////////////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+// sdf computation
|
|
|
+//
|
|
|
+
|
|
|
+#define STBTT_min(a,b) ((a) < (b) ? (a) : (b))
|
|
|
+#define STBTT_max(a,b) ((a) < (b) ? (b) : (a))
|
|
|
+
|
|
|
+static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])
|
|
|
+{
|
|
|
+ float q0perp = q0[1]*ray[0] - q0[0]*ray[1];
|
|
|
+ float q1perp = q1[1]*ray[0] - q1[0]*ray[1];
|
|
|
+ float q2perp = q2[1]*ray[0] - q2[0]*ray[1];
|
|
|
+ float roperp = orig[1]*ray[0] - orig[0]*ray[1];
|
|
|
+
|
|
|
+ float a = q0perp - 2*q1perp + q2perp;
|
|
|
+ float b = q1perp - q0perp;
|
|
|
+ float c = q0perp - roperp;
|
|
|
+
|
|
|
+ float s0 = 0., s1 = 0.;
|
|
|
+ int num_s = 0;
|
|
|
+
|
|
|
+ if (a != 0.0) {
|
|
|
+ float discr = b*b - a*c;
|
|
|
+ if (discr > 0.0) {
|
|
|
+ float rcpna = -1 / a;
|
|
|
+ float d = (float) sqrt(discr);
|
|
|
+ s0 = (b+d) * rcpna;
|
|
|
+ s1 = (b-d) * rcpna;
|
|
|
+ if (s0 >= 0.0 && s0 <= 1.0)
|
|
|
+ num_s = 1;
|
|
|
+ if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {
|
|
|
+ if (num_s == 0) s0 = s1;
|
|
|
+ ++num_s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 2*b*s + c = 0
|
|
|
+ // s = -c / (2*b)
|
|
|
+ s0 = c / (-2 * b);
|
|
|
+ if (s0 >= 0.0 && s0 <= 1.0)
|
|
|
+ num_s = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (num_s == 0)
|
|
|
+ return 0;
|
|
|
+ else {
|
|
|
+ float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);
|
|
|
+ float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;
|
|
|
+
|
|
|
+ float q0d = q0[0]*rayn_x + q0[1]*rayn_y;
|
|
|
+ float q1d = q1[0]*rayn_x + q1[1]*rayn_y;
|
|
|
+ float q2d = q2[0]*rayn_x + q2[1]*rayn_y;
|
|
|
+ float rod = orig[0]*rayn_x + orig[1]*rayn_y;
|
|
|
+
|
|
|
+ float q10d = q1d - q0d;
|
|
|
+ float q20d = q2d - q0d;
|
|
|
+ float q0rd = q0d - rod;
|
|
|
+
|
|
|
+ hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;
|
|
|
+ hits[0][1] = a*s0+b;
|
|
|
+
|
|
|
+ if (num_s > 1) {
|
|
|
+ hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;
|
|
|
+ hits[1][1] = a*s1+b;
|
|
|
+ return 2;
|
|
|
+ } else {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int equal(float *a, float *b)
|
|
|
+{
|
|
|
+ return (a[0] == b[0] && a[1] == b[1]);
|
|
|
+}
|
|
|
+
|
|
|
+static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ float orig[2], ray[2] = { 1, 0 };
|
|
|
+ float y_frac;
|
|
|
+ int winding = 0;
|
|
|
+
|
|
|
+ orig[0] = x;
|
|
|
+ orig[1] = y;
|
|
|
+
|
|
|
+ // make sure y never passes through a vertex of the shape
|
|
|
+ y_frac = (float) fmod(y, 1.0f);
|
|
|
+ if (y_frac < 0.01f)
|
|
|
+ y += 0.01f;
|
|
|
+ else if (y_frac > 0.99f)
|
|
|
+ y -= 0.01f;
|
|
|
+ orig[1] = y;
|
|
|
+
|
|
|
+ // test a ray from (-infinity,y) to (x,y)
|
|
|
+ for (i=0; i < nverts; ++i) {
|
|
|
+ if (verts[i].type == STBTT_vline) {
|
|
|
+ int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;
|
|
|
+ int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y;
|
|
|
+ if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
|
|
+ float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
|
|
|
+ if (x_inter < x)
|
|
|
+ winding += (y0 < y1) ? 1 : -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (verts[i].type == STBTT_vcurve) {
|
|
|
+ int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;
|
|
|
+ int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy;
|
|
|
+ int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ;
|
|
|
+ int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));
|
|
|
+ int by = STBTT_max(y0,STBTT_max(y1,y2));
|
|
|
+ if (y > ay && y < by && x > ax) {
|
|
|
+ float q0[2],q1[2],q2[2];
|
|
|
+ float hits[2][2];
|
|
|
+ q0[0] = (float)x0;
|
|
|
+ q0[1] = (float)y0;
|
|
|
+ q1[0] = (float)x1;
|
|
|
+ q1[1] = (float)y1;
|
|
|
+ q2[0] = (float)x2;
|
|
|
+ q2[1] = (float)y2;
|
|
|
+ if (equal(q0,q1) || equal(q1,q2)) {
|
|
|
+ x0 = (int)verts[i-1].x;
|
|
|
+ y0 = (int)verts[i-1].y;
|
|
|
+ x1 = (int)verts[i ].x;
|
|
|
+ y1 = (int)verts[i ].y;
|
|
|
+ if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
|
|
+ float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
|
|
|
+ if (x_inter < x)
|
|
|
+ winding += (y0 < y1) ? 1 : -1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);
|
|
|
+ if (num_hits >= 1)
|
|
|
+ if (hits[0][0] < 0)
|
|
|
+ winding += (hits[0][1] < 0 ? -1 : 1);
|
|
|
+ if (num_hits >= 2)
|
|
|
+ if (hits[1][0] < 0)
|
|
|
+ winding += (hits[1][1] < 0 ? -1 : 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return winding;
|
|
|
+}
|
|
|
+
|
|
|
+static float stbtt__cuberoot( float x )
|
|
|
+{
|
|
|
+ if (x<0)
|
|
|
+ return -(float) STBTT_pow(-x,1.0f/3.0f);
|
|
|
+ else
|
|
|
+ return (float) STBTT_pow( x,1.0f/3.0f);
|
|
|
+}
|
|
|
+
|
|
|
+// x^3 + c*x^2 + b*x + a = 0
|
|
|
+static int stbtt__solve_cubic(float a, float b, float c, float* r)
|
|
|
+{
|
|
|
+ float s = -a / 3;
|
|
|
+ float p = b - a*a / 3;
|
|
|
+ float q = a * (2*a*a - 9*b) / 27 + c;
|
|
|
+ float p3 = p*p*p;
|
|
|
+ float d = q*q + 4*p3 / 27;
|
|
|
+ if (d >= 0) {
|
|
|
+ float z = (float) STBTT_sqrt(d);
|
|
|
+ float u = (-q + z) / 2;
|
|
|
+ float v = (-q - z) / 2;
|
|
|
+ u = stbtt__cuberoot(u);
|
|
|
+ v = stbtt__cuberoot(v);
|
|
|
+ r[0] = s + u + v;
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+ float u = (float) STBTT_sqrt(-p/3);
|
|
|
+ float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
|
|
|
+ float m = (float) STBTT_cos(v);
|
|
|
+ float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
|
|
|
+ r[0] = s + u * 2 * m;
|
|
|
+ r[1] = s - u * (m + n);
|
|
|
+ r[2] = s - u * (m - n);
|
|
|
+
|
|
|
+ //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
|
|
|
+ //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
|
|
|
+ //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
|
|
|
+ return 3;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
|
|
|
+{
|
|
|
+ float scale_x = scale, scale_y = scale;
|
|
|
+ int ix0,iy0,ix1,iy1;
|
|
|
+ int w,h;
|
|
|
+ unsigned char *data;
|
|
|
+
|
|
|
+ // if one scale is 0, use same scale for both
|
|
|
+ if (scale_x == 0) scale_x = scale_y;
|
|
|
+ if (scale_y == 0) {
|
|
|
+ if (scale_x == 0) return NULL; // if both scales are 0, return NULL
|
|
|
+ scale_y = scale_x;
|
|
|
+ }
|
|
|
+
|
|
|
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
|
|
|
+
|
|
|
+ // if empty, return NULL
|
|
|
+ if (ix0 == ix1 || iy0 == iy1)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ ix0 -= padding;
|
|
|
+ iy0 -= padding;
|
|
|
+ ix1 += padding;
|
|
|
+ iy1 += padding;
|
|
|
+
|
|
|
+ w = (ix1 - ix0);
|
|
|
+ h = (iy1 - iy0);
|
|
|
+
|
|
|
+ if (width ) *width = w;
|
|
|
+ if (height) *height = h;
|
|
|
+ if (xoff ) *xoff = ix0;
|
|
|
+ if (yoff ) *yoff = iy0;
|
|
|
+
|
|
|
+ // invert for y-downwards bitmaps
|
|
|
+ scale_y = -scale_y;
|
|
|
+
|
|
|
+ {
|
|
|
+ int x,y,i,j;
|
|
|
+ float *precompute;
|
|
|
+ stbtt_vertex *verts;
|
|
|
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
|
|
|
+ data = (unsigned char *) STBTT_malloc(w * h, info->userdata);
|
|
|
+ precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);
|
|
|
+
|
|
|
+ for (i=0,j=num_verts-1; i < num_verts; j=i++) {
|
|
|
+ if (verts[i].type == STBTT_vline) {
|
|
|
+ float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
|
|
|
+ float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
|
|
|
+ float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
|
|
|
+ precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
|
|
|
+ } else if (verts[i].type == STBTT_vcurve) {
|
|
|
+ float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
|
|
|
+ float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
|
|
|
+ float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
|
|
|
+ float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
|
|
|
+ float len2 = bx*bx + by*by;
|
|
|
+ if (len2 != 0.0f)
|
|
|
+ precompute[i] = 1.0f / (bx*bx + by*by);
|
|
|
+ else
|
|
|
+ precompute[i] = 0.0f;
|
|
|
+ } else
|
|
|
+ precompute[i] = 0.0f;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (y=iy0; y < iy1; ++y) {
|
|
|
+ for (x=ix0; x < ix1; ++x) {
|
|
|
+ float val;
|
|
|
+ float min_dist = 999999.0f;
|
|
|
+ float sx = (float) x + 0.5f;
|
|
|
+ float sy = (float) y + 0.5f;
|
|
|
+ float x_gspace = (sx / scale_x);
|
|
|
+ float y_gspace = (sy / scale_y);
|
|
|
+
|
|
|
+ int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path
|
|
|
+
|
|
|
+ for (i=0; i < num_verts; ++i) {
|
|
|
+ float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
|
|
|
+
|
|
|
+ // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve
|
|
|
+ float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
|
|
|
+ if (dist2 < min_dist*min_dist)
|
|
|
+ min_dist = (float) STBTT_sqrt(dist2);
|
|
|
+
|
|
|
+ if (verts[i].type == STBTT_vline) {
|
|
|
+ float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
|
|
|
+
|
|
|
+ // coarse culling against bbox
|
|
|
+ //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
|
|
|
+ // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
|
|
|
+ float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
|
|
|
+ STBTT_assert(i != 0);
|
|
|
+ if (dist < min_dist) {
|
|
|
+ // check position along line
|
|
|
+ // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)
|
|
|
+ // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)
|
|
|
+ float dx = x1-x0, dy = y1-y0;
|
|
|
+ float px = x0-sx, py = y0-sy;
|
|
|
+ // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy
|
|
|
+ // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve
|
|
|
+ float t = -(px*dx + py*dy) / (dx*dx + dy*dy);
|
|
|
+ if (t >= 0.0f && t <= 1.0f)
|
|
|
+ min_dist = dist;
|
|
|
+ }
|
|
|
+ } else if (verts[i].type == STBTT_vcurve) {
|
|
|
+ float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;
|
|
|
+ float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y;
|
|
|
+ float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);
|
|
|
+ float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);
|
|
|
+ float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);
|
|
|
+ float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);
|
|
|
+ // coarse culling against bbox to avoid computing cubic unnecessarily
|
|
|
+ if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {
|
|
|
+ int num=0;
|
|
|
+ float ax = x1-x0, ay = y1-y0;
|
|
|
+ float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
|
|
|
+ float mx = x0 - sx, my = y0 - sy;
|
|
|
+ float res[3],px,py,t,it;
|
|
|
+ float a_inv = precompute[i];
|
|
|
+ if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
|
|
|
+ float a = 3*(ax*bx + ay*by);
|
|
|
+ float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
|
|
|
+ float c = mx*ax+my*ay;
|
|
|
+ if (a == 0.0) { // if a is 0, it's linear
|
|
|
+ if (b != 0.0) {
|
|
|
+ res[num++] = -c/b;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ float discriminant = b*b - 4*a*c;
|
|
|
+ if (discriminant < 0)
|
|
|
+ num = 0;
|
|
|
+ else {
|
|
|
+ float root = (float) STBTT_sqrt(discriminant);
|
|
|
+ res[0] = (-b - root)/(2*a);
|
|
|
+ res[1] = (-b + root)/(2*a);
|
|
|
+ num = 2; // don't bother distinguishing 1-solution case, as code below will still work
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point
|
|
|
+ float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;
|
|
|
+ float d = (mx*ax+my*ay) * a_inv;
|
|
|
+ num = stbtt__solve_cubic(b, c, d, res);
|
|
|
+ }
|
|
|
+ if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
|
|
|
+ t = res[0], it = 1.0f - t;
|
|
|
+ px = it*it*x0 + 2*t*it*x1 + t*t*x2;
|
|
|
+ py = it*it*y0 + 2*t*it*y1 + t*t*y2;
|
|
|
+ dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
|
|
|
+ if (dist2 < min_dist * min_dist)
|
|
|
+ min_dist = (float) STBTT_sqrt(dist2);
|
|
|
+ }
|
|
|
+ if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {
|
|
|
+ t = res[1], it = 1.0f - t;
|
|
|
+ px = it*it*x0 + 2*t*it*x1 + t*t*x2;
|
|
|
+ py = it*it*y0 + 2*t*it*y1 + t*t*y2;
|
|
|
+ dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
|
|
|
+ if (dist2 < min_dist * min_dist)
|
|
|
+ min_dist = (float) STBTT_sqrt(dist2);
|
|
|
+ }
|
|
|
+ if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {
|
|
|
+ t = res[2], it = 1.0f - t;
|
|
|
+ px = it*it*x0 + 2*t*it*x1 + t*t*x2;
|
|
|
+ py = it*it*y0 + 2*t*it*y1 + t*t*y2;
|
|
|
+ dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
|
|
|
+ if (dist2 < min_dist * min_dist)
|
|
|
+ min_dist = (float) STBTT_sqrt(dist2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (winding == 0)
|
|
|
+ min_dist = -min_dist; // if outside the shape, value is negative
|
|
|
+ val = onedge_value + pixel_dist_scale * min_dist;
|
|
|
+ if (val < 0)
|
|
|
+ val = 0;
|
|
|
+ else if (val > 255)
|
|
|
+ val = 255;
|
|
|
+ data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ STBTT_free(precompute, info->userdata);
|
|
|
+ STBTT_free(verts, info->userdata);
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+}
|
|
|
+
|
|
|
+STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
|
|
|
+{
|
|
|
+ return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);
|
|
|
+}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
//
|