From 23a080ca45ec3bd69e5677d520898155c13074fb Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 28 May 2020 12:15:24 +0200 Subject: [PATCH] WIP: compiles but completely broken AGG files imported from AGG repository, sdl-testing branch. the pixfmt_bgra32_lcd is just implemented and is not tested. --- meson.build | 4 +- notes.txt | 34 ++ src/agg_font_freetype.cpp | 1149 +++++++++++++++++++++++++++++++++++ src/agg_font_freetype.h | 196 ++++++ src/agg_pixfmt_bgra32_lcd.h | 274 +++++++++ src/agg_pixfmt_rgb24_lcd.h | 238 ++++++++ src/font_render_capi.cpp | 26 + src/font_render_capi.h | 26 + src/font_render_lcd.h | 166 +++++ src/meson.build | 4 +- src/renderer.c | 12 + 11 files changed, 2127 insertions(+), 2 deletions(-) create mode 100644 notes.txt create mode 100644 src/agg_font_freetype.cpp create mode 100644 src/agg_font_freetype.h create mode 100644 src/agg_pixfmt_bgra32_lcd.h create mode 100644 src/agg_pixfmt_rgb24_lcd.h create mode 100644 src/font_render_capi.cpp create mode 100644 src/font_render_capi.h create mode 100644 src/font_render_lcd.h diff --git a/meson.build b/meson.build index edc69d70..eba00f44 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('lite', 'c', version: '1.05', default_options : ['c_std=gnu11']) +project('lite', 'c', 'cpp', version: '1.05', default_options : ['c_std=gnu11', 'cpp_std=c++03']) cc = meson.get_compiler('c') libm = cc.find_library('m', required : false) @@ -6,6 +6,8 @@ libdl = cc.find_library('dl', required : false) lua_dep = dependency('lua') sdl_dep = dependency('sdl2', method: 'config-tool') +libagg_dep = dependency('libagg') +freetype_dep = dependency('freetype2') stb_truetype_dep = dependency('stb_truetype') lite_include = include_directories('src') diff --git a/notes.txt b/notes.txt new file mode 100644 index 00000000..e5bf6bea --- /dev/null +++ b/notes.txt @@ -0,0 +1,34 @@ +stbtt_InitFont + +stbtt_ScaleForMappingEmToPixels x 3 +stbtt_ScaleForPixelHeight +stbtt_BakeFontBitmap +stbtt_GetFontVMetrics x 2 + +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; +}; + +typedef struct { + unsigned short x0, y0, x1, y1; // coordinates of bbox in bitmap + float xoff, yoff, xadvance; +} stbtt_bakedchar; + +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. diff --git a/src/agg_font_freetype.cpp b/src/agg_font_freetype.cpp new file mode 100644 index 00000000..dc45f807 --- /dev/null +++ b/src/agg_font_freetype.cpp @@ -0,0 +1,1149 @@ +//---------------------------------------------------------------------------- +// 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; + 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; + 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; + } + + + //------------------------------------------------------------------------ + 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/src/agg_font_freetype.h b/src/agg_font_freetype.h new file mode 100644 index 00000000..273243c3 --- /dev/null +++ b/src/agg_font_freetype.h @@ -0,0 +1,196 @@ +//---------------------------------------------------------------------------- +// 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; + 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/src/agg_pixfmt_bgra32_lcd.h b/src/agg_pixfmt_bgra32_lcd.h new file mode 100644 index 00000000..83304fdf --- /dev/null +++ b/src/agg_pixfmt_bgra32_lcd.h @@ -0,0 +1,274 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_BGRA32_LCD_INCLUDED +#define AGG_PIXFMT_BGRA32_LCD_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + + //=====================================================lcd_distribution_lut + class lcd_distribution_lut + { + public: + lcd_distribution_lut(double prim, double second, double tert) + { + double norm = 1.0 / (prim + second*2 + tert*2); + prim *= norm; + second *= norm; + tert *= norm; + for(unsigned i = 0; i < 256; i++) + { + unsigned b = (i << 8); + unsigned s = round(second * b); + unsigned t = round(tert * b); + unsigned p = b - (2*s + 2*t); + + m_data[3*i + 1] = s; /* secondary */ + m_data[3*i + 2] = t; /* tertiary */ + m_data[3*i ] = p; /* primary */ + } + } + + unsigned convolution(const int8u* covers, int i0, int i_min, int i_max) const + { + unsigned sum = 0; + int k_min = (i0 >= i_min + 2 ? -2 : i_min - i0); + int k_max = (i0 <= i_max - 2 ? 2 : i_max - i0); + for (int k = k_min; k <= k_max; k++) + { + /* select the primary, secondary or tertiary channel */ + int channel = abs(k) % 3; + int8u c = covers[i0 + k]; + sum += m_data[3*c + channel]; + } + + return (sum + 128) >> 8; + } + + private: + unsigned short m_data[256*3]; + }; + + + //========================================================pixfmt_bgra32_lcd + class pixfmt_bgra32_lcd + { + public: + typedef rgba8 color_type; + typedef rendering_buffer::row_data row_data; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + pixfmt_bgra32_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut) + : m_rbuf(&rb), m_lut(&lut) + { + } + + //-------------------------------------------------------------------- + unsigned width() const { + return m_rbuf->width() * 3; + } + 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) + { + int8u* p = m_rbuf->row_ptr(y) + (x / 3) * 4; + for (int ilen = len; ilen > 0; p += 4, ilen -= 3) + { + p[0] = c.b; + p[1] = c.g; + p[2] = c.r; + p[3] = 0xff; + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + unsigned rowlen = width(); + int cx = (x - 2 >= 0 ? -2 : -x); + int cx_max = (len + 2 <= rowlen ? len + 1 : rowlen - 1); + + int i = (x + cx) % 3; + + const int8u rgb[3] = { c.r, c.g, c.b }; + int8u* p = m_rbuf->row_ptr(y) + (x + cx); + + const int pixel_index[3] = {2, 1, 0}; + for (/* */; cx <= cx_max; cx += 3) + { + unsigned c_conv, alpha, dst_col, src_col; + c_conv = m_lut->convolution(covers, cx, 0, len - 1); + alpha = (c_conv + 1) * (c.a + 1); + dst_col = rgb[i]; + src_col = *(p + pixel_index[i]); + *(p + pixel_index[i]) = (int8u)((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + i = (i + 1) % 3; + + c_conv = m_lut->convolution(covers, cx + 1, 0, len - 1); + alpha = (c_conv + 1) * (c.a + 1); + dst_col = rgb[i]; + src_col = *(p + pixel_index[i]); + *(p + pixel_index[i]) = (int8u)((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + i = (i + 1) % 3; + + c_conv = m_lut->convolution(covers, cx + 2, 0, len - 1); + alpha = (c_conv + 1) * (c.a + 1); + dst_col = rgb[i]; + src_col = *(p + pixel_index[i]); + *(p + pixel_index[i]) = (int8u)((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + i = (i + 1) % 3; + + p[3] = 0xff; + p += 4; + } + } + + private: + rendering_buffer* m_rbuf; + const lcd_distribution_lut* m_lut; + }; + + + template + class pixfmt_bgra32_lcd_gamma + { + public: + typedef rgba8 color_type; + typedef rendering_buffer::row_data row_data; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + pixfmt_bgra32_lcd_gamma(rendering_buffer& rb, const lcd_distribution_lut& lut, const Gamma& gamma) + : m_rbuf(&rb), m_lut(&lut), m_gamma(gamma) + { + } + + //-------------------------------------------------------------------- + unsigned width() const { + return m_rbuf->width() * 3; + } + 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) + { + int8u* p = m_rbuf->row_ptr(y) + (x / 3) * 4; + for (int ilen = len; ilen > 0; p += 4, ilen -= 3) + { + p[0] = c.r; + p[1] = c.g; + p[2] = c.b; + p[3] = 0xff; + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + unsigned rowlen = width(); + int cx = (x - 2 >= 0 ? -2 : -x); + int cx_max = (len + 2 <= rowlen ? len + 1 : rowlen - 1); + + int i = (x + cx) % 3; + + const int8u rgb[3] = { c.r, c.g, c.b }; + int8u* p = m_rbuf->row_ptr(y) + (x + cx); + + const int pixel_index[3] = {2, 1, 0}; + for (/* */; cx <= cx_max; cx += 3) + { + unsigned c_conv, alpha, dst_col, src_col; + c_conv = m_lut->convolution(covers, cx, 0, len - 1); + alpha = (c_conv + 1) * (c.a + 1); + dst_col = m_gamma.dir(rgb[i]); + src_col = m_gamma.dir(*(p + pixel_index[i])); + *(p + pixel_index[i]) = m_gamma.inv((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + i = (i + 1) % 3; + + c_conv = m_lut->convolution(covers, cx + 1, 0, len - 1); + alpha = (c_conv + 1) * (c.a + 1); + dst_col = m_gamma.dir(rgb[i]); + src_col = m_gamma.dir(*(p + pixel_index[i])); + *(p + pixel_index[i]) = m_gamma.inv((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + i = (i + 1) % 3; + + c_conv = m_lut->convolution(covers, cx + 2, 0, len - 1); + alpha = (c_conv + 1) * (c.a + 1); + dst_col = m_gamma.dir(rgb[i]); + src_col = m_gamma.dir(*(p + pixel_index[i])); + *(p + pixel_index[i]) = m_gamma.inv((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + i = (i + 1) % 3; + + p[3] = 0xff; + p += 4; + } + } + + private: + rendering_buffer* m_rbuf; + const lcd_distribution_lut* m_lut; + const Gamma& m_gamma; + }; + +} + +#endif + diff --git a/src/agg_pixfmt_rgb24_lcd.h b/src/agg_pixfmt_rgb24_lcd.h new file mode 100644 index 00000000..0ce36645 --- /dev/null +++ b/src/agg_pixfmt_rgb24_lcd.h @@ -0,0 +1,238 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_RGB24_LCD_INCLUDED +#define AGG_PIXFMT_RGB24_LCD_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + + //=====================================================lcd_distribution_lut + class lcd_distribution_lut + { + public: + lcd_distribution_lut(double prim, double second, double tert) + { + double norm = 1.0 / (prim + second*2 + tert*2); + prim *= norm; + second *= norm; + tert *= norm; + for(unsigned i = 0; i < 256; i++) + { + unsigned b = (i << 8); + unsigned s = round(second * b); + unsigned t = round(tert * b); + unsigned p = b - (2*s + 2*t); + + m_data[3*i + 1] = s; /* secondary */ + m_data[3*i + 2] = t; /* tertiary */ + m_data[3*i ] = p; /* primary */ + } + } + + unsigned convolution(const int8u* covers, int i0, int i_min, int i_max) const + { + unsigned sum = 0; + int k_min = (i0 >= i_min + 2 ? -2 : i_min - i0); + int k_max = (i0 <= i_max - 2 ? 2 : i_max - i0); + for (int k = k_min; k <= k_max; k++) + { + /* select the primary, secondary or tertiary channel */ + int channel = abs(k) % 3; + int8u c = covers[i0 + k]; + sum += m_data[3*c + channel]; + } + + return (sum + 128) >> 8; + } + + private: + unsigned short m_data[256*3]; + }; + + + //========================================================pixfmt_rgb24_lcd + class pixfmt_rgb24_lcd + { + public: + typedef rgba8 color_type; + typedef rendering_buffer::row_data row_data; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + pixfmt_rgb24_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut) + : m_rbuf(&rb), m_lut(&lut) + { + } + + //-------------------------------------------------------------------- + unsigned width() const { + return m_rbuf->width() * 3; + } + 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) + { + int xr = x - (x % 3); + int8u* p = m_rbuf->row_ptr(y) + xr; + for (int ilen = len; ilen > 0; p += 3, ilen -= 3) + { + p[0] = c.r; + p[1] = c.g; + p[2] = c.b; + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + unsigned rowlen = width(); + int cx = (x - 2 >= 0 ? -2 : -x); + int cx_max = (len + 2 <= rowlen ? len + 1 : rowlen - 1); + + int i = (x + cx) % 3; + + int8u rgb[3] = { c.r, c.g, c.b }; + int8u* p = m_rbuf->row_ptr(y) + (x + cx); + + for (/* */; cx <= cx_max; cx++) + { + unsigned c_conv = m_lut->convolution(covers, cx, 0, len - 1); + unsigned alpha = (c_conv + 1) * (c.a + 1); + unsigned dst_col = rgb[i], src_col = (*p); + *p = (int8u)((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + + p ++; + i = (i + 1) % 3; + } + } + + private: + rendering_buffer* m_rbuf; + const lcd_distribution_lut* m_lut; + }; + + + template + class pixfmt_rgb24_lcd_gamma + { + public: + typedef rgba8 color_type; + typedef rendering_buffer::row_data row_data; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + pixfmt_rgb24_lcd_gamma(rendering_buffer& rb, const lcd_distribution_lut& lut, const Gamma& gamma) + : m_rbuf(&rb), m_lut(&lut), m_gamma(gamma) + { + } + + //-------------------------------------------------------------------- + unsigned width() const { + return m_rbuf->width() * 3; + } + 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) + { + int xr = x - (x % 3); + int8u* p = m_rbuf->row_ptr(y) + xr; + for (int ilen = len; ilen > 0; p += 3, ilen -= 3) + { + p[0] = c.r; + p[1] = c.g; + p[2] = c.b; + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + unsigned rowlen = width(); + int cx = (x - 2 >= 0 ? -2 : -x); + int cx_max = (len + 2 <= rowlen ? len + 1 : rowlen - 1); + + int i = (x + cx) % 3; + + int8u rgb[3] = { c.r, c.g, c.b }; + int8u* p = m_rbuf->row_ptr(y) + (x + cx); + + for (/* */; cx <= cx_max; cx++) + { + unsigned c_conv = m_lut->convolution(covers, cx, 0, len - 1); + unsigned alpha = (c_conv + 1) * (c.a + 1); + unsigned dst_col = m_gamma.dir(rgb[i]), src_col = m_gamma.dir(*p); + *p = m_gamma.inv((((dst_col - src_col) * alpha) + (src_col << 16)) >> 16); + + p ++; + i = (i + 1) % 3; + } + } + + private: + rendering_buffer* m_rbuf; + const lcd_distribution_lut* m_lut; + const Gamma& m_gamma; + }; + +} + +#endif + diff --git a/src/font_render_capi.cpp b/src/font_render_capi.cpp new file mode 100644 index 00000000..4b2f3131 --- /dev/null +++ b/src/font_render_capi.cpp @@ -0,0 +1,26 @@ +#include "font_render_capi.h" + +#include "font_render_lcd.h" + +FontRenderer *FontRendererNew(unsigned int flags, float gamma) { + bool hinting = ((flags&FONT_RENDERER_HINTING) != 0); + bool kerning = ((flags&FONT_RENDERER_KERNING) != 0); + bool subpixel = ((flags&FONT_RENDERER_SUBPIXEL) != 0); + font_renderer_lcd *font_renderer = new font_renderer_lcd(hinting, kerning, subpixel, (double) gamma); + return (FontRenderer *) font_renderer; +} + +int FontRendererLoadFont(FontRenderer *fr_, const char *filename) { + font_renderer_lcd *font_renderer = (font_renderer_lcd *) fr_; + bool success = font_renderer->load_font(filename); + return (success ? 0 : 1); +} + +int FontRendererDrawText(FontRenderer *fr_, RenColor *pixels, int width, int height, int pitch, const char *text, float text_size, int x, int y, RenColor color) { + // FIXME: it works below as long as font_renderer_lcd use a BGRA32 pixel format + font_renderer_lcd *font_renderer = (font_renderer_lcd *) fr_; + agg::rendering_buffer ren_buf((agg::int8u *) pixels, width, height, -pitch); + const agg::rgba8 agg_color(color.r, color.g, color.b); + double new_x = font_renderer->render_text(ren_buf, (double) text_size, agg_color, (double) x, (double) y, text); + return int(new_x); +} diff --git a/src/font_render_capi.h b/src/font_render_capi.h new file mode 100644 index 00000000..4e531278 --- /dev/null +++ b/src/font_render_capi.h @@ -0,0 +1,26 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "renderer.h" + +struct FontRenderer_; +typedef struct FontRenderer_ FontRenderer; + +enum { + FONT_RENDERER_HINTING = 1 << 0, + FONT_RENDERER_KERNING = 1 << 1, + FONT_RENDERER_SUBPIXEL = 1 << 2, + + FONT_RENDERER_DEFAULT = FONT_RENDERER_HINTING | FONT_RENDERER_KERNING | FONT_RENDERER_SUBPIXEL, +}; + +FontRenderer *FontRendererNew(unsigned int flags, float gamma); +int FontRendererLoadFont(FontRenderer *, const char *filename); +int FontRendererDrawText(FontRenderer *fr_, RenColor *pixels, int width, int height, int pitch, const char *text, float text_size, int x, int y, RenColor color); + +#ifdef __cplusplus +} +#endif diff --git a/src/font_render_lcd.h b/src/font_render_lcd.h new file mode 100644 index 00000000..03a6e600 --- /dev/null +++ b/src/font_render_lcd.h @@ -0,0 +1,166 @@ +#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_rgb.h" +#include "agg_pixfmt_rgba.h" +// #include "agg_pixfmt_rgb24_lcd.h" +#include "agg_pixfmt_bgra32_lcd.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" + +// FIXME: the pixel type rgb24 of rgba32 is hard coded below +class font_renderer_lcd +{ + typedef agg::pixfmt_bgra32 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; + agg::gamma_lut<> m_gamma_lut; + + // Font rendering options. + bool m_hinting; + bool m_kerning; + bool m_grayscale; + double m_gamma; + + 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: + font_renderer_lcd(bool hinting, bool kerning, bool subpixel, double gamma): + m_feng(), + m_fman(m_feng), + m_hinting(hinting), + m_kerning(kerning), + m_grayscale(!subpixel), + m_gamma(gamma), + m_font_loaded(false), + m_curves(m_fman.path_adaptor()), + m_trans(m_curves, m_mtx) + { + m_gamma_lut.gamma(m_gamma); + } + + bool load_font(const char *font_filename) { + if(m_feng.load_font(font_filename, 0, agg::glyph_ren_outline)) { + m_font_loaded = true; + } + return m_font_loaded; + } + + template + double draw_text(Rasterizer& ras, Scanline& sl, + RenSolid& ren_solid, const agg::rgba8 color, + const char* text, + double x, double y, double height, + unsigned subpixel_scale) + { + const double scale_x = 100; + + m_feng.height(height); + m_feng.width(height * scale_x * subpixel_scale); + m_feng.hinting(m_hinting); + + const char* p = text; + + // FIXME: check we need to multiply x by scale_x. + x *= scale_x * subpixel_scale; + double start_x = x; + + while(*p) + { + if(*p == '\n') + { + x = start_x; + y -= height * 1.25; + ++p; + continue; + } + + const agg::glyph_cache* glyph = m_fman.glyph(*p); + if(glyph) + { + if(m_kerning) + { + m_fman.add_kerning(&x, &y); + } + + m_fman.init_embedded_adaptors(glyph, 0, 0); + if(glyph->data_type == agg::glyph_data_outline) + { + double ty = m_hinting ? floor(y + 0.5) : y; + ras.reset(); + m_mtx.reset(); + m_mtx *= agg::trans_affine_scaling(1.0 / scale_x, 1); + m_mtx *= agg::trans_affine_translation(start_x + x/scale_x, ty); + ras.add_path(m_trans); + ren_solid.color(color); + agg::render_scanlines(ras, sl, ren_solid); + } + + // increment pen position + x += glyph->advance_x; + y += glyph->advance_y; + } + ++p; + } + return x / (subpixel_scale * scale_x); + } + + void clear(agg::rendering_buffer& ren_buf, const agg::rgba8 color) { + agg::pixfmt_bgra32 pf(ren_buf); + base_ren_type ren_base(pf); + ren_base.clear(color); + } + + double render_text(agg::rendering_buffer& ren_buf, + const double text_size, + const agg::rgba8 text_color, + double x, double y, + const char *text) + { + if (!m_font_loaded) { + return y; + } + + agg::scanline_u8 sl; + agg::rasterizer_scanline_aa<> ras; + ras.clip_box(0, 0, ren_buf.width()*3, ren_buf.height()); + + //-------------------------------------- + if(m_grayscale) + { + agg::pixfmt_bgra32 pf(ren_buf); + base_ren_type ren_base(pf); + renderer_solid ren_solid(ren_base); + y = draw_text(ras, sl, ren_solid, text_color, text, x, y, text_size, 1); + } + else + { + // Conventional LUT values: (1./3., 2./9., 1./9.) + // The values below are fine tuned as in the Elementary Plot library. + // The primary weight should be 0.448 but is left adjustable. + agg::lcd_distribution_lut lut(0.448, 0.184, 0.092); + typedef agg::pixfmt_bgra32_lcd_gamma > pixfmt_lcd_type; + pixfmt_lcd_type pf_lcd(ren_buf, lut, m_gamma_lut); + agg::renderer_base ren_base_lcd(pf_lcd); + agg::renderer_scanline_aa_solid > ren_solid_lcd(ren_base_lcd); + y = draw_text(ras, sl, ren_solid_lcd, text_color, text, x, y, text_size, 3); + } + return y; + } +}; diff --git a/src/meson.build b/src/meson.build index ec6988a7..26a56b88 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,6 +4,8 @@ lite_sources = [ 'api/renderer_font.c', 'api/system.c', 'renderer.c', + 'agg_font_freetype.cpp', + 'font_render_capi.cpp', 'rencache.c', 'main.c', ] @@ -11,7 +13,7 @@ lite_sources = [ executable('lite', lite_sources, include_directories: lite_include, - dependencies: [lua_dep, sdl_dep, stb_truetype_dep, libm, libdl], + dependencies: [lua_dep, sdl_dep, stb_truetype_dep, libagg_dep, freetype_dep, libm, libdl], install: true, gui_app: true, ) diff --git a/src/renderer.c b/src/renderer.c index 0495bcbe..8c8cc7db 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -4,6 +4,7 @@ #include #include "stb_truetype.h" #include "renderer.h" +#include "font_render_capi.h" #define MAX_GLYPHSET 256 @@ -23,6 +24,7 @@ struct RenFont { GlyphSet *sets[MAX_GLYPHSET]; float size; int height; + FontRenderer *renderer; }; @@ -193,6 +195,10 @@ RenFont* ren_load_font(const char *filename, float size) { g['\t'].x1 = g['\t'].x0; g['\n'].x1 = g['\n'].x0; + font->renderer = FontRendererNew(FONT_RENDERER_HINTING|FONT_RENDERER_SUBPIXEL, 1.8); + // FIXME check for errors + FontRendererLoadFont(font->renderer, filename); + return font; fail: @@ -328,6 +334,11 @@ void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { + SDL_Surface *surf = SDL_GetWindowSurface(window); + // FIXME: pixels are arranged in bgra order while AGG::fontrender use rgb + RenColor *pixels = (RenColor*) surf->pixels; + return FontRendererDrawText(font->renderer, pixels, surf->w, surf->h, surf->pitch, text, font->height, x, y, color); +#if 0 RenRect rect; const char *p = text; unsigned codepoint; @@ -343,4 +354,5 @@ int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) x += g->xadvance; } return x; +#endif }