gl_font.cc (5943B)
1 /* 2 * Copyright 2012 Google, Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * Google Author(s): Behdad Esfahbod 17 */ 18 #include <assert.h> 19 #include <err.h> 20 #include <math.h> 21 #include <string> // std::string 22 #include <vector> // std::vector 23 24 #include "gl_font.h" 25 #include "glyphy/glyphy-freetype.h" 26 27 28 citrun::gl_font::gl_font(std::string const &font_path, citrun::gl_atlas &at) : 29 face(NULL), 30 num_glyphs(0), 31 sum_error(0), 32 sum_endpoints(0), 33 sum_fetch(0), 34 sum_bytes(0), 35 atlas(at), 36 acc(glyphy_arc_accumulator_create()) 37 { 38 FT_Init_FreeType(&ft_library); 39 FT_New_Face(ft_library, font_path.c_str(), /* face_index */ 0, &face); 40 } 41 42 citrun::gl_font::~gl_font() 43 { 44 glyphy_arc_accumulator_destroy(acc); 45 } 46 47 FT_Face 48 citrun::gl_font::get_face() const 49 { 50 return face; 51 } 52 53 citrun::gl_atlas & 54 citrun::gl_font::get_atlas() 55 { 56 return atlas; 57 } 58 59 60 static glyphy_bool_t 61 accumulate_endpoint(glyphy_arc_endpoint_t *endpoint, 62 std::vector<glyphy_arc_endpoint_t> *endpoints) 63 { 64 endpoints->push_back (*endpoint); 65 return true; 66 } 67 68 void 69 citrun::gl_font::encode_ft_glyph(unsigned int glyph_index, 70 double tolerance_per_em, 71 glyphy_rgba_t *buffer, 72 unsigned int buffer_len, 73 unsigned int *output_len, 74 unsigned int *nominal_width, 75 unsigned int *nominal_height, 76 glyphy_extents_t *extents, 77 double *advance) 78 { 79 /* Used for testing only */ 80 #define SCALE (1. * (1 << 0)) 81 82 if (FT_Err_Ok != FT_Load_Glyph (face, 83 glyph_index, 84 FT_LOAD_NO_BITMAP | 85 FT_LOAD_NO_HINTING | 86 FT_LOAD_NO_AUTOHINT | 87 FT_LOAD_NO_SCALE | 88 FT_LOAD_LINEAR_DESIGN | 89 FT_LOAD_IGNORE_TRANSFORM)) 90 errx(1, "Failed loading FreeType glyph"); 91 92 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) 93 errx(1, "FreeType loaded glyph format is not outline"); 94 95 unsigned int upem = face->units_per_EM; 96 double tolerance = upem * tolerance_per_em; /* in font design units */ 97 double faraway = double (upem) / (MIN_FONT_SIZE * M_SQRT2); 98 std::vector<glyphy_arc_endpoint_t> endpoints; 99 100 glyphy_arc_accumulator_reset (acc); 101 glyphy_arc_accumulator_set_tolerance (acc, tolerance); 102 glyphy_arc_accumulator_set_callback (acc, 103 (glyphy_arc_endpoint_accumulator_callback_t) accumulate_endpoint, 104 &endpoints); 105 106 if (FT_Err_Ok != glyphy_freetype(outline_decompose) (&face->glyph->outline, acc)) 107 errx(1, "Failed converting glyph outline to arcs"); 108 109 assert (glyphy_arc_accumulator_get_error (acc) <= tolerance); 110 111 if (endpoints.size ()) 112 { 113 #if 0 114 /* Technically speaking, we want the following code, 115 * however, crappy fonts have crappy flags. So we just 116 * fixup unconditionally... */ 117 if (face->glyph->outline.flags & FT_OUTLINE_EVEN_ODD_FILL) 118 glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false); 119 else if (face->glyph->outline.flags & FT_OUTLINE_REVERSE_FILL) 120 glyphy_outline_reverse (&endpoints[0], endpoints.size ()); 121 #else 122 glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false); 123 #endif 124 } 125 126 if (SCALE != 1.) 127 for (unsigned int i = 0; i < endpoints.size (); i++) 128 { 129 endpoints[i].p.x /= SCALE; 130 endpoints[i].p.y /= SCALE; 131 } 132 133 double avg_fetch_achieved; 134 if (!glyphy_arc_list_encode_blob (endpoints.size () ? &endpoints[0] : NULL, endpoints.size (), 135 buffer, 136 buffer_len, 137 faraway / SCALE, 138 4, /* UNUSED */ 139 &avg_fetch_achieved, 140 output_len, 141 nominal_width, 142 nominal_height, 143 extents)) 144 errx(1, "Failed encoding arcs"); 145 146 glyphy_extents_scale (extents, 1. / upem, 1. / upem); 147 glyphy_extents_scale (extents, SCALE, SCALE); 148 149 *advance = face->glyph->metrics.horiAdvance / (double) upem; 150 151 if (0) 152 LOGI ("gid%3u: endpoints%3d; err%3g%%; tex fetch%4.1f; mem%4.1fkb\n", 153 glyph_index, 154 (unsigned int) glyphy_arc_accumulator_get_num_endpoints (acc), 155 round (100 * glyphy_arc_accumulator_get_error (acc) / tolerance), 156 avg_fetch_achieved, 157 (*output_len * sizeof (glyphy_rgba_t)) / 1024.); 158 159 num_glyphs++; 160 sum_error += glyphy_arc_accumulator_get_error (acc) / tolerance; 161 sum_endpoints += glyphy_arc_accumulator_get_num_endpoints (acc); 162 sum_fetch += avg_fetch_achieved; 163 sum_bytes += (*output_len * sizeof (glyphy_rgba_t)); 164 } 165 166 void 167 citrun::gl_font::_upload_glyph(unsigned int glyph_index, 168 glyph_info_t *glyph_info) 169 { 170 glyphy_rgba_t buffer[4096 * 16]; 171 unsigned int output_len; 172 173 encode_ft_glyph(glyph_index, 174 TOLERANCE, 175 buffer, ARRAY_LEN (buffer), 176 &output_len, 177 &glyph_info->nominal_w, 178 &glyph_info->nominal_h, 179 &glyph_info->extents, 180 &glyph_info->advance); 181 182 glyph_info->is_empty = glyphy_extents_is_empty (&glyph_info->extents); 183 if (!glyph_info->is_empty) 184 atlas.alloc(buffer, output_len, 185 &glyph_info->atlas_x, &glyph_info->atlas_y); 186 } 187 188 void 189 citrun::gl_font::lookup_glyph(unsigned int glyph_index, 190 glyph_info_t *glyph_info) 191 { 192 if (glyph_cache.find(glyph_index) == glyph_cache.end()) { 193 _upload_glyph(glyph_index, glyph_info); 194 glyph_cache[glyph_index] = *glyph_info; 195 } else 196 *glyph_info = glyph_cache[glyph_index]; 197 } 198 199 void 200 citrun::gl_font::print_stats() 201 { 202 LOGI("%3d glyphs; avg num endpoints%6.2f; avg error%5.1f%%; avg tex fetch%5.2f; avg %5.2fkb per glyph\n", 203 num_glyphs, 204 (double) sum_endpoints / num_glyphs, 205 100. * sum_error / num_glyphs, 206 sum_fetch / num_glyphs, 207 sum_bytes / 1024. / num_glyphs); 208 }