Panda3D
Loading...
Searching...
No Matches
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
28using std::istream;
29using std::ostream;
30using 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.
34const PN_stdfloat FreetypeFont::_points_per_unit = 10.0f;
35
36// A universal typographic convention.
37const PN_stdfloat FreetypeFont::_points_per_inch = 72.0f;
38
39/**
40 *
41 */
42FreetypeFont::
43FreetypeFont() {
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 */
66FreetypeFont::
67FreetypeFont(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 */
92bool FreetypeFont::
93load_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 */
149bool FreetypeFont::
150load_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 */
190void FreetypeFont::
191unload_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 */
200FreetypeFont::WindingOrder FreetypeFont::
201string_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 */
217bool FreetypeFont::
218load_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 */
243void FreetypeFont::
244copy_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 */
304bool FreetypeFont::
305reset_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 */
398void FreetypeFont::
399render_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 */
527void FreetypeFont::
528decompose_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 */
565int FreetypeFont::
566outline_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 */
585int FreetypeFont::
586outline_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 */
613int FreetypeFont::
614outline_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 */
644int FreetypeFont::
645outline_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 */
676int FreetypeFont::
677outline_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 */
749ostream &
750operator << (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 */
769istream &
770operator >> (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:44
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.