Browse Source

Add signed-distance-field text rendering to DynamicTextFont and egg-mkfont

rdb 10 years ago
parent
commit
f4f7df9949

+ 67 - 0
panda/src/pnmtext/freetypeFont.I

@@ -232,6 +232,30 @@ get_points_per_inch() {
   return _points_per_inch;
   return _points_per_inch;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::set_winding_order
+//       Access: Published
+//  Description: Specifies an explicitly winding order on this
+//               particular font.  This is only necessary if the
+//               render_mode is RM_polygon or RM_solid, and only if
+//               FreeType appears to guess wrong on this font.
+//               Normally, you should leave this at WO_default.
+////////////////////////////////////////////////////////////////////
+INLINE void FreetypeFont::
+set_winding_order(WindingOrder winding_order) {
+  _winding_order = winding_order;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::get_winding_order
+//       Access: Published
+//  Description: Returns the winding order set via set_winding_order().
+////////////////////////////////////////////////////////////////////
+INLINE FreetypeFont::WindingOrder FreetypeFont::
+get_winding_order() const {
+  return _winding_order;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: FreetypeFont::acquire_face
 //     Function: FreetypeFont::acquire_face
 //       Access: Protected
 //       Access: Protected
@@ -259,3 +283,46 @@ release_face(FT_Face face) const {
   nassertv(_face != NULL);
   nassertv(_face != NULL);
   _face->release_face(face);
   _face->release_face(face);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::ContourPoint::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE FreetypeFont::ContourPoint::
+ContourPoint(const LPoint2 &p, const LVector2 &in, const LVector2 &out) :
+  _p(p), _in(in), _out(out), _radius(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::ContourPoint::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE FreetypeFont::ContourPoint::
+ContourPoint(PN_stdfloat px, PN_stdfloat py, PN_stdfloat tx, PN_stdfloat ty) :
+  _p(px, py), _in(tx, ty), _out(tx, ty), _radius(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::connect_to
+//       Access: Public
+//  Description: Connects the indicated point to the next point, whose
+//               tangent is given.  The given tangent becomes the out
+//               tangent at this point.  If the in tangent and out
+//               tangent are sufficiently close, they will be smoothed
+//               together.
+////////////////////////////////////////////////////////////////////
+INLINE void FreetypeFont::ContourPoint::
+connect_to(const LVector2 &out) {
+  _out = out;
+  if (_in.dot(_out) > 0.7071) {
+    // Less than 45 degrees of difference: smooth them.
+    LVector2 av = (_in + _out) * 0.5f;
+    av.normalize();
+    _in = av;
+    _out = av;
+  }
+}

+ 431 - 0
panda/src/pnmtext/freetypeFont.cxx

@@ -20,6 +20,11 @@
 #include "config_util.h"
 #include "config_util.h"
 #include "config_express.h"
 #include "config_express.h"
 #include "virtualFileSystem.h"
 #include "virtualFileSystem.h"
+#include "nurbsCurveEvaluator.h"
+#include "nurbsCurveResult.h"
+
+#undef interface  // I don't know where this symbol is defined, but it interferes with FreeType.
+#include FT_OUTLINE_H
 
 
 // This constant determines how big a particular point size font
 // This constant determines how big a particular point size font
 // appears to be.  By convention, 10 points is 1 unit (e.g. 1 foot)
 // appears to be.  By convention, 10 points is 1 unit (e.g. 1 foot)
@@ -44,6 +49,7 @@ FreetypeFont() {
   _requested_scale_factor = text_scale_factor;
   _requested_scale_factor = text_scale_factor;
   _scale_factor = text_scale_factor;
   _scale_factor = text_scale_factor;
   _native_antialias = text_native_antialias;
   _native_antialias = text_native_antialias;
+  _winding_order = WO_default;
 
 
   _line_height = 1.0f;
   _line_height = 1.0f;
   _space_advance = 0.25f;
   _space_advance = 0.25f;
@@ -69,6 +75,7 @@ FreetypeFont(const FreetypeFont &copy) :
   _scale_factor(copy._scale_factor),
   _scale_factor(copy._scale_factor),
   _native_antialias(copy._native_antialias),
   _native_antialias(copy._native_antialias),
   _font_pixels_per_unit(copy._font_pixels_per_unit),
   _font_pixels_per_unit(copy._font_pixels_per_unit),
+  _winding_order(copy._winding_order),
   _line_height(copy._line_height),
   _line_height(copy._line_height),
   _space_advance(copy._space_advance),
   _space_advance(copy._space_advance),
   _face(copy._face),
   _face(copy._face),
@@ -193,6 +200,26 @@ unload_font() {
   _face = NULL;
   _face = NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::string_winding_order
+//       Access: Public
+//  Description: Returns the WindingOrder value associated with the given
+//               string representation, or WO_invalid if the string
+//               does not match any known WindingOrder value.
+////////////////////////////////////////////////////////////////////
+FreetypeFont::WindingOrder FreetypeFont::
+string_winding_order(const string &string) {
+  if (cmp_nocase_uh(string, "default") == 0) {
+    return WO_default;
+  } else if (cmp_nocase_uh(string, "left") == 0) {
+    return WO_left;
+  } else if (cmp_nocase_uh(string, "right") == 0) {
+    return WO_right;
+  } else {
+    return WO_invalid;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: FreetypeFont::load_glyph
 //     Function: FreetypeFont::load_glyph
 //       Access: Protected
 //       Access: Protected
@@ -383,4 +410,408 @@ reset_scale() {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::render_distance_field
+//       Access: Private
+//  Description: Renders a signed distance field to the PNMImage
+//               based on the contours.
+////////////////////////////////////////////////////////////////////
+void FreetypeFont::
+render_distance_field(PNMImage &image, int outline, int min_x, int min_y) {
+  Contours::const_iterator ci;
+
+  PN_stdfloat offset_x = -outline / _tex_pixels_per_unit;
+  PN_stdfloat offset_y = (image.get_y_size() - 1 - outline) / _tex_pixels_per_unit;;
+
+  offset_x += min_x / (64.0f * _font_pixels_per_unit);
+  offset_y += min_y / (64.0f * _font_pixels_per_unit);
+
+  PN_stdfloat scale = _tex_pixels_per_unit / (outline * 2);
+
+  for (int y = 0; y < image.get_y_size(); ++y) {
+    LPoint2 p(0, offset_y - (y / _tex_pixels_per_unit));
+
+    for (int x = 0; x < image.get_x_size(); ++x) {
+      p[0] = offset_x + (x / _tex_pixels_per_unit);
+
+      PN_stdfloat min_dist_sq = 100000000;
+      int winding_number = 0;
+
+      for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
+        // Find the shortest distance between this point and the contour.
+        // Also keep track of the winding number, so we will know whether
+        // this point is inside or outside the polygon.
+        const Contour &contour = (*ci);
+
+        for (size_t i = 1; i < contour._points.size(); ++i) {
+          const LPoint2 &begin = contour._points[i - 1]._p;
+          const LPoint2 &end = contour._points[i]._p;
+          PN_stdfloat radius = contour._points[i]._radius;
+
+          LVector2 v = end - begin;
+          PN_stdfloat length_sq = v.length_squared();
+          PN_stdfloat dist_sq;
+
+          if (length_sq == 0) {
+            dist_sq = (p - begin).length_squared();
+
+          } else if (radius != 0) {
+            // Circular arc approximation.
+            LVector2 v1 = begin - contour._points[i]._center;
+            LVector2 v2 = end - contour._points[i]._center;
+            LVector2 vp = p - contour._points[i]._center;
+            PN_stdfloat dist_to_center = vp.length();
+            vp /= dist_to_center;
+            v1 /= radius;
+            v2 /= radius;
+            PN_stdfloat range = v1.dot(v2);
+            if (vp.dot(v1) > range && vp.dot(v2) > range) {
+              dist_sq = dist_to_center - radius;
+              bool inside = dist_sq < 0;
+              dist_sq *= dist_sq;
+
+              //if (v1[0] * vp[1] - vp[0] * v1[1] < 0 && v2[0] * vp[1] - vp[0] * v2[1] < 0) {
+              //if (v1.signed_angle_deg(vp) < v1.signed_angle_deg(v2) && v1.signed_angle_deg(vp) > 0) {
+              if (begin[1] <= p[1]) {
+                if (end[1] > p[1]) {
+                  if (inside != (v[0] * v1[1] > v[1] * v1[0])) {
+                    ++winding_number;
+                  }
+                }
+              } else {
+                if (end[1] <= p[1]) {
+                  if (inside == (v[0] * v1[1] > v[1] * v1[0])) {
+                    --winding_number;
+                  }
+                }
+              }
+
+            } else {
+              dist_sq = min((p - begin).length_squared(), (p - end).length_squared());
+              if (begin[1] <= p[1]) {
+                if (end[1] > p[1]) {
+                  if ((v[0] * (p[1] - begin[1]) > v[1] * (p[0] - begin[0]))) {
+                    ++winding_number;
+                  }
+                }
+              } else {
+                if (end[1] <= p[1]) {
+                  if ((v[0] * (p[1] - begin[1]) < v[1] * (p[0] - begin[0]))) {
+                    --winding_number;
+                  }
+                }
+              }
+            }
+
+          } else {
+            // Just a straight line.
+            if (begin[1] <= p[1]) {
+              if (end[1] > p[1]) {
+                if ((v[0] * (p[1] - begin[1]) > v[1] * (p[0] - begin[0]))) {
+                  ++winding_number;
+                }
+              }
+            } else {
+              if (end[1] <= p[1]) {
+                if ((v[0] * (p[1] - begin[1]) < v[1] * (p[0] - begin[0]))) {
+                  --winding_number;
+                }
+              }
+            }
+
+            PN_stdfloat t = v.dot(p - begin) / length_sq;
+            if (t <= 0.0) {
+              dist_sq = (p - begin).length_squared();
+            } else if (t >= 1.0) {
+              dist_sq = (p - end).length_squared();
+            } else {
+              dist_sq = (p - (begin + v * t)).length_squared();
+            }
+          }
+
+          min_dist_sq = min(min_dist_sq, dist_sq);
+        }
+      }
+      // Determine the sign based on whether we're inside the contour.
+      int sign = (winding_number != 0) ? 1 : -1;
+
+      PN_stdfloat signed_dist = csqrt(min_dist_sq) * sign;
+      image.set_gray(x, y, signed_dist * scale + (PN_stdfloat)0.5);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::decompose_outline
+//       Access: Protected
+//  Description: Ask FreeType to extract the contours out of the
+//               outline description.
+////////////////////////////////////////////////////////////////////
+void FreetypeFont::
+decompose_outline(FT_Outline &outline) {
+  FT_Outline_Funcs funcs;
+  memset(&funcs, 0, sizeof(funcs));
+  funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
+  funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
+  funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
+  funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
+
+  WindingOrder wo = _winding_order;
+  if (wo == WO_default) {
+    // If we weren't told an explicit winding order, ask FreeType to
+    // figure it out.  Sometimes it appears to guess wrong.
+#ifdef FT_ORIENTATION_FILL_RIGHT
+    if (FT_Outline_Get_Orientation(&outline) == FT_ORIENTATION_FILL_RIGHT) {
+      wo = WO_right;
+    } else {
+      wo = WO_left;
+    }
+#else
+    // Hmm.  Assign a right-winding (TTF) orientation if FreeType
+    // can't tell us.
+    wo = WO_right;
+#endif  // FT_ORIENTATION_FILL_RIGHT
+  }
+
+  if (wo != WO_left) {
+    FT_Outline_Reverse(&outline);
+  }
+
+  _contours.clear();
+  FT_Outline_Decompose(&outline, &funcs, (void *)this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::outline_move_to
+//       Access: Private, Static
+//  Description: A callback from FT_Outline_Decompose().  It marks the
+//               beginning of a new contour.
+////////////////////////////////////////////////////////////////////
+int FreetypeFont::
+outline_move_to(const FT_Vector *to, void *user) {
+  FreetypeFont *self = (FreetypeFont *)user;
+
+  // Convert from 26.6 pixel units to Panda units.
+  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
+  LPoint2 p = LPoint2(to->x, to->y) * scale;
+
+  if (self->_contours.empty() ||
+      !self->_contours.back()._points.empty()) {
+    self->_contours.push_back(Contour());
+  }
+  self->_q = p;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::outline_line_to
+//       Access: Private, Static
+//  Description: A callback from FT_Outline_Decompose().  It marks a
+//               straight line in the contour.
+////////////////////////////////////////////////////////////////////
+int FreetypeFont::
+outline_line_to(const FT_Vector *to, void *user) {
+  FreetypeFont *self = (FreetypeFont *)user;
+  nassertr(!self->_contours.empty(), 1);
+
+  // Convert from 26.6 pixel units to Panda units.
+  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
+  LPoint2 p = LPoint2(to->x, to->y) * scale;
+
+  // Compute the tangent: this is just the vector from the last point.
+  LVector2 t = (p - self->_q);
+  t.normalize();
+
+  if (self->_contours.back()._points.empty()) {
+    self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2::zero(), t));
+  } else {
+    self->_contours.back()._points.back().connect_to(t);
+  }
+
+  self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2::zero()));
+  self->_q = p;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::outline_conic_to
+//       Access: Private, Static
+//  Description: A callback from FT_Outline_Decompose().  It marks a
+//               parabolic (3rd-order) Bezier curve in the contour.
+////////////////////////////////////////////////////////////////////
+int FreetypeFont::
+outline_conic_to(const FT_Vector *control,
+                 const FT_Vector *to, void *user) {
+  FreetypeFont *self = (FreetypeFont *)user;
+  nassertr(!self->_contours.empty(), 1);
+
+  // Convert from 26.6 pixel units to Panda units.
+  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
+
+  LPoint2 c = LPoint2(control->x, control->y) * scale;
+  LPoint2 p = LPoint2(to->x, to->y) * scale;
+
+  // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
+  NurbsCurveEvaluator nce;
+  nce.local_object();
+  nce.set_order(3);
+  nce.reset(3);
+  nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
+  nce.set_vertex(1, LVecBase3(c[0], c[1], 0.0f));
+  nce.set_vertex(2, LVecBase3(p[0], p[1], 0.0f));
+
+  self->_q = p;
+
+  PT(NurbsCurveResult) ncr = nce.evaluate();
+  return self->outline_nurbs(ncr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::outline_cubic_to
+//       Access: Private, Static
+//  Description: A callback from FT_Outline_Decompose().  It marks a
+//               cubic (4th-order) Bezier curve in the contour.
+////////////////////////////////////////////////////////////////////
+int FreetypeFont::
+outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
+                 const FT_Vector *to, void *user) {
+  FreetypeFont *self = (FreetypeFont *)user;
+  nassertr(!self->_contours.empty(), 1);
+
+  // Convert from 26.6 pixel units to Panda units.
+  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
+
+  LPoint2 c1 = LPoint2(control1->x, control1->y) * scale;
+  LPoint2 c2 = LPoint2(control2->x, control2->y) * scale;
+  LPoint2 p = LPoint2(to->x, to->y) * scale;
+
+  // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
+  NurbsCurveEvaluator nce;
+  nce.local_object();
+  nce.set_order(4);
+  nce.reset(4);
+  nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
+  nce.set_vertex(1, LVecBase3(c1[0], c1[1], 0.0f));
+  nce.set_vertex(2, LVecBase3(c2[0], c2[1], 0.0f));
+  nce.set_vertex(3, LVecBase3(p[0], p[1], 0.0f));
+
+  self->_q = p;
+
+  PT(NurbsCurveResult) ncr = nce.evaluate();
+  return self->outline_nurbs(ncr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::outline_nurbs
+//       Access: Private
+//  Description: Called internally by outline_cubic_to() and
+//               outline_conic_to().
+////////////////////////////////////////////////////////////////////
+int FreetypeFont::
+outline_nurbs(NurbsCurveResult *ncr) {
+  // Sample it down so that the lines approximate the curve to within
+  // a "pixel."
+  ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
+
+  int num_samples = ncr->get_num_samples();
+
+  bool needs_connect = false;
+  int start = 1;
+  if (_contours.back()._points.empty()) {
+    // If we haven't got the first point of this contour yet, we must
+    // add it now.
+    start = 0;
+  } else {
+    needs_connect = true;
+  }
+
+  for (int i = start; i < num_samples; ++i) {
+    PN_stdfloat st = ncr->get_sample_t(i);
+    const LPoint3 &p = ncr->get_sample_point(i);
+
+    PN_stdfloat st0 = st, st1 = st;
+    if (i > 0) {
+      PN_stdfloat last_t = ncr->get_sample_t(i - 1);
+      st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
+    }
+    if (i < num_samples - 1) {
+      st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
+    }
+    // Compute the tangent by deltaing nearby points.  Don't evaluate
+    // the tangent from the NURBS, since that doesn't appear to be
+    // reliable.
+    LPoint3 p0, p1;
+    ncr->eval_point(st0, p0);
+    ncr->eval_point(st1, p1);
+    LVector3 t = p1 - p0;
+    t.normalize();
+
+    if (needs_connect) {
+      _contours.back()._points.back().connect_to(LVector2(t[0], t[1]));
+      needs_connect = false;
+    }
+
+    _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
+
+    if (i > 0) {
+      // Approximate the curve using a circular arc.  This is used in the
+      // signed distance field generation code.  We do this by sampling a
+      // point in the middle of the segment and calculating the circle that
+      // goes through the three points.
+      LPoint2 v1 = ncr->get_sample_point(i - 1).get_xy();
+      LPoint3 v2;
+      ncr->eval_point((ncr->get_sample_t(i - 1) + ncr->get_sample_t(i)) / 2, v2);
+      PN_stdfloat temp = v1.length_squared();
+      PN_stdfloat bc = (v2[0]*v2[0] + v2[1]*v2[1] - temp) * (PN_stdfloat)0.5f;
+      PN_stdfloat cd = (temp - p[0]*p[0] - p[1]*p[1]) * (PN_stdfloat)0.5f;
+      PN_stdfloat det = (v2[0]-v1[0])*(v1[1]-p[1])-(v1[0]-p[0])*(v2[1]-v1[1]);
+      if (!IS_NEARLY_ZERO(det)) {
+        LPoint2 center;
+        center[0] = (bc*(v1[1]-p[1])-cd*(v2[1]-v1[1]));
+        center[1] = ((v2[0]-v1[0])*cd-(v1[0]-p[0])*bc);
+        center /= det;
+        _contours.back()._points.back()._center = center;
+        _contours.back()._points.back()._radius = (center - v1).length();
+      }
+    }
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::WindingOrder output operator
+//  Description:
+////////////////////////////////////////////////////////////////////
+ostream &
+operator << (ostream &out, FreetypeFont::WindingOrder wo) {
+  switch (wo) {
+  case FreetypeFont::WO_default:
+    return out << "default";
+  case FreetypeFont::WO_left:
+    return out << "left";
+  case FreetypeFont::WO_right:
+    return out << "right";
+
+  case FreetypeFont::WO_invalid:
+    return out << "invalid";
+  }
+
+  return out << "(**invalid FreetypeFont::WindingOrder(" << (int)wo << ")**)";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FreetypeFont::WindingOrder input operator
+//  Description:
+////////////////////////////////////////////////////////////////////
+istream &
+operator >> (istream &in, FreetypeFont::WindingOrder &wo) {
+  string word;
+  in >> word;
+
+  wo = FreetypeFont::string_winding_order(word);
+  return in;
+}
+
+
 #endif  // HAVE_FREETYPE
 #endif  // HAVE_FREETYPE

+ 60 - 0
panda/src/pnmtext/freetypeFont.h

@@ -30,6 +30,8 @@
 #include <ft2build.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_FREETYPE_H
 
 
+class NurbsCurveResult;
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : FreetypeFont
 //       Class : FreetypeFont
 // Description : This is a common base class for both DynamicTextFont
 // Description : This is a common base class for both DynamicTextFont
@@ -50,6 +52,14 @@ protected:
 PUBLISHED:
 PUBLISHED:
   INLINE ~FreetypeFont();
   INLINE ~FreetypeFont();
 
 
+  enum WindingOrder {
+    WO_default,
+    WO_left,
+    WO_right,
+
+    WO_invalid,
+  };
+
   INLINE bool set_point_size(PN_stdfloat point_size);
   INLINE bool set_point_size(PN_stdfloat point_size);
   INLINE PN_stdfloat get_point_size() const;
   INLINE PN_stdfloat get_point_size() const;
 
 
@@ -73,16 +83,35 @@ PUBLISHED:
   INLINE static PN_stdfloat get_points_per_unit();
   INLINE static PN_stdfloat get_points_per_unit();
   INLINE static PN_stdfloat get_points_per_inch();
   INLINE static PN_stdfloat get_points_per_inch();
 
 
+  INLINE void set_winding_order(WindingOrder winding_order);
+  INLINE WindingOrder get_winding_order() const;
+  MAKE_PROPERTY(winding_order, get_winding_order, set_winding_order);
+
+public:
+  static WindingOrder string_winding_order(const string &string);
+
 protected:
 protected:
   INLINE FT_Face acquire_face() const;
   INLINE FT_Face acquire_face() const;
   INLINE void release_face(FT_Face face) const;
   INLINE void release_face(FT_Face face) const;
 
 
   bool load_glyph(FT_Face face, int glyph_index, bool prerender = true);
   bool load_glyph(FT_Face face, int glyph_index, bool prerender = true);
   void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image);
   void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image);
+  void render_distance_field(PNMImage &image, int radius, int min_x, int min_y);
+
+  void decompose_outline(FT_Outline &outline);
 
 
 private:
 private:
   bool reset_scale();
   bool reset_scale();
 
 
+  static int outline_move_to(const FT_Vector *to, void *user);
+  static int outline_line_to(const FT_Vector *to, void *user);
+  static int outline_conic_to(const FT_Vector *control,
+                              const FT_Vector *to, void *user);
+  static int outline_cubic_to(const FT_Vector *control1,
+                              const FT_Vector *control2,
+                              const FT_Vector *to, void *user);
+  int outline_nurbs(NurbsCurveResult *ncr);
+
 protected:
 protected:
   PN_stdfloat _point_size;
   PN_stdfloat _point_size;
   PN_stdfloat _requested_pixels_per_unit;
   PN_stdfloat _requested_pixels_per_unit;
@@ -91,6 +120,7 @@ protected:
   PN_stdfloat _scale_factor;
   PN_stdfloat _scale_factor;
   bool _native_antialias;
   bool _native_antialias;
   PN_stdfloat _font_pixels_per_unit;
   PN_stdfloat _font_pixels_per_unit;
+  WindingOrder _winding_order;
 
 
   int _font_pixel_size;
   int _font_pixel_size;
   PN_stdfloat _line_height;
   PN_stdfloat _line_height;
@@ -102,6 +132,33 @@ protected:
   int _pixel_width;
   int _pixel_width;
   int _pixel_height;
   int _pixel_height;
 
 
+  class ContourPoint {
+  public:
+    INLINE ContourPoint(const LPoint2 &p, const LVector2 &in,
+                        const LVector2 &out);
+    INLINE ContourPoint(PN_stdfloat px, PN_stdfloat py, PN_stdfloat tx, PN_stdfloat ty);
+    INLINE void connect_to(const LVector2 &out);
+    LPoint2 _p;
+    LVector2 _in, _out;  // tangents into and out of the vertex.
+
+    // Circular arc approximation of the curve from previous point.
+    // If radius is 0, this is a straight line.
+    LPoint2 _center;
+    PN_stdfloat _radius;
+  };
+  typedef pvector<ContourPoint> Points;
+
+  class Contour {
+  public:
+    Points _points;
+    bool _is_solid;
+    int _start_vertex;
+  };
+
+  typedef pvector<Contour> Contours;
+  Contours _contours;
+  LPoint2 _q;  // The "current point".
+
 protected:
 protected:
   static const PN_stdfloat _points_per_unit;
   static const PN_stdfloat _points_per_unit;
   static const PN_stdfloat _points_per_inch;
   static const PN_stdfloat _points_per_inch;
@@ -109,6 +166,9 @@ protected:
 
 
 #include "freetypeFont.I"
 #include "freetypeFont.I"
 
 
+EXPCL_PANDA_PNMTEXT ostream &operator << (ostream &out, FreetypeFont::WindingOrder wo);
+EXPCL_PANDA_PNMTEXT istream &operator >> (istream &in, FreetypeFont::WindingOrder &wo);
+
 #endif  // HAVE_FREETYPE
 #endif  // HAVE_FREETYPE
 
 
 #endif
 #endif

+ 23 - 0
panda/src/pnmtext/pnmTextMaker.I

@@ -124,6 +124,29 @@ get_interior() const {
   return _interior;
   return _interior;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMTextMaker::set_distance_field_radius
+//       Access: Published
+//  Description: If this is set to something other than 0, Panda
+//               will generate a signed distance field with the
+//               given radius.
+////////////////////////////////////////////////////////////////////
+INLINE void PNMTextMaker::
+set_distance_field_radius(int distance_field_radius) {
+  _distance_field_radius = distance_field_radius;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMTextMaker::get_distance_field_radius
+//       Access: Published
+//  Description: Returns the radius previously set with
+//               set_distance_field_radius, or 0 otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE int PNMTextMaker::
+get_distance_field_radius() const {
+  return _distance_field_radius;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMTextMaker::generate_into
 //     Function: PNMTextMaker::generate_into
 //       Access: Public
 //       Access: Public

+ 55 - 7
panda/src/pnmtext/pnmTextMaker.cxx

@@ -55,7 +55,8 @@ PNMTextMaker(const PNMTextMaker &copy) :
   _align(copy._align),
   _align(copy._align),
   _interior_flag(copy._interior_flag),
   _interior_flag(copy._interior_flag),
   _fg(copy._fg),
   _fg(copy._fg),
-  _interior(copy._interior)
+  _interior(copy._interior),
+  _distance_field_radius(copy._distance_field_radius)
 {
 {
 }
 }
 
 
@@ -180,6 +181,7 @@ initialize() {
   _interior_flag = false;
   _interior_flag = false;
   _fg.set(0.0f, 0.0f, 0.0f, 1.0f);
   _fg.set(0.0f, 0.0f, 0.0f, 1.0f);
   _interior.set(0.5f, 0.5f, 0.5f, 1.0f);
   _interior.set(0.5f, 0.5f, 0.5f, 1.0f);
+  _distance_field_radius = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -210,15 +212,61 @@ make_glyph(int glyph_index) {
 
 
   } else {
   } else {
     PNMImage &glyph_image = glyph->_image;
     PNMImage &glyph_image = glyph->_image;
-    glyph_image.clear(bitmap.width, bitmap.rows, 3);
-    copy_bitmap_to_pnmimage(bitmap, glyph_image);
 
 
-    glyph->_top = slot->bitmap_top;
-    glyph->_left = slot->bitmap_left;
+    if (_distance_field_radius != 0) {
+      // Ask FreeType to extract the contours out of the outline
+      // description.
+      decompose_outline(slot->outline);
 
 
-    if (_interior_flag) {
-      glyph->determine_interior();
+      PN_stdfloat tex_x_size, tex_y_size, tex_x_orig, tex_y_orig;
+      FT_BBox bounds;
+      TransparencyAttrib::Mode alpha_mode;
+
+      // Calculate suitable texture dimensions for the signed distance field.
+      // This is the same calculation that Freetype uses in its bitmap renderer.
+      FT_Outline_Get_CBox(&slot->outline, &bounds);
+
+      bounds.xMin = bounds.xMin & ~63;
+      bounds.yMin = bounds.yMin & ~63;
+      bounds.xMax = (bounds.xMax + 63) & ~63;
+      bounds.yMax = (bounds.yMax + 63) & ~63;
+
+      tex_x_size = (bounds.xMax - bounds.xMin) >> 6;
+      tex_y_size = (bounds.yMax - bounds.yMin) >> 6;
+      tex_x_orig = (bounds.xMin >> 6);
+      tex_y_orig = (bounds.yMax >> 6);
+
+      if (tex_x_size == 0 || tex_y_size == 0) {
+        // If we got an empty bitmap, it's a special case.
+      } else {
+        int outline = 0;
+
+        int int_x_size = (int)ceil(tex_x_size);
+        int int_y_size = (int)ceil(tex_y_size);
+
+        outline = _distance_field_radius;
+        int_x_size += outline * 2;
+        int_y_size += outline * 2;
+
+        glyph_image.clear(int_x_size, int_y_size, 1);
+        render_distance_field(glyph_image, outline, bounds.xMin, bounds.yMin);
+
+        glyph->_top = tex_y_orig + outline * _scale_factor;
+        glyph->_left = tex_x_orig + outline * _scale_factor;
+
+      }
+    } else {
+      glyph_image.clear(bitmap.width, bitmap.rows, 3);
+      copy_bitmap_to_pnmimage(bitmap, glyph_image);
+
+      glyph->_top = slot->bitmap_top;
+      glyph->_left = slot->bitmap_left;
+
+      if (_interior_flag) {
+        glyph->determine_interior();
+      }
     }
     }
+
     glyph->rescale(_scale_factor);
     glyph->rescale(_scale_factor);
   }
   }
 
 

+ 4 - 0
panda/src/pnmtext/pnmTextMaker.h

@@ -64,6 +64,9 @@ PUBLISHED:
   INLINE void set_interior(const LColor &interior);
   INLINE void set_interior(const LColor &interior);
   INLINE const LColor &get_interior() const;
   INLINE const LColor &get_interior() const;
 
 
+  INLINE void set_distance_field_radius(int radius);
+  INLINE int get_distance_field_radius() const;
+
   INLINE int generate_into(const string &text,
   INLINE int generate_into(const string &text,
                            PNMImage &dest_image, int x, int y);
                            PNMImage &dest_image, int x, int y);
   int generate_into(const wstring &text,
   int generate_into(const wstring &text,
@@ -87,6 +90,7 @@ private:
   bool _interior_flag;
   bool _interior_flag;
   LColor _fg;
   LColor _fg;
   LColor _interior;
   LColor _interior;
+  int _distance_field_radius;
 };
 };
 
 
 #include "pnmTextMaker.I"
 #include "pnmTextMaker.I"

+ 0 - 67
panda/src/text/dynamicTextFont.I

@@ -402,30 +402,6 @@ get_render_mode() const {
   return _render_mode;
   return _render_mode;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::set_winding_order
-//       Access: Published
-//  Description: Specifies an explicitly winding order on this
-//               particular font.  This is only necessary if the
-//               render_mode is RM_polygon or RM_solid, and only if
-//               FreeType appears to guess wrong on this font.
-//               Normally, you should leave this at WO_default.
-////////////////////////////////////////////////////////////////////
-INLINE void DynamicTextFont::
-set_winding_order(DynamicTextFont::WindingOrder winding_order) {
-  _winding_order = winding_order;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::get_winding_order
-//       Access: Published
-//  Description: Returns the winding order set via set_winding_order().
-////////////////////////////////////////////////////////////////////
-INLINE DynamicTextFont::WindingOrder DynamicTextFont::
-get_winding_order() const {
-  return _winding_order;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DynamicTextFont::set_fg
 //     Function: DynamicTextFont::set_fg
 //       Access: Published
 //       Access: Published
@@ -585,49 +561,6 @@ get_tex_format() const {
   return _tex_format;
   return _tex_format;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::ContourPoint::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE DynamicTextFont::ContourPoint::
-ContourPoint(const LPoint2 &p, const LVector2 &in, const LVector2 &out) :
-  _p(p), _in(in), _out(out)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::ContourPoint::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE DynamicTextFont::ContourPoint::
-ContourPoint(PN_stdfloat px, PN_stdfloat py, PN_stdfloat tx, PN_stdfloat ty) :
-  _p(px, py), _in(tx, ty), _out(tx, ty)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::connect_to
-//       Access: Public
-//  Description: Connects the indicated point to the next point, whose
-//               tangent is given.  The given tangent becomes the out
-//               tangent at this point.  If the in tangent and out
-//               tangent are sufficiently close, they will be smoothed
-//               together.
-////////////////////////////////////////////////////////////////////
-INLINE void DynamicTextFont::ContourPoint::
-connect_to(const LVector2 &out) {
-  _out = out;
-  if (_in.dot(_out) > 0.7071) {
-    // Less than 45 degrees of difference: smooth them.
-    LVector2 av = (_in + _out) * 0.5f;
-    av.normalize();
-    _in = av;
-    _out = av;
-  }
-}
-
 
 
 INLINE ostream &
 INLINE ostream &
 operator << (ostream &out, const DynamicTextFont &dtf) {
 operator << (ostream &out, const DynamicTextFont &dtf) {

+ 68 - 228
panda/src/text/dynamicTextFont.cxx

@@ -40,8 +40,6 @@
 #include "renderState.h"
 #include "renderState.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "triangulator.h"
 #include "triangulator.h"
-#include "nurbsCurveEvaluator.h"
-#include "nurbsCurveResult.h"
 //#include "renderModeAttrib.h"
 //#include "renderModeAttrib.h"
 //#include "antialiasAttrib.h"
 //#include "antialiasAttrib.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
@@ -117,7 +115,6 @@ DynamicTextFont(const DynamicTextFont &copy) :
   _magfilter(copy._magfilter),
   _magfilter(copy._magfilter),
   _anisotropic_degree(copy._anisotropic_degree),
   _anisotropic_degree(copy._anisotropic_degree),
   _render_mode(copy._render_mode),
   _render_mode(copy._render_mode),
-  _winding_order(copy._winding_order),
   _fg(copy._fg),
   _fg(copy._fg),
   _bg(copy._bg),
   _bg(copy._bg),
   _outline_color(copy._outline_color),
   _outline_color(copy._outline_color),
@@ -195,7 +192,7 @@ garbage_collect() {
   Cache new_cache;
   Cache new_cache;
   Cache::iterator ci;
   Cache::iterator ci;
   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
-    TextGlyph *glyph = (*ci).second;
+    const TextGlyph *glyph = (*ci).second;
     if (glyph == (TextGlyph *)NULL || glyph->get_ref_count() > 1) {
     if (glyph == (TextGlyph *)NULL || glyph->get_ref_count() > 1) {
       // Keep this one.
       // Keep this one.
       new_cache.insert(new_cache.end(), (*ci));
       new_cache.insert(new_cache.end(), (*ci));
@@ -253,7 +250,7 @@ write(ostream &out, int indent_level) const {
   Cache::const_iterator ci;
   Cache::const_iterator ci;
   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
     int glyph_index = (*ci).first;
     int glyph_index = (*ci).first;
-    TextGlyph *glyph = (*ci).second;
+    const TextGlyph *glyph = (*ci).second;
     indent(out, indent_level + 2) 
     indent(out, indent_level + 2) 
       << glyph_index;
       << glyph_index;
 
 
@@ -303,16 +300,15 @@ get_glyph(int character, CPT(TextGlyph) &glyph) {
   if (ci != _cache.end()) {
   if (ci != _cache.end()) {
     glyph = (*ci).second;
     glyph = (*ci).second;
   } else {
   } else {
-    TextGlyph *dynamic_glyph = make_glyph(character, face, glyph_index);
-    _cache.insert(Cache::value_type(glyph_index, dynamic_glyph));
-    glyph = dynamic_glyph;
+    glyph = make_glyph(character, face, glyph_index);
+    _cache.insert(Cache::value_type(glyph_index, glyph.p()));
   }
   }
 
 
-  if (glyph == (TextGlyph *)NULL) {
+  if (glyph.is_null()) {
     glyph = get_invalid_glyph();
     glyph = get_invalid_glyph();
     glyph_index = 0;
     glyph_index = 0;
   }
   }
-    
+
   release_face(face);
   release_face(face);
   return (glyph_index != 0);
   return (glyph_index != 0);
 }
 }
@@ -442,7 +438,7 @@ determine_tex_format() {
 //               newly-created TextGlyph object, or NULL if the
 //               newly-created TextGlyph object, or NULL if the
 //               glyph cannot be created for some reason.
 //               glyph cannot be created for some reason.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-TextGlyph *DynamicTextFont::
+CPT(TextGlyph) DynamicTextFont::
 make_glyph(int character, FT_Face face, int glyph_index) {
 make_glyph(int character, FT_Face face, int glyph_index) {
   if (!load_glyph(face, glyph_index, false)) {
   if (!load_glyph(face, glyph_index, false)) {
     return (TextGlyph *)NULL;
     return (TextGlyph *)NULL;
@@ -490,36 +486,7 @@ make_glyph(int character, FT_Face face, int glyph_index) {
 
 
     // Ask FreeType to extract the contours out of the outline
     // Ask FreeType to extract the contours out of the outline
     // description.
     // description.
-    FT_Outline_Funcs funcs;
-    memset(&funcs, 0, sizeof(funcs));
-    funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
-    funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
-    funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
-    funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
-
-    WindingOrder wo = _winding_order;
-    if (wo == WO_default) {
-      // If we weren't told an explicit winding order, ask FreeType to
-      // figure it out.  Sometimes it appears to guess wrong.
-#ifdef FT_ORIENTATION_FILL_RIGHT
-      if (FT_Outline_Get_Orientation(&slot->outline) == FT_ORIENTATION_FILL_RIGHT) {
-        wo = WO_right;
-      } else {
-        wo = WO_left;
-      }
-#else
-      // Hmm.  Assign a right-winding (TTF) orientation if FreeType
-      // can't tell us.
-      wo = WO_right;
-#endif  // FT_ORIENTATION_FILL_RIGHT
-    }
-
-    if (wo != WO_left) {
-      FT_Outline_Reverse(&slot->outline);
-    }
-
-    _contours.clear();
-    FT_Outline_Decompose(&slot->outline, &funcs, (void *)this);
+    decompose_outline(slot->outline);
 
 
     PT(TextGlyph) glyph =
     PT(TextGlyph) glyph =
       new TextGlyph(character, advance);
       new TextGlyph(character, advance);
@@ -541,17 +508,46 @@ make_glyph(int character, FT_Face face, int glyph_index) {
       return glyph;
       return glyph;
 
 
     case RM_texture:
     case RM_texture:
+    case RM_distance_field:
     default:
     default:
       break;
       break;
     }
     }
   }
   }
 
 
-  // Render the glyph if necessary.
-  if (slot->format != ft_glyph_format_bitmap) {
-    FT_Render_Glyph(slot, ft_render_mode_normal);
+  PN_stdfloat tex_x_size, tex_y_size, tex_x_orig, tex_y_orig;
+  FT_BBox bounds;
+  TransparencyAttrib::Mode alpha_mode;
+
+  if (_render_mode == RM_texture) {
+    // Render the glyph if necessary.
+    if (slot->format != ft_glyph_format_bitmap) {
+      FT_Render_Glyph(slot, ft_render_mode_normal);
+    }
+
+    tex_x_size = bitmap.width;
+    tex_y_size = bitmap.rows;
+    tex_x_orig = slot->bitmap_left;
+    tex_y_orig = slot->bitmap_top;
+    alpha_mode = TransparencyAttrib::M_alpha;
+
+  } else {
+    // Calculate suitable texture dimensions for the signed distance field.
+    // This is the same calculation that Freetype uses in its bitmap renderer.
+    FT_Outline_Get_CBox(&slot->outline, &bounds);
+
+    bounds.xMin = bounds.xMin & ~63;
+    bounds.yMin = bounds.yMin & ~63;
+    bounds.xMax = (bounds.xMax + 63) & ~63;
+    bounds.yMax = (bounds.yMax + 63) & ~63;
+
+    tex_x_size = (bounds.xMax - bounds.xMin) >> 6;
+    tex_y_size = (bounds.yMax - bounds.yMin) >> 6;
+    tex_x_orig = (bounds.xMin >> 6);
+    tex_y_orig = (bounds.yMax >> 6);
+    alpha_mode = TransparencyAttrib::M_binary;
   }
   }
 
 
-  if (bitmap.width == 0 || bitmap.rows == 0) {
+  if (tex_x_size == 0 || tex_y_size == 0) {
     // If we got an empty bitmap, it's a special case.
     // If we got an empty bitmap, it's a special case.
 
 
     PT(TextGlyph) glyph =
     PT(TextGlyph) glyph =
@@ -562,13 +558,32 @@ make_glyph(int character, FT_Face face, int glyph_index) {
   } else {
   } else {
     DynamicTextGlyph *glyph;
     DynamicTextGlyph *glyph;
 
 
-    PN_stdfloat tex_x_size = bitmap.width;
-    PN_stdfloat tex_y_size = bitmap.rows;
-
     int outline = 0;
     int outline = 0;
 
 
-    if (_tex_pixels_per_unit == _font_pixels_per_unit &&
-        !_needs_image_processing) {
+    if (_render_mode == RM_distance_field) {
+      tex_x_size /= _scale_factor;
+      tex_y_size /= _scale_factor;
+      int int_x_size = (int)ceil(tex_x_size);
+      int int_y_size = (int)ceil(tex_y_size);
+
+      outline = 4;
+      int_x_size += outline * 2;
+      int_y_size += outline * 2;
+      tex_x_size += outline * 2;
+      tex_y_size += outline * 2;
+
+      PNMImage image(int_x_size, int_y_size, PNMImage::CT_grayscale);
+      render_distance_field(image, outline, bounds.xMin, bounds.yMin);
+
+      glyph = slot_glyph(character, int_x_size, int_y_size, advance);
+      if (!_needs_image_processing) {
+        copy_pnmimage_to_texture(image, glyph);
+      } else {
+        blend_pnmimage_to_texture(image, glyph, _fg);
+      }
+
+    } else if (_tex_pixels_per_unit == _font_pixels_per_unit &&
+               !_needs_image_processing) {
       // If the bitmap produced from the font doesn't require scaling
       // If the bitmap produced from the font doesn't require scaling
       // or any other processing before it goes to the texture, we can
       // or any other processing before it goes to the texture, we can
       // just copy it directly into the texture.
       // just copy it directly into the texture.
@@ -615,8 +630,8 @@ make_glyph(int character, FT_Face face, int glyph_index) {
 
 
     DynamicTextPage *page = glyph->get_page();
     DynamicTextPage *page = glyph->get_page();
     if (page != NULL) {
     if (page != NULL) {
-      int bitmap_top = (int)floor(slot->bitmap_top + outline * _scale_factor + 0.5f);
-      int bitmap_left = (int)floor(slot->bitmap_left - outline * _scale_factor + 0.5f);
+      int bitmap_top = (int)floor(tex_y_orig + outline * _scale_factor + 0.5f);
+      int bitmap_left = (int)floor(tex_x_orig - outline * _scale_factor + 0.5f);
 
 
       tex_x_size += glyph->_margin * 2;
       tex_x_size += glyph->_margin * 2;
       tex_y_size += glyph->_margin * 2;
       tex_y_size += glyph->_margin * 2;
@@ -644,7 +659,7 @@ make_glyph(int character, FT_Face face, int glyph_index) {
 
 
       CPT(RenderState) state;
       CPT(RenderState) state;
       state = RenderState::make(TextureAttrib::make(page),
       state = RenderState::make(TextureAttrib::make(page),
-                                TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+                                TransparencyAttrib::make(alpha_mode));
       state = state->add_attrib(ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 1.0f)), -1);
       state = state->add_attrib(ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 1.0f)), -1);
 
 
       glyph->set_quad(dimensions, texcoords, state);
       glyph->set_quad(dimensions, texcoords, state);
@@ -1123,179 +1138,4 @@ render_polygon_contours(TextGlyph *glyph, bool face, bool extrude) {
   _contours.clear();
   _contours.clear();
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::outline_move_to
-//       Access: Private, Static
-//  Description: A callback from FT_Outline_Decompose().  It marks the
-//               beginning of a new contour.
-////////////////////////////////////////////////////////////////////
-int DynamicTextFont::
-outline_move_to(const FT_Vector *to, void *user) {
-  DynamicTextFont *self = (DynamicTextFont *)user;
-
-  // Convert from 26.6 pixel units to Panda units.
-  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
-  LPoint2 p = LPoint2(to->x, to->y) * scale;
-
-  if (self->_contours.empty() ||
-      !self->_contours.back()._points.empty()) {
-    self->_contours.push_back(Contour());
-  }
-  self->_q = p;
-  return 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::outline_line_to
-//       Access: Private, Static
-//  Description: A callback from FT_Outline_Decompose().  It marks a
-//               straight line in the contour.
-////////////////////////////////////////////////////////////////////
-int DynamicTextFont::
-outline_line_to(const FT_Vector *to, void *user) {
-  DynamicTextFont *self = (DynamicTextFont *)user;
-  nassertr(!self->_contours.empty(), 1);
-
-  // Convert from 26.6 pixel units to Panda units.
-  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
-  LPoint2 p = LPoint2(to->x, to->y) * scale;
-
-  // Compute the tangent: this is just the vector from the last point.
-  LVector2 t = (p - self->_q);
-  t.normalize();
-
-  if (self->_contours.back()._points.empty()) {
-    self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2::zero(), t));
-  } else {
-    self->_contours.back()._points.back().connect_to(t);
-  }
-  
-  self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2::zero()));
-  self->_q = p;
-  return 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::outline_conic_to
-//       Access: Private, Static
-//  Description: A callback from FT_Outline_Decompose().  It marks a
-//               parabolic (3rd-order) Bezier curve in the contour.
-////////////////////////////////////////////////////////////////////
-int DynamicTextFont::
-outline_conic_to(const FT_Vector *control,
-                 const FT_Vector *to, void *user) {
-  DynamicTextFont *self = (DynamicTextFont *)user;
-  nassertr(!self->_contours.empty(), 1);
-
-  // Convert from 26.6 pixel units to Panda units.
-  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
-
-  LPoint2 c = LPoint2(control->x, control->y) * scale;
-  LPoint2 p = LPoint2(to->x, to->y) * scale;
-
-  // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
-  NurbsCurveEvaluator nce;
-  nce.local_object();
-  nce.set_order(3);
-  nce.reset(3);
-  nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
-  nce.set_vertex(1, LVecBase3(c[0], c[1], 0.0f));
-  nce.set_vertex(2, LVecBase3(p[0], p[1], 0.0f));
-
-  self->_q = p;
-
-  PT(NurbsCurveResult) ncr = nce.evaluate();
-  return self->outline_nurbs(ncr);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::outline_cubic_to
-//       Access: Private, Static
-//  Description: A callback from FT_Outline_Decompose().  It marks a
-//               cubic (4th-order) Bezier curve in the contour.
-////////////////////////////////////////////////////////////////////
-int DynamicTextFont::
-outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
-                 const FT_Vector *to, void *user) {
-  DynamicTextFont *self = (DynamicTextFont *)user;
-  nassertr(!self->_contours.empty(), 1);
-
-  // Convert from 26.6 pixel units to Panda units.
-  PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
-
-  LPoint2 c1 = LPoint2(control1->x, control1->y) * scale;
-  LPoint2 c2 = LPoint2(control2->x, control2->y) * scale;
-  LPoint2 p = LPoint2(to->x, to->y) * scale;
-
-  // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
-  NurbsCurveEvaluator nce;
-  nce.local_object();
-  nce.set_order(4);
-  nce.reset(4);
-  nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
-  nce.set_vertex(1, LVecBase3(c1[0], c1[1], 0.0f));
-  nce.set_vertex(2, LVecBase3(c2[0], c2[1], 0.0f));
-  nce.set_vertex(3, LVecBase3(p[0], p[1], 0.0f));
-
-  self->_q = p;
-
-  PT(NurbsCurveResult) ncr = nce.evaluate();
-  return self->outline_nurbs(ncr);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DynamicTextFont::outline_nurbs
-//       Access: Private
-//  Description: Called internally by outline_cubic_to() and
-//               outline_conic_to().
-////////////////////////////////////////////////////////////////////
-int DynamicTextFont::
-outline_nurbs(NurbsCurveResult *ncr) {
-  // Sample it down so that the lines approximate the curve to within
-  // a "pixel."
-  ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
-
-  int num_samples = ncr->get_num_samples();
-
-  bool needs_connect = false;
-  int start = 1;
-  if (_contours.back()._points.empty()) {
-    // If we haven't got the first point of this contour yet, we must
-    // add it now.
-    start = 0;
-  } else {
-    needs_connect = true;
-  }
-
-  for (int i = start; i < num_samples; ++i) {
-    PN_stdfloat st = ncr->get_sample_t(i);
-    const LPoint3 &p = ncr->get_sample_point(i);
-
-    PN_stdfloat st0 = st, st1 = st;
-    if (i > 0) {
-      st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
-    }
-    if (i < num_samples - 1) {
-      st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
-    }
-    // Compute the tangent by deltaing nearby points.  Don't evaluate
-    // the tangent from the NURBS, since that doesn't appear to be
-    // reliable.
-    LPoint3 p0, p1;
-    ncr->eval_point(st0, p0);
-    ncr->eval_point(st1, p1);
-    LVector3 t = p1 - p0;
-    t.normalize();
-
-    if (needs_connect) {
-      _contours.back()._points.back().connect_to(LVector2(t[0], t[1]));
-      needs_connect = false;
-    }
-
-    _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
-  }
-
-  return 0;
-}
-
 #endif  // HAVE_FREETYPE
 #endif  // HAVE_FREETYPE

+ 2 - 37
panda/src/text/dynamicTextFont.h

@@ -99,10 +99,7 @@ PUBLISHED:
 
 
   INLINE void set_render_mode(RenderMode render_mode);
   INLINE void set_render_mode(RenderMode render_mode);
   INLINE RenderMode get_render_mode() const;
   INLINE RenderMode get_render_mode() const;
-  INLINE void set_winding_order(WindingOrder winding_order);
-  INLINE WindingOrder get_winding_order() const;
   MAKE_PROPERTY(render_mode, get_render_mode, set_render_mode);
   MAKE_PROPERTY(render_mode, get_render_mode, set_render_mode);
-  MAKE_PROPERTY(winding_order, get_winding_order, set_winding_order);
 
 
   INLINE void set_fg(const LColor &fg);
   INLINE void set_fg(const LColor &fg);
   INLINE const LColor &get_fg() const;
   INLINE const LColor &get_fg() const;
@@ -134,7 +131,7 @@ private:
   void initialize();
   void initialize();
   void update_filters();
   void update_filters();
   void determine_tex_format();
   void determine_tex_format();
-  TextGlyph *make_glyph(int character, FT_Face face, int glyph_index);
+  CPT(TextGlyph) make_glyph(int character, FT_Face face, int glyph_index);
   void copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph);
   void copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph);
   void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph);
   void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph);
   void blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
   void blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
@@ -144,15 +141,6 @@ private:
   void render_wireframe_contours(TextGlyph *glyph);
   void render_wireframe_contours(TextGlyph *glyph);
   void render_polygon_contours(TextGlyph *glyph, bool face, bool extrude);
   void render_polygon_contours(TextGlyph *glyph, bool face, bool extrude);
 
 
-  static int outline_move_to(const FT_Vector *to, void *user);
-  static int outline_line_to(const FT_Vector *to, void *user);
-  static int outline_conic_to(const FT_Vector *control,
-                              const FT_Vector *to, void *user);
-  static int outline_cubic_to(const FT_Vector *control1, 
-                              const FT_Vector *control2, 
-                              const FT_Vector *to, void *user);
-  int outline_nurbs(NurbsCurveResult *ncr);
-
   int _texture_margin;
   int _texture_margin;
   PN_stdfloat _poly_margin;
   PN_stdfloat _poly_margin;
   LVecBase2i _page_size;
   LVecBase2i _page_size;
@@ -162,7 +150,6 @@ private:
   int _anisotropic_degree;
   int _anisotropic_degree;
 
 
   RenderMode _render_mode;
   RenderMode _render_mode;
-  WindingOrder _winding_order;
 
 
   LColor _fg, _bg, _outline_color;
   LColor _fg, _bg, _outline_color;
   PN_stdfloat _outline_width;
   PN_stdfloat _outline_width;
@@ -177,7 +164,7 @@ private:
 
 
   // This doesn't need to be a reference-counting pointer, because the
   // This doesn't need to be a reference-counting pointer, because the
   // reference to each glyph is kept by the DynamicTextPage object.
   // reference to each glyph is kept by the DynamicTextPage object.
-  typedef pmap<int, TextGlyph *> Cache;
+  typedef pmap<int, const TextGlyph *> Cache;
   Cache _cache;
   Cache _cache;
 
 
   // This is a list of the glyphs that do not have any printable
   // This is a list of the glyphs that do not have any printable
@@ -187,28 +174,6 @@ private:
   typedef pvector< PT(TextGlyph) > EmptyGlyphs;
   typedef pvector< PT(TextGlyph) > EmptyGlyphs;
   EmptyGlyphs _empty_glyphs;
   EmptyGlyphs _empty_glyphs;
 
 
-  class ContourPoint {
-  public:
-    INLINE ContourPoint(const LPoint2 &p, const LVector2 &in, 
-                        const LVector2 &out);
-    INLINE ContourPoint(PN_stdfloat px, PN_stdfloat py, PN_stdfloat tx, PN_stdfloat ty);
-    INLINE void connect_to(const LVector2 &out);
-    LPoint2 _p;
-    LVector2 _in, _out;  // tangents into and out of the vertex.
-  };
-  typedef pvector<ContourPoint> Points;
-
-  class Contour {
-  public:
-    Points _points;
-    bool _is_solid;
-    int _start_vertex;
-  };
-
-  typedef pvector<Contour> Contours;
-  Contours _contours;
-  LPoint2 _q;  // The "current point".
-
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;

+ 3 - 1
panda/src/text/staticTextFont.cxx

@@ -343,7 +343,9 @@ find_characters(PandaNode *root, const RenderState *net_state) {
       // Get the first vertex from the "dot" geoset.  This will be the
       // Get the first vertex from the "dot" geoset.  This will be the
       // design size indicator.
       // design size indicator.
       GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
       GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
-      _line_height = reader.get_data3()[2];
+      LVecBase3 data = reader.get_data3();
+      _line_height = data[2];
+      _total_poly_margin = data[0];
       _space_advance = 0.25f * _line_height;
       _space_advance = 0.25f * _line_height;
     }
     }
 
 

+ 12 - 6
panda/src/text/textAssembler.cxx

@@ -2232,28 +2232,34 @@ tack_on_accent(wchar_t accent_mark, TextAssembler::CheesyPosition position,
           has_mat = false;
           has_mat = false;
         }
         }
 
 
+        PN_stdfloat total_margin = font->get_total_poly_margin();
+
         LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
         LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
-        PN_stdfloat accent_height = max_accent[2] - min_accent[2];
+        PN_stdfloat accent_height = max_accent[2] - min_accent[2] - total_margin * 2;
+        PN_stdfloat accent_x = centroid[0] - accent_centroid[0];
         PN_stdfloat accent_y = 0;
         PN_stdfloat accent_y = 0;
+        PN_stdfloat min_y = min_vert[2] + total_margin;
+        PN_stdfloat max_y = max_vert[2] - total_margin;
+
         switch (position) {
         switch (position) {
         case CP_above:
         case CP_above:
           // A little above the character.
           // A little above the character.
-          accent_y = max_vert[2] - accent_centroid[2] + accent_height * 0.5f;
+          accent_y = max_y - accent_centroid[2] + accent_height * 0.75f;
           break;
           break;
 
 
         case CP_below:
         case CP_below:
           // A little below the character.
           // A little below the character.
-          accent_y = min_vert[2] - accent_centroid[2] - accent_height * 0.5f;
+          accent_y = min_y - accent_centroid[2] - accent_height * 0.75f;
           break;
           break;
 
 
         case CP_top:
         case CP_top:
           // Touching the top of the character.
           // Touching the top of the character.
-          accent_y = max_vert[2] - accent_centroid[2];
+          accent_y = max_y - accent_centroid[2];
           break;
           break;
 
 
         case CP_bottom:
         case CP_bottom:
           // Touching the bottom of the character.
           // Touching the bottom of the character.
-          accent_y = min_vert[2] - accent_centroid[2];
+          accent_y = min_y - accent_centroid[2];
           break;
           break;
 
 
         case CP_within:
         case CP_within:
@@ -2262,7 +2268,7 @@ tack_on_accent(wchar_t accent_mark, TextAssembler::CheesyPosition position,
           break;
           break;
         }
         }
 
 
-        placement._xpos += placement._scale * (centroid[0] - accent_centroid[0] + placement._slant * accent_y);
+        placement._xpos += placement._scale * (accent_x + placement._slant * accent_y);
         placement._ypos += placement._scale * accent_y;
         placement._ypos += placement._scale * accent_y;
 
 
         if (has_mat) {
         if (has_mat) {

+ 12 - 0
panda/src/text/textFont.I

@@ -77,6 +77,18 @@ set_space_advance(PN_stdfloat space_advance) {
   _space_advance = space_advance;
   _space_advance = space_advance;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextFont::get_total_poly_margin
+//       Access: Public
+//  Description: Returns the total margin between the edge of the
+//               glyph and the edge of the cards.  This includes
+//               _poly_margin and any additional outline.
+////////////////////////////////////////////////////////////////////
+INLINE PN_stdfloat TextFont::
+get_total_poly_margin() const {
+  return _total_poly_margin;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DynamicTextFont::get_glyph
 //     Function: DynamicTextFont::get_glyph
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 7 - 55
panda/src/text/textFont.cxx

@@ -34,6 +34,7 @@ TextFont() {
   _is_valid = false;
   _is_valid = false;
   _line_height = 1.0f;
   _line_height = 1.0f;
   _space_advance = 0.25f;
   _space_advance = 0.25f;
+  _total_poly_margin = 0.0f;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -46,7 +47,8 @@ TextFont(const TextFont &copy) :
   Namable(copy),
   Namable(copy),
   _is_valid(copy._is_valid),
   _is_valid(copy._is_valid),
   _line_height(copy._line_height),
   _line_height(copy._line_height),
-  _space_advance(copy._space_advance)
+  _space_advance(copy._space_advance),
+  _total_poly_margin(copy._total_poly_margin)
 {
 {
 }
 }
 
 
@@ -112,31 +114,13 @@ string_render_mode(const string &string) {
     return RM_extruded;
     return RM_extruded;
   } else if (cmp_nocase_uh(string, "solid") == 0) {
   } else if (cmp_nocase_uh(string, "solid") == 0) {
     return RM_solid;
     return RM_solid;
+  } else if (cmp_nocase_uh(string, "distance_field") == 0) {
+    return RM_distance_field;
   } else {
   } else {
     return RM_invalid;
     return RM_invalid;
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: TextFont::string_winding_order
-//       Access: Public
-//  Description: Returns the WindingOrder value associated with the given
-//               string representation, or WO_invalid if the string
-//               does not match any known WindingOrder value.
-////////////////////////////////////////////////////////////////////
-TextFont::WindingOrder TextFont::
-string_winding_order(const string &string) {
-  if (cmp_nocase_uh(string, "default") == 0) {
-    return WO_default;
-  } else if (cmp_nocase_uh(string, "left") == 0) {
-    return WO_left;
-  } else if (cmp_nocase_uh(string, "right") == 0) {
-    return WO_right;
-  } else {
-    return WO_invalid;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextFont::make_invalid_glyph
 //     Function: TextFont::make_invalid_glyph
 //       Access: Private
 //       Access: Private
@@ -184,6 +168,8 @@ operator << (ostream &out, TextFont::RenderMode rm) {
     return out << "extruded";
     return out << "extruded";
   case TextFont::RM_solid:
   case TextFont::RM_solid:
     return out << "solid";
     return out << "solid";
+  case TextFont::RM_distance_field:
+    return out << "distance-field";
 
 
   case TextFont::RM_invalid:
   case TextFont::RM_invalid:
     return out << "invalid";
     return out << "invalid";
@@ -204,37 +190,3 @@ operator >> (istream &in, TextFont::RenderMode &rm) {
   rm = TextFont::string_render_mode(word);
   rm = TextFont::string_render_mode(word);
   return in;
   return in;
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: TextFont::WindingOrder output operator
-//  Description:
-////////////////////////////////////////////////////////////////////
-ostream &
-operator << (ostream &out, TextFont::WindingOrder wo) {
-  switch (wo) {
-  case TextFont::WO_default:
-    return out << "default";
-  case TextFont::WO_left:
-    return out << "left";
-  case TextFont::WO_right:
-    return out << "right";
-
-  case TextFont::WO_invalid:
-    return out << "invalid";
-  }
-
-  return out << "(**invalid TextFont::WindingOrder(" << (int)wo << ")**)";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: TextFont::WindingOrder input operator
-//  Description:
-////////////////////////////////////////////////////////////////////
-istream &
-operator >> (istream &in, TextFont::WindingOrder &wo) {
-  string word;
-  in >> word;
-
-  wo = TextFont::string_winding_order(word);
-  return in;
-}

+ 5 - 11
panda/src/text/textFont.h

@@ -57,18 +57,12 @@ PUBLISHED:
     // combination of RM_extruded and RM_polygon
     // combination of RM_extruded and RM_polygon
     RM_solid,
     RM_solid,
 
 
+    RM_distance_field,
+
     // Returned by string_render_mode() for an invalid match.
     // Returned by string_render_mode() for an invalid match.
     RM_invalid,
     RM_invalid,
   };
   };
 
 
-  enum WindingOrder {
-    WO_default,
-    WO_left,
-    WO_right,
-
-    WO_invalid,
-  };
-
   virtual PT(TextFont) make_copy() const=0;
   virtual PT(TextFont) make_copy() const=0;
 
 
   INLINE bool is_valid() const;
   INLINE bool is_valid() const;
@@ -87,11 +81,12 @@ PUBLISHED:
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
 
 
 public:
 public:
+  INLINE PN_stdfloat get_total_poly_margin() const;
+
   virtual bool get_glyph(int character, CPT(TextGlyph) &glyph)=0;
   virtual bool get_glyph(int character, CPT(TextGlyph) &glyph)=0;
   TextGlyph *get_invalid_glyph();
   TextGlyph *get_invalid_glyph();
 
 
   static RenderMode string_render_mode(const string &string);
   static RenderMode string_render_mode(const string &string);
-  static WindingOrder string_winding_order(const string &string);
 
 
 private:
 private:
   void make_invalid_glyph();
   void make_invalid_glyph();
@@ -100,6 +95,7 @@ protected:
   bool _is_valid;
   bool _is_valid;
   PN_stdfloat _line_height;
   PN_stdfloat _line_height;
   PN_stdfloat _space_advance;
   PN_stdfloat _space_advance;
+  PN_stdfloat _total_poly_margin;
   PT(TextGlyph) _invalid_glyph;
   PT(TextGlyph) _invalid_glyph;
 
 
 public:
 public:
@@ -122,8 +118,6 @@ private:
 
 
 EXPCL_PANDA_TEXT ostream &operator << (ostream &out, TextFont::RenderMode rm);
 EXPCL_PANDA_TEXT ostream &operator << (ostream &out, TextFont::RenderMode rm);
 EXPCL_PANDA_TEXT istream &operator >> (istream &in, TextFont::RenderMode &rm);
 EXPCL_PANDA_TEXT istream &operator >> (istream &in, TextFont::RenderMode &rm);
-EXPCL_PANDA_TEXT ostream &operator << (ostream &out, TextFont::WindingOrder wo);
-EXPCL_PANDA_TEXT istream &operator >> (istream &in, TextFont::WindingOrder &wo);
 
 
 #include "textFont.I"
 #include "textFont.I"
 
 

+ 1 - 1
panda/src/text/textGlyph.cxx

@@ -132,7 +132,7 @@ set_quad(const LVecBase4 &dimensions, const LVecBase4 &texcoords,
 void TextGlyph::
 void TextGlyph::
 set_geom(GeomVertexData *vdata, GeomPrimitive *prim, 
 set_geom(GeomVertexData *vdata, GeomPrimitive *prim, 
          const RenderState *state) {
          const RenderState *state) {
-  PT(Geom) geom = new Geom(vdata);
+  PT(Geom) geom = new GeomTextGlyph(this, vdata);
   geom->add_primitive(prim);
   geom->add_primitive(prim);
   _geom = geom;
   _geom = geom;
 
 

+ 30 - 7
pandatool/src/egg-mkfont/eggMakeFont.cxx

@@ -115,7 +115,7 @@ EggMakeFont() : EggWriter(true, false) {
      "generated texture map that are used for each onscreen unit (or each "
      "generated texture map that are used for each onscreen unit (or each "
      "10 points of font; see -ps).  Setting this number larger results in "
      "10 points of font; see -ps).  Setting this number larger results in "
      "an easier-to-read font, but at the cost of more texture memory.  "
      "an easier-to-read font, but at the cost of more texture memory.  "
-     "The default is 30.",
+     "The default is 40.",
      &EggMakeFont::dispatch_double, NULL, &_pixels_per_unit);
      &EggMakeFont::dispatch_double, NULL, &_pixels_per_unit);
 
 
   add_option
   add_option
@@ -125,6 +125,12 @@ EggMakeFont() : EggWriter(true, false) {
      "a 10 point font is 1 screen unit high, so the default is 10.",
      "a 10 point font is 1 screen unit high, so the default is 10.",
      &EggMakeFont::dispatch_double, NULL, &_point_size);
      &EggMakeFont::dispatch_double, NULL, &_point_size);
 
 
+  add_option
+    ("sdf", "", 0,
+     "If this is set, a signed distance field will be generated, which "
+     "results in crisp text even when the text is enlarged or zoomed in.",
+     &EggMakeFont::dispatch_true, NULL, &_generate_distance_field);
+
   add_option
   add_option
     ("pm", "n", 0,
     ("pm", "n", 0,
      "The number of extra pixels around a single character in the "
      "The number of extra pixels around a single character in the "
@@ -216,13 +222,14 @@ EggMakeFont() : EggWriter(true, false) {
   _fg.set(1.0, 1.0, 1.0, 1.0);
   _fg.set(1.0, 1.0, 1.0, 1.0);
   _bg.set(1.0, 1.0, 1.0, 0.0);
   _bg.set(1.0, 1.0, 1.0, 0.0);
   _interior.set(1.0, 1.0, 1.0, 1.0);
   _interior.set(1.0, 1.0, 1.0, 1.0);
-  _pixels_per_unit = 30.0;
+  _pixels_per_unit = 40.0;
   _point_size = 10.0;
   _point_size = 10.0;
   _poly_margin = 1.0;
   _poly_margin = 1.0;
   _tex_margin = 2;
   _tex_margin = 2;
   _render_margin = 0.0;
   _render_margin = 0.0;
-  _palette_size[0] = _palette_size[1] = 256;
+  _palette_size[0] = _palette_size[1] = 512;
   _face_index = 0;
   _face_index = 0;
+  _generate_distance_field = false;
 
 
   _text_maker = NULL;
   _text_maker = NULL;
   _vpool = NULL;
   _vpool = NULL;
@@ -275,7 +282,9 @@ run() {
   if (!_got_scale_factor) {
   if (!_got_scale_factor) {
     // The default scale factor is 4 if we are not using FreeType's
     // The default scale factor is 4 if we are not using FreeType's
     // antialias, or 2 if we are.
     // antialias, or 2 if we are.
-    if (_no_native_aa) {
+    if (_generate_distance_field) {
+      _scale_factor = 1.0;
+    } else if (_no_native_aa) {
       _scale_factor = 4.0;
       _scale_factor = 4.0;
     } else {
     } else {
       _scale_factor = 2.0;
       _scale_factor = 2.0;
@@ -410,7 +419,7 @@ run() {
       _num_channels = 1;
       _num_channels = 1;
       _format = EggTexture::F_luminance;
       _format = EggTexture::F_luminance;
     }
     }
-  }      
+  }
 
 
   // Create a global Palettizer object.  We'll use this even if the
   // Create a global Palettizer object.  We'll use this even if the
   // user specified -nopal, if nothing else just to hold all of the
   // user specified -nopal, if nothing else just to hold all of the
@@ -449,10 +458,24 @@ run() {
   _group->set_switch_flag(true);
   _group->set_switch_flag(true);
   _group->set_switch_fps(2.0);
   _group->set_switch_fps(2.0);
 
 
-  // Also create an egg group indicating the font's design size.
+  double margin = _poly_margin;
+  if (_generate_distance_field) {
+    // Distance fields are always rendered with binary alpha.
+    _group->set_alpha_mode(EggRenderMode::AM_binary);
+
+    // Fudged to make most fonts fit on 512x256.
+    if (_poly_margin >= 1) {
+      margin += 3.5;
+      _poly_margin -= 0.5;
+    }
+
+    _text_maker->set_distance_field_radius(4);
+  }
+
+  // Also create an egg group indicating the font's design size and poly margin.
   EggGroup *ds_group = new EggGroup("ds");
   EggGroup *ds_group = new EggGroup("ds");
   _group->add_child(ds_group);
   _group->add_child(ds_group);
-  EggVertex *vtx = make_vertex(LPoint2d(0.0, _text_maker->get_line_height()));
+  EggVertex *vtx = make_vertex(LPoint2d(margin / _pixels_per_unit, _text_maker->get_line_height()));
   EggPoint *point = new EggPoint;
   EggPoint *point = new EggPoint;
   ds_group->add_child(point);
   ds_group->add_child(point);
   point->add_vertex(vtx);
   point->add_vertex(vtx);

+ 1 - 0
pandatool/src/egg-mkfont/eggMakeFont.h

@@ -78,6 +78,7 @@ private:
   bool _no_native_aa;
   bool _no_native_aa;
   bool _no_palettize;
   bool _no_palettize;
   int _palette_size[2];
   int _palette_size[2];
+  bool _generate_distance_field;
 
 
   double _palettize_scale_factor;
   double _palettize_scale_factor;
   Filename _input_font_filename;
   Filename _input_font_filename;