Implement subpixel LCD font rendering
This commit is contained in:
parent
f61ffc4710
commit
a0e7d16167
|
@ -0,0 +1,78 @@
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Anti-Grain Geometry (AGG) - Version 2.5
|
||||||
|
// A high quality rendering engine for C++
|
||||||
|
// Copyright (C) 2002-2006 Maxim Shemanarev
|
||||||
|
// Contact: mcseem@antigrain.com
|
||||||
|
// mcseemagg@yahoo.com
|
||||||
|
// http://antigrain.com
|
||||||
|
//
|
||||||
|
// AGG is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// AGG is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with AGG; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301, USA.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef AGG_LCD_DISTRIBUTION_LUT_INCLUDED
|
||||||
|
#define AGG_LCD_DISTRIBUTION_LUT_INCLUDED
|
||||||
|
|
||||||
|
#include "agg_basics.h"
|
||||||
|
|
||||||
|
namespace agg
|
||||||
|
{
|
||||||
|
|
||||||
|
//=====================================================lcd_distribution_lut
|
||||||
|
class lcd_distribution_lut
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
lcd_distribution_lut(double prim, double second, double tert)
|
||||||
|
{
|
||||||
|
double norm = 1.0 / (prim + second*2 + tert*2);
|
||||||
|
prim *= norm;
|
||||||
|
second *= norm;
|
||||||
|
tert *= norm;
|
||||||
|
for(unsigned i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
unsigned b = (i << 8);
|
||||||
|
unsigned s = round(second * b);
|
||||||
|
unsigned t = round(tert * b);
|
||||||
|
unsigned p = b - (2*s + 2*t);
|
||||||
|
|
||||||
|
m_data[3*i + 1] = s; /* secondary */
|
||||||
|
m_data[3*i + 2] = t; /* tertiary */
|
||||||
|
m_data[3*i ] = p; /* primary */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned convolution(const int8u* covers, int i0, int i_min, int i_max) const
|
||||||
|
{
|
||||||
|
unsigned sum = 0;
|
||||||
|
int k_min = (i0 >= i_min + 2 ? -2 : i_min - i0);
|
||||||
|
int k_max = (i0 <= i_max - 2 ? 2 : i_max - i0);
|
||||||
|
for (int k = k_min; k <= k_max; k++)
|
||||||
|
{
|
||||||
|
/* select the primary, secondary or tertiary channel */
|
||||||
|
int channel = abs(k) % 3;
|
||||||
|
int8u c = covers[i0 + k];
|
||||||
|
sum += m_data[3*c + channel];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sum + 128) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned short m_data[256*3];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,6 @@
|
||||||
#include "font_renderer.h"
|
#include "font_renderer.h"
|
||||||
|
|
||||||
|
#include "agg_lcd_distribution_lut.h"
|
||||||
#include "agg_pixfmt_rgb.h"
|
#include "agg_pixfmt_rgb.h"
|
||||||
#include "agg_pixfmt_rgba.h"
|
#include "agg_pixfmt_rgba.h"
|
||||||
#include "agg_gamma_lut.h"
|
#include "agg_gamma_lut.h"
|
||||||
|
@ -10,21 +11,28 @@ typedef agg::blender_rgb_gamma<agg::rgba8, agg::order_bgra, agg::gamma_lut<> > b
|
||||||
|
|
||||||
class FontRendererImpl {
|
class FontRendererImpl {
|
||||||
public:
|
public:
|
||||||
|
// Conventional LUT values: (1./3., 2./9., 1./9.)
|
||||||
|
// The values below are fine tuned as in the Elementary Plot library.
|
||||||
|
|
||||||
FontRendererImpl(bool hinting, bool kerning, float gamma_value) :
|
FontRendererImpl(bool hinting, bool kerning, float gamma_value) :
|
||||||
m_renderer(hinting, kerning),
|
m_renderer(hinting, kerning),
|
||||||
m_gamma_lut(double(gamma_value)),
|
m_gamma_lut(double(gamma_value)),
|
||||||
m_blender()
|
m_blender(),
|
||||||
|
m_lcd_lut(0.448, 0.184, 0.092)
|
||||||
{
|
{
|
||||||
m_blender.gamma(m_gamma_lut);
|
m_blender.gamma(m_gamma_lut);
|
||||||
}
|
}
|
||||||
|
|
||||||
font_renderer_alpha& renderer_alpha() { return m_renderer; }
|
font_renderer_alpha& renderer_alpha() { return m_renderer; }
|
||||||
blender_gamma_type& blender() { return m_blender; }
|
blender_gamma_type& blender() { return m_blender; }
|
||||||
|
agg::gamma_lut<>& gamma() { return m_gamma_lut; }
|
||||||
|
agg::lcd_distribution_lut& lcd_distribution_lut() { return m_lcd_lut; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
font_renderer_alpha m_renderer;
|
font_renderer_alpha m_renderer;
|
||||||
agg::gamma_lut<> m_gamma_lut;
|
agg::gamma_lut<> m_gamma_lut;
|
||||||
blender_gamma_type m_blender;
|
blender_gamma_type m_blender;
|
||||||
|
agg::lcd_distribution_lut m_lcd_lut;
|
||||||
};
|
};
|
||||||
|
|
||||||
FontRenderer *FontRendererNew(unsigned int flags, float gamma) {
|
FontRenderer *FontRendererNew(unsigned int flags, float gamma) {
|
||||||
|
@ -51,9 +59,10 @@ int FontRendererGetFontHeight(FontRenderer *font_renderer, float size) {
|
||||||
return int((ascender - descender) * face_height * scale + 0.5);
|
return int((ascender - descender) * face_height * scale + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void glyph_trim_rect(agg::rendering_buffer& ren_buf, GlyphBitmapInfo *gli) {
|
static void glyph_trim_rect(agg::rendering_buffer& ren_buf, GlyphBitmapInfo *gli, int subpixel_scale) {
|
||||||
const int height = ren_buf.height();
|
const int height = ren_buf.height();
|
||||||
int x0 = gli->x0, y0 = gli->y0, x1 = gli->x1, y1 = gli->y1;
|
int x0 = gli->x0 * subpixel_scale, x1 = gli->x1 * subpixel_scale;
|
||||||
|
int y0 = gli->y0, y1 = gli->y1;
|
||||||
for (int y = gli->y0; y < gli->y1; y++) {
|
for (int y = gli->y0; y < gli->y1; y++) {
|
||||||
uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
||||||
unsigned int row_bitsum = 0;
|
unsigned int row_bitsum = 0;
|
||||||
|
@ -81,35 +90,48 @@ static void glyph_trim_rect(agg::rendering_buffer& ren_buf, GlyphBitmapInfo *gli
|
||||||
int xtriml = x0, xtrimr = x1;
|
int xtriml = x0, xtrimr = x1;
|
||||||
for (int y = y0; y < y1; y++) {
|
for (int y = y0; y < y1; y++) {
|
||||||
uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
||||||
for (int x = x0; x < x1; x++) {
|
for (int x = x0; x < x1; x += subpixel_scale) {
|
||||||
if (row[x]) {
|
unsigned int xaccu = 0;
|
||||||
|
for (int i = 0; i < subpixel_scale; i++) {
|
||||||
|
xaccu |= row[x + i];
|
||||||
|
}
|
||||||
|
if (xaccu > 0) {
|
||||||
|
// FIXME: fix xs comparaison below.
|
||||||
if (x < xtriml) xtriml = x;
|
if (x < xtriml) xtriml = x;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int x = x1 - 1; x >= x0; x--) {
|
for (int x = x1 - subpixel_scale; x >= x0; x -= subpixel_scale) {
|
||||||
if (row[x]) {
|
unsigned int xaccu = 0;
|
||||||
|
for (int i = 0; i < subpixel_scale; i++) {
|
||||||
|
xaccu |= row[x + i];
|
||||||
|
}
|
||||||
|
if (xaccu > 0) {
|
||||||
if (x > xtrimr) xtrimr = x + 1;
|
if (x > xtrimr) xtrimr = x + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gli->xoff += (xtriml - x0);
|
gli->xoff += (xtriml - x0) / subpixel_scale;
|
||||||
gli->yoff += (y0 - gli->y0);
|
gli->yoff += (y0 - gli->y0);
|
||||||
gli->x0 = xtriml;
|
gli->x0 = xtriml / subpixel_scale;
|
||||||
gli->y0 = y0;
|
gli->y0 = y0;
|
||||||
gli->x1 = xtrimr;
|
gli->x1 = xtrimr / subpixel_scale;
|
||||||
gli->y1 = y1;
|
gli->y1 = y1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ceil_to_multiple(int n, int p) {
|
||||||
|
return p * ((n + p - 1) / p);
|
||||||
|
}
|
||||||
|
|
||||||
int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
||||||
void *pixels, int pixels_width, int pixels_height,
|
void *pixels, int pixels_width, int pixels_height,
|
||||||
int first_char, int num_chars, GlyphBitmapInfo *glyphs)
|
int first_char, int num_chars, GlyphBitmapInfo *glyphs, int subpixel_scale)
|
||||||
{
|
{
|
||||||
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
|
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
|
||||||
|
|
||||||
const int pixel_size = 1;
|
const int pixel_size = 1;
|
||||||
memset(pixels, 0x00, pixels_width * pixels_height * pixel_size);
|
memset(pixels, 0x00, pixels_width * pixels_height * subpixel_scale * pixel_size);
|
||||||
|
|
||||||
double ascender, descender;
|
double ascender, descender;
|
||||||
renderer_alpha.get_font_vmetrics(ascender, descender);
|
renderer_alpha.get_font_vmetrics(ascender, descender);
|
||||||
|
@ -117,11 +139,12 @@ int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
||||||
const int ascender_px = int(ascender * font_height + 0.5);
|
const int ascender_px = int(ascender * font_height + 0.5);
|
||||||
const int descender_px = int(descender * font_height + 0.5);
|
const int descender_px = int(descender * font_height + 0.5);
|
||||||
|
|
||||||
const int pad_y = font_height / 10, pad_x = 1;
|
const int pad_y = font_height / 10;
|
||||||
const int y_step = font_height + 2 * pad_y;
|
const int y_step = font_height + 2 * pad_y;
|
||||||
|
|
||||||
agg::rendering_buffer ren_buf((agg::int8u *) pixels, pixels_width, pixels_height, -pixels_width * pixel_size);
|
agg::rendering_buffer ren_buf((agg::int8u *) pixels, pixels_width * subpixel_scale, pixels_height, -pixels_width * subpixel_scale * pixel_size);
|
||||||
int x = 0, y = pixels_height;
|
const int x_start = subpixel_scale;
|
||||||
|
int x = x_start, y = pixels_height;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
const agg::alpha8 text_color(0xff);
|
const agg::alpha8 text_color(0xff);
|
||||||
#ifdef FONT_RENDERER_HEIGHT_HACK
|
#ifdef FONT_RENDERER_HEIGHT_HACK
|
||||||
|
@ -131,8 +154,8 @@ int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
||||||
#endif
|
#endif
|
||||||
for (int i = 0; i < num_chars; i++) {
|
for (int i = 0; i < num_chars; i++) {
|
||||||
int codepoint = first_char + i;
|
int codepoint = first_char + i;
|
||||||
if (x + font_height > pixels_width) {
|
if (x + font_height * subpixel_scale > pixels_width * subpixel_scale) {
|
||||||
x = 0;
|
x = x_start;
|
||||||
y -= y_step;
|
y -= y_step;
|
||||||
}
|
}
|
||||||
if (y - font_height - 2 * pad_y < 0) {
|
if (y - font_height - 2 * pad_y < 0) {
|
||||||
|
@ -142,22 +165,22 @@ int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
||||||
const int y_baseline = y - pad_y - font_height;
|
const int y_baseline = y - pad_y - font_height;
|
||||||
|
|
||||||
double x_next = x, y_next = y_baseline;
|
double x_next = x, y_next = y_baseline;
|
||||||
renderer_alpha.render_codepoint(ren_buf, font_height_reduced, text_color, x_next, y_next, codepoint);
|
renderer_alpha.render_codepoint(ren_buf, font_height_reduced, text_color, x_next, y_next, codepoint, subpixel_scale);
|
||||||
int x_next_i = int(x_next + 1.0);
|
int x_next_i = (subpixel_scale == 1 ? int(x_next + 1.0) : ceil_to_multiple(x_next + 0.5, subpixel_scale));
|
||||||
|
|
||||||
GlyphBitmapInfo& glyph_info = glyphs[i];
|
GlyphBitmapInfo& glyph_info = glyphs[i];
|
||||||
glyph_info.x0 = x;
|
glyph_info.x0 = x / subpixel_scale;
|
||||||
glyph_info.y0 = pixels_height - (y_baseline + ascender_px + pad_y);
|
glyph_info.y0 = pixels_height - (y_baseline + ascender_px + pad_y);
|
||||||
glyph_info.x1 = x_next_i;
|
glyph_info.x1 = x_next_i / subpixel_scale;
|
||||||
glyph_info.y1 = pixels_height - (y_baseline + descender_px - pad_y);
|
glyph_info.y1 = pixels_height - (y_baseline + descender_px - pad_y);
|
||||||
|
|
||||||
glyph_info.xoff = 0;
|
glyph_info.xoff = 0;
|
||||||
glyph_info.yoff = -pad_y;
|
glyph_info.yoff = -pad_y;
|
||||||
glyph_info.xadvance = x_next - x;
|
glyph_info.xadvance = (x_next - x) / subpixel_scale;
|
||||||
|
|
||||||
glyph_trim_rect(ren_buf, &glyph_info);
|
glyph_trim_rect(ren_buf, &glyph_info, subpixel_scale);
|
||||||
|
|
||||||
x = x_next_i + pad_x;
|
x = x_next_i;
|
||||||
|
|
||||||
#ifdef FONT_RENDERER_DEBUG
|
#ifdef FONT_RENDERER_DEBUG
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -171,6 +194,7 @@ int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: remove the Blender template argument.
|
||||||
template <typename Blender>
|
template <typename Blender>
|
||||||
void blend_solid_hspan(agg::rendering_buffer& rbuf, Blender& blender,
|
void blend_solid_hspan(agg::rendering_buffer& rbuf, Blender& blender,
|
||||||
int x, int y, unsigned len,
|
int x, int y, unsigned len,
|
||||||
|
@ -205,6 +229,52 @@ void blend_solid_hspan(agg::rendering_buffer& rbuf, Blender& blender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int floor_div(int a, int b) {
|
||||||
|
int rem = a % b;
|
||||||
|
if (rem < 0) {
|
||||||
|
rem += b;
|
||||||
|
}
|
||||||
|
return (a - rem) / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blend_solid_hspan_rgb_subpixel(agg::rendering_buffer& rbuf, agg::gamma_lut<>& gamma, agg::lcd_distribution_lut& lcd_lut,
|
||||||
|
int x_lcd, int y, unsigned len,
|
||||||
|
const agg::rgba8& c,
|
||||||
|
const agg::int8u* covers)
|
||||||
|
{
|
||||||
|
// const int subpixel_scale = 3;
|
||||||
|
// FIXME: rowlen à verifier
|
||||||
|
// unsigned rowlen = rbuf.width() * subpixel_scale;
|
||||||
|
// FIXME: no correct boundary limits for cx and cx_max
|
||||||
|
// int cx = (x_lcd - 2 >= 0 ? -2 : -x_lcd);
|
||||||
|
// int cx_max = (len + 2 <= rowlen ? len + 1 : rowlen - 1);
|
||||||
|
const int pixel_size = 4;
|
||||||
|
int cx = -2;
|
||||||
|
int cx_max = len + 1;
|
||||||
|
|
||||||
|
const int x_min = floor_div(x_lcd + cx, 3);
|
||||||
|
const int x_max = floor_div(x_lcd + cx_max, 3);
|
||||||
|
|
||||||
|
const agg::int8u rgb[3] = { c.r, c.g, c.b };
|
||||||
|
agg::int8u* p = rbuf.row_ptr(y) + x_min * pixel_size;
|
||||||
|
|
||||||
|
// Indexes to adress RGB colors in a BGRA32 format.
|
||||||
|
const int pixel_index[3] = {2, 1, 0};
|
||||||
|
for (int x = x_min; x <= x_max; x++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int new_cx = x * 3 - x_lcd + i;
|
||||||
|
unsigned c_conv = lcd_lut.convolution(covers, new_cx, 0, len - 1);
|
||||||
|
unsigned alpha = (c_conv + 1) * (c.a + 1);
|
||||||
|
unsigned dst_col = gamma.dir(rgb[i]);
|
||||||
|
unsigned src_col = gamma.dir(*(p + pixel_index[i]));
|
||||||
|
*(p + pixel_index[i]) = gamma.inv((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16);
|
||||||
|
}
|
||||||
|
//p[3] = 0xff;
|
||||||
|
p += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// destination implicitly BGRA32. Source implictly single-byte renderer_alpha coverage.
|
// destination implicitly BGRA32. Source implictly single-byte renderer_alpha coverage.
|
||||||
void FontRendererBlendGamma(FontRenderer *font_renderer, uint8_t *dst, int dst_stride, uint8_t *src, int src_stride, int region_width, int region_height, FontRendererColor color) {
|
void FontRendererBlendGamma(FontRenderer *font_renderer, uint8_t *dst, int dst_stride, uint8_t *src, int src_stride, int region_width, int region_height, FontRendererColor color) {
|
||||||
blender_gamma_type& blender = font_renderer->blender();
|
blender_gamma_type& blender = font_renderer->blender();
|
||||||
|
@ -215,3 +285,16 @@ void FontRendererBlendGamma(FontRenderer *font_renderer, uint8_t *dst, int dst_s
|
||||||
blend_solid_hspan<blender_gamma_type>(dst_ren_buf, blender, x, y, region_width, color_a, covers);
|
blend_solid_hspan<blender_gamma_type>(dst_ren_buf, blender, x, y, region_width, color_a, covers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// destination implicitly BGRA32. Source implictly single-byte renderer_alpha coverage.
|
||||||
|
void FontRendererBlendGammaSubpixel(FontRenderer *font_renderer, uint8_t *dst, int dst_stride, uint8_t *src, int src_stride, int region_width, int region_height, FontRendererColor color) {
|
||||||
|
const int subpixel_scale = 3;
|
||||||
|
agg::gamma_lut<>& gamma = font_renderer->gamma();
|
||||||
|
agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut();
|
||||||
|
agg::rendering_buffer dst_ren_buf(dst, region_width, region_height, dst_stride);
|
||||||
|
const agg::rgba8 color_a(color.r, color.g, color.b);
|
||||||
|
for (int x = 0, y = 0; y < region_height; y++) {
|
||||||
|
agg::int8u *covers = src + y * src_stride;
|
||||||
|
blend_solid_hspan_rgb_subpixel(dst_ren_buf, gamma, lcd_lut, x, y, region_width * subpixel_scale, color_a, covers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ int FontRendererGetFontHeight(FontRenderer *, float size);
|
||||||
|
|
||||||
int FontRendererBakeFontBitmap(FontRenderer *, int font_height,
|
int FontRendererBakeFontBitmap(FontRenderer *, int font_height,
|
||||||
void *pixels, int pixels_width, int pixels_height,
|
void *pixels, int pixels_width, int pixels_height,
|
||||||
int first_char, int num_chars, GlyphBitmapInfo *glyph_info);
|
int first_char, int num_chars, GlyphBitmapInfo *glyph_info,
|
||||||
|
int subpixel_scale);
|
||||||
|
|
||||||
void FontRendererBlendGamma(FontRenderer *,
|
void FontRendererBlendGamma(FontRenderer *,
|
||||||
uint8_t *dst, int dst_stride,
|
uint8_t *dst, int dst_stride,
|
||||||
|
@ -43,6 +44,12 @@ void FontRendererBlendGamma(FontRenderer *,
|
||||||
int region_width, int region_height,
|
int region_width, int region_height,
|
||||||
FontRendererColor color);
|
FontRendererColor color);
|
||||||
|
|
||||||
|
void FontRendererBlendGammaSubpixel(FontRenderer *,
|
||||||
|
uint8_t *dst, int dst_stride,
|
||||||
|
uint8_t *src, int src_stride,
|
||||||
|
int region_width, int region_height,
|
||||||
|
FontRendererColor color);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -78,74 +78,15 @@ public:
|
||||||
return m_font_loaded;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Rasterizer, class Scanline, class RenSolid>
|
template<class Rasterizer, class Scanline, class RenSolid>
|
||||||
void draw_codepoint(Rasterizer& ras, Scanline& sl,
|
void draw_codepoint(Rasterizer& ras, Scanline& sl,
|
||||||
RenSolid& ren_solid, const color_type color,
|
RenSolid& ren_solid, const color_type color,
|
||||||
int codepoint, double& x, double& y, double height)
|
int codepoint, double& x, double& y, double height, int subpixel_scale)
|
||||||
{
|
{
|
||||||
const double scale_x = 100;
|
const double scale_x = 100;
|
||||||
|
|
||||||
m_feng.height(height);
|
m_feng.height(height);
|
||||||
m_feng.width(height * scale_x);
|
m_feng.width(height * scale_x * subpixel_scale);
|
||||||
m_feng.hinting(m_hinting);
|
m_feng.hinting(m_hinting);
|
||||||
|
|
||||||
// Represent the delta in x scaled by scale_x.
|
// Represent the delta in x scaled by scale_x.
|
||||||
|
@ -184,30 +125,11 @@ public:
|
||||||
ren_base.clear(color);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_codepoint(agg::rendering_buffer& ren_buf,
|
void render_codepoint(agg::rendering_buffer& ren_buf,
|
||||||
const double text_size,
|
const double text_size,
|
||||||
const color_type text_color,
|
const color_type text_color,
|
||||||
double& x, double& y,
|
double& x, double& y,
|
||||||
int codepoint)
|
int codepoint, int subpixel_scale)
|
||||||
{
|
{
|
||||||
if (!m_font_loaded) {
|
if (!m_font_loaded) {
|
||||||
return;
|
return;
|
||||||
|
@ -219,6 +141,6 @@ public:
|
||||||
agg::pixfmt_alpha8 pf(ren_buf);
|
agg::pixfmt_alpha8 pf(ren_buf);
|
||||||
base_ren_type ren_base(pf);
|
base_ren_type ren_base(pf);
|
||||||
renderer_solid ren_solid(ren_base);
|
renderer_solid ren_solid(ren_base);
|
||||||
draw_codepoint(ras, sl, ren_solid, text_color, codepoint, x, y, text_size);
|
draw_codepoint(ras, sl, ren_solid, text_color, codepoint, x, y, text_size, subpixel_scale);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -105,9 +105,9 @@ RenImage* ren_new_image(int width, int height) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenCoverageImage* ren_new_coverage(int width, int height) {
|
RenCoverageImage* ren_new_coverage(int width, int height, int subpixel_scale) {
|
||||||
assert(width > 0 && height > 0);
|
assert(width > 0 && height > 0);
|
||||||
RenCoverageImage *image = malloc(sizeof(RenCoverageImage) + width * height * sizeof(uint8_t));
|
RenCoverageImage *image = malloc(sizeof(RenCoverageImage) + width * height * subpixel_scale * sizeof(uint8_t));
|
||||||
check_alloc(image);
|
check_alloc(image);
|
||||||
image->pixels = (void*) (image + 1);
|
image->pixels = (void*) (image + 1);
|
||||||
image->width = width;
|
image->width = width;
|
||||||
|
@ -126,15 +126,17 @@ void ren_free_coverage(RenCoverageImage *coverage) {
|
||||||
static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
||||||
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
||||||
|
|
||||||
|
const int subpixel_scale = 3;
|
||||||
|
|
||||||
/* init image */
|
/* init image */
|
||||||
int width = 128;
|
int width = 128;
|
||||||
int height = 128;
|
int height = 128;
|
||||||
retry:
|
retry:
|
||||||
set->coverage = ren_new_coverage(width, height);
|
set->coverage = ren_new_coverage(width, height, subpixel_scale);
|
||||||
|
|
||||||
int res = FontRendererBakeFontBitmap(font->renderer, font->height,
|
int res = FontRendererBakeFontBitmap(font->renderer, font->height,
|
||||||
(void *) set->coverage->pixels, width, height,
|
(void *) set->coverage->pixels, width, height,
|
||||||
idx << 8, 256, set->glyphs);
|
idx << 8, 256, set->glyphs, subpixel_scale);
|
||||||
|
|
||||||
/* retry with a larger image buffer if the buffer wasn't large enough */
|
/* retry with a larger image buffer if the buffer wasn't large enough */
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -169,7 +171,7 @@ RenFont* ren_load_font(const char *filename, float size) {
|
||||||
font = check_alloc(calloc(1, sizeof(RenFont)));
|
font = check_alloc(calloc(1, sizeof(RenFont)));
|
||||||
font->size = size;
|
font->size = size;
|
||||||
|
|
||||||
const float gamma = 1.8;
|
const float gamma = 1.5;
|
||||||
font->renderer = FontRendererNew(FONT_RENDERER_HINTING, gamma);
|
font->renderer = FontRendererNew(FONT_RENDERER_HINTING, gamma);
|
||||||
if (FontRendererLoadFont(font->renderer, filename)) {
|
if (FontRendererLoadFont(font->renderer, filename)) {
|
||||||
free(font);
|
free(font);
|
||||||
|
@ -321,17 +323,26 @@ static void ren_draw_coverage_with_color(FontRenderer *renderer, RenCoverageImag
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int subpixel_scale = 3;
|
||||||
|
#if 0
|
||||||
|
// FIXME: find a more robust solution.
|
||||||
|
const int sub_width_rem = sub->width % subpixel_scale;
|
||||||
|
if (sub_width_rem > 0) {
|
||||||
|
sub->width += (subpixel_scale - sub_width_rem);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* draw */
|
/* draw */
|
||||||
SDL_Surface *surf = SDL_GetWindowSurface(window);
|
SDL_Surface *surf = SDL_GetWindowSurface(window);
|
||||||
uint8_t *s = image->pixels;
|
uint8_t *s = image->pixels;
|
||||||
RenColor *d = (RenColor*) surf->pixels;
|
RenColor *d = (RenColor*) surf->pixels;
|
||||||
s += sub->x + sub->y * image->width;
|
s += (sub->x + sub->y * image->width) * subpixel_scale;
|
||||||
d += x + y * surf->w;
|
d += x + y * surf->w;
|
||||||
const int surf_pixel_size = 4;
|
const int surf_pixel_size = 4;
|
||||||
FontRendererBlendGamma(
|
FontRendererBlendGammaSubpixel(
|
||||||
renderer,
|
renderer,
|
||||||
(uint8_t *) d, surf->w * surf_pixel_size,
|
(uint8_t *) d, surf->w * surf_pixel_size,
|
||||||
s, image->width,
|
s, image->width * subpixel_scale,
|
||||||
sub->width, sub->height,
|
sub->width, sub->height,
|
||||||
(FontRendererColor) { .r = color.r, .g = color.g, .b = color.b });
|
(FontRendererColor) { .r = color.r, .g = color.g, .b = color.b });
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,10 @@ static GlyphSet* load_glyphset_agg(RenFont *font, int idx) {
|
||||||
retry:
|
retry:
|
||||||
set->image = ren_new_image(width, height);
|
set->image = ren_new_image(width, height);
|
||||||
|
|
||||||
|
const int subpixel_scale = 3;
|
||||||
int res = FontRendererBakeFontBitmap(font->renderer, font->height,
|
int res = FontRendererBakeFontBitmap(font->renderer, font->height,
|
||||||
(void *) set->image->pixels, width, height,
|
(void *) set->image->pixels, width, height,
|
||||||
idx << 8, 256, set->glyphs);
|
idx << 8, 256, set->glyphs, subpixel_scale);
|
||||||
|
|
||||||
/* retry with a larger image buffer if the buffer wasn't large enough */
|
/* retry with a larger image buffer if the buffer wasn't large enough */
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
|
Loading…
Reference in New Issue