From 9292457ce55ff05498809fd0fd8678b11b64c725 Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 9 Feb 2002 21:56:24 +0000 Subject: [PATCH] initial pass at dynamic text --- panda/src/text/Sources.pp | 15 +- panda/src/text/config_text.cxx | 4 + panda/src/text/dynamicTextFont.I | 76 ++++++++ panda/src/text/dynamicTextFont.cxx | 284 ++++++++++++++++++++++++++++ panda/src/text/dynamicTextFont.h | 111 +++++++++++ panda/src/text/dynamicTextGlyph.I | 54 ++++++ panda/src/text/dynamicTextGlyph.cxx | 116 ++++++++++++ panda/src/text/dynamicTextGlyph.h | 57 ++++++ panda/src/text/dynamicTextPage.I | 50 +++++ panda/src/text/dynamicTextPage.cxx | 140 ++++++++++++++ panda/src/text/dynamicTextPage.h | 85 +++++++++ panda/src/text/staticTextFont.cxx | 17 +- panda/src/text/staticTextFont.h | 16 +- panda/src/text/textFont.I | 11 ++ panda/src/text/textFont.cxx | 7 +- panda/src/text/textFont.h | 10 +- panda/src/text/textGlyph.I | 2 + panda/src/text/textGlyph.h | 10 +- panda/src/text/textNode.h | 24 ++- panda/src/text/text_composite1.cxx | 3 + 20 files changed, 1054 insertions(+), 38 deletions(-) create mode 100644 panda/src/text/dynamicTextFont.I create mode 100644 panda/src/text/dynamicTextFont.cxx create mode 100644 panda/src/text/dynamicTextFont.h create mode 100644 panda/src/text/dynamicTextGlyph.I create mode 100644 panda/src/text/dynamicTextGlyph.cxx create mode 100644 panda/src/text/dynamicTextGlyph.h create mode 100644 panda/src/text/dynamicTextPage.I create mode 100644 panda/src/text/dynamicTextPage.cxx create mode 100644 panda/src/text/dynamicTextPage.h diff --git a/panda/src/text/Sources.pp b/panda/src/text/Sources.pp index e0ad683247..b8b3c56b19 100644 --- a/panda/src/text/Sources.pp +++ b/panda/src/text/Sources.pp @@ -2,6 +2,8 @@ dtoolutil:c dtoolbase:c dtool:m #begin lib_target + #define USE_FREETYPE yes + #define TARGET text #define LOCAL_LIBS \ cull putil gobj sgattrib graph sgraph linmath sgraphutil pnmimage gsgbase \ @@ -11,16 +13,27 @@ #define SOURCES \ config_text.h \ + dynamicTextFont.I dynamicTextFont.h \ + dynamicTextGlyph.I dynamicTextGlyph.h \ + dynamicTextPage.I dynamicTextPage.h \ staticTextFont.I staticTextFont.h \ textFont.I textFont.h \ textGlyph.I textGlyph.h \ textNode.I textNode.h textNode.cxx #define INCLUDED_SOURCES \ - config_text.cxx staticTextFont.cxx textFont.cxx textGlyph.cxx + config_text.cxx \ + dynamicTextFont.cxx \ + dynamicTextGlyph.cxx \ + dynamicTextPage.cxx \ + staticTextFont.cxx \ + textFont.cxx textGlyph.cxx #define INSTALL_HEADERS \ config_text.h \ + dynamicTextFont.I dynamicTextFont.h \ + dynamicTextGlyph.I dynamicTextGlyph.h \ + dynamicTextPage.I dynamicTextPage.h \ staticTextFont.I staticTextFont.h \ textFont.I textFont.h \ textGlyph.I textGlyph.h \ diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 69c8813e24..e50a01a20c 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -18,6 +18,8 @@ #include "config_text.h" #include "staticTextFont.h" +#include "dynamicTextFont.h" +#include "dynamicTextPage.h" #include "textFont.h" #include "textNode.h" @@ -28,6 +30,8 @@ NotifyCategoryDef(text, ""); ConfigureFn(config_text) { StaticTextFont::init_type(); + DynamicTextFont::init_type(); + DynamicTextPage::init_type(); TextFont::init_type(); TextNode::init_type(); } diff --git a/panda/src/text/dynamicTextFont.I b/panda/src/text/dynamicTextFont.I new file mode 100644 index 0000000000..70e8374e64 --- /dev/null +++ b/panda/src/text/dynamicTextFont.I @@ -0,0 +1,76 @@ +// Filename: dynamicTextFont.I +// Created by: drose (08Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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: DynamicTextFont::set_margin +// Access: Published +// Description: Sets the number of pixels of padding that is added +// around the border of each glyph before adding it to +// the texture map. This reduces the bleed in from +// neighboring glyphs in the texture map. +//////////////////////////////////////////////////////////////////// +INLINE void DynamicTextFont:: +set_margin(int margin) { + _margin = margin; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_margin +// Access: Published +// Description: Returns the number of pixels of padding that is added +// around the border of each glyph. See set_margin(). +//////////////////////////////////////////////////////////////////// +INLINE int DynamicTextFont:: +get_margin() const { + return _margin; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::set_page_size +// Access: Published +// Description: Sets the x, y size of the textures that are created +// for the DynamicTextFont. +//////////////////////////////////////////////////////////////////// +INLINE void DynamicTextFont:: +set_page_size(int x_size, int y_size) { + _page_x_size = x_size; + _page_y_size = y_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_page_x_size +// Access: Published +// Description: Returns the x size of the textures that are created +// for the DynamicTextFont. See set_page_size(). +//////////////////////////////////////////////////////////////////// +INLINE int DynamicTextFont:: +get_page_x_size() const { + return _page_x_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_page_y_size +// Access: Published +// Description: Returns the y size of the textures that are created +// for the DynamicTextFont. See set_page_size(). +//////////////////////////////////////////////////////////////////// +INLINE int DynamicTextFont:: +get_page_y_size() const { + return _page_y_size; +} diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx new file mode 100644 index 0000000000..2042cb04f6 --- /dev/null +++ b/panda/src/text/dynamicTextFont.cxx @@ -0,0 +1,284 @@ +// Filename: dynamicTextFont.cxx +// Created by: drose (08Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "dynamicTextFont.h" + +#ifdef HAVE_FREETYPE + +#include "config_text.h" +#include "config_util.h" + +FT_Library DynamicTextFont::_ft_library; +bool DynamicTextFont::_ft_initialized = false; +bool DynamicTextFont::_ft_ok = false; + +TypeHandle DynamicTextFont::_type_handle; + + +// This constant determines how big a particular point size font +// appears. By convention, 10 points is 1 foot high. +static const float points_per_unit = 10.0f; + +// A universal convention. +static const float points_per_inch = 72.0f; + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::Constructor +// Access: Published +// Description: The constructor expects the name of some font file +// that FreeType can read, along with face_index, +// indicating which font within the file to load +// (usually 0), the point size of the font, and the +// resolution at which to generate the font. +// +// The choice of point size affects the apparent size of +// the generated characters (as well as the clarity), +// while the pixels_per_unit affects only the clarity. +//////////////////////////////////////////////////////////////////// +DynamicTextFont:: +DynamicTextFont(const Filename &font_filename, int face_index, + float point_size, float pixels_per_unit) { + _margin = 2; + _page_x_size = 256; + _page_y_size = 256; + _pixels_per_unit = pixels_per_unit; + + float units_per_inch = (points_per_inch / points_per_unit); + int dpi = (int)(_pixels_per_unit * units_per_inch); + + if (!_ft_initialized) { + initialize_ft_library(); + } + if (!_ft_ok) { + text_cat.error() + << "Unable to read font " << font_filename << ": FreeType library not available.\n"; + return; + } + + Filename path(font_filename); + if (!path.resolve_filename(get_model_path())) { + text_cat.error() + << "Unable to find font file " << font_filename << "\n"; + } else { + string os_specific = path.to_os_specific(); + + int error = FT_New_Face(_ft_library, + os_specific.c_str(), + face_index, + &_face); + if (error == FT_Err_Unknown_File_Format) { + text_cat.error() + << "Unable to read font " << font_filename << ": unknown file format.\n"; + } else if (error) { + text_cat.error() + << "Unable to read font " << font_filename << ": invalid.\n"; + + } else { + string name = _face->family_name; + name += " "; + name += _face->style_name; + + _is_valid = true; + set_name(name); + + text_cat.info() + << "Loaded font " << get_name() << "\n"; + + error = FT_Set_Char_Size(_face, + (int)(point_size * 64), (int)(point_size * 64), + dpi, dpi); + if (error) { + text_cat.warning() + << "Unable to set point size of " << get_name() + << " to " << point_size << " at " << dpi << " dots per inch.\n"; + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_num_pages +// Access: Published +// Description: Returns the number of pages associated with the font. +// Initially, the font has zero pages; when the first +// piece of text is rendered with the font, it will add +// additional pages as needed. Each page is a Texture +// object that contains the images for each of the +// glyphs currently in use somewhere. +//////////////////////////////////////////////////////////////////// +int DynamicTextFont:: +get_num_pages() const { + return _pages.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_page +// Access: Published +// Description: Returns the nth page associated with the font. +// Initially, the font has zero pages; when the first +// piece of text is rendered with the font, it will add +// additional pages as needed. Each page is a Texture +// object that contains the images for each of the +// glyphs currently in use somewhere. +//////////////////////////////////////////////////////////////////// +DynamicTextPage *DynamicTextFont:: +get_page(int n) const { + nassertr(n >= 0 && n < (int)_pages.size(), (DynamicTextPage *)NULL); + return _pages[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::write +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DynamicTextFont:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << "DynamicTextFont " << get_name() << ".\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. +//////////////////////////////////////////////////////////////////// +const TextGlyph *DynamicTextFont:: +get_glyph(int character) { + Cache::iterator ci = _cache.find(character); + if (ci != _cache.end()) { + return (*ci).second; + } + DynamicTextGlyph *glyph = make_glyph(character); + _cache.insert(Cache::value_type(character, glyph)); + return glyph; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::make_glyph +// Access: Private +// Description: Slots a space in the texture map for the new +// character and renders the glyph, returning the +// newly-created TextGlyph object, or NULL if the +// glyph cannot be created for some reason. +//////////////////////////////////////////////////////////////////// +DynamicTextGlyph *DynamicTextFont:: +make_glyph(int character) { + int error = FT_Load_Char(_face, character, FT_LOAD_RENDER); + if (error) { + text_cat.error() + << "Unable to render character " << character << "\n"; + return (DynamicTextGlyph *)NULL; + } + + FT_GlyphSlot slot = _face->glyph; + FT_Bitmap &bitmap = slot->bitmap; + + if (bitmap.pixel_mode != ft_pixel_mode_grays) { + text_cat.error() + << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; + return (DynamicTextGlyph *)NULL; + } + + if (bitmap.num_grays != 256) { + // We expect 256 levels of grayscale to come back from FreeType, + // since that's what we asked for. + text_cat.warning() + << "Expected 256 levels of gray, got " << bitmap.num_grays << "\n"; + } + + DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows); + + // Now copy the rendered glyph into the texture. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + unsigned char *texture_row = glyph->get_row(yi); + nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL); + memcpy(texture_row, buffer_row, bitmap.width); + buffer_row += bitmap.pitch; + } + glyph->_page->mark_dirty(Texture::DF_image); + + float advance = slot->advance.x / 64.0; + glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance, + _pixels_per_unit); + return glyph; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::slot_glyph +// Access: Private +// Description: Chooses a page that will have room for a glyph of the +// indicated size (after expanding the indicated size by +// the current margin). Returns the newly-allocated +// glyph on the chosen page; the glyph has not been +// filled in yet except with its size. +//////////////////////////////////////////////////////////////////// +DynamicTextGlyph *DynamicTextFont:: +slot_glyph(int x_size, int y_size) { + // Increase the indicated size by the current margin. + x_size += _margin * 2; + y_size += _margin * 2; + + Pages::iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + DynamicTextPage *page = (*pi); + + DynamicTextGlyph *glyph = page->slot_glyph(x_size, y_size, _margin); + if (glyph != (DynamicTextGlyph *)NULL) { + return glyph; + } + + if (page->is_empty()) { + // If we couldn't even put in on an empty page, we're screwed. + text_cat.error() + << "Glyph of size " << x_size << " by " << y_size + << " won't fit on an empty page.\n"; + return (DynamicTextGlyph *)NULL; + } + } + + // We need to make a new page. + PT(DynamicTextPage) page = new DynamicTextPage(this); + _pages.push_back(page); + return page->slot_glyph(x_size, y_size, _margin); +} + + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::initialize_ft_library +// Access: Private, Static +// Description: Should be called exactly once to initialize the +// FreeType library. +//////////////////////////////////////////////////////////////////// +void DynamicTextFont:: +initialize_ft_library() { + if (!_ft_initialized) { + int error = FT_Init_FreeType(&_ft_library); + _ft_initialized = true; + if (error) { + text_cat.error() + << "Unable to initialize FreeType; DynamicTextFonts will not load.\n"; + } else { + _ft_ok = true; + } + } +} + +#endif // HAVE_FREETYPE diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h new file mode 100644 index 0000000000..b8f440cd9c --- /dev/null +++ b/panda/src/text/dynamicTextFont.h @@ -0,0 +1,111 @@ +// Filename: dynamicTextFont.h +// Created by: drose (08Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 DYNAMICTEXTFONT_H +#define DYNAMICTEXTFONT_H + +#include "pandabase.h" + +#ifdef HAVE_FREETYPE + +#include "config_text.h" +#include "textFont.h" +#include "dynamicTextGlyph.h" +#include "dynamicTextPage.h" +#include "filename.h" +#include "pvector.h" +#include "pmap.h" + +#include +#include FT_FREETYPE_H + +//////////////////////////////////////////////////////////////////// +// Class : DynamicTextFont +// Description : A DynamicTextFont is a special TextFont object that +// rasterizes its glyphs from a standard font file +// (e.g. a TTF file) on the fly. It requires the +// FreeType 2.0 library (or any higher, +// backward-compatible version). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DynamicTextFont : public TextFont { +PUBLISHED: + DynamicTextFont(const Filename &font_filename, int face_index, + float point_size, float pixels_per_unit); + + INLINE void set_margin(int margin); + INLINE int get_margin() const; + + INLINE void set_page_size(int x_size, int y_size); + INLINE int get_page_x_size() const; + INLINE int get_page_y_size() const; + + int get_num_pages() const; + DynamicTextPage *get_page(int n) const; + + virtual void write(ostream &out, int indent_level) const; + +public: + virtual const TextGlyph *get_glyph(int character); + +private: + DynamicTextGlyph *make_glyph(int character); + DynamicTextGlyph *slot_glyph(int x_size, int y_size); + + static void initialize_ft_library(); + + int _margin; + int _page_x_size, _page_y_size; + float _pixels_per_unit; + + typedef pvector< PT(DynamicTextPage) > Pages; + Pages _pages; + + typedef pmap Cache; + Cache _cache; + + FT_Face _face; + + static FT_Library _ft_library; + static bool _ft_initialized; + static bool _ft_ok; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TextFont::init_type(); + register_type(_type_handle, "DynamicTextFont", + TextFont::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; + + friend class TextNode; +}; + +#include "dynamicTextFont.I" + +#endif // HAVE_FREETYPE + +#endif diff --git a/panda/src/text/dynamicTextGlyph.I b/panda/src/text/dynamicTextGlyph.I new file mode 100644 index 0000000000..fd75c4787b --- /dev/null +++ b/panda/src/text/dynamicTextGlyph.I @@ -0,0 +1,54 @@ +// Filename: dynamicTextGlyph.I +// Created by: drose (09Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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: DynamicTextGlyph::Constructor +// Access: Publiic +// Description: +//////////////////////////////////////////////////////////////////// +INLINE DynamicTextGlyph:: +DynamicTextGlyph(DynamicTextPage *page, int x, int y, + int x_size, int y_size, int margin) : + _page(page), + _x(x), _y(y), + _x_size(x_size), _y_size(y_size), + _margin(margin) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::intersects +// Access: Public +// Description: Returns true if the particular position this glyph +// has been assigned to overlaps the rectangle whose +// top left corner is at x, y and whose size is given by +// x_size, y_size, or false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool DynamicTextGlyph:: +intersects(int x, int y, int x_size, int y_size) const { + int hright = x + x_size; + int hbot = y + y_size; + + int mright = _x + _x_size; + int mbot = _y + _y_size; + + return !(x >= mright || hright <= _x || + y >= mbot || hbot <= _y); +} diff --git a/panda/src/text/dynamicTextGlyph.cxx b/panda/src/text/dynamicTextGlyph.cxx new file mode 100644 index 0000000000..5f0d150df4 --- /dev/null +++ b/panda/src/text/dynamicTextGlyph.cxx @@ -0,0 +1,116 @@ +// Filename: dynamicTextGlyph.cxx +// Created by: drose (09Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "dynamicTextGlyph.h" + +#ifdef HAVE_FREETYPE + +#include "dynamicTextPage.h" +#include "geomTristrip.h" +#include "textureTransition.h" +#include "transparencyTransition.h" + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::get_row +// Access: Publiic +// Description: Returns a pointer to the first byte in the pixel +// buffer associated with the leftmost pixel in the +// indicated row, where 0 is the topmost row and _y_size +// - _margin * 2 - 1 is the bottommost row. +//////////////////////////////////////////////////////////////////// +unsigned char *DynamicTextGlyph:: +get_row(int y) { + nassertr(y >= 0 && y < _y_size - _margin * 2, (unsigned char *)NULL); + nassertr(_page != (DynamicTextPage *)NULL, (unsigned char *)NULL); + nassertr(_page->_pbuffer != (PixelBuffer *)NULL, (unsigned char *)NULL); + + // First, offset y by the glyph's start. + y += _y + _margin; + // Also, get the x start. + int x = _x + _margin; + + // Invert y. + y = _page->_pbuffer->get_ysize() - 1 - y; + + int offset = (y * _page->_pbuffer->get_xsize()) + x; + return _page->_pbuffer->_image + offset; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextGlyph::make_geom +// Access: Publiic +// 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, + float advance, float pixels_per_unit) { + // Determine the corners of the rectangle in geometric units. + float top = (bitmap_top + _margin) / pixels_per_unit; + float left = (bitmap_left - _margin) / pixels_per_unit; + float bottom = (bitmap_top - _y_size - _margin) / pixels_per_unit; + float right = (bitmap_left + _x_size + _margin) / pixels_per_unit; + + // And the corresponding corners in UV units. + float uv_top = 1.0f - (float)_y / _page->get_y_size(); + float uv_left = (float)_x / _page->get_x_size(); + float uv_bottom = 1.0f - (float)(_y + _y_size) / _page->get_y_size(); + float uv_right = (float)(_x + _x_size) / _page->get_x_size(); + + // Create a corresponding tristrip. + _geom = new GeomTristrip; + + PTA_Vertexf coords; + coords.push_back(Vertexf(left, 0, top)); + coords.push_back(Vertexf(left, 0, bottom)); + coords.push_back(Vertexf(right, 0, top)); + coords.push_back(Vertexf(right, 0, bottom)); + + PTA_TexCoordf texcoords; + texcoords.push_back(TexCoordf(uv_left, uv_top)); + texcoords.push_back(TexCoordf(uv_left, uv_bottom)); + texcoords.push_back(TexCoordf(uv_right, uv_top)); + texcoords.push_back(TexCoordf(uv_right, uv_bottom)); + + PTA_Colorf colors; + colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); + + PTA_int lengths; + lengths.push_back(4); + + _geom->set_coords(coords); + _geom->set_texcoords(texcoords, G_PER_VERTEX); + _geom->set_colors(colors, G_OVERALL); + _geom->set_lengths(lengths); + _geom->set_num_prims(1); + + TextureTransition *tex = new TextureTransition(_page); + TransparencyTransition *trans = new TransparencyTransition(TransparencyProperty::M_alpha); + + _trans.set_transition(tex); + _trans.set_transition(trans); + + _advance = advance / pixels_per_unit; +} + + +#endif // HAVE_FREETYPE diff --git a/panda/src/text/dynamicTextGlyph.h b/panda/src/text/dynamicTextGlyph.h new file mode 100644 index 0000000000..b22e634fe0 --- /dev/null +++ b/panda/src/text/dynamicTextGlyph.h @@ -0,0 +1,57 @@ +// Filename: dynamicTextGlyph.h +// Created by: drose (09Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 DYNAMICTEXTGLYPH_H +#define DYNAMICTEXTGLYPH_H + +#include "pandabase.h" + +#ifdef HAVE_FREETYPE + +#include "textGlyph.h" + +class DynamicTextPage; + +//////////////////////////////////////////////////////////////////// +// Class : DynamicTextGlyph +// Description : A specialization on TextGlyph that is generated and +// stored by a DynamicTextFont. This keeps some +// additional information, such as where the glyph +// appears on a texture map. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DynamicTextGlyph : public TextGlyph { +public: + INLINE DynamicTextGlyph(DynamicTextPage *page, int x, int y, + int x_size, int y_size, int margin); + + INLINE bool intersects(int x, int y, int x_size, int y_size) const; + unsigned char *get_row(int y); + void make_geom(int top, int left, float advance, float pixels_per_unit); + + DynamicTextPage *_page; + + int _x, _y; + int _x_size, _y_size; + int _margin; +}; + +#include "dynamicTextGlyph.I" + +#endif // HAVE_FREETYPE + +#endif diff --git a/panda/src/text/dynamicTextPage.I b/panda/src/text/dynamicTextPage.I new file mode 100644 index 0000000000..af1d163dbc --- /dev/null +++ b/panda/src/text/dynamicTextPage.I @@ -0,0 +1,50 @@ +// Filename: dynamicTextPage.I +// Created by: drose (09Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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: DynamicTextPage::get_x_size +// Access: Published +// Description: Returns the x size of the page (texture), in pixels. +//////////////////////////////////////////////////////////////////// +INLINE int DynamicTextPage:: +get_x_size() const { + return _x_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::get_y_size +// Access: Published +// Description: Returns the y size of the page (texture), in pixels. +//////////////////////////////////////////////////////////////////// +INLINE int DynamicTextPage:: +get_y_size() const { + return _y_size; +} + + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::is_empty +// Access: Published +// Description: Returns true if the page has no glyphs, false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool DynamicTextPage:: +is_empty() const { + return _glyphs.empty(); +} diff --git a/panda/src/text/dynamicTextPage.cxx b/panda/src/text/dynamicTextPage.cxx new file mode 100644 index 0000000000..9a741984ff --- /dev/null +++ b/panda/src/text/dynamicTextPage.cxx @@ -0,0 +1,140 @@ +// Filename: dynamicTextPage.cxx +// Created by: drose (09Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "dynamicTextPage.h" +#include "dynamicTextFont.h" + +#ifdef HAVE_FREETYPE + + +TypeHandle DynamicTextPage::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::Constructor +// Access: Publiic +// Description: +//////////////////////////////////////////////////////////////////// +DynamicTextPage:: +DynamicTextPage(DynamicTextFont *font) : + _font(font) +{ + _x_size = _font->get_page_x_size(); + _y_size = _font->get_page_y_size(); + + // Initialize the Texture to an empty, black (transparent) image of + // the appropriate size. + _pbuffer = new PixelBuffer(_x_size, _y_size, 1, 1, + PixelBuffer::T_unsigned_byte, + PixelBuffer::F_alpha); + mark_dirty(DF_image); + + // 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); +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::slot_glyph +// Access: Publiic +// Description: Finds space within the page for a glyph of the +// indicated size. If space is found, creates a new +// glyph object and returns it; otherwise, returns NULL. +//////////////////////////////////////////////////////////////////// +DynamicTextGlyph *DynamicTextPage:: +slot_glyph(int x_size, int y_size, int margin) { + int x, y; + if (!find_hole(x, y, x_size, y_size)) { + // No room for the glyph. + return (DynamicTextGlyph *)NULL; + } + + // The glyph can be fit at (x, y). Slot it. + PT(DynamicTextGlyph) glyph = + new DynamicTextGlyph(this, x, y, x_size, y_size, margin); + _glyphs.push_back(glyph); + return glyph; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::find_hole +// Access: Private +// Description: Searches for a hole of at least x_size by y_size +// pixels somewhere within the page. If a suitable hole +// is found, sets x and y to the top left corner and +// returns true; otherwise, returns false. +//////////////////////////////////////////////////////////////////// +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; + // Scan along the row at 'y'. + x = 0; + while (x + x_size <= _x_size) { + int next_x = x; + + // Consider the spot at x, y. + DynamicTextGlyph *overlap = find_overlap(x, y, x_size, y_size); + + if (overlap == (DynamicTextGlyph *)NULL) { + // Hooray! + return true; + } + + next_x = overlap->_x + overlap->_x_size; + next_y = min(next_y, overlap->_y + overlap->_y_size); + nassertr(next_x > x, false); + x = next_x; + } + + nassertr(next_y > y, false); + y = next_y; + } + + // Nope, wouldn't fit anywhere. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextPage::find_overlap +// Access: Private +// Description: If the rectangle whose top left corner is x, y and +// whose size is x_size, y_size describes an empty hole +// that does not overlap any placed glyphs, returns +// NULL; otherwise, returns the first placed glyph +// that the image does overlap. It is assumed the +// rectangle lies completely within the boundaries of +// the page itself. +//////////////////////////////////////////////////////////////////// +DynamicTextGlyph *DynamicTextPage:: +find_overlap(int x, int y, int x_size, int y_size) const { + Glyphs::const_iterator gi; + for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) { + DynamicTextGlyph *glyph = (*gi); + if (glyph->intersects(x, y, x_size, y_size)) { + return glyph; + } + } + + return (DynamicTextGlyph *)NULL; +} + + +#endif // HAVE_FREETYPE diff --git a/panda/src/text/dynamicTextPage.h b/panda/src/text/dynamicTextPage.h new file mode 100644 index 0000000000..9664b746e1 --- /dev/null +++ b/panda/src/text/dynamicTextPage.h @@ -0,0 +1,85 @@ +// Filename: dynamicTextPage.h +// Created by: drose (09Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 DYNAMICTEXTPAGE_H +#define DYNAMICTEXTPAGE_H + +#include "pandabase.h" + +#ifdef HAVE_FREETYPE + +#include "texture.h" +#include "dynamicTextGlyph.h" +#include "pointerTo.h" +#include "pvector.h" + +class DynamicTextFont; + +//////////////////////////////////////////////////////////////////// +// Class : DynamicTextPage +// Description : A single "page" of a DynamicTextFont. This is a +// single texture that holds a number of glyphs for +// rendering. The font starts out with one page, and +// will add more as it needs them. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA DynamicTextPage : public Texture { +public: + DynamicTextPage(DynamicTextFont *font); + + DynamicTextGlyph *slot_glyph(int x_size, int y_size, int margin); + + INLINE int get_x_size() const; + INLINE int get_y_size() const; + +PUBLISHED: + INLINE bool is_empty() const; + +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; + + typedef pvector< PT(DynamicTextGlyph) > Glyphs; + Glyphs _glyphs; + + int _x_size, _y_size; + + DynamicTextFont *_font; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + Texture::init_type(); + register_type(_type_handle, "DynamicTextPage", + Texture::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 "dynamicTextPage.I" + +#endif // HAVE_FREETYPE + +#endif diff --git a/panda/src/text/staticTextFont.cxx b/panda/src/text/staticTextFont.cxx index a2e345e3b8..1772979ac6 100644 --- a/panda/src/text/staticTextFont.cxx +++ b/panda/src/text/staticTextFont.cxx @@ -41,6 +41,7 @@ StaticTextFont(Node *font_def) { _glyphs.clear(); find_characters(font_def); + _is_valid = !_glyphs.empty(); if (_font->is_of_type(NamedNode::get_class_type())) { NamedNode *named_node = DCAST(NamedNode, _font); @@ -48,16 +49,6 @@ StaticTextFont(Node *font_def) { } } -//////////////////////////////////////////////////////////////////// -// Function: StaticTextFont::Destructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -StaticTextFont:: -~StaticTextFont() { -} - - //////////////////////////////////////////////////////////////////// // Function: StaticTextFont::write // Access: Published, Virtual @@ -167,13 +158,13 @@ write(ostream &out, int indent_level) const { // code, or NULL if there is no such glyph. //////////////////////////////////////////////////////////////////// const TextGlyph *StaticTextFont:: -get_glyph(int character) const { +get_glyph(int character) { Glyphs::const_iterator gi = _glyphs.find(character); if (gi == _glyphs.end()) { // No definition for this character. return (TextGlyph *)NULL; } else { - return &(*gi).second; + return (*gi).second; } } @@ -262,7 +253,7 @@ find_characters(Node *root) { width = alist[ilist[0]][0]; } - _glyphs[character] = TextGlyph(ch, trans, width); + _glyphs[character] = new TextGlyph(ch, trans, width); } } else if (name == "ds") { diff --git a/panda/src/text/staticTextFont.h b/panda/src/text/staticTextFont.h index 73fbb605c0..fd45072532 100644 --- a/panda/src/text/staticTextFont.h +++ b/panda/src/text/staticTextFont.h @@ -19,17 +19,12 @@ #ifndef STATICTEXTFONT_H #define STATICTEXTFONT_H -#include +#include "pandabase.h" #include "config_text.h" #include "textFont.h" #include "textGlyph.h" - -#include -#include -#include -#include - +#include "pt_Node.h" #include "pmap.h" class Node; @@ -47,19 +42,18 @@ class GeomPoint; class EXPCL_PANDA StaticTextFont : public TextFont { PUBLISHED: StaticTextFont(Node *font_def); - virtual ~StaticTextFont(); virtual void write(ostream &out, int indent_level) const; public: - virtual const TextGlyph *get_glyph(int character) const; + virtual const TextGlyph *get_glyph(int character); private: bool find_character_gsets(Node *root, Geom *&ch, GeomPoint *&dot, AllTransitionsWrapper &trans); void find_characters(Node *root); - typedef pmap Glyphs; + typedef pmap Glyphs; Glyphs _glyphs; float _font_height; PT_Node _font; @@ -80,8 +74,6 @@ public: private: static TypeHandle _type_handle; - - friend class TextNode; }; #include "staticTextFont.I" diff --git a/panda/src/text/textFont.I b/panda/src/text/textFont.I index aa56fa81fb..880677eee8 100644 --- a/panda/src/text/textFont.I +++ b/panda/src/text/textFont.I @@ -17,6 +17,17 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: TextFont::is_valid +// Access: Published +// Description: Returns true if the font is valid and ready to use, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool TextFont:: +is_valid() const { + return _is_valid; +} + //////////////////////////////////////////////////////////////////// // Function: TextFont::get_line_height // Access: Published diff --git a/panda/src/text/textFont.cxx b/panda/src/text/textFont.cxx index 6acb127bd9..f42da1722b 100644 --- a/panda/src/text/textFont.cxx +++ b/panda/src/text/textFont.cxx @@ -40,6 +40,7 @@ isblank(char ch) { //////////////////////////////////////////////////////////////////// TextFont:: TextFont() { + _is_valid = false; _line_height = 1.0; } @@ -59,7 +60,7 @@ TextFont:: // or 0.0 if the character is not known. //////////////////////////////////////////////////////////////////// float TextFont:: -calc_width(int ch) const { +calc_width(int ch) { if (ch == ' ') { // A space is a special case. return 0.25; @@ -82,7 +83,7 @@ calc_width(int ch) const { // character. //////////////////////////////////////////////////////////////////// float TextFont:: -calc_width(const string &line) const { +calc_width(const string &line) { float width = 0.0; string::const_iterator si; @@ -104,7 +105,7 @@ calc_width(const string &line) const { //////////////////////////////////////////////////////////////////// string TextFont:: wordwrap_to(const string &text, float wordwrap_width, - bool preserve_trailing_whitespace) const { + bool preserve_trailing_whitespace) { string output_text; size_t p = 0; diff --git a/panda/src/text/textFont.h b/panda/src/text/textFont.h index b08eb22626..ec9f98cd21 100644 --- a/panda/src/text/textFont.h +++ b/panda/src/text/textFont.h @@ -46,19 +46,21 @@ public: PUBLISHED: virtual ~TextFont(); + INLINE bool is_valid() const; INLINE float get_line_height() const; - float calc_width(int ch) const; - float calc_width(const string &line) const; + float calc_width(int ch); + float calc_width(const string &line); string wordwrap_to(const string &text, float wordwrap_width, - bool preserve_trailing_whitespace) const; + bool preserve_trailing_whitespace); virtual void write(ostream &out, int indent_level) const; public: - virtual const TextGlyph *get_glyph(int character) const=0; + virtual const TextGlyph *get_glyph(int character)=0; protected: + bool _is_valid; float _line_height; public: diff --git a/panda/src/text/textGlyph.I b/panda/src/text/textGlyph.I index 14b4f61a43..7ee0792810 100644 --- a/panda/src/text/textGlyph.I +++ b/panda/src/text/textGlyph.I @@ -24,6 +24,8 @@ //////////////////////////////////////////////////////////////////// INLINE TextGlyph:: TextGlyph() { + _geom = (Geom *)NULL; + _advance = 0.0f; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/text/textGlyph.h b/panda/src/text/textGlyph.h index 2db7adf746..cc7f9ae846 100644 --- a/panda/src/text/textGlyph.h +++ b/panda/src/text/textGlyph.h @@ -21,8 +21,10 @@ #include "pandabase.h" #include "allTransitionsWrapper.h" +#include "referenceCount.h" +#include "geom.h" +#include "pointerTo.h" -class Geom; class TextGlyph; //////////////////////////////////////////////////////////////////// @@ -31,7 +33,7 @@ class TextGlyph; // font. This is a piece of renderable geometry of some // kind. //////////////////////////////////////////////////////////////////// -class TextGlyph { +class TextGlyph : public ReferenceCount { public: INLINE TextGlyph(); INLINE TextGlyph(Geom *geom, const AllTransitionsWrapper &trans, float advance); @@ -42,8 +44,8 @@ public: INLINE const AllTransitionsWrapper &get_trans() const; INLINE float get_advance() const; -private: - Geom *_geom; +protected: + PT(Geom) _geom; AllTransitionsWrapper _trans; float _advance; }; diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h index 63c8fa5392..421a6e8877 100644 --- a/panda/src/text/textNode.h +++ b/panda/src/text/textNode.h @@ -47,7 +47,29 @@ END_PUBLISH //////////////////////////////////////////////////////////////////// // Class : TextNode -// Description : +// Description : The primary interface to this module. This class +// does basic text assembly; given a string of text and +// a TextFont object, it creates a piece of geometry +// that may be placed in the 3-d or 2-d world to +// represent the indicated text. +// +// The TextNode may be used in one of two ways. +// Naively, it may be parented to the scene graph +// directly; used in this way, you can optionally call +// freeze() and thaw() between changing many parameters +// in the text at once, to avoid unnecessary expensive +// regeneration with each parameter change. However, it +// will work, if slowly, even if you never call freeze() +// and thaw(). +// +// The second way TextNode may be used is as a text +// generator. To use it in this way, call freeze() once +// on the TextNode when you create it, and never call +// thaw(). Do not parent the TextNode to the scene +// graph; instea, set the properties of the text and +// call generate() to return a node which you may parent +// wherever you like. Each time you call generate() a +// new node is returned. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA TextNode : public NamedNode { PUBLISHED: diff --git a/panda/src/text/text_composite1.cxx b/panda/src/text/text_composite1.cxx index 47c8e4dbbf..18afab1cfe 100644 --- a/panda/src/text/text_composite1.cxx +++ b/panda/src/text/text_composite1.cxx @@ -1,4 +1,7 @@ #include "config_text.cxx" +#include "dynamicTextFont.cxx" +#include "dynamicTextGlyph.cxx" +#include "dynamicTextPage.cxx" #include "staticTextFont.cxx" #include "textFont.cxx" #include "textGlyph.cxx"