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, even though the files may originate from several different sources that may not be related to the actual OS's 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.