mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-18 04:33:04 -04:00
support solid interiors
This commit is contained in:
parent
17e4330c0c
commit
1b97d7e178
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user