mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
further refinements, proper caching for dynamic text
This commit is contained in:
parent
d53603f85b
commit
d0b7f1b24c
@ -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 \
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "textNode.h"
|
||||
#include "dynamicTextFont.h"
|
||||
#include "dynamicTextPage.h"
|
||||
#include "geomTextGlyph.h"
|
||||
|
||||
#include <dconfig.h>
|
||||
|
||||
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,17 +324,26 @@ 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;
|
||||
|
||||
float advance = slot->advance.x / 64.0;
|
||||
|
||||
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";
|
||||
@ -257,11 +369,11 @@ make_glyph(int character) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::slot_glyph
|
||||
@ -278,29 +390,51 @@ 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;
|
||||
|
||||
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.
|
||||
// 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
|
||||
<< " won't fit on an empty page.\n";
|
||||
<< " 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.
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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<int, DynamicTextGlyph *> 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;
|
||||
|
@ -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 &) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
47
panda/src/text/geomTextGlyph.I
Normal file
47
panda/src/text/geomTextGlyph.I
Normal file
@ -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++;
|
||||
}
|
||||
}
|
97
panda/src/text/geomTextGlyph.cxx
Normal file
97
panda/src/text/geomTextGlyph.cxx
Normal file
@ -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;
|
||||
}
|
77
panda/src/text/geomTextGlyph.h
Normal file
77
panda/src/text/geomTextGlyph.h
Normal file
@ -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
|
@ -80,6 +80,7 @@ write(ostream &out, int indent_level) const {
|
||||
|
||||
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
|
||||
int ch = (*gi).first;
|
||||
if (ch < 128) {
|
||||
if (islower(ch)) {
|
||||
count_lowercase++;
|
||||
lowercase[ch - 'a'] = true;
|
||||
@ -93,6 +94,7 @@ write(ostream &out, int indent_level) const {
|
||||
digits[ch - '0'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count_lowercase == num_letters) {
|
||||
indent(out, indent_level + 2)
|
||||
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
if (char_geom != (Geom *)NULL) {
|
||||
LMatrix4f mat = LMatrix4f::scale_mat(glyph_scale);
|
||||
mat.set_row(3, LVector3f(xpos, 0.0f, 0.0f));
|
||||
if (char_geom != NULL) {
|
||||
|
||||
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++;
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user