DynamicTextFont::set_outline()
This commit is contained in:
parent
31091e8974
commit
89aeadbc72
|
|
@ -405,6 +405,164 @@ get_winding_order() const {
|
|||
return _winding_order;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::set_fg
|
||||
// Access: Published
|
||||
// Description: Changes the color of the foreground pixels of the
|
||||
// font as they are rendered into the font texture. The
|
||||
// default is (1, 1, 1, 1), or opaque white, which
|
||||
// allows text created with the font to be colored
|
||||
// individually. Normally, you would not change this
|
||||
// unless you really need a particular color effect to
|
||||
// appear in the font itself.
|
||||
//
|
||||
// This should only be called before any characters have
|
||||
// been requested out of the font, or immediately after
|
||||
// calling clear().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DynamicTextFont::
|
||||
set_fg(const Colorf &fg) {
|
||||
// If this assertion fails, you didn't call clear() first. RTFM.
|
||||
nassertv(get_num_pages() == 0);
|
||||
|
||||
_fg = fg;
|
||||
determine_tex_format();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::get_fg
|
||||
// Access: Published
|
||||
// Description: Returns the color of the foreground pixels of the
|
||||
// font as they are rendered into the font texture.
|
||||
// See set_fg().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Colorf &DynamicTextFont::
|
||||
get_fg() const {
|
||||
return _fg;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::set_bg
|
||||
// Access: Published
|
||||
// Description: Changes the color of the background pixels of the
|
||||
// font as they are rendered into the font texture. The
|
||||
// default is (1, 1, 1, 0), or transparent white, which
|
||||
// allows text created with the font to be colored
|
||||
// individually. (Note that it should not generally be
|
||||
// (0, 0, 0, 0), which would tend to bleed into the
|
||||
// foreground color, unless you have also specified a
|
||||
// outline color of (0, 0, 0, 1)) .
|
||||
//
|
||||
// Normally, you would not change this unless you really
|
||||
// need a particular color effect to appear in the font
|
||||
// itself.
|
||||
//
|
||||
// This should only be called before any characters have
|
||||
// been requested out of the font, or immediately after
|
||||
// calling clear().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DynamicTextFont::
|
||||
set_bg(const Colorf &bg) {
|
||||
// If this assertion fails, you didn't call clear() first. RTFM.
|
||||
nassertv(get_num_pages() == 0);
|
||||
|
||||
_bg = bg;
|
||||
determine_tex_format();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::get_bg
|
||||
// Access: Published
|
||||
// Description: Returns the color of the background pixels of the
|
||||
// font as they are rendered into the font texture.
|
||||
// See set_bg().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Colorf &DynamicTextFont::
|
||||
get_bg() const {
|
||||
return _bg;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::set_outline
|
||||
// Access: Published
|
||||
// Description: Sets up the font to have an outline around each font
|
||||
// letter. This is achieved via a Gaussian post-process
|
||||
// as each letter is generated; there is some runtime
|
||||
// cost for this effect, but it is minimal as each
|
||||
// letter is normally generated only once and then
|
||||
// cached.
|
||||
//
|
||||
// The color is the desired color of the outline, width
|
||||
// is the number of pixels beyond the letter that the
|
||||
// outline extends, and feather is a number in the range
|
||||
// 0.0 .. 1.0 that controls the softness of the outline.
|
||||
// Set the width to 0.0 to disable the outline.
|
||||
//
|
||||
// This should only be called before any characters have
|
||||
// been requested out of the font, or immediately after
|
||||
// calling clear().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DynamicTextFont::
|
||||
set_outline(const Colorf &outline_color, float outline_width,
|
||||
float outline_feather) {
|
||||
// If this assertion fails, you didn't call clear() first. RTFM.
|
||||
nassertv(get_num_pages() == 0);
|
||||
|
||||
_outline_color = outline_color;
|
||||
_outline_width = outline_width;
|
||||
_outline_feather = outline_feather;
|
||||
determine_tex_format();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::get_outline_color
|
||||
// Access: Published
|
||||
// Description: Returns the color of the outline pixels of the
|
||||
// font as they are rendered into the font texture.
|
||||
// See set_outline().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Colorf &DynamicTextFont::
|
||||
get_outline_color() const {
|
||||
return _outline_color;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::get_outline_width
|
||||
// Access: Published
|
||||
// Description: Returns the width of the outline pixels of the
|
||||
// font, as the number of pixels beyond each letter.
|
||||
// See set_outline().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float DynamicTextFont::
|
||||
get_outline_width() const {
|
||||
return _outline_width;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::get_outline_feather
|
||||
// Access: Published
|
||||
// Description: Returns the softness of the outline pixels of the
|
||||
// font, as a value in the range 0.0 to 1.0.
|
||||
// See set_outline().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float DynamicTextFont::
|
||||
get_outline_feather() const {
|
||||
return _outline_feather;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::get_tex_format
|
||||
// Access: Published
|
||||
// Description: Returns the texture format used to render the
|
||||
// individual pages. This is set automatically
|
||||
// according to the colors selected.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE Texture::Format DynamicTextFont::
|
||||
get_tex_format() const {
|
||||
return _tex_format;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::ContourPoint::Constructor
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -63,6 +63,15 @@ DynamicTextFont(const Filename &font_filename, int face_index) {
|
|||
TextFont::set_name(FreetypeFont::get_name());
|
||||
TextFont::_line_height = FreetypeFont::get_line_height();
|
||||
TextFont::_space_advance = FreetypeFont::get_space_advance();
|
||||
|
||||
_fg.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
_bg.set(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
_outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
_outline_width = 0.0f;
|
||||
_outline_feather = 0.0f;
|
||||
_has_outline = false;
|
||||
_tex_format = Texture::F_alpha;
|
||||
_needs_image_processing = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -79,6 +88,15 @@ DynamicTextFont(const char *font_data, int data_length, int face_index) {
|
|||
TextFont::set_name(FreetypeFont::get_name());
|
||||
TextFont::_line_height = FreetypeFont::_line_height;
|
||||
TextFont::_space_advance = FreetypeFont::_space_advance;
|
||||
|
||||
_fg.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
_bg.set(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
_outline_color.set(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
_outline_width = 0.0f;
|
||||
_outline_feather = 0.0f;
|
||||
_has_outline = false;
|
||||
_tex_format = Texture::F_alpha;
|
||||
_needs_image_processing = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -151,7 +169,7 @@ garbage_collect() {
|
|||
Pages::iterator pi;
|
||||
for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
|
||||
DynamicTextPage *page = (*pi);
|
||||
page->garbage_collect();
|
||||
page->garbage_collect(this);
|
||||
}
|
||||
|
||||
return removed_count;
|
||||
|
|
@ -303,6 +321,75 @@ update_filters() {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::determine_tex_format
|
||||
// Access: Private
|
||||
// Description: Examines the _fg, _bg, and _outline colors to
|
||||
// determine the appropriate format for the font pages,
|
||||
// including the outline properties.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DynamicTextFont::
|
||||
determine_tex_format() {
|
||||
nassertv(get_num_pages() == 0);
|
||||
|
||||
_has_outline = (_outline_color != _bg && _outline_width > 0.0f);
|
||||
_needs_image_processing = true;
|
||||
|
||||
bool needs_color = false;
|
||||
bool needs_grayscale = false;
|
||||
bool needs_alpha = false;
|
||||
|
||||
if (_fg[1] != _fg[0] || _fg[2] != _fg[0] ||
|
||||
_bg[1] != _bg[0] || _bg[2] != _bg[0] ||
|
||||
(_has_outline && (_outline_color[1] != _outline_color[0] || _outline_color[2] != _outline_color[0]))) {
|
||||
// At least one of fg, bg, or outline contains a color, not just a
|
||||
// grayscale value.
|
||||
needs_color = true;
|
||||
|
||||
} else if (_fg[0] != 1.0f || _fg[1] != 1.0f || _fg[2] != 1.0f ||
|
||||
_bg[0] != 1.0f || _bg[1] != 1.0f || _bg[2] != 1.0f ||
|
||||
(_has_outline && (_outline_color[0] != 1.0f || _outline_color[1] != 1.0f || _outline_color[2] != 1.0f))) {
|
||||
// fg, bg, and outline contain non-white grayscale values.
|
||||
needs_grayscale = true;
|
||||
}
|
||||
|
||||
if (_fg[3] != 1.0f || _bg[3] != 1.0f ||
|
||||
(_has_outline && (_outline_color[3] != 1.0f))) {
|
||||
// fg, bg, and outline contain non-opaque alpha values.
|
||||
needs_alpha = true;
|
||||
}
|
||||
|
||||
if (needs_color) {
|
||||
if (needs_alpha) {
|
||||
_tex_format = Texture::F_rgba;
|
||||
} else {
|
||||
_tex_format = Texture::F_rgb;
|
||||
}
|
||||
} else if (needs_grayscale) {
|
||||
if (needs_alpha) {
|
||||
_tex_format = Texture::F_luminance_alpha;
|
||||
} else {
|
||||
_tex_format = Texture::F_luminance;
|
||||
}
|
||||
} else {
|
||||
if (needs_alpha) {
|
||||
_tex_format = Texture::F_alpha;
|
||||
|
||||
if (!_has_outline &&
|
||||
_fg == Colorf(1.0f, 1.0f, 1.0f, 1.0f) &&
|
||||
_bg == Colorf(1.0f, 1.0f, 1.0f, 0.0f)) {
|
||||
// This is the standard font color. It can be copied directly
|
||||
// without any need for special processing.
|
||||
_needs_image_processing = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// This won't be a very interesting font.
|
||||
_tex_format = Texture::F_luminance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::make_glyph
|
||||
// Access: Private
|
||||
|
|
@ -432,33 +519,53 @@ make_glyph(int character, int glyph_index) {
|
|||
float tex_x_size = bitmap.width;
|
||||
float tex_y_size = bitmap.rows;
|
||||
|
||||
if (_tex_pixels_per_unit == _font_pixels_per_unit) {
|
||||
int outline = 0;
|
||||
|
||||
if (_tex_pixels_per_unit == _font_pixels_per_unit &&
|
||||
!_needs_image_processing) {
|
||||
// If the bitmap produced from the font doesn't require scaling
|
||||
// before it goes to the texture, we can just copy it directly
|
||||
// into the texture.
|
||||
// or any other processing before it goes to the texture, we can
|
||||
// just copy it directly into the texture.
|
||||
glyph = slot_glyph(character, bitmap.width, bitmap.rows);
|
||||
copy_bitmap_to_texture(bitmap, glyph);
|
||||
|
||||
} else {
|
||||
// Otherwise, we need to copy to a PNMImage first, so we can
|
||||
// scale it; and then copy it to the texture from there.
|
||||
// scale it and/or process it; and then copy it to the texture
|
||||
// from there.
|
||||
tex_x_size /= _scale_factor;
|
||||
tex_y_size /= _scale_factor;
|
||||
int int_x_size = (int)ceil(tex_x_size);
|
||||
int int_y_size = (int)ceil(tex_y_size);
|
||||
int bmp_x_size = (int)(int_x_size * _scale_factor + 0.5f);
|
||||
int bmp_y_size = (int)(int_y_size * _scale_factor + 0.5f);
|
||||
glyph = slot_glyph(character, int_x_size, int_y_size);
|
||||
|
||||
|
||||
PNMImage image(bmp_x_size, bmp_y_size, PNMImage::CT_grayscale);
|
||||
copy_bitmap_to_pnmimage(bitmap, image);
|
||||
|
||||
PNMImage reduced(int_x_size, int_y_size, PNMImage::CT_grayscale);
|
||||
reduced.quick_filter_from(image);
|
||||
copy_pnmimage_to_texture(reduced, glyph);
|
||||
|
||||
outline = (int)ceil(_outline_width);
|
||||
int_x_size += outline * 2;
|
||||
int_y_size += outline * 2;
|
||||
tex_x_size += outline * 2;
|
||||
tex_y_size += outline * 2;
|
||||
glyph = slot_glyph(character, int_x_size, int_y_size);
|
||||
|
||||
if (outline != 0) {
|
||||
// Pad the glyph image to make room for the outline.
|
||||
PNMImage padded(int_x_size, int_y_size, PNMImage::CT_grayscale);
|
||||
padded.copy_sub_image(reduced, outline, outline);
|
||||
copy_pnmimage_to_texture(padded, glyph);
|
||||
|
||||
} else {
|
||||
copy_pnmimage_to_texture(reduced, glyph);
|
||||
}
|
||||
}
|
||||
|
||||
glyph->make_geom(slot->bitmap_top, slot->bitmap_left,
|
||||
glyph->make_geom(slot->bitmap_top + outline * _scale_factor,
|
||||
slot->bitmap_left - outline * _scale_factor,
|
||||
advance, _poly_margin,
|
||||
tex_x_size, tex_y_size,
|
||||
_font_pixels_per_unit, _tex_pixels_per_unit);
|
||||
|
|
@ -541,11 +648,125 @@ copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void DynamicTextFont::
|
||||
copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph) {
|
||||
for (int yi = 0; yi < image.get_y_size(); yi++) {
|
||||
unsigned char *texture_row = glyph->get_row(yi);
|
||||
nassertv(texture_row != (unsigned char *)NULL);
|
||||
for (int xi = 0; xi < image.get_x_size(); xi++) {
|
||||
texture_row[xi] = image.get_gray_val(xi, yi);
|
||||
if (!_needs_image_processing) {
|
||||
// Copy the image directly into the alpha component of the
|
||||
// texture.
|
||||
nassertv(glyph->_page->get_num_components() == 1);
|
||||
for (int yi = 0; yi < image.get_y_size(); yi++) {
|
||||
unsigned char *texture_row = glyph->get_row(yi);
|
||||
nassertv(texture_row != (unsigned char *)NULL);
|
||||
for (int xi = 0; xi < image.get_x_size(); xi++) {
|
||||
texture_row[xi] = image.get_gray_val(xi, yi);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (_has_outline) {
|
||||
// Gaussian blur the glyph to generate an outline.
|
||||
PNMImage outline(image.get_x_size(), image.get_y_size(), PNMImage::CT_grayscale);
|
||||
outline.gaussian_filter_from(_outline_width, image);
|
||||
|
||||
// Filter the resulting outline to make a harder edge.
|
||||
for (int yi = 0; yi < outline.get_y_size(); yi++) {
|
||||
for (int xi = 0; xi < outline.get_x_size(); xi++) {
|
||||
float v = outline.get_gray(xi, yi);
|
||||
if (v == 0.0f) {
|
||||
// Do nothing.
|
||||
} else if (v >= _outline_feather) {
|
||||
// Clamp to 1.
|
||||
outline.set_gray(xi, yi, 1.0);
|
||||
} else {
|
||||
// Linearly scale the range 0 .. _outline_feather onto 0 .. 1.
|
||||
outline.set_gray(xi, yi, v / _outline_feather);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now blend that into the texture.
|
||||
blend_pnmimage_to_texture(outline, glyph, _outline_color);
|
||||
}
|
||||
|
||||
// Colorize the image as we copy it in. This assumes the previous
|
||||
// color at this part of the texture was already initialized to
|
||||
// the background color.
|
||||
blend_pnmimage_to_texture(image, glyph, _fg);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextFont::blend_pnmimage_to_texture
|
||||
// Access: Private
|
||||
// Description: Blends the PNMImage into the appropriate part of the
|
||||
// texture, where 0.0 in the image indicates the color
|
||||
// remains the same, and 1.0 indicates the color is
|
||||
// assigned the indicated foreground color.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DynamicTextFont::
|
||||
blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
|
||||
const Colorf &fg) {
|
||||
Colorf fgv = fg * 255.0f;
|
||||
|
||||
int num_components = glyph->_page->get_num_components();
|
||||
if (num_components == 1) {
|
||||
// Luminance or alpha.
|
||||
int ci = 3;
|
||||
if (glyph->_page->get_format() != Texture::F_alpha) {
|
||||
ci = 0;
|
||||
}
|
||||
|
||||
for (int yi = 0; yi < image.get_y_size(); yi++) {
|
||||
unsigned char *texture_row = glyph->get_row(yi);
|
||||
nassertv(texture_row != (unsigned char *)NULL);
|
||||
for (int xi = 0; xi < image.get_x_size(); xi++) {
|
||||
unsigned char *tr = texture_row + xi;
|
||||
float t = (float)image.get_gray(xi, yi);
|
||||
tr[0] = (unsigned char)(tr[0] + t * (fgv[ci] - tr[0]));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (num_components == 2) {
|
||||
// Luminance + alpha.
|
||||
|
||||
for (int yi = 0; yi < image.get_y_size(); yi++) {
|
||||
unsigned char *texture_row = glyph->get_row(yi);
|
||||
nassertv(texture_row != (unsigned char *)NULL);
|
||||
for (int xi = 0; xi < image.get_x_size(); xi++) {
|
||||
unsigned char *tr = texture_row + xi * 2;
|
||||
float t = (float)image.get_gray(xi, yi);
|
||||
tr[0] = (unsigned char)(tr[0] + t * (fgv[0] - tr[0]));
|
||||
tr[1] = (unsigned char)(tr[1] + t * (fgv[3] - tr[1]));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (num_components == 3) {
|
||||
// RGB.
|
||||
|
||||
for (int yi = 0; yi < image.get_y_size(); yi++) {
|
||||
unsigned char *texture_row = glyph->get_row(yi);
|
||||
nassertv(texture_row != (unsigned char *)NULL);
|
||||
for (int xi = 0; xi < image.get_x_size(); xi++) {
|
||||
unsigned char *tr = texture_row + xi * 3;
|
||||
float t = (float)image.get_gray(xi, yi);
|
||||
tr[0] = (unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
|
||||
tr[1] = (unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
|
||||
tr[2] = (unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
|
||||
}
|
||||
}
|
||||
|
||||
} else { // (num_components == 4)
|
||||
// RGBA.
|
||||
|
||||
for (int yi = 0; yi < image.get_y_size(); yi++) {
|
||||
unsigned char *texture_row = glyph->get_row(yi);
|
||||
nassertv(texture_row != (unsigned char *)NULL);
|
||||
for (int xi = 0; xi < image.get_x_size(); xi++) {
|
||||
unsigned char *tr = texture_row + xi * 4;
|
||||
float t = (float)image.get_gray(xi, yi);
|
||||
tr[0] = (unsigned char)(tr[0] + t * (fgv[2] - tr[0]));
|
||||
tr[1] = (unsigned char)(tr[1] + t * (fgv[1] - tr[1]));
|
||||
tr[2] = (unsigned char)(tr[2] + t * (fgv[0] - tr[2]));
|
||||
tr[3] = (unsigned char)(tr[3] + t * (fgv[3] - tr[3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,17 @@ PUBLISHED:
|
|||
INLINE void set_winding_order(WindingOrder winding_order);
|
||||
INLINE WindingOrder get_winding_order() const;
|
||||
|
||||
INLINE void set_fg(const Colorf &fg);
|
||||
INLINE const Colorf &get_fg() const;
|
||||
INLINE void set_bg(const Colorf &bg);
|
||||
INLINE const Colorf &get_bg() const;
|
||||
INLINE void set_outline(const Colorf &outline_color, float outline_width,
|
||||
float outline_feather);
|
||||
INLINE const Colorf &get_outline_color() const;
|
||||
INLINE float get_outline_width() const;
|
||||
INLINE float get_outline_feather() const;
|
||||
INLINE Texture::Format get_tex_format() const;
|
||||
|
||||
int get_num_pages() const;
|
||||
DynamicTextPage *get_page(int n) const;
|
||||
MAKE_SEQ(get_pages, get_num_pages, get_page);
|
||||
|
|
@ -101,9 +112,12 @@ public:
|
|||
private:
|
||||
void initialize();
|
||||
void update_filters();
|
||||
void determine_tex_format();
|
||||
DynamicTextGlyph *make_glyph(int character, int glyph_index);
|
||||
void copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph);
|
||||
void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph);
|
||||
void blend_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph,
|
||||
const Colorf &fg);
|
||||
DynamicTextGlyph *slot_glyph(int character, int x_size, int y_size);
|
||||
|
||||
void render_wireframe_contours(DynamicTextGlyph *glyph);
|
||||
|
|
@ -129,6 +143,13 @@ private:
|
|||
RenderMode _render_mode;
|
||||
WindingOrder _winding_order;
|
||||
|
||||
Colorf _fg, _bg, _outline_color;
|
||||
float _outline_width;
|
||||
float _outline_feather;
|
||||
bool _has_outline;
|
||||
Texture::Format _tex_format;
|
||||
bool _needs_image_processing;
|
||||
|
||||
typedef pvector< PT(DynamicTextPage) > Pages;
|
||||
Pages _pages;
|
||||
int _preferred_page;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ get_row(int y) {
|
|||
y = _page->get_y_size() - 1 - y;
|
||||
|
||||
int offset = (y * _page->get_x_size()) + x;
|
||||
return _page->modify_ram_image() + offset;
|
||||
int pixel_width = _page->get_num_components() * _page->get_component_width();
|
||||
|
||||
return _page->modify_ram_image() + offset * pixel_width;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -68,18 +70,14 @@ get_row(int y) {
|
|||
// Description: Erases the glyph from the texture map.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DynamicTextGlyph::
|
||||
erase() {
|
||||
erase(DynamicTextFont *font) {
|
||||
nassertv(_page != (DynamicTextPage *)NULL);
|
||||
nassertv(_page->has_ram_image());
|
||||
|
||||
int ysizetop = _page->get_y_size() - 1;
|
||||
int width = _page->get_x_size();
|
||||
unsigned char *buffer = _page->modify_ram_image();
|
||||
|
||||
for (int y = _y; y < _y + _y_size; y++) {
|
||||
int offset = (ysizetop - y) * width + _x;
|
||||
memset(buffer + offset, 0, _x_size);
|
||||
}
|
||||
_page->fill_region(_x, _y, _x_size, _y_size, font->get_bg());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -121,7 +119,7 @@ make_geom(int bitmap_top, int bitmap_left, float advance, float poly_margin,
|
|||
float uv_bottom = 1.0f - ((float)(_y + poly_margin + tex_y_size) + 0.5f) / _page->get_y_size();
|
||||
float uv_right = ((float)(_x + poly_margin + tex_x_size) + 0.5f) / _page->get_x_size();
|
||||
// Create a corresponding triangle pair. We use a pair of indexed
|
||||
// triangles rther than a single triangle strip, to avoid the bad
|
||||
// triangles rather than a single triangle strip, to avoid the bad
|
||||
// vertex duplication behavior with lots of two-triangle strips.
|
||||
PT(GeomVertexData) vdata = new GeomVertexData
|
||||
(string(), GeomVertexFormat::get_v3t2(),
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "textGlyph.h"
|
||||
|
||||
class DynamicTextPage;
|
||||
class DynamicTextFont;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : DynamicTextGlyph
|
||||
|
|
@ -45,7 +46,7 @@ public:
|
|||
|
||||
INLINE bool intersects(int x, int y, int x_size, int y_size) const;
|
||||
unsigned char *get_row(int y);
|
||||
void erase();
|
||||
void erase(DynamicTextFont *font);
|
||||
void make_geom(int top, int left, float advance, float poly_margin,
|
||||
float tex_x_size, float tex_y_size,
|
||||
float font_pixels_per_unit, float tex_pixels_per_unit);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ DynamicTextPage(DynamicTextFont *font, int page_number) :
|
|||
_x_size = _font->get_page_x_size();
|
||||
_y_size = _font->get_page_y_size();
|
||||
|
||||
setup_2d_texture(_x_size, _y_size, T_unsigned_byte, F_alpha);
|
||||
setup_2d_texture(_x_size, _y_size, T_unsigned_byte, font->get_tex_format());
|
||||
|
||||
// Assign a name to the Texture.
|
||||
ostringstream strm;
|
||||
|
|
@ -59,12 +59,15 @@ DynamicTextPage(DynamicTextFont *font, int page_number) :
|
|||
// at the edges at all.
|
||||
set_wrap_u(text_wrap_mode);
|
||||
set_wrap_v(text_wrap_mode);
|
||||
set_border_color(Colorf(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
set_border_color(font->get_bg());
|
||||
|
||||
// Fill the page with the font's background color.
|
||||
fill_region(0, 0, _x_size, _y_size, font->get_bg());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextPage::slot_glyph
|
||||
// Access: Publiic
|
||||
// Access: Public
|
||||
// Description: Finds space within the page for a glyph of the
|
||||
// indicated size. If space is found, creates a new
|
||||
// glyph object and returns it; otherwise, returns NULL.
|
||||
|
|
@ -85,6 +88,89 @@ slot_glyph(int character, int x_size, int y_size, int margin) {
|
|||
return glyph;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextPage::fill_region
|
||||
// Access: Private
|
||||
// Description: Fills a rectangular region of the texture with the
|
||||
// indicated color.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DynamicTextPage::
|
||||
fill_region(int x, int y, int x_size, int y_size, const Colorf &color) {
|
||||
nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
|
||||
int num_components = get_num_components();
|
||||
if (num_components == 1) {
|
||||
// Luminance or alpha.
|
||||
int ci = 3;
|
||||
if (get_format() != Texture::F_alpha) {
|
||||
ci = 0;
|
||||
}
|
||||
|
||||
unsigned char v = (unsigned char)(color[ci] * 255.0f);
|
||||
|
||||
unsigned char *image = modify_ram_image();
|
||||
for (int yi = y; yi < y + y_size; yi++) {
|
||||
unsigned char *row = image + yi * _x_size;
|
||||
memset(row + x, v, x_size);
|
||||
}
|
||||
|
||||
} else if (num_components == 2) {
|
||||
// Luminance + alpha.
|
||||
|
||||
union {
|
||||
unsigned char p[2];
|
||||
PN_uint16 v;
|
||||
} v;
|
||||
|
||||
v.p[0] = (unsigned char)(color[0] * 255.0f);
|
||||
v.p[1] = (unsigned char)(color[3] * 255.0f);
|
||||
|
||||
PN_uint16 *image = (PN_uint16 *)modify_ram_image().p();
|
||||
for (int yi = y; yi < y + y_size; yi++) {
|
||||
PN_uint16 *row = image + yi * _x_size ;
|
||||
for (int xi = x; xi < x + x_size; xi++) {
|
||||
row[xi] = v.v;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (num_components == 3) {
|
||||
// RGB.
|
||||
|
||||
unsigned char p0 = (unsigned char)(color[2] * 255.0f);
|
||||
unsigned char p1 = (unsigned char)(color[1] * 255.0f);
|
||||
unsigned char p2 = (unsigned char)(color[0] * 255.0f);
|
||||
|
||||
unsigned char *image = modify_ram_image();
|
||||
for (int yi = y; yi < y + y_size; yi++) {
|
||||
unsigned char *row = image + yi * _x_size * 3;
|
||||
for (int xi = x; xi < x + x_size; xi++) {
|
||||
row[xi * 3] = p0;
|
||||
row[xi * 3 + 1] = p1;
|
||||
row[xi * 3 + 2] = p2;
|
||||
}
|
||||
}
|
||||
|
||||
} else { // (num_components == 4)
|
||||
// RGBA.
|
||||
union {
|
||||
unsigned char p[4];
|
||||
PN_uint32 v;
|
||||
} v;
|
||||
|
||||
v.p[0] = (unsigned char)(color[2] * 255.0f);
|
||||
v.p[1] = (unsigned char)(color[1] * 255.0f);
|
||||
v.p[2] = (unsigned char)(color[0] * 255.0f);
|
||||
v.p[3] = (unsigned char)(color[3] * 255.0f);
|
||||
|
||||
PN_uint32 *image = (PN_uint32 *)modify_ram_image().p();
|
||||
for (int yi = y; yi < y + y_size; yi++) {
|
||||
PN_uint32 *row = image + yi * _x_size;
|
||||
for (int xi = x; xi < x + x_size; xi++) {
|
||||
row[xi] = v.v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DynamicTextPage::garbage_collect
|
||||
// Access: Private
|
||||
|
|
@ -95,7 +181,7 @@ slot_glyph(int character, int x_size, int y_size, int margin) {
|
|||
// font's index first.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int DynamicTextPage::
|
||||
garbage_collect() {
|
||||
garbage_collect(DynamicTextFont *font) {
|
||||
int removed_count = 0;
|
||||
|
||||
Glyphs new_glyphs;
|
||||
|
|
@ -108,7 +194,7 @@ garbage_collect() {
|
|||
} else {
|
||||
// Drop this one.
|
||||
removed_count++;
|
||||
glyph->erase();
|
||||
glyph->erase(font);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,13 +43,14 @@ public:
|
|||
INLINE int get_x_size() const;
|
||||
INLINE int get_y_size() const;
|
||||
|
||||
void fill_region(int x, int y, int x_size, int y_size, const Colorf &color);
|
||||
|
||||
PUBLISHED:
|
||||
INLINE bool is_empty() const;
|
||||
|
||||
private:
|
||||
int garbage_collect();
|
||||
int garbage_collect(DynamicTextFont *font);
|
||||
|
||||
private:
|
||||
bool find_hole(int &x, int &y, int x_size, int y_size) const;
|
||||
DynamicTextGlyph *find_overlap(int x, int y, int x_size, int y_size) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -589,11 +589,9 @@ assemble_text() {
|
|||
}
|
||||
|
||||
if (properties->has_shadow()) {
|
||||
if (properties->has_shadow_color()) {
|
||||
shadow_state = shadow_state->add_attrib(ColorAttrib::make_flat(properties->get_shadow_color()));
|
||||
if (properties->get_shadow_color()[3] != 1.0) {
|
||||
shadow_state = shadow_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
|
||||
}
|
||||
shadow_state = shadow_state->add_attrib(ColorAttrib::make_flat(properties->get_shadow_color()));
|
||||
if (properties->get_shadow_color()[3] != 1.0) {
|
||||
shadow_state = shadow_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
|
||||
}
|
||||
|
||||
if (properties->has_bin()) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ TextProperties() {
|
|||
_wordwrap_width = 0.0f;
|
||||
_preserve_trailing_whitespace = false;
|
||||
_text_color.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
_shadow_color.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
_shadow_color.set(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
_shadow_offset.set(0.0f, 0.0f);
|
||||
_draw_order = 1;
|
||||
_tab_width = text_tab_width;
|
||||
|
|
|
|||
Loading…
Reference in New Issue