diff --git a/src/agg_pixfmt_alpha8.h b/src/agg_pixfmt_alpha8.h new file mode 100644 index 00000000..b7b84f8d --- /dev/null +++ b/src/agg_pixfmt_alpha8.h @@ -0,0 +1,93 @@ +#pragma once + +#include +#include "agg_basics.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + // This is a special purpose color type that only has the alpha channel. + // It can be thought as a gray color but with the intensity always on. + // It is actually used to store coverage information. + struct alpha8 + { + typedef int8u value_type; + typedef int32u calc_type; + typedef int32 long_type; + enum base_scale_e + { + base_shift = 8, + base_scale = 1 << base_shift, + base_mask = base_scale - 1 + }; + + value_type a; + + //-------------------------------------------------------------------- + alpha8(unsigned a_=base_mask) : + a(int8u(a_)) {} + }; + + // Pixer format to store coverage information. + class pixfmt_alpha8 + { + public: + typedef alpha8 color_type; + typedef int8u value_type; + typedef int32u calc_type; + typedef typename agg::rendering_buffer::row_data row_data; + + //-------------------------------------------------------------------- + pixfmt_alpha8(rendering_buffer& rb): m_rbuf(&rb) + { + } + + //-------------------------------------------------------------------- + unsigned width() const { + return m_rbuf->width(); + } + unsigned height() const { + return m_rbuf->height(); + } + + // This method should never be called when using the scanline_u8. + // The use of scanline_p8 should be avoided because if does not works + // properly for rendering fonts because single hspan are split in many + // hline/hspan elements and pixel whitening happens. + void blend_hline(int x, int y, unsigned len, + const color_type& c, int8u cover) + { } + + void copy_hline(int x, int y, unsigned len, const color_type& c) + { + value_type* p = (value_type*) m_rbuf->row_ptr(y) + x; + do + { + *p = c.a; + p++; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + value_type* p = (value_type*) m_rbuf->row_ptr(y) + x; + do + { + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + *p = alpha; + p++; + ++covers; + } + while(--len); + } + + private: + rendering_buffer* m_rbuf; + }; + +} diff --git a/src/font_renderer_alpha.h b/src/font_renderer_alpha.h new file mode 100644 index 00000000..5d8681f5 --- /dev/null +++ b/src/font_renderer_alpha.h @@ -0,0 +1,160 @@ +#pragma once + +#include "agg_basics.h" +#include "agg_conv_curve.h" +#include "agg_conv_transform.h" +#include "agg_gamma_lut.h" +#include "agg_font_freetype.h" +#include "agg_pixfmt_alpha8.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_renderer_primitives.h" +#include "agg_renderer_scanline.h" +#include "agg_rendering_buffer.h" +#include "agg_scanline_u.h" + +class font_renderer_alpha +{ + typedef agg::pixfmt_alpha8 pixfmt_type; + typedef agg::renderer_base base_ren_type; + typedef agg::renderer_scanline_aa_solid renderer_solid; + typedef agg::font_engine_freetype_int32 font_engine_type; + typedef agg::font_cache_manager font_manager_type; + + font_engine_type m_feng; + font_manager_type m_fman; + + // Font rendering options. + bool m_hinting; + bool m_kerning; + + bool m_font_loaded; + + // Pipeline to process the vectors glyph paths (curves + contour) + agg::trans_affine m_mtx; + agg::conv_curve m_curves; + agg::conv_transform > m_trans; +public: + typedef agg::pixfmt_alpha8::color_type color_type; + + font_renderer_alpha(bool hinting, bool kerning): + m_feng(), + m_fman(m_feng), + m_hinting(hinting), + m_kerning(kerning), + m_font_loaded(false), + m_curves(m_fman.path_adaptor()), + m_trans(m_curves, m_mtx) + { } + + bool get_font_vmetrics(int& ascender, int& descender) { + int face_height = m_feng.face_height(); + if (face_height > 0) { + double current_height = m_feng.height(); + m_feng.height(1.0); + ascender = m_feng.ascender() * face_height; + descender = m_feng.descender() * face_height; + m_feng.height(current_height); + return true; + } + return false; + } + + float scale_for_em_to_pixels(float size) { + int units_per_em = m_feng.face_units_em(); + if (units_per_em > 0) { + return size / units_per_em; + } + return 0.0; + } + + bool load_font(const char *font_filename) { + if(m_feng.load_font(font_filename, 0, agg::glyph_ren_outline)) { + m_font_loaded = true; + } + return m_font_loaded; + } + + template + void draw_text(Rasterizer& ras, Scanline& sl, + RenSolid& ren_solid, const color_type color, + const char* text, + double& x, double& y, double height) + { + const double scale_x = 100; + + m_feng.height(height); + m_feng.width(height * scale_x); + m_feng.hinting(m_hinting); + + const char* p = text; + + // Represent the delta in x scaled by scale_x. + double x_delta = 0; + double start_x = x; + + while(*p) + { + if(*p == '\n') + { + x_delta = 0; + y -= height * 1.25; + ++p; + continue; + } + + const agg::glyph_cache* glyph = m_fman.glyph(*p); + if(glyph) + { + if(m_kerning) + { + m_fman.add_kerning(&x_delta, &y); + } + + m_fman.init_embedded_adaptors(glyph, 0, 0); + if(glyph->data_type == agg::glyph_data_outline) + { + double ty = m_hinting ? floor(y + 0.5) : y; + ras.reset(); + m_mtx.reset(); + m_mtx *= agg::trans_affine_scaling(1.0 / scale_x, 1); + m_mtx *= agg::trans_affine_translation(start_x + x_delta / scale_x, ty); + ras.add_path(m_trans); + ren_solid.color(color); + agg::render_scanlines(ras, sl, ren_solid); + } + + // increment pen position + x_delta += glyph->advance_x; + y += glyph->advance_y; + } + ++p; + } + // Update x value befor returning. + x += x_delta / scale_x; + } + + void clear(agg::rendering_buffer& ren_buf, const color_type color) { + pixfmt_type pf(ren_buf); + base_ren_type ren_base(pf); + ren_base.clear(color); + } + + void render_text(agg::rendering_buffer& ren_buf, + const double text_size, + const color_type text_color, + double& x, double& y, + const char *text) + { + if (!m_font_loaded) { + return; + } + agg::scanline_u8 sl; + agg::rasterizer_scanline_aa<> ras; + ras.clip_box(0, 0, ren_buf.width(), ren_buf.height()); + + agg::pixfmt_alpha8 pf(ren_buf); + base_ren_type ren_base(pf); + renderer_solid ren_solid(ren_base); + draw_text(ras, sl, ren_solid, text_color, text, x, y, text_size); + } +}; diff --git a/tests/agg_font_render_test.cpp b/tests/agg_font_render_test.cpp index bcd1668e..7a50a341 100644 --- a/tests/agg_font_render_test.cpp +++ b/tests/agg_font_render_test.cpp @@ -3,7 +3,7 @@ #include #include "SDL_surface.h" -#include "font_render_lcd.h" +#include "font_renderer_alpha.h" #define MAX_GLYPHSET 256 @@ -22,7 +22,7 @@ struct GlyphSetA { }; struct RenFontA { - font_renderer_lcd *renderer; + font_renderer_alpha *renderer; float size; int height; }; @@ -57,9 +57,7 @@ RenFontA* ren_load_font_agg(const char *filename, float size) { font = (RenFontA *) check_alloc(calloc(1, sizeof(RenFontA))); font->size = size; - // FIXME: we should remove the gamma correction. - // So that we have just the coverage. - font->renderer = new font_renderer_lcd(true, false, false, 1.8); + font->renderer = new font_renderer_alpha(true, false); font->renderer->load_font(filename); int ascender, descender; @@ -75,21 +73,22 @@ RenFontA* ren_load_font_agg(const char *filename, float size) { } static GlyphSetA* load_glyphset_agg(RenFontA *font, int idx) { - const int pixel_size = 4; + const int pixel_size = 1; GlyphSetA *set = (GlyphSetA *) check_alloc(calloc(1, sizeof(GlyphSetA))); /* init image */ - int width = 512; // 128; - int height = 512; // 128; + int width = 128; + int height = 128; retry: set->image = ren_new_image(width, height); - memset(set->image->pixels, 0xff, width * height * pixel_size); + memset(set->image->pixels, 0x00, width * height * pixel_size); agg::rendering_buffer ren_buf((agg::int8u *) set->image->pixels, width, height, -width * pixel_size); + // FIXME: figure out how to precisely layout each glyph. double x = 4, y = height - font->size * 3 / 2; int res = 0; - const agg::rgba8 text_color(0x00, 0x00, 0x00); + const agg::alpha8 text_color(0xff); for (int i = 0; i < 256; i++) { if (x + font->size * 3 / 2 > width) { x = 4; @@ -113,6 +112,13 @@ retry: goto retry; } + /* convert 8bit data to 32bit */ + for (int i = width * height - 1; i >= 0; i--) { + uint8_t n = *((uint8_t*) set->image->pixels + i); + RenColor c = {0xff, 0xff, 0xff, n}; + set->image->pixels[i] = c; + } + return set; }