diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 9d33566392..b3ac9bcef3 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -36,12 +36,13 @@ ConfigureFn(config_text) { const bool text_flatten = config_text.GetBool("text-flatten", true); const bool text_update_cleared_glyphs = config_text.GetBool("text-update-cleared-glyphs", false); const int text_anisotropic_degree = config_text.GetInt("text-anisotropic-degree", 1); -const int text_texture_margin = config_text.GetInt("text-texture-margin", 3); -const float text_poly_margin = config_text.GetFloat("text-poly-margin", 1.0f); +const int text_texture_margin = config_text.GetInt("text-texture-margin", 2); +const float text_poly_margin = config_text.GetFloat("text-poly-margin", 0.6f); const int text_page_x_size = config_text.GetInt("text-page-x-size", 256); const int text_page_y_size = config_text.GetInt("text-page-y-size", 256); const float text_point_size = config_text.GetFloat("text-point-size", 10.0f); const float text_pixels_per_unit = config_text.GetFloat("text-pixels-per-unit", 30.0f); +const float text_scale_factor = config_text.GetFloat("text-scale-factor", 2.0f); const bool text_small_caps = config_text.GetBool("text-small-caps", false); const float text_small_caps_scale = config_text.GetFloat("text-small-caps-scale", 0.8f); diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index a113391217..37840d80b2 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -36,6 +36,7 @@ extern const int text_page_x_size; extern const int text_page_y_size; extern const float text_point_size; extern const float text_pixels_per_unit; +extern const float text_scale_factor; extern const bool text_small_caps; extern const float text_small_caps_scale; diff --git a/panda/src/text/dynamicTextFont.I b/panda/src/text/dynamicTextFont.I index 0e2cfa8617..f9439e06b1 100644 --- a/panda/src/text/dynamicTextFont.I +++ b/panda/src/text/dynamicTextFont.I @@ -67,7 +67,7 @@ set_pixels_per_unit(float pixels_per_unit) { // If this assertion fails, you didn't call clear() first. RTFM. nassertr(get_num_pages() == 0, false); - _pixels_per_unit = pixels_per_unit; + _tex_pixels_per_unit = pixels_per_unit; return reset_scale(); } @@ -79,7 +79,43 @@ set_pixels_per_unit(float pixels_per_unit) { //////////////////////////////////////////////////////////////////// INLINE float DynamicTextFont:: get_pixels_per_unit() const { - return _pixels_per_unit; + return _tex_pixels_per_unit; +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::set_scale_factor +// Access: Published +// Description: Sets the factor by which the font is rendered larger +// by the FreeType library before being filtered down to +// its actual size in the texture as specified by +// set_pixels_per_unit(). This may be set to a number +// larger than 1.0 to improve the font's antialiasing +// (since FreeType doesn't really do a swell job of +// antialiasing by itself). There is some performance +// implication for setting this different than 1.0. +// +// This should only be called before any characters have +// been requested out of the font, or immediately after +// calling clear(). +//////////////////////////////////////////////////////////////////// +INLINE bool DynamicTextFont:: +set_scale_factor(float scale_factor) { + // If this assertion fails, you didn't call clear() first. RTFM. + nassertr(get_num_pages() == 0, false); + + _scale_factor = scale_factor; + return reset_scale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::get_scale_factor +// Access: Published +// Description: Returns the antialiasing scale factor. See +// set_scale_factor(). +//////////////////////////////////////////////////////////////////// +INLINE float DynamicTextFont:: +get_scale_factor() const { + return _scale_factor; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index 5a765baeed..0e3b1afd63 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/panda/src/text/dynamicTextFont.cxx @@ -55,7 +55,8 @@ DynamicTextFont(const Filename &font_filename, int face_index) { _page_x_size = text_page_x_size; _page_y_size = text_page_y_size; _point_size = text_point_size; - _pixels_per_unit = text_pixels_per_unit; + _tex_pixels_per_unit = text_pixels_per_unit; + _scale_factor = text_scale_factor; _small_caps = text_small_caps; _small_caps_scale = text_small_caps_scale; @@ -337,14 +338,19 @@ update_filters() { //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::reset_scale // Access: Private -// Description: Resets the font to use the current _point_size and -// _pixels_per_unit. Returns true if successful, false -// otherwise. +// Description: Resets the font based on the current values for +// _point_size, _tex_pixels_per_unit, and _scale_factor. +// Returns true if successful, false otherwise. //////////////////////////////////////////////////////////////////// bool DynamicTextFont:: reset_scale() { + // The font may be rendered larger (by a factor of _scale_factor), + // and then reduced into the texture. Hence the difference between + // _font_pixels_per_unit aned _tex_pixels_per_unit. + _font_pixels_per_unit = _tex_pixels_per_unit * _scale_factor; + float units_per_inch = (points_per_inch / points_per_unit); - int dpi = (int)(_pixels_per_unit * units_per_inch); + int dpi = (int)(_font_pixels_per_unit * units_per_inch); int error = FT_Set_Char_Size(_face, (int)(_point_size * 64), (int)(_point_size * 64), @@ -353,7 +359,7 @@ reset_scale() { // If we were unable to set a particular char size, perhaps we // have a non-scalable font. Try to figure out the closest // available size. - int desired_height = (int)(_pixels_per_unit * _point_size / points_per_unit + 0.5f); + int desired_height = (int)(_font_pixels_per_unit * _point_size / points_per_unit + 0.5f); int best_size = -1; if (_face->num_fixed_sizes > 0) { best_size = 0; @@ -375,7 +381,8 @@ reset_scale() { << "Using " << pixel_height << "-pixel font for " << get_name() << "\n"; - _pixels_per_unit = pixel_height * points_per_unit / _point_size; + _font_pixels_per_unit = pixel_height * points_per_unit / _point_size; + _tex_pixels_per_unit = _font_pixels_per_unit; } } } @@ -388,7 +395,7 @@ reset_scale() { return false; } - _line_height = _face->size->metrics.height / (_pixels_per_unit * 64.0f); + _line_height = _face->size->metrics.height / (_font_pixels_per_unit * 64.0f); // Determine the correct width for a space. error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT); @@ -397,7 +404,7 @@ reset_scale() { _space_advance = 0.25f * _line_height; } else { - _space_advance = _face->glyph->advance.x / (_pixels_per_unit * 64.0f); + _space_advance = _face->glyph->advance.x / (_font_pixels_per_unit * 64.0f); } return true; @@ -427,77 +434,190 @@ make_glyph(int glyph_index) { if (bitmap.width == 0 || bitmap.rows == 0) { // If we got an empty bitmap, it's a special case. - PT(DynamicTextGlyph) glyph = new DynamicTextGlyph(advance / _pixels_per_unit); + PT(DynamicTextGlyph) glyph = + new DynamicTextGlyph(advance / _font_pixels_per_unit); _empty_glyphs.push_back(glyph); return glyph; } else { - DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows); + DynamicTextGlyph *glyph; - if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) { - // This is the easy case: we can memcpy the rendered glyph - // directly into our texture image, one row at a time. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - unsigned char *texture_row = glyph->get_row(yi); - nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL); - memcpy(texture_row, buffer_row, bitmap.width); - buffer_row += bitmap.pitch; - } - - } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { - // This is a little bit more work: we have to expand the - // one-bit-per-pixel bitmap into a one-byte-per-pixel texture. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - unsigned char *texture_row = glyph->get_row(yi); - nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL); - - int bit = 0x80; - unsigned char *b = buffer_row; - for (int xi = 0; xi < bitmap.width; xi++) { - if (*b & bit) { - texture_row[xi] = 0xff; - } else { - texture_row[xi] = 0x00; - } - bit >>= 1; - if (bit == 0) { - ++b; - bit = 0x80; - } - } - - buffer_row += bitmap.pitch; - } - - - } else if (bitmap.pixel_mode == ft_pixel_mode_grays) { - // Here we must expand a grayscale pixmap with n levels of gray - // into our 256-level texture. - unsigned char *buffer_row = bitmap.buffer; - for (int yi = 0; yi < bitmap.rows; yi++) { - unsigned char *texture_row = glyph->get_row(yi); - nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL); - for (int xi = 0; xi < bitmap.width; xi++) { - texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1); - } - buffer_row += bitmap.pitch; - } + if (_tex_pixels_per_unit == _font_pixels_per_unit) { + // 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. + glyph = slot_glyph(bitmap.width, bitmap.rows); + copy_bitmap_to_texture(bitmap, glyph); } else { - text_cat.error() - << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; - } + // Otherwise, we need to copy to a PNMImage first, so we can + // scale it; and then copy it to the texture from there. + int tex_x_size = (int)(bitmap.width / _scale_factor + 0.5f); + int tex_y_size = (int)(bitmap.rows / _scale_factor + 0.5f); + glyph = slot_glyph(tex_x_size, tex_y_size); + PNMImage image(bitmap.width, bitmap.rows, PNMImage::CT_grayscale); + copy_bitmap_to_pnmimage(bitmap, image); + + PNMImage reduced(tex_x_size, tex_y_size, PNMImage::CT_grayscale); + reduced.quick_filter_from(image); + copy_pnmimage_to_texture(reduced, glyph); + } + glyph->_page->mark_dirty(Texture::DF_image); - glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance, - _poly_margin, _pixels_per_unit); + glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance, _poly_margin, + _font_pixels_per_unit, _tex_pixels_per_unit); return glyph; } } +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::copy_bitmap_to_texture +// Access: Private +// Description: Copies a bitmap as rendered by FreeType directly into +// the texture memory image for the indicated glyph, +// without any scaling of pixels. +//////////////////////////////////////////////////////////////////// +void DynamicTextFont:: +copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph) { + if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) { + // This is the easy case: we can memcpy the rendered glyph + // directly into our texture image, one row at a time. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + + unsigned char *texture_row = glyph->get_row(yi); + nassertv(texture_row != (unsigned char *)NULL); + memcpy(texture_row, buffer_row, bitmap.width); + buffer_row += bitmap.pitch; + } + + } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { + // This is a little bit more work: we have to expand the + // one-bit-per-pixel bitmap into a one-byte-per-pixel texture. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + unsigned char *texture_row = glyph->get_row(yi); + nassertv(texture_row != (unsigned char *)NULL); + + int bit = 0x80; + unsigned char *b = buffer_row; + for (int xi = 0; xi < bitmap.width; xi++) { + if (*b & bit) { + texture_row[xi] = 0xff; + } else { + texture_row[xi] = 0x00; + } + bit >>= 1; + if (bit == 0) { + ++b; + bit = 0x80; + } + } + + buffer_row += bitmap.pitch; + } + + + } else if (bitmap.pixel_mode == ft_pixel_mode_grays) { + // Here we must expand a grayscale pixmap with n levels of gray + // into our 256-level texture. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + unsigned char *texture_row = glyph->get_row(yi); + nassertv(texture_row != (unsigned char *)NULL); + for (int xi = 0; xi < bitmap.width; xi++) { + texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1); + } + buffer_row += bitmap.pitch; + } + + } else { + text_cat.error() + << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::copy_bitmap_to_pnmimage +// Access: Private +// Description: Copies a bitmap as rendered by FreeType into a +// PNMImage, so it can be rescaled. +//////////////////////////////////////////////////////////////////// +void DynamicTextFont:: +copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image) { + if (bitmap.pixel_mode == ft_pixel_mode_grays && + bitmap.num_grays == (int)image.get_maxval() + 1) { + // This is the easy case: we can copy the rendered glyph + // directly into our image, one pixel at a time. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + for (int xi = 0; xi < bitmap.width; xi++) { + image.set_gray_val(xi, yi, buffer_row[xi]); + } + buffer_row += bitmap.pitch; + } + + } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { + // This is a little bit more work: we have to expand the + // one-bit-per-pixel bitmap into a one-byte-per-pixel image. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + xelval maxval = image.get_maxval(); + int bit = 0x80; + unsigned char *b = buffer_row; + for (int xi = 0; xi < bitmap.width; xi++) { + if (*b & bit) { + image.set_gray_val(xi, yi, maxval); + } else { + image.set_gray_val(xi, yi, 0); + } + bit >>= 1; + if (bit == 0) { + ++b; + bit = 0x80; + } + } + + buffer_row += bitmap.pitch; + } + + + } else if (bitmap.pixel_mode == ft_pixel_mode_grays) { + // Here we must expand a grayscale pixmap with n levels of gray + // into our 256-level texture. + unsigned char *buffer_row = bitmap.buffer; + for (int yi = 0; yi < bitmap.rows; yi++) { + for (int xi = 0; xi < bitmap.width; xi++) { + image.set_gray(xi, yi, (float)buffer_row[xi] / (bitmap.num_grays - 1)); + } + buffer_row += bitmap.pitch; + } + + } else { + text_cat.error() + << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DynamicTextFont::copy_pnmimage_to_texture +// Access: Private +// Description: Copies a bitmap stored in a PNMImage into +// the texture memory image for the indicated 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); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: DynamicTextFont::slot_glyph // Access: Private diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h index bd95c0ca3a..2fd4effdc8 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -53,6 +53,9 @@ PUBLISHED: INLINE bool set_pixels_per_unit(float pixels_per_unit); INLINE float get_pixels_per_unit() const; + INLINE bool set_scale_factor(float scale_factor); + INLINE float get_scale_factor() const; + INLINE void set_small_caps(bool small_caps); INLINE bool get_small_caps() const; INLINE void set_small_caps_scale(float small_caps_scale); @@ -94,12 +97,17 @@ private: void update_filters(); bool reset_scale(); DynamicTextGlyph *make_glyph(int glyph_index); + void copy_bitmap_to_texture(const FT_Bitmap &bitmap, DynamicTextGlyph *glyph); + void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image); + void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph); DynamicTextGlyph *slot_glyph(int x_size, int y_size); static void initialize_ft_library(); float _point_size; - float _pixels_per_unit; + float _tex_pixels_per_unit; + float _scale_factor; + float _font_pixels_per_unit; bool _small_caps; float _small_caps_scale; int _texture_margin; diff --git a/panda/src/text/dynamicTextGlyph.cxx b/panda/src/text/dynamicTextGlyph.cxx index 5b6110e5c7..1baf114100 100644 --- a/panda/src/text/dynamicTextGlyph.cxx +++ b/panda/src/text/dynamicTextGlyph.cxx @@ -108,18 +108,21 @@ erase() { // the pen should be advanced after drawing this glyph. //////////////////////////////////////////////////////////////////// void DynamicTextGlyph:: -make_geom(int bitmap_top, int bitmap_left, - float advance, float poly_margin, float pixels_per_unit) { +make_geom(int bitmap_top, int bitmap_left, float advance, float poly_margin, + float font_pixels_per_unit, float tex_pixels_per_unit) { nassertv(_page != (DynamicTextPage *)NULL); // This function should not be called twice. nassertv(_geom_count == 0); // Determine the corners of the rectangle in geometric units. - float top = (bitmap_top + poly_margin) / pixels_per_unit; - float left = (bitmap_left - poly_margin) / pixels_per_unit; - float bottom = (bitmap_top - _y_size - poly_margin) / pixels_per_unit; - float right = (bitmap_left + _x_size + poly_margin) / pixels_per_unit; + float tex_poly_margin = poly_margin / tex_pixels_per_unit; + float origin_y = bitmap_top / font_pixels_per_unit; + float origin_x = bitmap_left / font_pixels_per_unit; + float top = origin_y + tex_poly_margin; + float left = origin_x - tex_poly_margin; + float bottom = origin_y - _y_size / tex_pixels_per_unit - tex_poly_margin; + float right = origin_x + _x_size / tex_pixels_per_unit + tex_poly_margin; // And the corresponding corners in UV units. float uv_top = 1.0f - (float)(_y - poly_margin) / _page->get_y_size(); @@ -162,7 +165,7 @@ make_geom(int bitmap_top, int bitmap_left, _state = RenderState::make(TextureAttrib::make(_page), TransparencyAttrib::make(TransparencyAttrib::M_alpha)); - _advance = advance / pixels_per_unit; + _advance = advance / font_pixels_per_unit; } diff --git a/panda/src/text/dynamicTextGlyph.h b/panda/src/text/dynamicTextGlyph.h index b9b1c5bc7c..09394ee92c 100644 --- a/panda/src/text/dynamicTextGlyph.h +++ b/panda/src/text/dynamicTextGlyph.h @@ -51,7 +51,7 @@ public: unsigned char *get_row(int y); void erase(); void make_geom(int top, int left, float advance, float poly_margin, - float pixels_per_unit); + float font_pixels_per_unit, float tex_pixels_per_unit); DynamicTextPage *_page; int _geom_count;