diff --git a/data/core/docview.lua b/data/core/docview.lua index 161eac47..553a9888 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -150,14 +150,14 @@ function DocView:get_col_x_offset(line, col) local font = style.syntax_fonts[type] or default_font for char in common.utf8_chars(text) do if column == col then - return xoffset / font:subpixel_scale() + return xoffset end - xoffset = xoffset + font:get_width_subpixel(char) + xoffset = xoffset + font:get_width(char) column = column + #char end end - return xoffset / default_font:subpixel_scale() + return xoffset end @@ -166,14 +166,12 @@ function DocView:get_x_offset_col(line, x) local xoffset, last_i, i = 0, 1, 1 local default_font = self:get_font() - local subpixel_scale = default_font:subpixel_scale() - local x_subpixel = subpixel_scale * x + subpixel_scale / 2 for _, type, text in self.doc.highlighter:each_token(line) do local font = style.syntax_fonts[type] or default_font for char in common.utf8_chars(text) do - local w = font:get_width_subpixel(char) - if xoffset >= subpixel_scale * x then - return (xoffset - x_subpixel > w / 2) and last_i or i + local w = font:get_width(char) + if xoffset >= x then + return (xoffset - x > w / 2) and last_i or i end xoffset = xoffset + w last_i = i @@ -339,16 +337,11 @@ end function DocView:draw_line_text(idx, x, y) local default_font = self:get_font() - local subpixel_scale = default_font:subpixel_scale() - local tx, ty = subpixel_scale * x, y + self:get_line_text_y_offset() + local tx, ty = x, y + self:get_line_text_y_offset() for _, type, text in self.doc.highlighter:each_token(idx) do local color = style.syntax[type] local font = style.syntax_fonts[type] or default_font - if config.draw_whitespace then - tx = renderer.draw_text_subpixel(font, text, tx, ty, color, core.replacements, style.syntax.comment) - else - tx = renderer.draw_text_subpixel(font, text, tx, ty, color) - end + tx = renderer.draw_text(font, text, tx, ty, color) end end diff --git a/data/core/init.lua b/data/core/init.lua index af291767..cab93c11 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -393,15 +393,6 @@ function core.remove_project_directory(path) return false end - -local function whitespace_replacements() - local r = renderer.replacements.new() - r:add(" ", "·") - r:add("\t", "»") - return r -end - - local function reload_on_user_module_save() -- auto-realod style when user's module is saved by overriding Doc:Save() local doc_save = Doc.save @@ -499,7 +490,6 @@ function core.init() core.visited_files = {} core.restart_request = false core.quit_request = false - core.replacements = whitespace_replacements() core.root_view = RootView() core.command_view = CommandView() diff --git a/data/plugins/drawwhitespace.lua b/data/plugins/drawwhitespace.lua new file mode 100644 index 00000000..d6700615 --- /dev/null +++ b/data/plugins/drawwhitespace.lua @@ -0,0 +1,31 @@ +-- mod-version:2 -- lite-xl 2.0 + +local style = require "core.style" +local DocView = require "core.docview" +local common = require "core.common" + +local draw_line_text = DocView.draw_line_text + +function DocView:draw_line_text(idx, x, y) + local font = (self:get_font() or style.syntax_fonts["comment"]) + local color = style.syntax.comment + local ty, tx = y + self:get_line_text_y_offset() + local text, offset, s, e = self.doc.lines[idx], 1 + while true do + s, e = text:find(" +", offset) + if not s then break end + tx = self:get_col_x_offset(idx, s) + x + renderer.draw_text(font, string.rep("·", e - s + 1), tx, ty, color) + offset = e + 1 + end + while true do + s, e = text:find("\t", offset) + if not s then break end + tx = self:get_col_x_offset(idx, s) + x + renderer.draw_text(font, "»", tx, ty, color) + offset = e + 1 + end + draw_line_text(self, idx, x, y) +end + + diff --git a/lib/font_renderer/agg_font_freetype.cpp b/lib/font_renderer/agg_font_freetype.cpp deleted file mode 100644 index c240927e..00000000 --- a/lib/font_renderer/agg_font_freetype.cpp +++ /dev/null @@ -1,1161 +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 -//---------------------------------------------------------------------------- - - -#include -#include "agg_font_freetype.h" -#include "agg_bitset_iterator.h" -#include "agg_renderer_scanline.h" - - -namespace agg -{ - - //------------------------------------------------------------------------------ - // - // This code implements the AUTODIN II polynomial - // The variable corresponding to the macro argument "crc" should - // be an unsigned long. - // Oroginal code by Spencer Garrett - // - - // generated using the AUTODIN II polynomial - // x^32 + x^26 + x^23 + x^22 + x^16 + - // x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - // - //------------------------------------------------------------------------------ - - static const unsigned crc32tab[256] = - { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - }; - - - //------------------------------------------------------------------------------ - - static unsigned calc_crc32(const unsigned char* buf, unsigned size) - { - unsigned crc = (unsigned)~0; - const unsigned char* p; - unsigned len = 0; - unsigned nr = size; - - for (len += nr, p = buf; nr--; ++p) - { - crc = (crc >> 8) ^ crc32tab[(crc ^ *p) & 0xff]; - } - return ~crc; - } - - //------------------------------------------------------------------------ - static inline int dbl_to_plain_fx(double d) - { - return int(d * 65536.0); - } - - //------------------------------------------------------------------------ - static inline double int26p6_to_dbl(int p) - { - return double(p) / 64.0; - } - - //------------------------------------------------------------------------ - static inline int dbl_to_int26p6(double p) - { - return int(p * 64.0 + 0.5); - } - - - //------------------------------------------------------------------------ - template - bool decompose_ft_outline(const FT_Outline& outline, - bool flip_y, - const trans_affine& mtx, - PathStorage& path) - { - typedef typename PathStorage::value_type value_type; - - FT_Vector v_last; - FT_Vector v_control; - FT_Vector v_start; - double x1, y1, x2, y2, x3, y3; - - FT_Vector* point; - FT_Vector* limit; - char* tags; - - int n; // index of contour in outline - int first; // index of first point in contour - char tag; // current point's state - - first = 0; - - for(n = 0; n < outline.n_contours; n++) - { - int last; // index of last point in contour - - last = outline.contours[n]; - limit = outline.points + last; - - v_start = outline.points[first]; - v_last = outline.points[last]; - - v_control = v_start; - - point = outline.points + first; - tags = outline.tags + first; - tag = FT_CURVE_TAG(tags[0]); - - // A contour cannot start with a cubic control point! - if(tag == FT_CURVE_TAG_CUBIC) return false; - - // check first point to determine origin - if( tag == FT_CURVE_TAG_CONIC) - { - // first point is conic control. Yes, this happens. - if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) - { - // start at last point if it is on the curve - v_start = v_last; - limit--; - } - else - { - // if both first and last points are conic, - // start at their middle and record its position - // for closure - v_start.x = (v_start.x + v_last.x) / 2; - v_start.y = (v_start.y + v_last.y) / 2; - - v_last = v_start; - } - point--; - tags--; - } - - x1 = int26p6_to_dbl(v_start.x); - y1 = int26p6_to_dbl(v_start.y); - if(flip_y) y1 = -y1; - mtx.transform(&x1, &y1); - path.move_to(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1))); - - while(point < limit) - { - point++; - tags++; - - tag = FT_CURVE_TAG(tags[0]); - switch(tag) - { - case FT_CURVE_TAG_ON: // emit a single line_to - { - x1 = int26p6_to_dbl(point->x); - y1 = int26p6_to_dbl(point->y); - if(flip_y) y1 = -y1; - mtx.transform(&x1, &y1); - path.line_to(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1))); - //path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y)); - continue; - } - - case FT_CURVE_TAG_CONIC: // consume conic arcs - { - v_control.x = point->x; - v_control.y = point->y; - - Do_Conic: - if(point < limit) - { - FT_Vector vec; - FT_Vector v_middle; - - point++; - tags++; - tag = FT_CURVE_TAG(tags[0]); - - vec.x = point->x; - vec.y = point->y; - - if(tag == FT_CURVE_TAG_ON) - { - x1 = int26p6_to_dbl(v_control.x); - y1 = int26p6_to_dbl(v_control.y); - x2 = int26p6_to_dbl(vec.x); - y2 = int26p6_to_dbl(vec.y); - if(flip_y) { y1 = -y1; y2 = -y2; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - path.curve3(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2))); - continue; - } - - if(tag != FT_CURVE_TAG_CONIC) return false; - - v_middle.x = (v_control.x + vec.x) / 2; - v_middle.y = (v_control.y + vec.y) / 2; - - x1 = int26p6_to_dbl(v_control.x); - y1 = int26p6_to_dbl(v_control.y); - x2 = int26p6_to_dbl(v_middle.x); - y2 = int26p6_to_dbl(v_middle.y); - if(flip_y) { y1 = -y1; y2 = -y2; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - path.curve3(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2))); - - //path.curve3(conv(v_control.x), - // flip_y ? -conv(v_control.y) : conv(v_control.y), - // conv(v_middle.x), - // flip_y ? -conv(v_middle.y) : conv(v_middle.y)); - - v_control = vec; - goto Do_Conic; - } - - x1 = int26p6_to_dbl(v_control.x); - y1 = int26p6_to_dbl(v_control.y); - x2 = int26p6_to_dbl(v_start.x); - y2 = int26p6_to_dbl(v_start.y); - if(flip_y) { y1 = -y1; y2 = -y2; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - path.curve3(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2))); - - //path.curve3(conv(v_control.x), - // flip_y ? -conv(v_control.y) : conv(v_control.y), - // conv(v_start.x), - // flip_y ? -conv(v_start.y) : conv(v_start.y)); - goto Close; - } - - default: // FT_CURVE_TAG_CUBIC - { - FT_Vector vec1, vec2; - - if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) - { - return false; - } - - vec1.x = point[0].x; - vec1.y = point[0].y; - vec2.x = point[1].x; - vec2.y = point[1].y; - - point += 2; - tags += 2; - - if(point <= limit) - { - FT_Vector vec; - - vec.x = point->x; - vec.y = point->y; - - x1 = int26p6_to_dbl(vec1.x); - y1 = int26p6_to_dbl(vec1.y); - x2 = int26p6_to_dbl(vec2.x); - y2 = int26p6_to_dbl(vec2.y); - x3 = int26p6_to_dbl(vec.x); - y3 = int26p6_to_dbl(vec.y); - if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - mtx.transform(&x3, &y3); - path.curve4(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2)), - value_type(dbl_to_int26p6(x3)), - value_type(dbl_to_int26p6(y3))); - - //path.curve4(conv(vec1.x), - // flip_y ? -conv(vec1.y) : conv(vec1.y), - // conv(vec2.x), - // flip_y ? -conv(vec2.y) : conv(vec2.y), - // conv(vec.x), - // flip_y ? -conv(vec.y) : conv(vec.y)); - continue; - } - - x1 = int26p6_to_dbl(vec1.x); - y1 = int26p6_to_dbl(vec1.y); - x2 = int26p6_to_dbl(vec2.x); - y2 = int26p6_to_dbl(vec2.y); - x3 = int26p6_to_dbl(v_start.x); - y3 = int26p6_to_dbl(v_start.y); - if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - mtx.transform(&x3, &y3); - path.curve4(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2)), - value_type(dbl_to_int26p6(x3)), - value_type(dbl_to_int26p6(y3))); - - //path.curve4(conv(vec1.x), - // flip_y ? -conv(vec1.y) : conv(vec1.y), - // conv(vec2.x), - // flip_y ? -conv(vec2.y) : conv(vec2.y), - // conv(v_start.x), - // flip_y ? -conv(v_start.y) : conv(v_start.y)); - goto Close; - } - } - } - - path.close_polygon(); - - Close: - first = last + 1; - } - - return true; - } - - - - //------------------------------------------------------------------------ - template - void decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, - int x, int y, - bool flip_y, - Scanline& sl, - ScanlineStorage& storage) - { - int i; - const int8u* buf = (const int8u*)bitmap.buffer; - const int bitmap_rows = bitmap.rows, bitmap_width = bitmap.width; - int pitch = bitmap.pitch; - sl.reset(x, x + bitmap.width); - storage.prepare(); - if(flip_y) - { - buf += bitmap.pitch * (bitmap.rows - 1); - y += bitmap.rows; - pitch = -pitch; - } - for(i = 0; i < bitmap_rows; i++) - { - sl.reset_spans(); - bitset_iterator bits(buf, 0); - int j; - for(j = 0; j < bitmap_width; j++) - { - if(bits.bit()) sl.add_cell(x + j, cover_full); - ++bits; - } - buf += pitch; - if(sl.num_spans()) - { - sl.finalize(y - i - 1); - storage.render(sl); - } - } - } - - - - //------------------------------------------------------------------------ - template - void decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, - int x, int y, - bool flip_y, - Rasterizer& ras, - Scanline& sl, - ScanlineStorage& storage) - { - int i, j; - const int8u* buf = (const int8u*)bitmap.buffer; - const int bitmap_rows = bitmap.rows, bitmap_width = bitmap.width; - int pitch = bitmap.pitch; - sl.reset(x, x + bitmap.width); - storage.prepare(); - if(flip_y) - { - buf += bitmap.pitch * (bitmap.rows - 1); - y += bitmap.rows; - pitch = -pitch; - } - for(i = 0; i < bitmap_rows; i++) - { - sl.reset_spans(); - const int8u* p = buf; - for(j = 0; j < bitmap_width; j++) - { - if(*p) sl.add_cell(x + j, ras.apply_gamma(*p)); - ++p; - } - buf += pitch; - if(sl.num_spans()) - { - sl.finalize(y - i - 1); - storage.render(sl); - } - } - } - - - - - - - - - - - - - - //------------------------------------------------------------------------ - font_engine_freetype_base::~font_engine_freetype_base() - { - unsigned i; - for(i = 0; i < m_num_faces; ++i) - { - delete [] m_face_names[i]; - FT_Done_Face(m_faces[i]); - } - delete [] m_face_names; - delete [] m_faces; - delete [] m_signature; - if(m_library_initialized) FT_Done_FreeType(m_library); - } - - - //------------------------------------------------------------------------ - font_engine_freetype_base::font_engine_freetype_base(bool flag32, - unsigned max_faces) : - m_flag32(flag32), - m_change_stamp(0), - m_last_error(0), - m_name(0), - m_name_len(256-16-1), - m_face_index(0), - m_char_map(FT_ENCODING_NONE), - m_signature(new char [256+256-16]), - m_height(0), - m_width(0), - m_hinting(true), - m_flip_y(false), - m_library_initialized(false), - m_library(0), - m_faces(new FT_Face [max_faces]), - m_face_names(new char* [max_faces]), - m_num_faces(0), - m_max_faces(max_faces), - m_cur_face(0), - m_resolution(0), - m_glyph_rendering(glyph_ren_native_gray8), - m_glyph_index(0), - m_data_size(0), - m_data_type(glyph_data_invalid), - m_bounds(1,1,0,0), - m_advance_x(0.0), - m_advance_y(0.0), - - m_path16(), - m_path32(), - m_curves16(m_path16), - m_curves32(m_path32), - m_scanline_aa(), - m_scanline_bin(), - m_scanlines_aa(), - m_scanlines_bin(), - m_rasterizer() - { - m_curves16.approximation_scale(4.0); - m_curves32.approximation_scale(4.0); - m_last_error = FT_Init_FreeType(&m_library); - if(m_last_error == 0) m_library_initialized = true; - } - - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::resolution(unsigned dpi) - { - m_resolution = dpi; - update_char_size(); - } - - - //------------------------------------------------------------------------ - int font_engine_freetype_base::find_face(const char* face_name) const - { - unsigned i; - for(i = 0; i < m_num_faces; ++i) - { - if(strcmp(face_name, m_face_names[i]) == 0) return i; - } - return -1; - } - - //------------------------------------------------------------------------ - int font_engine_freetype_base::face_height() const - { - return (m_cur_face ? m_cur_face->height : -1); - } - - int font_engine_freetype_base::face_units_em() const - { - return (m_cur_face ? m_cur_face->units_per_EM : -1); - } - - //------------------------------------------------------------------------ - double font_engine_freetype_base::ascender() const - { - if(m_cur_face) - { - return m_cur_face->ascender * height() / m_cur_face->height; - } - return 0.0; - } - - //------------------------------------------------------------------------ - double font_engine_freetype_base::descender() const - { - if(m_cur_face) - { - return m_cur_face->descender * height() / m_cur_face->height; - } - return 0.0; - } - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::load_font(const char* font_name, - unsigned face_index, - glyph_rendering ren_type, - const char* font_mem, - const long font_mem_size) - { - bool ret = false; - - if(m_library_initialized) - { - m_last_error = 0; - - int idx = find_face(font_name); - if(idx >= 0) - { - m_cur_face = m_faces[idx]; - m_name = m_face_names[idx]; - } - else - { - if(m_num_faces >= m_max_faces) - { - delete [] m_face_names[0]; - FT_Done_Face(m_faces[0]); - memcpy(m_faces, - m_faces + 1, - (m_max_faces - 1) * sizeof(FT_Face)); - memcpy(m_face_names, - m_face_names + 1, - (m_max_faces - 1) * sizeof(char*)); - m_num_faces = m_max_faces - 1; - } - - if (font_mem && font_mem_size) - { - m_last_error = FT_New_Memory_Face(m_library, - (const FT_Byte*)font_mem, - font_mem_size, - face_index, - &m_faces[m_num_faces]); - } - else - { - m_last_error = FT_New_Face(m_library, - font_name, - face_index, - &m_faces[m_num_faces]); - } - - if(m_last_error == 0) - { - m_face_names[m_num_faces] = new char [strlen(font_name) + 1]; - strcpy(m_face_names[m_num_faces], font_name); - m_cur_face = m_faces[m_num_faces]; - m_name = m_face_names[m_num_faces]; - ++m_num_faces; - } - else - { - m_face_names[m_num_faces] = 0; - m_cur_face = 0; - m_name = 0; - } - } - - - if(m_last_error == 0) - { - ret = true; - - switch(ren_type) - { - case glyph_ren_native_mono: - m_glyph_rendering = glyph_ren_native_mono; - break; - - case glyph_ren_native_gray8: - m_glyph_rendering = glyph_ren_native_gray8; - break; - - case glyph_ren_outline: - if(FT_IS_SCALABLE(m_cur_face)) - { - m_glyph_rendering = glyph_ren_outline; - } - else - { - m_glyph_rendering = glyph_ren_native_gray8; - } - break; - - case glyph_ren_agg_mono: - if(FT_IS_SCALABLE(m_cur_face)) - { - m_glyph_rendering = glyph_ren_agg_mono; - } - else - { - m_glyph_rendering = glyph_ren_native_mono; - } - break; - - case glyph_ren_agg_gray8: - if(FT_IS_SCALABLE(m_cur_face)) - { - m_glyph_rendering = glyph_ren_agg_gray8; - } - else - { - m_glyph_rendering = glyph_ren_native_gray8; - } - break; - } - update_signature(); - } - } - return ret; - } - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::attach(const char* file_name) - { - if(m_cur_face) - { - m_last_error = FT_Attach_File(m_cur_face, file_name); - return m_last_error == 0; - } - return false; - } - - //------------------------------------------------------------------------ - unsigned font_engine_freetype_base::num_faces() const - { - if(m_cur_face) - { - return m_cur_face->num_faces; - } - return 0; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::char_map(FT_Encoding char_map) - { - if(m_cur_face) - { - m_last_error = FT_Select_Charmap(m_cur_face, m_char_map); - if(m_last_error == 0) - { - update_signature(); - return true; - } - } - return false; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::height(double h) - { - m_height = int(h * 64.0); - if(m_cur_face) - { - update_char_size(); - return true; - } - return false; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::width(double w) - { - m_width = int(w * 64.0); - if(m_cur_face) - { - update_char_size(); - return true; - } - return false; - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::hinting(bool h) - { - m_hinting = h; - if(m_cur_face) - { - update_signature(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::flip_y(bool f) - { - m_flip_y = f; - if(m_cur_face) - { - update_signature(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::transform(const trans_affine& affine) - { - m_affine = affine; - if(m_cur_face) - { - update_signature(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_signature() - { - if(m_cur_face && m_name) - { - unsigned name_len = strlen(m_name); - if(name_len > m_name_len) - { - delete [] m_signature; - m_signature = new char [name_len + 32 + 256]; - m_name_len = name_len + 32 - 1; - } - - unsigned gamma_hash = 0; - if(m_glyph_rendering == glyph_ren_native_gray8 || - m_glyph_rendering == glyph_ren_agg_mono || - m_glyph_rendering == glyph_ren_agg_gray8) - { - unsigned char gamma_table[rasterizer_scanline_aa<>::aa_scale]; - unsigned i; - for(i = 0; i < rasterizer_scanline_aa<>::aa_scale; ++i) - { - gamma_table[i] = m_rasterizer.apply_gamma(i); - } - gamma_hash = calc_crc32(gamma_table, sizeof(gamma_table)); - } - - sprintf(m_signature, - "%s,%u,%d,%d,%d:%dx%d,%d,%d,%08X", - m_name, - m_char_map, - m_face_index, - int(m_glyph_rendering), - m_resolution, - m_height, - m_width, - int(m_hinting), - int(m_flip_y), - gamma_hash); - if(m_glyph_rendering == glyph_ren_outline || - m_glyph_rendering == glyph_ren_agg_mono || - m_glyph_rendering == glyph_ren_agg_gray8) - { - double mtx[6]; - char buf[100]; - m_affine.store_to(mtx); - sprintf(buf, ",%08X%08X%08X%08X%08X%08X", - dbl_to_plain_fx(mtx[0]), - dbl_to_plain_fx(mtx[1]), - dbl_to_plain_fx(mtx[2]), - dbl_to_plain_fx(mtx[3]), - dbl_to_plain_fx(mtx[4]), - dbl_to_plain_fx(mtx[5])); - strcat(m_signature, buf); - } - ++m_change_stamp; - } - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_char_size() - { - if(m_cur_face) - { - if(m_resolution) - { - FT_Set_Char_Size(m_cur_face, - m_width, // char_width in 1/64th of points - m_height, // char_height in 1/64th of points - m_resolution, // horizontal device resolution - m_resolution); // vertical device resolution - } - else - { - FT_Set_Pixel_Sizes(m_cur_face, - m_width >> 6, // pixel_width - m_height >> 6); // pixel_height - } - update_signature(); - } - } - - - - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::prepare_glyph(unsigned glyph_code) - { - m_glyph_index = FT_Get_Char_Index(m_cur_face, glyph_code); - // For hinting FT_LOAD_DEFAULT could be used but it gives severe - // visual artefacts when scaling fonts x100 along X like - // done by AGG. - m_last_error = FT_Load_Glyph(m_cur_face, - m_glyph_index, - m_hinting ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_NO_HINTING); - if(m_last_error == 0) - { - switch(m_glyph_rendering) - { - case glyph_ren_native_mono: - m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_MONO); - if(m_last_error == 0) - { - decompose_ft_bitmap_mono(m_cur_face->glyph->bitmap, - m_cur_face->glyph->bitmap_left, - m_flip_y ? -m_cur_face->glyph->bitmap_top : - m_cur_face->glyph->bitmap_top, - m_flip_y, - m_scanline_bin, - m_scanlines_bin); - m_bounds.x1 = m_scanlines_bin.min_x(); - m_bounds.y1 = m_scanlines_bin.min_y(); - m_bounds.x2 = m_scanlines_bin.max_x() + 1; - m_bounds.y2 = m_scanlines_bin.max_y() + 1; - m_data_size = m_scanlines_bin.byte_size(); - m_data_type = glyph_data_mono; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - return true; - } - break; - - - case glyph_ren_native_gray8: - m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_NORMAL); - if(m_last_error == 0) - { - decompose_ft_bitmap_gray8(m_cur_face->glyph->bitmap, - m_cur_face->glyph->bitmap_left, - m_flip_y ? -m_cur_face->glyph->bitmap_top : - m_cur_face->glyph->bitmap_top, - m_flip_y, - m_rasterizer, - m_scanline_aa, - m_scanlines_aa); - m_bounds.x1 = m_scanlines_aa.min_x(); - m_bounds.y1 = m_scanlines_aa.min_y(); - m_bounds.x2 = m_scanlines_aa.max_x() + 1; - m_bounds.y2 = m_scanlines_aa.max_y() + 1; - m_data_size = m_scanlines_aa.byte_size(); - m_data_type = glyph_data_gray8; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - return true; - } - break; - - - case glyph_ren_outline: - if(m_last_error == 0) - { - if(m_flag32) - { - m_path32.remove_all(); - if(decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path32)) - { - rect_d bnd = m_path32.bounding_rect(); - m_data_size = m_path32.byte_size(); - m_data_type = glyph_data_outline; - m_bounds.x1 = int(floor(bnd.x1)); - m_bounds.y1 = int(floor(bnd.y1)); - m_bounds.x2 = int(ceil(bnd.x2)); - m_bounds.y2 = int(ceil(bnd.y2)); - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - } - else - { - m_path16.remove_all(); - if(decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path16)) - { - rect_d bnd = m_path16.bounding_rect(); - m_data_size = m_path16.byte_size(); - m_data_type = glyph_data_outline; - m_bounds.x1 = int(floor(bnd.x1)); - m_bounds.y1 = int(floor(bnd.y1)); - m_bounds.x2 = int(ceil(bnd.x2)); - m_bounds.y2 = int(ceil(bnd.y2)); - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - } - } - return false; - - case glyph_ren_agg_mono: - if(m_last_error == 0) - { - m_rasterizer.reset(); - if(m_flag32) - { - m_path32.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path32); - m_rasterizer.add_path(m_curves32); - } - else - { - m_path16.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path16); - m_rasterizer.add_path(m_curves16); - } - m_scanlines_bin.prepare(); // Remove all - render_scanlines(m_rasterizer, m_scanline_bin, m_scanlines_bin); - m_bounds.x1 = m_scanlines_bin.min_x(); - m_bounds.y1 = m_scanlines_bin.min_y(); - m_bounds.x2 = m_scanlines_bin.max_x() + 1; - m_bounds.y2 = m_scanlines_bin.max_y() + 1; - m_data_size = m_scanlines_bin.byte_size(); - m_data_type = glyph_data_mono; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - return false; - - - case glyph_ren_agg_gray8: - if(m_last_error == 0) - { - m_rasterizer.reset(); - if(m_flag32) - { - m_path32.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path32); - m_rasterizer.add_path(m_curves32); - } - else - { - m_path16.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path16); - m_rasterizer.add_path(m_curves16); - } - m_scanlines_aa.prepare(); // Remove all - render_scanlines(m_rasterizer, m_scanline_aa, m_scanlines_aa); - m_bounds.x1 = m_scanlines_aa.min_x(); - m_bounds.y1 = m_scanlines_aa.min_y(); - m_bounds.x2 = m_scanlines_aa.max_x() + 1; - m_bounds.y2 = m_scanlines_aa.max_y() + 1; - m_data_size = m_scanlines_aa.byte_size(); - m_data_type = glyph_data_gray8; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - return false; - } - } - return false; - } - - - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::write_glyph_to(int8u* data) const - { - if(data && m_data_size) - { - switch(m_data_type) - { - default: return; - case glyph_data_mono: m_scanlines_bin.serialize(data); break; - case glyph_data_gray8: m_scanlines_aa.serialize(data); break; - case glyph_data_outline: - if(m_flag32) - { - m_path32.serialize(data); - } - else - { - m_path16.serialize(data); - } - break; - case glyph_data_invalid: break; - } - } - } - - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::add_kerning(unsigned first, unsigned second, - double* x, double* y) - { - if(m_cur_face && first && second && FT_HAS_KERNING(m_cur_face)) - { - FT_Vector delta; - FT_Get_Kerning(m_cur_face, first, second, - FT_KERNING_DEFAULT, &delta); - double dx = int26p6_to_dbl(delta.x); - double dy = int26p6_to_dbl(delta.y); - if(m_glyph_rendering == glyph_ren_outline || - m_glyph_rendering == glyph_ren_agg_mono || - m_glyph_rendering == glyph_ren_agg_gray8) - { - m_affine.transform_2x2(&dx, &dy); - } - *x += dx; - *y += dy; - - return true; - } - return false; - } - - - -} - - diff --git a/lib/font_renderer/agg_font_freetype.h b/lib/font_renderer/agg_font_freetype.h deleted file mode 100644 index 6d83999f..00000000 --- a/lib/font_renderer/agg_font_freetype.h +++ /dev/null @@ -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 -#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 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 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 m_path16; - path_storage_integer m_path32; - conv_curve > m_curves16; - conv_curve > 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 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 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 diff --git a/lib/font_renderer/agg_lcd_distribution_lut.h b/lib/font_renderer/agg_lcd_distribution_lut.h deleted file mode 100644 index 5e305d70..00000000 --- a/lib/font_renderer/agg_lcd_distribution_lut.h +++ /dev/null @@ -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 - -#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 diff --git a/lib/font_renderer/agg_pixfmt_alpha8.h b/lib/font_renderer/agg_pixfmt_alpha8.h deleted file mode 100644 index f91378fe..00000000 --- a/lib/font_renderer/agg_pixfmt_alpha8.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include -#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; - }; - -} diff --git a/lib/font_renderer/font_renderer.cpp b/lib/font_renderer/font_renderer.cpp deleted file mode 100644 index 14110107..00000000 --- a/lib/font_renderer/font_renderer.cpp +++ /dev/null @@ -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 -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 -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(dst_ren_buf, x, y, glyph_width, color_a, covers); - } else { - blend_solid_hspan_subpixel(dst_ren_buf, lcd_lut, x, y, glyph_width * subpixel_scale, color_a, covers); - } - } -} - diff --git a/lib/font_renderer/font_renderer.h b/lib/font_renderer/font_renderer.h deleted file mode 100644 index 019d2ae7..00000000 --- a/lib/font_renderer/font_renderer.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef FONT_RENDERER_H -#define FONT_RENDERER_H - -#include - -#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 diff --git a/lib/font_renderer/font_renderer_alpha.h b/lib/font_renderer/font_renderer_alpha.h deleted file mode 100644 index a0f45a27..00000000 --- a/lib/font_renderer/font_renderer_alpha.h +++ /dev/null @@ -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 base_ren_type; - typedef agg::renderer_scanline_aa_solid renderer_solid; - typedef agg::font_engine_freetype_int32 font_engine_type; - typedef agg::font_cache_manager 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 m_curves; - agg::conv_transform > 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 - 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; - } -}; diff --git a/lib/font_renderer/meson.build b/lib/font_renderer/meson.build deleted file mode 100644 index d596e152..00000000 --- a/lib/font_renderer/meson.build +++ /dev/null @@ -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, -) - diff --git a/lib/font_renderer/notes-lite-font-rendering.md b/lib/font_renderer/notes-lite-font-rendering.md deleted file mode 100644 index 175da8fc..00000000 --- a/lib/font_renderer/notes-lite-font-rendering.md +++ /dev/null @@ -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; diff --git a/src/api/api.c b/src/api/api.c index c479ca4a..9f74f6b5 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -6,7 +6,6 @@ int luaopen_renderer(lua_State *L); int luaopen_regex(lua_State *L); int luaopen_process(lua_State *L); - static const luaL_Reg libs[] = { { "system", luaopen_system }, { "renderer", luaopen_renderer }, @@ -16,7 +15,6 @@ static const luaL_Reg libs[] = { }; void api_load_libs(lua_State *L) { - for (int i = 0; libs[i].name; i++) { + for (int i = 0; libs[i].name; i++) luaL_requiref(L, libs[i].name, libs[i].func, 1); - } } diff --git a/src/api/api.h b/src/api/api.h index 51ebb9a8..e7bc57ea 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -1,14 +1,15 @@ #ifndef API_H #define API_H -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" +#include +#include +#include #define API_TYPE_FONT "Font" -#define API_TYPE_REPLACE "Replace" #define API_TYPE_PROCESS "Process" +#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key)) + void api_load_libs(lua_State *L); #endif diff --git a/src/api/cp_replace.c b/src/api/cp_replace.c deleted file mode 100644 index a0fb3ac8..00000000 --- a/src/api/cp_replace.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "api.h" -#include "renderer.h" - - -static int f_new(lua_State *L) { - CPReplaceTable *rep_table = lua_newuserdata(L, sizeof(CPReplaceTable)); - luaL_setmetatable(L, API_TYPE_REPLACE); - ren_cp_replace_init(rep_table); - return 1; -} - - -static int f_gc(lua_State *L) { - CPReplaceTable *rep_table = luaL_checkudata(L, 1, API_TYPE_REPLACE); - ren_cp_replace_free(rep_table); - return 0; -} - - -static int f_add(lua_State *L) { - CPReplaceTable *rep_table = luaL_checkudata(L, 1, API_TYPE_REPLACE); - const char *src = luaL_checkstring(L, 2); - const char *dst = luaL_checkstring(L, 3); - ren_cp_replace_add(rep_table, src, dst); - return 0; -} - - -static const luaL_Reg lib[] = { - { "__gc", f_gc }, - { "new", f_new }, - { "add", f_add }, - { NULL, NULL } -}; - -int luaopen_renderer_replacements(lua_State *L) { - luaL_newmetatable(L, API_TYPE_REPLACE); - luaL_setfuncs(L, lib, 0); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - return 1; -} diff --git a/src/api/regex.c b/src/api/regex.c index 1043b1c5..e0f94056 100644 --- a/src/api/regex.c +++ b/src/api/regex.c @@ -101,17 +101,11 @@ int luaopen_regex(lua_State *L) { lua_setfield(L, -2, "__name"); lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, "regex"); - lua_pushnumber(L, PCRE2_ANCHORED); - lua_setfield(L, -2, "ANCHORED"); - lua_pushnumber(L, PCRE2_ANCHORED) ; - lua_setfield(L, -2, "ENDANCHORED"); - lua_pushnumber(L, PCRE2_NOTBOL); - lua_setfield(L, -2, "NOTBOL"); - lua_pushnumber(L, PCRE2_NOTEOL); - lua_setfield(L, -2, "NOTEOL"); - lua_pushnumber(L, PCRE2_NOTEMPTY); - lua_setfield(L, -2, "NOTEMPTY"); - lua_pushnumber(L, PCRE2_NOTEMPTY_ATSTART); - lua_setfield(L, -2, "NOTEMPTY_ATSTART"); + API_CONSTANT_DEFINE(L, -1, "ANCHORED", PCRE2_ANCHORED); + API_CONSTANT_DEFINE(L, -1, "ENDANCHORED", PCRE2_ENDANCHORED); + API_CONSTANT_DEFINE(L, -1, "NOTBOL", PCRE2_NOTBOL); + API_CONSTANT_DEFINE(L, -1, "NOTEOL", PCRE2_NOTEOL); + API_CONSTANT_DEFINE(L, -1, "NOTEMPTY", PCRE2_NOTEMPTY); + API_CONSTANT_DEFINE(L, -1, "NOTEMPTY_ATSTART", PCRE2_NOTEMPTY_ATSTART); return 1; } diff --git a/src/api/renderer.c b/src/api/renderer.c index 8dc13ada..8571dac6 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -2,6 +2,92 @@ #include "renderer.h" #include "rencache.h" +static int f_font_load(lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + float size = luaL_checknumber(L, 2); + unsigned int font_hinting = FONT_HINTING_SLIGHT; + bool subpixel = true; + if (lua_gettop(L) > 2 && lua_istable(L, 3)) { + lua_getfield(L, 3, "antialiasing"); + if (lua_isstring(L, -1)) { + const char *antialiasing = lua_tostring(L, -1); + if (antialiasing) { + if (strcmp(antialiasing, "grayscale") == 0) { + subpixel = false; + } else if (strcmp(antialiasing, "subpixel") == 0) { + subpixel = true; + } else { + return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing); + } + } + } + lua_pop(L, 1); + lua_getfield(L, 3, "hinting"); + if (lua_isstring(L, -1)) { + const char *hinting = lua_tostring(L, -1); + if (hinting) { + if (strcmp(hinting, "slight") == 0) { + font_hinting = FONT_HINTING_SLIGHT; + } else if (strcmp(hinting, "none") == 0) { + font_hinting = FONT_HINTING_NONE; + } else if (strcmp(hinting, "full") == 0) { + font_hinting = FONT_HINTING_FULL; + } else { + return luaL_error(L, "error in renderer.font.load, unknown hinting option: \"%s\"", hinting); + } + } + } + lua_pop(L, 1); + } + RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); + *font = ren_font_load(filename, size, subpixel, font_hinting); + if (!*font) + return luaL_error(L, "failed to load font"); + luaL_setmetatable(L, API_TYPE_FONT); + return 1; +} + +static int f_font_copy(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_get_height(*self); + RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); + *font = ren_font_copy(*self, size); + if (!*font) + return luaL_error(L, "failed to copy font"); + luaL_setmetatable(L, API_TYPE_FONT); + return 1; +} + +static int f_font_set_tab_size(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + int n = luaL_checknumber(L, 2); + ren_font_set_tab_size(*self, n); + return 0; +} + +static int f_font_gc(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + ren_font_free(*self); + return 0; +} + +static int f_font_get_width(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_font_get_width(*self, luaL_checkstring(L, 2))); + return 1; +} + +static int f_font_get_height(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_font_get_height(*self)); + return 1; +} + +static int f_font_get_size(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_font_get_size(*self)); + return 1; +} static RenColor checkcolor(lua_State *L, int idx, int def) { RenColor color; @@ -71,40 +157,17 @@ static int f_draw_rect(lua_State *L) { return 0; } -static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { - FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT); +static int f_draw_text(lua_State *L) { + RenFont** font = luaL_checkudata(L, 1, API_TYPE_FONT); const char *text = luaL_checkstring(L, 2); - /* The coordinate below will be in subpixel iff draw_subpixel is true. - Otherwise it will be in pixels. */ int x_subpixel = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - - CPReplaceTable *rep_table; - RenColor replace_color; - if (lua_gettop(L) >= 7) { - rep_table = luaL_checkudata(L, 6, API_TYPE_REPLACE); - replace_color = checkcolor(L, 7, 255); - } else { - rep_table = NULL; - replace_color = (RenColor) {0}; - } - - x_subpixel = rencache_draw_text(L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); + x_subpixel = rencache_draw_text(L, *font, text, x_subpixel, y, color); lua_pushnumber(L, x_subpixel); return 1; } -static int f_draw_text(lua_State *L) { - return draw_text_subpixel_impl(L, false); -} - - -static int f_draw_text_subpixel(lua_State *L) { - return draw_text_subpixel_impl(L, true); -} - - static const luaL_Reg lib[] = { { "show_debug", f_show_debug }, { "get_size", f_get_size }, @@ -113,19 +176,26 @@ static const luaL_Reg lib[] = { { "set_clip_rect", f_set_clip_rect }, { "draw_rect", f_draw_rect }, { "draw_text", f_draw_text }, - { "draw_text_subpixel", f_draw_text_subpixel }, { NULL, NULL } }; - -int luaopen_renderer_font(lua_State *L); -int luaopen_renderer_replacements(lua_State *L); +static const luaL_Reg fontLib[] = { + { "__gc", f_font_gc }, + { "load", f_font_load }, + { "copy", f_font_copy }, + { "set_tab_size", f_font_set_tab_size }, + { "get_width", f_font_get_width }, + { "get_height", f_font_get_height }, + { "get_size", f_font_get_size }, + { NULL, NULL } +}; int luaopen_renderer(lua_State *L) { luaL_newlib(L, lib); - luaopen_renderer_font(L); + luaL_newmetatable(L, API_TYPE_FONT); + luaL_setfuncs(L, fontLib, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "font"); - luaopen_renderer_replacements(L); - lua_setfield(L, -2, "replacements"); return 1; } diff --git a/src/api/renderer_font.c b/src/api/renderer_font.c deleted file mode 100644 index 7c6587ea..00000000 --- a/src/api/renderer_font.c +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include - -#include "api.h" -#include "fontdesc.h" -#include "renderer.h" -#include "rencache.h" - -static int f_load(lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - float size = luaL_checknumber(L, 2); - unsigned int font_options = 0; - if (lua_gettop(L) > 2 && lua_istable(L, 3)) { - lua_getfield(L, 3, "antialiasing"); - if (lua_isstring(L, -1)) { - const char *antialiasing = lua_tostring(L, -1); - if (antialiasing) { - if (strcmp(antialiasing, "grayscale") == 0) { - font_options |= RenFontGrayscale; - } else if (strcmp(antialiasing, "subpixel") == 0) { - font_options |= RenFontSubpixel; - } else { - return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing); - } - } - } - lua_pop(L, 1); - - lua_getfield(L, 3, "hinting"); - if (lua_isstring(L, -1)) { - const char *hinting = lua_tostring(L, -1); - if (hinting) { - if (strcmp(hinting, "slight") == 0) { - font_options |= RenFontHintingSlight; - } else if (strcmp(hinting, "none") == 0) { - font_options |= RenFontHintingNone; - } else if (strcmp(hinting, "full") == 0) { - font_options |= RenFontHintingFull; - } else { - return luaL_error(L, "error in renderer.font.load, unknown hinting option: \"%s\"", hinting); - } - } - } - lua_pop(L, 1); - } - - if (ren_verify_font(filename)) { - luaL_error(L, "failed to load font"); - } - - FontDesc *font_desc = lua_newuserdata(L, font_desc_alloc_size(filename)); - font_desc_init(font_desc, filename, size, font_options); - luaL_setmetatable(L, API_TYPE_FONT); - return 1; -} - - -static int f_copy(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - float size; - if (lua_gettop(L) >= 2) { - size = luaL_checknumber(L, 2); - } else { - size = self->size; - } - FontDesc *new_font_desc = lua_newuserdata(L, font_desc_alloc_size(self->filename)); - font_desc_init(new_font_desc, self->filename, size, self->options); - luaL_setmetatable(L, API_TYPE_FONT); - return 1; -} - - -static int f_set_tab_size(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - int n = luaL_checknumber(L, 2); - font_desc_set_tab_size(self, n); - return 0; -} - - -static int f_gc(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - font_desc_clear(self); - return 0; -} - -static int f_get_width(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - const char *text = luaL_checkstring(L, 2); - /* By calling ren_get_font_width with NULL as third arguments - we will obtain the width in points. */ - int w = ren_get_font_width(self, text, NULL); - lua_pushnumber(L, w); - return 1; -} - - -static int f_subpixel_scale(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_get_font_subpixel_scale(self)); - return 1; -} - -static int f_get_width_subpixel(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - const char *text = luaL_checkstring(L, 2); - int subpixel_scale; - /* We need to pass a non-null subpixel_scale pointer to force - subpixel width calculation. */ - lua_pushnumber(L, ren_get_font_width(self, text, &subpixel_scale)); - return 1; -} - - -static int f_get_height(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_get_font_height(self) ); - return 1; -} - - -static int f_get_size(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, self->size); - return 1; -} - - -static int f_set_size(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - float new_size = luaL_checknumber(L, 2); - font_desc_clear(self); - self->size = new_size; - return 0; -} - - -static const luaL_Reg lib[] = { - { "__gc", f_gc }, - { "load", f_load }, - { "copy", f_copy }, - { "set_tab_size", f_set_tab_size }, - { "get_width", f_get_width }, - { "get_width_subpixel", f_get_width_subpixel }, - { "get_height", f_get_height }, - { "subpixel_scale", f_subpixel_scale }, - { "get_size", f_get_size }, - { "set_size", f_set_size }, - { NULL, NULL } -}; - -int luaopen_renderer_font(lua_State *L) { - luaL_newmetatable(L, API_TYPE_FONT); - luaL_setfuncs(L, lib, 0); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - return 1; -} diff --git a/src/fontdesc.c b/src/fontdesc.c deleted file mode 100644 index 44460a6d..00000000 --- a/src/fontdesc.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include - -#include "fontdesc.h" -#include "renderer.h" - - -int font_desc_alloc_size(const char *filename) { - return offsetof(FontDesc, filename) + strlen(filename) + 1; -} - -void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options) { - memcpy(font_desc->filename, filename, strlen(filename) + 1); - font_desc->size = size; - font_desc->options = font_options; - font_desc->tab_size = 4; - font_desc->cache_length = 0; - font_desc->cache_last_index = 0; /* Normally no need to initialize. */ -} - -void font_desc_clear(FontDesc *font_desc) { - for (int i = 0; i < font_desc->cache_length; i++) { - ren_free_font(font_desc->cache[i].font); - } - font_desc->cache_length = 0; - font_desc->cache_last_index = 0; -} - -void font_desc_set_tab_size(FontDesc *font_desc, int tab_size) { - font_desc->tab_size = tab_size; - for (int i = 0; i < font_desc->cache_length; i++) { - ren_set_font_tab_size(font_desc->cache[i].font, tab_size); - } -} - -int font_desc_get_tab_size(FontDesc *font_desc) { - return font_desc->tab_size; -} - -static void load_scaled_font(FontDesc *font_desc, int index, int scale) { - RenFont *font = ren_load_font(font_desc->filename, scale * font_desc->size, font_desc->options); - if (!font) { - /* The font was able to load when initially loaded using renderer.load.font. - If now is no longer available we just abort the application. */ - fprintf(stderr, "Fatal error: unable to load font %s. Application will abort.\n", - font_desc->filename); - exit(1); - } - font_desc->cache[index].font = font; - font_desc->cache[index].scale = scale; -} - -RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale) { - int index = -1; - for (int i = 0; i < font_desc->cache_length; i++) { - if (font_desc->cache[i].scale == scale) { - index = i; - break; - } - } - if (index < 0) { - index = font_desc->cache_length; - if (index < FONT_CACHE_ARRAY_MAX) { - load_scaled_font(font_desc, index, scale); - font_desc->cache_length = index + 1; - } else { - // FIXME: should not print into the stderr or stdout. - fprintf(stderr, "Warning: max array of font scale reached.\n"); - index = (font_desc->cache_last_index == 0 ? 1 : 0); - ren_free_font(font_desc->cache[index].font); - load_scaled_font(font_desc, index, scale); - } - } - font_desc->cache_last_index = index; - return font_desc->cache[index].font; -} - diff --git a/src/fontdesc.h b/src/fontdesc.h deleted file mode 100644 index bf591801..00000000 --- a/src/fontdesc.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef FONT_DESC_H -#define FONT_DESC_H - -typedef struct RenFont RenFont; - -struct FontInstance { - RenFont *font; - short int scale; -}; -typedef struct FontInstance FontInstance; - -#define FONT_CACHE_ARRAY_MAX 2 - -struct FontDesc { - float size; - unsigned int options; - short int tab_size; - FontInstance cache[FONT_CACHE_ARRAY_MAX]; - short int cache_length; - short int cache_last_index; /* More recently used instance. */ - char filename[0]; -}; -typedef struct FontDesc FontDesc; - -void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options); -int font_desc_alloc_size(const char *filename); -int font_desc_get_tab_size(FontDesc *font_desc); -void font_desc_set_tab_size(FontDesc *font_desc, int tab_size); -void font_desc_clear(FontDesc *font_desc); -RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale); - -#endif - diff --git a/src/rencache.c b/src/rencache.c index 31165e90..7bbb9df6 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -16,32 +16,18 @@ #define COMMAND_BUF_SIZE (1024 * 512) #define COMMAND_BARE_SIZE offsetof(Command, text) -enum { SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL }; +enum { SET_CLIP, DRAW_TEXT, DRAW_RECT }; typedef struct { int8_t type; int8_t tab_size; - int8_t subpixel_scale; - int8_t x_subpixel_offset; int32_t size; RenRect rect; RenColor color; - FontDesc *font_desc; - CPReplaceTable *replacements; - RenColor replace_color; + RenFont *font; char text[0]; } Command; -#define FONT_REFS_MAX 12 -struct FontRef { - FontDesc *font_desc; - int index; -}; -typedef struct FontRef FontRef; -FontRef font_refs[FONT_REFS_MAX]; -int font_refs_len = 0; - - static unsigned cells_buf1[CELLS_X * CELLS_Y]; static unsigned cells_buf2[CELLS_X * CELLS_Y]; static unsigned *cells_prev = cells_buf1; @@ -52,36 +38,9 @@ static int command_buf_idx; static RenRect screen_rect; static bool show_debug; - static inline int min(int a, int b) { return a < b ? a : b; } static inline int max(int a, int b) { return a > b ? a : b; } -static int font_refs_add(lua_State *L, FontDesc *font_desc, int index) { - for (int i = 0; i < font_refs_len; i++) { - if (font_refs[i].font_desc == font_desc) { - return font_refs[i].index; - } - } - - if (font_refs_len >= FONT_REFS_MAX) { - fprintf(stderr, "Warning: (" __FILE__ "): exhausted font reference buffer\n"); - return LUA_NOREF; - } - - lua_pushvalue(L, index); - int ref = luaL_ref(L, LUA_REGISTRYINDEX); - - font_refs[font_refs_len++] = (FontRef) { font_desc, ref }; - return ref; -} - - -static void font_refs_clear(lua_State *L) { - for (int i = 0; i < font_refs_len; i++) { - luaL_unref(L, LUA_REGISTRYINDEX, font_refs[i].index); - } - font_refs_len = 0; -} /* 32bit fnv-1a hash */ #define HASH_INITIAL 2166136261 @@ -168,35 +127,21 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } -int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, - const char *text, int x, int y, RenColor color, bool draw_subpixel, - CPReplaceTable *replacements, RenColor replace_color) +int rencache_draw_text(lua_State *L, RenFont *font, const char *text, int x, int y, RenColor color) { - int subpixel_scale; - int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale); - RenRect rect; - rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x); - rect.y = y; - rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); - rect.height = ren_get_font_height(font_desc); - - if (rects_overlap(screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) { + RenRect rect = { x, y, ren_font_get_width(font, text), ren_font_get_height(font) }; + if (rects_overlap(screen_rect, rect)) { int sz = strlen(text) + 1; - Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz); + Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz); if (cmd) { memcpy(cmd->text, text, sz); cmd->color = color; - cmd->font_desc = font_desc; + cmd->font = font; cmd->rect = rect; - cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1); - cmd->x_subpixel_offset = x - subpixel_scale * rect.x; - cmd->tab_size = font_desc_get_tab_size(font_desc); - cmd->replacements = replacements; - cmd->replace_color = replace_color; + cmd->tab_size = ren_font_get_tab_size(font); } } - - return x + (draw_subpixel ? w_subpixel : rect.width); + return x + rect.width; } @@ -214,7 +159,6 @@ void rencache_begin_frame(lua_State *L) { screen_rect.height = h; rencache_invalidate(); } - font_refs_clear(L); } @@ -301,15 +245,8 @@ void rencache_end_frame(lua_State *L) { ren_draw_rect(cmd->rect, cmd->color); break; case DRAW_TEXT: - font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); - ren_draw_text(cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color, - cmd->replacements, cmd->replace_color); - break; - case DRAW_TEXT_SUBPIXEL: - font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); - ren_draw_text_subpixel(cmd->font_desc, cmd->text, - cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color, - cmd->replacements, cmd->replace_color); + ren_font_set_tab_size(cmd->font, cmd->tab_size); + ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); break; } } diff --git a/src/rencache.h b/src/rencache.h index 1d0f45a6..7a7f80f5 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -8,8 +8,8 @@ void rencache_show_debug(bool enable); void rencache_set_clip_rect(RenRect rect); void rencache_draw_rect(RenRect rect, RenColor color); -int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, - bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); +int rencache_draw_text(lua_State *L, RenFont *font, + const char *text, int x, int y, RenColor color); void rencache_invalidate(void); void rencache_begin_frame(lua_State *L); void rencache_end_frame(lua_State *L); diff --git a/src/renderer.c b/src/renderer.c index cd269687..93394a9c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -2,37 +2,21 @@ #include #include #include -#include "font_renderer.h" +#include +#include +#include +#include FT_FREETYPE_H + #include "renderer.h" #include "renwindow.h" +#define DIVIDE_BY_255_SIGNED(x, sign_val) (((x) + (sign_val) + ((x)>>8)) >> 8) +#define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1) #define MAX_GLYPHSET 256 -#define REPLACEMENT_CHUNK_SIZE 8 - -struct RenImage { - RenColor *pixels; - int width, height; -}; - -struct GlyphSet { - FR_Bitmap *image; - FR_Bitmap_Glyph_Metrics glyphs[256]; -}; -typedef struct GlyphSet GlyphSet; - -/* The field "padding" below must be there just before GlyphSet *sets[MAX_GLYPHSET] - because the field "sets" can be indexed and writted with an index -1. For this - reason the "padding" field must be there but is never explicitly used. */ -struct RenFont { - GlyphSet *padding; - GlyphSet *sets[MAX_GLYPHSET]; - float size; - int height; - int space_advance; - FR_Renderer *renderer; -}; +#define SUBPIXEL_BITMAPS_CACHED 3 static RenWindow window_renderer = {0}; +static FT_Library library; static void* check_alloc(void *ptr) { if (!ptr) { @@ -42,6 +26,28 @@ static void* check_alloc(void *ptr) { return ptr; } +/************************* Fonts *************************/ + +typedef struct { + unsigned short x0, x1, y0, y1; + short bitmap_left, bitmap_top; + float xadvance; +} GlyphMetric; + +typedef struct { + SDL_Surface* surface[SUBPIXEL_BITMAPS_CACHED]; + GlyphMetric metrics[256]; +} GlyphSet; + +typedef struct RenFont { + FT_Face face; + GlyphSet* sets[MAX_GLYPHSET]; + float size; + short max_height, space_advance, tab_advance; + bool subpixel; + ERenFontHinting hinting; + char path[0]; +} RenFont; static const char* utf8_to_codepoint(const char *p, unsigned *dst) { unsigned res, n; @@ -59,213 +65,206 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) { return p + 1; } - -void ren_cp_replace_init(CPReplaceTable *rep_table) { - rep_table->size = 0; - rep_table->replacements = NULL; +static int font_set_load_options(RenFont* font) { + switch (font->hinting) { + case FONT_HINTING_SLIGHT: return FT_LOAD_TARGET_LIGHT | FT_LOAD_FORCE_AUTOHINT; + case FONT_HINTING_FULL: return FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT; + case FONT_HINTING_NONE: return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + } + return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; } - -void ren_cp_replace_free(CPReplaceTable *rep_table) { - free(rep_table->replacements); -} - - -void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst) { - int table_size = rep_table->size; - if (table_size % REPLACEMENT_CHUNK_SIZE == 0) { - CPReplace *old_replacements = rep_table->replacements; - const int new_size = (table_size / REPLACEMENT_CHUNK_SIZE + 1) * REPLACEMENT_CHUNK_SIZE; - rep_table->replacements = malloc(new_size * sizeof(CPReplace)); - if (!rep_table->replacements) { - rep_table->replacements = old_replacements; - return; +static int font_set_render_options(RenFont* font) { + if (font->subpixel) { + switch (font->hinting) { + case FONT_HINTING_NONE: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE); break; + case FONT_HINTING_SLIGHT: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_LIGHT); break; + case FONT_HINTING_FULL: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_DEFAULT); break; + } + return FT_RENDER_MODE_LCD; + } else { + switch (font->hinting) { + case FONT_HINTING_NONE: return FT_RENDER_MODE_NORMAL; break; + case FONT_HINTING_SLIGHT: return FT_RENDER_MODE_LIGHT; break; + case FONT_HINTING_FULL: return FT_RENDER_MODE_LIGHT; break; } - memcpy(rep_table->replacements, old_replacements, table_size * sizeof(CPReplace)); - free(old_replacements); } - CPReplace *rep = &rep_table->replacements[table_size]; - utf8_to_codepoint(src, &rep->codepoint_src); - utf8_to_codepoint(dst, &rep->codepoint_dst); - rep_table->size = table_size + 1; -} - -void ren_free_window_resources() { - renwin_free(&window_renderer); -} - -void ren_init(SDL_Window *win) { - assert(win); - window_renderer.window = win; - renwin_init_surface(&window_renderer); - renwin_clip_to_surface(&window_renderer); -} - - -void ren_resize_window() { - renwin_resize_surface(&window_renderer); -} - - -void ren_update_rects(RenRect *rects, int count) { - static bool initial_frame = true; - if (initial_frame) { - renwin_show_window(&window_renderer); - initial_frame = false; - } - renwin_update_rects(&window_renderer, rects, count); -} - - -void ren_set_clip_rect(RenRect rect) { - renwin_set_clip_rect(&window_renderer, rect); -} - - -void ren_get_size(int *x, int *y) { - RenWindow *ren = &window_renderer; - const int scale = renwin_surface_scale(ren); - SDL_Surface *surface = renwin_get_surface(ren); - *x = surface->w / scale; - *y = surface->h / scale; -} - - -RenImage* ren_new_image(int width, int height) { - assert(width > 0 && height > 0); - RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); - check_alloc(image); - image->pixels = (void*) (image + 1); - image->width = width; - image->height = height; - return image; -} - -void ren_free_image(RenImage *image) { - free(image); -} - -static GlyphSet* load_glyphset(RenFont *font, int idx) { - GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); - - set->image = FR_Bake_Font_Bitmap(font->renderer, font->height, idx << 8, 256, set->glyphs); - check_alloc(set->image); - - return set; -} - - -static GlyphSet* get_glyphset(RenFont *font, int codepoint) { - int idx = (codepoint >> 8) % MAX_GLYPHSET; - if (!font->sets[idx]) { - font->sets[idx] = load_glyphset(font, idx); - } - return font->sets[idx]; -} - - -int ren_verify_font(const char *filename) { - RenFont font[1]; - font->renderer = FR_Renderer_New(0); - if (FR_Load_Font(font->renderer, filename)) { - return 1; - } - FR_Renderer_Free(font->renderer); return 0; } - -RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags) { - RenFont *font = NULL; - - /* init font */ - font = check_alloc(calloc(1, sizeof(RenFont))); - font->size = size; - - unsigned int fr_renderer_flags = 0; - if ((renderer_flags & RenFontAntialiasingMask) == RenFontSubpixel) { - fr_renderer_flags |= FR_SUBPIXEL; +static GlyphSet* font_load_glyphset(RenFont* font, int idx) { + GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); + unsigned int render_option = font_set_render_options(font), load_option = font_set_load_options(font); + int pen_x = 0, bitmaps_cached = font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1; + unsigned int byte_width = font->subpixel ? 3 : 1; + for (int i = 0; i < 256; ++i) { + int glyph_index = FT_Get_Char_Index( font->face, i + idx * MAX_GLYPHSET); + if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY) || FT_Render_Glyph(font->face->glyph, render_option)) + continue; + FT_GlyphSlot slot = font->face->glyph; + int glyph_width = slot->bitmap.width / byte_width; + set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; + pen_x += glyph_width; + font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; + // xadvance for some reason should be computed like this. + if (FT_Load_Glyph(font->face, glyph_index, FT_LOAD_BITMAP_METRICS_ONLY) || FT_Render_Glyph(font->face->glyph, render_option)) + continue; + set->metrics[i].xadvance = (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f; + } - if ((renderer_flags & RenFontHintingMask) == RenFontHintingSlight) { - fr_renderer_flags |= (FR_HINTING | FR_PRESCALE_X); - } else if ((renderer_flags & RenFontHintingMask) == RenFontHintingFull) { - fr_renderer_flags |= FR_HINTING; - } - font->renderer = FR_Renderer_New(fr_renderer_flags); - if (FR_Load_Font(font->renderer, filename)) { - free(font); - return NULL; - } - font->height = FR_Get_Font_Height(font->renderer, size); - - FR_Bitmap_Glyph_Metrics *gs = get_glyphset(font, ' ')->glyphs; - font->space_advance = gs[' '].xadvance; - - /* make tab and newline glyphs invisible */ - FR_Bitmap_Glyph_Metrics *g = get_glyphset(font, '\n')->glyphs; - g['\t'].x1 = g['\t'].x0; - g['\n'].x1 = g['\n'].x0; - - return font; -} - - -void ren_free_font(RenFont *font) { - for (int i = 0; i < MAX_GLYPHSET; i++) { - GlyphSet *set = font->sets[i]; - if (set) { - FR_Bitmap_Free(set->image); - free(set); + for (int j = 0; j < bitmaps_cached; ++j) { + set->surface[j] = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0)); + unsigned char* pixels = set->surface[j]->pixels; + for (int i = 0; i < 256; ++i) { + int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET); + if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option)) + continue; + FT_GlyphSlot slot = font->face->glyph; + FT_Outline_Translate(&slot->outline, (64 / bitmaps_cached) * j, 0 ); + if (FT_Render_Glyph(slot, render_option)) + continue; + for (int line = 0; line < slot->bitmap.rows; ++line) { + int target_offset = set->surface[j]->pitch * line + set->metrics[i].x0 * byte_width; + int source_offset = line * slot->bitmap.pitch; + memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width); + } } } - FR_Renderer_Free(font->renderer); + return set; +} + +static void font_free_glyphset(GlyphSet* set) { + for (int i = 0; i < SUBPIXEL_BITMAPS_CACHED; ++i) { + if (set->surface[i]) + SDL_FreeSurface(set->surface[i]); + } + free(set); +} + +static GlyphSet* font_get_glyphset(RenFont* font, int codepoint) { + int idx = (codepoint >> 8) % MAX_GLYPHSET; + if (!font->sets[idx]) + font->sets[idx] = font_load_glyphset(font, idx); + return font->sets[idx]; +} + +RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting) { + FT_Face face; + if (FT_New_Face( library, path, 0, &face)) + return NULL; + if (FT_Set_Pixel_Sizes(face, 0, (int)size)) + goto failure; + int len = strlen(path); + RenFont* font = check_alloc(calloc(1, sizeof(RenFont) + len + 1)); + strcpy(font->path, path); + font->face = face; + font->size = size; + font->subpixel = subpixel; + font->hinting = hinting; + GlyphSet* set = font_get_glyphset(font, ' '); + font->space_advance = (int)set->metrics[' '].xadvance; + font->tab_advance = font->space_advance * 2; + return font; + failure: + FT_Done_Face(face); + return NULL; +} + +RenFont* ren_font_copy(RenFont* font, float size) { + return ren_font_load(font->path, size, font->subpixel, font->hinting); +} + +void ren_font_free(RenFont* font) { + for (int i = 0; i < MAX_GLYPHSET; ++i) { + if (font->sets[i]) + font_free_glyphset(font->sets[i]); + } + FT_Done_Face(font->face); free(font); } - -void ren_set_font_tab_size(RenFont *font, int n) { - GlyphSet *set = get_glyphset(font, '\t'); - set->glyphs['\t'].xadvance = font->space_advance * n; +void ren_font_set_tab_size(RenFont *font, int n) { + font_get_glyphset(font, '\t')->metrics['\t'].xadvance = font->space_advance * n; } - -int ren_get_font_tab_size(RenFont *font) { - GlyphSet *set = get_glyphset(font, '\t'); - return set->glyphs['\t'].xadvance / font->space_advance; +int ren_font_get_tab_size(RenFont *font) { + return font_get_glyphset(font, '\t')->metrics['\t'].xadvance / font->space_advance; } - -/* Important: if subpixel_scale is NULL we will return width in points. Otherwise we will - return width in subpixels. */ -int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale) { - int x = 0; - const char *p = text; - unsigned codepoint; - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - while (*p) { - p = utf8_to_codepoint(p, &codepoint); - GlyphSet *set = get_glyphset(font, codepoint); - FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; - x += g->xadvance; +int ren_font_get_width(RenFont *font, const char *text) { + int width = 0; + const char* end = text + strlen(text); + while (text < end) { + unsigned int codepoint; + text = utf8_to_codepoint(text, &codepoint); + width += font_get_glyphset(font, codepoint)->metrics[codepoint % 256].xadvance; } - /* At this point here x is in subpixel units */ - const int x_scale_to_points = FR_Subpixel_Scale(font->renderer) * surface_scale; - if (subpixel_scale) { - *subpixel_scale = x_scale_to_points; - return x; + return width; +} + +float ren_font_get_size(RenFont *font) { + return font->size; +} +int ren_font_get_height(RenFont *font) { + return font->size + 3; +} + +int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) { + SDL_Surface *surface = renwin_get_surface(&window_renderer); + const RenRect clip = window_renderer.clip; + float pen_x = x; + int bytes_per_pixel = surface->format->BytesPerPixel; + const char* end = text + strlen(text); + unsigned char* destination_pixels = surface->pixels; + int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height; + while (text < end) { + unsigned int codepoint, r, g, b; + text = utf8_to_codepoint(text, &codepoint); + GlyphSet* set = font_get_glyphset(font, codepoint); + GlyphMetric* metric = &set->metrics[codepoint % 256]; + int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; + unsigned char* source_pixels = set->surface[bitmap_index]->pixels; + int start_x = pen_x + metric->bitmap_left, endX = metric->x1 - metric->x0 + pen_x; + int glyph_end = metric->x1, glyphStart = metric->x0; + if (color.a > 0 && endX >= clip.x && start_x <= clip_end_x) { + for (int line = metric->y0; line < metric->y1; ++line) { + int targetY = line + y - metric->y0 - metric->bitmap_top + font->size; + if (targetY < clip.y) + continue; + if (targetY >= clip_end_y) + break; + unsigned int* destination_pixel = (unsigned int*)&destination_pixels[surface->pitch * targetY + start_x * bytes_per_pixel]; + unsigned char* source_pixel = &source_pixels[line * set->surface[bitmap_index]->pitch + metric->x0 * (font->subpixel ? 3 : 1)]; + for (int x = glyphStart; x < glyph_end; ++x) { + unsigned int destination_color = *destination_pixel; + SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; + if (font->subpixel) { + SDL_Color src = { *source_pixel++, *source_pixel++, *source_pixel++ }; + r = color.r * src.r + dst.r * (255 - src.r) + 127; + r = DIVIDE_BY_255(r); + g = color.g * src.g + dst.g * (255 - src.g) + 127; + g = DIVIDE_BY_255(g); + b = color.b * src.b + dst.b * (255 - src.b) + 127; + b = DIVIDE_BY_255(b); + } else { + unsigned char intensity = *source_pixel++; + r = color.r * intensity + dst.r * (255 - intensity) + 127; + r = DIVIDE_BY_255(r); + g = color.g * intensity + dst.g * (255 - intensity) + 127; + g = DIVIDE_BY_255(g); + b = color.b * intensity + dst.b * (255 - intensity) + 127; + b = DIVIDE_BY_255(b); + } + *destination_pixel++ = dst.a << 24 | r << 16 | g << 8 | b; + } + } + } + pen_x += metric->xadvance ? metric->xadvance : font->space_advance; } - return (x + x_scale_to_points / 2) / x_scale_to_points; + return pen_x; } - -int ren_get_font_height(FontDesc *font_desc) { - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - return (font->height + surface_scale / 2) / surface_scale; -} - - +/******************* Rectangles **********************/ static inline RenColor blend_pixel(RenColor dst, RenColor src) { int ia = 0xff - src.a; dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8; @@ -316,88 +315,49 @@ void ren_draw_rect(RenRect rect, RenColor color) { } } +/*************** Window Management ****************/ +void ren_free_window_resources() { + renwin_free(&window_renderer); +} -static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { - for (int i = 0; i < rep_table->size; i++) { - const CPReplace *rep = &rep_table->replacements[i]; - if (*codepoint == rep->codepoint_src) { - *codepoint = rep->codepoint_dst; - return 1; - } +void ren_init(SDL_Window *win) { + assert(win); + int error = FT_Init_FreeType( &library ); + if ( error ) { + fprintf(stderr, "internal font error when starting the application\n"); + return; } - return 0; + window_renderer.window = win; + renwin_init_surface(&window_renderer); + renwin_clip_to_surface(&window_renderer); } -static FR_Clip_Area clip_area_from_rect(const RenRect r) { - return (FR_Clip_Area) {r.x, r.y, r.x + r.width, r.y + r.height}; +void ren_resize_window() { + renwin_resize_surface(&window_renderer); } -static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color, - CPReplaceTable *replacements, RenColor replace_color) -{ - SDL_Surface *surf = renwin_get_surface(&window_renderer); - FR_Clip_Area clip = clip_area_from_rect(window_renderer.clip); - const char *p = text; - unsigned codepoint; - const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; - while (*p) { - FR_Color color_rep; - p = utf8_to_codepoint(p, &codepoint); - GlyphSet *set = get_glyphset(font, codepoint); - FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; - const int xadvance_original_cp = g->xadvance; - const int replaced = replacements ? codepoint_replace(replacements, &codepoint) : 0; - if (replaced) { - set = get_glyphset(font, codepoint); - g = &set->glyphs[codepoint & 0xff]; - color_rep = (FR_Color) { .r = replace_color.r, .g = replace_color.g, .b = replace_color.b}; - } else { - color_rep = color_fr; - } - if (color.a != 0) { - FR_Blend_Glyph(font->renderer, &clip, - x_subpixel, y_pixel, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep); - } - x_subpixel += xadvance_original_cp; +void ren_update_rects(RenRect *rects, int count) { + static bool initial_frame = true; + if (initial_frame) { + renwin_show_window(&window_renderer); + initial_frame = false; } + renwin_update_rects(&window_renderer, rects, count); } -void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, - CPReplaceTable *replacements, RenColor replace_color) -{ - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - draw_text_impl(font, text, x_subpixel, surface_scale * y, color, replacements, replace_color); -} - -void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, - CPReplaceTable *replacements, RenColor replace_color) -{ - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer); - draw_text_impl(font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color); -} - -// Could be declared as static inline -int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) { - int w_mult; - if (orientation < 0) { - w_mult = width; - } else if (orientation == 0) { - w_mult = width + subpixel_scale / 2; - } else { - w_mult = width + subpixel_scale - 1; - } - return w_mult / subpixel_scale; +void ren_set_clip_rect(RenRect rect) { + renwin_set_clip_rect(&window_renderer, rect); } -int ren_get_font_subpixel_scale(FontDesc *font_desc) { - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - return FR_Subpixel_Scale(font->renderer) * surface_scale; +void ren_get_size(int *x, int *y) { + RenWindow *ren = &window_renderer; + const int scale = renwin_surface_scale(ren); + SDL_Surface *surface = renwin_get_surface(ren); + *x = surface->w / scale; + *y = surface->h / scale; } + diff --git a/src/renderer.h b/src/renderer.h index bab059bb..dfc21c06 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -3,37 +3,24 @@ #include #include -#include "fontdesc.h" - -typedef struct RenImage RenImage; - -enum { - RenFontAntialiasingMask = 1, - RenFontGrayscale = 1, - RenFontSubpixel = 0, - - RenFontHintingMask = 3 << 1, - RenFontHintingSlight = 0 << 1, - RenFontHintingNone = 1 << 1, - RenFontHintingFull = 2 << 1, -}; +#include +typedef struct RenFont RenFont; +typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting; typedef struct { uint8_t b, g, r, a; } RenColor; typedef struct { int x, y, width, height; } RenRect; -struct CPReplace { - unsigned codepoint_src; - unsigned codepoint_dst; -}; -typedef struct CPReplace CPReplace; - - -struct CPReplaceTable { - int size; - CPReplace *replacements; -}; -typedef struct CPReplaceTable CPReplaceTable; +RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting); +RenFont* ren_font_copy(RenFont* font, float size); +void ren_font_free(RenFont *font); +void ren_font_set_tab_size(RenFont *font, int n); +int ren_font_get_tab_size(RenFont *font); +int ren_font_get_width(RenFont *font, const char *text); +int ren_font_get_height(RenFont *font); +float ren_font_get_size(RenFont *font); +int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color); +void ren_draw_rect(RenRect rect, RenColor color); void ren_init(SDL_Window *win); void ren_resize_window(); @@ -42,27 +29,5 @@ void ren_set_clip_rect(RenRect rect); void ren_get_size(int *x, int *y); /* Reports the size in points. */ void ren_free_window_resources(); -RenImage* ren_new_image(int width, int height); -void ren_free_image(RenImage *image); - -RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags); -int ren_verify_font(const char *filename); -void ren_free_font(RenFont *font); -void ren_set_font_tab_size(RenFont *font, int n); -int ren_get_font_tab_size(RenFont *font); - -int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale); -int ren_get_font_height(FontDesc *font_desc); -int ren_get_font_subpixel_scale(FontDesc *font_desc); -int ren_font_subpixel_round(int width, int subpixel_scale, int orientation); - -void ren_draw_rect(RenRect rect, RenColor color); -void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); -void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); - -void ren_cp_replace_init(CPReplaceTable *rep_table); -void ren_cp_replace_free(CPReplaceTable *rep_table); -void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst); -void ren_cp_replace_clear(CPReplaceTable *rep_table); #endif