2020-06-01 12:56:23 +02:00
|
|
|
#include "font_renderer.h"
|
|
|
|
|
2020-06-02 14:47:06 +02:00
|
|
|
#include "agg_pixfmt_rgb.h"
|
|
|
|
#include "agg_pixfmt_rgba.h"
|
|
|
|
#include "agg_gamma_lut.h"
|
|
|
|
|
2020-06-01 12:56:23 +02:00
|
|
|
#include "font_renderer_alpha.h"
|
|
|
|
|
2020-06-02 17:18:52 +02:00
|
|
|
typedef agg::blender_rgb_gamma<agg::rgba8, agg::order_bgra, agg::gamma_lut<> > blender_gamma_type;
|
|
|
|
|
|
|
|
class FontRendererImpl {
|
|
|
|
public:
|
|
|
|
FontRendererImpl(bool hinting, bool kerning, float gamma_value) :
|
|
|
|
m_renderer(hinting, kerning),
|
|
|
|
m_gamma_lut(double(gamma_value)),
|
|
|
|
m_blender()
|
|
|
|
{
|
|
|
|
m_blender.gamma(m_gamma_lut);
|
|
|
|
}
|
|
|
|
|
|
|
|
font_renderer_alpha& renderer_alpha() { return m_renderer; }
|
|
|
|
blender_gamma_type& blender() { return m_blender; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
font_renderer_alpha m_renderer;
|
|
|
|
agg::gamma_lut<> m_gamma_lut;
|
|
|
|
blender_gamma_type m_blender;
|
|
|
|
};
|
|
|
|
|
|
|
|
FontRenderer *FontRendererNew(unsigned int flags, float gamma) {
|
2020-06-01 12:56:23 +02:00
|
|
|
bool hinting = ((flags & FONT_RENDERER_HINTING) != 0);
|
|
|
|
bool kerning = ((flags & FONT_RENDERER_KERNING) != 0);
|
2020-06-02 17:18:52 +02:00
|
|
|
return new FontRendererImpl(hinting, kerning, gamma);
|
2020-06-01 12:56:23 +02:00
|
|
|
}
|
|
|
|
|
2020-06-02 17:18:52 +02:00
|
|
|
void FontRendererFree(FontRenderer *font_renderer) {
|
2020-06-01 14:42:57 +02:00
|
|
|
delete font_renderer;
|
|
|
|
}
|
|
|
|
|
2020-06-02 17:18:52 +02:00
|
|
|
int FontRendererLoadFont(FontRenderer *font_renderer, const char *filename) {
|
|
|
|
bool success = font_renderer->renderer_alpha().load_font(filename);
|
2020-06-01 12:56:23 +02:00
|
|
|
return (success ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
2020-06-02 17:18:52 +02:00
|
|
|
int FontRendererGetFontHeight(FontRenderer *font_renderer, float size) {
|
|
|
|
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
|
2020-06-01 12:56:23 +02:00
|
|
|
double ascender, descender;
|
2020-06-02 17:18:52 +02:00
|
|
|
renderer_alpha.get_font_vmetrics(ascender, descender);
|
|
|
|
int face_height = renderer_alpha.get_face_height();
|
|
|
|
float scale = renderer_alpha.scale_for_em_to_pixels(size);
|
2020-06-01 12:56:23 +02:00
|
|
|
return int((ascender - descender) * face_height * scale + 0.5);
|
|
|
|
}
|
|
|
|
|
2020-06-02 17:18:52 +02:00
|
|
|
int FontRendererBakeFontBitmap(FontRenderer *font_renderer, int font_height,
|
2020-06-01 12:56:23 +02:00
|
|
|
void *pixels, int pixels_width, int pixels_height,
|
|
|
|
int first_char, int num_chars, GlyphBitmapInfo *glyphs)
|
|
|
|
{
|
2020-06-02 17:18:52 +02:00
|
|
|
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
|
2020-06-01 12:56:23 +02:00
|
|
|
|
|
|
|
const int pixel_size = 1;
|
|
|
|
memset(pixels, 0x00, pixels_width * pixels_height * pixel_size);
|
|
|
|
|
|
|
|
double ascender, descender;
|
2020-06-02 17:18:52 +02:00
|
|
|
renderer_alpha.get_font_vmetrics(ascender, descender);
|
2020-06-01 12:56:23 +02:00
|
|
|
|
|
|
|
const int ascender_px = int(ascender * font_height + 0.5);
|
|
|
|
const int descender_px = int(descender * font_height + 0.5);
|
|
|
|
|
2020-06-02 17:51:26 +02:00
|
|
|
const int pad_y = font_height / 10, pad_x = 1;
|
2020-06-01 12:56:23 +02:00
|
|
|
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);
|
|
|
|
int x = 0, y = pixels_height;
|
|
|
|
int res = 0;
|
|
|
|
const agg::alpha8 text_color(0xff);
|
2020-06-01 17:23:18 +02:00
|
|
|
#ifdef FONT_RENDERER_HEIGHT_HACK
|
2020-06-01 15:33:14 +02:00
|
|
|
const int font_height_reduced = (font_height * 86) / 100;
|
2020-06-01 17:23:18 +02:00
|
|
|
#else
|
|
|
|
const int font_height_reduced = font_height;
|
|
|
|
#endif
|
2020-06-01 12:56:23 +02:00
|
|
|
for (int i = 0; i < num_chars; i++) {
|
|
|
|
int codepoint = first_char + i;
|
|
|
|
if (x + font_height > pixels_width) {
|
|
|
|
x = 0;
|
|
|
|
y -= y_step;
|
|
|
|
}
|
|
|
|
if (y - font_height - 2 * pad_y < 0) {
|
|
|
|
res = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const int y_baseline = y - pad_y - font_height;
|
|
|
|
|
|
|
|
double x_next = x, y_next = y_baseline;
|
2020-06-02 17:18:52 +02:00
|
|
|
renderer_alpha.render_codepoint(ren_buf, font_height_reduced, text_color, x_next, y_next, codepoint);
|
2020-06-01 17:23:18 +02:00
|
|
|
int x_next_i = int(x_next + 1.0);
|
2020-06-01 14:45:07 +02:00
|
|
|
|
2020-06-01 12:56:23 +02:00
|
|
|
GlyphBitmapInfo& glyph_info = glyphs[i];
|
|
|
|
glyph_info.x0 = x;
|
|
|
|
glyph_info.y0 = pixels_height - (y_baseline + ascender_px + pad_y);
|
|
|
|
glyph_info.x1 = x_next_i;
|
|
|
|
glyph_info.y1 = pixels_height - (y_baseline + descender_px - pad_y);
|
|
|
|
glyph_info.xoff = 0;
|
2020-06-01 14:33:46 +02:00
|
|
|
glyph_info.yoff = -pad_y;
|
2020-06-01 12:56:23 +02:00
|
|
|
glyph_info.xadvance = x_next - x;
|
2020-06-01 14:45:07 +02:00
|
|
|
|
2020-06-01 17:23:18 +02:00
|
|
|
x = x_next_i + pad_x;
|
2020-06-01 16:57:33 +02:00
|
|
|
|
2020-06-01 17:23:18 +02:00
|
|
|
#ifdef FONT_RENDERER_DEBUG
|
2020-06-01 16:57:33 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"glyph codepoint %3d (ascii: %1c), BOX (%3d, %3d) (%3d, %3d), "
|
|
|
|
"OFFSET (%.5g, %.5g), X ADVANCE %.5g\n",
|
|
|
|
codepoint, i,
|
|
|
|
glyph_info.x0, glyph_info.y0, glyph_info.x1, glyph_info.y1,
|
|
|
|
glyph_info.xoff, glyph_info.yoff, glyph_info.xadvance);
|
|
|
|
#endif
|
2020-06-01 12:56:23 +02:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2020-06-02 14:47:06 +02:00
|
|
|
|
|
|
|
template <typename Blender>
|
|
|
|
void blend_solid_hspan(agg::rendering_buffer& rbuf, Blender& blender,
|
|
|
|
int x, int y, unsigned len,
|
|
|
|
const typename Blender::color_type& c, const agg::int8u* covers)
|
|
|
|
{
|
|
|
|
typedef Blender blender_type;
|
|
|
|
typedef typename blender_type::color_type color_type;
|
|
|
|
typedef typename blender_type::order_type order_type;
|
|
|
|
typedef typename color_type::value_type value_type;
|
|
|
|
typedef typename color_type::calc_type calc_type;
|
|
|
|
|
|
|
|
if (c.a)
|
|
|
|
{
|
|
|
|
value_type* p = (value_type*)rbuf.row_ptr(x, y, len) + (x << 2);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8;
|
|
|
|
if(alpha == color_type::base_mask)
|
|
|
|
{
|
|
|
|
p[order_type::R] = c.r;
|
|
|
|
p[order_type::G] = c.g;
|
|
|
|
p[order_type::B] = c.b;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blender.blend_pix(p, c.r, c.g, c.b, alpha, *covers);
|
|
|
|
}
|
|
|
|
p += 4;
|
|
|
|
++covers;
|
|
|
|
}
|
|
|
|
while(--len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 17:18:52 +02:00
|
|
|
// 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) {
|
|
|
|
blender_gamma_type& blender = font_renderer->blender();
|
2020-06-02 14:47:06 +02:00
|
|
|
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;
|
2020-06-02 17:18:52 +02:00
|
|
|
blend_solid_hspan<blender_gamma_type>(dst_ren_buf, blender, x, y, region_width, color_a, covers);
|
2020-06-02 14:47:06 +02:00
|
|
|
}
|
|
|
|
}
|