Panda3D
|
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 ©) : 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