From 6409ff6b7f5e586e296a475c4e9570028b8d1434 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 9 Sep 2003 19:26:25 +0000 Subject: [PATCH] add pnmtext library --- panda/metalibs/panda/Sources.pp | 2 +- panda/src/pnmtext/freetypeFont.I | 188 ++++++++++++++ panda/src/pnmtext/freetypeFont.cxx | 379 +++++++++++++++++++++++++++++ panda/src/pnmtext/freetypeFont.h | 111 +++++++++ panda/src/putil/string_utils.cxx | 21 ++ panda/src/putil/string_utils.h | 1 + panda/src/text/Sources.pp | 2 +- panda/src/text/config_text.cxx | 3 - panda/src/text/config_text.h | 3 - panda/src/text/dynamicTextFont.I | 53 +++- panda/src/text/dynamicTextFont.cxx | 312 +----------------------- panda/src/text/dynamicTextFont.h | 29 +-- 12 files changed, 766 insertions(+), 338 deletions(-) create mode 100644 panda/src/pnmtext/freetypeFont.I create mode 100644 panda/src/pnmtext/freetypeFont.cxx create mode 100644 panda/src/pnmtext/freetypeFont.h diff --git a/panda/metalibs/panda/Sources.pp b/panda/metalibs/panda/Sources.pp index b0aff217ff..3eb5a145b9 100644 --- a/panda/metalibs/panda/Sources.pp +++ b/panda/metalibs/panda/Sources.pp @@ -17,7 +17,7 @@ gsgmisc light linmath mathutil net \ parametrics pnm \ pnmimagetypes pnmimage sgattrib sgmanip sgraph sgraphutil \ - switchnode text tform tiff lerp loader putil \ + switchnode pnmtext text tform tiff lerp loader putil \ audio pgui pandabase diff --git a/panda/src/pnmtext/freetypeFont.I b/panda/src/pnmtext/freetypeFont.I new file mode 100644 index 0000000000..9ec4d3e1ad --- /dev/null +++ b/panda/src/pnmtext/freetypeFont.I @@ -0,0 +1,188 @@ +// Filename: freetypeFont.I +// Created by: drose (07Sep03) +// +//////////////////////////////////////////////////////////////////// +// +// 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: FreetypeFont::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FreetypeFont:: +~FreetypeFont() { + unload_font(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::set_point_size +// Access: Public +// Description: Sets the point size of the font. This controls the +// apparent size of the font onscreen. By convention, a +// 10 point font is about 1 screen unit high. +// +// This should only be called before any characters have +// been requested out of the font, or immediately after +// calling clear(). +//////////////////////////////////////////////////////////////////// +INLINE bool FreetypeFont:: +set_point_size(float point_size) { + _point_size = point_size; + return reset_scale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_point_size +// Access: Public +// Description: Returns the point size of the font. +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_point_size() const { + return _point_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::set_pixels_per_unit +// Access: Public +// Description: Set the resolution of the texture map, and hence the +// clarity of the resulting font. This sets the number +// of pixels in the texture map that are used for each +// onscreen unit. +// +// Setting this number larger results in an easier to +// read font, but at the cost of more texture memory. +// +// This should only be called before any characters have +// been requested out of the font, or immediately after +// calling clear(). +//////////////////////////////////////////////////////////////////// +INLINE bool FreetypeFont:: +set_pixels_per_unit(float pixels_per_unit) { + _tex_pixels_per_unit = pixels_per_unit; + return reset_scale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_pixels_per_unit +// Access: Public +// Description: Returns the resolution of the texture map. See +// set_pixels_per_unit(). +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_pixels_per_unit() const { + return _tex_pixels_per_unit; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::set_pixel_size +// Access: Public +// Description: Computes the appropriate pixels_per_unit value to set +// the size of the font in the texture to the indicated +// number of pixels. This is just another way to +// specify pixels_per_unit(). +//////////////////////////////////////////////////////////////////// +INLINE bool FreetypeFont:: +set_pixel_size(float pixel_size) { + return set_pixels_per_unit(pixel_size * _points_per_unit / _point_size); +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_pixel_size +// Access: Public +// Description: Returns the size of the font in pixels, as it appears +// in the texture. +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_pixel_size() const { + return _tex_pixels_per_unit * _point_size / _points_per_unit; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::set_scale_factor +// Access: Public +// Description: Sets the factor by which the font is rendered larger +// by the FreeType library before being filtered down to +// its actual size in the texture as specified by +// set_pixels_per_unit(). This may be set to a number +// larger than 1.0 to improve the font's antialiasing +// (since FreeType doesn't really do a swell job of +// antialiasing by itself). There is some performance +// implication for setting this different than 1.0. +// +// This should only be called before any characters have +// been requested out of the font, or immediately after +// calling clear(). +//////////////////////////////////////////////////////////////////// +INLINE bool FreetypeFont:: +set_scale_factor(float scale_factor) { + _scale_factor = scale_factor; + return reset_scale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_scale_factor +// Access: Public +// Description: Returns the antialiasing scale factor. See +// set_scale_factor(). +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_scale_factor() const { + return _scale_factor; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_line_height +// Access: Public +// Description: Returns the number of units high each line of text +// is. +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_line_height() const { + return _line_height; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_space_advance +// Access: Public +// Description: Returns the number of units wide a space is. +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_space_advance() const { + return _space_advance; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_points_per_unit +// Access: Public, Static +// Description: Returns the point size of the font that is one Panda +// unit high. This is an arbitrary Panda convention for +// text, and is set to 10.0. +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_points_per_unit() { + return _points_per_unit; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::get_points_per_inch +// Access: Public, Static +// Description: Returns the number of points in one inch. This is a +// universal typographic convention. +//////////////////////////////////////////////////////////////////// +INLINE float FreetypeFont:: +get_points_per_inch() { + return _points_per_inch; +} diff --git a/panda/src/pnmtext/freetypeFont.cxx b/panda/src/pnmtext/freetypeFont.cxx new file mode 100644 index 0000000000..01d0dd0bb2 --- /dev/null +++ b/panda/src/pnmtext/freetypeFont.cxx @@ -0,0 +1,379 @@ +// Filename: freetypeFont.cxx +// Created by: drose (07Sep03) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "freetypeFont.h" + +#ifdef HAVE_FREETYPE + +#include "config_pnmtext.h" +#include "config_util.h" +#include "config_express.h" +#include "virtualFileSystem.h" + +FT_Library FreetypeFont::_ft_library; +bool FreetypeFont::_ft_initialized = false; +bool FreetypeFont::_ft_ok = false; + +// This constant determines how big a particular point size font +// appears to be. By convention, 10 points is 1 unit (e.g. 1 foot) +// high. +const float FreetypeFont::_points_per_unit = 10.0f; + +// A universal typographic convention. +const float FreetypeFont::_points_per_inch = 72.0f; + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +FreetypeFont:: +FreetypeFont() { + _font_loaded = false; + + _point_size = text_point_size; + _tex_pixels_per_unit = text_pixels_per_unit; + _scale_factor = text_scale_factor; + + _line_height = 1.0f; + _space_advance = 0.25f; + + if (!_ft_initialized) { + initialize_ft_library(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::load_font +// Access: Protected +// Description: This method accepts the name of some font file +// that FreeType can read, along with face_index, +// indicating which font within the file to load +// (usually 0). +//////////////////////////////////////////////////////////////////// +bool FreetypeFont:: +load_font(const Filename &font_filename, int face_index) { + if (!_ft_ok) { + pnmtext_cat.error() + << "Unable to read font " << font_filename + << ": FreeType library not initialized properly.\n"; + return false; + } + + unload_font(); + + bool exists = false; + int error; + Filename path(font_filename); + if (use_vfs) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + vfs->resolve_filename(path, get_model_path()); + exists = vfs->read_file(path, _raw_font_data); + if (exists) { + error = FT_New_Memory_Face(_ft_library, + (const FT_Byte *)_raw_font_data.data(), + _raw_font_data.length(), + face_index, &_face); + } + } else { + path.resolve_filename(get_model_path()); + exists = path.exists(); + if (exists) { + string os_specific = path.to_os_specific(); + error = FT_New_Face(_ft_library, os_specific.c_str(), + face_index, &_face); + } + } + + if (!exists) { + pnmtext_cat.error() + << "Unable to find font file " << font_filename << "\n"; + } else { + if (error == FT_Err_Unknown_File_Format) { + pnmtext_cat.error() + << "Unable to read font " << font_filename << ": unknown file format.\n"; + } else if (error) { + pnmtext_cat.error() + << "Unable to read font " << font_filename << ": invalid.\n"; + + } else { + return font_loaded(); + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::load_font +// Access: Protected +// Description: This method accepts a table of data representing +// the font file, loaded from some source other than a +// filename on disk. +//////////////////////////////////////////////////////////////////// +bool FreetypeFont:: +load_font(const char *font_data, int data_length, int face_index) { + if (!_ft_ok) { + pnmtext_cat.error() + << "Unable to read font: FreeType library not initialized properly.\n"; + return false; + } + + unload_font(); + + int error; + error = FT_New_Memory_Face(_ft_library, + (const FT_Byte *)font_data, data_length, + face_index, &_face); + + if (error == FT_Err_Unknown_File_Format) { + pnmtext_cat.error() + << "Unable to read font: unknown file format.\n"; + } else if (error) { + pnmtext_cat.error() + << "Unable to read font: invalid.\n"; + + } else { + return font_loaded(); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::unload_font +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +void FreetypeFont:: +unload_font() { + if (_font_loaded) { + FT_Done_Face(_face); + _font_loaded = false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::copy_bitmap_to_pnmimage +// Access: Protected +// Description: Copies a bitmap as rendered by FreeType into a +// PNMImage, so it can be rescaled. +//////////////////////////////////////////////////////////////////// +void FreetypeFont:: +copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) { + if (bitmap.pixel_mode == ft_pixel_mode_grays && + bitmap.num_grays == (int)image.get_maxval() + 1) { + // This is the easy case: we can copy the rendered glyph + // directly into our image, one pixel at a time. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + for (int xi = 0; xi < bitmap.width; xi++) { + image.set_gray_val(xi, yi, buffer_row[xi]); + } + buffer_row += bitmap.pitch; + } + + } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { + // This is a little bit more work: we have to expand the + // one-bit-per-pixel bitmap into a one-byte-per-pixel image. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + xelval maxval = image.get_maxval(); + int bit = 0x80; + unsigned char *b = buffer_row; + for (int xi = 0; xi < bitmap.width; xi++) { + if (*b & bit) { + image.set_gray_val(xi, yi, maxval); + } else { + image.set_gray_val(xi, yi, 0); + } + bit >>= 1; + if (bit == 0) { + ++b; + bit = 0x80; + } + } + + buffer_row += bitmap.pitch; + } + + + } else if (bitmap.pixel_mode == ft_pixel_mode_grays) { + // Here we must expand a grayscale pixmap with n levels of gray + // into our 256-level texture. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + for (int xi = 0; xi < bitmap.width; xi++) { + image.set_gray(xi, yi, (float)buffer_row[xi] / (bitmap.num_grays - 1)); + } + buffer_row += bitmap.pitch; + } + + } else { + pnmtext_cat.error() + << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::font_loaded +// Access: Private +// Description: Called after a font has been successfully loaded, +// either from disk or from memory image. +//////////////////////////////////////////////////////////////////// +bool FreetypeFont:: +font_loaded() { + string name = _face->family_name; + if (_face->style_name != NULL) { + name += " "; + name += _face->style_name; + } + set_name(name); + + pnmtext_cat.info() + << "Loaded font " << name << "\n"; + _font_loaded = true; + reset_scale(); + + if (pnmtext_cat.is_debug()) { + pnmtext_cat.debug() + << name << " has " << _face->num_charmaps << " charmaps:\n"; + for (int i = 0; i < _face->num_charmaps; i++) { + pnmtext_cat.debug(false) << " " << (void *)_face->charmaps[i]; + } + pnmtext_cat.debug(false) << "\n"; + pnmtext_cat.debug() + << "default charmap is " << (void *)_face->charmap << "\n"; + } + if (_face->charmap == NULL) { + // If for some reason FreeType didn't set us up a charmap, + // then set it up ourselves. + if (_face->num_charmaps == 0) { + pnmtext_cat.warning() + << name << " has no charmaps available.\n"; + } else { + pnmtext_cat.warning() + << name << " has no default Unicode charmap.\n"; + if (_face->num_charmaps > 1) { + pnmtext_cat.warning() + << "Arbitrarily choosing first of " + << _face->num_charmaps << " charmaps.\n"; + } + FT_Set_Charmap(_face, _face->charmaps[0]); + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::reset_scale +// Access: Private +// Description: Resets the font based on the current values for +// _point_size, _tex_pixels_per_unit, and _scale_factor. +// Returns true if successful, false otherwise. +//////////////////////////////////////////////////////////////////// +bool FreetypeFont:: +reset_scale() { + // The font may be rendered larger (by a factor of _scale_factor), + // and then reduced into the texture. Hence the difference between + // _font_pixels_per_unit and _tex_pixels_per_unit. + _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor; + + float units_per_inch = (_points_per_inch / _points_per_unit); + int dpi = (int)(_font_pixels_per_unit * units_per_inch); + + int error = FT_Set_Char_Size(_face, + (int)(_point_size * 64), (int)(_point_size * 64), + dpi, dpi); + if (error) { + // If we were unable to set a particular char size, perhaps we + // have a non-scalable font. Try to figure out the closest + // available size. + int desired_height = (int)(_font_pixels_per_unit * _point_size / _points_per_unit + 0.5f); + int best_size = -1; + if (_face->num_fixed_sizes > 0) { + best_size = 0; + int best_diff = abs(desired_height - _face->available_sizes[0].height); + for (int i = 1; i < _face->num_fixed_sizes; i++) { + int diff = abs(desired_height - _face->available_sizes[i].height); + if (diff < best_diff) { + best_size = i; + best_diff = diff; + } + } + } + if (best_size >= 0) { + int pixel_height = _face->available_sizes[best_size].height; + int pixel_width = _face->available_sizes[best_size].width; + error = FT_Set_Pixel_Sizes(_face, pixel_width, pixel_height); + if (!error) { + pnmtext_cat.info() + << "Using " << pixel_height << "-pixel font for " + << get_name() << "\n"; + + _font_pixels_per_unit = pixel_height * _points_per_unit / _point_size; + _tex_pixels_per_unit = _font_pixels_per_unit; + } + } + } + + if (error) { + pnmtext_cat.warning() + << "Unable to set " << get_name() + << " to " << _point_size << "pt at " << dpi << " dpi.\n"; + _line_height = 1.0f; + return false; + } + + _line_height = _face->size->metrics.height / (_font_pixels_per_unit * 64.0f); + + // Determine the correct width for a space. + error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT); + if (error) { + // Space isn't defined. Oh well. + _space_advance = 0.25f * _line_height; + + } else { + _space_advance = _face->glyph->advance.x / (_font_pixels_per_unit * 64.0f); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: FreetypeFont::initialize_ft_library +// Access: Private, Static +// Description: Should be called exactly once to initialize the +// FreeType library. +//////////////////////////////////////////////////////////////////// +void FreetypeFont:: +initialize_ft_library() { + if (!_ft_initialized) { + int error = FT_Init_FreeType(&_ft_library); + _ft_initialized = true; + if (error) { + pnmtext_cat.error() + << "Unable to initialize FreeType; dynamic fonts will not load.\n"; + } else { + _ft_ok = true; + } + } +} + +#endif // HAVE_FREETYPE diff --git a/panda/src/pnmtext/freetypeFont.h b/panda/src/pnmtext/freetypeFont.h new file mode 100644 index 0000000000..863addc5e3 --- /dev/null +++ b/panda/src/pnmtext/freetypeFont.h @@ -0,0 +1,111 @@ +// Filename: freetypeFont.h +// Created by: drose (07Sep03) +// +//////////////////////////////////////////////////////////////////// +// +// 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 FREETYPEFONT_H +#define FREETYPEFONT_H + +#include "pandabase.h" + +#ifdef HAVE_FREETYPE + +#include "config_text.h" +#include "filename.h" +#include "pvector.h" +#include "pmap.h" +#include "namable.h" + +#include +#include FT_FREETYPE_H + +//////////////////////////////////////////////////////////////////// +// Class : FreetypeFont +// Description : This is a common base class for both DynamicTextFont +// and PNMTextMaker. Both of these are utility classes +// that use the FreeType library to generate glyphs from +// fonts; this class abstracts out that common wrapper +// around FreeType. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA FreetypeFont : public Namable { +protected: + FreetypeFont(); + + bool load_font(const Filename &font_filename, int face_index); + bool load_font(const char *font_data, int data_length, int face_index); + void unload_font(); + +public: + INLINE ~FreetypeFont(); + + INLINE bool set_point_size(float point_size); + INLINE float get_point_size() const; + + INLINE bool set_pixels_per_unit(float pixels_per_unit); + INLINE float get_pixels_per_unit() const; + + INLINE bool set_pixel_size(float pixel_size); + INLINE float get_pixel_size() const; + + INLINE bool set_scale_factor(float scale_factor); + INLINE float get_scale_factor() const; + + INLINE float get_line_height() const; + INLINE float get_space_advance() const; + + INLINE static float get_points_per_unit(); + INLINE static float get_points_per_inch(); + +protected: + void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image); + +private: + bool font_loaded(); + bool reset_scale(); + static void initialize_ft_library(); + +protected: + float _point_size; + float _tex_pixels_per_unit; + float _scale_factor; + float _font_pixels_per_unit; + + float _line_height; + float _space_advance; + + FT_Face _face; + +private: + bool _font_loaded; + + // This string is used to hold the data read from the font file in + // vfs mode. Since the FreeType library keeps pointers into this + // data, we have to keep it around. + string _raw_font_data; + + static FT_Library _ft_library; + static bool _ft_initialized; + static bool _ft_ok; + + static const float _points_per_unit; + static const float _points_per_inch; +}; + +#include "freetypeFont.I" + +#endif // HAVE_FREETYPE + +#endif diff --git a/panda/src/putil/string_utils.cxx b/panda/src/putil/string_utils.cxx index cf1a355da8..42872fbf4d 100644 --- a/panda/src/putil/string_utils.cxx +++ b/panda/src/putil/string_utils.cxx @@ -189,6 +189,27 @@ trim_right(const string &str) { return str.substr(begin, end - begin); } +//////////////////////////////////////////////////////////////////// +// Function: trim +// Description: Returns a new string representing the contents of the +// given string with both leading and trailing +// whitespace removed. +//////////////////////////////////////////////////////////////////// +string +trim(const string &str) { + size_t begin = 0; + while (begin < str.size() && isspace((unsigned int)str[begin])) { + begin++; + } + + size_t end = str.size(); + while (end > begin && isspace((unsigned int)str[end - 1])) { + end--; + } + + return str.substr(begin, end - begin); +} + //////////////////////////////////////////////////////////////////// // Function: string_to_int // Description: A string-interface wrapper around the C library diff --git a/panda/src/putil/string_utils.h b/panda/src/putil/string_utils.h index 3d541b9635..dba18eeff8 100644 --- a/panda/src/putil/string_utils.h +++ b/panda/src/putil/string_utils.h @@ -47,6 +47,7 @@ EXPCL_PANDA void tokenize(const string &str, vector_string &words, // Trims leading and/or trailing whitespace from the string. EXPCL_PANDA string trim_left(const string &str); EXPCL_PANDA string trim_right(const string &str); +EXPCL_PANDA string trim(const string &str); // Functions to parse numeric values out of a string. EXPCL_PANDA int string_to_int(const string &str, string &tail); diff --git a/panda/src/text/Sources.pp b/panda/src/text/Sources.pp index 59689bb3a9..08a8459644 100644 --- a/panda/src/text/Sources.pp +++ b/panda/src/text/Sources.pp @@ -7,7 +7,7 @@ #define TARGET text #define LOCAL_LIBS \ putil gobj pgraph linmath \ - pnmimage gsgbase mathutil + pnmtext pnmimage gsgbase mathutil #define COMBINED_SOURCES $[TARGET]_composite1.cxx diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 4d1daf4edb..3195575245 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -42,9 +42,6 @@ const int text_texture_margin = config_text.GetInt("text-texture-margin", 2); const float text_poly_margin = config_text.GetFloat("text-poly-margin", 0.0f); const int text_page_x_size = config_text.GetInt("text-page-x-size", 256); const int text_page_y_size = config_text.GetInt("text-page-y-size", 256); -const float text_point_size = config_text.GetFloat("text-point-size", 10.0f); -const float text_pixels_per_unit = config_text.GetFloat("text-pixels-per-unit", 30.0f); -const float text_scale_factor = config_text.GetFloat("text-scale-factor", 2.0f); const bool text_small_caps = config_text.GetBool("text-small-caps", false); const float text_small_caps_scale = config_text.GetFloat("text-small-caps-scale", 0.8f); const string text_default_font = config_text.GetString("text-default-font", ""); diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index ccd43fe102..1b1f6bb575 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -34,9 +34,6 @@ extern const int text_texture_margin; extern const float text_poly_margin; extern const int text_page_x_size; extern const int text_page_y_size; -extern const float text_point_size; -extern const float text_pixels_per_unit; -extern const float text_scale_factor; extern const bool text_small_caps; extern const float text_small_caps_scale; extern const string text_default_font; diff --git a/panda/src/text/dynamicTextFont.I b/panda/src/text/dynamicTextFont.I index 717213268f..50c101f38e 100644 --- a/panda/src/text/dynamicTextFont.I +++ b/panda/src/text/dynamicTextFont.I @@ -17,6 +17,18 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_name +// Access: Public +// Description: Disambiguates the get_name() method between that +// inherited from TextFont and that inherited from +// FreetypeFont. +//////////////////////////////////////////////////////////////////// +INLINE const string &DynamicTextFont:: +get_name() const { + return TextFont::get_name(); +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::set_point_size // Access: Published @@ -33,8 +45,7 @@ set_point_size(float point_size) { // If this assertion fails, you didn't call clear() first. RTFM. nassertr(get_num_pages() == 0, false); - _point_size = point_size; - return reset_scale(); + return FreetypeFont::set_point_size(point_size); } //////////////////////////////////////////////////////////////////// @@ -44,7 +55,7 @@ set_point_size(float point_size) { //////////////////////////////////////////////////////////////////// INLINE float DynamicTextFont:: get_point_size() const { - return _point_size; + return FreetypeFont::get_point_size(); } //////////////////////////////////////////////////////////////////// @@ -67,8 +78,7 @@ set_pixels_per_unit(float pixels_per_unit) { // If this assertion fails, you didn't call clear() first. RTFM. nassertr(get_num_pages() == 0, false); - _tex_pixels_per_unit = pixels_per_unit; - return reset_scale(); + return FreetypeFont::set_pixels_per_unit(pixels_per_unit); } //////////////////////////////////////////////////////////////////// @@ -79,7 +89,7 @@ set_pixels_per_unit(float pixels_per_unit) { //////////////////////////////////////////////////////////////////// INLINE float DynamicTextFont:: get_pixels_per_unit() const { - return _tex_pixels_per_unit; + return FreetypeFont::get_pixels_per_unit(); } //////////////////////////////////////////////////////////////////// @@ -103,8 +113,7 @@ set_scale_factor(float scale_factor) { // If this assertion fails, you didn't call clear() first. RTFM. nassertr(get_num_pages() == 0, false); - _scale_factor = scale_factor; - return reset_scale(); + return FreetypeFont::set_scale_factor(scale_factor); } //////////////////////////////////////////////////////////////////// @@ -115,7 +124,28 @@ set_scale_factor(float scale_factor) { //////////////////////////////////////////////////////////////////// INLINE float DynamicTextFont:: get_scale_factor() const { - return _scale_factor; + return FreetypeFont::get_scale_factor(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_line_height +// Access: Public +// Description: Returns the number of units high each line of text +// is. +//////////////////////////////////////////////////////////////////// +INLINE float DynamicTextFont:: +get_line_height() const { + return TextFont::get_line_height(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_space_advance +// Access: Public +// Description: Returns the number of units wide a space is. +//////////////////////////////////////////////////////////////////// +INLINE float DynamicTextFont:: +get_space_advance() const { + return TextFont::get_space_advance(); } //////////////////////////////////////////////////////////////////// @@ -314,3 +344,8 @@ INLINE bool DynamicTextFont:: get_update_cleared_glyphs() { return _update_cleared_glyphs; } + +INLINE ostream & +operator << (ostream &out, const DynamicTextFont &dtf) { + return out << dtf.get_name(); +} diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index 0c009335a3..0f00f46043 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/panda/src/text/dynamicTextFont.cxx @@ -27,21 +27,9 @@ bool DynamicTextFont::_update_cleared_glyphs = text_update_cleared_glyphs; -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 to be. By convention, 10 points is 1 unit (e.g. 1 foot) -// high. -static const float points_per_unit = 10.0f; - -// A universal typographic convention. -static const float points_per_inch = 72.0f; - //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::Constructor // Access: Published @@ -53,90 +41,10 @@ static const float points_per_inch = 72.0f; DynamicTextFont:: DynamicTextFont(const Filename &font_filename, int face_index) { initialize(); - - if (!_ft_ok) { - text_cat.error() - << "Unable to read font " << font_filename - << ": FreeType library not initialized properly.\n"; - return; - } - - bool exists = false; - int error; - Filename path(font_filename); - if (use_vfs) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->resolve_filename(path, get_model_path()); - exists = vfs->read_file(path, _raw_font_data); - if (exists) { - error = FT_New_Memory_Face(_ft_library, - (const FT_Byte *)_raw_font_data.data(), - _raw_font_data.length(), - face_index, &_face); - } - } else { - path.resolve_filename(get_model_path()); - exists = path.exists(); - if (exists) { - string os_specific = path.to_os_specific(); - error = FT_New_Face(_ft_library, os_specific.c_str(), - face_index, &_face); - } - } - - if (!exists) { - text_cat.error() - << "Unable to find font file " << font_filename << "\n"; - } else { - 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; - if (_face->style_name != NULL) { - name += " "; - name += _face->style_name; - } - set_name(name); - - text_cat.info() - << "Loaded font " << get_name() << "\n"; - _is_valid = true; - reset_scale(); - - if (text_cat.is_debug()) { - text_cat.debug() - << *this << " has " << _face->num_charmaps << " charmaps:\n"; - for (int i = 0; i < _face->num_charmaps; i++) { - text_cat.debug(false) << " " << (void *)_face->charmaps[i]; - } - text_cat.debug(false) << "\n"; - text_cat.debug() - << "default charmap is " << (void *)_face->charmap << "\n"; - } - if (_face->charmap == NULL) { - // If for some reason FreeType didn't set us up a charmap, - // then set it up ourselves. - if (_face->num_charmaps == 0) { - text_cat.warning() - << *this << " has no charmaps available.\n"; - } else { - text_cat.warning() - << *this << " has no default Unicode charmap.\n"; - if (_face->num_charmaps > 1) { - text_cat.warning() - << "Arbitrarily choosing first of " - << _face->num_charmaps << " charmaps.\n"; - } - FT_Set_Charmap(_face, _face->charmaps[0]); - } - } - } - } + _is_valid = load_font(font_filename, face_index); + TextFont::set_name(FreetypeFont::get_name()); + TextFont::_line_height = FreetypeFont::get_line_height(); + TextFont::_space_advance = FreetypeFont::get_space_advance(); } //////////////////////////////////////////////////////////////////// @@ -149,51 +57,19 @@ DynamicTextFont(const Filename &font_filename, int face_index) { DynamicTextFont:: DynamicTextFont(const char *font_data, int data_length, int face_index) { initialize(); - - if (!_ft_ok) { - text_cat.error() - << "Unable to read font: FreeType library not initialized properly.\n"; - return; - } - - int error; - error = FT_New_Memory_Face(_ft_library, - (const FT_Byte *)font_data, data_length, - face_index, &_face); - - if (error == FT_Err_Unknown_File_Format) { - text_cat.error() - << "Unable to read font: unknown file format.\n"; - } else if (error) { - text_cat.error() - << "Unable to read font: invalid.\n"; - - } else { - string name = _face->family_name; - if (_face->style_name != NULL) { - name += " "; - name += _face->style_name; - } - set_name(name); - - text_cat.info() - << "Loaded font " << get_name() << "\n"; - _is_valid = true; - reset_scale(); - } + _is_valid = load_font(font_data, data_length, face_index); + TextFont::set_name(FreetypeFont::get_name()); + TextFont::_line_height = FreetypeFont::_line_height; + TextFont::_space_advance = FreetypeFont::_space_advance; } //////////////////////////////////////////////////////////////////// -// Function: DynamicTextFont::Constructor +// Function: DynamicTextFont::Destructor // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// DynamicTextFont:: ~DynamicTextFont() { - if (_is_valid) { - FT_Done_Face(_face); - _is_valid = false; - } } //////////////////////////////////////////////////////////////////// @@ -380,8 +256,8 @@ get_glyph(int character, const TextGlyph *&glyph) { //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::initialize // Access: Private -// Description: Called from both constructors to set up some internal -// structures. +// Description: Called from both constructors to set up some initial +// values. //////////////////////////////////////////////////////////////////// void DynamicTextFont:: initialize() { @@ -389,9 +265,6 @@ initialize() { _poly_margin = text_poly_margin; _page_x_size = text_page_x_size; _page_y_size = text_page_y_size; - _point_size = text_point_size; - _tex_pixels_per_unit = text_pixels_per_unit; - _scale_factor = text_scale_factor; // We don't necessarily want to use mipmaps, since we don't want to // regenerate those every time the texture changes, but we probably @@ -404,12 +277,7 @@ initialize() { // require generating mipmaps, but does require hardware support. _anisotropic_degree = text_anisotropic_degree; - _preferred_page = 0; - - if (!_ft_initialized) { - initialize_ft_library(); - } } //////////////////////////////////////////////////////////////////// @@ -429,81 +297,6 @@ update_filters() { page->set_anisotropic_degree(_anisotropic_degree); } } - -//////////////////////////////////////////////////////////////////// -// Function: DynamicTextFont::reset_scale -// Access: Private -// Description: Resets the font based on the current values for -// _point_size, _tex_pixels_per_unit, and _scale_factor. -// Returns true if successful, false otherwise. -//////////////////////////////////////////////////////////////////// -bool DynamicTextFont:: -reset_scale() { - // The font may be rendered larger (by a factor of _scale_factor), - // and then reduced into the texture. Hence the difference between - // _font_pixels_per_unit aned _tex_pixels_per_unit. - _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor; - - float units_per_inch = (points_per_inch / points_per_unit); - int dpi = (int)(_font_pixels_per_unit * units_per_inch); - - int error = FT_Set_Char_Size(_face, - (int)(_point_size * 64), (int)(_point_size * 64), - dpi, dpi); - if (error) { - // If we were unable to set a particular char size, perhaps we - // have a non-scalable font. Try to figure out the closest - // available size. - int desired_height = (int)(_font_pixels_per_unit * _point_size / points_per_unit + 0.5f); - int best_size = -1; - if (_face->num_fixed_sizes > 0) { - best_size = 0; - int best_diff = abs(desired_height - _face->available_sizes[0].height); - for (int i = 1; i < _face->num_fixed_sizes; i++) { - int diff = abs(desired_height - _face->available_sizes[i].height); - if (diff < best_diff) { - best_size = i; - best_diff = diff; - } - } - } - if (best_size >= 0) { - int pixel_height = _face->available_sizes[best_size].height; - int pixel_width = _face->available_sizes[best_size].width; - error = FT_Set_Pixel_Sizes(_face, pixel_width, pixel_height); - if (!error) { - text_cat.info() - << "Using " << pixel_height << "-pixel font for " - << get_name() << "\n"; - - _font_pixels_per_unit = pixel_height * points_per_unit / _point_size; - _tex_pixels_per_unit = _font_pixels_per_unit; - } - } - } - - if (error) { - text_cat.warning() - << "Unable to set " << get_name() - << " to " << _point_size << "pt at " << dpi << " dpi.\n"; - _line_height = 1.0f; - return false; - } - - _line_height = _face->size->metrics.height / (_font_pixels_per_unit * 64.0f); - - // Determine the correct width for a space. - error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT); - if (error) { - // Space isn't defined. Oh well. - _space_advance = 0.25f * _line_height; - - } else { - _space_advance = _face->glyph->advance.x / (_font_pixels_per_unit * 64.0f); - } - - return true; -} //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::make_glyph @@ -643,68 +436,6 @@ copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) { } } -//////////////////////////////////////////////////////////////////// -// Function: DynamicTextFont::copy_bitmap_to_pnmimage -// Access: Private -// Description: Copies a bitmap as rendered by FreeType into a -// PNMImage, so it can be rescaled. -//////////////////////////////////////////////////////////////////// -void DynamicTextFont:: -copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) { - if (bitmap.pixel_mode == ft_pixel_mode_grays && - bitmap.num_grays == (int)image.get_maxval() + 1) { - // This is the easy case: we can copy the rendered glyph - // directly into our image, one pixel at a time. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - for (int xi = 0; xi < bitmap.width; xi++) { - image.set_gray_val(xi, yi, buffer_row[xi]); - } - buffer_row += bitmap.pitch; - } - - } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { - // This is a little bit more work: we have to expand the - // one-bit-per-pixel bitmap into a one-byte-per-pixel image. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - xelval maxval = image.get_maxval(); - int bit = 0x80; - unsigned char *b = buffer_row; - for (int xi = 0; xi < bitmap.width; xi++) { - if (*b & bit) { - image.set_gray_val(xi, yi, maxval); - } else { - image.set_gray_val(xi, yi, 0); - } - bit >>= 1; - if (bit == 0) { - ++b; - bit = 0x80; - } - } - - buffer_row += bitmap.pitch; - } - - - } else if (bitmap.pixel_mode == ft_pixel_mode_grays) { - // Here we must expand a grayscale pixmap with n levels of gray - // into our 256-level texture. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - for (int xi = 0; xi < bitmap.width; xi++) { - image.set_gray(xi, yi, (float)buffer_row[xi] / (bitmap.num_grays - 1)); - } - buffer_row += bitmap.pitch; - } - - } else { - text_cat.error() - << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; - } -} - //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::copy_pnmimage_to_texture // Access: Private @@ -783,25 +514,4 @@ slot_glyph(int x_size, int y_size) { } } - -//////////////////////////////////////////////////////////////////// -// 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 index 31b16c5163..d3cd25e256 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -25,6 +25,7 @@ #include "config_text.h" #include "textFont.h" +#include "freetypeFont.h" #include "dynamicTextGlyph.h" #include "dynamicTextPage.h" #include "filename.h" @@ -42,12 +43,14 @@ // FreeType 2.0 library (or any higher, // backward-compatible version). //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA DynamicTextFont : public TextFont { +class EXPCL_PANDA DynamicTextFont : public TextFont, public FreetypeFont { PUBLISHED: DynamicTextFont(const Filename &font_filename, int face_index = 0); DynamicTextFont(const char *font_data, int data_length, int face_index); virtual ~DynamicTextFont(); + INLINE const string &get_name() const; + INLINE bool set_point_size(float point_size); INLINE float get_point_size() const; @@ -57,6 +60,9 @@ PUBLISHED: INLINE bool set_scale_factor(float scale_factor); INLINE float get_scale_factor() const; + INLINE float get_line_height() const; + INLINE float get_space_advance() const; + INLINE void set_texture_margin(int texture_margin); INLINE int get_texture_margin() const; INLINE void set_poly_margin(float poly_margin); @@ -91,19 +97,11 @@ public: private: void initialize(); void update_filters(); - bool reset_scale(); DynamicTextGlyph *make_glyph(int glyph_index); void copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph); - void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image); void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph); DynamicTextGlyph *slot_glyph(int x_size, int y_size); - static void initialize_ft_library(); - - float _point_size; - float _tex_pixels_per_unit; - float _scale_factor; - float _font_pixels_per_unit; int _texture_margin; float _poly_margin; int _page_x_size, _page_y_size; @@ -129,17 +127,6 @@ private: typedef pvector< PT(DynamicTextGlyph) > EmptyGlyphs; EmptyGlyphs _empty_glyphs; - FT_Face _face; - - // This string is used to hold the data read from the font file in - // vfs mode. Since the FreeType library keeps pointers into this - // data, we have to keep it around. - string _raw_font_data; - - static FT_Library _ft_library; - static bool _ft_initialized; - static bool _ft_ok; - public: static TypeHandle get_class_type() { return _type_handle; @@ -160,6 +147,8 @@ private: friend class TextNode; }; +INLINE ostream &operator << (ostream &out, const DynamicTextFont &dtf); + #include "dynamicTextFont.I" #endif // HAVE_FREETYPE