diff --git a/panda/src/text/Sources.pp b/panda/src/text/Sources.pp index b8b3c56b19..a0f3a68d5b 100644 --- a/panda/src/text/Sources.pp +++ b/panda/src/text/Sources.pp @@ -16,6 +16,7 @@ dynamicTextFont.I dynamicTextFont.h \ dynamicTextGlyph.I dynamicTextGlyph.h \ dynamicTextPage.I dynamicTextPage.h \ + geomTextGlyph.I geomTextGlyph.h \ staticTextFont.I staticTextFont.h \ textFont.I textFont.h \ textGlyph.I textGlyph.h \ @@ -26,6 +27,7 @@ dynamicTextFont.cxx \ dynamicTextGlyph.cxx \ dynamicTextPage.cxx \ + geomTextGlyph.cxx \ staticTextFont.cxx \ textFont.cxx textGlyph.cxx @@ -34,6 +36,7 @@ dynamicTextFont.I dynamicTextFont.h \ dynamicTextGlyph.I dynamicTextGlyph.h \ dynamicTextPage.I dynamicTextPage.h \ + geomTextGlyph.I geomTextGlyph.h \ staticTextFont.I staticTextFont.h \ textFont.I textFont.h \ textGlyph.I textGlyph.h \ diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 4a0fc0dbf5..fcdb2921ac 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -22,6 +22,7 @@ #include "textNode.h" #include "dynamicTextFont.h" #include "dynamicTextPage.h" +#include "geomTextGlyph.h" #include @@ -36,7 +37,18 @@ ConfigureFn(config_text) { #ifdef HAVE_FREETYPE DynamicTextFont::init_type(); DynamicTextPage::init_type(); + GeomTextGlyph::init_type(); + GeomTextGlyph::register_with_read_factory(); #endif } -const bool flatten_text = config_text.GetBool("flatten-text", true); +const bool text_flatten = config_text.GetBool("text-flatten", true); +const bool text_update_cleared_glyphs = config_text.GetBool("text-update-cleared-glyphs", false); +const int text_texture_margin = config_text.GetInt("text-texture-margin", 2); +const float text_poly_margin = config_text.GetFloat("text-poly-margin", 1.0f); +const int text_page_x_size = config_text.GetInt("text-page-x-size", 256); +const int text_page_y_size = config_text.GetInt("text-page-y-size", 256); +const float text_point_size = config_text.GetFloat("text-point-size", 10.0f); +const float text_pixels_per_unit = config_text.GetFloat("text-pixels-per-unit", 40.0f); +const bool text_small_caps = config_text.GetBool("text-small-caps", false); +const float text_small_caps_scale = config_text.GetFloat("text-small-caps-scale", 0.8f); diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index eda5594698..0bff692018 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -26,6 +26,15 @@ class DSearchPath; NotifyCategoryDecl(text, EXPCL_PANDA, EXPTP_PANDA); -extern const bool flatten_text; +extern const bool text_flatten; +extern const bool text_update_cleared_glyphs; +extern const int text_texture_margin; +extern const float text_poly_margin; +extern const int text_page_x_size; +extern const int text_page_y_size; +extern const float text_point_size; +extern const float text_pixels_per_unit; +extern const bool text_small_caps; +extern const float text_small_caps_scale; #endif diff --git a/panda/src/text/dynamicTextFont.I b/panda/src/text/dynamicTextFont.I index 54c6309dcc..ea8d8fad3c 100644 --- a/panda/src/text/dynamicTextFont.I +++ b/panda/src/text/dynamicTextFont.I @@ -82,6 +82,67 @@ get_pixels_per_unit() const { return _pixels_per_unit; } +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::set_small_caps +// Access: Published +// Description: Sets the small_caps flag. When this is set, +// lowercase letters are generated as scaled-down +// versions of their uppercase equivalents. This is +// particularly useful to set for fonts that do not have +// lowercase letters. +// +// It is also a good idea to set this for a font that +// has already implemented lowercase letters as +// scaled-down versions of their uppercase equivalents, +// since without this flag the texture memory may +// needlessly duplicate equivalent glyphs for upper and +// lowercase letters. Setting this flag causes the +// texture memory to share the mixed-case letters. +// +// The amount by which the lowercase letters are scaled +// is specified by set_small_caps_scale(). +//////////////////////////////////////////////////////////////////// +INLINE void DynamicTextFont:: +set_small_caps(bool small_caps) { + _small_caps = small_caps; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_small_caps +// Access: Published +// Description: Returns the small_caps flag. See set_small_caps(). +//////////////////////////////////////////////////////////////////// +INLINE bool DynamicTextFont:: +get_small_caps() const { + return _small_caps; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::set_small_caps_scale +// Access: Published +// Description: Sets the scale factor applied to lowercase letters +// from their uppercase equivalents, when the small_caps +// flag is in effect. See set_small_caps(). Normally, +// this will be a number less than one. +//////////////////////////////////////////////////////////////////// +INLINE void DynamicTextFont:: +set_small_caps_scale(float small_caps_scale) { + _small_caps_scale = small_caps_scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_small_caps_scale +// Access: Published +// Description: Returns the scale factor applied to lowercase letters +// from their uppercase equivalents, when the small_caps +// flag is in effect. See set_small_caps() and +// set_small_caps_scale(). +//////////////////////////////////////////////////////////////////// +INLINE float DynamicTextFont:: +get_small_caps_scale() const { + return _small_caps_scale; +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::set_texture_margin // Access: Published @@ -168,3 +229,40 @@ INLINE int DynamicTextFont:: get_page_y_size() const { return _page_y_size; } + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::set_update_cleared_glyphs +// Access: Published, Static +// Description: Sets the flag indicating whether texture memory +// should be updated immediately as old glyphs are +// removed. If this is true, texture memory will be +// immediately updated when old glyphs are removed from +// the pages. If this is false (the default), texture +// memory may not be updated until the page is next +// written to, that is, the next time a glyph is +// recorded on that page. +// +// Most of the time, there is no reason to set this +// true, unless you are debugging the DynamicTextFont +// code and want to be able to see exactly what is in +// each texture map at any given time. +// +// This is a global flag across all DynamicTextFont +// objects. +//////////////////////////////////////////////////////////////////// +INLINE void DynamicTextFont:: +set_update_cleared_glyphs(bool update_cleared_glyphs) { + _update_cleared_glyphs = update_cleared_glyphs; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_update_cleared_glyphs +// Access: Published, Static +// Description: Returns the flag indicating whether texture memory +// should be updated immediately as old glyphs are +// removed. See set_update_cleared_glyphs(). +//////////////////////////////////////////////////////////////////// +INLINE bool DynamicTextFont:: +get_update_cleared_glyphs() { + return _update_cleared_glyphs; +} diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index e0af9e7f42..879935c199 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/panda/src/text/dynamicTextFont.cxx @@ -22,6 +22,9 @@ #include "config_text.h" #include "config_util.h" +#include "ctype.h" + +bool DynamicTextFont::_update_cleared_glyphs = text_update_cleared_glyphs; FT_Library DynamicTextFont::_ft_library; bool DynamicTextFont::_ft_initialized = false; @@ -31,10 +34,11 @@ TypeHandle DynamicTextFont::_type_handle; // This constant determines how big a particular point size font -// appears. By convention, 10 points is 1 foot high. +// appears to be. By convention, 10 points is 1 unit (e.g. 1 foot) +// high. static const float points_per_unit = 10.0f; -// A universal convention. +// A universal typographic convention. static const float points_per_inch = 72.0f; //////////////////////////////////////////////////////////////////// @@ -47,19 +51,24 @@ static const float points_per_inch = 72.0f; //////////////////////////////////////////////////////////////////// DynamicTextFont:: DynamicTextFont(const Filename &font_filename, int face_index) { - _texture_margin = 2; - _poly_margin = 1.0f; - _page_x_size = 256; - _page_y_size = 256; - _point_size = 10.0f; - _pixels_per_unit = 40.0f; + _texture_margin = text_texture_margin; + _poly_margin = text_poly_margin; + _page_x_size = text_page_x_size; + _page_y_size = text_page_y_size; + _point_size = text_point_size; + _pixels_per_unit = text_pixels_per_unit; + _small_caps = text_small_caps; + _small_caps_scale = text_small_caps_scale; + + _preferred_page = 0; if (!_ft_initialized) { initialize_ft_library(); } if (!_ft_ok) { text_cat.error() - << "Unable to read font " << font_filename << ": FreeType library not available.\n"; + << "Unable to read font " << font_filename + << ": FreeType library not initialized properly.\n"; return; } @@ -128,6 +137,64 @@ get_page(int n) const { return _pages[n]; } +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::garbage_collect +// Access: Published +// Description: Removes all of the glyphs from the font that are no +// longer being used by any Geoms. Returns the number +// of glyphs removed. +//////////////////////////////////////////////////////////////////// +int DynamicTextFont:: +garbage_collect() { + int removed_count = 0; + + // First, remove all the old entries from our cache index. + Cache new_cache; + Cache::iterator ci; + for (ci = _cache.begin(); ci != _cache.end(); ++ci) { + DynamicTextGlyph *glyph = (*ci).second; + if (glyph->_geom_count != 0) { + // Keep this one. + new_cache.insert(new_cache.end(), (*ci)); + } else { + // Drop this one. + removed_count++; + } + } + _cache.swap(new_cache); + + // Now, go through each page and do the same thing. + Pages::iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + DynamicTextPage *page = (*pi); + page->garbage_collect(); + } + + return removed_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::update_texture_memory +// Access: Published +// Description: Marks all of the pages dirty so they will be reloaded +// into texture memory. This is necessary only if +// set_update_cleared_glyphs() is false, and some +// textures have recently been removed from the pages +// (for instance, after a call to garbage_collect()). +// +// Calling this just ensures that what you see when you +// apply the texture page to a polygon represents what +// is actually stored on the page. +//////////////////////////////////////////////////////////////////// +void DynamicTextFont:: +update_texture_memory() { + Pages::iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + DynamicTextPage *page = (*pi); + page->mark_dirty(Texture::DF_image); + } +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::clear // Access: Published @@ -143,8 +210,9 @@ get_page(int n) const { //////////////////////////////////////////////////////////////////// void DynamicTextFont:: clear() { - _pages.clear(); _cache.clear(); + _pages.clear(); + _empty_glyphs.clear(); } //////////////////////////////////////////////////////////////////// @@ -156,29 +224,53 @@ void DynamicTextFont:: write(ostream &out, int indent_level) const { indent(out, indent_level) << "DynamicTextFont " << get_name() << ", " - << _cache.size() << " glyphs, " - << get_num_pages() << " pages.\n"; + << get_num_pages() << " pages, " + << _cache.size() << " glyphs:\n"; + Cache::const_iterator ci; + for (ci = _cache.begin(); ci != _cache.end(); ++ci) { + int glyph_index = (*ci).first; + DynamicTextGlyph *glyph = (*ci).second; + indent(out, indent_level + 2) + << glyph_index; + out << ", count = " << glyph->_geom_count << "\n"; + } } //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::get_glyph // Access: Public, Virtual -// Description: Returns the glyph associated with the given character -// code, or NULL if there is no such glyph. +// Description: Gets the glyph associated with the given character +// code, as well as an optional scaling parameter that +// should be applied to the glyph's geometry and advance +// parameters. Returns true if the glyph exists, false +// if it does not. //////////////////////////////////////////////////////////////////// -const TextGlyph *DynamicTextFont:: -get_glyph(int character) { - Cache::iterator ci = _cache.find(character); - if (ci != _cache.end()) { - return (*ci).second; - } +bool DynamicTextFont:: +get_glyph(int character, const TextGlyph *&glyph, float &glyph_scale) { if (!_is_valid) { - return (TextGlyph *)NULL; + return false; } - DynamicTextGlyph *glyph = make_glyph(character); - _cache.insert(Cache::value_type(character, glyph)); - return glyph; + glyph_scale = 1.0f; + if (character < 128 && islower(character) && get_small_caps()) { + // If we have small_caps on, we implement lowercase letters by + // applying a scale to the corresponding uppercase letter. + glyph_scale = get_small_caps_scale(); + character = toupper(character); + } + + int glyph_index = FT_Get_Char_Index(_face, character); + + Cache::iterator ci = _cache.find(glyph_index); + if (ci != _cache.end()) { + glyph = (*ci).second; + } else { + DynamicTextGlyph *dynamic_glyph = make_glyph(glyph_index); + _cache.insert(Cache::value_type(glyph_index, dynamic_glyph)); + glyph = dynamic_glyph; + } + + return (glyph != (DynamicTextGlyph *)NULL); } //////////////////////////////////////////////////////////////////// @@ -209,6 +301,17 @@ reset_scale() { // have a scalable font or otherwise? float pixel_size = _point_size * (_pixels_per_unit / points_per_unit); _line_height = (float)_face->height * pixel_size / ((float)_face->units_per_EM * 64.0f); + + // Determine the correct width for a space. + error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT); + if (error) { + // Space isn't defined. Oh well. + _space_advance = 0.25f * _line_height; + + } else { + _space_advance = _face->glyph->advance.x / (_pixels_per_unit * 64.0f); + } + return true; } @@ -221,46 +324,55 @@ reset_scale() { // glyph cannot be created for some reason. //////////////////////////////////////////////////////////////////// DynamicTextGlyph *DynamicTextFont:: -make_glyph(int character) { - int error = FT_Load_Char(_face, character, FT_LOAD_RENDER); +make_glyph(int glyph_index) { + int error = FT_Load_Glyph(_face, glyph_index, FT_LOAD_RENDER); if (error) { text_cat.error() - << "Unable to render character " << character << "\n"; + << "Unable to render glyph " << glyph_index << "\n"; return (DynamicTextGlyph *)NULL; } FT_GlyphSlot slot = _face->glyph; FT_Bitmap &bitmap = slot->bitmap; - if (bitmap.pixel_mode != ft_pixel_mode_grays) { - text_cat.error() - << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; - return (DynamicTextGlyph *)NULL; - } - - if (bitmap.num_grays != 256) { - // We expect 256 levels of grayscale to come back from FreeType, - // since that's what we asked for. - text_cat.warning() - << "Expected 256 levels of gray, got " << bitmap.num_grays << "\n"; - } - - DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows); - - // Now copy the rendered glyph into the texture. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - unsigned char *texture_row = glyph->get_row(yi); - nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL); - memcpy(texture_row, buffer_row, bitmap.width); - buffer_row += bitmap.pitch; - } - glyph->_page->mark_dirty(Texture::DF_image); - float advance = slot->advance.x / 64.0; - glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance, - _poly_margin, _pixels_per_unit); - return glyph; + + if (bitmap.width == 0 || bitmap.rows == 0) { + // If we got an empty bitmap, it's a special case. + PT(DynamicTextGlyph) glyph = new DynamicTextGlyph(advance / _pixels_per_unit); + _empty_glyphs.push_back(glyph); + return glyph; + + } else { + if (bitmap.pixel_mode != ft_pixel_mode_grays) { + text_cat.error() + << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; + return (DynamicTextGlyph *)NULL; + } + + if (bitmap.num_grays != 256) { + // We expect 256 levels of grayscale to come back from FreeType, + // since that's what we asked for. + text_cat.warning() + << "Expected 256 levels of gray, got " << bitmap.num_grays << "\n"; + } + + DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows); + + // Now copy the rendered glyph into the texture. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + unsigned char *texture_row = glyph->get_row(yi); + nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL); + memcpy(texture_row, buffer_row, bitmap.width); + buffer_row += bitmap.pitch; + } + glyph->_page->mark_dirty(Texture::DF_image); + + glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance, + _poly_margin, _pixels_per_unit); + return glyph; + } } //////////////////////////////////////////////////////////////////// @@ -278,28 +390,50 @@ slot_glyph(int x_size, int y_size) { x_size += _texture_margin * 2; y_size += _texture_margin * 2; - Pages::iterator pi; - for (pi = _pages.begin(); pi != _pages.end(); ++pi) { - DynamicTextPage *page = (*pi); + if (!_pages.empty()) { + // Start searching on the preferred page. That way, we'll fill up + // the preferred page first, and we can gradually rotate this page + // around; it keeps us from spending too much time checking + // already-filled pages for space. + _preferred_page = _preferred_page % _pages.size(); + int pi = _preferred_page; - DynamicTextGlyph *glyph = page->slot_glyph(x_size, y_size, _texture_margin); - if (glyph != (DynamicTextGlyph *)NULL) { - return glyph; - } + do { + DynamicTextPage *page = _pages[pi]; + DynamicTextGlyph *glyph = page->slot_glyph(x_size, y_size, _texture_margin); + if (glyph != (DynamicTextGlyph *)NULL) { + // Once we found a page to hold the glyph, that becomes our + // new preferred page. + _preferred_page = pi; + return glyph; + } - if (page->is_empty()) { - // If we couldn't even put in on an empty page, we're screwed. - text_cat.error() - << "Glyph of size " << x_size << " by " << y_size - << " won't fit on an empty page.\n"; - return (DynamicTextGlyph *)NULL; - } + if (page->is_empty()) { + // If we couldn't even put it on an empty page, we're screwed. + text_cat.error() + << "Glyph of size " << x_size << " by " << y_size + << " pixels won't fit on an empty page.\n"; + return (DynamicTextGlyph *)NULL; + } + + pi = (pi + 1) % _pages.size(); + } while (pi != _preferred_page); } - // We need to make a new page. - PT(DynamicTextPage) page = new DynamicTextPage(this); - _pages.push_back(page); - return page->slot_glyph(x_size, y_size, _texture_margin); + // All pages are filled. Can we free up space by removing some old + // glyphs? + if (garbage_collect() != 0) { + // Yes, we just freed up some space. Try once more, recursively. + return slot_glyph(x_size, y_size); + + } else { + // No good; all recorded glyphs are actually in use. We need to + // make a new page. + _preferred_page = _pages.size(); + PT(DynamicTextPage) page = new DynamicTextPage(this); + _pages.push_back(page); + return page->slot_glyph(x_size, y_size, _texture_margin); + } } diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h index 6544e5631c..a9e1cce245 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -52,6 +52,11 @@ PUBLISHED: INLINE bool set_pixels_per_unit(float pixels_per_unit); INLINE float get_pixels_per_unit() const; + INLINE void set_small_caps(bool small_caps); + INLINE bool get_small_caps() const; + INLINE void set_small_caps_scale(float small_caps_scale); + INLINE float get_small_caps_scale() const; + INLINE void set_texture_margin(int texture_margin); INLINE int get_texture_margin() const; INLINE void set_poly_margin(float poly_margin); @@ -61,35 +66,54 @@ PUBLISHED: INLINE int get_page_x_size() const; INLINE int get_page_y_size() const; + INLINE static void set_update_cleared_glyphs(bool update_cleared_glyphs); + INLINE static bool get_update_cleared_glyphs(); + int get_num_pages() const; DynamicTextPage *get_page(int n) const; + int garbage_collect(); + void update_texture_memory(); void clear(); virtual void write(ostream &out, int indent_level) const; public: - virtual const TextGlyph *get_glyph(int character); + virtual bool get_glyph(int character, const TextGlyph *&glyph, + float &glyph_scale); private: bool reset_scale(); - DynamicTextGlyph *make_glyph(int character); + DynamicTextGlyph *make_glyph(int glyph_index); DynamicTextGlyph *slot_glyph(int x_size, int y_size); static void initialize_ft_library(); float _point_size; float _pixels_per_unit; + bool _small_caps; + float _small_caps_scale; int _texture_margin; float _poly_margin; int _page_x_size, _page_y_size; + static bool _update_cleared_glyphs; typedef pvector< PT(DynamicTextPage) > Pages; Pages _pages; + int _preferred_page; + // This doesn't need to be a reference-counting pointer, because the + // reference to each glyph is kept by the DynamicTextPage object. typedef pmap Cache; Cache _cache; + // This is a list of the glyphs that do not have any printable + // properties (e.g. space), but still have an advance measure. We + // store them here to keep their reference counts; they also appear + // in the above table. + typedef pvector< PT(DynamicTextGlyph) > EmptyGlyphs; + EmptyGlyphs _empty_glyphs; + FT_Face _face; static FT_Library _ft_library; diff --git a/panda/src/text/dynamicTextGlyph.I b/panda/src/text/dynamicTextGlyph.I index fd75c4787b..f7a1413232 100644 --- a/panda/src/text/dynamicTextGlyph.I +++ b/panda/src/text/dynamicTextGlyph.I @@ -31,6 +31,43 @@ DynamicTextGlyph(DynamicTextPage *page, int x, int y, _x_size(x_size), _y_size(y_size), _margin(margin) { + _geom_count = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::Constructor +// Access: Publiic +// Description: This constructor makes an empty glyph, whose only +// purpose is to remember its width. It has no bitmap +// and no Geom. +//////////////////////////////////////////////////////////////////// +INLINE DynamicTextGlyph:: +DynamicTextGlyph(float advance) : + _page((DynamicTextPage *)NULL), + _x(0), _y(0), + _x_size(0), _y_size(0), + _margin(0) +{ + _advance = advance; + _geom_count = 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::Copy Constructor +// Access: Private +// Description: Copying DynamicTextGlyph objects is not allowed. +//////////////////////////////////////////////////////////////////// +INLINE DynamicTextGlyph:: +DynamicTextGlyph(const DynamicTextGlyph &) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::Copy Assignment Operator +// Access: Private +// Description: Copying DynamicTextGlyph objects is not allowed. +//////////////////////////////////////////////////////////////////// +INLINE void DynamicTextGlyph:: +operator = (const DynamicTextGlyph &) { } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/text/dynamicTextGlyph.cxx b/panda/src/text/dynamicTextGlyph.cxx index 865cfdeaf9..14dc5ea26a 100644 --- a/panda/src/text/dynamicTextGlyph.cxx +++ b/panda/src/text/dynamicTextGlyph.cxx @@ -21,10 +21,35 @@ #ifdef HAVE_FREETYPE #include "dynamicTextPage.h" -#include "geomTristrip.h" +#include "geomTextGlyph.h" #include "textureTransition.h" #include "transparencyTransition.h" +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DynamicTextGlyph:: +~DynamicTextGlyph() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::get_geom +// Access: Public, Virtual +// Description: Returns a Geom that renders the particular glyph. +//////////////////////////////////////////////////////////////////// +PT(Geom) DynamicTextGlyph:: +get_geom() const { + if (_geom == (Geom *)NULL) { + return _geom; + } + + // A DynamicTextGlyph must make a copy of its GeomTextGlyph, so that + // it will increase the reference count properly. + return _geom->make_copy(); +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextGlyph::get_row // Access: Publiic @@ -51,6 +76,26 @@ get_row(int y) { return _page->_pbuffer->_image + offset; } +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::erase +// Access: Publiic +// Description: Erases the glyph from the texture map. +//////////////////////////////////////////////////////////////////// +void DynamicTextGlyph:: +erase() { + nassertv(_page != (DynamicTextPage *)NULL); + nassertv(_page->_pbuffer != (PixelBuffer *)NULL); + + int ysizetop = _page->_pbuffer->get_ysize() - 1; + int xsize = _page->_pbuffer->get_xsize(); + unsigned char *buffer = _page->_pbuffer->_image; + + for (int y = _y; y < _y + _y_size; y++) { + int offset = (ysizetop - y) * xsize + _x; + memset(buffer + offset, 0, _x_size); + } +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextGlyph::make_geom // Access: Publiic @@ -64,6 +109,11 @@ get_row(int y) { void DynamicTextGlyph:: make_geom(int bitmap_top, int bitmap_left, float advance, float poly_margin, float pixels_per_unit) { + nassertv(_page != (DynamicTextPage *)NULL); + + // This function should not be called twice. + nassertv(_geom_count == 0); + // Determine the corners of the rectangle in geometric units. float top = (bitmap_top + poly_margin) / pixels_per_unit; float left = (bitmap_left - poly_margin) / pixels_per_unit; @@ -77,7 +127,12 @@ make_geom(int bitmap_top, int bitmap_left, float uv_right = (float)(_x + _x_size + poly_margin) / _page->get_x_size(); // Create a corresponding tristrip. - _geom = new GeomTristrip; + _geom = new GeomTextGlyph(this); + + // The above will increment our _geom_count to 1. Reset it back + // down to 0, since our own internal Geom doesn't count. + nassertv(_geom_count == 1); + _geom_count--; PTA_Vertexf coords; coords.push_back(Vertexf(left, 0, top)); diff --git a/panda/src/text/dynamicTextGlyph.h b/panda/src/text/dynamicTextGlyph.h index fe0c90b407..b9b1c5bc7c 100644 --- a/panda/src/text/dynamicTextGlyph.h +++ b/panda/src/text/dynamicTextGlyph.h @@ -38,13 +38,23 @@ class EXPCL_PANDA DynamicTextGlyph : public TextGlyph { public: INLINE DynamicTextGlyph(DynamicTextPage *page, int x, int y, int x_size, int y_size, int margin); + INLINE DynamicTextGlyph(float advance); +private: + INLINE DynamicTextGlyph(const DynamicTextGlyph ©); + INLINE void operator = (const DynamicTextGlyph ©); + +public: + virtual ~DynamicTextGlyph(); + virtual PT(Geom) get_geom() const; INLINE bool intersects(int x, int y, int x_size, int y_size) const; unsigned char *get_row(int y); + void erase(); void make_geom(int top, int left, float advance, float poly_margin, float pixels_per_unit); DynamicTextPage *_page; + int _geom_count; int _x, _y; int _x_size, _y_size; diff --git a/panda/src/text/dynamicTextPage.cxx b/panda/src/text/dynamicTextPage.cxx index 9a741984ff..e7fe5cc050 100644 --- a/panda/src/text/dynamicTextPage.cxx +++ b/panda/src/text/dynamicTextPage.cxx @@ -43,11 +43,19 @@ DynamicTextPage(DynamicTextFont *font) : PixelBuffer::F_alpha); mark_dirty(DF_image); + // We'd better never free this image. + set_keep_ram_image(true); + // We don't necessarily want to use mipmaps, since we don't want to // regenerate those every time the texture changes, but we do want // at least linear filtering. set_magfilter(FT_linear); set_minfilter(FT_linear); + + // It's slightly better to let the texture clamp, rather than + // wrapping, so we're less likely to get bleeding at the edges. + set_wrapu(WM_clamp); + set_wrapv(WM_clamp); } //////////////////////////////////////////////////////////////////// @@ -72,6 +80,43 @@ slot_glyph(int x_size, int y_size, int margin) { return glyph; } +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::garbage_collect +// Access: Private +// Description: Removes all of the glyphs from the page that are no +// longer being used by any Geoms. This should only be +// called from DynamicTextFont::garbage_collect(), since +// it is important to remove these glyphs from the +// font's index first. +//////////////////////////////////////////////////////////////////// +int DynamicTextPage:: +garbage_collect() { + int removed_count = 0; + + Glyphs new_glyphs; + Glyphs::iterator gi; + for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) { + DynamicTextGlyph *glyph = (*gi); + if (glyph->_geom_count != 0) { + // Keep this one. + new_glyphs.insert(new_glyphs.end(), (*gi)); + } else { + // Drop this one. + removed_count++; + glyph->erase(); + } + } + + if (removed_count != 0 && DynamicTextFont::get_update_cleared_glyphs()) { + // Only mark the texture dirty if the user specifically requested + // an automatic texture memory update upon clearing glyphs. + mark_dirty(Texture::DF_image); + } + + _glyphs.swap(new_glyphs); + return removed_count; +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextPage::find_hole // Access: Private diff --git a/panda/src/text/dynamicTextPage.h b/panda/src/text/dynamicTextPage.h index 9664b746e1..c549239e4a 100644 --- a/panda/src/text/dynamicTextPage.h +++ b/panda/src/text/dynamicTextPage.h @@ -49,6 +49,9 @@ public: PUBLISHED: INLINE bool is_empty() const; +private: + int garbage_collect(); + private: bool find_hole(int &x, int &y, int x_size, int y_size) const; DynamicTextGlyph *find_overlap(int x, int y, int x_size, int y_size) const; @@ -76,6 +79,8 @@ public: private: static TypeHandle _type_handle; + + friend DynamicTextFont; }; #include "dynamicTextPage.I" diff --git a/panda/src/text/geomTextGlyph.I b/panda/src/text/geomTextGlyph.I new file mode 100644 index 0000000000..2484b81743 --- /dev/null +++ b/panda/src/text/geomTextGlyph.I @@ -0,0 +1,47 @@ +// Filename: geomTextGlyph.I +// Created by: drose (11Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomTextGlyph:: +GeomTextGlyph(DynamicTextGlyph *glyph) : + _glyph(glyph) +{ + if (_glyph != (DynamicTextGlyph *)NULL) { + _glyph->_geom_count++; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE GeomTextGlyph:: +GeomTextGlyph(const GeomTextGlyph ©) : + GeomTristrip(copy), + _glyph(copy._glyph) +{ + if (_glyph != (DynamicTextGlyph *)NULL) { + _glyph->_geom_count++; + } +} diff --git a/panda/src/text/geomTextGlyph.cxx b/panda/src/text/geomTextGlyph.cxx new file mode 100644 index 0000000000..af2419e6c5 --- /dev/null +++ b/panda/src/text/geomTextGlyph.cxx @@ -0,0 +1,97 @@ +// Filename: geomTextGlyph.cxx +// Created by: drose (11Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "geomTextGlyph.h" + +#include "datagramIterator.h" +#include "bamReader.h" + +TypeHandle GeomTextGlyph::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void GeomTextGlyph:: +operator = (const GeomTextGlyph ©) { + GeomTristrip::operator = (copy); + if (_glyph != copy._glyph) { + if (_glyph != (DynamicTextGlyph *)NULL) { + _glyph->_geom_count--; + } + _glyph = copy._glyph; + if (_glyph != (DynamicTextGlyph *)NULL) { + _glyph->_geom_count++; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +GeomTextGlyph:: +~GeomTextGlyph() { + if (_glyph != (DynamicTextGlyph *)NULL) { + _glyph->_geom_count--; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Geom that is a shallow copy +// of this one. It will be a different Geom pointer, +// but its internal data may or may not be shared with +// that of the original Geom. +//////////////////////////////////////////////////////////////////// +Geom *GeomTextGlyph:: +make_copy() const { + return new GeomTextGlyph(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::register_with_factory +// Access: Public, Static +// Description: Factory method to generate a GeomTextGlyph object +//////////////////////////////////////////////////////////////////// +void GeomTextGlyph:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_GeomTextGlyph); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTextGlyph::make_GeomTextGlyph +// Access: Public +// Description: Factory method to generate a GeomTextGlyph object +//////////////////////////////////////////////////////////////////// +TypedWritable* GeomTextGlyph:: +make_GeomTextGlyph(const FactoryParams ¶ms) { + GeomTextGlyph *me = new GeomTextGlyph((DynamicTextGlyph *)NULL); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + me->fillin(scan, manager); + me->make_dirty(); + me->config(); + return me; +} diff --git a/panda/src/text/geomTextGlyph.h b/panda/src/text/geomTextGlyph.h new file mode 100644 index 0000000000..e690f9206a --- /dev/null +++ b/panda/src/text/geomTextGlyph.h @@ -0,0 +1,77 @@ +// Filename: geomTextGlyph.h +// Created by: drose (11Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef GEOMTEXTGLYPH_H +#define GEOMTEXTGLYPH_H + +#include "pandabase.h" + +#ifdef HAVE_FREETYPE + +#include "geomTristrip.h" +#include "dynamicTextGlyph.h" + +//////////////////////////////////////////////////////////////////// +// Class : GeomTextGlyph +// Description : This is a specialization on GeomTristrip for +// containing a triangle strip intended to represent a +// DynamicTextGlyph. Its sole purpose is to maintain +// the geom count on the glyph, so we can determine the +// actual usage count on a dynamic glyph (and thus know +// when it is safe to recycle the glyph). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA GeomTextGlyph : public GeomTristrip { +public: + INLINE GeomTextGlyph(DynamicTextGlyph *glyph); + INLINE GeomTextGlyph(const GeomTextGlyph ©); + void operator = (const GeomTextGlyph ©); + virtual ~GeomTextGlyph(); + + virtual Geom *make_copy() const; + +private: + PT(DynamicTextGlyph) _glyph; + +public: + static void register_with_read_factory(); + static TypedWritable *make_GeomTextGlyph(const FactoryParams ¶ms); + +PUBLISHED: + static TypeHandle get_class_type() { + return _type_handle; + } +public: + static void init_type() { + GeomTristrip::init_type(); + register_type(_type_handle, "GeomTextGlyph", + GeomTristrip::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "geomTextGlyph.I" + +#endif // HAVE_FREETYPE + +#endif // GEOMTEXTGLYPH_H diff --git a/panda/src/text/staticTextFont.cxx b/panda/src/text/staticTextFont.cxx index 1772979ac6..8490a61b03 100644 --- a/panda/src/text/staticTextFont.cxx +++ b/panda/src/text/staticTextFont.cxx @@ -80,17 +80,19 @@ write(ostream &out, int indent_level) const { for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) { int ch = (*gi).first; - if (islower(ch)) { - count_lowercase++; - lowercase[ch - 'a'] = true; - - } else if (isupper(ch)) { - count_uppercase++; - uppercase[ch - 'A'] = true; - - } else if (isdigit(ch)) { - count_digits++; - digits[ch - '0'] = true; + if (ch < 128) { + if (islower(ch)) { + count_lowercase++; + lowercase[ch - 'a'] = true; + + } else if (isupper(ch)) { + count_uppercase++; + uppercase[ch - 'A'] = true; + + } else if (isdigit(ch)) { + count_digits++; + digits[ch - '0'] = true; + } } } @@ -141,10 +143,10 @@ write(ostream &out, int indent_level) const { for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) { int ch = (*gi).first; - if (!isalnum(ch)) { + if (ch >= 128 || !isalnum(ch)) { indent(out, indent_level + 2) << ch; - if (isprint(ch)) { + if (ch < isprint(ch)) { out << " = '" << (char)ch << "'\n"; } } @@ -154,18 +156,23 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// // Function: StaticTextFont::get_glyph // Access: Public, Virtual -// Description: Returns the glyph associated with the given character -// code, or NULL if there is no such glyph. +// Description: Gets the glyph associated with the given character +// code, as well as an optional scaling parameter that +// should be applied to the glyph's geometry and advance +// parameters. Returns true if the glyph exists, false +// if it does not. //////////////////////////////////////////////////////////////////// -const TextGlyph *StaticTextFont:: -get_glyph(int character) { +bool StaticTextFont:: +get_glyph(int character, const TextGlyph *&glyph, float &glyph_scale) { Glyphs::const_iterator gi = _glyphs.find(character); if (gi == _glyphs.end()) { // No definition for this character. - return (TextGlyph *)NULL; - } else { - return (*gi).second; + return false; } + + glyph = (*gi).second; + glyph_scale = 1.0f; + return true; } //////////////////////////////////////////////////////////////////// @@ -275,6 +282,7 @@ find_characters(Node *root) { } else { _line_height = alist[ilist[0]][2]; } + _space_advance = 0.25f * _line_height; } } else { diff --git a/panda/src/text/staticTextFont.h b/panda/src/text/staticTextFont.h index fd45072532..b2560fccae 100644 --- a/panda/src/text/staticTextFont.h +++ b/panda/src/text/staticTextFont.h @@ -46,7 +46,8 @@ PUBLISHED: virtual void write(ostream &out, int indent_level) const; public: - virtual const TextGlyph *get_glyph(int character); + virtual bool get_glyph(int character, const TextGlyph *&glyph, + float &glyph_scale); private: bool find_character_gsets(Node *root, Geom *&ch, GeomPoint *&dot, diff --git a/panda/src/text/textFont.I b/panda/src/text/textFont.I index 880677eee8..eb62b484a2 100644 --- a/panda/src/text/textFont.I +++ b/panda/src/text/textFont.I @@ -38,3 +38,13 @@ INLINE float TextFont:: get_line_height() const { return _line_height; } + +//////////////////////////////////////////////////////////////////// +// Function: TextFont::get_space_advance +// Access: Public +// Description: Returns the number of units wide a space is. +//////////////////////////////////////////////////////////////////// +INLINE float TextFont:: +get_space_advance() const { + return _space_advance; +} diff --git a/panda/src/text/textFont.cxx b/panda/src/text/textFont.cxx index f42da1722b..598ca3aaea 100644 --- a/panda/src/text/textFont.cxx +++ b/panda/src/text/textFont.cxx @@ -41,7 +41,8 @@ isblank(char ch) { TextFont:: TextFont() { _is_valid = false; - _line_height = 1.0; + _line_height = 1.0f; + _space_advance = 0.25f; } //////////////////////////////////////////////////////////////////// @@ -60,19 +61,20 @@ TextFont:: // or 0.0 if the character is not known. //////////////////////////////////////////////////////////////////// float TextFont:: -calc_width(int ch) { - if (ch == ' ') { +calc_width(int character) { + if (character == ' ') { // A space is a special case. - return 0.25; + return _space_advance; } - const TextGlyph *glyph = get_glyph(ch); - if (glyph == (TextGlyph *)NULL) { + const TextGlyph *glyph; + float glyph_scale; + if (!get_glyph(character, glyph, glyph_scale)) { // Unknown character. - return 0.0; + return 0.0f; } - return glyph->get_advance(); + return glyph->get_advance() * glyph_scale; } //////////////////////////////////////////////////////////////////// @@ -84,7 +86,7 @@ calc_width(int ch) { //////////////////////////////////////////////////////////////////// float TextFont:: calc_width(const string &line) { - float width = 0.0; + float width = 0.0f; string::const_iterator si; for (si = line.begin(); si != line.end(); ++si) { @@ -111,10 +113,10 @@ wordwrap_to(const string &text, float wordwrap_width, size_t p = 0; // Preserve any initial whitespace and newlines. - float initial_width = 0.0; + float initial_width = 0.0f; while (p < text.length() && isspace(text[p])) { if (text[p] == '\n') { - initial_width = 0.0; + initial_width = 0.0f; } else { initial_width += calc_width(text[p]); } @@ -201,10 +203,10 @@ wordwrap_to(const string &text, float wordwrap_width, p = next_start; // Preserve any initial whitespace and newlines. - initial_width = 0.0; + initial_width = 0.0f; while (p < text.length() && isspace(text[p])) { if (text[p] == '\n') { - initial_width = 0.0; + initial_width = 0.0f; } else { initial_width += calc_width(text[p]); } diff --git a/panda/src/text/textFont.h b/panda/src/text/textFont.h index ec9f98cd21..75a1e50e36 100644 --- a/panda/src/text/textFont.h +++ b/panda/src/text/textFont.h @@ -49,7 +49,7 @@ PUBLISHED: INLINE bool is_valid() const; INLINE float get_line_height() const; - float calc_width(int ch); + float calc_width(int character); float calc_width(const string &line); string wordwrap_to(const string &text, float wordwrap_width, bool preserve_trailing_whitespace); @@ -57,11 +57,14 @@ PUBLISHED: virtual void write(ostream &out, int indent_level) const; public: - virtual const TextGlyph *get_glyph(int character)=0; + INLINE float get_space_advance() const; + virtual bool get_glyph(int character, const TextGlyph *&glyph, + float &glyph_scale)=0; protected: bool _is_valid; float _line_height; + float _space_advance; public: static TypeHandle get_class_type() { diff --git a/panda/src/text/textGlyph.I b/panda/src/text/textGlyph.I index 7ee0792810..dc518da824 100644 --- a/panda/src/text/textGlyph.I +++ b/panda/src/text/textGlyph.I @@ -64,16 +64,6 @@ operator = (const TextGlyph ©) { _advance = copy._advance; } -//////////////////////////////////////////////////////////////////// -// Function: TextGlyph::get_geom -// Access: Public -// Description: Returns a Geom that renders the particular glyph. -//////////////////////////////////////////////////////////////////// -INLINE Geom *TextGlyph:: -get_geom() const { - return _geom; -} - //////////////////////////////////////////////////////////////////// // Function: TextGlyph::get_trans // Access: Public diff --git a/panda/src/text/textGlyph.cxx b/panda/src/text/textGlyph.cxx index 4fc3d8568a..ea7ee6c73f 100644 --- a/panda/src/text/textGlyph.cxx +++ b/panda/src/text/textGlyph.cxx @@ -17,3 +17,22 @@ //////////////////////////////////////////////////////////////////// #include "textGlyph.h" + +//////////////////////////////////////////////////////////////////// +// Function: TextGlyph::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +TextGlyph:: +~TextGlyph() { +} + +//////////////////////////////////////////////////////////////////// +// Function: TextGlyph::get_geom +// Access: Public, Virtual +// Description: Returns a Geom that renders the particular glyph. +//////////////////////////////////////////////////////////////////// +PT(Geom) TextGlyph:: +get_geom() const { + return _geom; +} diff --git a/panda/src/text/textGlyph.h b/panda/src/text/textGlyph.h index cc7f9ae846..d6c243333f 100644 --- a/panda/src/text/textGlyph.h +++ b/panda/src/text/textGlyph.h @@ -39,8 +39,9 @@ public: INLINE TextGlyph(Geom *geom, const AllTransitionsWrapper &trans, float advance); INLINE TextGlyph(const TextGlyph ©); INLINE void operator = (const TextGlyph ©); + virtual ~TextGlyph(); - INLINE Geom *get_geom() const; + virtual PT(Geom) get_geom() const; INLINE const AllTransitionsWrapper &get_trans() const; INLINE float get_advance() const; diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx index ef315a34c6..59e186021f 100644 --- a/panda/src/text/textNode.cxx +++ b/panda/src/text/textNode.cxx @@ -346,7 +346,7 @@ generate() { // Now flatten our hierarchy to get rid of the transforms we put in, // applying them to the vertices. - if (flatten_text) { + if (text_flatten) { SceneGraphReducer gr(RenderRelation::get_class_type()); gr.apply_transitions(root_arc); gr.flatten(root, true); @@ -443,13 +443,14 @@ assemble_row(const char *&source, Node *dest) { if (character == ' ') { // A space is a special case. - xpos += 0.25f; + xpos += _font->get_space_advance(); } else { // A printable character. - const TextGlyph *glyph = _font->get_glyph(character); - if (glyph == (const TextGlyph *)NULL) { + const TextGlyph *glyph; + float glyph_scale; + if (!_font->get_glyph(character, glyph, glyph_scale)) { text_cat.warning() << "No definition in " << _font->get_name() << " for character " << character; @@ -461,13 +462,13 @@ assemble_row(const char *&source, Node *dest) { << "\n"; } else { - Geom *char_geom = glyph->get_geom(); - float char_advance = glyph->get_advance(); + PT(Geom) char_geom = glyph->get_geom(); const AllTransitionsWrapper &trans = glyph->get_trans(); - LMatrix4f mat = LMatrix4f::ident_mat(); - mat.set_row(3, LVector3f(xpos, 0.0f, 0.0f)); - if (char_geom != NULL) { + if (char_geom != (Geom *)NULL) { + LMatrix4f mat = LMatrix4f::scale_mat(glyph_scale); + mat.set_row(3, LVector3f(xpos, 0.0f, 0.0f)); + string ch(1, (char)character); GeomNode *geode = new GeomNode(ch); geode->add_geom(char_geom); @@ -476,7 +477,7 @@ assemble_row(const char *&source, Node *dest) { trans.store_to(rel); } - xpos += char_advance; + xpos += glyph->get_advance() * glyph_scale; } } source++; @@ -576,10 +577,10 @@ measure_row(const char *&source) { } else { // A printable character. - const TextGlyph *glyph = _font->get_glyph(character); - if (glyph != (const TextGlyph *)NULL) { - float char_advance = glyph->get_advance(); - xpos += char_advance; + const TextGlyph *glyph; + float glyph_scale; + if (_font->get_glyph(character, glyph, glyph_scale)) { + xpos += glyph->get_advance() * glyph_scale; } } source++; diff --git a/panda/src/text/text_composite1.cxx b/panda/src/text/text_composite1.cxx index 18afab1cfe..0facf45237 100644 --- a/panda/src/text/text_composite1.cxx +++ b/panda/src/text/text_composite1.cxx @@ -2,6 +2,7 @@ #include "dynamicTextFont.cxx" #include "dynamicTextGlyph.cxx" #include "dynamicTextPage.cxx" +#include "geomTextGlyph.cxx" #include "staticTextFont.cxx" #include "textFont.cxx" #include "textGlyph.cxx"