Panda3D
freetypeFont.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file freetypeFont.cxx
10  * @author drose
11  * @date 2003-09-07
12  */
13 
14 #include "freetypeFont.h"
15 
16 #ifdef HAVE_FREETYPE
17 
18 #include "config_pnmtext.h"
19 #include "config_putil.h"
20 #include "config_express.h"
21 #include "virtualFileSystem.h"
22 #include "nurbsCurveEvaluator.h"
23 #include "nurbsCurveResult.h"
24 
25 #undef interface // I don't know where this symbol is defined, but it interferes with FreeType.
26 #include FT_OUTLINE_H
27 
28 using std::istream;
29 using std::ostream;
30 using std::string;
31 
32 // This constant determines how big a particular point size font appears to
33 // be. By convention, 10 points is 1 unit (e.g. 1 foot) high.
34 const PN_stdfloat FreetypeFont::_points_per_unit = 10.0f;
35 
36 // A universal typographic convention.
37 const PN_stdfloat FreetypeFont::_points_per_inch = 72.0f;
38 
39 /**
40  *
41  */
42 FreetypeFont::
43 FreetypeFont() {
44  _face = nullptr;
45 
46  _point_size = text_point_size;
47  _requested_pixels_per_unit = text_pixels_per_unit;
48  _tex_pixels_per_unit = text_pixels_per_unit;
49  _requested_scale_factor = text_scale_factor;
50  _scale_factor = text_scale_factor;
51  _native_antialias = text_native_antialias;
52  _winding_order = WO_default;
53 
54  _line_height = 1.0f;
55  _space_advance = 0.25f;
56 
57  _char_size = 0;
58  _dpi = 0;
59  _pixel_width = 0;
60  _pixel_height = 0;
61 }
62 
63 /**
64  *
65  */
66 FreetypeFont::
67 FreetypeFont(const FreetypeFont &copy) :
68  Namable(copy),
69  _point_size(copy._point_size),
70  _requested_pixels_per_unit(copy._requested_pixels_per_unit),
71  _tex_pixels_per_unit(copy._tex_pixels_per_unit),
72  _requested_scale_factor(copy._requested_scale_factor),
73  _scale_factor(copy._scale_factor),
74  _native_antialias(copy._native_antialias),
75  _font_pixels_per_unit(copy._font_pixels_per_unit),
76  _winding_order(copy._winding_order),
77  _line_height(copy._line_height),
78  _space_advance(copy._space_advance),
79  _face(copy._face),
80  _char_size(copy._char_size),
81  _dpi(copy._dpi),
82  _pixel_width(copy._pixel_width),
83  _pixel_height(copy._pixel_height)
84 {
85 }
86 
87 /**
88  * This method accepts the name of some font file that FreeType can read,
89  * along with face_index, indicating which font within the file to load
90  * (usually 0).
91  */
92 bool FreetypeFont::
93 load_font(const Filename &font_filename, int face_index) {
94  unload_font();
95  _face = new FreetypeFace;
96  if (!_face->_ft_ok) {
97  pnmtext_cat.error()
98  << "Unable to read font " << font_filename
99  << ": FreeType library not initialized properly.\n";
100  unload_font();
101  return false;
102  }
103 
104  bool exists = false;
105  int error;
106  Filename path(font_filename);
108  vfs->resolve_filename(path, get_model_path());
109  exists = vfs->read_file(path, _face->_font_data, true);
110  if (exists) {
111  FT_Face face = 0;
112  error = FT_New_Memory_Face(_face->_ft_library,
113  (const FT_Byte *)_face->_font_data.data(),
114  _face->_font_data.length(),
115  face_index, &face);
116  if (face) {
117  _face->set_face(face);
118  }
119  }
120 
121  bool okflag = false;
122  if (!exists) {
123  pnmtext_cat.error()
124  << "Unable to find font file " << font_filename << "\n";
125  } else {
126  if (error == FT_Err_Unknown_File_Format) {
127  pnmtext_cat.error()
128  << "Unable to read font " << font_filename << ": unknown file format.\n";
129  } else if (error) {
130  pnmtext_cat.error()
131  << "Unable to read font " << font_filename << ": invalid.\n";
132 
133  } else {
134  okflag = reset_scale();
135  }
136  }
137 
138  if (!okflag) {
139  unload_font();
140  }
141 
142  return okflag;
143 }
144 
145 /**
146  * This method accepts a table of data representing the font file, loaded from
147  * some source other than a filename on disk.
148  */
149 bool FreetypeFont::
150 load_font(const char *font_data, int data_length, int face_index) {
151  unload_font();
152  _face = new FreetypeFace;
153 
154  if (!_face->_ft_ok) {
155  pnmtext_cat.error()
156  << "Unable to read font: FreeType library not initialized properly.\n";
157  unload_font();
158  return false;
159  }
160 
161  int error;
162  FT_Face face;
163  error = FT_New_Memory_Face(_face->_ft_library,
164  (const FT_Byte *)font_data, data_length,
165  face_index, &face);
166  _face->set_face(face);
167 
168  bool okflag = false;
169  if (error == FT_Err_Unknown_File_Format) {
170  pnmtext_cat.error()
171  << "Unable to read font: unknown file format.\n";
172  } else if (error) {
173  pnmtext_cat.error()
174  << "Unable to read font: invalid.\n";
175 
176  } else {
177  okflag = reset_scale();
178  }
179 
180  if (!okflag) {
181  unload_font();
182  }
183 
184  return okflag;
185 }
186 
187 /**
188  *
189  */
190 void FreetypeFont::
191 unload_font() {
192  _face = nullptr;
193 }
194 
195 /**
196  * Returns the WindingOrder value associated with the given string
197  * representation, or WO_invalid if the string does not match any known
198  * WindingOrder value.
199  */
200 FreetypeFont::WindingOrder FreetypeFont::
201 string_winding_order(const string &string) {
202  if (cmp_nocase_uh(string, "default") == 0) {
203  return WO_default;
204  } else if (cmp_nocase_uh(string, "left") == 0) {
205  return WO_left;
206  } else if (cmp_nocase_uh(string, "right") == 0) {
207  return WO_right;
208  } else {
209  return WO_invalid;
210  }
211 }
212 
213 /**
214  * Invokes Freetype to load and render the indicated glyph into a bitmap.
215  * Returns true if successful, false otherwise.
216  */
217 bool FreetypeFont::
218 load_glyph(FT_Face face, int glyph_index, bool prerender) {
219  int flags = FT_LOAD_RENDER;
220  if (!_native_antialias) {
221  flags |= FT_LOAD_MONOCHROME;
222  }
223 
224  if (!prerender) {
225  // If we want to render as an outline font, don't pre-render it to a
226  // bitmap.
227  flags = 0;
228  }
229 
230  int error = FT_Load_Glyph(face, glyph_index, flags);
231  if (error) {
232  pnmtext_cat.error()
233  << "Unable to render glyph " << glyph_index << "\n";
234  return false;
235  }
236  return true;
237 }
238 
239 /**
240  * Copies a bitmap as rendered by FreeType into a PNMImage, so it can be
241  * rescaled.
242  */
243 void FreetypeFont::
244 copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) {
245  if (bitmap.pixel_mode == ft_pixel_mode_grays &&
246  bitmap.num_grays == (int)image.get_maxval() + 1) {
247  // This is the easy case: we can copy the rendered glyph directly into our
248  // image, one pixel at a time.
249  unsigned char *buffer_row = bitmap.buffer;
250  for (int yi = 0; yi < (int)bitmap.rows; yi++) {
251  for (int xi = 0; xi < (int)bitmap.width; xi++) {
252  image.set_gray_val(xi, yi, buffer_row[xi]);
253  }
254  buffer_row += bitmap.pitch;
255  }
256 
257  } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
258  // This is a little bit more work: we have to expand the one-bit-per-pixel
259  // bitmap into a one-byte-per-pixel image.
260  unsigned char *buffer_row = bitmap.buffer;
261  for (int yi = 0; yi < (int)bitmap.rows; yi++) {
262  xelval maxval = image.get_maxval();
263  int bit = 0x80;
264  unsigned char *b = buffer_row;
265  for (int xi = 0; xi < (int)bitmap.width; xi++) {
266  if (*b & bit) {
267  image.set_gray_val(xi, yi, maxval);
268  } else {
269  image.set_gray_val(xi, yi, 0);
270  }
271  bit >>= 1;
272  if (bit == 0) {
273  ++b;
274  bit = 0x80;
275  }
276  }
277 
278  buffer_row += bitmap.pitch;
279  }
280 
281 
282  } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
283  // Here we must expand a grayscale pixmap with n levels of gray into our
284  // 256-level texture.
285  unsigned char *buffer_row = bitmap.buffer;
286  for (int yi = 0; yi < (int)bitmap.rows; yi++) {
287  for (int xi = 0; xi < (int)bitmap.width; xi++) {
288  image.set_gray(xi, yi, (PN_stdfloat)buffer_row[xi] / (bitmap.num_grays - 1));
289  }
290  buffer_row += bitmap.pitch;
291  }
292 
293  } else {
294  pnmtext_cat.error()
295  << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
296  }
297 }
298 
299 /**
300  * Resets the font based on the current values for _point_size,
301  * _tex_pixels_per_unit, and _scale_factor. Returns true if successful, false
302  * otherwise.
303  */
304 bool FreetypeFont::
305 reset_scale() {
306  if (_face == nullptr) {
307  return false;
308  }
309 
310  // Get the face, without requesting a particular size yet (we'll figure out
311  // the size in a second).
312  FT_Face face = _face->acquire_face(0, 0, 0, 0);
313 
314  // The font may be rendered larger (by a factor of _scale_factor), and then
315  // reduced into the texture. Hence the difference between
316  // _font_pixels_per_unit and _tex_pixels_per_unit.
317  _tex_pixels_per_unit = _requested_pixels_per_unit;
318  _scale_factor = _requested_scale_factor;
319  _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor;
320 
321  _pixel_height = 0;
322  _pixel_width = 0;
323  PN_stdfloat units_per_inch = (_points_per_inch / _points_per_unit);
324  _dpi = (int)(_font_pixels_per_unit * units_per_inch);
325  _char_size = (int)(_point_size * 64);
326 
327  int error = FT_Set_Char_Size(face, _char_size, _char_size, _dpi, _dpi);
328  if (error) {
329  // If we were unable to set a particular char size, perhaps we have a non-
330  // scalable font. Try to figure out the next larger available size, or
331  // the largest size available if nothing is larger.
332  int desired_height = (int)(_font_pixels_per_unit * _point_size / _points_per_unit + 0.5f);
333  int best_size = -1;
334  int largest_size = -1;
335  if (face->num_fixed_sizes > 0) {
336  largest_size = 0;
337  int best_diff = 0;
338  for (int i = 0; i < face->num_fixed_sizes; i++) {
339  int diff = face->available_sizes[i].height - desired_height;
340  if (diff > 0 && (best_size == -1 || diff < best_diff)) {
341  best_size = i;
342  best_diff = diff;
343  }
344  if (face->available_sizes[i].height > face->available_sizes[largest_size].height) {
345  largest_size = i;
346  }
347  }
348  }
349  if (best_size < 0) {
350  best_size = largest_size;
351  }
352 
353  if (best_size >= 0) {
354  _pixel_height = face->available_sizes[best_size].height;
355  _pixel_width = face->available_sizes[best_size].width;
356  error = FT_Set_Pixel_Sizes(face, _pixel_width, _pixel_height);
357  if (!error) {
358  _font_pixels_per_unit = _pixel_height * _points_per_unit / _point_size;
359  _scale_factor = _font_pixels_per_unit / _tex_pixels_per_unit;
360 
361  if (_scale_factor < 1.0) {
362  // No point in enlarging a fixed-point font.
363  _scale_factor = 1.0;
364  _tex_pixels_per_unit = _font_pixels_per_unit;
365  }
366  }
367  }
368  }
369 
370  if (error) {
371  pnmtext_cat.warning()
372  << "Unable to set " << get_name()
373  << " to " << _point_size << "pt at " << _dpi << " dpi.\n";
374  _line_height = 1.0f;
375  _face->release_face(face);
376  return false;
377  }
378 
379  _line_height = face->size->metrics.height / (_font_pixels_per_unit * 64.0f);
380 
381  // Determine the correct width for a space.
382  error = FT_Load_Char(face, ' ', FT_LOAD_DEFAULT);
383  if (error) {
384  // Space isn't defined. Oh well.
385  _space_advance = 0.25f * _line_height;
386 
387  } else {
388  _space_advance = face->glyph->advance.x / (_font_pixels_per_unit * 64.0f);
389  }
390 
391  _face->release_face(face);
392  return true;
393 }
394 
395 /**
396  * Renders a signed distance field to the PNMImage based on the contours.
397  */
398 void FreetypeFont::
399 render_distance_field(PNMImage &image, int outline, int min_x, int min_y) {
400  Contours::const_iterator ci;
401 
402  PN_stdfloat offset_x = -outline / _tex_pixels_per_unit;
403  PN_stdfloat offset_y = (image.get_y_size() - 1 - outline) / _tex_pixels_per_unit;;
404 
405  offset_x += min_x / (64.0f * _font_pixels_per_unit);
406  offset_y += min_y / (64.0f * _font_pixels_per_unit);
407 
408  PN_stdfloat scale = _tex_pixels_per_unit / (outline * 2);
409 
410  for (int y = 0; y < image.get_y_size(); ++y) {
411  LPoint2 p(0, offset_y - (y / _tex_pixels_per_unit));
412 
413  for (int x = 0; x < image.get_x_size(); ++x) {
414  p[0] = offset_x + (x / _tex_pixels_per_unit);
415 
416  PN_stdfloat min_dist_sq = 100000000;
417  int winding_number = 0;
418 
419  for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
420  // Find the shortest distance between this point and the contour.
421  // Also keep track of the winding number, so we will know whether this
422  // point is inside or outside the polygon.
423  const Contour &contour = (*ci);
424 
425  for (size_t i = 1; i < contour._points.size(); ++i) {
426  const LPoint2 &begin = contour._points[i - 1]._p;
427  const LPoint2 &end = contour._points[i]._p;
428  PN_stdfloat radius = contour._points[i]._radius;
429 
430  LVector2 v = end - begin;
431  PN_stdfloat length_sq = v.length_squared();
432  PN_stdfloat dist_sq;
433 
434  if (length_sq == 0) {
435  dist_sq = (p - begin).length_squared();
436 
437  } else if (radius != 0) {
438  // Circular arc approximation.
439  LVector2 v1 = begin - contour._points[i]._center;
440  LVector2 v2 = end - contour._points[i]._center;
441  LVector2 vp = p - contour._points[i]._center;
442  PN_stdfloat dist_to_center = vp.length();
443  vp /= dist_to_center;
444  v1 /= radius;
445  v2 /= radius;
446  PN_stdfloat range = v1.dot(v2);
447  if (vp.dot(v1) > range && vp.dot(v2) > range) {
448  dist_sq = dist_to_center - radius;
449  bool inside = dist_sq < 0;
450  dist_sq *= dist_sq;
451 
452  // if (v1[0] * vp[1] - vp[0] * v1[1] < 0 && v2[0] * vp[1] -
453  // vp[0] * v2[1] < 0) { if (v1.signed_angle_deg(vp) <
454  // v1.signed_angle_deg(v2) && v1.signed_angle_deg(vp) > 0) {
455  if (begin[1] <= p[1]) {
456  if (end[1] > p[1]) {
457  if (inside != (v[0] * v1[1] > v[1] * v1[0])) {
458  ++winding_number;
459  }
460  }
461  } else {
462  if (end[1] <= p[1]) {
463  if (inside == (v[0] * v1[1] > v[1] * v1[0])) {
464  --winding_number;
465  }
466  }
467  }
468 
469  } else {
470  dist_sq = std::min((p - begin).length_squared(), (p - end).length_squared());
471  if (begin[1] <= p[1]) {
472  if (end[1] > p[1]) {
473  if ((v[0] * (p[1] - begin[1]) > v[1] * (p[0] - begin[0]))) {
474  ++winding_number;
475  }
476  }
477  } else {
478  if (end[1] <= p[1]) {
479  if ((v[0] * (p[1] - begin[1]) < v[1] * (p[0] - begin[0]))) {
480  --winding_number;
481  }
482  }
483  }
484  }
485 
486  } else {
487  // Just a straight line.
488  if (begin[1] <= p[1]) {
489  if (end[1] > p[1]) {
490  if ((v[0] * (p[1] - begin[1]) > v[1] * (p[0] - begin[0]))) {
491  ++winding_number;
492  }
493  }
494  } else {
495  if (end[1] <= p[1]) {
496  if ((v[0] * (p[1] - begin[1]) < v[1] * (p[0] - begin[0]))) {
497  --winding_number;
498  }
499  }
500  }
501 
502  PN_stdfloat t = v.dot(p - begin) / length_sq;
503  if (t <= 0.0) {
504  dist_sq = (p - begin).length_squared();
505  } else if (t >= 1.0) {
506  dist_sq = (p - end).length_squared();
507  } else {
508  dist_sq = (p - (begin + v * t)).length_squared();
509  }
510  }
511 
512  min_dist_sq = std::min(min_dist_sq, dist_sq);
513  }
514  }
515  // Determine the sign based on whether we're inside the contour.
516  int sign = (winding_number != 0) ? 1 : -1;
517 
518  PN_stdfloat signed_dist = csqrt(min_dist_sq) * sign;
519  image.set_gray(x, y, signed_dist * scale + (PN_stdfloat)0.5);
520  }
521  }
522 }
523 
524 /**
525  * Ask FreeType to extract the contours out of the outline description.
526  */
527 void FreetypeFont::
528 decompose_outline(FT_Outline &outline) {
529  FT_Outline_Funcs funcs;
530  memset(&funcs, 0, sizeof(funcs));
531  funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
532  funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
533  funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
534  funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
535 
536  WindingOrder wo = _winding_order;
537  if (wo == WO_default) {
538  // If we weren't told an explicit winding order, ask FreeType to figure it
539  // out. Sometimes it appears to guess wrong.
540 #ifdef FT_ORIENTATION_FILL_RIGHT
541  if (FT_Outline_Get_Orientation(&outline) == FT_ORIENTATION_FILL_RIGHT) {
542  wo = WO_right;
543  } else {
544  wo = WO_left;
545  }
546 #else
547  // Hmm. Assign a right-winding (TTF) orientation if FreeType can't tell
548  // us.
549  wo = WO_right;
550 #endif // FT_ORIENTATION_FILL_RIGHT
551  }
552 
553  if (wo != WO_left) {
554  FT_Outline_Reverse(&outline);
555  }
556 
557  _contours.clear();
558  FT_Outline_Decompose(&outline, &funcs, (void *)this);
559 }
560 
561 /**
562  * A callback from FT_Outline_Decompose(). It marks the beginning of a new
563  * contour.
564  */
565 int FreetypeFont::
566 outline_move_to(const FT_Vector *to, void *user) {
567  FreetypeFont *self = (FreetypeFont *)user;
568 
569  // Convert from 26.6 pixel units to Panda units.
570  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
571  LPoint2 p = LPoint2(to->x, to->y) * scale;
572 
573  if (self->_contours.empty() ||
574  !self->_contours.back()._points.empty()) {
575  self->_contours.push_back(Contour());
576  }
577  self->_q = p;
578  return 0;
579 }
580 
581 /**
582  * A callback from FT_Outline_Decompose(). It marks a straight line in the
583  * contour.
584  */
585 int FreetypeFont::
586 outline_line_to(const FT_Vector *to, void *user) {
587  FreetypeFont *self = (FreetypeFont *)user;
588  nassertr(!self->_contours.empty(), 1);
589 
590  // Convert from 26.6 pixel units to Panda units.
591  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
592  LPoint2 p = LPoint2(to->x, to->y) * scale;
593 
594  // Compute the tangent: this is just the vector from the last point.
595  LVector2 t = (p - self->_q);
596  t.normalize();
597 
598  if (self->_contours.back()._points.empty()) {
599  self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2::zero(), t));
600  } else {
601  self->_contours.back()._points.back().connect_to(t);
602  }
603 
604  self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2::zero()));
605  self->_q = p;
606  return 0;
607 }
608 
609 /**
610  * A callback from FT_Outline_Decompose(). It marks a parabolic (3rd-order)
611  * Bezier curve in the contour.
612  */
613 int FreetypeFont::
614 outline_conic_to(const FT_Vector *control,
615  const FT_Vector *to, void *user) {
616  FreetypeFont *self = (FreetypeFont *)user;
617  nassertr(!self->_contours.empty(), 1);
618 
619  // Convert from 26.6 pixel units to Panda units.
620  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
621 
622  LPoint2 c = LPoint2(control->x, control->y) * scale;
623  LPoint2 p = LPoint2(to->x, to->y) * scale;
624 
625  // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
627  nce.local_object();
628  nce.set_order(3);
629  nce.reset(3);
630  nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
631  nce.set_vertex(1, LVecBase3(c[0], c[1], 0.0f));
632  nce.set_vertex(2, LVecBase3(p[0], p[1], 0.0f));
633 
634  self->_q = p;
635 
636  PT(NurbsCurveResult) ncr = nce.evaluate();
637  return self->outline_nurbs(ncr);
638 }
639 
640 /**
641  * A callback from FT_Outline_Decompose(). It marks a cubic (4th-order)
642  * Bezier curve in the contour.
643  */
644 int FreetypeFont::
645 outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
646  const FT_Vector *to, void *user) {
647  FreetypeFont *self = (FreetypeFont *)user;
648  nassertr(!self->_contours.empty(), 1);
649 
650  // Convert from 26.6 pixel units to Panda units.
651  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
652 
653  LPoint2 c1 = LPoint2(control1->x, control1->y) * scale;
654  LPoint2 c2 = LPoint2(control2->x, control2->y) * scale;
655  LPoint2 p = LPoint2(to->x, to->y) * scale;
656 
657  // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
659  nce.local_object();
660  nce.set_order(4);
661  nce.reset(4);
662  nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
663  nce.set_vertex(1, LVecBase3(c1[0], c1[1], 0.0f));
664  nce.set_vertex(2, LVecBase3(c2[0], c2[1], 0.0f));
665  nce.set_vertex(3, LVecBase3(p[0], p[1], 0.0f));
666 
667  self->_q = p;
668 
669  PT(NurbsCurveResult) ncr = nce.evaluate();
670  return self->outline_nurbs(ncr);
671 }
672 
673 /**
674  * Called internally by outline_cubic_to() and outline_conic_to().
675  */
676 int FreetypeFont::
677 outline_nurbs(NurbsCurveResult *ncr) {
678  // Sample it down so that the lines approximate the curve to within a
679  // "pixel."
680  ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
681 
682  int num_samples = ncr->get_num_samples();
683 
684  bool needs_connect = false;
685  int start = 1;
686  if (_contours.back()._points.empty()) {
687  // If we haven't got the first point of this contour yet, we must add it
688  // now.
689  start = 0;
690  } else {
691  needs_connect = true;
692  }
693 
694  for (int i = start; i < num_samples; ++i) {
695  PN_stdfloat st = ncr->get_sample_t(i);
696  const LPoint3 &p = ncr->get_sample_point(i);
697 
698  PN_stdfloat st0 = st, st1 = st;
699  if (i > 0) {
700  st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
701  }
702  if (i < num_samples - 1) {
703  st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
704  }
705  // Compute the tangent by deltaing nearby points. Don't evaluate the
706  // tangent from the NURBS, since that doesn't appear to be reliable.
707  LPoint3 p0, p1;
708  ncr->eval_point(st0, p0);
709  ncr->eval_point(st1, p1);
710  LVector3 t = p1 - p0;
711  t.normalize();
712 
713  if (needs_connect) {
714  _contours.back()._points.back().connect_to(LVector2(t[0], t[1]));
715  needs_connect = false;
716  }
717 
718  _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
719 
720  if (i > 0) {
721  // Approximate the curve using a circular arc. This is used in the
722  // signed distance field generation code. We do this by sampling a
723  // point in the middle of the segment and calculating the circle that
724  // goes through the three points.
725  LPoint2 v1 = ncr->get_sample_point(i - 1).get_xy();
726  LPoint3 v2;
727  ncr->eval_point((ncr->get_sample_t(i - 1) + ncr->get_sample_t(i)) / 2, v2);
728  PN_stdfloat temp = v1.length_squared();
729  PN_stdfloat bc = (v2[0]*v2[0] + v2[1]*v2[1] - temp) * (PN_stdfloat)0.5f;
730  PN_stdfloat cd = (temp - p[0]*p[0] - p[1]*p[1]) * (PN_stdfloat)0.5f;
731  PN_stdfloat det = (v2[0]-v1[0])*(v1[1]-p[1])-(v1[0]-p[0])*(v2[1]-v1[1]);
732  if (!IS_NEARLY_ZERO(det)) {
733  LPoint2 center;
734  center[0] = (bc*(v1[1]-p[1])-cd*(v2[1]-v1[1]));
735  center[1] = ((v2[0]-v1[0])*cd-(v1[0]-p[0])*bc);
736  center /= det;
737  _contours.back()._points.back()._center = center;
738  _contours.back()._points.back()._radius = (center - v1).length();
739  }
740  }
741  }
742 
743  return 0;
744 }
745 
746 /**
747  *
748  */
749 ostream &
750 operator << (ostream &out, FreetypeFont::WindingOrder wo) {
751  switch (wo) {
752  case FreetypeFont::WO_default:
753  return out << "default";
754  case FreetypeFont::WO_left:
755  return out << "left";
756  case FreetypeFont::WO_right:
757  return out << "right";
758 
759  case FreetypeFont::WO_invalid:
760  return out << "invalid";
761  }
762 
763  return out << "(**invalid FreetypeFont::WindingOrder(" << (int)wo << ")**)";
764 }
765 
766 /**
767  *
768  */
769 istream &
770 operator >> (istream &in, FreetypeFont::WindingOrder &wo) {
771  string word;
772  in >> word;
773 
774  wo = FreetypeFont::string_winding_order(word);
775  return in;
776 }
777 
778 
779 #endif // HAVE_FREETYPE
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
A base class for all things which can have a name.
Definition: namable.h:26
This class is an abstraction for evaluating NURBS curves.
void reset(int num_vertices)
Resets all the vertices and knots to their default values, and sets the curve up with the indicated n...
void set_vertex(int i, const LVecBase4 &vertex)
Sets the nth control vertex of the curve, as a vertex in 4-d homogeneous space.
void set_order(int order)
Sets the order of the curve.
The result of a NurbsCurveEvaluator.
get_sample_point
Returns the point on the curve of the nth sample point generated by the previous call to adaptive_sam...
get_sample_t
Returns the t value of the nth sample point generated by the previous call to adaptive_sample().
bool eval_point(PN_stdfloat t, LVecBase3 &point)
Computes the point on the curve corresponding to the indicated value in parametric time.
void adaptive_sample(PN_stdfloat tolerance)
Determines the set of subdivisions necessary to approximate the curve with a set of linear segments,...
get_num_samples
Returns the number of sample points generated by the previous call to adaptive_sample().
get_maxval
Returns the maximum channel value allowable for any pixel in this image; for instance,...
int get_x_size() const
Returns the number of pixels in the X direction.
int get_y_size() const
Returns the number of pixels in the Y direction.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
void set_gray(int x, int y, float gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:849
void set_gray_val(int x, int y, xelval gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:545
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
A hierarchy of directories and files that appears to be one continuous file system,...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.