support solid interiors

This commit is contained in:
David Rose 2003-09-10 16:30:38 +00:00
parent 17e4330c0c
commit 1b97d7e178
13 changed files with 490 additions and 83 deletions

View File

@ -30,6 +30,7 @@ ConfigureFn(config_pnmtext) {
const float text_point_size = config_pnmtext.GetFloat("text-point-size", 10.0f);
const float text_pixels_per_unit = config_pnmtext.GetFloat("text-pixels-per-unit", 30.0f);
const float text_scale_factor = config_pnmtext.GetFloat("text-scale-factor", 2.0f);
const bool text_native_antialias = config_pnmtext.GetBool("text-native-antialias", true);
////////////////////////////////////////////////////////////////////
// Function: init_libpnmtext

View File

@ -29,6 +29,7 @@ NotifyCategoryDecl(pnmtext, EXPCL_PANDA, EXPTP_PANDA);
extern const float text_point_size;
extern const float text_pixels_per_unit;
extern const float text_scale_factor;
extern const bool text_native_antialias;
extern EXPCL_PANDA void init_libpnmtext();

View File

@ -143,6 +143,37 @@ get_scale_factor() const {
return _scale_factor;
}
////////////////////////////////////////////////////////////////////
// Function: FreetypeFont::set_native_antialias
// Access: Public
// Description: Sets whether the Freetype library's built-in
// antialias mode is enabled. There are two unrelated
// ways to achieve antialiasing: with Freetype's native
// antialias mode, and with the use of a scale_factor
// greater than one. By default, both modes are
// enabled.
//
// At low resolutions, some fonts may do better with one
// mode or the other. In general, Freetype's native
// antialiasing will produce less blurry results, but
// may introduce more artifacts.
////////////////////////////////////////////////////////////////////
INLINE void FreetypeFont::
set_native_antialias(bool native_antialias) {
_native_antialias = native_antialias;
}
////////////////////////////////////////////////////////////////////
// Function: FreetypeFont::get_native_antialias
// Access: Public
// Description: Returns whether Freetype's built-in antialias mode is
// enabled. See set_native_antialias().
////////////////////////////////////////////////////////////////////
INLINE bool FreetypeFont::
get_native_antialias() const {
return _native_antialias;
}
////////////////////////////////////////////////////////////////////
// Function: FreetypeFont::get_line_height
// Access: Public

View File

@ -49,6 +49,7 @@ FreetypeFont() {
_point_size = text_point_size;
_tex_pixels_per_unit = text_pixels_per_unit;
_scale_factor = text_scale_factor;
_native_antialias = text_native_antialias;
_line_height = 1.0f;
_space_advance = 0.25f;
@ -168,6 +169,29 @@ unload_font() {
}
}
////////////////////////////////////////////////////////////////////
// Function: FreetypeFont::load_glyph
// Access: Protected
// Description: Invokes Freetype to load and render the indicated
// glyph into a bitmap. Returns true if successful,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool FreetypeFont::
load_glyph(int glyph_index) {
int flags = FT_LOAD_RENDER;
if (!_native_antialias) {
flags |= FT_LOAD_MONOCHROME;
}
int error = FT_Load_Glyph(_face, glyph_index, flags);
if (error) {
pnmtext_cat.error()
<< "Unable to render glyph " << glyph_index << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: FreetypeFont::copy_bitmap_to_pnmimage
// Access: Protected

View File

@ -64,6 +64,9 @@ public:
INLINE bool set_scale_factor(float scale_factor);
INLINE float get_scale_factor() const;
INLINE void set_native_antialias(bool native_antialias);
INLINE bool get_native_antialias() const;
INLINE float get_line_height() const;
INLINE float get_space_advance() const;
@ -71,6 +74,7 @@ public:
INLINE static float get_points_per_inch();
protected:
bool load_glyph(int glyph_index);
void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image);
private:
@ -82,6 +86,7 @@ protected:
float _point_size;
float _tex_pixels_per_unit;
float _scale_factor;
bool _native_antialias;
float _font_pixels_per_unit;
float _line_height;

View File

@ -105,5 +105,23 @@ INLINE double PNMTextGlyph::
get_value(int x, int y) const {
nassertr(x >= 0 && x < get_width() &&
y >= 0 && y < get_height(), 0.0);
return _image.get_gray(x, y);
// By convention, the "value" attribute is stored in the blue
// component.
return _image.get_blue(x, y);
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::get_interior_flag
// Access: Public
// Description: Returns true if the indicated pixel represents a
// pixel in the interior of a hollow font, false
// otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PNMTextGlyph::
get_interior_flag(int x, int y) const {
nassertr(x >= 0 && x < get_width() &&
y >= 0 && y < get_height(), false);
// By convention, the "interior_value" attribute is stored in the red
// component.
return _image.get_red_val(x, y) != 0;
}

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "pnmTextGlyph.h"
#include "indent.h"
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::Constructor
@ -41,66 +42,6 @@ PNMTextGlyph::
~PNMTextGlyph() {
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::rescale
// Access: Public
// Description: After the image has been rendered large by FreeType,
// scales it small again for placing.
////////////////////////////////////////////////////////////////////
void PNMTextGlyph::
rescale(double scale_factor) {
if (scale_factor == 1.0) {
return;
}
nassertv(scale_factor != 0.0);
_advance /= scale_factor;
_int_advance = (int)floor(_advance + 0.5);
if (_image.is_valid()) {
int orig_x_size = _image.get_x_size();
int orig_y_size = _image.get_y_size();
int orig_left = _left;
int orig_top = _top;
// Pad the image by a few pixels all around to allow for
// antialiasing at the edges.
int extra_pad = (int)ceil(scale_factor);
orig_x_size += 2*extra_pad;
orig_y_size += 2*extra_pad;
orig_left -= extra_pad;
orig_top += extra_pad;
// Now compute the reduced size.
int new_x_size = (int)ceil(orig_x_size / scale_factor);
int new_y_size = (int)ceil(orig_y_size / scale_factor);
int new_left = (int)floor(orig_left / scale_factor);
int new_top = (int)ceil(orig_top / scale_factor);
// And scale those back up so we can determine the amount of
// additional padding we need to make the pixels remain in the
// right place after the integer reduction.
int old_x_size = (int)(new_x_size * scale_factor + 0.5);
int old_y_size = (int)(new_y_size * scale_factor + 0.5);
int old_left = (int)(new_left * scale_factor + 0.5);
int old_top = (int)(new_top * scale_factor + 0.5);
int pad_left = orig_left - old_left;
int pad_top = old_top - orig_top;
// These shouldn't go negative.
nassertv(extra_pad + pad_left >= 0 && extra_pad + pad_top >= 0);
PNMImage enlarged(old_x_size, old_y_size, 1);
enlarged.copy_sub_image(_image, pad_left + extra_pad, pad_top + extra_pad);
_image.clear(new_x_size, new_y_size, 1);
_image.quick_filter_from(enlarged);
_left = new_left;
_top = new_top;
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::place
// Access: Public
@ -151,3 +92,247 @@ place(PNMImage &dest_image, int xp, int yp, const Colorf &fg) {
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::place
// Access: Public
// Description: This flavor of place() also fills in the interior
// color. This requires that determine_interior was
// called earlier.
////////////////////////////////////////////////////////////////////
void PNMTextGlyph::
place(PNMImage &dest_image, int xp, int yp, const Colorf &fg,
const Colorf &interior) {
if (!_image.is_valid()) {
// If we have no image, do nothing.
return;
}
RGBColord fg_rgb(fg[0], fg[1], fg[2]);
double fg_alpha = fg[3];
RGBColord interior_rgb(interior[0], interior[1], interior[2]);
double interior_alpha = interior[3];
int left = xp + _left;
int top = yp - _top;
int right = left + _image.get_x_size();
int bottom = top + _image.get_y_size();
// Clip the glyph to the destination image.
int cleft = max(left, 0);
int ctop = max(top, 0);
int cright = min(right, dest_image.get_x_size());
int cbottom = min(bottom, dest_image.get_y_size());
for (int y = ctop; y < cbottom; y++) {
for (int x = cleft; x < cright; x++) {
double gval = get_value(x - left, y - top);
if (gval == 1.0) {
dest_image.set_xel(x, y, fg_rgb);
if (dest_image.has_alpha()) {
dest_image.set_alpha(x, y, fg_alpha);
}
} else if (gval > 0.0) {
bool is_interior = get_interior_flag(x - left, y - top);
RGBColord bg_rgb;
if (is_interior) {
bg_rgb = interior_rgb;
} else {
bg_rgb = dest_image.get_xel(x, y);
}
dest_image.set_xel(x, y, fg_rgb * gval + bg_rgb * (1.0 - gval));
if (dest_image.has_alpha()) {
double bg_alpha;
if (is_interior) {
bg_alpha = interior_alpha;
} else {
bg_alpha = dest_image.get_alpha(x, y);
}
dest_image.set_alpha(x, y, fg_alpha * gval + bg_alpha * (1.0 - gval));
}
} else { // gval == 0.0
bool is_interior = get_interior_flag(x - left, y - top);
if (is_interior) {
dest_image.set_xel(x, y, interior_rgb);
if (dest_image.has_alpha()) {
dest_image.set_alpha(x, y, interior_alpha);
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::determine_interior
// Access: Private
// Description: Once the glyph has been generated, but before it has
// been scaled down by _scale_factor, walk through the
// glyph and try to determine which parts represent the
// interior portions of a hollow font, and mark them so
// they may be properly colored.
////////////////////////////////////////////////////////////////////
void PNMTextGlyph::
determine_interior() {
// We will use the red component as a working buffer. First, we
// fill the whole thing to maxval.
int x_size = _image.get_x_size();
int y_size = _image.get_y_size();
xelval maxval = _image.get_maxval();
for (int yi = 0; yi < y_size; yi++) {
for (int xi = 0; xi < x_size; xi++) {
_image.set_red_val(xi, yi, maxval);
}
}
// Now we recursively analyze the image to determine the number of
// walls between each pixel and any edge. All outer edge pixels
// have a value of 0; all dark pixels adjacent to those pixels have
// a value of 1, and light pixels adjacent to those have a value of
// 2, and so on.
_scan_interior_points.clear();
for (int yi = 0; yi < y_size; yi++) {
scan_interior(0, yi, 0, false, 0);
scan_interior(x_size - 1, yi, 0, false, 0);
}
for (int xi = 0; xi < x_size; xi++) {
scan_interior(xi, 0, 0, false, 0);
scan_interior(xi, y_size - 1, 0, false, 0);
}
// Pick up any points that we couldn't visit recursively because of
// the lame stack limit on Windows.
while (!_scan_interior_points.empty()) {
int index = _scan_interior_points.back();
_scan_interior_points.pop_back();
int y = index / _image.get_x_size();
int x = index % _image.get_x_size();
xelval new_code = _image.get_red_val(x, y);
bool this_dark = (_image.get_blue_val(x, y) > 0);
scan_interior(x - 1, y, new_code, this_dark, 0);
scan_interior(x, y - 1, new_code, this_dark, 0);
scan_interior(x + 1, y, new_code, this_dark, 0);
scan_interior(x, y + 1, new_code, this_dark, 0);
}
_scan_interior_points.clear();
// Finally, go back and set any pixel whose red value is two more
// than a multiple of 4 to dark. This indicates the interior part
// of a hollow font.
for (int yi = 0; yi < y_size; yi++) {
for (int xi = 0; xi < x_size; xi++) {
xelval code = _image.get_red_val(xi, yi);
if (((code + 2) & 0x3) == 0) {
_image.set_red_val(xi, yi, maxval);
} else {
_image.set_red_val(xi, yi, 0);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::scan_interior
// Access: Private
// Description: Recursively scans the image for interior pixels. On
// completion, the image's red channel will be filled
// with 0, 1, 2, etc., representing the number of edges
// between each pixel and the border.
////////////////////////////////////////////////////////////////////
void PNMTextGlyph::
scan_interior(int x, int y, xelval new_code, bool neighbor_dark,
int recurse_level) {
if (x < 0 || y < 0 || x >= _image.get_x_size() || y >= _image.get_y_size()) {
return;
}
bool this_dark = (_image.get_blue_val(x, y) > 0);
if (this_dark != neighbor_dark) {
// If we just crossed an edge, we have to increment the code.
if (new_code < _image.get_maxval()) {
new_code++;
}
nassertv(new_code > 0);
}
if (new_code < _image.get_red_val(x, y)) {
_image.set_red_val(x, y, new_code);
recurse_level++;
if (recurse_level > 1024) {
// To cobble around a lame Windows limitation on the length of
// the stack, we must prevent the recursion from going too deep.
// But we still need to remember this pixel so we can come back
// to it later.
int index = y * _image.get_x_size() + x;
_scan_interior_points.push_back(index);
} else {
scan_interior(x - 1, y, new_code, this_dark, recurse_level);
scan_interior(x, y - 1, new_code, this_dark, recurse_level);
scan_interior(x + 1, y, new_code, this_dark, recurse_level);
scan_interior(x, y + 1, new_code, this_dark, recurse_level);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextGlyph::rescale
// Access: Private
// Description: After the image has been rendered large by FreeType,
// scales it small again for placing.
////////////////////////////////////////////////////////////////////
void PNMTextGlyph::
rescale(double scale_factor) {
if (scale_factor == 1.0) {
return;
}
nassertv(scale_factor != 0.0);
_advance /= scale_factor;
_int_advance = (int)floor(_advance + 0.5);
if (_image.is_valid()) {
int orig_x_size = _image.get_x_size();
int orig_y_size = _image.get_y_size();
int orig_left = _left;
int orig_top = _top;
// Pad the image by a few pixels all around to allow for
// antialiasing at the edges.
int extra_pad = (int)ceil(scale_factor);
orig_x_size += 2*extra_pad;
orig_y_size += 2*extra_pad;
orig_left -= extra_pad;
orig_top += extra_pad;
// Now compute the reduced size.
int new_x_size = (int)ceil(orig_x_size / scale_factor);
int new_y_size = (int)ceil(orig_y_size / scale_factor);
int new_left = (int)floor(orig_left / scale_factor);
int new_top = (int)ceil(orig_top / scale_factor);
// And scale those back up so we can determine the amount of
// additional padding we need to make the pixels remain in the
// right place after the integer reduction.
int old_x_size = (int)(new_x_size * scale_factor + 0.5);
int old_y_size = (int)(new_y_size * scale_factor + 0.5);
int old_left = (int)(new_left * scale_factor + 0.5);
int old_top = (int)(new_top * scale_factor + 0.5);
int pad_left = orig_left - old_left;
int pad_top = old_top - orig_top;
// These shouldn't go negative.
nassertv(extra_pad + pad_left >= 0 && extra_pad + pad_top >= 0);
PNMImage enlarged(old_x_size, old_y_size, _image.get_num_channels());
enlarged.copy_sub_image(_image, pad_left + extra_pad, pad_top + extra_pad);
_image.clear(new_x_size, new_y_size, _image.get_num_channels());
_image.quick_filter_from(enlarged);
_left = new_left;
_top = new_top;
}
}

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "pnmImage.h"
#include "vector_int.h"
////////////////////////////////////////////////////////////////////
// Class : PNMTextGlyph
@ -32,11 +33,12 @@ public:
PNMTextGlyph(double advance);
~PNMTextGlyph();
void rescale(double scale_factor);
INLINE int get_advance() const;
void place(PNMImage &dest_image, int xp, int yp,
const Colorf &fg = Colorf(0.0f, 0.0f, 0.0f, 1.0f));
const Colorf &fg);
void place(PNMImage &dest_image, int xp, int yp,
const Colorf &fg, const Colorf &interior);
INLINE int get_left() const;
INLINE int get_right() const;
@ -46,13 +48,20 @@ public:
INLINE int get_height() const;
INLINE int get_width() const;
INLINE double get_value(int x, int y) const;
INLINE bool get_interior_flag(int x, int y) const;
private:
void determine_interior();
void scan_interior(int x, int y, xelval new_code, bool neighbor_dark,
int recurse_level);
void rescale(double scale_factor);
PNMImage _image;
int _top;
int _left;
double _advance;
int _int_advance;
vector_int _scan_interior_points;
friend class PNMTextMaker;
};

View File

@ -35,9 +35,7 @@ is_valid() const {
////////////////////////////////////////////////////////////////////
INLINE void PNMTextMaker::
set_align(PNMTextMaker::Alignment align_type) {
if (_align != align_type) {
_align = align_type;
}
_align = align_type;
}
////////////////////////////////////////////////////////////////////
@ -50,6 +48,86 @@ get_align() const {
return _align;
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::set_interior_flag
// Access: Published
// Description: Sets the flag that indicates whether the interior of
// hollow fonts is identified as a preprocess as each
// glyph is loaded. If this flag is true, you may
// specify an interior color along with a fg and bg
// color when you place text; if the flag is false, the
// interior color is ignored.
//
// It is generally best to set_native_antialias(0) when
// using this feature. Also, this works best when the
// pixel size is not very small.
////////////////////////////////////////////////////////////////////
INLINE void PNMTextMaker::
set_interior_flag(bool interior_flag) {
if (_interior_flag != interior_flag) {
_interior_flag = interior_flag;
empty_cache();
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::get_interior_flag
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool PNMTextMaker::
get_interior_flag() const {
return _interior_flag;
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::set_fg
// Access: Published
// Description: Sets the foreground color of text that will be
// generated by future calls to generate_into(). This
// is the color that all of the "on" pixels in the font
// will show as.
////////////////////////////////////////////////////////////////////
INLINE void PNMTextMaker::
set_fg(const Colorf &fg) {
_fg = fg;
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::get_fg
// Access: Published
// Description: Returns the foreground color of text that will be
// generated by future calls to generate_into().
////////////////////////////////////////////////////////////////////
INLINE const Colorf &PNMTextMaker::
get_fg() const {
return _fg;
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::set_interior
// Access: Published
// Description: Sets the color that will be used to render the
// interior portions of hollow fonts in future calls to
// generate_into(). This is respected only if
// interior_flag is true.
////////////////////////////////////////////////////////////////////
INLINE void PNMTextMaker::
set_interior(const Colorf &interior) {
_interior = interior;
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::get_interior
// Access: Published
// Description: Returns the color that will be used to render the
// interior portions of hollow fonts.
////////////////////////////////////////////////////////////////////
INLINE const Colorf &PNMTextMaker::
get_interior() const {
return _interior;
}
////////////////////////////////////////////////////////////////////
// Function: PNMTextMaker::generate_into
// Access: Public

View File

@ -95,7 +95,11 @@ generate_into(const wstring &text, PNMImage &dest_image, int x, int y) {
for (ti = text.begin(); ti != text.end(); ++ti) {
int ch = (*ti);
PNMTextGlyph *glyph = get_glyph(ch);
glyph->place(dest_image, xp, yp);
if (_interior_flag) {
glyph->place(dest_image, xp, yp, _fg, _interior);
} else {
glyph->place(dest_image, xp, yp, _fg);
}
xp += glyph->get_advance();
}
}
@ -130,6 +134,9 @@ get_glyph(int character) {
void PNMTextMaker::
initialize() {
_align = A_left;
_interior_flag = false;
_fg.set(0.0f, 0.0f, 0.0f, 1.0f);
_interior.set(0.5f, 0.5f, 0.5f, 1.0f);
}
////////////////////////////////////////////////////////////////////
@ -140,9 +147,7 @@ initialize() {
////////////////////////////////////////////////////////////////////
PNMTextGlyph *PNMTextMaker::
make_glyph(int glyph_index) {
int error = FT_Load_Glyph(_face, glyph_index, FT_LOAD_RENDER);
if (error) {
nout << "Unable to render glyph " << glyph_index << "\n";
if (!load_glyph(glyph_index)) {
return (PNMTextGlyph *)NULL;
}
@ -160,12 +165,15 @@ make_glyph(int glyph_index) {
} else {
PNMTextGlyph *glyph = new PNMTextGlyph(advance);
PNMImage &glyph_image = glyph->_image;
glyph_image.clear(bitmap.width, bitmap.rows, 1);
glyph_image.clear(bitmap.width, bitmap.rows, 3);
copy_bitmap_to_pnmimage(bitmap, glyph_image);
glyph->_top = slot->bitmap_top;
glyph->_left = slot->bitmap_left;
if (_interior_flag) {
glyph->determine_interior();
}
glyph->rescale(_scale_factor);
return glyph;
}

View File

@ -57,6 +57,15 @@ public:
INLINE void set_align(Alignment align_type);
INLINE Alignment get_align() const;
INLINE void set_interior_flag(bool interior_flag);
INLINE bool get_interior_flag() const;
INLINE void set_fg(const Colorf &fg);
INLINE const Colorf &get_fg() const;
INLINE void set_interior(const Colorf &interior);
INLINE const Colorf &get_interior() const;
INLINE void generate_into(const string &text,
PNMImage &dest_image, int x, int y);
void generate_into(const wstring &text,
@ -75,6 +84,9 @@ private:
Glyphs _glyphs;
Alignment _align;
bool _interior_flag;
Colorf _fg;
Colorf _interior;
};
#include "pnmTextMaker.I"

View File

@ -81,6 +81,15 @@ EggMakeFont() : EggWriter(true, false) {
"will not include an alpha component.",
&EggMakeFont::dispatch_color, NULL, &_bg[0]);
add_option
("interior", "r,g,b[,a]", 0,
"Specifies the color to render the interior part of a hollow font. "
"This is a special effect that involves analysis of the bitmap after "
"the font has been rendered, and so is more effective when the pixel "
"size is large. It also implies -noaa (but you can use a scale "
"factor with -sf to achieve antialiasing).",
&EggMakeFont::dispatch_color, &_got_interior, &_interior[0]);
add_option
("chars", "range", 0,
"Specifies the characters of the font that are used. The range "
@ -110,13 +119,14 @@ EggMakeFont() : EggWriter(true, false) {
add_option
("bp", "n", 0,
"The number of extra pixels around a single character in the "
"generated polygon. [1.0]",
"generated polygon. This may be a floating-point number.",
&EggMakeFont::dispatch_double, NULL, &_poly_margin);
add_option
("bt", "n", 0,
"The number of extra pixels around each character in the texture map.",
&EggMakeFont::dispatch_double, NULL, &_tex_margin);
"The number of extra pixels around each character in the texture map. "
"This may only be an integer.",
&EggMakeFont::dispatch_int, NULL, &_tex_margin);
add_option
("sf", "factor", 0,
@ -128,6 +138,14 @@ EggMakeFont() : EggWriter(true, false) {
"matching font to the desired pixel size.",
&EggMakeFont::dispatch_double, NULL, &_scale_factor);
add_option
("noaa", "", 0,
"Disable low-level antialiasing by the Freetype library. "
"This is unrelated to the antialiasing that is applied due to the "
"scale factor specified by -sf; you may have either one, neither, or "
"both kinds of antialiasing enabled.",
&EggMakeFont::dispatch_none, &_no_native_aa);
add_option
("face", "index", 0,
"Specify the face index of the particular face within the font file "
@ -137,10 +155,11 @@ EggMakeFont() : EggWriter(true, false) {
_fg.set(1.0, 1.0, 1.0, 1.0);
_bg.set(1.0, 1.0, 1.0, 0.0);
_interior.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;
_tex_margin = 2;
_scale_factor = 2.0;
_face_index = 0;
@ -183,6 +202,8 @@ run() {
}
_text_maker->set_point_size(_point_size);
_text_maker->set_scale_factor(_scale_factor);
_text_maker->set_native_antialias(!_no_native_aa && !_got_interior);
_text_maker->set_interior_flag(_got_interior);
_text_maker->set_pixels_per_unit(_pixels_per_unit);
if (_range.is_empty()) {
@ -192,20 +213,27 @@ run() {
}
if (_output_image_pattern.empty()) {
// Create a default texture filename pattern.
_output_image_pattern = _input_font_filename.get_fullpath_wo_extension() + "%03d.rgb";
_output_image_pattern = get_output_filename().get_fullpath_wo_extension() + "%03d.rgb";
}
// 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_alpha = (_fg[3] != 1.0 || _bg[3] != 1.0 || _interior[3] != 1.0);
bool needs_color = (_fg[0] != _fg[1] || _fg[1] != _fg[2] ||
_bg[0] != _bg[1] || _bg[1] != _bg[2]);
_bg[0] != _bg[1] || _bg[1] != _bg[2] ||
_interior[0] != _interior[1] || _interior[1] != _interior[2]);
cerr << "needs_alpha = " << needs_alpha << "\n"
<< "needs_color = " << needs_color << "\n"
<< "fg = " << _fg << "\n"
<< "bg = " << _bg << "\n"
<< "interior = " << _interior << "\n";
if (needs_alpha) {
if (needs_color) {
_num_channels = 4;
_format = EggTexture::F_rgba;
} else {
if (_fg[0] == 1.0 && _bg[0] == 1.0) {
if (_fg[0] == 1.0 && _bg[0] == 1.0 && _interior[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.
@ -404,8 +432,13 @@ make_tref(PNMTextGlyph *glyph, int character) {
if (image.has_alpha()) {
image.alpha_fill(_bg[3]);
}
glyph->place(image, -glyph->get_left() + _tex_margin,
glyph->get_top() + _tex_margin, _fg);
if (_got_interior) {
glyph->place(image, -glyph->get_left() + _tex_margin,
glyph->get_top() + _tex_margin, _fg, _interior);
} else {
glyph->place(image, -glyph->get_left() + _tex_margin,
glyph->get_top() + _tex_margin, _fg);
}
if (!image.write(texture_filename)) {
nout << "Unable to write " << texture_filename << "\n";

View File

@ -60,13 +60,15 @@ private:
EggTexture *make_tref(PNMTextGlyph *glyph, int character);
private:
Colorf _fg, _bg;
Colorf _fg, _bg, _interior;
bool _got_interior;
RangeDescription _range;
double _pixels_per_unit;
double _point_size;
double _poly_margin;
double _tex_margin;
int _tex_margin;
double _scale_factor;
bool _no_native_aa;
Filename _input_font_filename;
int _face_index;