Removed font renderer.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,198 +0,0 @@
|
||||
//----------------------------------------------------------------------------
|
||||
// Anti-Grain Geometry - Version 2.4
|
||||
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
|
||||
//
|
||||
// Permission to copy, use, modify, sell and distribute this software
|
||||
// is granted provided this copyright notice appears in all copies.
|
||||
// This software is provided "as is" without express or implied
|
||||
// warranty, and with no claim as to its suitability for any purpose.
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
// Contact: mcseem@antigrain.com
|
||||
// mcseemagg@yahoo.com
|
||||
// http://www.antigrain.com
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// See implementation agg_font_freetype.cpp
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#ifndef AGG_FONT_FREETYPE_INCLUDED
|
||||
#define AGG_FONT_FREETYPE_INCLUDED
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
|
||||
#include "agg_scanline_storage_aa.h"
|
||||
#include "agg_scanline_storage_bin.h"
|
||||
#include "agg_scanline_u.h"
|
||||
#include "agg_scanline_bin.h"
|
||||
#include "agg_path_storage_integer.h"
|
||||
#include "agg_rasterizer_scanline_aa.h"
|
||||
#include "agg_conv_curve.h"
|
||||
#include "agg_font_cache_manager.h"
|
||||
#include "agg_trans_affine.h"
|
||||
|
||||
namespace agg
|
||||
{
|
||||
|
||||
|
||||
//-----------------------------------------------font_engine_freetype_base
|
||||
class font_engine_freetype_base
|
||||
{
|
||||
public:
|
||||
//--------------------------------------------------------------------
|
||||
typedef serialized_scanlines_adaptor_aa<int8u> gray8_adaptor_type;
|
||||
typedef serialized_scanlines_adaptor_bin mono_adaptor_type;
|
||||
typedef scanline_storage_aa8 scanlines_aa_type;
|
||||
typedef scanline_storage_bin scanlines_bin_type;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
~font_engine_freetype_base();
|
||||
font_engine_freetype_base(bool flag32, unsigned max_faces = 32);
|
||||
|
||||
// Set font parameters
|
||||
//--------------------------------------------------------------------
|
||||
void resolution(unsigned dpi);
|
||||
bool load_font(const char* font_name, unsigned face_index, glyph_rendering ren_type,
|
||||
const char* font_mem = 0, const long font_mem_size = 0);
|
||||
bool attach(const char* file_name);
|
||||
bool char_map(FT_Encoding map);
|
||||
bool height(double h);
|
||||
bool width(double w);
|
||||
void hinting(bool h);
|
||||
void flip_y(bool f);
|
||||
void transform(const trans_affine& affine);
|
||||
|
||||
// Set Gamma
|
||||
//--------------------------------------------------------------------
|
||||
template<class GammaF> void gamma(const GammaF& f)
|
||||
{
|
||||
m_rasterizer.gamma(f);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
//--------------------------------------------------------------------
|
||||
int last_error() const { return m_last_error; }
|
||||
unsigned resolution() const { return m_resolution; }
|
||||
const char* name() const { return m_name; }
|
||||
unsigned num_faces() const;
|
||||
FT_Encoding char_map() const { return m_char_map; }
|
||||
double height() const { return double(m_height) / 64.0; }
|
||||
double width() const { return double(m_width) / 64.0; }
|
||||
double ascender() const;
|
||||
double descender() const;
|
||||
int face_height() const;
|
||||
int face_units_em()const;
|
||||
bool hinting() const { return m_hinting; }
|
||||
bool flip_y() const { return m_flip_y; }
|
||||
|
||||
|
||||
// Interface mandatory to implement for font_cache_manager
|
||||
//--------------------------------------------------------------------
|
||||
const char* font_signature() const { return m_signature; }
|
||||
int change_stamp() const { return m_change_stamp; }
|
||||
|
||||
bool prepare_glyph(unsigned glyph_code);
|
||||
unsigned glyph_index() const { return m_glyph_index; }
|
||||
unsigned data_size() const { return m_data_size; }
|
||||
glyph_data_type data_type() const { return m_data_type; }
|
||||
const rect_i& bounds() const { return m_bounds; }
|
||||
double advance_x() const { return m_advance_x; }
|
||||
double advance_y() const { return m_advance_y; }
|
||||
void write_glyph_to(int8u* data) const;
|
||||
bool add_kerning(unsigned first, unsigned second,
|
||||
double* x, double* y);
|
||||
|
||||
private:
|
||||
font_engine_freetype_base(const font_engine_freetype_base&);
|
||||
const font_engine_freetype_base& operator = (const font_engine_freetype_base&);
|
||||
|
||||
void update_char_size();
|
||||
void update_signature();
|
||||
int find_face(const char* face_name) const;
|
||||
|
||||
bool m_flag32;
|
||||
int m_change_stamp;
|
||||
int m_last_error;
|
||||
char* m_name;
|
||||
unsigned m_name_len;
|
||||
unsigned m_face_index;
|
||||
FT_Encoding m_char_map;
|
||||
char* m_signature;
|
||||
unsigned m_height;
|
||||
unsigned m_width;
|
||||
bool m_hinting;
|
||||
bool m_flip_y;
|
||||
bool m_library_initialized;
|
||||
FT_Library m_library; // handle to library
|
||||
FT_Face* m_faces; // A pool of font faces
|
||||
char** m_face_names;
|
||||
unsigned m_num_faces;
|
||||
unsigned m_max_faces;
|
||||
FT_Face m_cur_face; // handle to the current face object
|
||||
int m_resolution;
|
||||
glyph_rendering m_glyph_rendering;
|
||||
unsigned m_glyph_index;
|
||||
unsigned m_data_size;
|
||||
glyph_data_type m_data_type;
|
||||
rect_i m_bounds;
|
||||
double m_advance_x;
|
||||
double m_advance_y;
|
||||
trans_affine m_affine;
|
||||
|
||||
path_storage_integer<int16, 6> m_path16;
|
||||
path_storage_integer<int32, 6> m_path32;
|
||||
conv_curve<path_storage_integer<int16, 6> > m_curves16;
|
||||
conv_curve<path_storage_integer<int32, 6> > m_curves32;
|
||||
scanline_u8 m_scanline_aa;
|
||||
scanline_bin m_scanline_bin;
|
||||
scanlines_aa_type m_scanlines_aa;
|
||||
scanlines_bin_type m_scanlines_bin;
|
||||
rasterizer_scanline_aa<> m_rasterizer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------font_engine_freetype_int16
|
||||
// This class uses values of type int16 (10.6 format) for the vector cache.
|
||||
// The vector cache is compact, but when rendering glyphs of height
|
||||
// more that 200 there integer overflow can occur.
|
||||
//
|
||||
class font_engine_freetype_int16 : public font_engine_freetype_base
|
||||
{
|
||||
public:
|
||||
typedef serialized_integer_path_adaptor<int16, 6> path_adaptor_type;
|
||||
typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type;
|
||||
typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type;
|
||||
typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type;
|
||||
typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type;
|
||||
|
||||
font_engine_freetype_int16(unsigned max_faces = 32) :
|
||||
font_engine_freetype_base(false, max_faces) {}
|
||||
};
|
||||
|
||||
//------------------------------------------------font_engine_freetype_int32
|
||||
// This class uses values of type int32 (26.6 format) for the vector cache.
|
||||
// The vector cache is twice larger than in font_engine_freetype_int16,
|
||||
// but it allows you to render glyphs of very large sizes.
|
||||
//
|
||||
class font_engine_freetype_int32 : public font_engine_freetype_base
|
||||
{
|
||||
public:
|
||||
typedef serialized_integer_path_adaptor<int32, 6> path_adaptor_type;
|
||||
typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type;
|
||||
typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type;
|
||||
typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type;
|
||||
typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type;
|
||||
|
||||
font_engine_freetype_int32(unsigned max_faces = 32) :
|
||||
font_engine_freetype_base(true, max_faces) {}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,73 +0,0 @@
|
||||
// Adapted by Francesco Abbate for GSL Shell
|
||||
// Original code's copyright below.
|
||||
//----------------------------------------------------------------------------
|
||||
// Anti-Grain Geometry - Version 2.4
|
||||
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
|
||||
//
|
||||
// Permission to copy, use, modify, sell and distribute this software
|
||||
// is granted provided this copyright notice appears in all copies.
|
||||
// This software is provided "as is" without express or implied
|
||||
// warranty, and with no claim as to its suitability for any purpose.
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
// Contact: mcseem@antigrain.com
|
||||
// mcseemagg@yahoo.com
|
||||
// http://www.antigrain.com
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#ifndef AGG_LCD_DISTRIBUTION_LUT_INCLUDED
|
||||
#define AGG_LCD_DISTRIBUTION_LUT_INCLUDED
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#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 = std::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,93 +0,0 @@
|
||||
#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 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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,445 +0,0 @@
|
||||
#include "font_renderer.h"
|
||||
|
||||
#include "agg_lcd_distribution_lut.h"
|
||||
#include "agg_pixfmt_rgb.h"
|
||||
#include "agg_pixfmt_rgba.h"
|
||||
|
||||
#include "font_renderer_alpha.h"
|
||||
|
||||
// Important: when a subpixel scale is used the width below will be the width in logical pixel.
|
||||
// As each logical pixel contains 3 subpixels it means that the 'pixels' pointer
|
||||
// will hold enough space for '3 * width' uint8_t values.
|
||||
struct FR_Bitmap {
|
||||
agg::int8u *pixels;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
class FR_Renderer {
|
||||
public:
|
||||
// Conventional LUT values: (1./3., 2./9., 1./9.)
|
||||
// The values below are fine tuned as in the Elementary Plot library.
|
||||
|
||||
FR_Renderer(bool hinting, bool kerning, bool subpixel, bool prescale_x) :
|
||||
m_renderer(hinting, kerning, subpixel, prescale_x),
|
||||
m_lcd_lut(0.448, 0.184, 0.092),
|
||||
m_subpixel(subpixel)
|
||||
{ }
|
||||
|
||||
font_renderer_alpha& renderer_alpha() { return m_renderer; }
|
||||
agg::lcd_distribution_lut& lcd_distribution_lut() { return m_lcd_lut; }
|
||||
int subpixel_scale() const { return (m_subpixel ? 3 : 1); }
|
||||
|
||||
private:
|
||||
font_renderer_alpha m_renderer;
|
||||
agg::lcd_distribution_lut m_lcd_lut;
|
||||
int m_subpixel;
|
||||
};
|
||||
|
||||
FR_Renderer *FR_Renderer_New(unsigned int flags) {
|
||||
bool hinting = ((flags & FR_HINTING) != 0);
|
||||
bool kerning = ((flags & FR_KERNING) != 0);
|
||||
bool subpixel = ((flags & FR_SUBPIXEL) != 0);
|
||||
bool prescale_x = ((flags & FR_PRESCALE_X) != 0);
|
||||
return new FR_Renderer(hinting, kerning, subpixel, prescale_x);
|
||||
}
|
||||
|
||||
FR_Bitmap* FR_Bitmap_New(FR_Renderer *font_renderer, int width, int height) {
|
||||
const int subpixel_scale = font_renderer->subpixel_scale();
|
||||
FR_Bitmap *image = (FR_Bitmap *) malloc(sizeof(FR_Bitmap) + width * height * subpixel_scale);
|
||||
if (!image) { return NULL; }
|
||||
image->pixels = (agg::int8u *) (image + 1);
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
return image;
|
||||
}
|
||||
|
||||
void FR_Bitmap_Free(FR_Bitmap *image) {
|
||||
free(image);
|
||||
}
|
||||
|
||||
void FR_Renderer_Free(FR_Renderer *font_renderer) {
|
||||
delete font_renderer;
|
||||
}
|
||||
|
||||
int FR_Subpixel_Scale(FR_Renderer *font_renderer) {
|
||||
return font_renderer->subpixel_scale();
|
||||
}
|
||||
|
||||
int FR_Load_Font(FR_Renderer *font_renderer, const char *filename) {
|
||||
bool success = font_renderer->renderer_alpha().load_font(filename);
|
||||
return (success ? 0 : 1);
|
||||
}
|
||||
|
||||
int FR_Get_Font_Height(FR_Renderer *font_renderer, float size) {
|
||||
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
|
||||
double ascender, descender;
|
||||
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);
|
||||
return int((ascender - descender) * face_height * scale + 0.5);
|
||||
}
|
||||
|
||||
static void glyph_trim_rect(agg::rendering_buffer& ren_buf, FR_Bitmap_Glyph_Metrics& gli, int subpixel_scale) {
|
||||
const int height = ren_buf.height();
|
||||
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++) {
|
||||
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
||||
unsigned int row_bitsum = 0;
|
||||
for (int x = x0; x < x1; x++) {
|
||||
row_bitsum |= row[x];
|
||||
}
|
||||
if (row_bitsum == 0) {
|
||||
y0++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int y = gli.y1 - 1; y >= y0; y--) {
|
||||
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
||||
unsigned int row_bitsum = 0;
|
||||
for (int x = x0; x < x1; x++) {
|
||||
row_bitsum |= row[x];
|
||||
}
|
||||
if (row_bitsum == 0) {
|
||||
y1--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int x = gli.x0 * subpixel_scale; x < gli.x1 * subpixel_scale; x += subpixel_scale) {
|
||||
unsigned int xaccu = 0;
|
||||
for (int y = y0; y < y1; y++) {
|
||||
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
||||
for (int i = 0; i < subpixel_scale; i++) {
|
||||
xaccu |= row[x + i];
|
||||
}
|
||||
}
|
||||
if (xaccu == 0) {
|
||||
x0 += subpixel_scale;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int x = (gli.x1 - 1) * subpixel_scale; x >= x0; x -= subpixel_scale) {
|
||||
unsigned int xaccu = 0;
|
||||
for (int y = y0; y < y1; y++) {
|
||||
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
|
||||
for (int i = 0; i < subpixel_scale; i++) {
|
||||
xaccu |= row[x + i];
|
||||
}
|
||||
}
|
||||
if (xaccu == 0) {
|
||||
x1 -= subpixel_scale;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
gli.xoff += (x0 / subpixel_scale) - gli.x0;
|
||||
gli.yoff += (y0 - gli.y0);
|
||||
gli.x0 = x0 / subpixel_scale;
|
||||
gli.y0 = y0;
|
||||
gli.x1 = x1 / subpixel_scale;
|
||||
gli.y1 = y1;
|
||||
}
|
||||
|
||||
static void glyph_lut_convolution(agg::rendering_buffer ren_buf, agg::lcd_distribution_lut& lcd_lut, agg::int8u *covers_buf, FR_Bitmap_Glyph_Metrics& gli) {
|
||||
const int subpixel = 3;
|
||||
const int x0 = gli.x0, y0 = gli.y0, x1 = gli.x1, y1 = gli.y1;
|
||||
const int len = (x1 - x0) * subpixel;
|
||||
const int height = ren_buf.height();
|
||||
for (int y = y0; y < y1; y++) {
|
||||
agg::int8u *covers = ren_buf.row_ptr(height - 1 - y) + x0 * subpixel;
|
||||
memcpy(covers_buf, covers, len);
|
||||
for (int x = x0 - 1; x < x1 + 1; x++) {
|
||||
for (int i = 0; i < subpixel; i++) {
|
||||
const int cx = (x - x0) * subpixel + i;
|
||||
covers[cx] = lcd_lut.convolution(covers_buf, cx, 0, len - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
gli.x0 -= 1;
|
||||
gli.x1 += 1;
|
||||
gli.xoff -= 1;
|
||||
}
|
||||
|
||||
// The two functions below are needed because in C and C++ integer division
|
||||
// is rounded toward zero.
|
||||
|
||||
// euclidean division rounded toward positive infinite
|
||||
static int div_pos(int n, int p) {
|
||||
return n >= 0 ? (n + p - 1) / p : (n / p);
|
||||
}
|
||||
|
||||
// euclidean division rounded toward negative infinite
|
||||
static int div_neg(int n, int p) {
|
||||
return n >= 0 ? (n / p) : ((n - p + 1) / p);
|
||||
}
|
||||
|
||||
FR_Bitmap *FR_Bake_Font_Bitmap(FR_Renderer *font_renderer, int font_height,
|
||||
int first_char, int num_chars, FR_Bitmap_Glyph_Metrics *glyphs)
|
||||
{
|
||||
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
|
||||
agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut();
|
||||
const int subpixel_scale = font_renderer->subpixel_scale();
|
||||
|
||||
double ascender, descender;
|
||||
renderer_alpha.get_font_vmetrics(ascender, descender);
|
||||
const int ascender_px = int(ascender * font_height);
|
||||
const int pad_y = 1;
|
||||
|
||||
// When using subpixel font rendering it is needed to leave a padding pixel on the left and on the right.
|
||||
// Since each pixel is composed by n subpixel we set below x_start to subpixel_scale instead than zero.
|
||||
// In addition we need one more pixel on the left because of subpixel positioning so
|
||||
// it adds up to 2 * subpixel_scale.
|
||||
// Note about the coordinates: they are AGG-like so x is positive toward the right and
|
||||
// y is positive in the upper direction.
|
||||
const int x_start = 2 * subpixel_scale;
|
||||
const agg::alpha8 text_color(0xff);
|
||||
#ifdef FONT_RENDERER_HEIGHT_HACK
|
||||
const int font_height_reduced = (font_height * 86) / 100;
|
||||
#else
|
||||
const int font_height_reduced = font_height;
|
||||
#endif
|
||||
renderer_alpha.set_font_height(font_height_reduced);
|
||||
|
||||
int *index = (int *) malloc(num_chars * sizeof(int));
|
||||
agg::rect_i *bounds = (agg::rect_i *) malloc(num_chars * sizeof(agg::rect_i));
|
||||
if (!index || !bounds) {
|
||||
free(index);
|
||||
free(bounds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int x_size_sum = 0, glyph_count = 0;
|
||||
for (int i = 0; i < num_chars; i++) {
|
||||
int codepoint = first_char + i;
|
||||
index[i] = i;
|
||||
if (renderer_alpha.codepoint_bounds(codepoint, subpixel_scale, bounds[i])) {
|
||||
// Invalid glyph
|
||||
bounds[i].x1 = 0;
|
||||
bounds[i].y1 = 0;
|
||||
bounds[i].x2 = -1;
|
||||
bounds[i].y2 = -1;
|
||||
} else {
|
||||
if (bounds[i].x2 > bounds[i].x1) {
|
||||
x_size_sum += bounds[i].x2 - bounds[i].x1;
|
||||
glyph_count++;
|
||||
}
|
||||
bounds[i].x1 = subpixel_scale * div_neg(bounds[i].x1, subpixel_scale);
|
||||
bounds[i].x2 = subpixel_scale * div_pos(bounds[i].x2, subpixel_scale);
|
||||
}
|
||||
}
|
||||
|
||||
// Simple insertion sort algorithm: https://en.wikipedia.org/wiki/Insertion_sort
|
||||
int i = 1;
|
||||
while (i < num_chars) {
|
||||
int j = i;
|
||||
while (j > 0 && bounds[index[j-1]].y2 - bounds[index[j-1]].y1 > bounds[index[j]].y2 - bounds[index[j]].y1) {
|
||||
int tmp = index[j];
|
||||
index[j] = index[j-1];
|
||||
index[j-1] = tmp;
|
||||
j = j - 1;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
const int glyph_avg_width = glyph_count > 0 ? x_size_sum / (glyph_count * subpixel_scale) : font_height;
|
||||
const int pixels_width = glyph_avg_width > 0 ? glyph_avg_width * 28 : 28;
|
||||
|
||||
// dry run simulating pixel position to estimate required image's height
|
||||
int x = x_start, y = 0, y_bottom = y;
|
||||
for (int i = 0; i < num_chars; i++) {
|
||||
const agg::rect_i& gbounds = bounds[index[i]];
|
||||
if (gbounds.x2 < gbounds.x1) continue;
|
||||
// 1. It is very important to ensure that the x's increment below (1) and in
|
||||
// (2), (3) and (4) are perfectly the same.
|
||||
// Note that x_step below is always an integer multiple of subpixel_scale.
|
||||
const int x_step = gbounds.x2 + 3 * subpixel_scale;
|
||||
if (x + x_step >= pixels_width * subpixel_scale) {
|
||||
x = x_start;
|
||||
y = y_bottom;
|
||||
}
|
||||
// 5. Ensure that y's increment below is exactly the same to the one used in (6)
|
||||
const int glyph_y_bottom = y - 2 * pad_y - (gbounds.y2 - gbounds.y1);
|
||||
y_bottom = (y_bottom > glyph_y_bottom ? glyph_y_bottom : y_bottom);
|
||||
// 2. Ensure x's increment is aligned with (1)
|
||||
x = x + x_step;
|
||||
}
|
||||
|
||||
agg::int8u *cover_swap_buffer = (agg::int8u *) malloc(sizeof(agg::int8u) * (pixels_width * subpixel_scale));
|
||||
if (!cover_swap_buffer) {
|
||||
free(index);
|
||||
free(bounds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int pixels_height = -y_bottom + 1;
|
||||
const int pixel_size = 1;
|
||||
FR_Bitmap *image = FR_Bitmap_New(font_renderer, pixels_width, pixels_height);
|
||||
if (!image) {
|
||||
free(index);
|
||||
free(bounds);
|
||||
free(cover_swap_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
agg::int8u *pixels = image->pixels;
|
||||
memset(pixels, 0x00, pixels_width * pixels_height * subpixel_scale * pixel_size);
|
||||
agg::rendering_buffer ren_buf(pixels, pixels_width * subpixel_scale, pixels_height, -pixels_width * subpixel_scale * pixel_size);
|
||||
|
||||
// The variable y_bottom will be used to go down to the next row by taking into
|
||||
// account the space occupied by each glyph of the current row along the y direction.
|
||||
x = x_start;
|
||||
// Set y to the image's height minus one to begin writing glyphs in the upper part of the image.
|
||||
y = pixels_height - 1;
|
||||
y_bottom = y;
|
||||
for (int i = 0; i < num_chars; i++) {
|
||||
// Important: the variable x in this loop should always be an integer multiple
|
||||
// of subpixel_scale.
|
||||
int codepoint = first_char + index[i];
|
||||
const agg::rect_i& gbounds = bounds[index[i]];
|
||||
if (gbounds.x2 < gbounds.x1) continue;
|
||||
|
||||
// 3. Ensure x's increment is aligned with (1)
|
||||
// Note that x_step below is always an integer multiple of subpixel_scale.
|
||||
// We need 3 * subpixel_scale because:
|
||||
// . +1 pixel on the left, because of RGB color filter
|
||||
// . +1 pixel on the right, because of RGB color filter
|
||||
// . +1 pixel on the right, because of subpixel positioning
|
||||
// and each pixel requires "subpixel_scale" sub-pixels.
|
||||
const int x_step = gbounds.x2 + 3 * subpixel_scale;
|
||||
if (x + x_step >= pixels_width * subpixel_scale) {
|
||||
// No more space along x, begin writing the row below.
|
||||
x = x_start;
|
||||
y = y_bottom;
|
||||
}
|
||||
|
||||
const int y_baseline = y - pad_y - gbounds.y2;
|
||||
// 6. Ensure the y's increment below is aligned with the increment used in (5)
|
||||
const int glyph_y_bottom = y - 2 * pad_y - (gbounds.y2 - gbounds.y1);
|
||||
y_bottom = (y_bottom > glyph_y_bottom ? glyph_y_bottom : y_bottom);
|
||||
|
||||
double x_next = x, y_next = y_baseline;
|
||||
renderer_alpha.render_codepoint(ren_buf, text_color, x_next, y_next, codepoint, subpixel_scale);
|
||||
|
||||
// The y coordinate for the glyph below is positive in the bottom direction,
|
||||
// like is used by Lite's drawing system.
|
||||
FR_Bitmap_Glyph_Metrics& glyph_info = glyphs[index[i]];
|
||||
glyph_info.x0 = x / subpixel_scale;
|
||||
glyph_info.y0 = pixels_height - 1 - (y_baseline + gbounds.y2 + pad_y);
|
||||
glyph_info.x1 = div_pos(x_next + 0.5, subpixel_scale);
|
||||
glyph_info.y1 = pixels_height - 1 - (y_baseline + gbounds.y1 - pad_y);
|
||||
|
||||
glyph_info.xoff = 0;
|
||||
glyph_info.yoff = -pad_y - gbounds.y2 + ascender_px;
|
||||
// Note that below the xadvance is in pixels times the subpixel_scale.
|
||||
// This is meant for subpixel positioning.
|
||||
glyph_info.xadvance = roundf(x_next - x);
|
||||
|
||||
if (subpixel_scale != 1 && glyph_info.x1 > glyph_info.x0) {
|
||||
glyph_lut_convolution(ren_buf, lcd_lut, cover_swap_buffer, glyph_info);
|
||||
}
|
||||
glyph_trim_rect(ren_buf, glyph_info, subpixel_scale);
|
||||
|
||||
// When subpixel is activated we need one padding pixel on the left and on the right
|
||||
// and one more because of subpixel positioning.
|
||||
// 4. Ensure x's increment is aligned with (1)
|
||||
x = x + x_step;
|
||||
}
|
||||
|
||||
free(index);
|
||||
free(bounds);
|
||||
free(cover_swap_buffer);
|
||||
return image;
|
||||
}
|
||||
|
||||
template <typename Order>
|
||||
void blend_solid_hspan(agg::rendering_buffer& rbuf, int x, int y, unsigned len,
|
||||
const agg::rgba8& c, const agg::int8u* covers)
|
||||
{
|
||||
const int pixel_size = 4;
|
||||
agg::int8u* p = rbuf.row_ptr(y) + x * pixel_size;
|
||||
do
|
||||
{
|
||||
const unsigned alpha = *covers;
|
||||
const unsigned r = p[Order::R], g = p[Order::G], b = p[Order::B];
|
||||
p[Order::R] = (((unsigned(c.r) - r) * alpha) >> 8) + r;
|
||||
p[Order::G] = (((unsigned(c.g) - g) * alpha) >> 8) + g;
|
||||
p[Order::B] = (((unsigned(c.b) - b) * alpha) >> 8) + b;
|
||||
// Leave p[3], the alpha channel value unmodified.
|
||||
p += 4;
|
||||
++covers;
|
||||
}
|
||||
while(--len);
|
||||
}
|
||||
|
||||
template <typename Order>
|
||||
void blend_solid_hspan_subpixel(agg::rendering_buffer& rbuf, agg::lcd_distribution_lut& lcd_lut,
|
||||
const int x, const int y, unsigned len,
|
||||
const agg::rgba8& c,
|
||||
const agg::int8u* covers)
|
||||
{
|
||||
const int pixel_size = 4;
|
||||
const unsigned rgb[3] = { c.r, c.g, c.b };
|
||||
agg::int8u* p = rbuf.row_ptr(y) + x * pixel_size;
|
||||
|
||||
// Indexes to adress RGB colors in a BGRA32 format.
|
||||
const int pixel_index[3] = {Order::R, Order::G, Order::B};
|
||||
for (unsigned cx = 0; cx < len; cx += 3)
|
||||
{
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const unsigned cover_value = covers[cx + i];
|
||||
const unsigned alpha = (cover_value + 1) * (c.a + 1);
|
||||
const unsigned src_col = *(p + pixel_index[i]);
|
||||
*(p + pixel_index[i]) = (((rgb[i] - src_col) * alpha) + (src_col << 16)) >> 16;
|
||||
}
|
||||
// Leave p[3], the alpha channel value unmodified.
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// destination implicitly BGRA32. Source implictly single-byte renderer_alpha coverage with subpixel scale = 3.
|
||||
// FIXME: consider using something like RenColor* instead of uint8_t * for dst.
|
||||
void FR_Blend_Glyph(FR_Renderer *font_renderer, FR_Clip_Area *clip, int x_mult, int y, uint8_t *dst, int dst_width, const FR_Bitmap *glyphs_bitmap, const FR_Bitmap_Glyph_Metrics *glyph, FR_Color color) {
|
||||
agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut();
|
||||
const int subpixel_scale = font_renderer->subpixel_scale();
|
||||
const int pixel_size = 4; // Pixel size for BGRA32 format.
|
||||
|
||||
int x = x_mult / subpixel_scale;
|
||||
|
||||
x += glyph->xoff;
|
||||
y += glyph->yoff;
|
||||
|
||||
int glyph_x = glyph->x0, glyph_y = glyph->y0;
|
||||
int glyph_x_subpixel = -(x_mult % subpixel_scale);
|
||||
int glyph_width = glyph->x1 - glyph->x0;
|
||||
int glyph_height = glyph->y1 - glyph->y0;
|
||||
|
||||
int n;
|
||||
if ((n = clip->left - x) > 0) { glyph_width -= n; glyph_x += n; x += n; }
|
||||
if ((n = clip->top - y) > 0) { glyph_height -= n; glyph_y += n; y += n; }
|
||||
if ((n = x + glyph_width - clip->right ) > 0) { glyph_width -= n; }
|
||||
if ((n = y + glyph_height - clip->bottom) > 0) { glyph_height -= n; }
|
||||
|
||||
if (glyph_width <= 0 || glyph_height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dst += (x + y * dst_width) * pixel_size;
|
||||
agg::rendering_buffer dst_ren_buf(dst, glyph_width, glyph_height, dst_width * pixel_size);
|
||||
|
||||
uint8_t *src = glyphs_bitmap->pixels + (glyph_x + glyph_y * glyphs_bitmap->width) * subpixel_scale + glyph_x_subpixel;
|
||||
int src_stride = glyphs_bitmap->width * subpixel_scale;
|
||||
|
||||
const agg::rgba8 color_a(color.r, color.g, color.b);
|
||||
for (int x = 0, y = 0; y < glyph_height; y++) {
|
||||
agg::int8u *covers = src + y * src_stride;
|
||||
if (subpixel_scale == 1) {
|
||||
blend_solid_hspan<agg::order_bgra>(dst_ren_buf, x, y, glyph_width, color_a, covers);
|
||||
} else {
|
||||
blend_solid_hspan_subpixel<agg::order_bgra>(dst_ren_buf, lcd_lut, x, y, glyph_width * subpixel_scale, color_a, covers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#ifndef FONT_RENDERER_H
|
||||
#define FONT_RENDERER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned short x0, y0, x1, y1;
|
||||
float xoff, yoff, xadvance;
|
||||
} FR_Bitmap_Glyph_Metrics;
|
||||
|
||||
typedef struct FR_Bitmap FR_Bitmap;
|
||||
|
||||
#ifdef __cplusplus
|
||||
class FR_Renderer;
|
||||
#else
|
||||
struct FR_Renderer;
|
||||
typedef struct FR_Renderer FR_Renderer;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
FR_HINTING = 1 << 0,
|
||||
FR_KERNING = 1 << 1,
|
||||
FR_SUBPIXEL = 1 << 2,
|
||||
FR_PRESCALE_X = 1 << 3,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t r, g, b;
|
||||
} FR_Color;
|
||||
|
||||
typedef struct {
|
||||
int left, top, right, bottom;
|
||||
} FR_Clip_Area;
|
||||
|
||||
FR_Renderer * FR_Renderer_New(unsigned int flags);
|
||||
void FR_Renderer_Free(FR_Renderer *);
|
||||
int FR_Load_Font(FR_Renderer *, const char *filename);
|
||||
FR_Bitmap* FR_Bitmap_New(FR_Renderer *, int width, int height);
|
||||
void FR_Bitmap_Free(FR_Bitmap *image);
|
||||
int FR_Get_Font_Height(FR_Renderer *, float size);
|
||||
FR_Bitmap * FR_Bake_Font_Bitmap(FR_Renderer *, int font_height,
|
||||
int first_char, int num_chars, FR_Bitmap_Glyph_Metrics *glyph_info);
|
||||
void FR_Blend_Glyph(FR_Renderer *font_renderer,
|
||||
FR_Clip_Area *clip, int x, int y,
|
||||
uint8_t *dst, int dst_width,
|
||||
const FR_Bitmap *glyphs_bitmap,
|
||||
const FR_Bitmap_Glyph_Metrics *glyph, FR_Color color);
|
||||
int FR_Subpixel_Scale(FR_Renderer *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,164 +0,0 @@
|
||||
#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_subpixel;
|
||||
bool m_prescale_x;
|
||||
|
||||
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, bool subpixel, bool prescale_x):
|
||||
m_feng(),
|
||||
m_fman(m_feng),
|
||||
m_hinting(hinting),
|
||||
m_kerning(kerning),
|
||||
m_subpixel(subpixel),
|
||||
m_prescale_x(prescale_x),
|
||||
m_font_loaded(false),
|
||||
m_curves(m_fman.path_adaptor()),
|
||||
m_trans(m_curves, m_mtx)
|
||||
{ }
|
||||
|
||||
int get_face_height() const {
|
||||
return m_feng.face_height();
|
||||
}
|
||||
|
||||
void get_font_vmetrics(double& ascender, double& descender) {
|
||||
double current_height = m_feng.height();
|
||||
m_feng.height(1.0);
|
||||
ascender = m_feng.ascender();
|
||||
descender = m_feng.descender();
|
||||
m_feng.height(current_height);
|
||||
}
|
||||
|
||||
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;
|
||||
m_feng.hinting(m_hinting);
|
||||
}
|
||||
return m_font_loaded;
|
||||
}
|
||||
|
||||
void set_font_height(double height) {
|
||||
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
|
||||
m_feng.height(height);
|
||||
m_feng.width(height * scale_x);
|
||||
}
|
||||
|
||||
template<class Rasterizer, class Scanline, class RenSolid>
|
||||
void draw_codepoint(Rasterizer& ras, Scanline& sl,
|
||||
RenSolid& ren_solid, const color_type color,
|
||||
int codepoint, double& x, double& y, const int subpixel_scale)
|
||||
{
|
||||
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
|
||||
// Coefficient to scale back the glyph to the final scale.
|
||||
const double cx_inv_scale = subpixel_scale / scale_x;
|
||||
|
||||
// Represent the delta in x scaled by scale_x.
|
||||
double x_delta = 0;
|
||||
double start_x = x;
|
||||
|
||||
const agg::glyph_cache* glyph = m_fman.glyph(codepoint);
|
||||
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(cx_inv_scale, 1);
|
||||
m_mtx *= agg::trans_affine_translation(start_x + cx_inv_scale * x_delta, ty);
|
||||
ras.add_path(m_trans);
|
||||
ren_solid.color(color);
|
||||
agg::render_scanlines(ras, sl, ren_solid);
|
||||
}
|
||||
|
||||
y += glyph->advance_y;
|
||||
x += cx_inv_scale * (x_delta + glyph->advance_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_codepoint(agg::rendering_buffer& ren_buf,
|
||||
const color_type text_color,
|
||||
double& x, double& y,
|
||||
int codepoint, const int subpixel_scale)
|
||||
{
|
||||
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_codepoint(ras, sl, ren_solid, text_color, codepoint, x, y, subpixel_scale);
|
||||
}
|
||||
|
||||
int codepoint_bounds(int codepoint, const int subpixel_scale, agg::rect_i& bounds)
|
||||
{
|
||||
if (!m_font_loaded) return 1;
|
||||
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
|
||||
const double cx_inv_scale = subpixel_scale / scale_x;
|
||||
const agg::glyph_cache* glyph = m_fman.glyph(codepoint);
|
||||
if (glyph) {
|
||||
bounds = glyph->bounds;
|
||||
bounds.x1 *= cx_inv_scale;
|
||||
bounds.x2 *= cx_inv_scale;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
freetype_dep = dependency('freetype2')
|
||||
|
||||
libagg_dep = dependency('libagg', fallback: ['libagg', 'libagg_dep'])
|
||||
|
||||
font_renderer_sources = [
|
||||
'agg_font_freetype.cpp',
|
||||
'font_renderer.cpp',
|
||||
]
|
||||
|
||||
font_renderer_cdefs = ['-DFONT_RENDERER_HEIGHT_HACK']
|
||||
|
||||
font_renderer_include = include_directories('.')
|
||||
|
||||
libfontrenderer = static_library('fontrenderer',
|
||||
font_renderer_sources,
|
||||
dependencies: [libagg_dep, freetype_dep],
|
||||
cpp_args: font_renderer_cdefs,
|
||||
)
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
```c
|
||||
stbtt_InitFont
|
||||
|
||||
stbtt_ScaleForMappingEmToPixels x 3
|
||||
stbtt_ScaleForPixelHeight
|
||||
stbtt_BakeFontBitmap
|
||||
stbtt_GetFontVMetrics x 2
|
||||
|
||||
typedef struct {
|
||||
unsigned short x0, y0, x1, y1; // coordinates of bbox in bitmap
|
||||
float xoff, yoff, xadvance;
|
||||
} stbtt_bakedchar;
|
||||
|
||||
struct RenImage {
|
||||
RenColor *pixels;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
RenImage *image;
|
||||
stbtt_bakedchar glyphs[256];
|
||||
} GlyphSet;
|
||||
|
||||
struct RenFont {
|
||||
void *data;
|
||||
stbtt_fontinfo stbfont;
|
||||
GlyphSet *sets[MAX_GLYPHSET];
|
||||
float size;
|
||||
int height;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
The function stbtt_BakeFontBitmap is used to write bitmap data into set->image->pixels (where set is a GlyphSet).
|
||||
Note that set->image->pixels need data in RGB format. After stbtt_BakeFontBitmap call the bitmap data are converted into RGB.
|
||||
With a single call many glyphs corresponding to a range of codepoints, all in a
|
||||
single image.
|
||||
|
||||
## STB truetype font metrics
|
||||
|
||||
stbtt_ScaleForPixelHeight takes a float 'height' and returns height / (ascent - descent).
|
||||
|
||||
stbtt_ScaleForMappingEmToPixels take a float 'pixels' and returns pixels / unitsPerEm.
|
||||
|
||||
### Computing RenFont
|
||||
|
||||
When loading a font, in renderer.c, the font->height is determined as:
|
||||
|
||||
```c
|
||||
int ascent, descent, linegap;
|
||||
stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
|
||||
float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size);
|
||||
font->height = (ascent - descent + linegap) * scale + 0.5;
|
||||
```
|
||||
|
||||
so, mathematically
|
||||
|
||||
```c
|
||||
font->height = (ascent - descent + linegap) * font->size / unitsPerEm + 0.5;
|
||||
```
|
||||
|
||||
**TO DO**: find out for what font->height is actually used.
|
||||
|
||||
### Call to BakeFontBitmap
|
||||
|
||||
In the same file, renderer.c, to create the glyphset image it computes:
|
||||
|
||||
```c
|
||||
// Using stbtt functions
|
||||
float s = ScaleForMappingEmToPixels(1) / ScaleForPixelHeight(1);
|
||||
```
|
||||
|
||||
so 's' is actually equal to (ascent - descent) / unitsPerEm.
|
||||
|
||||
Then BakeFontBitmap is called and `font->size * s` is used for the pixel_height argument.
|
||||
So BakeFontBitmap gets
|
||||
|
||||
```c
|
||||
pixel_height = (ascent - descent) * font->size / unitsPerEm;
|
||||
```
|
||||
|
||||
In turns BakeFontBitmap passes pixel_height to ScaleForPixelHeight() and uses the
|
||||
resulting 'scale' to scale the glyphs by passing it to MakeGlyphBitmap().
|
||||
|
||||
This is equal almost equal to font->height except the 0.5, the missing linegap calculation
|
||||
and the fact that this latter is an integer instead of a float.
|
||||
|
||||
## AGG Font Engine
|
||||
|
||||
Calls
|
||||
|
||||
`FT_Init_FreeType` (initialize the library)
|
||||
|
||||
In `load_font()` method:
|
||||
`FT_New_Face` or `FT_New_Memory_Face` (use `FT_Done_Face` when done)
|
||||
|
||||
`FT_Attach_File`
|
||||
`FT_Select_Charmap`
|
||||
|
||||
In `update_char_size()` method:
|
||||
`FT_Set_Char_Size` or `FT_Set_Pixel_Sizes`
|
||||
|
||||
In `prepare_glyph()` method:
|
||||
`FT_Get_Char_Index`
|
||||
`FT_Load_Glyph`
|
||||
`FT_Render_Glyph` (if glyph_render_native_mono or native_gray8)
|
||||
|
||||
in `add_kerning()` method
|
||||
`FT_Get_Kerning`
|
||||
|
||||
`FT_Done_FreeType` (end with library)
|
||||
|
||||
## Freetype2's metrics related function and structs
|
||||
|
||||
`FT_New_Face` return a `FT_Face` (a pointer) with font face information.
|
||||
|
||||
## AGG font engine's font size
|
||||
|
||||
The variable `m_height` is the size of the font muliplied by 64.
|
||||
It will be used to set font size with:
|
||||
|
||||
- `FT_Set_Char_Size` if m_resolution is set (> 0)
|
||||
- `FT_Set_Pixel_Sizes`, divided by 64, if m_resolution is not set (= 0)
|
||||
|
||||
The method height() returns m_height / 64;
|
||||
Reference in New Issue
Block a user