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:
parent
010966d60d
commit
103336945e
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
|
@ -3,7 +3,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "SDL_surface.h"
|
#include "SDL_surface.h"
|
||||||
#include "font_render_lcd.h"
|
#include "font_renderer_alpha.h"
|
||||||
|
|
||||||
#define MAX_GLYPHSET 256
|
#define MAX_GLYPHSET 256
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ struct GlyphSetA {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RenFontA {
|
struct RenFontA {
|
||||||
font_renderer_lcd *renderer;
|
font_renderer_alpha *renderer;
|
||||||
float size;
|
float size;
|
||||||
int height;
|
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 = (RenFontA *) check_alloc(calloc(1, sizeof(RenFontA)));
|
||||||
font->size = size;
|
font->size = size;
|
||||||
|
|
||||||
// FIXME: we should remove the gamma correction.
|
font->renderer = new font_renderer_alpha(true, false);
|
||||||
// So that we have just the coverage.
|
|
||||||
font->renderer = new font_renderer_lcd(true, false, false, 1.8);
|
|
||||||
font->renderer->load_font(filename);
|
font->renderer->load_font(filename);
|
||||||
|
|
||||||
int ascender, descender;
|
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) {
|
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)));
|
GlyphSetA *set = (GlyphSetA *) check_alloc(calloc(1, sizeof(GlyphSetA)));
|
||||||
|
|
||||||
/* init image */
|
/* init image */
|
||||||
int width = 512; // 128;
|
int width = 128;
|
||||||
int height = 512; // 128;
|
int height = 128;
|
||||||
retry:
|
retry:
|
||||||
set->image = ren_new_image(width, height);
|
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);
|
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;
|
double x = 4, y = height - font->size * 3 / 2;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
const agg::rgba8 text_color(0x00, 0x00, 0x00);
|
const agg::alpha8 text_color(0xff);
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
if (x + font->size * 3 / 2 > width) {
|
if (x + font->size * 3 / 2 > width) {
|
||||||
x = 4;
|
x = 4;
|
||||||
|
@ -113,6 +112,13 @@ retry:
|
||||||
goto 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;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue