25 #undef interface // I don't know where this symbol is defined, but it interferes with FreeType. 34 const PN_stdfloat FreetypeFont::_points_per_unit = 10.0f;
37 const PN_stdfloat FreetypeFont::_points_per_inch = 72.0f;
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;
55 _space_advance = 0.25f;
67 FreetypeFont(
const FreetypeFont ©) :
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),
80 _char_size(copy._char_size),
82 _pixel_width(copy._pixel_width),
83 _pixel_height(copy._pixel_height)
93 load_font(
const Filename &font_filename,
int face_index) {
95 _face =
new FreetypeFace;
98 <<
"Unable to read font " << font_filename
99 <<
": FreeType library not initialized properly.\n";
109 exists = vfs->read_file(path, _face->_font_data,
true);
112 error = FT_New_Memory_Face(_face->_ft_library,
113 (
const FT_Byte *)_face->_font_data.data(),
114 _face->_font_data.length(),
116 _face->set_face(face);
122 <<
"Unable to find font file " << font_filename <<
"\n";
124 if (error == FT_Err_Unknown_File_Format) {
126 <<
"Unable to read font " << font_filename <<
": unknown file format.\n";
129 <<
"Unable to read font " << font_filename <<
": invalid.\n";
132 okflag = reset_scale();
148 load_font(
const char *font_data,
int data_length,
int face_index) {
150 _face =
new FreetypeFace;
152 if (!_face->_ft_ok) {
154 <<
"Unable to read font: FreeType library not initialized properly.\n";
161 error = FT_New_Memory_Face(_face->_ft_library,
162 (
const FT_Byte *)font_data, data_length,
164 _face->set_face(face);
167 if (error == FT_Err_Unknown_File_Format) {
169 <<
"Unable to read font: unknown file format.\n";
172 <<
"Unable to read font: invalid.\n";
175 okflag = reset_scale();
198 FreetypeFont::WindingOrder FreetypeFont::
199 string_winding_order(
const string &
string) {
200 if (cmp_nocase_uh(
string,
"default") == 0) {
202 }
else if (cmp_nocase_uh(
string,
"left") == 0) {
204 }
else if (cmp_nocase_uh(
string,
"right") == 0) {
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;
228 int error = FT_Load_Glyph(face, glyph_index, flags);
231 <<
"Unable to render glyph " << glyph_index <<
"\n";
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) {
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++) {
252 buffer_row += bitmap.pitch;
255 }
else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
258 unsigned char *buffer_row = bitmap.buffer;
259 for (
int yi = 0; yi < (int)bitmap.rows; yi++) {
262 unsigned char *b = buffer_row;
263 for (
int xi = 0; xi < (int)bitmap.width; xi++) {
276 buffer_row += bitmap.pitch;
280 }
else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
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));
288 buffer_row += bitmap.pitch;
293 <<
"Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode <<
"\n";
304 if (_face ==
nullptr) {
310 FT_Face face = _face->acquire_face(0, 0, 0, 0);
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;
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);
325 int error = FT_Set_Char_Size(face, _char_size, _char_size, _dpi, _dpi);
330 int desired_height = (int)(_font_pixels_per_unit * _point_size / _points_per_unit + 0.5f);
332 int largest_size = -1;
333 if (face->num_fixed_sizes > 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)) {
342 if (face->available_sizes[i].height > face->available_sizes[largest_size].height) {
348 best_size = largest_size;
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);
356 _font_pixels_per_unit = _pixel_height * _points_per_unit / _point_size;
357 _scale_factor = _font_pixels_per_unit / _tex_pixels_per_unit;
359 if (_scale_factor < 1.0) {
362 _tex_pixels_per_unit = _font_pixels_per_unit;
369 pnmtext_cat.warning()
370 <<
"Unable to set " << get_name()
371 <<
" to " << _point_size <<
"pt at " << _dpi <<
" dpi.\n";
373 _face->release_face(face);
377 _line_height = face->size->metrics.height / (_font_pixels_per_unit * 64.0f);
380 error = FT_Load_Char(face,
' ', FT_LOAD_DEFAULT);
383 _space_advance = 0.25f * _line_height;
386 _space_advance = face->glyph->advance.x / (_font_pixels_per_unit * 64.0f);
389 _face->release_face(face);
397 render_distance_field(
PNMImage &image,
int outline,
int min_x,
int min_y) {
398 Contours::const_iterator ci;
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;;
403 offset_x += min_x / (64.0f * _font_pixels_per_unit);
404 offset_y += min_y / (64.0f * _font_pixels_per_unit);
406 PN_stdfloat scale = _tex_pixels_per_unit / (outline * 2);
408 for (
int y = 0; y < image.
get_y_size(); ++y) {
409 LPoint2 p(0, offset_y - (y / _tex_pixels_per_unit));
411 for (
int x = 0; x < image.
get_x_size(); ++x) {
412 p[0] = offset_x + (x / _tex_pixels_per_unit);
414 PN_stdfloat min_dist_sq = 100000000;
415 int winding_number = 0;
417 for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
421 const Contour &contour = (*ci);
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;
428 LVector2 v = end - begin;
429 PN_stdfloat length_sq = v.length_squared();
432 if (length_sq == 0) {
433 dist_sq = (p - begin).length_squared();
435 }
else if (radius != 0) {
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;
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;
453 if (begin[1] <= p[1]) {
455 if (inside != (v[0] * v1[1] > v[1] * v1[0])) {
460 if (end[1] <= p[1]) {
461 if (inside == (v[0] * v1[1] > v[1] * v1[0])) {
468 dist_sq = std::min((p - begin).length_squared(), (p - end).length_squared());
469 if (begin[1] <= p[1]) {
471 if ((v[0] * (p[1] - begin[1]) > v[1] * (p[0] - begin[0]))) {
476 if (end[1] <= p[1]) {
477 if ((v[0] * (p[1] - begin[1]) < v[1] * (p[0] - begin[0]))) {
486 if (begin[1] <= p[1]) {
488 if ((v[0] * (p[1] - begin[1]) > v[1] * (p[0] - begin[0]))) {
493 if (end[1] <= p[1]) {
494 if ((v[0] * (p[1] - begin[1]) < v[1] * (p[0] - begin[0]))) {
500 PN_stdfloat t = v.dot(p - begin) / length_sq;
502 dist_sq = (p - begin).length_squared();
503 }
else if (t >= 1.0) {
504 dist_sq = (p - end).length_squared();
506 dist_sq = (p - (begin + v * t)).length_squared();
510 min_dist_sq = std::min(min_dist_sq, dist_sq);
514 int sign = (winding_number != 0) ? 1 : -1;
516 PN_stdfloat signed_dist = csqrt(min_dist_sq) * sign;
517 image.
set_gray(x, y, signed_dist * scale + (PN_stdfloat)0.5);
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;
534 WindingOrder wo = _winding_order;
535 if (wo == WO_default) {
538 #ifdef FT_ORIENTATION_FILL_RIGHT 539 if (FT_Outline_Get_Orientation(&outline) == FT_ORIENTATION_FILL_RIGHT) {
548 #endif // FT_ORIENTATION_FILL_RIGHT 552 FT_Outline_Reverse(&outline);
556 FT_Outline_Decompose(&outline, &funcs, (
void *)
this);
564 outline_move_to(
const FT_Vector *to,
void *user) {
565 FreetypeFont *
self = (FreetypeFont *)user;
568 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
569 LPoint2 p = LPoint2(to->x, to->y) * scale;
571 if (self->_contours.empty() ||
572 !
self->_contours.back()._points.empty()) {
573 self->_contours.push_back(Contour());
584 outline_line_to(
const FT_Vector *to,
void *user) {
585 FreetypeFont *
self = (FreetypeFont *)user;
586 nassertr(!self->_contours.empty(), 1);
589 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
590 LPoint2 p = LPoint2(to->x, to->y) * scale;
593 LVector2 t = (p -
self->_q);
596 if (self->_contours.back()._points.empty()) {
597 self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2::zero(), t));
599 self->_contours.back()._points.back().connect_to(t);
602 self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2::zero()));
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);
618 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
620 LPoint2 c = LPoint2(control->x, control->y) * scale;
621 LPoint2 p = LPoint2(to->x, to->y) * scale;
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));
635 return self->outline_nurbs(ncr);
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);
649 PN_stdfloat scale = 1.0f / (64.0f *
self->_font_pixels_per_unit);
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;
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));
668 return self->outline_nurbs(ncr);
682 bool needs_connect =
false;
684 if (_contours.back()._points.empty()) {
689 needs_connect =
true;
692 for (
int i = start; i < num_samples; ++i) {
696 PN_stdfloat st0 = st, st1 = st;
700 if (i < num_samples - 1) {
708 LVector3 t = p1 - p0;
712 _contours.back()._points.back().connect_to(LVector2(t[0], t[1]));
713 needs_connect =
false;
716 _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
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)) {
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);
735 _contours.back()._points.back()._center = center;
736 _contours.back()._points.back()._radius = (center - v1).length();
748 operator << (ostream &out, FreetypeFont::WindingOrder 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";
757 case FreetypeFont::WO_invalid:
758 return out <<
"invalid";
761 return out <<
"(**invalid FreetypeFont::WindingOrder(" << (int)wo <<
")**)";
768 operator >> (istream &in, FreetypeFont::WindingOrder &wo) {
772 wo = FreetypeFont::string_winding_order(word);
777 #endif // HAVE_FREETYPE void set_gray_val(int x, int y, xelval gray)
Sets the gray component color at the indicated pixel.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
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.
A base class for all things which can have a name.
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.
void set_gray(int x, int y, float gray)
Sets the gray component color at the indicated pixel.
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.