Add pixel format to render font in bitmap format

The purpose it to add later subpixel by storing the bitmap with RGB
channels to have subpixel LCD coverage information.

The colorization and gamma blending will be done when blitting the glyph
on the destination surface.
This commit is contained in:
Francesco Abbate 2020-05-30 18:07:31 +02:00
parent 010966d60d
commit 103336945e
3 changed files with 269 additions and 10 deletions

93
src/agg_pixfmt_alpha8.h Normal file
View File

@ -0,0 +1,93 @@
#pragma once
#include <string.h>
#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;
};
}

160
src/font_renderer_alpha.h Normal file
View File

@ -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<pixfmt_type> base_ren_type;
typedef agg::renderer_scanline_aa_solid<base_ren_type> renderer_solid;
typedef agg::font_engine_freetype_int32 font_engine_type;
typedef agg::font_cache_manager<font_engine_type> 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<font_manager_type::path_adaptor_type> m_curves;
agg::conv_transform<agg::conv_curve<font_manager_type::path_adaptor_type> > 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<class Rasterizer, class Scanline, class RenSolid>
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);
}
};

View File

@ -3,7 +3,7 @@
#include <assert.h>
#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;
}