|
@@ -17,19 +17,16 @@
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "eggMakeFont.h"
|
|
#include "eggMakeFont.h"
|
|
|
-#include "charBitmap.h"
|
|
|
|
|
-#include "charPlacement.h"
|
|
|
|
|
-#include "pkFontFile.h"
|
|
|
|
|
-
|
|
|
|
|
-#include "string_utils.h"
|
|
|
|
|
|
|
+#include "rangeIterator.h"
|
|
|
|
|
+#include "pnmTextMaker.h"
|
|
|
|
|
+#include "pnmTextGlyph.h"
|
|
|
#include "eggGroup.h"
|
|
#include "eggGroup.h"
|
|
|
|
|
+#include "eggPoint.h"
|
|
|
|
|
+#include "eggPolygon.h"
|
|
|
#include "eggTexture.h"
|
|
#include "eggTexture.h"
|
|
|
#include "eggVertexPool.h"
|
|
#include "eggVertexPool.h"
|
|
|
-#include "eggPolygon.h"
|
|
|
|
|
-#include "eggPoint.h"
|
|
|
|
|
-#include "pointerTo.h"
|
|
|
|
|
-
|
|
|
|
|
-#include <math.h>
|
|
|
|
|
|
|
+#include "eggVertex.h"
|
|
|
|
|
+#include "string_utils.h"
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: EggMakeFont::Constructor
|
|
// Function: EggMakeFont::Constructor
|
|
@@ -39,36 +36,31 @@
|
|
|
EggMakeFont::
|
|
EggMakeFont::
|
|
|
EggMakeFont() : EggWriter(true, false) {
|
|
EggMakeFont() : EggWriter(true, false) {
|
|
|
set_program_description
|
|
set_program_description
|
|
|
- ("egg-mkfont reads a rasterized font stored in a "
|
|
|
|
|
- "Metafont/TeX pk file format, and generates an egg "
|
|
|
|
|
- "file and corresponding texture map that can be used with "
|
|
|
|
|
- "Panda's TextNode object to render text in the font.\n\n"
|
|
|
|
|
-
|
|
|
|
|
- "The input pk file can come from any of a number of sources. "
|
|
|
|
|
- "It may be a Metafont font that ships with TeX, or it may "
|
|
|
|
|
- "have been converted from a PostScript font, or you can use "
|
|
|
|
|
- "freetype (see www.freetype.org) to convert a TTF font to pk.\n\n");
|
|
|
|
|
-
|
|
|
|
|
|
|
+ ("egg-mkfont uses the FreeType library to generate an egg file "
|
|
|
|
|
+ "and a series of texture images from a font file "
|
|
|
|
|
+ "input, such as a TTF file. The resulting egg file "
|
|
|
|
|
+ "can be loaded in Panda as a StaticTextFont object for "
|
|
|
|
|
+ "rendering text, even if FreeType is not compiled into "
|
|
|
|
|
+ "the executing Panda.\n\n"
|
|
|
|
|
+
|
|
|
|
|
+ "It is strongly recommended that the resulting egg file "
|
|
|
|
|
+ "be subsequently passed through egg-palettize to consolidate the many "
|
|
|
|
|
+ "generated texture images into a single texture image to "
|
|
|
|
|
+ "improve rendering performance. This can also reduce the "
|
|
|
|
|
+ "texture images to achieve antialiasing.");
|
|
|
|
|
|
|
|
clear_runlines();
|
|
clear_runlines();
|
|
|
- add_runline("[opts] -o output.egg file.pk");
|
|
|
|
|
- add_runline("[opts] file.pk output.egg");
|
|
|
|
|
-
|
|
|
|
|
- add_option
|
|
|
|
|
- ("i", "filename", 0,
|
|
|
|
|
- "Name of the texture image to write. The default if this is omitted "
|
|
|
|
|
- "is based on the name of the egg file.",
|
|
|
|
|
- &EggMakeFont::dispatch_filename, NULL, &_output_image_filename);
|
|
|
|
|
|
|
+ add_runline("[opts] -o output.egg font");
|
|
|
|
|
+ add_runline("[opts] font output.egg");
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("c", "num", 0,
|
|
|
|
|
- "Specifies the number of channels of the output image. This should "
|
|
|
|
|
- "either 1, 2, 3, or 4. If the number is 1 or 3 a grayscale image is "
|
|
|
|
|
- "generated, with the text in white on black. If the number is 2 or 4 "
|
|
|
|
|
- "a completely white image is generated, with the text in the alpha "
|
|
|
|
|
- "channel. This parameter may also be specified as the third number "
|
|
|
|
|
- "on -d, below. The default is 1.",
|
|
|
|
|
- &EggMakeFont::dispatch_int, NULL, &_output_zsize);
|
|
|
|
|
|
|
+ ("i", "pattern", 0,
|
|
|
|
|
+ "The pattern to be used to generate the texture images. This string "
|
|
|
|
|
+ "will be passed to sprintf to generate the actual file name; it "
|
|
|
|
|
+ "should contain the string %d or %x (or some variant such as %03d) "
|
|
|
|
|
+ "which will be filled in with the Unicode number of each symbol. "
|
|
|
|
|
+ "If it is omitted, the default is based on the name of the egg file.",
|
|
|
|
|
+ &EggMakeFont::dispatch_string, NULL, &_output_image_pattern);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
("fg", "r,g,b[,a]", 0,
|
|
("fg", "r,g,b[,a]", 0,
|
|
@@ -80,115 +72,81 @@ EggMakeFont() : EggWriter(true, false) {
|
|
|
add_option
|
|
add_option
|
|
|
("bg", "r,g,b[,a]", 0,
|
|
("bg", "r,g,b[,a]", 0,
|
|
|
"Specifies the background color of the generated texture map. The "
|
|
"Specifies the background color of the generated texture map. The "
|
|
|
- "default is transparent black: 0,0,0,0, which allows the text to be "
|
|
|
|
|
|
|
+ "default is transparent: 1,1,1,0, which allows the text to be "
|
|
|
"visible against any color background by placing a polygon of a "
|
|
"visible against any color background by placing a polygon of a "
|
|
|
- "suitable color behind it.",
|
|
|
|
|
|
|
+ "suitable color behind it. If the alpha component of either -fg "
|
|
|
|
|
+ "or -bg is not 1, the generated texture images will include an "
|
|
|
|
|
+ "alpha component; if both colors specify an alpha component of 1 "
|
|
|
|
|
+ "(or do not specify an alpha compenent), then the generated images "
|
|
|
|
|
+ "will not include an alpha component.",
|
|
|
&EggMakeFont::dispatch_color, NULL, &_bg[0]);
|
|
&EggMakeFont::dispatch_color, NULL, &_bg[0]);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("d", "x,y[,c]", 0,
|
|
|
|
|
- "Dimensions in pixels of the texture image, with an optional number of "
|
|
|
|
|
- "channels. Normally, you should not specify this parameter, as "
|
|
|
|
|
- "egg-mkfont will choose an image size that yields a scale factor "
|
|
|
|
|
- "between 2.5 and 4, which leads to good antialiased letters. If you "
|
|
|
|
|
- "want a larger or smaller image, you could force the image size "
|
|
|
|
|
- "with this parameter, but it would probably yield better results if "
|
|
|
|
|
- "you re-rasterized the font at a different DPI instead.",
|
|
|
|
|
- &EggMakeFont::dispatch_dimensions, &_got_output_size);
|
|
|
|
|
-
|
|
|
|
|
- add_option
|
|
|
|
|
- ("sf", "factor", 0,
|
|
|
|
|
-
|
|
|
|
|
- "The scale factor of the generated image. This is the factor by which "
|
|
|
|
|
- "the font image is generated oversized, then reduced to its final size, "
|
|
|
|
|
- "to generate antialiased letters. Values between 2.5 and 4 are generally "
|
|
|
|
|
- "best. Normally, you should not specify this parameter, as egg-mkfont "
|
|
|
|
|
- "will choose a scale factor automatically to best fit the letters in the "
|
|
|
|
|
- "space available. If you do specify a scale factor with -sf, you must "
|
|
|
|
|
- "also specify an image size with -d.",
|
|
|
|
|
- &EggMakeFont::dispatch_double, &_got_scale_factor, &_scale_factor);
|
|
|
|
|
-
|
|
|
|
|
- add_option
|
|
|
|
|
- ("nr", "", 0,
|
|
|
|
|
- "No reduce. After the oversized image is generated, rather than reducing "
|
|
|
|
|
- "it to its final size, just leave it as it is, and assume the user will "
|
|
|
|
|
- "reduce it later. This may be desireable if you intend to adjust the "
|
|
|
|
|
- "letters by hand in some way after the image is generated.",
|
|
|
|
|
- &EggMakeFont::dispatch_none, &_no_reduce);
|
|
|
|
|
|
|
+ ("chars", "range", 0,
|
|
|
|
|
+ "Specifies the characters of the font that are used. The range "
|
|
|
|
|
+ "specification may include combinations of decimal or hex unicode "
|
|
|
|
|
+ "values (where hex values are identified with a leading 0x), separated "
|
|
|
|
|
+ "by commas and hyphens to indicate ranges, e.g. '32-126,0xfa0-0xfff'. "
|
|
|
|
|
+ "It also may specify ranges of ASCII characters by enclosing them "
|
|
|
|
|
+ "within square brackets, e.g. '[A-Za-z0-9]'. If this is not specified, "
|
|
|
|
|
+ "the default is the set of ASCII characters.",
|
|
|
|
|
+ &EggMakeFont::dispatch_range, NULL, &_range);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("g", "radius", 0,
|
|
|
|
|
- "The radius of the Gaussian filter used to antialias the letters. [1.2]",
|
|
|
|
|
- &EggMakeFont::dispatch_double, NULL, &_gaussian_radius);
|
|
|
|
|
|
|
+ ("ppu", "pixels", 0,
|
|
|
|
|
+ "Specify the pixels per unit. This is the number of pixels in the "
|
|
|
|
|
+ "generated texture map that are used for each onscreen unit (or each "
|
|
|
|
|
+ "10 points of font; see -ps). Setting this number larger results in "
|
|
|
|
|
+ "an easier-to-read font, but at the cost of more texture memory.",
|
|
|
|
|
+ &EggMakeFont::dispatch_double, NULL, &_pixels_per_unit);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("b", "n", 0,
|
|
|
|
|
- "The number of buffer pixels between two adjacent characters in "
|
|
|
|
|
- "the palette image. [4.0]",
|
|
|
|
|
- &EggMakeFont::dispatch_double, NULL, &_buffer_pixels);
|
|
|
|
|
|
|
+ ("ps", "size", 0,
|
|
|
|
|
+ "Specify the point size of the resulting font. This controls the "
|
|
|
|
|
+ "apparent size of the font when it is rendered onscreen. By convention, "
|
|
|
|
|
+ "a 10 point font is 1 screen unit high.",
|
|
|
|
|
+ &EggMakeFont::dispatch_double, NULL, &_point_size);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("B", "n", 0,
|
|
|
|
|
|
|
+ ("bp", "n", 0,
|
|
|
"The number of extra pixels around a single character in the "
|
|
"The number of extra pixels around a single character in the "
|
|
|
"generated polygon. [1.0]",
|
|
"generated polygon. [1.0]",
|
|
|
- &EggMakeFont::dispatch_double, NULL, &_poly_pixels);
|
|
|
|
|
-
|
|
|
|
|
- add_option
|
|
|
|
|
- ("ds", "size", 0,
|
|
|
|
|
- "Specify the design size of the resulting font. The design size of "
|
|
|
|
|
- "a font is the height of a typical capital letter; it's the approximate "
|
|
|
|
|
- "height of a line of text. This sets the size of the polygons "
|
|
|
|
|
- "accordingly. [1.0]",
|
|
|
|
|
- &EggMakeFont::dispatch_double, NULL, &_ds);
|
|
|
|
|
|
|
+ &EggMakeFont::dispatch_double, NULL, &_poly_margin);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("scale", "size", 0,
|
|
|
|
|
- "Specify an additional scale to the font, without changing its design "
|
|
|
|
|
- "size. This makes the letters larger (or smaller) without changing "
|
|
|
|
|
- "the spacing between lines. Usually you should use -ds instead of "
|
|
|
|
|
- "-scale to change the size of the text.",
|
|
|
|
|
- &EggMakeFont::dispatch_double, NULL, &_scale);
|
|
|
|
|
|
|
+ ("bt", "n", 0,
|
|
|
|
|
+ "The number of extra pixels around each character in the texture map.",
|
|
|
|
|
+ &EggMakeFont::dispatch_double, NULL, &_tex_margin);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("all", "", 0,
|
|
|
|
|
- "Extract all the characters in the font. Normally, only the "
|
|
|
|
|
- "ASCII characters in the range 33 .. 127 are extracted.",
|
|
|
|
|
- &EggMakeFont::dispatch_none, &_get_all);
|
|
|
|
|
-
|
|
|
|
|
- add_option
|
|
|
|
|
- ("only", "'chars'", 0,
|
|
|
|
|
- "Extract *only* the indicated characters from the font. The parameter "
|
|
|
|
|
- "should be a quoted string of letters and symbols that are to be "
|
|
|
|
|
- "extracted. If the hyphen appears, it indicates a range of characters, "
|
|
|
|
|
- "e.g. A-Z to extract all the capital letters. If the hyphen appears "
|
|
|
|
|
- "as the first or last character it loses its special meaning.",
|
|
|
|
|
- &EggMakeFont::dispatch_string, NULL, &_only_chars);
|
|
|
|
|
-
|
|
|
|
|
- add_option
|
|
|
|
|
- ("sc", "", 0,
|
|
|
|
|
- "Small caps: generate lowercase letters as small capitals. This "
|
|
|
|
|
- "allows the lowercase and capital letters to share the same space "
|
|
|
|
|
- "on the texture map.",
|
|
|
|
|
- &EggMakeFont::dispatch_none, &_small_caps);
|
|
|
|
|
|
|
+ ("sf", "factor", 0,
|
|
|
|
|
+ "The scale factor of the generated image. This is the factor by which "
|
|
|
|
|
+ "the font image is generated oversized, then reduced to its final size, "
|
|
|
|
|
+ "to improve antialiasing. If the specified font contains one "
|
|
|
|
|
+ "or more fixed-size fonts instead of a scalable font, the scale factor "
|
|
|
|
|
+ "may be automatically adjusted as necessary to scale the closest-"
|
|
|
|
|
+ "matching font to the desired pixel size.",
|
|
|
|
|
+ &EggMakeFont::dispatch_double, NULL, &_scale_factor);
|
|
|
|
|
|
|
|
add_option
|
|
add_option
|
|
|
- ("scs", "", 0,
|
|
|
|
|
- "Small caps scale: the ratio of the size of a lowercase letter to "
|
|
|
|
|
- "its uppercase equivalent, when -sc is in effect. [0.8]",
|
|
|
|
|
- &EggMakeFont::dispatch_double, NULL, &_small_caps_scale);
|
|
|
|
|
|
|
+ ("face", "index", 0,
|
|
|
|
|
+ "Specify the face index of the particular face within the font file "
|
|
|
|
|
+ "to use. Some font files contain multiple faces, indexed beginning "
|
|
|
|
|
+ "at 0. The default is face 0.",
|
|
|
|
|
+ &EggMakeFont::dispatch_int, NULL, &_face_index);
|
|
|
|
|
|
|
|
_fg.set(1.0, 1.0, 1.0, 1.0);
|
|
_fg.set(1.0, 1.0, 1.0, 1.0);
|
|
|
- _bg.set(0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
- _output_xsize = 256;
|
|
|
|
|
- _output_ysize = 256;
|
|
|
|
|
- _output_zsize = 1;
|
|
|
|
|
- _buffer_pixels = 4.0;
|
|
|
|
|
- _poly_pixels = 1.0;
|
|
|
|
|
- _scale_factor = 3.0;
|
|
|
|
|
- _gaussian_radius = 1.2;
|
|
|
|
|
- _ds = 1.0;
|
|
|
|
|
- _scale = 1.0;
|
|
|
|
|
- _small_caps_scale = 0.8;
|
|
|
|
|
|
|
+ _bg.set(1.0, 1.0, 1.0, 0.0);
|
|
|
|
|
+ _pixels_per_unit = 30.0;
|
|
|
|
|
+ _point_size = 10.0;
|
|
|
|
|
+ _poly_margin = 1.0;
|
|
|
|
|
+ _tex_margin = 2.0;
|
|
|
|
|
+ _scale_factor = 2.0;
|
|
|
|
|
+ _face_index = 0;
|
|
|
|
|
+
|
|
|
|
|
+ _text_maker = NULL;
|
|
|
|
|
+ _vpool = NULL;
|
|
|
|
|
+ _group = NULL;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -203,7 +161,7 @@ EggMakeFont() : EggWriter(true, false) {
|
|
|
bool EggMakeFont::
|
|
bool EggMakeFont::
|
|
|
handle_args(ProgramBase::Args &args) {
|
|
handle_args(ProgramBase::Args &args) {
|
|
|
if (args.empty()) {
|
|
if (args.empty()) {
|
|
|
- nout << "Must specify name of pk file on command line.\n";
|
|
|
|
|
|
|
+ nout << "Must specify name of font file on command line.\n";
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -213,73 +171,100 @@ handle_args(ProgramBase::Args &args) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: EggMakeFont::dispatch_dimensions
|
|
|
|
|
-// Access: Protected, Static
|
|
|
|
|
-// Description: Reads the dimensions of the output image and stores
|
|
|
|
|
-// them in _output_[xyz]size.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-bool EggMakeFont::
|
|
|
|
|
-dispatch_dimensions(ProgramBase *self, const string &opt, const string &arg, void *) {
|
|
|
|
|
- EggSingleBase *base = (EggSingleBase *)self;
|
|
|
|
|
- EggMakeFont *me = (EggMakeFont *)base->as_writer();
|
|
|
|
|
- return me->ns_dispatch_dimensions(opt, arg);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::ns_dispatch_dimensions
|
|
|
|
|
-// Access: Protected
|
|
|
|
|
-// Description: Reads the dimensions of the output image and stores
|
|
|
|
|
-// them in _output_[xyz]size.
|
|
|
|
|
|
|
+// Function: EggMakeFont::run
|
|
|
|
|
+// Access: Public
|
|
|
|
|
+// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-bool EggMakeFont::
|
|
|
|
|
-ns_dispatch_dimensions(const string &opt, const string &arg) {
|
|
|
|
|
- vector_string words;
|
|
|
|
|
- tokenize(arg, words, ",");
|
|
|
|
|
-
|
|
|
|
|
- bool okflag = false;
|
|
|
|
|
- if (words.size() == 2) {
|
|
|
|
|
- okflag =
|
|
|
|
|
- string_to_int(words[0], _output_xsize) &&
|
|
|
|
|
- string_to_int(words[1], _output_ysize);
|
|
|
|
|
-
|
|
|
|
|
- } else if (words.size() == 3) {
|
|
|
|
|
- okflag =
|
|
|
|
|
- string_to_int(words[0], _output_xsize) &&
|
|
|
|
|
- string_to_int(words[1], _output_ysize) &&
|
|
|
|
|
- string_to_int(words[2], _output_zsize);
|
|
|
|
|
|
|
+void EggMakeFont::
|
|
|
|
|
+run() {
|
|
|
|
|
+ _text_maker = new PNMTextMaker(_input_font_filename, _face_index);
|
|
|
|
|
+ if (!_text_maker->is_valid()) {
|
|
|
|
|
+ exit(1);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if (!okflag) {
|
|
|
|
|
- nout << "-" << opt
|
|
|
|
|
- << " requires two or three integers separated by commas.\n";
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ _text_maker->set_point_size(_point_size);
|
|
|
|
|
+ _text_maker->set_scale_factor(_scale_factor);
|
|
|
|
|
+ _text_maker->set_pixels_per_unit(_pixels_per_unit);
|
|
|
|
|
+
|
|
|
|
|
+ if (_range.is_empty()) {
|
|
|
|
|
+ // If there's no specified range, the default is the entire ASCII
|
|
|
|
|
+ // set.
|
|
|
|
|
+ _range.add_range(0x20, 0x7e);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (_output_image_pattern.empty()) {
|
|
|
|
|
+ // Create a default texture filename pattern.
|
|
|
|
|
+ _output_image_pattern = _input_font_filename.get_fullpath_wo_extension() + "%03d.rgb";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ // Figure out how many channels we need based on the foreground and
|
|
|
|
|
+ // background colors.
|
|
|
|
|
+ bool needs_alpha = (_fg[3] != 1.0 || _bg[3] != 1.0);
|
|
|
|
|
+ bool needs_color = (_fg[0] != _fg[1] || _fg[1] != _fg[2] ||
|
|
|
|
|
+ _bg[0] != _bg[1] || _bg[1] != _bg[2]);
|
|
|
|
|
+ if (needs_alpha) {
|
|
|
|
|
+ if (needs_color) {
|
|
|
|
|
+ _num_channels = 4;
|
|
|
|
|
+ _format = EggTexture::F_rgba;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (_fg[0] == 1.0 && _bg[0] == 1.0) {
|
|
|
|
|
+ // A special case: we only need an alpha channel. Copy the
|
|
|
|
|
+ // alpha data into the color channels so we can write out a
|
|
|
|
|
+ // one-channel image.
|
|
|
|
|
+ _fg[0] = _fg[1] = _fg[2] = _fg[3];
|
|
|
|
|
+ _bg[0] = _bg[1] = _bg[2] = _bg[3];
|
|
|
|
|
+ _num_channels = 1;
|
|
|
|
|
+ _format = EggTexture::F_alpha;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ _num_channels = 2;
|
|
|
|
|
+ _format = EggTexture::F_luminance_alpha;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (needs_color) {
|
|
|
|
|
+ _num_channels = 3;
|
|
|
|
|
+ _format = EggTexture::F_rgb;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ _num_channels = 1;
|
|
|
|
|
+ _format = EggTexture::F_luminance;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::get_uv
|
|
|
|
|
-// Access: Private
|
|
|
|
|
-// Description: Given the X, Y coordinates of a particular pixel on
|
|
|
|
|
-// the image, return the corresponding UV coordinates.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-TexCoordd EggMakeFont::
|
|
|
|
|
-get_uv(double x, double y) {
|
|
|
|
|
- return TexCoordd(x / (double)_working_xsize,
|
|
|
|
|
- ((double)_working_ysize - y) / (double)_working_ysize);
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ _group = new EggGroup();
|
|
|
|
|
+ _data.add_child(_group);
|
|
|
|
|
+
|
|
|
|
|
+ _vpool = new EggVertexPool("vpool");
|
|
|
|
|
+ _group->add_child(_vpool);
|
|
|
|
|
|
|
|
|
|
+ // Make the group a sequence, as a convenience. If we view the
|
|
|
|
|
+ // egg file directly we can see all the characters one at a time.
|
|
|
|
|
+ _group->set_switch_flag(true);
|
|
|
|
|
+ _group->set_switch_fps(2.0);
|
|
|
|
|
+
|
|
|
|
|
+ // Also create an egg group indicating the font's design size.
|
|
|
|
|
+ EggGroup *ds_group = new EggGroup("ds");
|
|
|
|
|
+ _group->add_child(ds_group);
|
|
|
|
|
+ EggVertex *vtx = make_vertex(LPoint2d(0.0, _text_maker->get_line_height()));
|
|
|
|
|
+ EggPoint *point = new EggPoint;
|
|
|
|
|
+ ds_group->add_child(point);
|
|
|
|
|
+ point->add_vertex(vtx);
|
|
|
|
|
+
|
|
|
|
|
+ // Finally, add the characters, one at a time.
|
|
|
|
|
+ RangeIterator ri(_range);
|
|
|
|
|
+ do {
|
|
|
|
|
+ add_character(ri.get_code());
|
|
|
|
|
+ } while (ri.next());
|
|
|
|
|
+
|
|
|
|
|
+ write_egg_file();
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: EggMakeFont::get_xy
|
|
|
|
|
-// Access: Private
|
|
|
|
|
-// Description: Given X, Y coordinates in pixels, scale to unit
|
|
|
|
|
-// coordinates for the character's geometry.
|
|
|
|
|
|
|
+// Function: EggMakeFont::dispatch_range
|
|
|
|
|
+// Access: Private, Static
|
|
|
|
|
+// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-LPoint2d EggMakeFont::
|
|
|
|
|
-get_xy(double x, double y) {
|
|
|
|
|
- return LPoint2d(x / (_font->get_hppp() * _ppu), -y / (_font->get_vppp() * _ppu));
|
|
|
|
|
|
|
+bool EggMakeFont::
|
|
|
|
|
+dispatch_range(const string &, const string &arg, void *var) {
|
|
|
|
|
+ RangeDescription *ip = (RangeDescription *)var;
|
|
|
|
|
+ return ip->parse_parameter(arg);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
@@ -296,551 +281,147 @@ make_vertex(const LPoint2d &xy) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: EggMakeFont::copy_character
|
|
|
|
|
|
|
+// Function: EggMakeFont::add_character
|
|
|
// Access: Private
|
|
// Access: Private
|
|
|
-// Description: Copy the indicated character image to its home on the
|
|
|
|
|
-// bitmap and generate egg structures for it.
|
|
|
|
|
|
|
+// Description: Generates the indicated character and adds it to the
|
|
|
|
|
+// font description.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void EggMakeFont::
|
|
void EggMakeFont::
|
|
|
-copy_character(const CharPlacement &pl) {
|
|
|
|
|
- const CharBitmap *bm = pl._bm;
|
|
|
|
|
- int xp = pl._x;
|
|
|
|
|
- int yp = pl._y;
|
|
|
|
|
-
|
|
|
|
|
- int character = bm->_character;
|
|
|
|
|
- int hoff = bm->_hoff;
|
|
|
|
|
- int voff = bm->_voff;
|
|
|
|
|
- double dx = bm->_dx;
|
|
|
|
|
- double dy = bm->_dy;
|
|
|
|
|
- int width = bm->get_width();
|
|
|
|
|
- int height = bm->get_height();
|
|
|
|
|
-
|
|
|
|
|
- // Copy the character into the image.
|
|
|
|
|
- if (_output_image.has_alpha()) {
|
|
|
|
|
- for (int y = 0; y < height; y++) {
|
|
|
|
|
- for (int x = 0; x < width; x++) {
|
|
|
|
|
- if (bm->_block[y][x]) {
|
|
|
|
|
- _output_image.set_xel(xp + x, yp + y, _fg[0], _fg[1], _fg[2]);
|
|
|
|
|
- _output_image.set_alpha(xp + x, yp + y, _fg[3]);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- for (int y = 0; y < height; y++) {
|
|
|
|
|
- for (int x = 0; x < width; x++) {
|
|
|
|
|
- if (bm->_block[y][x]) {
|
|
|
|
|
- _output_image.set_xel(xp + x, yp + y, _fg[0], _fg[1], _fg[2]);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+add_character(int code) {
|
|
|
|
|
+ PNMTextGlyph *glyph = _text_maker->get_glyph(code);
|
|
|
|
|
+ if (glyph == (PNMTextGlyph *)NULL) {
|
|
|
|
|
+ nout << "No definition in font for character " << code << ".\n";
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Create the polygon that will have the character mapped onto it.
|
|
|
|
|
-
|
|
|
|
|
- // b is the number of pixels bigger than the character in each
|
|
|
|
|
- // direction the polygon will be. It needs to be larger than zero
|
|
|
|
|
- // just because when we filter the image down, we end up with some
|
|
|
|
|
- // antialiasing blur that extends beyond the original borders of the
|
|
|
|
|
- // character. But it shouldn't be too large, because we don't want
|
|
|
|
|
- // the neighboring polygons of a word to overlap any more than they
|
|
|
|
|
- // need to.
|
|
|
|
|
-
|
|
|
|
|
- double b = _working_poly_pixels;
|
|
|
|
|
-
|
|
|
|
|
- TexCoordd uv_ul = get_uv(xp - b, yp - b);
|
|
|
|
|
- TexCoordd uv_lr = get_uv(xp + width + b, yp + height + b);
|
|
|
|
|
- LPoint2d xy_ul = get_xy(-hoff - b, -voff - b);
|
|
|
|
|
- LPoint2d xy_lr = get_xy(-hoff + width + b, -voff + height + b);
|
|
|
|
|
- LPoint2d dp = get_xy(dx, dy);
|
|
|
|
|
-
|
|
|
|
|
- EggVertex *v1 = make_vertex(LPoint2d(xy_ul[0], xy_lr[1]));
|
|
|
|
|
- EggVertex *v2 = make_vertex(LPoint2d(xy_lr[0], xy_lr[1]));
|
|
|
|
|
- EggVertex *v3 = make_vertex(LPoint2d(xy_lr[0], xy_ul[1]));
|
|
|
|
|
- EggVertex *v4 = make_vertex(LPoint2d(xy_ul[0], xy_ul[1]));
|
|
|
|
|
|
|
+ make_geom(glyph, code);
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- v1->set_uv(TexCoordd(uv_ul[0], uv_lr[1]));
|
|
|
|
|
- v2->set_uv(TexCoordd(uv_lr[0], uv_lr[1]));
|
|
|
|
|
- v3->set_uv(TexCoordd(uv_lr[0], uv_ul[1]));
|
|
|
|
|
- v4->set_uv(TexCoordd(uv_ul[0], uv_ul[1]));
|
|
|
|
|
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: EggMakeFont::make_geom
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: Creates the actual geometry for the glyph.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void EggMakeFont::
|
|
|
|
|
+make_geom(PNMTextGlyph *glyph, int character) {
|
|
|
// Create an egg group to hold the polygon.
|
|
// Create an egg group to hold the polygon.
|
|
|
string group_name = format_string(character);
|
|
string group_name = format_string(character);
|
|
|
- PT(EggGroup) group = new EggGroup(group_name);
|
|
|
|
|
- _egg_defs[character] = group;
|
|
|
|
|
-
|
|
|
|
|
- EggPolygon *poly = new EggPolygon();
|
|
|
|
|
- group->add_child(poly);
|
|
|
|
|
- poly->set_texture(_tref);
|
|
|
|
|
-
|
|
|
|
|
- poly->add_vertex(v1);
|
|
|
|
|
- poly->add_vertex(v2);
|
|
|
|
|
- poly->add_vertex(v3);
|
|
|
|
|
- poly->add_vertex(v4);
|
|
|
|
|
-
|
|
|
|
|
- // Now create a single point where the origin of the next character
|
|
|
|
|
- // will be.
|
|
|
|
|
-
|
|
|
|
|
- EggVertex *v0 = make_vertex(dp);
|
|
|
|
|
- EggPoint *point = new EggPoint;
|
|
|
|
|
- group->add_child(point);
|
|
|
|
|
- point->add_vertex(v0);
|
|
|
|
|
-
|
|
|
|
|
- if (_small_caps && isupper(character)) {
|
|
|
|
|
- // Now create the polygon representing the lowercase letter, for
|
|
|
|
|
- // small caps mode. This uses the same texture on a smaller
|
|
|
|
|
- // polygon.
|
|
|
|
|
- xy_ul *= _small_caps_scale;
|
|
|
|
|
- xy_lr *= _small_caps_scale;
|
|
|
|
|
- dp *= _small_caps_scale;
|
|
|
|
|
- character = tolower(character);
|
|
|
|
|
-
|
|
|
|
|
- EggVertex *v1 = make_vertex(LPoint2d(xy_ul[0], xy_lr[1]));
|
|
|
|
|
- EggVertex *v2 = make_vertex(LPoint2d(xy_lr[0], xy_lr[1]));
|
|
|
|
|
- EggVertex *v3 = make_vertex(LPoint2d(xy_lr[0], xy_ul[1]));
|
|
|
|
|
- EggVertex *v4 = make_vertex(LPoint2d(xy_ul[0], xy_ul[1]));
|
|
|
|
|
-
|
|
|
|
|
- v1->set_uv(TexCoordd(uv_ul[0], uv_lr[1]));
|
|
|
|
|
- v2->set_uv(TexCoordd(uv_lr[0], uv_lr[1]));
|
|
|
|
|
- v3->set_uv(TexCoordd(uv_lr[0], uv_ul[1]));
|
|
|
|
|
- v4->set_uv(TexCoordd(uv_ul[0], uv_ul[1]));
|
|
|
|
|
-
|
|
|
|
|
- string group_name = format_string(character);
|
|
|
|
|
- PT(EggGroup) group = new EggGroup(group_name);
|
|
|
|
|
- _egg_defs[character] = group;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ EggGroup *group = new EggGroup(group_name);
|
|
|
|
|
+ _group->add_child(group);
|
|
|
|
|
+
|
|
|
|
|
+ if (glyph->get_width() != 0 && glyph->get_height() != 0) {
|
|
|
|
|
+ int bitmap_top = glyph->get_top();
|
|
|
|
|
+ int bitmap_left = glyph->get_left();
|
|
|
|
|
+ double tex_x_size = glyph->get_width();
|
|
|
|
|
+ double tex_y_size = glyph->get_height();
|
|
|
|
|
+
|
|
|
|
|
+ double poly_margin = _poly_margin;
|
|
|
|
|
+ double x_origin = _tex_margin;
|
|
|
|
|
+ double y_origin = _tex_margin;
|
|
|
|
|
+ double page_y_size = tex_y_size + _tex_margin * 2;
|
|
|
|
|
+ double page_x_size = tex_x_size + _tex_margin * 2;
|
|
|
|
|
+
|
|
|
|
|
+ // Determine the corners of the rectangle in geometric units.
|
|
|
|
|
+ double tex_poly_margin = poly_margin / _pixels_per_unit;
|
|
|
|
|
+ double origin_y = bitmap_top / _pixels_per_unit;
|
|
|
|
|
+ double origin_x = bitmap_left / _pixels_per_unit;
|
|
|
|
|
+ double top = origin_y + tex_poly_margin;
|
|
|
|
|
+ double left = origin_x - tex_poly_margin;
|
|
|
|
|
+ double bottom = origin_y - tex_y_size / _pixels_per_unit - tex_poly_margin;
|
|
|
|
|
+ double right = origin_x + tex_x_size / _pixels_per_unit + tex_poly_margin;
|
|
|
|
|
+
|
|
|
|
|
+ // And the corresponding corners in UV units.
|
|
|
|
|
+ double uv_top = 1.0f - (double)(y_origin - poly_margin) / page_y_size;
|
|
|
|
|
+ double uv_left = (double)(x_origin - poly_margin) / page_x_size;
|
|
|
|
|
+ double uv_bottom = 1.0f - (double)(y_origin + poly_margin + tex_y_size) / page_y_size;
|
|
|
|
|
+ double uv_right = (double)(x_origin + poly_margin + tex_x_size) / page_x_size;
|
|
|
|
|
+
|
|
|
|
|
+ // Create the vertices for the polygon.
|
|
|
|
|
+ EggVertex *v1 = make_vertex(LPoint2d(left, bottom));
|
|
|
|
|
+ EggVertex *v2 = make_vertex(LPoint2d(right, bottom));
|
|
|
|
|
+ EggVertex *v3 = make_vertex(LPoint2d(right, top));
|
|
|
|
|
+ EggVertex *v4 = make_vertex(LPoint2d(left, top));
|
|
|
|
|
+
|
|
|
|
|
+ v1->set_uv(TexCoordd(uv_left, uv_bottom));
|
|
|
|
|
+ v2->set_uv(TexCoordd(uv_right, uv_bottom));
|
|
|
|
|
+ v3->set_uv(TexCoordd(uv_right, uv_top));
|
|
|
|
|
+ v4->set_uv(TexCoordd(uv_left, uv_top));
|
|
|
|
|
+
|
|
|
EggPolygon *poly = new EggPolygon();
|
|
EggPolygon *poly = new EggPolygon();
|
|
|
group->add_child(poly);
|
|
group->add_child(poly);
|
|
|
- poly->set_texture(_tref);
|
|
|
|
|
|
|
+ poly->set_texture(get_tref(glyph, character));
|
|
|
|
|
|
|
|
poly->add_vertex(v1);
|
|
poly->add_vertex(v1);
|
|
|
poly->add_vertex(v2);
|
|
poly->add_vertex(v2);
|
|
|
poly->add_vertex(v3);
|
|
poly->add_vertex(v3);
|
|
|
poly->add_vertex(v4);
|
|
poly->add_vertex(v4);
|
|
|
-
|
|
|
|
|
- EggVertex *v0 = make_vertex(dp);
|
|
|
|
|
- EggPoint *point = new EggPoint;
|
|
|
|
|
- group->add_child(point);
|
|
|
|
|
- point->add_vertex(v0);
|
|
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::consider_scale_factor
|
|
|
|
|
-// Access: Private
|
|
|
|
|
-// Description: Attempts to place all of the characters on an image
|
|
|
|
|
-// of the indicated size (scaled up from the output
|
|
|
|
|
-// image size by scale_factor in each dimension).
|
|
|
|
|
-// Returns true if all the characters fit, or false if
|
|
|
|
|
-// the image was too small. In either case, leaves
|
|
|
|
|
-// _scale_factor and _working_* set to reflect the
|
|
|
|
|
-// chosen scale factor.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-bool EggMakeFont::
|
|
|
|
|
-consider_scale_factor(double scale_factor) {
|
|
|
|
|
- _scale_factor = scale_factor;
|
|
|
|
|
- _working_xsize = (int)floor(_output_xsize * _scale_factor + 0.5);
|
|
|
|
|
- _working_ysize = (int)floor(_output_ysize * _scale_factor + 0.5);
|
|
|
|
|
- _working_buffer_pixels = (int)floor(_buffer_pixels * _scale_factor + 0.5);
|
|
|
|
|
-
|
|
|
|
|
- _layout.reset(_working_xsize, _working_ysize, _working_buffer_pixels);
|
|
|
|
|
-
|
|
|
|
|
- bool ok = true;
|
|
|
|
|
- int num_chars = _font->get_num_chars();
|
|
|
|
|
- for (int i = 0; i < num_chars; i++) {
|
|
|
|
|
- CharBitmap *bm = _font->get_char(i);
|
|
|
|
|
- if (!(_small_caps && islower(bm->_character))) {
|
|
|
|
|
- ok = _layout.place_character(bm);
|
|
|
|
|
- if (!ok) {
|
|
|
|
|
- // Out of room.
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // They all fit!
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::choose_scale_factor
|
|
|
|
|
-// Access: Private
|
|
|
|
|
-// Description: Binary search on scale factor, given a factor that is
|
|
|
|
|
-// known to be too small and one that is known to be too
|
|
|
|
|
-// large.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void EggMakeFont::
|
|
|
|
|
-choose_scale_factor(double too_small, double too_large) {
|
|
|
|
|
- if (too_large - too_small < 0.000001) {
|
|
|
|
|
- // Close enough.
|
|
|
|
|
- consider_scale_factor(too_large);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Now create a single point where the origin of the next character
|
|
|
|
|
+ // will be.
|
|
|
|
|
|
|
|
- double mid = (too_small + too_large) / 2.0;
|
|
|
|
|
- if (consider_scale_factor(mid)) {
|
|
|
|
|
- // This midpoint is too large.
|
|
|
|
|
- choose_scale_factor(too_small, mid);
|
|
|
|
|
- } else {
|
|
|
|
|
- // This midpoint is too small.
|
|
|
|
|
- choose_scale_factor(mid, too_large);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ EggVertex *v0 = make_vertex(LPoint2d(glyph->get_advance() / _pixels_per_unit, 0.0));
|
|
|
|
|
+ EggPoint *point = new EggPoint;
|
|
|
|
|
+ group->add_child(point);
|
|
|
|
|
+ point->add_vertex(v0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: EggMakeFont::choose_scale_factor
|
|
|
|
|
|
|
+// Function: EggMakeFont::get_tref
|
|
|
// Access: Private
|
|
// Access: Private
|
|
|
-// Description: Tries several scale_factors until an optimal one
|
|
|
|
|
-// (that is, the smallest one that all letters fit
|
|
|
|
|
-// within) is found. Returns when all characters have
|
|
|
|
|
-// been successfully placed on the layout.
|
|
|
|
|
-//
|
|
|
|
|
-// Returns true if successful, or false if it just could
|
|
|
|
|
-// not be done.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-bool EggMakeFont::
|
|
|
|
|
-choose_scale_factor() {
|
|
|
|
|
- // We need to determine a scale factor that will definitely be too
|
|
|
|
|
- // small, and one that will definitely be too large.
|
|
|
|
|
- double too_small, too_large;
|
|
|
|
|
- int sanity_count = 0;
|
|
|
|
|
-
|
|
|
|
|
- double guess = 1.0;
|
|
|
|
|
- if (consider_scale_factor(guess)) {
|
|
|
|
|
- // This guess is too large.
|
|
|
|
|
- do {
|
|
|
|
|
- too_large = guess;
|
|
|
|
|
- guess = guess / 2.0;
|
|
|
|
|
- if (sanity_count++ > 20) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- } while (consider_scale_factor(guess));
|
|
|
|
|
- too_small = guess;
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- // This guess is too small.
|
|
|
|
|
- do {
|
|
|
|
|
- too_small = guess;
|
|
|
|
|
- guess = guess * 2.0;
|
|
|
|
|
- if (sanity_count++ > 20) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- } while (!consider_scale_factor(guess));
|
|
|
|
|
- too_large = guess;
|
|
|
|
|
|
|
+// Description: Returns the egg texture reference for a particular
|
|
|
|
|
+// glyph, creating it if it has not already been
|
|
|
|
|
+// created.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+EggTexture *EggMakeFont::
|
|
|
|
|
+get_tref(PNMTextGlyph *glyph, int character) {
|
|
|
|
|
+ TRefs::iterator ti = _trefs.find(glyph);
|
|
|
|
|
+ if (ti != _trefs.end()) {
|
|
|
|
|
+ return (*ti).second;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- choose_scale_factor(too_small, too_large);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ EggTexture *tref = make_tref(glyph, character);
|
|
|
|
|
+ _trefs[glyph] = tref;
|
|
|
|
|
+ return tref;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: EggMakeFont::choose_image_size
|
|
|
|
|
|
|
+// Function: EggMakeFont::make_tref
|
|
|
// Access: Private
|
|
// Access: Private
|
|
|
-// Description: Chooses a size for the output image that should yield
|
|
|
|
|
-// a scale_factor in the range (2.5 .. 4], which will give
|
|
|
|
|
-// pretty good antialiased letters.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void EggMakeFont::
|
|
|
|
|
-choose_image_size() {
|
|
|
|
|
- // Start with an arbitrary guess.
|
|
|
|
|
- _output_xsize = 256;
|
|
|
|
|
- _output_ysize = 256;
|
|
|
|
|
-
|
|
|
|
|
- bool sane = choose_scale_factor();
|
|
|
|
|
-
|
|
|
|
|
- if (sane && _scale_factor <= 2.5) {
|
|
|
|
|
- // The scale factor is too small. The letters may appear jaggy,
|
|
|
|
|
- // and we don't need so much image space.
|
|
|
|
|
- do {
|
|
|
|
|
- if (_output_ysize < _output_xsize) {
|
|
|
|
|
- _output_xsize /= 2;
|
|
|
|
|
- } else {
|
|
|
|
|
- _output_ysize /= 2;
|
|
|
|
|
- }
|
|
|
|
|
- sane = choose_scale_factor();
|
|
|
|
|
- } while (sane && _scale_factor <= 2.5);
|
|
|
|
|
-
|
|
|
|
|
- if (!sane) {
|
|
|
|
|
- // Oops, better backpedal.
|
|
|
|
|
- _output_xsize *= 2;
|
|
|
|
|
- }
|
|
|
|
|
- choose_scale_factor();
|
|
|
|
|
-
|
|
|
|
|
- } else if (_scale_factor > 4.0) {
|
|
|
|
|
- // The scale factor is too large. The letters will be overly
|
|
|
|
|
- // reduced and may be blurry. We need a larger image.
|
|
|
|
|
- do {
|
|
|
|
|
- if (_output_ysize < _output_xsize) {
|
|
|
|
|
- _output_ysize *= 2;
|
|
|
|
|
- } else {
|
|
|
|
|
- _output_xsize *= 2;
|
|
|
|
|
- }
|
|
|
|
|
- sane = choose_scale_factor();
|
|
|
|
|
- } while (!sane || _scale_factor > 4.0);
|
|
|
|
|
|
|
+// Description: Writes out the texture image for an indicated glyph,
|
|
|
|
|
+// and returns its egg reference.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+EggTexture *EggMakeFont::
|
|
|
|
|
+make_tref(PNMTextGlyph *glyph, int character) {
|
|
|
|
|
+ char buffer[1024];
|
|
|
|
|
+ sprintf(buffer, _output_image_pattern.c_str(), character);
|
|
|
|
|
+
|
|
|
|
|
+ Filename texture_filename = buffer;
|
|
|
|
|
+ PNMImage image(glyph->get_width() + _tex_margin * 2,
|
|
|
|
|
+ glyph->get_height() + _tex_margin * 2, _num_channels);
|
|
|
|
|
+ image.fill(_bg[0], _bg[1], _bg[2]);
|
|
|
|
|
+ if (image.has_alpha()) {
|
|
|
|
|
+ image.alpha_fill(_bg[3]);
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ glyph->place(image, -glyph->get_left() + _tex_margin,
|
|
|
|
|
+ glyph->get_top() + _tex_margin, _fg);
|
|
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::unsmooth_rgb
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description: Make the image jaggy in RGB (but leave it antialiased
|
|
|
|
|
-// in alpha). This will result in correct antialiasing
|
|
|
|
|
-// when the text is loaded in the player.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void EggMakeFont::
|
|
|
|
|
-unsmooth_rgb(PNMImage &image) {
|
|
|
|
|
- for (int y = 0; y < image.get_y_size(); y++) {
|
|
|
|
|
- for (int x = 0; x < image.get_x_size(); x++) {
|
|
|
|
|
- double alpha = image.get_alpha(x, y);
|
|
|
|
|
- if (alpha != 0.0) {
|
|
|
|
|
- image.set_xel(x, y, image.get_xel(x, y) / alpha);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!image.write(texture_filename)) {
|
|
|
|
|
+ nout << "Unable to write " << texture_filename << "\n";
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::expand_hyphen
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description: If a hyphen appears in the string anywhere but in the
|
|
|
|
|
-// first and last position, replace it with a sequence
|
|
|
|
|
-// of characters. For example, 0-9 becomes 0123456789.
|
|
|
|
|
-// Return the new string.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-string EggMakeFont::
|
|
|
|
|
-expand_hyphen(const string &str) {
|
|
|
|
|
- string result;
|
|
|
|
|
- size_t last = 0;
|
|
|
|
|
-
|
|
|
|
|
- size_t hyphen = str.find('-', last + 1);
|
|
|
|
|
- while (hyphen < str.length() - 1) {
|
|
|
|
|
- size_t ap = hyphen - 1;
|
|
|
|
|
- size_t zp = hyphen + 1;
|
|
|
|
|
- result += str.substr(last, ap - last);
|
|
|
|
|
- char a = str[ap];
|
|
|
|
|
- char z = str[zp];
|
|
|
|
|
-
|
|
|
|
|
- for (char i = a; i <= z; i++) {
|
|
|
|
|
- result += i;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ EggTexture *tref =
|
|
|
|
|
+ new EggTexture(texture_filename.get_basename_wo_extension(),
|
|
|
|
|
+ texture_filename);
|
|
|
|
|
+ tref->set_format(_format);
|
|
|
|
|
+ tref->set_wrap_mode(EggTexture::WM_clamp);
|
|
|
|
|
+ tref->set_minfilter(EggTexture::FT_linear_mipmap_linear);
|
|
|
|
|
+ tref->set_magfilter(EggTexture::FT_linear);
|
|
|
|
|
|
|
|
- last = zp + 1;
|
|
|
|
|
- hyphen = str.find('-', last + 1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- result += str.substr(last);
|
|
|
|
|
- return result;
|
|
|
|
|
|
|
+ return tref;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: EggMakeFont::run
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description:
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void EggMakeFont::
|
|
|
|
|
-run() {
|
|
|
|
|
- if (_output_image_filename.empty() && has_output_filename()) {
|
|
|
|
|
- _output_image_filename = get_output_filename();
|
|
|
|
|
- _output_image_filename.set_extension("rgb");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (_output_image_filename.empty()) {
|
|
|
|
|
- nout << "No output image filename given.\n";
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!_only_chars.empty()) {
|
|
|
|
|
- _only_chars = expand_hyphen(_only_chars);
|
|
|
|
|
- nout << "Extracting only characters: " << _only_chars << "\n";
|
|
|
|
|
- if (_small_caps) {
|
|
|
|
|
- _only_chars = upcase(_only_chars);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _font = new PkFontFile();
|
|
|
|
|
- if (!_font->read(_input_font_filename, _get_all, _only_chars)) {
|
|
|
|
|
- nout << "Unable to read " << _input_font_filename << ".\n";
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- nout << "Placing " << _font->get_num_chars() << " letters.\n";
|
|
|
|
|
-
|
|
|
|
|
- // Now that we've collected all the characters, sort them in order
|
|
|
|
|
- // from tallest to shortest so we will hopefully get a more optimal
|
|
|
|
|
- // packing.
|
|
|
|
|
- _font->sort_chars_by_height();
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // Choose a suitable image size and/or scale factor.
|
|
|
|
|
- if (_got_scale_factor && _got_output_size) {
|
|
|
|
|
- // The user specified both; we accept the scale factor.
|
|
|
|
|
- if (!consider_scale_factor(_scale_factor)) {
|
|
|
|
|
- nout << "Ran out of room on font image; try increasing the image "
|
|
|
|
|
- "size or the scale factor.\n";
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- } else if (_got_output_size) {
|
|
|
|
|
- // The user specified an output size, but not a scale factor.
|
|
|
|
|
- choose_scale_factor();
|
|
|
|
|
-
|
|
|
|
|
- } else if (_got_scale_factor) {
|
|
|
|
|
- // The user specified a scale factor, but not an output size.
|
|
|
|
|
- // This is really an error.
|
|
|
|
|
- nout << "It is meaningless to specify a scale factor (-sf) without "
|
|
|
|
|
- "also specifying an image size (-d). Ignoring scale factor.\n";
|
|
|
|
|
- choose_image_size();
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- // The user did not specify anything. This is really preferred.
|
|
|
|
|
- // We'll decide what's best.
|
|
|
|
|
- choose_image_size();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _working_poly_pixels = _poly_pixels * _scale_factor;
|
|
|
|
|
- _output_image.clear(_working_xsize, _working_ysize, _output_zsize);
|
|
|
|
|
-
|
|
|
|
|
- // If the user specified 1.0 for both foreground and background
|
|
|
|
|
- // alpha, we don't really want to use alpha.
|
|
|
|
|
- _use_alpha = (_output_zsize != 3) && (_fg[3] != 1.0 || _bg[3] != 1.0);
|
|
|
|
|
- if (_use_alpha && _output_zsize == 1) {
|
|
|
|
|
- // If we have only one channel and we're using alpha, then the
|
|
|
|
|
- // gray channel becomes the alpha channel.
|
|
|
|
|
- _fg[0] = _fg[3];
|
|
|
|
|
- _bg[0] = _bg[3];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _output_image.fill(_bg[0], _bg[1], _bg[2]);
|
|
|
|
|
- if (_output_image.has_alpha()) {
|
|
|
|
|
- _output_image.alpha_fill(_bg[3]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _group = new EggGroup();
|
|
|
|
|
- _data.add_child(_group);
|
|
|
|
|
- _tref = new EggTexture("chars", _output_image_filename);
|
|
|
|
|
- _group->add_child(_tref);
|
|
|
|
|
- _vpool = new EggVertexPool("vpool");
|
|
|
|
|
- _group->add_child(_vpool);
|
|
|
|
|
-
|
|
|
|
|
- // Set the appropriate flags on the texture.
|
|
|
|
|
- EggTexture::Format format = EggTexture::F_unspecified;
|
|
|
|
|
- if (_use_alpha) {
|
|
|
|
|
- switch (_output_zsize) {
|
|
|
|
|
- case 1:
|
|
|
|
|
- format = EggTexture::F_alpha;
|
|
|
|
|
- break;
|
|
|
|
|
- case 2:
|
|
|
|
|
- format = EggTexture::F_luminance_alpha;
|
|
|
|
|
- break;
|
|
|
|
|
- case 4:
|
|
|
|
|
- format = EggTexture::F_rgba;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- switch (_output_zsize) {
|
|
|
|
|
- case 1:
|
|
|
|
|
- case 2:
|
|
|
|
|
- format = EggTexture::F_luminance;
|
|
|
|
|
- break;
|
|
|
|
|
- case 3:
|
|
|
|
|
- case 4:
|
|
|
|
|
- format = EggTexture::F_rgb;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- _tref->set_format(format);
|
|
|
|
|
-
|
|
|
|
|
- // Make the group a sequence, as a convenience. If we view the
|
|
|
|
|
- // egg file directly we can see all the characters one at a time.
|
|
|
|
|
- _group->set_switch_flag(true);
|
|
|
|
|
- _group->set_switch_fps(2.0);
|
|
|
|
|
-
|
|
|
|
|
- // Compute the font points per polygon unit.
|
|
|
|
|
- _ppu = _font->get_ds() / (_ds * _scale);
|
|
|
|
|
-
|
|
|
|
|
- // Now we can copy all the characters onto the actual image.
|
|
|
|
|
- CharLayout::Placements::const_iterator pi;
|
|
|
|
|
- for (pi = _layout._placements.begin();
|
|
|
|
|
- pi != _layout._placements.end();
|
|
|
|
|
- ++pi) {
|
|
|
|
|
- copy_character(*pi);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // And now put all the Egg structures we created into the egg file,
|
|
|
|
|
- // in numeric order.
|
|
|
|
|
- EggDefs::const_iterator edi;
|
|
|
|
|
- for (edi = _egg_defs.begin(); edi != _egg_defs.end(); ++edi) {
|
|
|
|
|
- _group->add_child((*edi).second.p());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Also create an egg group indicating the font's design size.
|
|
|
|
|
- EggGroup *ds_group = new EggGroup("ds");
|
|
|
|
|
- _group->add_child(ds_group);
|
|
|
|
|
- EggVertex *vtx = make_vertex(LPoint2d(0.0, _ds));
|
|
|
|
|
- EggPoint *point = new EggPoint;
|
|
|
|
|
- ds_group->add_child(point);
|
|
|
|
|
- point->add_vertex(vtx);
|
|
|
|
|
-
|
|
|
|
|
- if (_use_alpha && _output_zsize != 1) {
|
|
|
|
|
- if (_bg[3] == 0.0) {
|
|
|
|
|
- // If we have a transparent background, then everything in the
|
|
|
|
|
- // color channels is pointless--the color information is
|
|
|
|
|
- // completely replaced. Might as well fill it white.
|
|
|
|
|
- _output_image.fill(_fg[0], _fg[1], _fg[2]);
|
|
|
|
|
-
|
|
|
|
|
- } else if (_bg[3] == 1.0) {
|
|
|
|
|
- // Similarly if we have a transparent foreground.
|
|
|
|
|
- _output_image.fill(_bg[0], _bg[1], _bg[2]);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // All done! Write everything out.
|
|
|
|
|
- nout << "Scale factor is " << _scale_factor << "\n";
|
|
|
|
|
-
|
|
|
|
|
- if (_no_reduce) {
|
|
|
|
|
- // Scaling of the final image forbidden by the user.
|
|
|
|
|
- nout << "Image destination size is " << _output_xsize
|
|
|
|
|
- << " by " << _output_ysize << " by " << _output_zsize
|
|
|
|
|
- << "; not reducing.\n";
|
|
|
|
|
- nout << "Generating " << _working_xsize << " by " << _working_ysize
|
|
|
|
|
- << " by " << _output_zsize << " image: "
|
|
|
|
|
- << _output_image_filename << "\n";
|
|
|
|
|
-
|
|
|
|
|
- _output_image.write(_output_image_filename);
|
|
|
|
|
-
|
|
|
|
|
- } else if (_output_xsize == _working_xsize &&
|
|
|
|
|
- _output_ysize == _working_ysize) {
|
|
|
|
|
- // Scaling unnecessary, because the scale factor is 1.0.
|
|
|
|
|
- nout << "Generating " << _output_xsize << " by " << _output_ysize
|
|
|
|
|
- << " by " << _output_zsize << " image: "
|
|
|
|
|
- << _output_image_filename << "\n";
|
|
|
|
|
- _output_image.write(_output_image_filename);
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- // The normal path: reduce the final image by the scale factor to
|
|
|
|
|
- // antialias the letters.
|
|
|
|
|
- PNMImage small_image(_output_xsize, _output_ysize, _output_zsize);
|
|
|
|
|
- small_image.gaussian_filter_from(_gaussian_radius, _output_image);
|
|
|
|
|
-
|
|
|
|
|
- // Fix antialiasing, if required.
|
|
|
|
|
- if (_use_alpha && _bg[3] != 0.0 && _bg[3] != 1.0) {
|
|
|
|
|
- // If we have some non-transparent background, we need to
|
|
|
|
|
- // compensate for the antialiasing.
|
|
|
|
|
- unsmooth_rgb(small_image);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- nout << "Generating " << _output_xsize << " by " << _output_ysize
|
|
|
|
|
- << " by " << _output_zsize << " image: "
|
|
|
|
|
- << _output_image_filename << "\n";
|
|
|
|
|
- small_image.write(_output_image_filename);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- write_egg_file();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int main(int argc, char *argv[]) {
|
|
|
EggMakeFont prog;
|
|
EggMakeFont prog;
|
|
|
prog.parse_command_line(argc, argv);
|
|
prog.parse_command_line(argc, argv);
|