Overhaul text generation; now 75x as fast as before.

This commit is contained in:
rdb 2015-11-13 01:44:03 +01:00
parent 0fffcdfc6b
commit a453ec1925
26 changed files with 1050 additions and 661 deletions

View File

@ -369,6 +369,8 @@ static const UnicodeLatinMap::Entry latin_map[] = {
UnicodeLatinMap::AT_none, UnicodeLatinMap::AF_reversed },
{ 0x0258, UnicodeLatinMap::CT_lower, 'e', 0, 0x0258, 0x018e,
UnicodeLatinMap::AT_none, UnicodeLatinMap::AF_reversed },
{ 0x0259, UnicodeLatinMap::CT_lower, 'e', 0, 0x0259, 0x018f,
UnicodeLatinMap::AT_none, UnicodeLatinMap::AF_turned },
{ 0x0066, UnicodeLatinMap::CT_lower, 'f', 0, 0x0066, 0x0046,
UnicodeLatinMap::AT_none, 0 },
{ 0x0046, UnicodeLatinMap::CT_upper, 'F', 0, 0x0066, 0x0046,
@ -465,7 +467,9 @@ static const UnicodeLatinMap::Entry latin_map[] = {
UnicodeLatinMap::AT_line_below, 0 },
{ 0x029c, UnicodeLatinMap::CT_upper, 'H', 0, 0x0068, 0x029c,
UnicodeLatinMap::AT_none, UnicodeLatinMap::AF_smallcap },
{ 0x0195, UnicodeLatinMap::CT_lower, 'h', 'v', 0x0195, 0x195,
{ 0x0195, UnicodeLatinMap::CT_lower, 'h', 'v', 0x0195, 0x01f6,
UnicodeLatinMap::AT_none, UnicodeLatinMap::AF_ligature },
{ 0x01f6, UnicodeLatinMap::CT_upper, 'H', 'v', 0x0195, 0x01f6,
UnicodeLatinMap::AT_none, UnicodeLatinMap::AF_ligature },
{ 0x0127, UnicodeLatinMap::CT_lower, 'h', 0, 0x0127, 0x0126,
UnicodeLatinMap::AT_stroke, 0 },

View File

@ -162,7 +162,7 @@ update_type(To *ptr) {
// (Assignment to a NULL pointer also works, of course.)
////////////////////////////////////////////////////////////////////
template<class T>
INLINE void PointerToBase<T>::
ALWAYS_INLINE void PointerToBase<T>::
clear() {
reassign((To *)NULL);
}

View File

@ -55,7 +55,7 @@ protected:
// vs. non-const later.
PUBLISHED:
INLINE void clear();
ALWAYS_INLINE void clear();
void output(ostream &out) const;
};

View File

@ -50,7 +50,7 @@ PointerToVoid(const PointerToVoid &) {
// false otherwise. (Direct comparison to a NULL
// pointer also works.)
////////////////////////////////////////////////////////////////////
INLINE bool PointerToVoid::
ALWAYS_INLINE bool PointerToVoid::
is_null() const {
return (_void_ptr == (void *)NULL);
}

View File

@ -42,7 +42,7 @@ private:
INLINE PointerToVoid(const PointerToVoid &copy);
PUBLISHED:
INLINE bool is_null() const;
ALWAYS_INLINE bool is_null() const;
INLINE size_t get_hash() const;
public:

View File

@ -260,8 +260,18 @@ get_poly_margin() const {
////////////////////////////////////////////////////////////////////
INLINE void DynamicTextFont::
set_page_size(int x_size, int y_size) {
_page_x_size = x_size;
_page_y_size = y_size;
_page_size.set(x_size, y_size);
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::get_page_size
// Access: Published
// Description: Returns the size of the textures that are created
// for the DynamicTextFont. See set_page_size().
////////////////////////////////////////////////////////////////////
INLINE const LVecBase2i &DynamicTextFont::
get_page_size() const {
return _page_size;
}
////////////////////////////////////////////////////////////////////
@ -272,7 +282,7 @@ set_page_size(int x_size, int y_size) {
////////////////////////////////////////////////////////////////////
INLINE int DynamicTextFont::
get_page_x_size() const {
return _page_x_size;
return _page_size.get_x();
}
////////////////////////////////////////////////////////////////////
@ -283,7 +293,7 @@ get_page_x_size() const {
////////////////////////////////////////////////////////////////////
INLINE int DynamicTextFont::
get_page_y_size() const {
return _page_y_size;
return _page_size.get_y();
}
////////////////////////////////////////////////////////////////////

View File

@ -44,6 +44,8 @@
#include "nurbsCurveResult.h"
//#include "renderModeAttrib.h"
//#include "antialiasAttrib.h"
#include "colorAttrib.h"
#include "textureAttrib.h"
TypeHandle DynamicTextFont::_type_handle;
@ -110,8 +112,7 @@ DynamicTextFont(const DynamicTextFont &copy) :
FreetypeFont(copy),
_texture_margin(copy._texture_margin),
_poly_margin(copy._poly_margin),
_page_x_size(copy._page_x_size),
_page_y_size(copy._page_y_size),
_page_size(copy._page_size),
_minfilter(copy._minfilter),
_magfilter(copy._magfilter),
_anisotropic_degree(copy._anisotropic_degree),
@ -194,8 +195,8 @@ garbage_collect() {
Cache new_cache;
Cache::iterator ci;
for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
DynamicTextGlyph *glyph = (*ci).second;
if (glyph == (DynamicTextGlyph *)NULL || glyph->_geom_count != 0) {
TextGlyph *glyph = (*ci).second;
if (glyph == (TextGlyph *)NULL || glyph->get_ref_count() > 1) {
// Keep this one.
new_cache.insert(new_cache.end(), (*ci));
} else {
@ -252,7 +253,7 @@ write(ostream &out, int indent_level) const {
Cache::const_iterator ci;
for (ci = _cache.begin(); ci != _cache.end(); ++ci) {
int glyph_index = (*ci).first;
DynamicTextGlyph *glyph = (*ci).second;
TextGlyph *glyph = (*ci).second;
indent(out, indent_level + 2)
<< glyph_index;
@ -269,7 +270,7 @@ write(ostream &out, int indent_level) const {
}
release_face(face);
out << ", count = " << glyph->_geom_count << "\n";
out << '\n';
}
}
@ -285,7 +286,7 @@ write(ostream &out, int indent_level) const {
// printable glyph.
////////////////////////////////////////////////////////////////////
bool DynamicTextFont::
get_glyph(int character, const TextGlyph *&glyph) {
get_glyph(int character, CPT(TextGlyph) &glyph) {
if (!_is_valid) {
glyph = (TextGlyph *)NULL;
return false;
@ -302,12 +303,12 @@ get_glyph(int character, const TextGlyph *&glyph) {
if (ci != _cache.end()) {
glyph = (*ci).second;
} else {
DynamicTextGlyph *dynamic_glyph = make_glyph(character, face, glyph_index);
TextGlyph *dynamic_glyph = make_glyph(character, face, glyph_index);
_cache.insert(Cache::value_type(glyph_index, dynamic_glyph));
glyph = dynamic_glyph;
}
if (glyph == (DynamicTextGlyph *)NULL) {
if (glyph == (TextGlyph *)NULL) {
glyph = get_invalid_glyph();
glyph_index = 0;
}
@ -327,8 +328,7 @@ void DynamicTextFont::
initialize() {
_texture_margin = text_texture_margin;
_poly_margin = text_poly_margin;
_page_x_size = text_page_size[0];
_page_y_size = text_page_size[1];
_page_size.set(text_page_size[0], text_page_size[1]);
// We don't necessarily want to use mipmaps, since we don't want to
// regenerate those every time the texture changes, but we probably
@ -442,10 +442,10 @@ determine_tex_format() {
// newly-created TextGlyph object, or NULL if the
// glyph cannot be created for some reason.
////////////////////////////////////////////////////////////////////
DynamicTextGlyph *DynamicTextFont::
TextGlyph *DynamicTextFont::
make_glyph(int character, FT_Face face, int glyph_index) {
if (!load_glyph(face, glyph_index, false)) {
return (DynamicTextGlyph *)NULL;
return (TextGlyph *)NULL;
}
FT_GlyphSlot slot = face->glyph;
@ -461,6 +461,7 @@ make_glyph(int character, FT_Face face, int glyph_index) {
}
PN_stdfloat advance = slot->advance.x / 64.0;
advance /= _font_pixels_per_unit;
if (_render_mode != RM_texture &&
slot->format == ft_glyph_format_outline) {
@ -520,8 +521,8 @@ make_glyph(int character, FT_Face face, int glyph_index) {
_contours.clear();
FT_Outline_Decompose(&slot->outline, &funcs, (void *)this);
PT(DynamicTextGlyph) glyph =
new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
PT(TextGlyph) glyph =
new TextGlyph(character, advance);
switch (_render_mode) {
case RM_wireframe:
render_wireframe_contours(glyph);
@ -553,8 +554,8 @@ make_glyph(int character, FT_Face face, int glyph_index) {
if (bitmap.width == 0 || bitmap.rows == 0) {
// If we got an empty bitmap, it's a special case.
PT(DynamicTextGlyph) glyph =
new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
PT(TextGlyph) glyph =
new TextGlyph(character, advance);
_empty_glyphs.push_back(glyph);
return glyph;
@ -571,7 +572,7 @@ make_glyph(int character, FT_Face face, int glyph_index) {
// If the bitmap produced from the font doesn't require scaling
// or any other processing before it goes to the texture, we can
// just copy it directly into the texture.
glyph = slot_glyph(character, bitmap.width, bitmap.rows);
glyph = slot_glyph(character, bitmap.width, bitmap.rows, advance);
copy_bitmap_to_texture(bitmap, glyph);
} else {
@ -599,7 +600,7 @@ make_glyph(int character, FT_Face face, int glyph_index) {
int_y_size += outline * 2;
tex_x_size += outline * 2;
tex_y_size += outline * 2;
glyph = slot_glyph(character, int_x_size, int_y_size);
glyph = slot_glyph(character, int_x_size, int_y_size, advance);
if (outline != 0) {
// Pad the glyph image to make room for the outline.
@ -612,11 +613,43 @@ make_glyph(int character, FT_Face face, int glyph_index) {
}
}
glyph->make_geom((int)floor(slot->bitmap_top + outline * _scale_factor + 0.5f),
(int)floor(slot->bitmap_left - outline * _scale_factor + 0.5f),
advance, _poly_margin,
tex_x_size, tex_y_size,
_font_pixels_per_unit, _tex_pixels_per_unit);
DynamicTextPage *page = glyph->get_page();
if (page != NULL) {
int bitmap_top = (int)floor(slot->bitmap_top + outline * _scale_factor + 0.5f);
int bitmap_left = (int)floor(slot->bitmap_left - outline * _scale_factor + 0.5f);
tex_x_size += glyph->_margin * 2;
tex_y_size += glyph->_margin * 2;
// Determine the corners of the rectangle in geometric units.
PN_stdfloat tex_poly_margin = _poly_margin / _tex_pixels_per_unit;
PN_stdfloat origin_y = bitmap_top / _font_pixels_per_unit;
PN_stdfloat origin_x = bitmap_left / _font_pixels_per_unit;
LVecBase4 dimensions(
origin_x - tex_poly_margin,
origin_y - tex_y_size / _tex_pixels_per_unit - tex_poly_margin,
origin_x + tex_x_size / _tex_pixels_per_unit + tex_poly_margin,
origin_y + tex_poly_margin);
// And the corresponding corners in UV units. We add 0.5f to center
// the UV in the middle of its texel, to minimize roundoff errors
// when we are close to 1-to-1 pixel size.
LVecBase2i page_size = page->get_size();
LVecBase4 texcoords(
((PN_stdfloat)(glyph->_x - _poly_margin) + 0.5f) / page_size[0],
1.0f - ((PN_stdfloat)(glyph->_y + _poly_margin + tex_y_size) + 0.5f) / page_size[1],
((PN_stdfloat)(glyph->_x + _poly_margin + tex_x_size) + 0.5f) / page_size[0],
1.0f - ((PN_stdfloat)(glyph->_y - _poly_margin) + 0.5f) / page_size[1]);
CPT(RenderState) state;
state = RenderState::make(TextureAttrib::make(page),
TransparencyAttrib::make(TransparencyAttrib::M_alpha));
state = state->add_attrib(ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 1.0f)), -1);
glyph->set_quad(dimensions, texcoords, state);
}
return glyph;
}
}
@ -835,7 +868,7 @@ blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
// filled in yet except with its size.
////////////////////////////////////////////////////////////////////
DynamicTextGlyph *DynamicTextFont::
slot_glyph(int character, int x_size, int y_size) {
slot_glyph(int character, int x_size, int y_size, PN_stdfloat advance) {
// Increase the indicated size by the current margin.
x_size += _texture_margin * 2;
y_size += _texture_margin * 2;
@ -850,7 +883,7 @@ slot_glyph(int character, int x_size, int y_size) {
do {
DynamicTextPage *page = _pages[pi];
DynamicTextGlyph *glyph = page->slot_glyph(character, x_size, y_size, _texture_margin);
DynamicTextGlyph *glyph = page->slot_glyph(character, x_size, y_size, _texture_margin, advance);
if (glyph != (DynamicTextGlyph *)NULL) {
// Once we found a page to hold the glyph, that becomes our
// new preferred page.
@ -874,7 +907,7 @@ slot_glyph(int character, int x_size, int y_size) {
// glyphs?
if (garbage_collect() != 0) {
// Yes, we just freed up some space. Try once more, recursively.
return slot_glyph(character, x_size, y_size);
return slot_glyph(character, x_size, y_size, advance);
} else {
// No good; all recorded glyphs are actually in use. We need to
@ -882,7 +915,7 @@ slot_glyph(int character, int x_size, int y_size) {
_preferred_page = _pages.size();
PT(DynamicTextPage) page = new DynamicTextPage(this, _preferred_page);
_pages.push_back(page);
return page->slot_glyph(character, x_size, y_size, _texture_margin);
return page->slot_glyph(character, x_size, y_size, _texture_margin, advance);
}
}
@ -893,7 +926,7 @@ slot_glyph(int character, int x_size, int y_size) {
// geometry, as a wireframe render.
////////////////////////////////////////////////////////////////////
void DynamicTextFont::
render_wireframe_contours(DynamicTextGlyph *glyph) {
render_wireframe_contours(TextGlyph *glyph) {
PT(GeomVertexData) vdata = new GeomVertexData
(string(), GeomVertexFormat::get_v3(),
Geom::UH_static);
@ -926,7 +959,7 @@ render_wireframe_contours(DynamicTextGlyph *glyph) {
// geometry, as a polygon render.
////////////////////////////////////////////////////////////////////
void DynamicTextFont::
render_polygon_contours(DynamicTextGlyph *glyph, bool face, bool extrude) {
render_polygon_contours(TextGlyph *glyph, bool face, bool extrude) {
PT(GeomVertexData) vdata = new GeomVertexData
(string(), GeomVertexFormat::get_v3n3(),
Geom::UH_static);

View File

@ -74,6 +74,7 @@ PUBLISHED:
INLINE PN_stdfloat get_poly_margin() const;
INLINE void set_page_size(int x_size, int y_size);
INLINE const LVecBase2i &get_page_size() const;
INLINE int get_page_x_size() const;
INLINE int get_page_y_size() const;
@ -110,21 +111,21 @@ PUBLISHED:
virtual void write(ostream &out, int indent_level) const;
public:
virtual bool get_glyph(int character, const TextGlyph *&glyph);
virtual bool get_glyph(int character, CPT(TextGlyph) &glyph);
private:
void initialize();
void update_filters();
void determine_tex_format();
DynamicTextGlyph *make_glyph(int character, FT_Face face, int glyph_index);
TextGlyph *make_glyph(int character, FT_Face face, int glyph_index);
void copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph);
void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph);
void blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
const LColor &fg);
DynamicTextGlyph *slot_glyph(int character, int x_size, int y_size);
DynamicTextGlyph *slot_glyph(int character, int x_size, int y_size, PN_stdfloat advance);
void render_wireframe_contours(DynamicTextGlyph *glyph);
void render_polygon_contours(DynamicTextGlyph *glyph, bool face, bool extrude);
void render_wireframe_contours(TextGlyph *glyph);
void render_polygon_contours(TextGlyph *glyph, bool face, bool extrude);
static int outline_move_to(const FT_Vector *to, void *user);
static int outline_line_to(const FT_Vector *to, void *user);
@ -137,7 +138,7 @@ private:
int _texture_margin;
PN_stdfloat _poly_margin;
int _page_x_size, _page_y_size;
LVecBase2i _page_size;
SamplerState::FilterType _minfilter;
SamplerState::FilterType _magfilter;
@ -159,14 +160,14 @@ private:
// 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;
typedef pmap<int, TextGlyph *> 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;
typedef pvector< PT(TextGlyph) > EmptyGlyphs;
EmptyGlyphs _empty_glyphs;
class ContourPoint {

View File

@ -21,16 +21,13 @@
////////////////////////////////////////////////////////////////////
INLINE DynamicTextGlyph::
DynamicTextGlyph(int character, DynamicTextPage *page, int x, int y,
int x_size, int y_size, int margin) :
TextGlyph(character),
int x_size, int y_size, int margin, PN_stdfloat advance) :
TextGlyph(character, advance),
_page(page),
_x(x), _y(y),
_x_size(x_size), _y_size(y_size),
_margin(margin),
_top(0), _left(0), _bottom(0), _right(0),
_uv_top(0), _uv_left(0), _uv_bottom(0), _uv_right(0)
_margin(margin)
{
_geom_count = 0;
}
////////////////////////////////////////////////////////////////////
@ -42,14 +39,12 @@ DynamicTextGlyph(int character, DynamicTextPage *page, int x, int y,
////////////////////////////////////////////////////////////////////
INLINE DynamicTextGlyph::
DynamicTextGlyph(int character, PN_stdfloat advance) :
TextGlyph(character),
TextGlyph(character, advance),
_page((DynamicTextPage *)NULL),
_x(0), _y(0),
_x_size(0), _y_size(0),
_margin(0)
{
_advance = advance;
_geom_count = 1;
}
////////////////////////////////////////////////////////////////////
@ -105,17 +100,6 @@ intersects(int x, int y, int x_size, int y_size) const {
y >= mbot || hbot <= _y);
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::get_top
// Access: Published
// Description: Returns the vertex coordinates that can be used when
// creating a custom text renderer.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_top() const {
return _top;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::get_left
// Access: Published
@ -124,7 +108,7 @@ get_top() const {
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_left() const {
return _left;
return _quad_dimensions[0];
}
////////////////////////////////////////////////////////////////////
@ -135,7 +119,7 @@ get_left() const {
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_bottom() const {
return _bottom;
return _quad_dimensions[1];
}
////////////////////////////////////////////////////////////////////
@ -146,18 +130,18 @@ get_bottom() const {
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_right() const {
return _right;
return _quad_dimensions[2];
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::get_uv_top
// Function: DynamicTextGlyph::get_top
// Access: Published
// Description: Returns the UV coordinates that can be used when
// Description: Returns the vertex coordinates that can be used when
// creating a custom text renderer.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_uv_top() const {
return _uv_top;
get_top() const {
return _quad_dimensions[3];
}
////////////////////////////////////////////////////////////////////
@ -168,7 +152,7 @@ get_uv_top() const {
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_uv_left() const {
return _uv_left;
return _quad_texcoords[0];
}
////////////////////////////////////////////////////////////////////
@ -179,7 +163,7 @@ get_uv_left() const {
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_uv_bottom() const {
return _uv_bottom;
return _quad_texcoords[1];
}
////////////////////////////////////////////////////////////////////
@ -190,5 +174,16 @@ get_uv_bottom() const {
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_uv_right() const {
return _uv_right;
return _quad_texcoords[2];
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::get_uv_top
// Access: Published
// Description: Returns the UV coordinates that can be used when
// creating a custom text renderer.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat DynamicTextGlyph::
get_uv_top() const {
return _quad_texcoords[3];
}

View File

@ -87,118 +87,6 @@ erase(DynamicTextFont *font) {
font->get_bg());
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::make_geom
// Access: Public
// Description: Creates the actual geometry for the glyph. The
// parameters bitmap_top and bitmap_left are from
// FreeType, and indicate the position of the top left
// corner of the bitmap relative to the glyph's origin.
// The advance number represents the number of pixels
// the pen should be advanced after drawing this glyph.
////////////////////////////////////////////////////////////////////
void DynamicTextGlyph::
make_geom(int bitmap_top, int bitmap_left, PN_stdfloat advance, PN_stdfloat poly_margin,
PN_stdfloat tex_x_size, PN_stdfloat tex_y_size,
PN_stdfloat font_pixels_per_unit, PN_stdfloat tex_pixels_per_unit) {
nassertv(_page != (DynamicTextPage *)NULL);
// This function should not be called twice.
nassertv(_geom_count == 0);
tex_x_size += _margin * 2;
tex_y_size += _margin * 2;
// Determine the corners of the rectangle in geometric units.
PN_stdfloat tex_poly_margin = poly_margin / tex_pixels_per_unit;
PN_stdfloat origin_y = bitmap_top / font_pixels_per_unit;
PN_stdfloat origin_x = bitmap_left / font_pixels_per_unit;
_top = origin_y + tex_poly_margin;
_left = origin_x - tex_poly_margin;
_bottom = origin_y - tex_y_size / tex_pixels_per_unit - tex_poly_margin;
_right = origin_x + tex_x_size / tex_pixels_per_unit + tex_poly_margin;
// And the corresponding corners in UV units. We add 0.5f to center
// the UV in the middle of its texel, to minimize roundoff errors
// when we are close to 1-to-1 pixel size.
_uv_top = 1.0f - ((PN_stdfloat)(_y - poly_margin) + 0.5f) / _page->get_y_size();
_uv_left = ((PN_stdfloat)(_x - poly_margin) + 0.5f) / _page->get_x_size();
_uv_bottom = 1.0f - ((PN_stdfloat)(_y + poly_margin + tex_y_size) + 0.5f) / _page->get_y_size();
_uv_right = ((PN_stdfloat)(_x + poly_margin + tex_x_size) + 0.5f) / _page->get_x_size();
// Create a corresponding triangle pair. We use a pair of indexed
// triangles rather than a single triangle strip, to avoid the bad
// vertex duplication behavior with lots of two-triangle strips.
PT(GeomVertexData) vdata = new GeomVertexData
(string(), GeomVertexFormat::get_v3t2(),
Geom::UH_static);
vdata->unclean_set_num_rows(4);
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
vertex.add_data3(_left, 0, _top);
vertex.add_data3(_left, 0, _bottom);
vertex.add_data3(_right, 0, _top);
vertex.add_data3(_right, 0, _bottom);
texcoord.add_data2(_uv_left, _uv_top);
texcoord.add_data2(_uv_left, _uv_bottom);
texcoord.add_data2(_uv_right, _uv_top);
texcoord.add_data2(_uv_right, _uv_bottom);
PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
tris->reserve_num_vertices(6);
tris->add_vertex(0);
tris->add_vertex(1);
tris->add_vertex(2);
tris->close_primitive();
tris->add_vertex(2);
tris->add_vertex(1);
tris->add_vertex(3);
tris->close_primitive();
PT(Geom) geom = new GeomTextGlyph(this, vdata);
geom->add_primitive(tris);
_geom = geom;
// 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--;
_state = RenderState::make(TextureAttrib::make(_page),
TransparencyAttrib::make(TransparencyAttrib::M_alpha));
_state = _state->add_attrib(ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 1.0f)), -1);
_advance = advance / font_pixels_per_unit;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::set_geom
// Access: Public
// Description: Sets the geom from a pre-built object.
////////////////////////////////////////////////////////////////////
void DynamicTextGlyph::
set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state) {
// This function is called when _geom_count = 1, because it was
// constructed via the empty Glyph constructor.
nassertv(_geom_count == 1);
_geom_count--;
PT(Geom) geom = new GeomTextGlyph(this, vdata);
geom->add_primitive(prim);
_geom = geom;
// 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--;
_state = state;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::is_whitespace
// Access: Public, Virtual

View File

@ -35,7 +35,7 @@ class EXPCL_PANDA_TEXT DynamicTextGlyph : public TextGlyph {
public:
INLINE DynamicTextGlyph(int character, DynamicTextPage *page,
int x, int y, int x_size, int y_size,
int margin);
int margin, PN_stdfloat advance);
INLINE DynamicTextGlyph(int character, PN_stdfloat advance);
private:
INLINE DynamicTextGlyph(const DynamicTextGlyph &copy);
@ -48,34 +48,28 @@ PUBLISHED:
INLINE bool intersects(int x, int y, int x_size, int y_size) const;
INLINE PN_stdfloat get_top() const;
INLINE PN_stdfloat get_left() const;
INLINE PN_stdfloat get_bottom() const;
INLINE PN_stdfloat get_right() const;
INLINE PN_stdfloat get_top() const;
INLINE PN_stdfloat get_uv_top() const;
INLINE PN_stdfloat get_uv_left() const;
INLINE PN_stdfloat get_uv_bottom() const;
INLINE PN_stdfloat get_uv_right() const;
INLINE PN_stdfloat get_uv_top() const;
public:
unsigned char *get_row(int y);
void erase(DynamicTextFont *font);
void make_geom(int top, int left, PN_stdfloat advance, PN_stdfloat poly_margin,
PN_stdfloat tex_x_size, PN_stdfloat tex_y_size,
PN_stdfloat font_pixels_per_unit, PN_stdfloat tex_pixels_per_unit);
void set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state);
virtual bool is_whitespace() const;
DynamicTextPage *_page;
int _geom_count;
int _x, _y;
int _x_size, _y_size;
int _margin;
PN_stdfloat _top, _left, _bottom, _right;
PN_stdfloat _uv_top, _uv_left, _uv_bottom, _uv_right;
friend class DynamicTextFont;
public:
static TypeHandle get_class_type() {

View File

@ -13,6 +13,16 @@
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: DynamicTextPage::get_size
// Access: Published
// Description: Returns the size of the page (texture), in pixels.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase2i &DynamicTextPage::
get_size() const {
return _size;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextPage::get_x_size
// Access: Published
@ -20,7 +30,7 @@
////////////////////////////////////////////////////////////////////
INLINE int DynamicTextPage::
get_x_size() const {
return _x_size;
return _size.get_x();
}
////////////////////////////////////////////////////////////////////
@ -30,10 +40,9 @@ get_x_size() const {
////////////////////////////////////////////////////////////////////
INLINE int DynamicTextPage::
get_y_size() const {
return _y_size;
return _size.get_y();
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextPage::is_empty
// Access: Published

View File

@ -38,10 +38,9 @@ DynamicTextPage(DynamicTextFont *font, int page_number) :
// doesn't usually have a high fill factor.
set_quality_level(text_quality_level);
_x_size = _font->get_page_x_size();
_y_size = _font->get_page_y_size();
_size = _font->get_page_size();
setup_2d_texture(_x_size, _y_size, T_unsigned_byte, font->get_tex_format());
setup_2d_texture(_size[0], _size[1], T_unsigned_byte, font->get_tex_format());
// Assign a name to the Texture.
ostringstream strm;
@ -63,7 +62,7 @@ DynamicTextPage(DynamicTextFont *font, int page_number) :
set_border_color(font->get_bg());
// Fill the page with the font's background color.
fill_region(0, 0, _x_size, _y_size, font->get_bg());
fill_region(0, 0, _size[0], _size[1], font->get_bg());
}
////////////////////////////////////////////////////////////////////
@ -74,7 +73,8 @@ DynamicTextPage(DynamicTextFont *font, int page_number) :
// glyph object and returns it; otherwise, returns NULL.
////////////////////////////////////////////////////////////////////
DynamicTextGlyph *DynamicTextPage::
slot_glyph(int character, int x_size, int y_size, int margin) {
slot_glyph(int character, int x_size, int y_size, int margin,
PN_stdfloat advance) {
int x, y;
if (!find_hole(x, y, x_size, y_size)) {
// No room for the glyph.
@ -84,7 +84,7 @@ slot_glyph(int character, int x_size, int y_size, int margin) {
// The glyph can be fit at (x, y). Slot it.
PT(DynamicTextGlyph) glyph =
new DynamicTextGlyph(character, this,
x, y, x_size, y_size, margin);
x, y, x_size, y_size, margin, advance);
_glyphs.push_back(glyph);
return glyph;
}
@ -97,7 +97,7 @@ slot_glyph(int character, int x_size, int y_size, int margin) {
////////////////////////////////////////////////////////////////////
void DynamicTextPage::
fill_region(int x, int y, int x_size, int y_size, const LColor &color) {
nassertv(x >= 0 && x + x_size <= _x_size && y >= 0 && y + y_size <= _y_size);
nassertv(x >= 0 && x + x_size <= _size[0] && y >= 0 && y + y_size <= _size[1]);
int num_components = get_num_components();
if (num_components == 1) {
// Luminance or alpha.
@ -110,7 +110,7 @@ fill_region(int x, int y, int x_size, int y_size, const LColor &color) {
unsigned char *image = modify_ram_image();
for (int yi = y; yi < y + y_size; yi++) {
unsigned char *row = image + yi * _x_size;
unsigned char *row = image + yi * _size[0];
memset(row + x, v, x_size);
}
@ -127,7 +127,7 @@ fill_region(int x, int y, int x_size, int y_size, const LColor &color) {
PN_uint16 *image = (PN_uint16 *)modify_ram_image().p();
for (int yi = y; yi < y + y_size; yi++) {
PN_uint16 *row = image + yi * _x_size ;
PN_uint16 *row = image + yi * _size[0] ;
for (int xi = x; xi < x + x_size; xi++) {
row[xi] = v.v;
}
@ -142,7 +142,7 @@ fill_region(int x, int y, int x_size, int y_size, const LColor &color) {
unsigned char *image = modify_ram_image();
for (int yi = y; yi < y + y_size; yi++) {
unsigned char *row = image + yi * _x_size * 3;
unsigned char *row = image + yi * _size[0] * 3;
for (int xi = x; xi < x + x_size; xi++) {
row[xi * 3] = p0;
row[xi * 3 + 1] = p1;
@ -164,12 +164,12 @@ fill_region(int x, int y, int x_size, int y_size, const LColor &color) {
PN_uint32 *image = (PN_uint32 *)modify_ram_image().p();
for (int yi = y; yi < y + y_size; yi++) {
PN_uint32 *row = image + yi * _x_size;
PN_uint32 *row = image + yi * _size[0];
for (int xi = x; xi < x + x_size; xi++) {
row[xi] = v.v;
}
}
}
}
}
////////////////////////////////////////////////////////////////////
@ -189,7 +189,8 @@ garbage_collect(DynamicTextFont *font) {
Glyphs::iterator gi;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
if (glyph->_geom_count != 0) {
cerr << glyph->get_character() << " - " << glyph->get_ref_count() << "\n";
if (glyph->get_ref_count() > 1) {
// Keep this one.
new_glyphs.insert(new_glyphs.end(), (*gi));
} else {
@ -214,11 +215,11 @@ garbage_collect(DynamicTextFont *font) {
bool DynamicTextPage::
find_hole(int &x, int &y, int x_size, int y_size) const {
y = 0;
while (y + y_size <= _y_size) {
int next_y = _y_size;
while (y + y_size <= _size[1]) {
int next_y = _size[1];
// Scan along the row at 'y'.
x = 0;
while (x + x_size <= _x_size) {
while (x + x_size <= _size[0]) {
int next_x = x;
// Consider the spot at x, y.

View File

@ -37,10 +37,11 @@ class EXPCL_PANDA_TEXT DynamicTextPage : public Texture {
public:
DynamicTextPage(DynamicTextFont *font, int page_number);
DynamicTextGlyph *slot_glyph(int character,
int x_size, int y_size, int margin);
DynamicTextGlyph *slot_glyph(int character, int x_size, int y_size,
int margin, PN_stdfloat advance);
PUBLISHED:
INLINE const LVecBase2i &get_size() const;
INLINE int get_x_size() const;
INLINE int get_y_size() const;
@ -58,7 +59,7 @@ private:
typedef pvector< PT(DynamicTextGlyph) > Glyphs;
Glyphs _glyphs;
int _x_size, _y_size;
LVecBase2i _size;
DynamicTextFont *_font;

View File

@ -13,9 +13,6 @@
////////////////////////////////////////////////////////////////////
#include "geomTextGlyph.h"
#ifdef HAVE_FREETYPE
#include "datagramIterator.h"
#include "bamReader.h"
#include "indent.h"
@ -26,26 +23,25 @@ TypeHandle GeomTextGlyph::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::Constructor
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomTextGlyph::
GeomTextGlyph(DynamicTextGlyph *glyph, const GeomVertexData *data) :
GeomTextGlyph(const TextGlyph *glyph, const GeomVertexData *data) :
Geom(data)
{
// Initially, there is only one glyph in the Geom. There might be
// additional Glyphs later when we flatten the graph and call
// Geom::unify().
if (glyph != (DynamicTextGlyph *)NULL) {
if (glyph != (const TextGlyph *)NULL) {
_glyphs.reserve(1);
_glyphs.push_back(glyph);
glyph->_geom_count++;
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::Constructor
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomTextGlyph::
GeomTextGlyph(const GeomVertexData *data) :
@ -57,59 +53,48 @@ GeomTextGlyph(const GeomVertexData *data) :
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::Copy Constructor
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomTextGlyph::
GeomTextGlyph(const GeomTextGlyph &copy) :
Geom(copy),
_glyphs(copy._glyphs)
{
Glyphs::iterator gi;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
nassertv(glyph != (DynamicTextGlyph *)NULL);
glyph->_geom_count++;
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
GeomTextGlyph::
GeomTextGlyph(const Geom &copy, const TextGlyph *glyph) :
Geom(copy)
{
if (glyph != (const TextGlyph *)NULL) {
_glyphs.reserve(1);
_glyphs.push_back(glyph);
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::Copy Assignment Operator
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void GeomTextGlyph::
operator = (const GeomTextGlyph &copy) {
Geom::operator = (copy);
Glyphs::iterator gi;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
nassertv(glyph != (DynamicTextGlyph *)NULL);
glyph->_geom_count--;
nassertv((*gi)->_geom_count >= 0);
}
_glyphs = copy._glyphs;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
nassertv(glyph != (DynamicTextGlyph *)NULL);
glyph->_geom_count++;
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::Destructor
// Access: Public, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
GeomTextGlyph::
~GeomTextGlyph() {
Glyphs::iterator gi;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
nassertv(glyph != (DynamicTextGlyph *)NULL);
glyph->_geom_count--;
nassertv(glyph->_geom_count >= 0);
}
}
////////////////////////////////////////////////////////////////////
@ -149,11 +134,8 @@ copy_primitives_from(const Geom *other) {
DCAST_INTO_R(tother, other, false);
// Also copy the glyph pointers.
Glyphs::const_iterator gi;
for (gi = tother->_glyphs.begin(); gi != tother->_glyphs.end(); ++gi) {
_glyphs.push_back(*gi);
(*gi)->_geom_count++;
}
_glyphs.reserve(_glyphs.size() + tother->_glyphs.size());
_glyphs.insert(_glyphs.end(), tother->_glyphs.begin(), tother->_glyphs.end());
return true;
}
@ -176,18 +158,15 @@ count_geom(const Geom *other) {
const GeomTextGlyph *tother;
DCAST_INTO_V(tother, other);
Glyphs::const_iterator gi;
for (gi = tother->_glyphs.begin(); gi != tother->_glyphs.end(); ++gi) {
_glyphs.push_back(*gi);
(*gi)->_geom_count++;
}
_glyphs.reserve(_glyphs.size() + tother->_glyphs.size());
_glyphs.insert(_glyphs.end(), tother->_glyphs.begin(), tother->_glyphs.end());
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::output
// Access: Public, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void GeomTextGlyph::
output(ostream &out) const {
@ -195,8 +174,8 @@ output(ostream &out) const {
out << ", glyphs: [";
Glyphs::const_iterator gi;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
nassertv(glyph != (DynamicTextGlyph *)NULL);
const TextGlyph *glyph = (*gi);
nassertv(glyph != (const TextGlyph *)NULL);
out << " " << glyph->get_character();
}
out << " ]";
@ -205,7 +184,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::write
// Access: Public, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void GeomTextGlyph::
write(ostream &out, int indent_level) const {
@ -214,13 +193,24 @@ write(ostream &out, int indent_level) const {
<< "Glyphs: [";
Glyphs::const_iterator gi;
for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
DynamicTextGlyph *glyph = (*gi);
nassertv(glyph != (DynamicTextGlyph *)NULL);
const TextGlyph *glyph = (*gi);
nassertv(glyph != (const TextGlyph *)NULL);
out << " " << glyph->get_character();
}
out << " ]\n";
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::add_glyph
// Access: Public
// Description: Adds a glyph to the list of glyphs referenced by
// this Geom.
////////////////////////////////////////////////////////////////////
void GeomTextGlyph::
add_glyph(const TextGlyph *glyph) {
_glyphs.push_back(glyph);
}
////////////////////////////////////////////////////////////////////
// Function: GeomTextGlyph::register_with_factory
// Access: Public, Static
@ -238,7 +228,7 @@ register_with_read_factory() {
////////////////////////////////////////////////////////////////////
TypedWritable* GeomTextGlyph::
make_GeomTextGlyph(const FactoryParams &params) {
GeomTextGlyph *me = new GeomTextGlyph((DynamicTextGlyph *)NULL,
GeomTextGlyph *me = new GeomTextGlyph((const TextGlyph *)NULL,
(GeomVertexData *)NULL);
DatagramIterator scan;
BamReader *manager;
@ -247,5 +237,3 @@ make_GeomTextGlyph(const FactoryParams &params) {
me->fillin(scan, manager);
return me;
}
#endif // HAVE_FREETYPE

View File

@ -17,15 +17,12 @@
#include "pandabase.h"
#include "geom.h"
#ifdef HAVE_FREETYPE
#include "dynamicTextGlyph.h"
#include "textGlyph.h"
////////////////////////////////////////////////////////////////////
// Class : GeomTextGlyph
// Description : This is a specialization on Geom for containing a
// primitive intended to represent a DynamicTextGlyph.
// primitive intended to represent a TextGlyph.
// 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
@ -33,10 +30,10 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_TEXT GeomTextGlyph : public Geom {
public:
GeomTextGlyph(DynamicTextGlyph *glyph,
const GeomVertexData *data);
GeomTextGlyph(const TextGlyph *glyph, const GeomVertexData *data);
GeomTextGlyph(const GeomVertexData *data);
GeomTextGlyph(const GeomTextGlyph &copy);
GeomTextGlyph(const Geom &copy, const TextGlyph *glyph);
void operator = (const GeomTextGlyph &copy);
virtual ~GeomTextGlyph();
ALLOC_DELETED_CHAIN(GeomTextGlyph);
@ -48,8 +45,12 @@ public:
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level = 0) const;
void add_glyph(const TextGlyph *glyph);
friend class TextAssembler;
private:
typedef pvector< PT(DynamicTextGlyph) > Glyphs;
typedef pvector< CPT(TextGlyph) > Glyphs;
Glyphs _glyphs;
public:
@ -77,11 +78,4 @@ private:
#include "geomTextGlyph.I"
#else // HAVE_FREETYPE
// Without Freetype, a GeomTextGlyph is really just an ordinary Geom.
typedef Geom GeomTextGlyph;
#endif // HAVE_FREETYPE
#endif // GEOMTEXTGLYPH_H

View File

@ -234,7 +234,7 @@ write(ostream &out, int indent_level) const {
// printable glyph.
////////////////////////////////////////////////////////////////////
bool StaticTextFont::
get_glyph(int character, const TextGlyph *&glyph) {
get_glyph(int character, CPT(TextGlyph) &glyph) {
Glyphs::const_iterator gi = _glyphs.find(character);
if (gi == _glyphs.end()) {
// No definition for this character.

View File

@ -47,7 +47,7 @@ PUBLISHED:
virtual void write(ostream &out, int indent_level) const;
public:
virtual bool get_glyph(int character, const TextGlyph *&glyph);
virtual bool get_glyph(int character, CPT(TextGlyph) &glyph);
private:
void find_character_gsets(PandaNode *root, CPT(Geom) &ch, CPT(Geom) &dot,

View File

@ -522,24 +522,6 @@ ComputedProperties(ComputedProperties *based_on, const wstring &wname,
<< "Unknown TextProperties: " << name << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::GlyphPlacement::add_piece
// Access: Public
// Description: Adds a piece of the glyph, consisting of a single
// Geom and an associated RenderState. Typically, a
// glyph will have exactly one piece; there will only be
// multiple pieces in the case of cheesy accent marks or
// ligatures.
////////////////////////////////////////////////////////////////////
INLINE void TextAssembler::GlyphPlacement::
add_piece(Geom *geom, const RenderState *state) {
Piece piece;
piece._geom = geom;
piece._state = state;
_pieces.push_back(piece);
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::GeomCollectorKey::Constructor

File diff suppressed because it is too large Load Diff

View File

@ -164,12 +164,10 @@ private:
// And here it is, wordwrapped.
TextBlock _text_block;
#ifndef CPPPARSER // interrogate has a bit of trouble with wstring iterators.
void scan_wtext(TextString &output_string,
wstring::const_iterator &si,
const wstring::const_iterator &send,
ComputedProperties *current_cprops);
#endif // CPPPARSER
bool wordwrap_text();
@ -179,13 +177,6 @@ private:
// These structures are built up by assemble_paragraph() and
// assemble_row(). They represent the actual Geoms as laid out in a
// paragraph.
class Piece {
public:
PT(Geom) _geom;
CPT(RenderState) _state;
};
typedef pvector<Piece> Pieces;
class GeomCollectorKey {
public:
@ -218,26 +209,50 @@ private:
};
typedef pmap<GeomCollectorKey, GeomCollector> GeomCollectorMap;
class GlyphPlacement : public MemoryBase {
struct QuadDef {
// Copying this class is a performance hotspot, hence we define the
// move constructor.
ALWAYS_INLINE QuadDef() {}
ALWAYS_INLINE QuadDef(const QuadDef &copy) :
_dimensions(copy._dimensions), _uvs(copy._uvs),
_slantl(copy._slantl), _slanth(copy._slanth),
_glyph(copy._glyph) {}
#ifdef USE_MOVE_SEMANTICS
ALWAYS_INLINE QuadDef(QuadDef &&from) NOEXCEPT :
_dimensions(from._dimensions), _uvs(from._uvs),
_slantl(from._slantl), _slanth(from._slanth),
_glyph(move(from._glyph)) {}
#endif
LVecBase4 _dimensions;
LVecBase4 _uvs;
PN_stdfloat _slantl, _slanth;
CPT(TextGlyph) _glyph;
};
typedef pvector<QuadDef> QuadDefs;
typedef pmap<CPT(RenderState), QuadDefs> QuadMap;
void generate_quads(GeomNode *geom_node, const QuadMap &quad_map);
class GlyphPlacement {
public:
INLINE void add_piece(Geom *geom, const RenderState *state);
void calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, Thread *current_thread) const;
void assign_to(GeomNode *geom_node, const RenderState *state) const;
void assign_copy_to(GeomNode *geom_node, const RenderState *state,
const LMatrix4 &extra_xform) const;
void assign_to(GeomNode *geom_node, const RenderState *state,
const LVector2 &offset = LVector2::zero()) const;
void assign_append_to(GeomCollectorMap &geom_collector_map, const RenderState *state,
const LMatrix4 &extra_xform) const;
void copy_graphic_to(PandaNode *node, const RenderState *state,
const LMatrix4 &extra_xform) const;
const LVector2 &offset = LVector2::zero()) const;
void assign_quad_to(QuadMap &quad_map, const RenderState *state,
const LVector2 &offset = LVector2::zero()) const;
void copy_graphic_to(PandaNode *node, const RenderState *state) const;
Pieces _pieces;
CPT(TextGlyph) _glyph;
PT(PandaNode) _graphic_model;
LMatrix4 _xform;
PN_stdfloat _xpos, _ypos;
PN_stdfloat _scale, _slant;
const TextProperties *_properties;
};
typedef pvector<GlyphPlacement *> PlacedGlyphs;
typedef pvector<GlyphPlacement> PlacedGlyphs;
void assemble_paragraph(PlacedGlyphs &placed_glyphs);
void assemble_row(TextRow &row,
@ -281,8 +296,8 @@ private:
static void
get_character_glyphs(int character, const TextProperties *properties,
bool &got_glyph, const TextGlyph *&glyph,
const TextGlyph *&second_glyph,
bool &got_glyph, CPT(TextGlyph) &glyph,
CPT(TextGlyph) &second_glyph,
UnicodeLatinMap::AccentType &accent_type,
int &additional_flags,
PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale);
@ -291,13 +306,13 @@ private:
tack_on_accent(UnicodeLatinMap::AccentType accent_type,
const LPoint3 &min_vert, const LPoint3 &max_vert,
const LPoint3 &centroid,
const TextProperties *properties, GlyphPlacement *placement) const;
const TextProperties *properties, GlyphPlacement &placement) const;
bool
tack_on_accent(char accent_mark, CheesyPosition position,
CheesyTransform transform,
const LPoint3 &min_vert, const LPoint3 &max_vert,
const LPoint3 &centroid,
const TextProperties *properties, GlyphPlacement *placement) const;
const TextProperties *properties, GlyphPlacement &placement) const;
// These are filled in by assemble_paragraph().
LVector2 _ul;

View File

@ -87,9 +87,9 @@ set_space_advance(PN_stdfloat space_advance) {
// it may still return a printable glyph, or it may
// return NULL.
////////////////////////////////////////////////////////////////////
INLINE const TextGlyph *TextFont::
INLINE CPT(TextGlyph) TextFont::
get_glyph(int character) {
const TextGlyph *glyph = NULL;
CPT(TextGlyph) glyph;
get_glyph(character, glyph);
return glyph;
}

View File

@ -78,12 +78,12 @@ PUBLISHED:
INLINE PN_stdfloat get_space_advance() const;
INLINE void set_space_advance(PN_stdfloat space_advance);
INLINE const TextGlyph *get_glyph(int character);
INLINE CPT(TextGlyph) get_glyph(int character);
virtual void write(ostream &out, int indent_level) const;
public:
virtual bool get_glyph(int character, const TextGlyph *&glyph)=0;
virtual bool get_glyph(int character, CPT(TextGlyph) &glyph)=0;
TextGlyph *get_invalid_glyph();
static RenderMode string_render_mode(const string &string);

View File

@ -16,14 +16,15 @@
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::Default constructor
// Access: Public
// Description: This constructor makes an invalid glyph.
// Description: This constructor makes an empty glyph.
////////////////////////////////////////////////////////////////////
INLINE TextGlyph::
TextGlyph(int character) :
_character(character)
TextGlyph(int character, PN_stdfloat advance) :
_character(character),
_geom((Geom *)NULL),
_advance(advance),
_has_quad(false)
{
_geom = (Geom *)NULL;
_advance = 0.0f;
}
////////////////////////////////////////////////////////////////////
@ -37,8 +38,12 @@ TextGlyph(int character, const Geom *geom,
_character(character),
_geom(geom),
_state(state),
_advance(advance)
{
_advance(advance),
_has_quad(false)
{
if (geom != NULL) {
check_quad_geom();
}
if (_state == (RenderState *)NULL) {
_state = RenderState::make_empty();
}
@ -54,8 +59,11 @@ TextGlyph(const TextGlyph &copy) :
_character(copy._character),
_geom(copy._geom),
_state(copy._state),
_advance(copy._advance)
{
_advance(copy._advance),
_has_quad(copy._has_quad),
_quad_dimensions(copy._quad_dimensions),
_quad_texcoords(copy._quad_texcoords)
{
}
////////////////////////////////////////////////////////////////////
@ -69,6 +77,9 @@ operator = (const TextGlyph &copy) {
_geom = copy._geom;
_state = copy._state;
_advance = copy._advance;
_has_quad = copy._has_quad;
_quad_dimensions = copy._quad_dimensions;
_quad_texcoords = copy._quad_texcoords;
}
////////////////////////////////////////////////////////////////////
@ -83,28 +94,40 @@ get_character() const {
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::get_geom
// Function: TextGlyph::has_quad
// Access: Public
// Description: Returns a Geom that renders the particular glyph.
// Description: Returns true if this glyph contains the definition
// for a simple quad, rather than a more complex piece
// of geometry.
//
// You may still call get_geom() even if this returns
// true, which will synthesize a Geom for this quad.
////////////////////////////////////////////////////////////////////
INLINE PT(Geom) TextGlyph::
get_geom(Geom::UsageHint usage_hint) const {
if (_geom == (Geom *)NULL) {
return NULL;
INLINE bool TextGlyph::
has_quad() const {
return _has_quad;
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::get_quad
// Access: Public
// Description: Assuming that this glyph is representable as a
// textured quad, returns its dimensions and UV range.
// Returns false if it is not representable as a quad,
// or if it is whitespace.
//
// The order of the components is left, bottom,
// right, top.
////////////////////////////////////////////////////////////////////
INLINE bool TextGlyph::
get_quad(LVecBase4 &dimensions, LVecBase4 &texcoords) const {
if (!_has_quad) {
return false;
}
// We always return a copy of the geom. That will allow the caller
// to modify its vertices without fear of stomping on other copies;
// it is also critical for the DynamicTextGlyph, which depends on
// this behavior to properly count references to this glyph.
PT(Geom) new_geom = _geom->make_copy();
new_geom->set_usage_hint(usage_hint);
const GeomVertexData *vdata = new_geom->get_vertex_data();
nassertr(vdata != NULL, new_geom);
if (vdata->get_usage_hint() != usage_hint) {
new_geom->modify_vertex_data()->set_usage_hint(usage_hint);
}
return new_geom;
dimensions = _quad_dimensions;
texcoords = _quad_texcoords;
return true;
}
////////////////////////////////////////////////////////////////////

View File

@ -27,7 +27,7 @@ TextGlyph::
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::is_whitespace
// Access: Public, Virtual
// Access: Published, Virtual
// Description: Returns true if this glyph represents invisible
// whitespace, or false if it corresponds to some
// visible character.
@ -38,3 +38,276 @@ is_whitespace() const {
// all glyphs are non-whitespace.
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::get_geom
// Access: Published
// Description: Returns a Geom that renders the particular glyph.
// It will be generated if necessary.
//
// This method will always return a copy of the Geom,
// so the caller is free to modify it.
////////////////////////////////////////////////////////////////////
PT(Geom) TextGlyph::
get_geom(Geom::UsageHint usage_hint) const {
if (_geom.is_null()) {
// Maybe we have yet to generate a suitable Geom.
if (_has_quad) {
((TextGlyph *)this)->make_quad_geom();
if (_geom.is_null()) {
return (Geom *)NULL;
}
} else {
// Nope.
return (Geom *)NULL;
}
}
// We always return a copy of the geom. That will allow the caller
// to modify its vertices without fear of stomping on other copies.
// It is also important that we store a reference to this glyph on
// the Geom, since the DynamicTextFont relies on counting references
// to determine whether a glyph is no longer used.
PT(Geom) new_geom = new GeomTextGlyph(*_geom, this);
new_geom->set_usage_hint(usage_hint);
const GeomVertexData *vdata = new_geom->get_vertex_data();
nassertr(vdata != NULL, new_geom);
if (vdata->get_usage_hint() != usage_hint) {
new_geom->modify_vertex_data()->set_usage_hint(usage_hint);
}
return new_geom;
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::calc_tight_bounds
// Access: Public
// Description: Expands min_point and max_point to include all of the
// vertices in the glyph, if any. found_any is set
// true if any points are found. It is the caller's
// responsibility to initialize min_point, max_point,
// and found_any before calling this function.
////////////////////////////////////////////////////////////////////
void TextGlyph::
calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, Thread *current_thread) const {
if (_has_quad) {
found_any = true;
min_point.set(_quad_dimensions[0], 0, _quad_dimensions[1]);
max_point.set(_quad_dimensions[2], 0, _quad_dimensions[3]);
} else if (!_geom.is_null()) {
_geom->calc_tight_bounds(min_point, max_point, found_any, current_thread);
}
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::set_quad
// Access: Public
// Description: Sets the glyph using the given quad parameters.
// Any Geom assigned will be cleared.
// The order of the components is left, bottom,
// right, top.
////////////////////////////////////////////////////////////////////
void TextGlyph::
set_quad(const LVecBase4 &dimensions, const LVecBase4 &texcoords,
const RenderState *state) {
_geom.clear();
_quad_dimensions = dimensions;
_quad_texcoords = texcoords;
_has_quad = true;
_state = state;
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::set_geom
// Access: Public
// Description: Sets the geom from a pre-built Geom object.
// Any quad parameters assigned will be cleared.
////////////////////////////////////////////////////////////////////
void TextGlyph::
set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state) {
PT(Geom) geom = new Geom(vdata);
geom->add_primitive(prim);
_geom = geom;
_has_quad = false;
_state = state;
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::check_quad_geom
// Access: Private
// Description: Checks if the geom that was passed in is actually
// a quad, and if so, sets the appropriate quad
// parameters.
//
// This is useful when loading static text fonts, so
// that they can still benefit from the fast text
// assembly that is done for quads.
////////////////////////////////////////////////////////////////////
void TextGlyph::
check_quad_geom() {
// Currently it looks for rather specific signs that this glyph
// has been generated using egg-mkfont. For now, this is fine.
CPT(GeomVertexData) vdata = _geom->get_vertex_data();
if (vdata->get_num_rows() != 4) {
return;
}
// Does it appear to have a single primitive with two triangles?
if (_geom->get_num_primitives() != 1) {
return;
}
CPT(GeomPrimitive) prim = _geom->get_primitive(0);
if (!prim->is_indexed() ||
prim->get_primitive_type() != GeomPrimitive::PT_polygons) {
return;
}
if (!prim->is_of_type(GeomTriangles::get_class_type())) {
prim = prim->decompose();
}
if (prim->get_num_vertices() != 6) {
return;
}
// Check that it has a vertex column and optionally a texcoord column.
CPT(GeomVertexFormat) format = vdata->get_format();
if (format->get_num_columns() > 2 ||
!format->has_column(InternalName::get_vertex())) {
return;
}
if (format->has_column(InternalName::get_texcoord()) != (format->get_num_columns() == 2)) {
// The second column, if there is one, is not a texcoord.
return;
}
// Check that the vertices are arranged in a square.
GeomVertexReader vertex(vdata, InternalName::get_vertex());
LVecBase3 v = vertex.get_data3();
if (!IS_NEARLY_ZERO(v[1])) {
return;
}
PN_stdfloat minx = v[0];
PN_stdfloat maxx = v[0];
PN_stdfloat miny = v[2];
PN_stdfloat maxy = v[2];
for (int i = 0; i < 3; ++i) {
v = vertex.get_data3();
if (!IS_NEARLY_ZERO(v[1])) {
return;
}
if (!IS_NEARLY_EQUAL(v[0], minx) && !IS_NEARLY_EQUAL(v[0], maxx)) {
if (!IS_NEARLY_EQUAL(minx, maxx)) {
return;
}
minx = min(v[0], minx);
maxx = max(v[0], maxx);
}
if (!IS_NEARLY_EQUAL(v[2], miny) && !IS_NEARLY_EQUAL(v[2], maxy)) {
if (!IS_NEARLY_EQUAL(miny, maxy)) {
return;
}
miny = min(v[2], miny);
maxy = max(v[2], maxy);
}
}
PN_stdfloat minu = 0;
PN_stdfloat maxu = 0;
PN_stdfloat minv = 0;
PN_stdfloat maxv = 0;
// Same for the texcoord data.
if (format->has_column(InternalName::get_texcoord())) {
GeomVertexReader texcoord(vdata, InternalName::get_texcoord());
LVecBase2 tc = texcoord.get_data2();
minu = tc[0];
maxu = tc[0];
minv = tc[1];
maxv = tc[1];
for (int i = 0; i < 3; ++i) {
tc = texcoord.get_data2();
if (!IS_NEARLY_EQUAL(tc[0], minu) && !IS_NEARLY_EQUAL(tc[0], maxu)) {
if (!IS_NEARLY_EQUAL(minu, maxu)) {
return;
}
minu = min(tc[0], minu);
maxu = max(tc[0], maxu);
}
if (!IS_NEARLY_EQUAL(tc[1], minv) && !IS_NEARLY_EQUAL(tc[1], maxv)) {
if (!IS_NEARLY_EQUAL(minv, maxv)) {
return;
}
minv = min(tc[1], minv);
maxv = max(tc[1], maxv);
}
}
}
_quad_dimensions.set(minx, miny, maxx, maxy);
_quad_texcoords.set(minu, minv, maxu, maxv);
_has_quad = true;
}
////////////////////////////////////////////////////////////////////
// Function: TextGlyph::make_quad_geom
// Access: Private
// Description: Generates a Geom representing this text glyph, if
// at all possible.
////////////////////////////////////////////////////////////////////
void TextGlyph::
make_quad_geom() {
// The default implementation is to generate a Geom based on the
// get_quad() implementation, if any.
LVecBase4 dimensions, uvs;
if (!get_quad(dimensions, uvs)) {
return;
}
// Create a corresponding triangle pair. We use a pair of indexed
// triangles rather than a single triangle strip, to avoid the bad
// vertex duplication behavior with lots of two-triangle strips.
PT(GeomVertexData) vdata = new GeomVertexData
(string(), GeomVertexFormat::get_v3t2(), Geom::UH_static);
vdata->unclean_set_num_rows(4);
PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
{
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
PT(GeomVertexArrayData) indices = tris->modify_vertices();
indices->unclean_set_num_rows(6);
GeomVertexWriter index(indices, 0);
vertex.set_data3(dimensions[0], 0, dimensions[3]);
vertex.set_data3(dimensions[0], 0, dimensions[1]);
vertex.set_data3(dimensions[2], 0, dimensions[3]);
vertex.set_data3(dimensions[2], 0, dimensions[1]);
texcoord.set_data2(uvs[0], uvs[3]);
texcoord.set_data2(uvs[0], uvs[1]);
texcoord.set_data2(uvs[2], uvs[3]);
texcoord.set_data2(uvs[2], uvs[1]);
index.set_data1i(0);
index.set_data1i(1);
index.set_data1i(2);
index.set_data1i(2);
index.set_data1i(1);
index.set_data1i(3);
}
// We create a regular Geom here, not a GeomTextGlyph, since doing so
// would create a circular reference. When the get_geom method makes
// a copy, it will add in a pointer to this text glyph.
PT(Geom) geom = new Geom(vdata);
geom->add_primitive(tris);
_geom = geom;
}

View File

@ -30,7 +30,7 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_TEXT TextGlyph : public TypedReferenceCount {
public:
INLINE TextGlyph(int character);
INLINE TextGlyph(int character, PN_stdfloat advance=0);
INLINE TextGlyph(int character, const Geom *geom,
const RenderState *state, PN_stdfloat advance);
INLINE TextGlyph(const TextGlyph &copy);
@ -39,18 +39,42 @@ public:
PUBLISHED:
INLINE int get_character() const;
INLINE PT(Geom) get_geom(Geom::UsageHint usage_hint) const;
INLINE bool has_quad() const;
INLINE bool get_quad(LVecBase4 &dimensions, LVecBase4 &texcoords) const;
INLINE const RenderState *get_state() const;
INLINE PN_stdfloat get_advance() const;
virtual bool is_whitespace() const;
PT(Geom) get_geom(Geom::UsageHint usage_hint) const;
public:
void calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
bool &found_any, Thread *current_thread) const;
void set_quad(const LVecBase4 &dimensions, const LVecBase4 &texcoords,
const RenderState *state);
void set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state);
private:
void check_quad_geom();
void make_quad_geom();
protected:
int _character;
CPT(Geom) _geom;
CPT(RenderState) _state;
PN_stdfloat _advance;
bool _has_quad;
// top, left, bottom, right
LVecBase4 _quad_dimensions;
LVecBase4 _quad_texcoords;
friend class GeomTextGlyph;
public:
static TypeHandle get_class_type() {
return _type_handle;