Panda3D

dynamicTextFont.cxx

00001 // Filename: dynamicTextFont.cxx
00002 // Created by:  drose (08Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "dynamicTextFont.h"
00016 
00017 #ifdef HAVE_FREETYPE
00018 
00019 #undef interface  // I don't know where this symbol is defined, but it interferes with FreeType.
00020 #include FT_OUTLINE_H 
00021 #ifdef FT_BBOX_H
00022 #include FT_BBOX_H
00023 #endif
00024 #ifdef FT_BITMAP_H
00025 #include FT_BITMAP_H
00026 #endif
00027 #ifdef FT_STROKER_H
00028 #include FT_STROKER_H
00029 #endif
00030 
00031 #include "config_text.h"
00032 #include "config_util.h"
00033 #include "config_express.h"
00034 #include "virtualFileSystem.h"
00035 #include "geomVertexData.h"
00036 #include "geomVertexFormat.h"
00037 #include "geomVertexWriter.h"
00038 #include "geomLinestrips.h"
00039 #include "geomTriangles.h"
00040 #include "renderState.h"
00041 #include "string_utils.h"
00042 #include "triangulator.h"
00043 #include "nurbsCurveEvaluator.h"
00044 #include "nurbsCurveResult.h"
00045 //#include "renderModeAttrib.h"
00046 //#include "antialiasAttrib.h"
00047 
00048 TypeHandle DynamicTextFont::_type_handle;
00049 
00050 
00051 ////////////////////////////////////////////////////////////////////
00052 //     Function: DynamicTextFont::Constructor
00053 //       Access: Published
00054 //  Description: The constructor expects the name of some font file
00055 //               that FreeType can read, along with face_index,
00056 //               indicating which font within the file to load
00057 //               (usually 0).
00058 ////////////////////////////////////////////////////////////////////
00059 DynamicTextFont::
00060 DynamicTextFont(const Filename &font_filename, int face_index) {
00061   initialize();
00062   _is_valid = load_font(font_filename, face_index);
00063   TextFont::set_name(FreetypeFont::get_name());
00064   TextFont::_line_height = FreetypeFont::get_line_height();
00065   TextFont::_space_advance = FreetypeFont::get_space_advance();
00066 
00067   _fg.set(1.0f, 1.0f, 1.0f, 1.0f);
00068   _bg.set(1.0f, 1.0f, 1.0f, 0.0f);
00069   _outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
00070   _outline_width = 0.0f;
00071   _outline_feather = 0.0f;
00072   _has_outline = false;
00073   _tex_format = Texture::F_alpha;
00074   _needs_image_processing = false;
00075 }
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: DynamicTextFont::Constructor
00079 //       Access: Published
00080 //  Description: This constructor accepts a table of data representing
00081 //               the font file, loaded from some source other than a
00082 //               filename on disk.
00083 ////////////////////////////////////////////////////////////////////
00084 DynamicTextFont::
00085 DynamicTextFont(const char *font_data, int data_length, int face_index) {
00086   initialize();
00087   _is_valid = load_font(font_data, data_length, face_index);
00088   TextFont::set_name(FreetypeFont::get_name());
00089   TextFont::_line_height = FreetypeFont::_line_height;
00090   TextFont::_space_advance = FreetypeFont::_space_advance;
00091 
00092   _fg.set(1.0f, 1.0f, 1.0f, 1.0f);
00093   _bg.set(1.0f, 1.0f, 1.0f, 0.0f);
00094   _outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
00095   _outline_width = 0.0f;
00096   _outline_feather = 0.0f;
00097   _has_outline = false;
00098   _tex_format = Texture::F_alpha;
00099   _needs_image_processing = false;
00100 }
00101 
00102 ////////////////////////////////////////////////////////////////////
00103 //     Function: DynamicTextFont::Copy Constructor
00104 //       Access: Published
00105 //  Description: 
00106 ////////////////////////////////////////////////////////////////////
00107 DynamicTextFont::
00108 DynamicTextFont(const DynamicTextFont &copy) :
00109   TextFont(copy),
00110   FreetypeFont(copy),
00111   _texture_margin(copy._texture_margin),
00112   _poly_margin(copy._poly_margin),
00113   _page_x_size(copy._page_x_size),
00114   _page_y_size(copy._page_y_size),
00115   _minfilter(copy._minfilter),
00116   _magfilter(copy._magfilter),
00117   _anisotropic_degree(copy._anisotropic_degree),
00118   _render_mode(copy._render_mode),
00119   _winding_order(copy._winding_order),
00120   _fg(copy._fg),
00121   _bg(copy._bg),
00122   _outline_color(copy._outline_color),
00123   _outline_width(copy._outline_width),
00124   _outline_feather(copy._outline_feather),
00125   _has_outline(copy._has_outline),
00126   _tex_format(copy._tex_format),
00127   _needs_image_processing(copy._needs_image_processing),
00128   _preferred_page(0)
00129 {
00130 }
00131 
00132 ////////////////////////////////////////////////////////////////////
00133 //     Function: DynamicTextFont::Destructor
00134 //       Access: Published, Virtual
00135 //  Description: 
00136 ////////////////////////////////////////////////////////////////////
00137 DynamicTextFont::
00138 ~DynamicTextFont() {
00139 }
00140 
00141 ////////////////////////////////////////////////////////////////////
00142 //     Function: DynamicTextFont::make_copy
00143 //       Access: Published
00144 //  Description: Returns a new copy of the same font.
00145 ////////////////////////////////////////////////////////////////////
00146 PT(TextFont) DynamicTextFont::
00147 make_copy() const {
00148   return new DynamicTextFont(*this);
00149 }
00150 
00151 ////////////////////////////////////////////////////////////////////
00152 //     Function: DynamicTextFont::get_num_pages
00153 //       Access: Published
00154 //  Description: Returns the number of pages associated with the font.
00155 //               Initially, the font has zero pages; when the first
00156 //               piece of text is rendered with the font, it will add
00157 //               additional pages as needed.  Each page is a Texture
00158 //               object that contains the images for each of the
00159 //               glyphs currently in use somewhere.
00160 ////////////////////////////////////////////////////////////////////
00161 int DynamicTextFont::
00162 get_num_pages() const {
00163   return _pages.size();
00164 }
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: DynamicTextFont::get_page
00168 //       Access: Published
00169 //  Description: Returns the nth page associated with the font.
00170 //               Initially, the font has zero pages; when the first
00171 //               piece of text is rendered with the font, it will add
00172 //               additional pages as needed.  Each page is a Texture
00173 //               object that contains the images for each of the
00174 //               glyphs currently in use somewhere.
00175 ////////////////////////////////////////////////////////////////////
00176 DynamicTextPage *DynamicTextFont::
00177 get_page(int n) const {
00178   nassertr(n >= 0 && n < (int)_pages.size(), (DynamicTextPage *)NULL);
00179   return _pages[n];
00180 }
00181 
00182 ////////////////////////////////////////////////////////////////////
00183 //     Function: DynamicTextFont::garbage_collect
00184 //       Access: Published
00185 //  Description: Removes all of the glyphs from the font that are no
00186 //               longer being used by any Geoms.  Returns the number
00187 //               of glyphs removed.
00188 ////////////////////////////////////////////////////////////////////
00189 int DynamicTextFont::
00190 garbage_collect() {
00191   int removed_count = 0;
00192 
00193   // First, remove all the old entries from our cache index.
00194   Cache new_cache;
00195   Cache::iterator ci;
00196   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
00197     DynamicTextGlyph *glyph = (*ci).second;
00198     if (glyph->_geom_count != 0) {
00199       // Keep this one.
00200       new_cache.insert(new_cache.end(), (*ci));
00201     } else {
00202       // Drop this one.
00203       removed_count++;
00204     }
00205   }
00206   _cache.swap(new_cache);
00207 
00208   // Now, go through each page and do the same thing.
00209   Pages::iterator pi;
00210   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00211     DynamicTextPage *page = (*pi);
00212     page->garbage_collect(this);
00213   }
00214 
00215   return removed_count;
00216 }
00217 
00218 ////////////////////////////////////////////////////////////////////
00219 //     Function: DynamicTextFont::clear
00220 //       Access: Published
00221 //  Description: Drops all the glyphs out of the cache and frees any
00222 //               association with any previously-generated pages.
00223 //
00224 //               Calling this frequently can result in wasted texture
00225 //               memory, as any previously rendered text will still
00226 //               keep a pointer to the old, previously-generated
00227 //               pages.  As long as the previously rendered text
00228 //               remains around, the old pages will also remain
00229 //               around.
00230 ////////////////////////////////////////////////////////////////////
00231 void DynamicTextFont::
00232 clear() {
00233   _cache.clear();
00234   _pages.clear();
00235   _empty_glyphs.clear();
00236 }
00237 
00238 ////////////////////////////////////////////////////////////////////
00239 //     Function: DynamicTextFont::write
00240 //       Access: Published, Virtual
00241 //  Description:
00242 ////////////////////////////////////////////////////////////////////
00243 void DynamicTextFont::
00244 write(ostream &out, int indent_level) const {
00245   static const int max_glyph_name = 1024;
00246   char glyph_name[max_glyph_name];
00247 
00248   indent(out, indent_level)
00249     << "DynamicTextFont " << get_name() << ", " 
00250     << get_num_pages() << " pages, "
00251     << _cache.size() << " glyphs:\n";
00252   Cache::const_iterator ci;
00253   for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
00254     int glyph_index = (*ci).first;
00255     DynamicTextGlyph *glyph = (*ci).second;
00256     indent(out, indent_level + 2) 
00257       << glyph_index;
00258 
00259     FT_Face face = acquire_face();
00260     if (FT_HAS_GLYPH_NAMES(face)) {
00261       int error = FT_Get_Glyph_Name(face, glyph_index, 
00262                                     glyph_name, max_glyph_name);
00263 
00264       // Some fonts, notably MS Mincho, claim to have glyph names but
00265       // only report ".notdef" as the name of each glyph.  Thanks.
00266       if (!error && strcmp(glyph_name, ".notdef") != 0) {
00267         out << " (" << glyph_name << ")";
00268       }
00269     }
00270     release_face(face);
00271 
00272     out << ", count = " << glyph->_geom_count << "\n";
00273   }
00274 }
00275 
00276 ////////////////////////////////////////////////////////////////////
00277 //     Function: DynamicTextFont::get_glyph
00278 //       Access: Public, Virtual
00279 //  Description: Gets the glyph associated with the given character
00280 //               code, as well as an optional scaling parameter that
00281 //               should be applied to the glyph's geometry and advance
00282 //               parameters.  Returns true if the glyph exists, false
00283 //               if it does not.  Even if the return value is false,
00284 //               the value for glyph might be filled in with a
00285 //               printable glyph.
00286 ////////////////////////////////////////////////////////////////////
00287 bool DynamicTextFont::
00288 get_glyph(int character, const TextGlyph *&glyph) {
00289   if (!_is_valid) {
00290     glyph = (TextGlyph *)NULL;
00291     return false;
00292   }
00293 
00294   FT_Face face = acquire_face();
00295   int glyph_index = FT_Get_Char_Index(face, character);
00296   if (text_cat.is_spam()) {
00297     text_cat.spam()
00298       << *this << " maps " << character << " to glyph " << glyph_index << "\n";
00299   }
00300 
00301   Cache::iterator ci = _cache.find(glyph_index);
00302   if (ci != _cache.end()) {
00303     glyph = (*ci).second;
00304   } else {
00305     DynamicTextGlyph *dynamic_glyph = make_glyph(character, face, glyph_index);
00306     _cache.insert(Cache::value_type(glyph_index, dynamic_glyph));
00307     glyph = dynamic_glyph;
00308   }
00309 
00310   if (glyph == (DynamicTextGlyph *)NULL) {
00311     glyph = get_invalid_glyph();
00312     glyph_index = 0;
00313   }
00314     
00315   release_face(face);
00316   return (glyph_index != 0);
00317 }
00318 
00319 
00320 ////////////////////////////////////////////////////////////////////
00321 //     Function: DynamicTextFont::initialize
00322 //       Access: Private
00323 //  Description: Called from both constructors to set up some initial
00324 //               values.
00325 ////////////////////////////////////////////////////////////////////
00326 void DynamicTextFont::
00327 initialize() {
00328   _texture_margin = text_texture_margin;
00329   _poly_margin = text_poly_margin;
00330   _page_x_size = text_page_size[0];
00331   _page_y_size = text_page_size[1];
00332 
00333   // We don't necessarily want to use mipmaps, since we don't want to
00334   // regenerate those every time the texture changes, but we probably
00335   // do want at least linear filtering.  Use whatever the Configrc
00336   // file suggests.
00337   _minfilter = text_minfilter;
00338   _magfilter = text_magfilter;
00339 
00340   // Anisotropic filtering can help the look of the text, and doesn't
00341   // require generating mipmaps, but does require hardware support.
00342   _anisotropic_degree = text_anisotropic_degree;
00343 
00344   _render_mode = text_render_mode;
00345   _winding_order = WO_default;
00346 
00347   _preferred_page = 0;
00348 }
00349 
00350 ////////////////////////////////////////////////////////////////////
00351 //     Function: DynamicTextFont::update_filters
00352 //       Access: Private
00353 //  Description: Reapplies all current filter settings to all of the
00354 //               pages.  This is normally called whenever the filter
00355 //               settings change.
00356 ////////////////////////////////////////////////////////////////////
00357 void DynamicTextFont::
00358 update_filters() {
00359   Pages::iterator pi;
00360   for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
00361     DynamicTextPage *page = (*pi);
00362     page->set_minfilter(_minfilter);
00363     page->set_magfilter(_magfilter);
00364     page->set_anisotropic_degree(_anisotropic_degree);
00365   }
00366 }
00367 
00368 ////////////////////////////////////////////////////////////////////
00369 //     Function: DynamicTextFont::determine_tex_format
00370 //       Access: Private
00371 //  Description: Examines the _fg, _bg, and _outline colors to
00372 //               determine the appropriate format for the font pages,
00373 //               including the outline properties.
00374 ////////////////////////////////////////////////////////////////////
00375 void DynamicTextFont::
00376 determine_tex_format() {
00377   nassertv(get_num_pages() == 0);
00378 
00379   _has_outline = (_outline_color != _bg && _outline_width > 0.0f);
00380   _needs_image_processing = true;
00381 
00382   bool needs_color = false;
00383   bool needs_grayscale = false;
00384   bool needs_alpha = false;
00385 
00386   if (_fg[1] != _fg[0] || _fg[2] != _fg[0] ||
00387       _bg[1] != _bg[0] || _bg[2] != _bg[0] ||
00388       (_has_outline && (_outline_color[1] != _outline_color[0] || _outline_color[2] != _outline_color[0]))) {
00389     // At least one of fg, bg, or outline contains a color, not just a
00390     // grayscale value.
00391     needs_color = true;
00392 
00393   } else if (_fg[0] != 1.0f || _fg[1] != 1.0f || _fg[2] != 1.0f ||
00394              _bg[0] != 1.0f || _bg[1] != 1.0f || _bg[2] != 1.0f ||
00395              (_has_outline && (_outline_color[0] != 1.0f || _outline_color[1] != 1.0f || _outline_color[2] != 1.0f))) {
00396     // fg, bg, and outline contain non-white grayscale values.
00397     needs_grayscale = true;
00398   }
00399 
00400   if (_fg[3] != 1.0f || _bg[3] != 1.0f || 
00401       (_has_outline && (_outline_color[3] != 1.0f))) {
00402     // fg, bg, and outline contain non-opaque alpha values.
00403     needs_alpha = true;
00404   }
00405 
00406   if (needs_color) {
00407     if (needs_alpha) {
00408       _tex_format = Texture::F_rgba;
00409     } else {
00410       _tex_format = Texture::F_rgb;
00411     }
00412   } else if (needs_grayscale) {
00413     if (needs_alpha) {
00414       _tex_format = Texture::F_luminance_alpha;
00415     } else {
00416       _tex_format = Texture::F_luminance;
00417     }
00418   } else {
00419     if (needs_alpha) {
00420       _tex_format = Texture::F_alpha;
00421 
00422       if (!_has_outline && 
00423           _fg == LColor(1.0f, 1.0f, 1.0f, 1.0f) &&
00424           _bg == LColor(1.0f, 1.0f, 1.0f, 0.0f)) {
00425         // This is the standard font color.  It can be copied directly
00426         // without any need for special processing.
00427         _needs_image_processing = false;
00428       }
00429 
00430     } else {
00431       // This won't be a very interesting font.
00432       _tex_format = Texture::F_luminance;
00433     }
00434   }
00435 }
00436 
00437 ////////////////////////////////////////////////////////////////////
00438 //     Function: DynamicTextFont::make_glyph
00439 //       Access: Private
00440 //  Description: Slots a space in the texture map for the new
00441 //               character and renders the glyph, returning the
00442 //               newly-created TextGlyph object, or NULL if the
00443 //               glyph cannot be created for some reason.
00444 ////////////////////////////////////////////////////////////////////
00445 DynamicTextGlyph *DynamicTextFont::
00446 make_glyph(int character, FT_Face face, int glyph_index) {
00447   if (!load_glyph(face, glyph_index, false)) {
00448     return (DynamicTextGlyph *)NULL;
00449   }
00450 
00451   FT_GlyphSlot slot = face->glyph;
00452   FT_Bitmap &bitmap = slot->bitmap;
00453 
00454   if ((bitmap.width == 0 || bitmap.rows == 0) && (glyph_index == 0)) {
00455     // Here's a special case: a glyph_index of 0 means an invalid
00456     // glyph.  Some fonts define a symbol to represent an invalid
00457     // glyph, but if that symbol is the empty bitmap, we return NULL,
00458     // and use Panda's invalid glyph in its place.  We do this to
00459     // guarantee that every invalid glyph is visible as *something*.
00460     return NULL;
00461   }
00462 
00463   PN_stdfloat advance = slot->advance.x / 64.0;
00464 
00465   if (_render_mode != RM_texture && 
00466       slot->format == ft_glyph_format_outline) {
00467     // Re-stroke the glyph to make it an outline glyph.
00468     /*
00469     FT_Stroker stroker;
00470     FT_Stroker_New(face->memory, &stroker);
00471     FT_Stroker_Set(stroker, 16 * 16, FT_STROKER_LINECAP_BUTT,
00472                    FT_STROKER_LINEJOIN_ROUND, 0);
00473 
00474     FT_Stroker_ParseOutline(stroker, &slot->outline, 0);
00475 
00476     FT_UInt num_points, num_contours;
00477     FT_Stroker_GetCounts(stroker, &num_points, &num_contours);
00478 
00479     FT_Outline border;
00480     FT_Outline_New(_ft_library, num_points, num_contours, &border);
00481     border.n_points = 0;
00482     border.n_contours = 0;
00483     FT_Stroker_Export(stroker, &border);
00484     FT_Stroker_Done(stroker);
00485 
00486     FT_Outline_Done(_ft_library, &slot->outline);
00487     memcpy(&slot->outline, &border, sizeof(border));
00488     */
00489 
00490     // Ask FreeType to extract the contours out of the outline
00491     // description.
00492     FT_Outline_Funcs funcs;
00493     memset(&funcs, 0, sizeof(funcs));
00494     funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
00495     funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
00496     funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
00497     funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
00498 
00499     WindingOrder wo = _winding_order;
00500     if (wo == WO_default) {
00501       // If we weren't told an explicit winding order, ask FreeType to
00502       // figure it out.  Sometimes it appears to guess wrong.
00503 #ifdef FT_ORIENTATION_FILL_RIGHT
00504       if (FT_Outline_Get_Orientation(&slot->outline) == FT_ORIENTATION_FILL_RIGHT) {
00505         wo = WO_right;
00506       } else {
00507         wo = WO_left;
00508       }
00509 #else
00510       // Hmm.  Assign a right-winding (TTF) orientation if FreeType
00511       // can't tell us.
00512       wo = WO_right;
00513 #endif  // FT_ORIENTATION_FILL_RIGHT
00514     }
00515 
00516     if (wo != WO_left) {
00517       FT_Outline_Reverse(&slot->outline);
00518     }
00519 
00520     _contours.clear();
00521     FT_Outline_Decompose(&slot->outline, &funcs, (void *)this);
00522 
00523     PT(DynamicTextGlyph) glyph = 
00524       new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
00525     switch (_render_mode) {
00526     case RM_wireframe:
00527       render_wireframe_contours(glyph);
00528       return glyph;
00529 
00530     case RM_polygon:
00531       render_polygon_contours(glyph, true, false);
00532       return glyph;
00533 
00534     case RM_extruded:
00535       render_polygon_contours(glyph, false, true);
00536       return glyph;
00537 
00538     case RM_solid:
00539       render_polygon_contours(glyph, true, true);
00540       return glyph;
00541 
00542     default:
00543       break;
00544     }
00545   }
00546 
00547   // Render the glyph if necessary.
00548   if (slot->format != ft_glyph_format_bitmap) {
00549     FT_Render_Glyph(slot, ft_render_mode_normal);
00550   }
00551 
00552   if (bitmap.width == 0 || bitmap.rows == 0) {
00553     // If we got an empty bitmap, it's a special case.
00554 
00555     PT(DynamicTextGlyph) glyph = 
00556       new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
00557     _empty_glyphs.push_back(glyph);
00558     return glyph;
00559 
00560   } else {
00561     DynamicTextGlyph *glyph;
00562 
00563     PN_stdfloat tex_x_size = bitmap.width;
00564     PN_stdfloat tex_y_size = bitmap.rows;
00565 
00566     int outline = 0;
00567 
00568     if (_tex_pixels_per_unit == _font_pixels_per_unit &&
00569         !_needs_image_processing) {
00570       // If the bitmap produced from the font doesn't require scaling
00571       // or any other processing before it goes to the texture, we can
00572       // just copy it directly into the texture.
00573       glyph = slot_glyph(character, bitmap.width, bitmap.rows);
00574       copy_bitmap_to_texture(bitmap, glyph);
00575 
00576     } else {
00577       // Otherwise, we need to copy to a PNMImage first, so we can
00578       // scale it and/or process it; and then copy it to the texture
00579       // from there.
00580       tex_x_size /= _scale_factor;
00581       tex_y_size /= _scale_factor;
00582       int int_x_size = (int)ceil(tex_x_size);
00583       int int_y_size = (int)ceil(tex_y_size);
00584       int bmp_x_size = (int)(int_x_size * _scale_factor + 0.5f);
00585       int bmp_y_size = (int)(int_y_size * _scale_factor + 0.5f);
00586 
00587       PNMImage image(bmp_x_size, bmp_y_size, PNMImage::CT_grayscale);
00588       copy_bitmap_to_pnmimage(bitmap, image);
00589 
00590       PNMImage reduced(int_x_size, int_y_size, PNMImage::CT_grayscale);
00591       reduced.quick_filter_from(image);
00592 
00593       // convert the outline width from points to tex_pixels.
00594       PN_stdfloat outline_pixels = _outline_width / _points_per_unit * _tex_pixels_per_unit;
00595       outline = (int)ceil(outline_pixels);
00596 
00597       int_x_size += outline * 2;
00598       int_y_size += outline * 2;
00599       tex_x_size += outline * 2;
00600       tex_y_size += outline * 2;
00601       glyph = slot_glyph(character, int_x_size, int_y_size);
00602 
00603       if (outline != 0) {
00604         // Pad the glyph image to make room for the outline.
00605         PNMImage padded(int_x_size, int_y_size, PNMImage::CT_grayscale);
00606         padded.copy_sub_image(reduced, outline, outline);
00607         copy_pnmimage_to_texture(padded, glyph);
00608 
00609       } else {
00610         copy_pnmimage_to_texture(reduced, glyph);
00611       }
00612     }
00613       
00614     glyph->make_geom((int)floor(slot->bitmap_top + outline * _scale_factor + 0.5f),
00615                      (int)floor(slot->bitmap_left - outline * _scale_factor + 0.5f),
00616                      advance, _poly_margin,
00617                      tex_x_size, tex_y_size,
00618                      _font_pixels_per_unit, _tex_pixels_per_unit);
00619     return glyph;
00620   }
00621 }
00622 
00623 ////////////////////////////////////////////////////////////////////
00624 //     Function: DynamicTextFont::copy_bitmap_to_texture
00625 //       Access: Private
00626 //  Description: Copies a bitmap as rendered by FreeType directly into
00627 //               the texture memory image for the indicated glyph,
00628 //               without any scaling of pixels.
00629 ////////////////////////////////////////////////////////////////////
00630 void DynamicTextFont::
00631 copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) {
00632   if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) {
00633     // This is the easy case: we can memcpy the rendered glyph
00634     // directly into our texture image, one row at a time.
00635     unsigned char *buffer_row = bitmap.buffer;
00636     for (int yi = 0; yi < bitmap.rows; yi++) {
00637       
00638       unsigned char *texture_row = glyph->get_row(yi);
00639       nassertv(texture_row != (unsigned char *)NULL);
00640       memcpy(texture_row, buffer_row, bitmap.width);
00641       buffer_row += bitmap.pitch;
00642     }
00643     
00644   } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00645     // This is a little bit more work: we have to expand the
00646     // one-bit-per-pixel bitmap into a one-byte-per-pixel texture.
00647     unsigned char *buffer_row = bitmap.buffer;
00648     for (int yi = 0; yi < bitmap.rows; yi++) {
00649       unsigned char *texture_row = glyph->get_row(yi);
00650       nassertv(texture_row != (unsigned char *)NULL);
00651       
00652       int bit = 0x80;
00653       unsigned char *b = buffer_row;
00654       for (int xi = 0; xi < bitmap.width; xi++) {
00655         if (*b & bit) {
00656           texture_row[xi] = 0xff;
00657         } else {
00658           texture_row[xi] = 0x00;
00659         }
00660         bit >>= 1;
00661         if (bit == 0) {
00662           ++b;
00663           bit = 0x80;
00664         }
00665       }
00666       
00667       buffer_row += bitmap.pitch;
00668     }
00669     
00670     
00671   } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00672     // Here we must expand a grayscale pixmap with n levels of gray
00673     // into our 256-level texture.
00674     unsigned char *buffer_row = bitmap.buffer;
00675     for (int yi = 0; yi < bitmap.rows; yi++) {
00676       unsigned char *texture_row = glyph->get_row(yi);
00677       nassertv(texture_row != (unsigned char *)NULL);
00678       for (int xi = 0; xi < bitmap.width; xi++) {
00679         texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1);
00680       }
00681       buffer_row += bitmap.pitch;
00682     }
00683     
00684   } else {
00685     text_cat.error()
00686       << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
00687   }
00688 }
00689 
00690 ////////////////////////////////////////////////////////////////////
00691 //     Function: DynamicTextFont::copy_pnmimage_to_texture
00692 //       Access: Private
00693 //  Description: Copies a bitmap stored in a PNMImage into
00694 //               the texture memory image for the indicated glyph.
00695 ////////////////////////////////////////////////////////////////////
00696 void DynamicTextFont::
00697 copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph) {
00698   if (!_needs_image_processing) {
00699     // Copy the image directly into the alpha component of the
00700     // texture.
00701     nassertv(glyph->_page->get_num_components() == 1);
00702     for (int yi = 0; yi < image.get_y_size(); yi++) {
00703       unsigned char *texture_row = glyph->get_row(yi);
00704       nassertv(texture_row != (unsigned char *)NULL);
00705       for (int xi = 0; xi < image.get_x_size(); xi++) {
00706         texture_row[xi] = image.get_gray_val(xi, yi);
00707       }
00708     }
00709 
00710   } else {
00711     if (_has_outline) {
00712       // Gaussian blur the glyph to generate an outline.
00713       PNMImage outline(image.get_x_size(), image.get_y_size(), PNMImage::CT_grayscale);
00714       PN_stdfloat outline_pixels = _outline_width / _points_per_unit * _tex_pixels_per_unit;
00715       outline.gaussian_filter_from(outline_pixels * 0.707, image);
00716 
00717       // Filter the resulting outline to make a harder edge.  Square
00718       // _outline_feather first to make the range more visually linear
00719       // (this approximately compensates for the Gaussian falloff of
00720       // the feathered edge).
00721       PN_stdfloat f = _outline_feather * _outline_feather;
00722 
00723       for (int yi = 0; yi < outline.get_y_size(); yi++) {
00724         for (int xi = 0; xi < outline.get_x_size(); xi++) {
00725           PN_stdfloat v = outline.get_gray(xi, yi);
00726           if (v == 0.0f) {
00727             // Do nothing.
00728           } else if (v >= f) {
00729             // Clamp to 1.
00730             outline.set_gray(xi, yi, 1.0);
00731           } else {
00732             // Linearly scale the range 0 .. f onto 0 .. 1.
00733             outline.set_gray(xi, yi, v / f);
00734           }
00735         }
00736       }
00737 
00738       // Now blend that into the texture.
00739       blend_pnmimage_to_texture(outline, glyph, _outline_color);
00740     }
00741 
00742     // Colorize the image as we copy it in.  This assumes the previous
00743     // color at this part of the texture was already initialized to
00744     // the background color.
00745     blend_pnmimage_to_texture(image, glyph, _fg);
00746   }    
00747 }
00748 
00749 ////////////////////////////////////////////////////////////////////
00750 //     Function: DynamicTextFont::blend_pnmimage_to_texture
00751 //       Access: Private
00752 //  Description: Blends the PNMImage into the appropriate part of the
00753 //               texture, where 0.0 in the image indicates the color
00754 //               remains the same, and 1.0 indicates the color is
00755 //               assigned the indicated foreground color.
00756 ////////////////////////////////////////////////////////////////////
00757 void DynamicTextFont::
00758 blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
00759                           const LColor &fg) {
00760   LColor fgv = fg * 255.0f;
00761 
00762   int num_components = glyph->_page->get_num_components();
00763   if (num_components == 1) {
00764     // Luminance or alpha.
00765     int ci = 3;
00766     if (glyph->_page->get_format() != Texture::F_alpha) {
00767       ci = 0;
00768     }
00769 
00770     for (int yi = 0; yi < image.get_y_size(); yi++) {
00771       unsigned char *texture_row = glyph->get_row(yi);
00772       nassertv(texture_row != (unsigned char *)NULL);
00773       for (int xi = 0; xi < image.get_x_size(); xi++) {
00774         unsigned char *tr = texture_row + xi;
00775         PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00776         tr[0] = (unsigned char)(tr[0] + t * (fgv[ci] - tr[0]));
00777       }
00778     }
00779 
00780   } else if (num_components == 2) {
00781     // Luminance + alpha.
00782 
00783     for (int yi = 0; yi < image.get_y_size(); yi++) {
00784       unsigned char *texture_row = glyph->get_row(yi);
00785       nassertv(texture_row != (unsigned char *)NULL);
00786       for (int xi = 0; xi < image.get_x_size(); xi++) {
00787         unsigned char *tr = texture_row + xi * 2;
00788         PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00789         tr[0] = (unsigned char)(tr[0] + t * (fgv[0] - tr[0]));
00790         tr[1] = (unsigned char)(tr[1] + t * (fgv[3] - tr[1]));
00791       }
00792     }
00793 
00794   } else if (num_components == 3) {
00795     // RGB.
00796 
00797     for (int yi = 0; yi < image.get_y_size(); yi++) {
00798       unsigned char *texture_row = glyph->get_row(yi);
00799       nassertv(texture_row != (unsigned char *)NULL);
00800       for (int xi = 0; xi < image.get_x_size(); xi++) {
00801         unsigned char *tr = texture_row + xi * 3;
00802         PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00803         tr[0] = (unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
00804         tr[1] = (unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
00805         tr[2] = (unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
00806       }
00807     }
00808 
00809   } else { // (num_components == 4)
00810     // RGBA.
00811 
00812     for (int yi = 0; yi < image.get_y_size(); yi++) {
00813       unsigned char *texture_row = glyph->get_row(yi);
00814       nassertv(texture_row != (unsigned char *)NULL);
00815       for (int xi = 0; xi < image.get_x_size(); xi++) {
00816         unsigned char *tr = texture_row + xi * 4;
00817         PN_stdfloat t = (PN_stdfloat)image.get_gray(xi, yi);
00818         tr[0] = (unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
00819         tr[1] = (unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
00820         tr[2] = (unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
00821         tr[3] = (unsigned char)(tr[3] + t * (fgv[3] - tr[3]));
00822       }
00823     }
00824   }
00825 }
00826 
00827 ////////////////////////////////////////////////////////////////////
00828 //     Function: DynamicTextFont::slot_glyph
00829 //       Access: Private
00830 //  Description: Chooses a page that will have room for a glyph of the
00831 //               indicated size (after expanding the indicated size by
00832 //               the current margin).  Returns the newly-allocated
00833 //               glyph on the chosen page; the glyph has not been
00834 //               filled in yet except with its size.
00835 ////////////////////////////////////////////////////////////////////
00836 DynamicTextGlyph *DynamicTextFont::
00837 slot_glyph(int character, int x_size, int y_size) {
00838   // Increase the indicated size by the current margin.
00839   x_size += _texture_margin * 2;
00840   y_size += _texture_margin * 2;
00841 
00842   if (!_pages.empty()) {
00843     // Start searching on the preferred page.  That way, we'll fill up
00844     // the preferred page first, and we can gradually rotate this page
00845     // around; it keeps us from spending too much time checking
00846     // already-filled pages for space.
00847     _preferred_page = _preferred_page % _pages.size();
00848     int pi = _preferred_page;
00849 
00850     do {
00851       DynamicTextPage *page = _pages[pi];
00852       DynamicTextGlyph *glyph = page->slot_glyph(character, x_size, y_size, _texture_margin);
00853       if (glyph != (DynamicTextGlyph *)NULL) {
00854         // Once we found a page to hold the glyph, that becomes our
00855         // new preferred page.
00856         _preferred_page = pi;
00857         return glyph;
00858       }
00859 
00860       if (page->is_empty()) {
00861         // If we couldn't even put it on an empty page, we're screwed.
00862         text_cat.error()
00863           << "Glyph of size " << x_size << " by " << y_size
00864           << " pixels won't fit on an empty page.\n";
00865         return (DynamicTextGlyph *)NULL;
00866       }
00867 
00868       pi = (pi + 1) % _pages.size();
00869     } while (pi != _preferred_page);
00870   }
00871 
00872   // All pages are filled.  Can we free up space by removing some old
00873   // glyphs?
00874   if (garbage_collect() != 0) {
00875     // Yes, we just freed up some space.  Try once more, recursively.
00876     return slot_glyph(character, x_size, y_size);
00877 
00878   } else {
00879     // No good; all recorded glyphs are actually in use.  We need to
00880     // make a new page.
00881     _preferred_page = _pages.size();
00882     PT(DynamicTextPage) page = new DynamicTextPage(this, _preferred_page);
00883     _pages.push_back(page);
00884     return page->slot_glyph(character, x_size, y_size, _texture_margin);
00885   }
00886 }
00887 
00888 ////////////////////////////////////////////////////////////////////
00889 //     Function: DynamicTextFont::render_wireframe_contours
00890 //       Access: Private
00891 //  Description: Converts from the _contours list to an actual glyph
00892 //               geometry, as a wireframe render.
00893 ////////////////////////////////////////////////////////////////////
00894 void DynamicTextFont::
00895 render_wireframe_contours(DynamicTextGlyph *glyph) {
00896   PT(GeomVertexData) vdata = new GeomVertexData
00897     (string(), GeomVertexFormat::get_v3(),
00898      Geom::UH_static);
00899   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00900 
00901   PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
00902 
00903   Contours::const_iterator ci;
00904   for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00905     const Contour &contour = (*ci);
00906     Points::const_iterator pi;
00907 
00908     for (pi = contour._points.begin(); pi != contour._points.end(); ++pi) {
00909       const LPoint2 &p = (*pi)._p;
00910       vertex.add_data3(p[0], 0.0f, p[1]);
00911     }
00912 
00913     lines->add_next_vertices(contour._points.size());
00914     lines->close_primitive();
00915   }
00916 
00917   glyph->set_geom(vdata, lines, RenderState::make_empty());
00918   _contours.clear();
00919 }
00920 
00921 ////////////////////////////////////////////////////////////////////
00922 //     Function: DynamicTextFont::render_polygon_contours
00923 //       Access: Private
00924 //  Description: Converts from the _contours list to an actual glyph
00925 //               geometry, as a polygon render.
00926 ////////////////////////////////////////////////////////////////////
00927 void DynamicTextFont::
00928 render_polygon_contours(DynamicTextGlyph *glyph, bool face, bool extrude) {
00929   PT(GeomVertexData) vdata = new GeomVertexData
00930     (string(), GeomVertexFormat::get_v3n3(),
00931      Geom::UH_static);
00932   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00933   GeomVertexWriter normal(vdata, InternalName::get_normal());
00934 
00935   PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
00936   Triangulator t;
00937 
00938   Contours::iterator ci;
00939 
00940   if (face) {
00941     // First, build up the list of vertices for the face, and
00942     // determine which contours are solid and which are holes.
00943     for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00944       Contour &contour = (*ci);
00945       
00946       t.clear_polygon();
00947       contour._start_vertex = t.get_num_vertices();
00948       for (size_t i = 0; i < contour._points.size() - 1; ++i) {
00949         const LPoint2 &p = contour._points[i]._p;
00950         vertex.add_data3(p[0], 0.0f, p[1]);
00951         normal.add_data3(0.0f, -1.0f, 0.0f);
00952         int vi = t.add_vertex(p[0], p[1]);
00953         t.add_polygon_vertex(vi);
00954       }
00955       
00956       contour._is_solid = t.is_left_winding();
00957     }
00958 
00959     // Now go back and generate the actual triangles for the face.
00960     for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00961       const Contour &contour = (*ci);
00962       
00963       if (contour._is_solid && !contour._points.empty()) {
00964         t.clear_polygon();
00965         for (size_t i = 0; i < contour._points.size() - 1; ++i) {
00966           t.add_polygon_vertex(contour._start_vertex + i);
00967         }
00968         
00969         // Also add all the holes to each polygon.
00970         Contours::iterator cj;
00971         for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
00972           Contour &hole = (*cj);
00973           if (!hole._is_solid && !hole._points.empty()) {
00974             t.begin_hole();
00975             for (size_t j = 0; j < hole._points.size() - 1; ++j) {
00976               t.add_hole_vertex(hole._start_vertex + j);
00977             }
00978           }
00979         }
00980         
00981         t.triangulate();
00982         int num_triangles = t.get_num_triangles();
00983         for (int ti = 0; ti < num_triangles; ++ti) {
00984           tris->add_vertex(t.get_triangle_v0(ti));
00985           tris->add_vertex(t.get_triangle_v1(ti));
00986           tris->add_vertex(t.get_triangle_v2(ti));
00987           tris->close_primitive();
00988         }
00989       }
00990     }
00991   }
00992 
00993   if (extrude) {
00994     // If we're generating extruded geometry (polygons along the
00995     // edges, down the y axis), generate them now.  These are pretty
00996     // easy, but we need to create more vertices--they don't share the
00997     // same normals.
00998     for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
00999       const Contour &contour = (*ci);
01000       Points::const_iterator pi;
01001 
01002       for (size_t i = 0; i < contour._points.size(); ++i) {
01003         const ContourPoint &cp = contour._points[i];
01004         const LPoint2 &p = cp._p;
01005         const LVector2 &t_in = cp._in;
01006         const LVector2 &t_out = cp._out;
01007 
01008         LVector3 n_in(t_in[1], 0.0f, -t_in[0]);
01009         vertex.add_data3(p[0], 1.0f, p[1]);
01010         vertex.add_data3(p[0], 0.0f, p[1]);
01011         normal.add_data3(n_in);
01012         normal.add_data3(n_in);
01013 
01014         if (i != 0) {
01015           int vi = vertex.get_write_row();
01016           tris->add_vertex(vi - 4);
01017           tris->add_vertex(vi - 2);
01018           tris->add_vertex(vi - 1);
01019           tris->close_primitive();
01020           tris->add_vertex(vi - 1);
01021           tris->add_vertex(vi - 3);
01022           tris->add_vertex(vi - 4);
01023           tris->close_primitive();
01024         }
01025 
01026         if (i != contour._points.size() - 1 && !t_in.almost_equal(t_out)) {
01027           // If the out tangent is different from the in tangent, we
01028           // need to store new vertices for the next quad.
01029           LVector3 n_out(t_out[1], 0.0f, -t_out[0]);
01030           vertex.add_data3(p[0], 1.0f, p[1]);
01031           vertex.add_data3(p[0], 0.0f, p[1]);
01032           normal.add_data3(n_out);
01033           normal.add_data3(n_out);
01034         }
01035       }
01036     }
01037 
01038     if (face) {
01039       // Render the back side of the face too.
01040       int back_start = vertex.get_write_row();
01041 
01042       for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
01043         Contour &contour = (*ci);
01044         for (size_t i = 0; i < contour._points.size() - 1; ++i) {
01045           const LPoint2 &p = contour._points[i]._p;
01046           vertex.add_data3(p[0], 1.0f, p[1]);
01047           normal.add_data3(0.0f, 1.0f, 0.0f);
01048         }
01049       }
01050 
01051       // Now go back and generate the actual triangles for the face.
01052       for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
01053         const Contour &contour = (*ci);
01054       
01055         if (contour._is_solid && !contour._points.empty()) {
01056           t.clear_polygon();
01057           for (size_t i = 0; i < contour._points.size() - 1; ++i) {
01058             t.add_polygon_vertex(contour._start_vertex + i);
01059           }
01060         
01061           // Also add all the holes to each polygon.
01062           Contours::iterator cj;
01063           for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
01064             Contour &hole = (*cj);
01065             if (!hole._is_solid && !hole._points.empty()) {
01066               t.begin_hole();
01067               for (size_t j = 0; j < hole._points.size() - 1; ++j) {
01068                 t.add_hole_vertex(hole._start_vertex + j);
01069               }
01070             }
01071           }
01072         
01073           t.triangulate();
01074           int num_triangles = t.get_num_triangles();
01075           for (int ti = 0; ti < num_triangles; ++ti) {
01076             tris->add_vertex(t.get_triangle_v2(ti) + back_start);
01077             tris->add_vertex(t.get_triangle_v1(ti) + back_start);
01078             tris->add_vertex(t.get_triangle_v0(ti) + back_start);
01079             tris->close_primitive();
01080           }
01081         }
01082       }
01083     }
01084   }
01085 
01086   glyph->set_geom(vdata, tris, RenderState::make_empty());
01087   //  glyph->set_geom(vdata, tris, RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_wireframe)));
01088   //  glyph->set_geom(vdata, tris, RenderState::make(AntialiasAttrib::make(AntialiasAttrib::M_auto)));
01089   _contours.clear();
01090 }
01091 
01092 ////////////////////////////////////////////////////////////////////
01093 //     Function: DynamicTextFont::outline_move_to
01094 //       Access: Private, Static
01095 //  Description: A callback from FT_Outline_Decompose().  It marks the
01096 //               beginning of a new contour.
01097 ////////////////////////////////////////////////////////////////////
01098 int DynamicTextFont::
01099 outline_move_to(const FT_Vector *to, void *user) {
01100   DynamicTextFont *self = (DynamicTextFont *)user;
01101 
01102   // Convert from 26.6 pixel units to Panda units.
01103   PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01104   LPoint2 p = LPoint2(to->x, to->y) * scale;
01105 
01106   if (self->_contours.empty() ||
01107       !self->_contours.back()._points.empty()) {
01108     self->_contours.push_back(Contour());
01109   }
01110   self->_q = p;
01111   return 0;
01112 }
01113 
01114 ////////////////////////////////////////////////////////////////////
01115 //     Function: DynamicTextFont::outline_line_to
01116 //       Access: Private, Static
01117 //  Description: A callback from FT_Outline_Decompose().  It marks a
01118 //               straight line in the contour.
01119 ////////////////////////////////////////////////////////////////////
01120 int DynamicTextFont::
01121 outline_line_to(const FT_Vector *to, void *user) {
01122   DynamicTextFont *self = (DynamicTextFont *)user;
01123   nassertr(!self->_contours.empty(), 1);
01124 
01125   // Convert from 26.6 pixel units to Panda units.
01126   PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01127   LPoint2 p = LPoint2(to->x, to->y) * scale;
01128 
01129   // Compute the tangent: this is just the vector from the last point.
01130   LVector2 t = (p - self->_q);
01131   t.normalize();
01132 
01133   if (self->_contours.back()._points.empty()) {
01134     self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2::zero(), t));
01135   } else {
01136     self->_contours.back()._points.back().connect_to(t);
01137   }
01138   
01139   self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2::zero()));
01140   self->_q = p;
01141   return 0;
01142 }
01143 
01144 ////////////////////////////////////////////////////////////////////
01145 //     Function: DynamicTextFont::outline_conic_to
01146 //       Access: Private, Static
01147 //  Description: A callback from FT_Outline_Decompose().  It marks a
01148 //               parabolic (3rd-order) Bezier curve in the contour.
01149 ////////////////////////////////////////////////////////////////////
01150 int DynamicTextFont::
01151 outline_conic_to(const FT_Vector *control,
01152                  const FT_Vector *to, void *user) {
01153   DynamicTextFont *self = (DynamicTextFont *)user;
01154   nassertr(!self->_contours.empty(), 1);
01155 
01156   // Convert from 26.6 pixel units to Panda units.
01157   PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01158 
01159   LPoint2 c = LPoint2(control->x, control->y) * scale;
01160   LPoint2 p = LPoint2(to->x, to->y) * scale;
01161 
01162   // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
01163   NurbsCurveEvaluator nce;
01164   nce.local_object();
01165   nce.set_order(3);
01166   nce.reset(3);
01167   nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
01168   nce.set_vertex(1, LVecBase3(c[0], c[1], 0.0f));
01169   nce.set_vertex(2, LVecBase3(p[0], p[1], 0.0f));
01170 
01171   self->_q = p;
01172 
01173   PT(NurbsCurveResult) ncr = nce.evaluate();
01174   return self->outline_nurbs(ncr);
01175 }
01176 
01177 ////////////////////////////////////////////////////////////////////
01178 //     Function: DynamicTextFont::outline_cubic_to
01179 //       Access: Private, Static
01180 //  Description: A callback from FT_Outline_Decompose().  It marks a
01181 //               cubic (4th-order) Bezier curve in the contour.
01182 ////////////////////////////////////////////////////////////////////
01183 int DynamicTextFont::
01184 outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
01185                  const FT_Vector *to, void *user) {
01186   DynamicTextFont *self = (DynamicTextFont *)user;
01187   nassertr(!self->_contours.empty(), 1);
01188 
01189   // Convert from 26.6 pixel units to Panda units.
01190   PN_stdfloat scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
01191 
01192   LPoint2 c1 = LPoint2(control1->x, control1->y) * scale;
01193   LPoint2 c2 = LPoint2(control2->x, control2->y) * scale;
01194   LPoint2 p = LPoint2(to->x, to->y) * scale;
01195 
01196   // The NurbsCurveEvaluator will evaluate the Bezier segment for us.
01197   NurbsCurveEvaluator nce;
01198   nce.local_object();
01199   nce.set_order(4);
01200   nce.reset(4);
01201   nce.set_vertex(0, LVecBase3(self->_q[0], self->_q[1], 0.0f));
01202   nce.set_vertex(1, LVecBase3(c1[0], c1[1], 0.0f));
01203   nce.set_vertex(2, LVecBase3(c2[0], c2[1], 0.0f));
01204   nce.set_vertex(3, LVecBase3(p[0], p[1], 0.0f));
01205 
01206   self->_q = p;
01207 
01208   PT(NurbsCurveResult) ncr = nce.evaluate();
01209   return self->outline_nurbs(ncr);
01210 }
01211 
01212 ////////////////////////////////////////////////////////////////////
01213 //     Function: DynamicTextFont::outline_nurbs
01214 //       Access: Private
01215 //  Description: Called internally by outline_cubic_to() and
01216 //               outline_conic_to().
01217 ////////////////////////////////////////////////////////////////////
01218 int DynamicTextFont::
01219 outline_nurbs(NurbsCurveResult *ncr) {
01220   // Sample it down so that the lines approximate the curve to within
01221   // a "pixel."
01222   ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
01223 
01224   int num_samples = ncr->get_num_samples();
01225 
01226   bool needs_connect = false;
01227   int start = 1;
01228   if (_contours.back()._points.empty()) {
01229     // If we haven't got the first point of this contour yet, we must
01230     // add it now.
01231     start = 0;
01232   } else {
01233     needs_connect = true;
01234   }
01235 
01236   for (int i = start; i < num_samples; ++i) {
01237     PN_stdfloat st = ncr->get_sample_t(i);
01238     const LPoint3 &p = ncr->get_sample_point(i);
01239 
01240     PN_stdfloat st0 = st, st1 = st;
01241     if (i > 0) {
01242       st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
01243     }
01244     if (i < num_samples - 1) {
01245       st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
01246     }
01247     // Compute the tangent by deltaing nearby points.  Don't evaluate
01248     // the tangent from the NURBS, since that doesn't appear to be
01249     // reliable.
01250     LPoint3 p0, p1;
01251     ncr->eval_point(st0, p0);
01252     ncr->eval_point(st1, p1);
01253     LVector3 t = p1 - p0;
01254     t.normalize();
01255 
01256     if (needs_connect) {
01257       _contours.back()._points.back().connect_to(LVector2(t[0], t[1]));
01258       needs_connect = false;
01259     }
01260 
01261     _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
01262   }
01263 
01264   return 0;
01265 }
01266 
01267 #endif  // HAVE_FREETYPE
 All Classes Functions Variables Enumerations