citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

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 }