Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/egg-mkfont/eggMakeFont.cxx

Go to the documentation of this file.
00001 // Filename: eggMakeFont.cxx
00002 // Created by:  drose (16Feb01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "eggMakeFont.h"
00020 #include "charBitmap.h"
00021 #include "charPlacement.h"
00022 #include "pkFontFile.h"
00023 
00024 #include <string_utils.h>
00025 #include <eggGroup.h>
00026 #include <eggTexture.h>
00027 #include <eggVertexPool.h>
00028 #include <eggPolygon.h>
00029 #include <eggPoint.h>
00030 #include <pointerTo.h>
00031 
00032 #include <math.h>
00033 
00034 ////////////////////////////////////////////////////////////////////
00035 //     Function: EggMakeFont::Constructor
00036 //       Access: Public
00037 //  Description:
00038 ////////////////////////////////////////////////////////////////////
00039 EggMakeFont::
00040 EggMakeFont() : EggWriter(true, false) {
00041   set_program_description
00042     ("egg-mkfont reads a rasterized font stored in a "
00043      "Metafont/TeX pk file format, and generates an egg "
00044      "file and corresponding texture map that can be used with "
00045      "Panda's TextNode object to render text in the font.\n\n"
00046 
00047      "The input pk file can come from any of a number of sources.  "
00048      "It may be a Metafont font that ships with TeX, or it may "
00049      "have been converted from a PostScript font, or you can use "
00050      "freetype (see www.freetype.org) to convert a TTF font to pk.\n\n");
00051 
00052 
00053   clear_runlines();
00054   add_runline("[opts] -o output.egg file.pk");
00055   add_runline("[opts] file.pk output.egg");
00056 
00057   add_option
00058     ("i", "filename", 0,
00059      "Name of the texture image to write.  The default if this is omitted "
00060      "is based on the name of the egg file.",
00061      &EggMakeFont::dispatch_filename, NULL, &_output_image_filename);
00062 
00063   add_option
00064     ("c", "num", 0,
00065      "Specifies the number of channels of the output image.  This should "
00066      "either 1, 2, 3, or 4.  If the number is 1 or 3 a grayscale image is "
00067      "generated, with the text in white on black.  If the number is 2 or 4 "
00068      "a completely white image is generated, with the text in the alpha "
00069      "channel.  This parameter may also be specified as the third number "
00070      "on -d, below.  The default is 1.",
00071      &EggMakeFont::dispatch_int, NULL, &_output_zsize);
00072 
00073   add_option
00074     ("fg", "r,g,b[,a]", 0,
00075      "Specifies the foreground color of the generated texture map.  The "
00076      "default is white: 1,1,1,1, which leads to the most flexibility "
00077      "as the color can be modulated at runtime to any suitable color.",
00078      &EggMakeFont::dispatch_color, NULL, &_fg[0]);
00079 
00080   add_option
00081     ("bg", "r,g,b[,a]", 0,
00082      "Specifies the background color of the generated texture map.  The "
00083      "default is transparent black: 0,0,0,0, which allows the text to be "
00084      "visible against any color background by placing a polygon of a "
00085      "suitable color behind it.",
00086      &EggMakeFont::dispatch_color, NULL, &_bg[0]);
00087 
00088   add_option
00089     ("d", "x,y[,c]", 0,
00090      "Dimensions in pixels of the texture image, with an optional number of "
00091      "channels.  Normally, you should not specify this parameter, as "
00092      "egg-mkfont will choose an image size that yields a scale factor "
00093      "between 2.5 and 4, which leads to good antialiased letters.  If you "
00094      "want a larger or smaller image, you could force the image size "
00095      "with this parameter, but it would probably yield better results if "
00096      "you re-rasterized the font at a different DPI instead.",
00097      &EggMakeFont::dispatch_dimensions, &_got_output_size);
00098 
00099   add_option
00100     ("sf", "factor", 0,
00101 
00102      "The scale factor of the generated image.  This is the factor by which "
00103      "the font image is generated oversized, then reduced to its final size, "
00104      "to generate antialiased letters.  Values between 2.5 and 4 are generally "
00105      "best.  Normally, you should not specify this parameter, as egg-mkfont "
00106      "will choose a scale factor automatically to best fit the letters in the "
00107      "space available.  If you do specify a scale factor with -sf, you must "
00108      "also specify an image size with -d.",
00109      &EggMakeFont::dispatch_double, &_got_scale_factor, &_scale_factor);
00110 
00111   add_option
00112     ("nr", "", 0,
00113      "No reduce.  After the oversized image is generated, rather than reducing "
00114      "it to its final size, just leave it as it is, and assume the user will "
00115      "reduce it later.  This may be desireable if you intend to adjust the "
00116      "letters by hand in some way after the image is generated.",
00117      &EggMakeFont::dispatch_none, &_no_reduce);
00118 
00119   add_option
00120     ("g", "radius", 0,
00121      "The radius of the Gaussian filter used to antialias the letters. [1.2]",
00122      &EggMakeFont::dispatch_double, NULL, &_gaussian_radius);
00123 
00124   add_option
00125     ("b", "n", 0,
00126      "The number of buffer pixels between two adjacent characters in "
00127      "the palette image. [4.0]",
00128      &EggMakeFont::dispatch_double, NULL, &_buffer_pixels);
00129 
00130   add_option
00131     ("B", "n", 0,
00132      "The number of extra pixels around a single character in the "
00133      "generated polygon. [1.0]",
00134      &EggMakeFont::dispatch_double, NULL, &_poly_pixels);
00135 
00136   add_option
00137     ("ds", "size", 0,
00138      "Specify the design size of the resulting font.  The design size of "
00139      "a font is the height of a typical capital letter; it's the approximate "
00140      "height of a line of text.  This sets the size of the polygons "
00141      "accordingly.  [1.0]",
00142      &EggMakeFont::dispatch_double, NULL, &_ds);
00143 
00144   add_option
00145     ("scale", "size", 0,
00146      "Specify an additional scale to the font, without changing its design "
00147      "size.  This makes the letters larger (or smaller) without changing "
00148      "the spacing between lines.  Usually you should use -ds instead of "
00149      "-scale to change the size of the text.",
00150      &EggMakeFont::dispatch_double, NULL, &_scale);
00151 
00152   add_option
00153     ("all", "", 0,
00154      "Extract all the characters in the font.  Normally, only the "
00155      "ASCII characters in the range 33 .. 127 are extracted.",
00156      &EggMakeFont::dispatch_none, &_get_all);
00157 
00158   add_option
00159     ("only", "'chars'", 0,
00160      "Extract *only* the indicated characters from the font.  The parameter "
00161      "should be a quoted string of letters and symbols that are to be "
00162      "extracted.  If the hyphen appears, it indicates a range of characters, "
00163      "e.g. A-Z to extract all the capital letters.  If the hyphen appears "
00164      "as the first or last character it loses its special meaning.",
00165      &EggMakeFont::dispatch_string, NULL, &_only_chars);
00166 
00167   add_option
00168     ("sc", "", 0,
00169      "Small caps: generate lowercase letters as small capitals.  This "
00170      "allows the lowercase and capital letters to share the same space "
00171      "on the texture map.",
00172      &EggMakeFont::dispatch_none, &_small_caps);
00173 
00174   add_option
00175     ("scs", "", 0,
00176      "Small caps scale: the ratio of the size of a lowercase letter to "
00177      "its uppercase equivalent, when -sc is in effect.  [0.8]",
00178      &EggMakeFont::dispatch_double, NULL, &_small_caps_scale);
00179 
00180   _fg.set(1.0, 1.0, 1.0, 1.0);
00181   _bg.set(0.0, 0.0, 0.0, 0.0);
00182   _output_xsize = 256;
00183   _output_ysize = 256;
00184   _output_zsize = 1;
00185   _buffer_pixels = 4.0;
00186   _poly_pixels = 1.0;
00187   _scale_factor = 3.0;
00188   _gaussian_radius = 1.2;
00189   _ds = 1.0;
00190   _scale = 1.0;
00191   _small_caps_scale = 0.8;
00192 }
00193 
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: EggMakeFont::handle_args
00197 //       Access: Protected, Virtual
00198 //  Description: Does something with the additional arguments on the
00199 //               command line (after all the -options have been
00200 //               parsed).  Returns true if the arguments are good,
00201 //               false otherwise.
00202 ////////////////////////////////////////////////////////////////////
00203 bool EggMakeFont::
00204 handle_args(ProgramBase::Args &args) {
00205   if (args.empty()) {
00206     nout << "Must specify name of pk file on command line.\n";
00207     return false;
00208   }
00209 
00210   _input_font_filename = args[0];
00211   args.pop_front();
00212   return EggWriter::handle_args(args);
00213 }
00214 
00215 ////////////////////////////////////////////////////////////////////
00216 //     Function: EggMakeFont::dispatch_dimensions
00217 //       Access: Protected, Static
00218 //  Description: Reads the dimensions of the output image and stores
00219 //               them in _output_[xyz]size.
00220 ////////////////////////////////////////////////////////////////////
00221 bool EggMakeFont::
00222 dispatch_dimensions(ProgramBase *self, const string &opt, const string &arg, void *) {
00223   EggBase *base = (EggBase *)self;
00224   EggMakeFont *me = (EggMakeFont *)base->as_writer();
00225   return me->ns_dispatch_dimensions(opt, arg);
00226 }
00227 
00228 ////////////////////////////////////////////////////////////////////
00229 //     Function: EggMakeFont::ns_dispatch_dimensions
00230 //       Access: Protected
00231 //  Description: Reads the dimensions of the output image and stores
00232 //               them in _output_[xyz]size.
00233 ////////////////////////////////////////////////////////////////////
00234 bool EggMakeFont::
00235 ns_dispatch_dimensions(const string &opt, const string &arg) {
00236   vector_string words;
00237   tokenize(arg, words, ",");
00238 
00239   bool okflag = false;
00240   if (words.size() == 2) {
00241     okflag =
00242       string_to_int(words[0], _output_xsize) &&
00243       string_to_int(words[1], _output_ysize);
00244 
00245   } else if (words.size() == 3) {
00246     okflag =
00247       string_to_int(words[0], _output_xsize) &&
00248       string_to_int(words[1], _output_ysize) &&
00249       string_to_int(words[2], _output_zsize);
00250   }
00251 
00252   if (!okflag) {
00253     nout << "-" << opt
00254          << " requires two or three integers separated by commas.\n";
00255     return false;
00256   }
00257 
00258   return true;
00259 }
00260 
00261 ////////////////////////////////////////////////////////////////////
00262 //     Function: EggMakeFont::get_uv
00263 //       Access: Private
00264 //  Description: Given the X, Y coordinates of a particular pixel on
00265 //               the image, return the corresponding UV coordinates.
00266 ////////////////////////////////////////////////////////////////////
00267 TexCoordd EggMakeFont::
00268 get_uv(double x, double y) {
00269   return TexCoordd(x / (double)_working_xsize,
00270                    ((double)_working_ysize - y) / (double)_working_ysize);
00271 }
00272 
00273 
00274 ////////////////////////////////////////////////////////////////////
00275 //     Function: EggMakeFont::get_xy
00276 //       Access: Private
00277 //  Description: Given X, Y coordinates in pixels, scale to unit
00278 //               coordinates for the character's geometry.
00279 ////////////////////////////////////////////////////////////////////
00280 LPoint2d EggMakeFont::
00281 get_xy(double x, double y) {
00282   return LPoint2d(x / (_font->get_hppp() * _ppu), -y / (_font->get_vppp() * _ppu));
00283 }
00284 
00285 ////////////////////////////////////////////////////////////////////
00286 //     Function: EggMakeFont::make_vertex
00287 //       Access: Private
00288 //  Description: Allocates and returns a new vertex from the vertex
00289 //               pool representing the indicated 2-d coordinates.
00290 ////////////////////////////////////////////////////////////////////
00291 EggVertex *EggMakeFont::
00292 make_vertex(const LPoint2d &xy) {
00293   return
00294     _vpool->make_new_vertex(LPoint3d::origin(_coordinate_system) +
00295                             LVector3d::rfu(xy[0], 0.0, xy[1], _coordinate_system));
00296 }
00297 
00298 ////////////////////////////////////////////////////////////////////
00299 //     Function: EggMakeFont::copy_character
00300 //       Access: Private
00301 //  Description: Copy the indicated character image to its home on the
00302 //               bitmap and generate egg structures for it.
00303 ////////////////////////////////////////////////////////////////////
00304 void EggMakeFont::
00305 copy_character(const CharPlacement &pl) {
00306   const CharBitmap *bm = pl._bm;
00307   int xp = pl._x;
00308   int yp = pl._y;
00309 
00310   int character = bm->_character;
00311   int hoff = bm->_hoff;
00312   int voff = bm->_voff;
00313   double dx = bm->_dx;
00314   double dy = bm->_dy;
00315   int width = bm->get_width();
00316   int height = bm->get_height();
00317 
00318   // Copy the character into the image.
00319   if (_output_image.has_alpha()) {
00320     for (int y = 0; y < height; y++) {
00321       for (int x = 0; x < width; x++) {
00322         if (bm->_block[y][x]) {
00323           _output_image.set_xel(xp + x, yp + y, _fg[0], _fg[1], _fg[2]);
00324           _output_image.set_alpha(xp + x, yp + y, _fg[3]);
00325         }
00326       }
00327     }
00328   } else {
00329     for (int y = 0; y < height; y++) {
00330       for (int x = 0; x < width; x++) {
00331         if (bm->_block[y][x]) {
00332           _output_image.set_xel(xp + x, yp + y, _fg[0], _fg[1], _fg[2]);
00333         }
00334       }
00335     }
00336   }
00337 
00338   // Create the polygon that will have the character mapped onto it.
00339 
00340   // b is the number of pixels bigger than the character in each
00341   // direction the polygon will be.  It needs to be larger than zero
00342   // just because when we filter the image down, we end up with some
00343   // antialiasing blur that extends beyond the original borders of the
00344   // character.  But it shouldn't be too large, because we don't want
00345   // the neighboring polygons of a word to overlap any more than they
00346   // need to.
00347 
00348   double b = _working_poly_pixels;
00349 
00350   TexCoordd uv_ul = get_uv(xp - b, yp - b);
00351   TexCoordd uv_lr = get_uv(xp + width + b, yp + height + b);
00352   LPoint2d xy_ul = get_xy(-hoff - b, -voff - b);
00353   LPoint2d xy_lr = get_xy(-hoff + width + b, -voff + height + b);
00354   LPoint2d dp = get_xy(dx, dy);
00355 
00356   EggVertex *v1 = make_vertex(LPoint2d(xy_ul[0], xy_lr[1]));
00357   EggVertex *v2 = make_vertex(LPoint2d(xy_lr[0], xy_lr[1]));
00358   EggVertex *v3 = make_vertex(LPoint2d(xy_lr[0], xy_ul[1]));
00359   EggVertex *v4 = make_vertex(LPoint2d(xy_ul[0], xy_ul[1]));
00360 
00361   v1->set_uv(TexCoordd(uv_ul[0], uv_lr[1]));
00362   v2->set_uv(TexCoordd(uv_lr[0], uv_lr[1]));
00363   v3->set_uv(TexCoordd(uv_lr[0], uv_ul[1]));
00364   v4->set_uv(TexCoordd(uv_ul[0], uv_ul[1]));
00365 
00366   // Create an egg group to hold the polygon.
00367   string group_name = format_string(character);
00368   PT(EggGroup) group = new EggGroup(group_name);
00369   _egg_defs[character] = group;
00370 
00371   EggPolygon *poly = new EggPolygon();
00372   group->add_child(poly);
00373   poly->set_texture(_tref);
00374 
00375   poly->add_vertex(v1);
00376   poly->add_vertex(v2);
00377   poly->add_vertex(v3);
00378   poly->add_vertex(v4);
00379 
00380   // Now create a single point where the origin of the next character
00381   // will be.
00382 
00383   EggVertex *v0 = make_vertex(dp);
00384   EggPoint *point = new EggPoint;
00385   group->add_child(point);
00386   point->add_vertex(v0);
00387 
00388   if (_small_caps && isupper(character)) {
00389     // Now create the polygon representing the lowercase letter, for
00390     // small caps mode.  This uses the same texture on a smaller
00391     // polygon.
00392     xy_ul *= _small_caps_scale;
00393     xy_lr *= _small_caps_scale;
00394     dp *= _small_caps_scale;
00395     character = tolower(character);
00396 
00397     EggVertex *v1 = make_vertex(LPoint2d(xy_ul[0], xy_lr[1]));
00398     EggVertex *v2 = make_vertex(LPoint2d(xy_lr[0], xy_lr[1]));
00399     EggVertex *v3 = make_vertex(LPoint2d(xy_lr[0], xy_ul[1]));
00400     EggVertex *v4 = make_vertex(LPoint2d(xy_ul[0], xy_ul[1]));
00401 
00402     v1->set_uv(TexCoordd(uv_ul[0], uv_lr[1]));
00403     v2->set_uv(TexCoordd(uv_lr[0], uv_lr[1]));
00404     v3->set_uv(TexCoordd(uv_lr[0], uv_ul[1]));
00405     v4->set_uv(TexCoordd(uv_ul[0], uv_ul[1]));
00406 
00407     string group_name = format_string(character);
00408     PT(EggGroup) group = new EggGroup(group_name);
00409     _egg_defs[character] = group;
00410 
00411     EggPolygon *poly = new EggPolygon();
00412     group->add_child(poly);
00413     poly->set_texture(_tref);
00414 
00415     poly->add_vertex(v1);
00416     poly->add_vertex(v2);
00417     poly->add_vertex(v3);
00418     poly->add_vertex(v4);
00419 
00420     EggVertex *v0 = make_vertex(dp);
00421     EggPoint *point = new EggPoint;
00422     group->add_child(point);
00423     point->add_vertex(v0);
00424   }
00425 }
00426 
00427 ////////////////////////////////////////////////////////////////////
00428 //     Function: EggMakeFont::consider_scale_factor
00429 //       Access: Private
00430 //  Description: Attempts to place all of the characters on an image
00431 //               of the indicated size (scaled up from the output
00432 //               image size by scale_factor in each dimension).
00433 //               Returns true if all the characters fit, or false if
00434 //               the image was too small.  In either case, leaves
00435 //               _scale_factor and _working_* set to reflect the
00436 //               chosen scale factor.
00437 ////////////////////////////////////////////////////////////////////
00438 bool EggMakeFont::
00439 consider_scale_factor(double scale_factor) {
00440   _scale_factor = scale_factor;
00441   _working_xsize = (int)floor(_output_xsize * _scale_factor + 0.5);
00442   _working_ysize = (int)floor(_output_ysize * _scale_factor + 0.5);
00443   _working_buffer_pixels = (int)floor(_buffer_pixels * _scale_factor + 0.5);
00444 
00445   _layout.reset(_working_xsize, _working_ysize, _working_buffer_pixels);
00446 
00447   bool ok = true;
00448   int num_chars = _font->get_num_chars();
00449   for (int i = 0; i < num_chars; i++) {
00450     CharBitmap *bm = _font->get_char(i);
00451     if (!(_small_caps && islower(bm->_character))) {
00452       ok = _layout.place_character(bm);
00453       if (!ok) {
00454         // Out of room.
00455         return false;
00456       }
00457     }
00458   }
00459 
00460   // They all fit!
00461   return true;
00462 }
00463 
00464 ////////////////////////////////////////////////////////////////////
00465 //     Function: EggMakeFont::choose_scale_factor
00466 //       Access: Private
00467 //  Description: Binary search on scale factor, given a factor that is
00468 //               known to be too small and one that is known to be too
00469 //               large.
00470 ////////////////////////////////////////////////////////////////////
00471 void EggMakeFont::
00472 choose_scale_factor(double too_small, double too_large) {
00473   if (too_large - too_small < 0.000001) {
00474     // Close enough.
00475     consider_scale_factor(too_large);
00476     return;
00477   }
00478 
00479   double mid = (too_small + too_large) / 2.0;
00480   if (consider_scale_factor(mid)) {
00481     // This midpoint is too large.
00482     choose_scale_factor(too_small, mid);
00483   } else {
00484     // This midpoint is too small.
00485     choose_scale_factor(mid, too_large);
00486   }
00487 }
00488 
00489 ////////////////////////////////////////////////////////////////////
00490 //     Function: EggMakeFont::choose_scale_factor
00491 //       Access: Private
00492 //  Description: Tries several scale_factors until an optimal one
00493 //               (that is, the smallest one that all letters fit
00494 //               within) is found.  Returns when all characters have
00495 //               been successfully placed on the layout.
00496 //
00497 //               Returns true if successful, or false if it just could
00498 //               not be done.
00499 ////////////////////////////////////////////////////////////////////
00500 bool EggMakeFont::
00501 choose_scale_factor() {
00502   // We need to determine a scale factor that will definitely be too
00503   // small, and one that will definitely be too large.
00504   double too_small, too_large;
00505   int sanity_count = 0;
00506 
00507   double guess = 1.0;
00508   if (consider_scale_factor(guess)) {
00509     // This guess is too large.
00510     do {
00511       too_large = guess;
00512       guess = guess / 2.0;
00513       if (sanity_count++ > 20) {
00514         return false;
00515       }
00516     } while (consider_scale_factor(guess));
00517     too_small = guess;
00518 
00519   } else {
00520     // This guess is too small.
00521     do {
00522       too_small = guess;
00523       guess = guess * 2.0;
00524       if (sanity_count++ > 20) {
00525         return false;
00526       }
00527     } while (!consider_scale_factor(guess));
00528     too_large = guess;
00529   }
00530 
00531   choose_scale_factor(too_small, too_large);
00532   return true;
00533 }
00534 
00535 ////////////////////////////////////////////////////////////////////
00536 //     Function: EggMakeFont::choose_image_size
00537 //       Access: Private
00538 //  Description: Chooses a size for the output image that should yield
00539 //               a scale_factor in the range (2.5 .. 4], which will give
00540 //               pretty good antialiased letters.
00541 ////////////////////////////////////////////////////////////////////
00542 void EggMakeFont::
00543 choose_image_size() {
00544   // Start with an arbitrary guess.
00545   _output_xsize = 256;
00546   _output_ysize = 256;
00547 
00548   bool sane = choose_scale_factor();
00549 
00550   if (sane && _scale_factor <= 2.5) {
00551     // The scale factor is too small.  The letters may appear jaggy,
00552     // and we don't need so much image space.
00553     do {
00554       if (_output_ysize < _output_xsize) {
00555         _output_xsize /= 2;
00556       } else {
00557         _output_ysize /= 2;
00558       }
00559       sane = choose_scale_factor();
00560     } while (sane && _scale_factor <= 2.5);
00561 
00562     if (!sane) {
00563       // Oops, better backpedal.
00564       _output_xsize *= 2;
00565     }
00566     choose_scale_factor();
00567 
00568   } else if (_scale_factor > 4.0) {
00569     // The scale factor is too large.  The letters will be overly
00570     // reduced and may be blurry.  We need a larger image.
00571     do {
00572       if (_output_ysize < _output_xsize) {
00573         _output_ysize *= 2;
00574       } else {
00575         _output_xsize *= 2;
00576       }
00577       sane = choose_scale_factor();
00578     } while (!sane || _scale_factor > 4.0);
00579   }
00580 }
00581 
00582 
00583 
00584 ////////////////////////////////////////////////////////////////////
00585 //     Function: EggMakeFont::unsmooth_rgb
00586 //       Access: Public
00587 //  Description: Make the image jaggy in RGB (but leave it antialiased
00588 //               in alpha).  This will result in correct antialiasing
00589 //               when the text is loaded in the player.
00590 ////////////////////////////////////////////////////////////////////
00591 void EggMakeFont::
00592 unsmooth_rgb(PNMImage &image) {
00593   for (int y = 0; y < image.get_y_size(); y++) {
00594     for (int x = 0; x < image.get_x_size(); x++) {
00595       double alpha = image.get_alpha(x, y);
00596       if (alpha != 0.0) {
00597         image.set_xel(x, y, image.get_xel(x, y) / alpha);
00598       }
00599     }
00600   }
00601 }
00602 
00603 ////////////////////////////////////////////////////////////////////
00604 //     Function: EggMakeFont::expand_hyphen
00605 //       Access: Public
00606 //  Description: If a hyphen appears in the string anywhere but in the
00607 //               first and last position, replace it with a sequence
00608 //               of characters.  For example, 0-9 becomes 0123456789.
00609 //               Return the new string.
00610 ////////////////////////////////////////////////////////////////////
00611 string EggMakeFont::
00612 expand_hyphen(const string &str) {
00613   string result;
00614   size_t last = 0;
00615 
00616   size_t hyphen = str.find('-', last + 1);
00617   while (hyphen < str.length() - 1) {
00618     size_t ap = hyphen - 1;
00619     size_t zp = hyphen + 1;
00620     result += str.substr(last, ap - last);
00621     char a = str[ap];
00622     char z = str[zp];
00623 
00624     for (char i = a; i <= z; i++) {
00625       result += i;
00626     }
00627 
00628     last = zp + 1;
00629     hyphen = str.find('-', last + 1);
00630   }
00631 
00632   result += str.substr(last);
00633   return result;
00634 }
00635 
00636 ////////////////////////////////////////////////////////////////////
00637 //     Function: EggMakeFont::run
00638 //       Access: Public
00639 //  Description:
00640 ////////////////////////////////////////////////////////////////////
00641 void EggMakeFont::
00642 run() {
00643   if (_output_image_filename.empty() && has_output_filename()) {
00644     _output_image_filename = get_output_filename();
00645     _output_image_filename.set_extension("rgb");
00646   }
00647 
00648   if (_output_image_filename.empty()) {
00649     nout << "No output image filename given.\n";
00650     exit(1);
00651   }
00652 
00653   if (!_only_chars.empty()) {
00654     _only_chars = expand_hyphen(_only_chars);
00655     nout << "Extracting only characters: " << _only_chars << "\n";
00656     if (_small_caps) {
00657       _only_chars = upcase(_only_chars);
00658     }
00659   }
00660 
00661   _font = new PkFontFile();
00662   if (!_font->read(_input_font_filename, _get_all, _only_chars)) {
00663     nout << "Unable to read " << _input_font_filename << ".\n";
00664     exit(1);
00665   }
00666 
00667   nout << "Placing " << _font->get_num_chars() << " letters.\n";
00668 
00669   // Now that we've collected all the characters, sort them in order
00670   // from tallest to shortest so we will hopefully get a more optimal
00671   // packing.
00672   _font->sort_chars_by_height();
00673 
00674 
00675   // Choose a suitable image size and/or scale factor.
00676   if (_got_scale_factor && _got_output_size) {
00677     // The user specified both; we accept the scale factor.
00678     if (!consider_scale_factor(_scale_factor)) {
00679       nout << "Ran out of room on font image; try increasing the image "
00680         "size or the scale factor.\n";
00681       exit(1);
00682     }
00683 
00684   } else if (_got_output_size) {
00685     // The user specified an output size, but not a scale factor.
00686     choose_scale_factor();
00687 
00688   } else if (_got_scale_factor) {
00689     // The user specified a scale factor, but not an output size.
00690     // This is really an error.
00691     nout << "It is meaningless to specify a scale factor (-sf) without "
00692       "also specifying an image size (-d).  Ignoring scale factor.\n";
00693     choose_image_size();
00694 
00695   } else {
00696     // The user did not specify anything.  This is really preferred.
00697     // We'll decide what's best.
00698     choose_image_size();
00699   }
00700 
00701   _working_poly_pixels = _poly_pixels * _scale_factor;
00702   _output_image.clear(_working_xsize, _working_ysize, _output_zsize);
00703 
00704   // If the user specified 1.0 for both foreground and background
00705   // alpha, we don't really want to use alpha.
00706   _use_alpha = (_output_zsize != 3) && (_fg[3] != 1.0 || _bg[3] != 1.0);
00707   if (_use_alpha && _output_zsize == 1) {
00708     // If we have only one channel and we're using alpha, then the
00709     // gray channel becomes the alpha channel.
00710     _fg[0] = _fg[3];
00711     _bg[0] = _bg[3];
00712   }
00713 
00714   _output_image.fill(_bg[0], _bg[1], _bg[2]);
00715   if (_output_image.has_alpha()) {
00716     _output_image.alpha_fill(_bg[3]);
00717   }
00718 
00719   _group = new EggGroup();
00720   _data.add_child(_group);
00721   _tref = new EggTexture("chars", _output_image_filename);
00722   _group->add_child(_tref);
00723   _vpool = new EggVertexPool("vpool");
00724   _group->add_child(_vpool);
00725 
00726   // Set the appropriate flags on the texture.
00727   EggTexture::Format format = EggTexture::F_unspecified;
00728   if (_use_alpha) {
00729     switch (_output_zsize) {
00730     case 1:
00731       format = EggTexture::F_alpha;
00732       break;
00733     case 2:
00734       format = EggTexture::F_luminance_alpha;
00735       break;
00736     case 4:
00737       format = EggTexture::F_rgba;
00738       break;
00739     }
00740   } else {
00741     switch (_output_zsize) {
00742     case 1:
00743     case 2:
00744       format = EggTexture::F_luminance;
00745       break;
00746     case 3:
00747     case 4:
00748       format = EggTexture::F_rgb;
00749       break;
00750     }
00751   }
00752   _tref->set_format(format);
00753 
00754   // Make the group a sequence, as a convenience.  If we view the
00755   // egg file directly we can see all the characters one at a time.
00756   _group->set_switch_flag(true);
00757   _group->set_switch_fps(2.0);
00758 
00759   // Compute the font points per polygon unit.
00760   _ppu = _font->get_ds() / (_ds * _scale);
00761 
00762   // Now we can copy all the characters onto the actual image.
00763   CharLayout::Placements::const_iterator pi;
00764   for (pi = _layout._placements.begin();
00765        pi != _layout._placements.end();
00766        ++pi) {
00767     copy_character(*pi);
00768   }
00769 
00770   // And now put all the Egg structures we created into the egg file,
00771   // in numeric order.
00772   EggDefs::const_iterator edi;
00773   for (edi = _egg_defs.begin(); edi != _egg_defs.end(); ++edi) {
00774     _group->add_child((*edi).second.p());
00775   }
00776 
00777   // Also create an egg group indicating the font's design size.
00778   EggGroup *ds_group = new EggGroup("ds");
00779   _group->add_child(ds_group);
00780   EggVertex *vtx = make_vertex(LPoint2d(0.0, _ds));
00781   EggPoint *point = new EggPoint;
00782   ds_group->add_child(point);
00783   point->add_vertex(vtx);
00784 
00785   if (_use_alpha && _output_zsize != 1) {
00786     if (_bg[3] == 0.0) {
00787       // If we have a transparent background, then everything in the
00788       // color channels is pointless--the color information is
00789       // completely replaced.  Might as well fill it white.
00790       _output_image.fill(_fg[0], _fg[1], _fg[2]);
00791 
00792     } else if (_bg[3] == 1.0) {
00793       // Similarly if we have a transparent foreground.
00794       _output_image.fill(_bg[0], _bg[1], _bg[2]);
00795     }
00796   }
00797 
00798   // All done!  Write everything out.
00799   nout << "Scale factor is " << _scale_factor << "\n";
00800 
00801   if (_no_reduce) {
00802     // Scaling of the final image forbidden by the user.
00803     nout << "Image destination size is " << _output_xsize
00804          << " by " << _output_ysize << " by " << _output_zsize
00805          << "; not reducing.\n";
00806     nout << "Generating " << _working_xsize << " by " << _working_ysize
00807          << " by " << _output_zsize << " image: "
00808          << _output_image_filename << "\n";
00809 
00810     _output_image.write(_output_image_filename);
00811 
00812   } else if (_output_xsize == _working_xsize &&
00813              _output_ysize == _working_ysize) {
00814     // Scaling unnecessary, because the scale factor is 1.0.
00815     nout << "Generating " << _output_xsize << " by " << _output_ysize
00816          << " by " << _output_zsize << " image: "
00817          << _output_image_filename << "\n";
00818     _output_image.write(_output_image_filename);
00819 
00820   } else {
00821     // The normal path: reduce the final image by the scale factor to
00822     // antialias the letters.
00823     PNMImage small_image(_output_xsize, _output_ysize, _output_zsize);
00824     small_image.gaussian_filter_from(_gaussian_radius, _output_image);
00825 
00826     // Fix antialiasing, if required.
00827     if (_use_alpha && _bg[3] != 0.0 && _bg[3] != 1.0) {
00828       // If we have some non-transparent background, we need to
00829       // compensate for the antialiasing.
00830       unsmooth_rgb(small_image);
00831     }
00832 
00833 
00834     nout << "Generating " << _output_xsize << " by " << _output_ysize
00835          << " by " << _output_zsize << " image: "
00836          << _output_image_filename << "\n";
00837     small_image.write(_output_image_filename);
00838   }
00839 
00840   write_egg_file();
00841 }
00842 
00843 
00844 int main(int argc, char *argv[]) {
00845   EggMakeFont prog;
00846   prog.parse_command_line(argc, argv);
00847   prog.run();
00848   return 0;
00849 }

Generated on Fri May 2 03:17:08 2003 for Panda-Tool by doxygen1.3