Panda3D
|
00001 // Filename: freetypeFont.cxx 00002 // Created by: drose (07Sep03) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "freetypeFont.h" 00016 00017 #ifdef HAVE_FREETYPE 00018 00019 #include "config_pnmtext.h" 00020 #include "config_util.h" 00021 #include "config_express.h" 00022 #include "virtualFileSystem.h" 00023 00024 // This constant determines how big a particular point size font 00025 // appears to be. By convention, 10 points is 1 unit (e.g. 1 foot) 00026 // high. 00027 const PN_stdfloat FreetypeFont::_points_per_unit = 10.0f; 00028 00029 // A universal typographic convention. 00030 const PN_stdfloat FreetypeFont::_points_per_inch = 72.0f; 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: FreetypeFont::Constructor 00034 // Access: Protected 00035 // Description: 00036 //////////////////////////////////////////////////////////////////// 00037 FreetypeFont:: 00038 FreetypeFont() { 00039 _face = NULL; 00040 00041 _point_size = text_point_size; 00042 _requested_pixels_per_unit = text_pixels_per_unit; 00043 _tex_pixels_per_unit = text_pixels_per_unit; 00044 _requested_scale_factor = text_scale_factor; 00045 _scale_factor = text_scale_factor; 00046 _native_antialias = text_native_antialias; 00047 00048 _line_height = 1.0f; 00049 _space_advance = 0.25f; 00050 00051 _char_size = 0; 00052 _dpi = 0; 00053 _pixel_width = 0; 00054 _pixel_height = 0; 00055 } 00056 00057 //////////////////////////////////////////////////////////////////// 00058 // Function: FreetypeFont::Copy Constructor 00059 // Access: Protected 00060 // Description: 00061 //////////////////////////////////////////////////////////////////// 00062 FreetypeFont:: 00063 FreetypeFont(const FreetypeFont ©) : 00064 Namable(copy), 00065 _point_size(copy._point_size), 00066 _requested_pixels_per_unit(copy._requested_pixels_per_unit), 00067 _tex_pixels_per_unit(copy._tex_pixels_per_unit), 00068 _requested_scale_factor(copy._requested_scale_factor), 00069 _scale_factor(copy._scale_factor), 00070 _native_antialias(copy._native_antialias), 00071 _line_height(copy._line_height), 00072 _space_advance(copy._space_advance), 00073 _face(copy._face), 00074 _char_size(copy._char_size), 00075 _dpi(copy._dpi), 00076 _pixel_width(copy._pixel_width), 00077 _pixel_height(copy._pixel_height) 00078 { 00079 } 00080 00081 //////////////////////////////////////////////////////////////////// 00082 // Function: FreetypeFont::load_font 00083 // Access: Protected 00084 // Description: This method accepts the name of some font file 00085 // that FreeType can read, along with face_index, 00086 // indicating which font within the file to load 00087 // (usually 0). 00088 //////////////////////////////////////////////////////////////////// 00089 bool FreetypeFont:: 00090 load_font(const Filename &font_filename, int face_index) { 00091 unload_font(); 00092 _face = new FreetypeFace; 00093 if (!_face->_ft_ok) { 00094 pnmtext_cat.error() 00095 << "Unable to read font " << font_filename 00096 << ": FreeType library not initialized properly.\n"; 00097 unload_font(); 00098 return false; 00099 } 00100 00101 bool exists = false; 00102 int error; 00103 Filename path(font_filename); 00104 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00105 vfs->resolve_filename(path, get_model_path()); 00106 exists = vfs->read_file(path, _face->_font_data, true); 00107 if (exists) { 00108 FT_Face face; 00109 error = FT_New_Memory_Face(_face->_ft_library, 00110 (const FT_Byte *)_face->_font_data.data(), 00111 _face->_font_data.length(), 00112 face_index, &face); 00113 _face->set_face(face); 00114 } 00115 00116 bool okflag = false; 00117 if (!exists) { 00118 pnmtext_cat.error() 00119 << "Unable to find font file " << font_filename << "\n"; 00120 } else { 00121 if (error == FT_Err_Unknown_File_Format) { 00122 pnmtext_cat.error() 00123 << "Unable to read font " << font_filename << ": unknown file format.\n"; 00124 } else if (error) { 00125 pnmtext_cat.error() 00126 << "Unable to read font " << font_filename << ": invalid.\n"; 00127 00128 } else { 00129 okflag = reset_scale(); 00130 } 00131 } 00132 00133 if (!okflag) { 00134 unload_font(); 00135 } 00136 00137 return okflag; 00138 } 00139 00140 //////////////////////////////////////////////////////////////////// 00141 // Function: FreetypeFont::load_font 00142 // Access: Protected 00143 // Description: This method accepts a table of data representing 00144 // the font file, loaded from some source other than a 00145 // filename on disk. 00146 //////////////////////////////////////////////////////////////////// 00147 bool FreetypeFont:: 00148 load_font(const char *font_data, int data_length, int face_index) { 00149 unload_font(); 00150 _face = new FreetypeFace; 00151 00152 if (!_face->_ft_ok) { 00153 pnmtext_cat.error() 00154 << "Unable to read font: FreeType library not initialized properly.\n"; 00155 unload_font(); 00156 return false; 00157 } 00158 00159 int error; 00160 FT_Face face; 00161 error = FT_New_Memory_Face(_face->_ft_library, 00162 (const FT_Byte *)font_data, data_length, 00163 face_index, &face); 00164 _face->set_face(face); 00165 00166 bool okflag = false; 00167 if (error == FT_Err_Unknown_File_Format) { 00168 pnmtext_cat.error() 00169 << "Unable to read font: unknown file format.\n"; 00170 } else if (error) { 00171 pnmtext_cat.error() 00172 << "Unable to read font: invalid.\n"; 00173 00174 } else { 00175 okflag = reset_scale(); 00176 } 00177 00178 if (!okflag) { 00179 unload_font(); 00180 } 00181 00182 return okflag; 00183 } 00184 00185 //////////////////////////////////////////////////////////////////// 00186 // Function: FreetypeFont::unload_font 00187 // Access: Protected 00188 // Description: 00189 //////////////////////////////////////////////////////////////////// 00190 void FreetypeFont:: 00191 unload_font() { 00192 _face = NULL; 00193 } 00194 00195 //////////////////////////////////////////////////////////////////// 00196 // Function: FreetypeFont::load_glyph 00197 // Access: Protected 00198 // Description: Invokes Freetype to load and render the indicated 00199 // glyph into a bitmap. Returns true if successful, 00200 // false otherwise. 00201 //////////////////////////////////////////////////////////////////// 00202 bool FreetypeFont:: 00203 load_glyph(FT_Face face, int glyph_index, bool prerender) { 00204 int flags = FT_LOAD_RENDER; 00205 if (!_native_antialias) { 00206 flags |= FT_LOAD_MONOCHROME; 00207 } 00208 00209 if (!prerender) { 00210 // If we want to render as an outline font, don't pre-render it to 00211 // a bitmap. 00212 flags = 0; 00213 } 00214 00215 int error = FT_Load_Glyph(face, glyph_index, flags); 00216 if (error) { 00217 pnmtext_cat.error() 00218 << "Unable to render glyph " << glyph_index << "\n"; 00219 return false; 00220 } 00221 return true; 00222 } 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: FreetypeFont::copy_bitmap_to_pnmimage 00226 // Access: Protected 00227 // Description: Copies a bitmap as rendered by FreeType into a 00228 // PNMImage, so it can be rescaled. 00229 //////////////////////////////////////////////////////////////////// 00230 void FreetypeFont:: 00231 copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) { 00232 if (bitmap.pixel_mode == ft_pixel_mode_grays && 00233 bitmap.num_grays == (int)image.get_maxval() + 1) { 00234 // This is the easy case: we can copy the rendered glyph 00235 // directly into our image, one pixel at a time. 00236 unsigned char *buffer_row = bitmap.buffer; 00237 for (int yi = 0; yi < bitmap.rows; yi++) { 00238 for (int xi = 0; xi < bitmap.width; xi++) { 00239 image.set_gray_val(xi, yi, buffer_row[xi]); 00240 } 00241 buffer_row += bitmap.pitch; 00242 } 00243 00244 } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { 00245 // This is a little bit more work: we have to expand the 00246 // one-bit-per-pixel bitmap into a one-byte-per-pixel image. 00247 unsigned char *buffer_row = bitmap.buffer; 00248 for (int yi = 0; yi < bitmap.rows; yi++) { 00249 xelval maxval = image.get_maxval(); 00250 int bit = 0x80; 00251 unsigned char *b = buffer_row; 00252 for (int xi = 0; xi < bitmap.width; xi++) { 00253 if (*b & bit) { 00254 image.set_gray_val(xi, yi, maxval); 00255 } else { 00256 image.set_gray_val(xi, yi, 0); 00257 } 00258 bit >>= 1; 00259 if (bit == 0) { 00260 ++b; 00261 bit = 0x80; 00262 } 00263 } 00264 00265 buffer_row += bitmap.pitch; 00266 } 00267 00268 00269 } else if (bitmap.pixel_mode == ft_pixel_mode_grays) { 00270 // Here we must expand a grayscale pixmap with n levels of gray 00271 // into our 256-level texture. 00272 unsigned char *buffer_row = bitmap.buffer; 00273 for (int yi = 0; yi < bitmap.rows; yi++) { 00274 for (int xi = 0; xi < bitmap.width; xi++) { 00275 image.set_gray(xi, yi, (PN_stdfloat)buffer_row[xi] / (bitmap.num_grays - 1)); 00276 } 00277 buffer_row += bitmap.pitch; 00278 } 00279 00280 } else { 00281 pnmtext_cat.error() 00282 << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; 00283 } 00284 } 00285 00286 //////////////////////////////////////////////////////////////////// 00287 // Function: FreetypeFont::reset_scale 00288 // Access: Private 00289 // Description: Resets the font based on the current values for 00290 // _point_size, _tex_pixels_per_unit, and _scale_factor. 00291 // Returns true if successful, false otherwise. 00292 //////////////////////////////////////////////////////////////////// 00293 bool FreetypeFont:: 00294 reset_scale() { 00295 if (_face == NULL) { 00296 return false; 00297 } 00298 00299 // Get the face, without requesting a particular size yet (we'll 00300 // figure out the size in a second). 00301 FT_Face face = _face->acquire_face(0, 0, 0, 0); 00302 00303 // The font may be rendered larger (by a factor of _scale_factor), 00304 // and then reduced into the texture. Hence the difference between 00305 // _font_pixels_per_unit and _tex_pixels_per_unit. 00306 _tex_pixels_per_unit = _requested_pixels_per_unit; 00307 _scale_factor = _requested_scale_factor; 00308 _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor; 00309 00310 _pixel_height = 0; 00311 _pixel_width = 0; 00312 PN_stdfloat units_per_inch = (_points_per_inch / _points_per_unit); 00313 _dpi = (int)(_font_pixels_per_unit * units_per_inch); 00314 _char_size = (int)(_point_size * 64); 00315 00316 int error = FT_Set_Char_Size(face, _char_size, _char_size, _dpi, _dpi); 00317 if (error) { 00318 // If we were unable to set a particular char size, perhaps we 00319 // have a non-scalable font. Try to figure out the next larger 00320 // available size, or the largest size available if nothing is 00321 // larger. 00322 int desired_height = (int)(_font_pixels_per_unit * _point_size / _points_per_unit + 0.5f); 00323 int best_size = -1; 00324 int largest_size = -1; 00325 if (face->num_fixed_sizes > 0) { 00326 largest_size = 0; 00327 int best_diff = 0; 00328 for (int i = 0; i < face->num_fixed_sizes; i++) { 00329 int diff = face->available_sizes[i].height - desired_height; 00330 if (diff > 0 && (best_size == -1 || diff < best_diff)) { 00331 best_size = i; 00332 best_diff = diff; 00333 } 00334 if (face->available_sizes[i].height > face->available_sizes[largest_size].height) { 00335 largest_size = i; 00336 } 00337 } 00338 } 00339 if (best_size < 0) { 00340 best_size = largest_size; 00341 } 00342 00343 if (best_size >= 0) { 00344 _pixel_height = face->available_sizes[best_size].height; 00345 _pixel_width = face->available_sizes[best_size].width; 00346 error = FT_Set_Pixel_Sizes(face, _pixel_width, _pixel_height); 00347 if (!error) { 00348 _font_pixels_per_unit = _pixel_height * _points_per_unit / _point_size; 00349 _scale_factor = _font_pixels_per_unit / _tex_pixels_per_unit; 00350 00351 if (_scale_factor < 1.0) { 00352 // No point in enlarging a fixed-point font. 00353 _scale_factor = 1.0; 00354 _tex_pixels_per_unit = _font_pixels_per_unit; 00355 } 00356 } 00357 } 00358 } 00359 00360 if (error) { 00361 pnmtext_cat.warning() 00362 << "Unable to set " << get_name() 00363 << " to " << _point_size << "pt at " << _dpi << " dpi.\n"; 00364 _line_height = 1.0f; 00365 _face->release_face(face); 00366 return false; 00367 } 00368 00369 _line_height = face->size->metrics.height / (_font_pixels_per_unit * 64.0f); 00370 00371 // Determine the correct width for a space. 00372 error = FT_Load_Char(face, ' ', FT_LOAD_DEFAULT); 00373 if (error) { 00374 // Space isn't defined. Oh well. 00375 _space_advance = 0.25f * _line_height; 00376 00377 } else { 00378 _space_advance = face->glyph->advance.x / (_font_pixels_per_unit * 64.0f); 00379 } 00380 00381 _face->release_face(face); 00382 return true; 00383 } 00384 00385 #endif // HAVE_FREETYPE