Created
April 4, 2012 18:37
-
-
Save ehsan/2304593 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| uniform sampler2D u_atlas_tex; | |
| uniform ivec4 u_atlas_info; | |
| #define GLYPHY_TEXTURE1D_EXTRA_DECLS , sampler2D _tex, ivec4 _atlas_info, ivec2 _atlas_pos | |
| #define GLYPHY_TEXTURE1D_EXTRA_ARGS , _tex, _atlas_info, _atlas_pos | |
| #define GLYPHY_DEMO_EXTRA_ARGS , u_atlas_tex, u_atlas_info, gi.atlas_pos | |
| vec4 | |
| glyphy_texture1D_func (int offset GLYPHY_TEXTURE1D_EXTRA_DECLS) | |
| { | |
| ivec2 item_geom = _atlas_info.zw; | |
| vec2 pos = (vec2 (_atlas_pos.xy * item_geom + | |
| ivec2 (mod (float (offset), float (item_geom.x)), offset / item_geom.x)) + | |
| + vec2 (.5, .5)) / vec2(_atlas_info.xy); | |
| return texture2D (_tex, pos); | |
| } | |
| /* | |
| * Copyright 2012 Google, Inc. All Rights Reserved. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| * | |
| * Google Author(s): Behdad Esfahbod, Maysum Panju | |
| */ | |
| #ifndef GLYPHY_INFINITY | |
| # define GLYPHY_INFINITY 1e9 | |
| #endif | |
| #ifndef GLYPHY_EPSILON | |
| # define GLYPHY_EPSILON 1e-5 | |
| #endif | |
| #ifndef GLYPHY_RGBA | |
| # ifdef GLYPHY_BGRA | |
| # define GLYPHY_RGBA(v) glyphy_bgra (v) | |
| # else | |
| # define GLYPHY_RGBA(v) glyphy_rgba (v) | |
| # endif | |
| #endif | |
| vec4 | |
| glyphy_rgba (vec4 v) | |
| { | |
| return v.rgba; | |
| } | |
| vec4 | |
| glyphy_bgra (vec4 v) | |
| { | |
| return v.bgra; | |
| } | |
| struct glyphy_arc_t { | |
| vec2 p0; | |
| vec2 p1; | |
| float d; | |
| }; | |
| struct glyphy_arc_endpoint_t { | |
| /* Second arc endpoint */ | |
| vec2 p; | |
| /* Infinity if this endpoint does not form an arc with the previous | |
| * endpoint. Ie. a "move_to". Test with glyphy_isinf(). | |
| * Arc depth otherwise. */ | |
| float d; | |
| }; | |
| struct glyphy_arc_list_t { | |
| /* Number of endpoints in the list. | |
| * Will be zero if we're far away inside or outside, in which case side is set. | |
| * Will be -1 if this arc-list encodes a single line, in which case line_* are set. */ | |
| int num_endpoints; | |
| /* If num_endpoints is zero, this specifies whether we are inside (-1) | |
| * or outside (+1). Otherwise we're unsure (0). */ | |
| int side; | |
| /* Offset to the arc-endpoints from the beginning of the glyph blob */ | |
| int offset; | |
| /* A single line is all we care about. It's right here. */ | |
| float line_angle; | |
| float line_distance; /* From nominal glyph center */ | |
| }; | |
| bool | |
| glyphy_isinf (float v) | |
| { | |
| return abs (v) >= GLYPHY_INFINITY * .5; | |
| } | |
| bool | |
| glyphy_iszero (float v) | |
| { | |
| return abs (v) <= GLYPHY_EPSILON * 2.; | |
| } | |
| vec2 | |
| glyphy_perpendicular (const vec2 v) | |
| { | |
| return vec2 (-v.y, v.x); | |
| } | |
| int | |
| glyphy_float_to_byte (const float v) | |
| { | |
| return int (v * (256. - GLYPHY_EPSILON)); | |
| } | |
| ivec4 | |
| glyphy_vec4_to_bytes (const vec4 v) | |
| { | |
| return ivec4 (v * (256. - GLYPHY_EPSILON)); | |
| } | |
| ivec2 | |
| glyphy_float_to_two_nimbles (const float v) | |
| { | |
| int f = glyphy_float_to_byte (v); | |
| return ivec2 (f / 16, int(mod (float(f), 16.))); | |
| } | |
| /* returns tan (2 * atan (d)) */ | |
| float | |
| glyphy_tan2atan (float d) | |
| { | |
| return 2. * d / (1. - d * d); | |
| } | |
| glyphy_arc_endpoint_t | |
| glyphy_arc_endpoint_decode (const vec4 v, ivec2 nominal_size) | |
| { | |
| vec2 p = (vec2 (glyphy_float_to_two_nimbles (v.a)) + v.gb) / 16.; | |
| float d = v.r; | |
| if (d == 0.) | |
| d = GLYPHY_INFINITY; | |
| else | |
| #define GLYPHY_MAX_D .5 | |
| d = float(glyphy_float_to_byte (d) - 128) * GLYPHY_MAX_D / 127.; | |
| #undef GLYPHY_MAX_D | |
| return glyphy_arc_endpoint_t (p * vec2(nominal_size), d); | |
| } | |
| vec2 | |
| glyphy_arc_center (glyphy_arc_t a) | |
| { | |
| return mix (a.p0, a.p1, .5) + | |
| glyphy_perpendicular (a.p1 - a.p0) / (2. * glyphy_tan2atan (a.d)); | |
| } | |
| bool | |
| glyphy_arc_wedge_contains (const glyphy_arc_t a, const vec2 p) | |
| { | |
| float d2 = glyphy_tan2atan (a.d); | |
| return dot (p - a.p0, (a.p1 - a.p0) * mat2(1, d2, -d2, 1)) >= 0. && | |
| dot (p - a.p1, (a.p1 - a.p0) * mat2(1, -d2, d2, 1)) <= 0.; | |
| } | |
| float | |
| glyphy_arc_wedge_signed_dist (const glyphy_arc_t a, const vec2 p) | |
| { | |
| if (abs (a.d) <= .01) { | |
| vec2 v = normalize (a.p1 - a.p0); | |
| float line_d = dot (p - a.p0, glyphy_perpendicular (v)); | |
| if (a.d == 0.) | |
| return line_d; | |
| float d0 = dot ((p - a.p0), v); | |
| if (d0 < 0.) | |
| return sign (line_d) * distance (p, a.p0); | |
| float d1 = dot ((a.p1 - p), v); | |
| if (d1 < 0.) | |
| return sign (line_d) * distance (p, a.p1); | |
| float r = 2. * a.d * (d0 * d1) / (d0 + d1); | |
| if (r * line_d > 0.) | |
| return sign (line_d) * min (abs (line_d + r), min (distance (p, a.p0), distance (p, a.p1))); | |
| return line_d + r; | |
| } | |
| vec2 c = glyphy_arc_center (a); | |
| return sign (a.d) * (distance (a.p0, c) - distance (p, c)); | |
| } | |
| float | |
| glyphy_arc_extended_dist (const glyphy_arc_t a, const vec2 p) | |
| { | |
| /* Note: this doesn't handle points inside the wedge. */ | |
| vec2 m = mix (a.p0, a.p1, .5); | |
| float d2 = glyphy_tan2atan (a.d); | |
| if (dot (p - m, a.p1 - m) < 0.) | |
| return dot (p - a.p0, normalize ((a.p1 - a.p0) * mat2(+d2, -1, +1, +d2))); | |
| else | |
| return dot (p - a.p1, normalize ((a.p1 - a.p0) * mat2(-d2, -1, +1, -d2))); | |
| } | |
| int | |
| glyphy_arc_list_offset (const vec2 p, ivec2 nominal_size) | |
| { | |
| ivec2 cell = ivec2 (clamp (vec2(floor (p)), vec2 (0.,0.), vec2(nominal_size - 1))); | |
| return cell.y * nominal_size.x + cell.x; | |
| } | |
| glyphy_arc_list_t | |
| glyphy_arc_list_decode (const vec4 v, ivec2 nominal_size) | |
| { | |
| glyphy_arc_list_t l; | |
| ivec4 iv = glyphy_vec4_to_bytes (v); | |
| l.side = 0; /* unsure */ | |
| if (iv.r == 0) { /* arc-list encoded */ | |
| l.offset = (iv.g * 256) + iv.b; | |
| l.num_endpoints = iv.a; | |
| if (l.num_endpoints == 255) { | |
| l.num_endpoints = 0; | |
| l.side = -1; | |
| } else if (l.num_endpoints == 0) | |
| l.side = +1; | |
| } else { /* single line encoded */ | |
| l.num_endpoints = -1; | |
| l.line_distance = float(((iv.r - 128) * 256 + iv.g) - 0x4000) / float (0x1FFF) | |
| * max (float (nominal_size.x), float (nominal_size.y)); | |
| l.line_angle = float(-((iv.b * 256 + iv.a) - 0x8000)) / float (0x7FFF) * 3.14159265358979; | |
| } | |
| return l; | |
| } | |
| /* | |
| * Copyright 2012 Google, Inc. All Rights Reserved. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| * | |
| * Google Author(s): Behdad Esfahbod, Maysum Panju | |
| */ | |
| #ifndef GLYPHY_TEXTURE1D_FUNC | |
| #define GLYPHY_TEXTURE1D_FUNC glyphy_texture1D_func | |
| #endif | |
| #ifndef GLYPHY_TEXTURE1D_EXTRA_DECLS | |
| #define GLYPHY_TEXTURE1D_EXTRA_DECLS | |
| #endif | |
| #ifndef GLYPHY_TEXTURE1D_EXTRA_ARGS | |
| #define GLYPHY_TEXTURE1D_EXTRA_ARGS | |
| #endif | |
| #ifndef GLYPHY_SDF_TEXTURE1D_FUNC | |
| #define GLYPHY_SDF_TEXTURE1D_FUNC GLYPHY_TEXTURE1D_FUNC | |
| #endif | |
| #ifndef GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS | |
| #define GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS GLYPHY_TEXTURE1D_EXTRA_DECLS | |
| #endif | |
| #ifndef GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS | |
| #define GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS GLYPHY_TEXTURE1D_EXTRA_ARGS | |
| #endif | |
| #ifndef GLYPHY_SDF_TEXTURE1D | |
| #define GLYPHY_SDF_TEXTURE1D(offset) GLYPHY_RGBA (GLYPHY_SDF_TEXTURE1D_FUNC (offset GLYPHY_TEXTURE1D_EXTRA_ARGS)) | |
| #endif | |
| #ifndef GLYPHY_MAX_NUM_ENDPOINTS | |
| #define GLYPHY_MAX_NUM_ENDPOINTS 32 | |
| #endif | |
| glyphy_arc_list_t | |
| glyphy_arc_list (vec2 p, ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS) | |
| { | |
| int cell_offset = glyphy_arc_list_offset (p, nominal_size); | |
| vec4 arc_list_data = GLYPHY_SDF_TEXTURE1D (cell_offset); | |
| return glyphy_arc_list_decode (arc_list_data, nominal_size); | |
| } | |
| float | |
| glyphy_sdf (vec2 p, ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS) | |
| { | |
| glyphy_arc_list_t arc_list = glyphy_arc_list (p, nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS); | |
| /* Short-circuits */ | |
| if (arc_list.num_endpoints == 0) { | |
| /* far-away cell */ | |
| return GLYPHY_INFINITY * float(arc_list.side); | |
| } if (arc_list.num_endpoints == -1) { | |
| /* single-line */ | |
| float angle = arc_list.line_angle; | |
| vec2 n = vec2 (cos (angle), sin (angle)); | |
| return dot (p - (vec2(nominal_size) * .5), n) - arc_list.line_distance; | |
| } | |
| float side = float(arc_list.side); | |
| float min_dist = GLYPHY_INFINITY; | |
| glyphy_arc_t closest_arc; | |
| glyphy_arc_endpoint_t endpoint_prev, endpoint; | |
| endpoint_prev = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset), nominal_size); | |
| for (int i = 1; i < GLYPHY_MAX_NUM_ENDPOINTS; i++) | |
| { | |
| if (i >= arc_list.num_endpoints) { | |
| break; | |
| } | |
| endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset + i), nominal_size); | |
| glyphy_arc_t a = glyphy_arc_t (endpoint_prev.p, endpoint.p, endpoint.d); | |
| endpoint_prev = endpoint; | |
| if (glyphy_isinf (a.d)) continue; | |
| if (glyphy_arc_wedge_contains (a, p)) | |
| { | |
| float sdist = glyphy_arc_wedge_signed_dist (a, p); | |
| float udist = abs (sdist) * (1. - GLYPHY_EPSILON); | |
| if (udist <= min_dist) { | |
| min_dist = udist; | |
| side = sdist <= 0. ? -1. : +1.; | |
| } | |
| } else { | |
| float udist = min (distance (p, a.p0), distance (p, a.p1)); | |
| if (udist < min_dist) { | |
| min_dist = udist; | |
| side = 0.; /* unsure */ | |
| closest_arc = a; | |
| } else if (side == 0. && udist == min_dist) { | |
| /* If this new distance is the same as the current minimum, | |
| * compare extended distances. Take the sign from the arc | |
| * with larger extended distance. */ | |
| float old_ext_dist = glyphy_arc_extended_dist (closest_arc, p); | |
| float new_ext_dist = glyphy_arc_extended_dist (a, p); | |
| float ext_dist = abs (new_ext_dist) <= abs (old_ext_dist) ? | |
| old_ext_dist : new_ext_dist; | |
| /* For emboldening and stuff: */ | |
| // min_dist = abs (ext_dist); | |
| side = sign (ext_dist); | |
| } | |
| } | |
| } | |
| if (side == 0.) { | |
| // Technically speaking this should not happen, but it does. So try to fix it. | |
| float ext_dist = glyphy_arc_extended_dist (closest_arc, p); | |
| side = sign (ext_dist); | |
| } | |
| return min_dist * side; | |
| } | |
| float | |
| glyphy_point_dist (vec2 p, ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS) | |
| { | |
| glyphy_arc_list_t arc_list = glyphy_arc_list (p, nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS); | |
| float side = float(arc_list.side); | |
| float min_dist = GLYPHY_INFINITY; | |
| if (arc_list.num_endpoints == 0) | |
| return min_dist; | |
| glyphy_arc_endpoint_t endpoint_prev, endpoint; | |
| endpoint_prev = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset), nominal_size); | |
| for (int i = 1; i < GLYPHY_MAX_NUM_ENDPOINTS; i++) | |
| { | |
| if (i >= arc_list.num_endpoints) { | |
| break; | |
| } | |
| endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset + i), nominal_size); | |
| if (glyphy_isinf (endpoint.d)) continue; | |
| min_dist = min (min_dist, distance (p, endpoint.p)); | |
| } | |
| return min_dist; | |
| } | |
| #ifdef GL_ES | |
| precision highp float; | |
| #endif | |
| uniform float u_smoothfunc; | |
| uniform float u_contrast; | |
| uniform float u_gamma_adjust; | |
| uniform bool u_debug; | |
| varying vec4 v_glyph; | |
| struct glyph_info_t { | |
| ivec2 nominal_size; | |
| ivec2 atlas_pos; | |
| }; | |
| glyph_info_t | |
| glyph_info_decode (vec4 v) | |
| { | |
| glyph_info_t gi; | |
| gi.nominal_size = (ivec2 (mod (v.zw, 256.)) + 2) / 4; | |
| gi.atlas_pos = ivec2 (v_glyph.zw) / 256; | |
| return gi; | |
| } | |
| float | |
| antialias0 (float d) | |
| { | |
| return clamp (d + .5, 0., 1.); | |
| } | |
| float | |
| antialias1 (float d) | |
| { | |
| return smoothstep (-.75, +.75, d); | |
| } | |
| float | |
| antialias2 (float d) | |
| { | |
| d = d * 16. / 30. + .5; | |
| if (d <= 0.) return 0.; | |
| if (d >= 1.) return 1.; | |
| return d*d*d*(d*(d*6. - 15.) + 10.); | |
| } | |
| float | |
| antialias (float d) | |
| { | |
| if (u_smoothfunc == 0.) return antialias0 (d); | |
| if (u_smoothfunc == 1.) return antialias1 (d); | |
| if (u_smoothfunc == 2.) return antialias2 (d); | |
| return 0.; | |
| } | |
| void | |
| main() | |
| { | |
| vec2 p = v_glyph.xy; | |
| glyph_info_t gi = glyph_info_decode (v_glyph); | |
| /* isotropic antialiasing */ | |
| vec2 dpdx = dFdx (p); | |
| vec2 dpdy = dFdy (p); | |
| float m = length (vec2 (length (dpdx), length (dpdy))) / sqrt(2.); | |
| vec4 color = vec4 (0,0,0,1); | |
| float gsdist = glyphy_sdf (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS); | |
| float sdist = gsdist / m * u_contrast; | |
| if (!u_debug) { | |
| if (sdist > 1.) | |
| discard; | |
| float alpha = antialias (-sdist); | |
| if (u_gamma_adjust != 1.) | |
| alpha = pow (alpha, 1./u_gamma_adjust); | |
| color = vec4 (color.rgb,color.a * alpha); | |
| } else { | |
| color = vec4 (0,0,0,0); | |
| // Color the inside of the glyph a light red | |
| color += vec4 (.5,0,0,.5) * smoothstep (1., -1., sdist); | |
| float udist = abs (sdist); | |
| float gudist = abs (gsdist); | |
| // Color the outline red | |
| color += vec4 (1,0,0,1) * smoothstep (2., 1., udist); | |
| // Color the distance field in green | |
| if (!glyphy_isinf (udist)) | |
| color += vec4 (0,.3,0,(1. + sin (sdist)) * abs(1. - gsdist * 3.) / 3.); | |
| float pdist = glyphy_point_dist (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS); | |
| // Color points green | |
| color = mix (vec4 (0,1,0,.5), color, smoothstep (.05, .06, pdist)); | |
| glyphy_arc_list_t arc_list = glyphy_arc_list (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS); | |
| // Color the number of endpoints per cell blue | |
| color += vec4 (0,0,1,.1) * float(arc_list.num_endpoints) * 32./255.; | |
| } | |
| gl_FragColor = color; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment