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