Panda3D
freetypeFont.cxx
1 // Filename: freetypeFont.cxx
2 // Created by: drose (07Sep03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "freetypeFont.h"
16 
17 #ifdef HAVE_FREETYPE
18 
19 #include "config_pnmtext.h"
20 #include "config_util.h"
21 #include "config_express.h"
22 #include "virtualFileSystem.h"
23 
24 // This constant determines how big a particular point size font
25 // appears to be. By convention, 10 points is 1 unit (e.g. 1 foot)
26 // high.
27 const PN_stdfloat FreetypeFont::_points_per_unit = 10.0f;
28 
29 // A universal typographic convention.
30 const PN_stdfloat FreetypeFont::_points_per_inch = 72.0f;
31 
32 ////////////////////////////////////////////////////////////////////
33 // Function: FreetypeFont::Constructor
34 // Access: Protected
35 // Description:
36 ////////////////////////////////////////////////////////////////////
37 FreetypeFont::
38 FreetypeFont() {
39  _face = NULL;
40 
41  _point_size = text_point_size;
42  _requested_pixels_per_unit = text_pixels_per_unit;
43  _tex_pixels_per_unit = text_pixels_per_unit;
44  _requested_scale_factor = text_scale_factor;
45  _scale_factor = text_scale_factor;
46  _native_antialias = text_native_antialias;
47 
48  _line_height = 1.0f;
49  _space_advance = 0.25f;
50 
51  _char_size = 0;
52  _dpi = 0;
53  _pixel_width = 0;
54  _pixel_height = 0;
55 }
56 
57 ////////////////////////////////////////////////////////////////////
58 // Function: FreetypeFont::Copy Constructor
59 // Access: Protected
60 // Description:
61 ////////////////////////////////////////////////////////////////////
62 FreetypeFont::
63 FreetypeFont(const FreetypeFont &copy) :
64  Namable(copy),
65  _point_size(copy._point_size),
66  _requested_pixels_per_unit(copy._requested_pixels_per_unit),
67  _tex_pixels_per_unit(copy._tex_pixels_per_unit),
68  _requested_scale_factor(copy._requested_scale_factor),
69  _scale_factor(copy._scale_factor),
70  _native_antialias(copy._native_antialias),
71  _font_pixels_per_unit(copy._font_pixels_per_unit),
72  _line_height(copy._line_height),
73  _space_advance(copy._space_advance),
74  _face(copy._face),
75  _char_size(copy._char_size),
76  _dpi(copy._dpi),
77  _pixel_width(copy._pixel_width),
78  _pixel_height(copy._pixel_height)
79 {
80 }
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: FreetypeFont::load_font
84 // Access: Protected
85 // Description: This method accepts the name of some font file
86 // that FreeType can read, along with face_index,
87 // indicating which font within the file to load
88 // (usually 0).
89 ////////////////////////////////////////////////////////////////////
90 bool FreetypeFont::
91 load_font(const Filename &font_filename, int face_index) {
92  unload_font();
93  _face = new FreetypeFace;
94  if (!_face->_ft_ok) {
95  pnmtext_cat.error()
96  << "Unable to read font " << font_filename
97  << ": FreeType library not initialized properly.\n";
98  unload_font();
99  return false;
100  }
101 
102  bool exists = false;
103  int error;
104  Filename path(font_filename);
106  vfs->resolve_filename(path, get_model_path());
107  exists = vfs->read_file(path, _face->_font_data, true);
108  if (exists) {
109  FT_Face face;
110  error = FT_New_Memory_Face(_face->_ft_library,
111  (const FT_Byte *)_face->_font_data.data(),
112  _face->_font_data.length(),
113  face_index, &face);
114  _face->set_face(face);
115  }
116 
117  bool okflag = false;
118  if (!exists) {
119  pnmtext_cat.error()
120  << "Unable to find font file " << font_filename << "\n";
121  } else {
122  if (error == FT_Err_Unknown_File_Format) {
123  pnmtext_cat.error()
124  << "Unable to read font " << font_filename << ": unknown file format.\n";
125  } else if (error) {
126  pnmtext_cat.error()
127  << "Unable to read font " << font_filename << ": invalid.\n";
128 
129  } else {
130  okflag = reset_scale();
131  }
132  }
133 
134  if (!okflag) {
135  unload_font();
136  }
137 
138  return okflag;
139 }
140 
141 ////////////////////////////////////////////////////////////////////
142 // Function: FreetypeFont::load_font
143 // Access: Protected
144 // Description: This method accepts a table of data representing
145 // the font file, loaded from some source other than a
146 // filename on disk.
147 ////////////////////////////////////////////////////////////////////
148 bool FreetypeFont::
149 load_font(const char *font_data, int data_length, int face_index) {
150  unload_font();
151  _face = new FreetypeFace;
152 
153  if (!_face->_ft_ok) {
154  pnmtext_cat.error()
155  << "Unable to read font: FreeType library not initialized properly.\n";
156  unload_font();
157  return false;
158  }
159 
160  int error;
161  FT_Face face;
162  error = FT_New_Memory_Face(_face->_ft_library,
163  (const FT_Byte *)font_data, data_length,
164  face_index, &face);
165  _face->set_face(face);
166 
167  bool okflag = false;
168  if (error == FT_Err_Unknown_File_Format) {
169  pnmtext_cat.error()
170  << "Unable to read font: unknown file format.\n";
171  } else if (error) {
172  pnmtext_cat.error()
173  << "Unable to read font: invalid.\n";
174 
175  } else {
176  okflag = reset_scale();
177  }
178 
179  if (!okflag) {
180  unload_font();
181  }
182 
183  return okflag;
184 }
185 
186 ////////////////////////////////////////////////////////////////////
187 // Function: FreetypeFont::unload_font
188 // Access: Protected
189 // Description:
190 ////////////////////////////////////////////////////////////////////
191 void FreetypeFont::
192 unload_font() {
193  _face = NULL;
194 }
195 
196 ////////////////////////////////////////////////////////////////////
197 // Function: FreetypeFont::load_glyph
198 // Access: Protected
199 // Description: Invokes Freetype to load and render the indicated
200 // glyph into a bitmap. Returns true if successful,
201 // false otherwise.
202 ////////////////////////////////////////////////////////////////////
203 bool FreetypeFont::
204 load_glyph(FT_Face face, int glyph_index, bool prerender) {
205  int flags = FT_LOAD_RENDER;
206  if (!_native_antialias) {
207  flags |= FT_LOAD_MONOCHROME;
208  }
209 
210  if (!prerender) {
211  // If we want to render as an outline font, don't pre-render it to
212  // a bitmap.
213  flags = 0;
214  }
215 
216  int error = FT_Load_Glyph(face, glyph_index, flags);
217  if (error) {
218  pnmtext_cat.error()
219  << "Unable to render glyph " << glyph_index << "\n";
220  return false;
221  }
222  return true;
223 }
224 
225 ////////////////////////////////////////////////////////////////////
226 // Function: FreetypeFont::copy_bitmap_to_pnmimage
227 // Access: Protected
228 // Description: Copies a bitmap as rendered by FreeType into a
229 // PNMImage, so it can be rescaled.
230 ////////////////////////////////////////////////////////////////////
231 void FreetypeFont::
232 copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) {
233  if (bitmap.pixel_mode == ft_pixel_mode_grays &&
234  bitmap.num_grays == (int)image.get_maxval() + 1) {
235  // This is the easy case: we can copy the rendered glyph
236  // directly into our image, one pixel at a time.
237  unsigned char *buffer_row = bitmap.buffer;
238  for (int yi = 0; yi < bitmap.rows; yi++) {
239  for (int xi = 0; xi < bitmap.width; xi++) {
240  image.set_gray_val(xi, yi, buffer_row[xi]);
241  }
242  buffer_row += bitmap.pitch;
243  }
244 
245  } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
246  // This is a little bit more work: we have to expand the
247  // one-bit-per-pixel bitmap into a one-byte-per-pixel image.
248  unsigned char *buffer_row = bitmap.buffer;
249  for (int yi = 0; yi < bitmap.rows; yi++) {
250  xelval maxval = image.get_maxval();
251  int bit = 0x80;
252  unsigned char *b = buffer_row;
253  for (int xi = 0; xi < bitmap.width; xi++) {
254  if (*b & bit) {
255  image.set_gray_val(xi, yi, maxval);
256  } else {
257  image.set_gray_val(xi, yi, 0);
258  }
259  bit >>= 1;
260  if (bit == 0) {
261  ++b;
262  bit = 0x80;
263  }
264  }
265 
266  buffer_row += bitmap.pitch;
267  }
268 
269 
270  } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
271  // Here we must expand a grayscale pixmap with n levels of gray
272  // into our 256-level texture.
273  unsigned char *buffer_row = bitmap.buffer;
274  for (int yi = 0; yi < bitmap.rows; yi++) {
275  for (int xi = 0; xi < bitmap.width; xi++) {
276  image.set_gray(xi, yi, (PN_stdfloat)buffer_row[xi] / (bitmap.num_grays - 1));
277  }
278  buffer_row += bitmap.pitch;
279  }
280 
281  } else {
282  pnmtext_cat.error()
283  << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
284  }
285 }
286 
287 ////////////////////////////////////////////////////////////////////
288 // Function: FreetypeFont::reset_scale
289 // Access: Private
290 // Description: Resets the font based on the current values for
291 // _point_size, _tex_pixels_per_unit, and _scale_factor.
292 // Returns true if successful, false otherwise.
293 ////////////////////////////////////////////////////////////////////
294 bool FreetypeFont::
295 reset_scale() {
296  if (_face == NULL) {
297  return false;
298  }
299 
300  // Get the face, without requesting a particular size yet (we'll
301  // figure out the size in a second).
302  FT_Face face = _face->acquire_face(0, 0, 0, 0);
303 
304  // The font may be rendered larger (by a factor of _scale_factor),
305  // and then reduced into the texture. Hence the difference between
306  // _font_pixels_per_unit and _tex_pixels_per_unit.
307  _tex_pixels_per_unit = _requested_pixels_per_unit;
308  _scale_factor = _requested_scale_factor;
309  _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor;
310 
311  _pixel_height = 0;
312  _pixel_width = 0;
313  PN_stdfloat units_per_inch = (_points_per_inch / _points_per_unit);
314  _dpi = (int)(_font_pixels_per_unit * units_per_inch);
315  _char_size = (int)(_point_size * 64);
316 
317  int error = FT_Set_Char_Size(face, _char_size, _char_size, _dpi, _dpi);
318  if (error) {
319  // If we were unable to set a particular char size, perhaps we
320  // have a non-scalable font. Try to figure out the next larger
321  // available size, or the largest size available if nothing is
322  // larger.
323  int desired_height = (int)(_font_pixels_per_unit * _point_size / _points_per_unit + 0.5f);
324  int best_size = -1;
325  int largest_size = -1;
326  if (face->num_fixed_sizes > 0) {
327  largest_size = 0;
328  int best_diff = 0;
329  for (int i = 0; i < face->num_fixed_sizes; i++) {
330  int diff = face->available_sizes[i].height - desired_height;
331  if (diff > 0 && (best_size == -1 || diff < best_diff)) {
332  best_size = i;
333  best_diff = diff;
334  }
335  if (face->available_sizes[i].height > face->available_sizes[largest_size].height) {
336  largest_size = i;
337  }
338  }
339  }
340  if (best_size < 0) {
341  best_size = largest_size;
342  }
343 
344  if (best_size >= 0) {
345  _pixel_height = face->available_sizes[best_size].height;
346  _pixel_width = face->available_sizes[best_size].width;
347  error = FT_Set_Pixel_Sizes(face, _pixel_width, _pixel_height);
348  if (!error) {
349  _font_pixels_per_unit = _pixel_height * _points_per_unit / _point_size;
350  _scale_factor = _font_pixels_per_unit / _tex_pixels_per_unit;
351 
352  if (_scale_factor < 1.0) {
353  // No point in enlarging a fixed-point font.
354  _scale_factor = 1.0;
355  _tex_pixels_per_unit = _font_pixels_per_unit;
356  }
357  }
358  }
359  }
360 
361  if (error) {
362  pnmtext_cat.warning()
363  << "Unable to set " << get_name()
364  << " to " << _point_size << "pt at " << _dpi << " dpi.\n";
365  _line_height = 1.0f;
366  _face->release_face(face);
367  return false;
368  }
369 
370  _line_height = face->size->metrics.height / (_font_pixels_per_unit * 64.0f);
371 
372  // Determine the correct width for a space.
373  error = FT_Load_Char(face, ' ', FT_LOAD_DEFAULT);
374  if (error) {
375  // Space isn't defined. Oh well.
376  _space_advance = 0.25f * _line_height;
377 
378  } else {
379  _space_advance = face->glyph->advance.x / (_font_pixels_per_unit * 64.0f);
380  }
381 
382  _face->release_face(face);
383  return true;
384 }
385 
386 #endif // HAVE_FREETYPE
void set_gray_val(int x, int y, xelval gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:568
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const string &default_extension=string()) const
Searches the given search path for the filename.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
A base class for all things which can have a name.
Definition: namable.h:29
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
xelval get_maxval() const
Returns the maximum channel value allowable for any pixel in this image; for instance, 255 for a typical 8-bit-per-channel image.
void set_gray(int x, int y, float gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:994
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.