add pnmtext library

This commit is contained in:
David Rose 2003-09-09 19:26:25 +00:00
parent 276ab8bfb9
commit 6409ff6b7f
12 changed files with 766 additions and 338 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 <ft2build.h>
#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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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", "");

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

@ -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