diff options
author | Klaus Schmidinger <vdr@tvdr.de> | 2007-06-10 13:02:43 +0200 |
---|---|---|
committer | Klaus Schmidinger <vdr@tvdr.de> | 2007-06-10 13:02:43 +0200 |
commit | c6f8a149574f4e5196f802439e7439406ca82e71 (patch) | |
tree | 0b04b012f87d7033abb37aeb986385672b85da18 /font.c | |
parent | 32dd727d057a1ba22d403f48306adae10285ac77 (diff) | |
download | vdr-c6f8a149574f4e5196f802439e7439406ca82e71.tar.gz vdr-c6f8a149574f4e5196f802439e7439406ca82e71.tar.bz2 |
Freetype font support; full UTF-8 support; dropped pixel fonts
Diffstat (limited to 'font.c')
-rw-r--r-- | font.c | 352 |
1 files changed, 265 insertions, 87 deletions
@@ -4,125 +4,296 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: font.c 1.14 2007/03/11 09:51:44 kls Exp $ + * $Id: font.c 1.15 2007/06/09 14:41:27 kls Exp $ */ -#include "config.h" -#include <ctype.h> #include "font.h" +#include <ctype.h> +#include <ft2build.h> +#include FT_FREETYPE_H +#include "config.h" +#include "osd.h" #include "tools.h" -#include "fontfix-iso8859-1.c" -#include "fontosd-iso8859-1.c" -#include "fontsml-iso8859-1.c" - -#include "fontfix-iso8859-2.c" -#include "fontosd-iso8859-2.c" -#include "fontsml-iso8859-2.c" +// --- cFreetypeFont --------------------------------------------------------- -#include "fontfix-iso8859-5.c" -#include "fontosd-iso8859-5.c" -#include "fontsml-iso8859-5.c" +#define KERNING_UNKNOWN (-10000) -#include "fontfix-iso8859-7.c" -#include "fontosd-iso8859-7.c" -#include "fontsml-iso8859-7.c" +struct tKerning { + uint prevSym; + int kerning; + tKerning(uint PrevSym, int Kerning) { prevSym = PrevSym; kerning = Kerning; } + }; -#include "fontfix-iso8859-9.c" -#include "fontosd-iso8859-9.c" -#include "fontsml-iso8859-9.c" +class cGlyph : public cListObject { +private: + uint charCode; + uchar *bitmap; + int advanceX; + int advanceY; + int left; ///< The bitmap's left bearing expressed in integer pixels. + int top; ///< The bitmap's top bearing expressed in integer pixels. + int width; ///< The number of pixels per bitmap row. + int rows; ///< The number of bitmap rows. + int pitch; ///< The pitch's absolute value is the number of bytes taken by one bitmap row, including padding. + cVector<tKerning> kerningCache; +public: + cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData); + virtual ~cGlyph(); + uint CharCode(void) const { return charCode; } + uchar *Bitmap(void) const { return bitmap; } + int AdvanceX(void) const { return advanceX; } + int AdvanceY(void) const { return advanceY; } + int Left(void) const { return left; } + int Top(void) const { return top; } + int Width(void) const { return width; } + int Rows(void) const { return rows; } + int Pitch(void) const { return pitch; } + int GetKerningCache(uint PrevSym) const; + void SetKerningCache(uint PrevSym, int Kerning); + }; -#include "fontfix-iso8859-13.c" -#include "fontosd-iso8859-13.c" -#include "fontsml-iso8859-13.c" +cGlyph::cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData) +{ + charCode = CharCode; + advanceX = GlyphData->advance.x >> 6; + advanceY = GlyphData->advance.y >> 6; + left = GlyphData->bitmap_left; + top = GlyphData->bitmap_top; + width = GlyphData->bitmap.width; + rows = GlyphData->bitmap.rows; + pitch = GlyphData->bitmap.pitch; + bitmap = MALLOC(uchar, rows * pitch); + memcpy(bitmap, GlyphData->bitmap.buffer, rows * pitch); +} -#include "fontfix-iso8859-15.c" -#include "fontosd-iso8859-15.c" -#include "fontsml-iso8859-15.c" +cGlyph::~cGlyph() +{ + free(bitmap); +} -// --- cFont ----------------------------------------------------------------- +int cGlyph::GetKerningCache(uint PrevSym) const +{ + for (int i = kerningCache.Size(); --i > 0; ) { + if (kerningCache[i].prevSym == PrevSym) + return kerningCache[i].kerning; + } + return KERNING_UNKNOWN; +} -static const void *const FontData[eDvbCodeSize][eDvbFontSize] = { - { FontOsd_iso8859_1, FontFix_iso8859_1, FontSml_iso8859_1 }, - { FontOsd_iso8859_2, FontFix_iso8859_2, FontSml_iso8859_2 }, - { FontOsd_iso8859_5, FontFix_iso8859_5, FontSml_iso8859_5 }, - { FontOsd_iso8859_7, FontFix_iso8859_7, FontSml_iso8859_7 }, - { FontOsd_iso8859_9, FontFix_iso8859_9, FontSml_iso8859_9 }, - { FontOsd_iso8859_13, FontFix_iso8859_13, FontSml_iso8859_13 }, - { FontOsd_iso8859_15, FontFix_iso8859_15, FontSml_iso8859_15 }, - }; +void cGlyph::SetKerningCache(uint PrevSym, int Kerning) +{ + kerningCache.Append(tKerning(PrevSym, Kerning)); +} -static const char *FontCode[eDvbCodeSize] = { - "iso8859-1", - "iso8859-2", - "iso8859-5", - "iso8859-7", - "iso8859-9", - "iso8859-13", - "iso8859-15", +class cFreetypeFont : public cFont { +private: + int height; + int bottom; + FT_Library library; ///< Handle to library + FT_Face face; ///< Handle to face object + mutable cList<cGlyph> glyphCacheMonochrome; + mutable cList<cGlyph> glyphCacheAntiAliased; + int Bottom(void) const { return bottom; } + int Kerning(cGlyph *Glyph, uint PrevSym) const; + cGlyph* Glyph(uint CharCode, bool AntiAliased = false) const; +public: + cFreetypeFont(const char *Name, int CharHeight); + virtual ~cFreetypeFont(); + virtual int Width(uint c) const; + virtual int Width(const char *s) const; + virtual int Height(void) const { return height; } + virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const; }; -eDvbCode cFont::code = code_iso8859_1; -cFont *cFont::fonts[eDvbFontSize] = { NULL }; +cFreetypeFont::cFreetypeFont(const char *Name, int CharHeight) +{ + height = 0; + bottom = 0; + int error = FT_Init_FreeType(&library); + if (!error) { + error = FT_New_Face(library, Name, 0, &face); + if (!error) { + if (face->num_fixed_sizes && face->available_sizes) { // fixed font + // TODO what exactly does all this mean? + height = face->available_sizes->height; + for (uint sym ='A'; sym < 'z'; sym++) { // search for descender for fixed font FIXME + FT_UInt glyph_index = FT_Get_Char_Index(face, sym); + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (!error) { + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (!error) { + if (face->glyph->bitmap.rows-face->glyph->bitmap_top > bottom) + bottom = face->glyph->bitmap.rows-face->glyph->bitmap_top; + } + else + esyslog("ERROR: FreeType: error %d in FT_Render_Glyph", error); + } + else + esyslog("ERROR: FreeType: error %d in FT_Load_Glyph", error); + } + } + else { + error = FT_Set_Char_Size(face, // handle to face object + 0, // char_width in 1/64th of points + CharHeight * 64, // CharHeight in 1/64th of points + 0, // horizontal device resolution + 0); // vertical device resolution + if (!error) { + height = ((face->size->metrics.ascender-face->size->metrics.descender) + 63) / 64; + bottom = abs((face->size->metrics.descender - 63) / 64); + } + else + esyslog("ERROR: FreeType: error %d during FT_Set_Char_Size (font = %s)\n", error, Name); + } + } + else + esyslog("ERROR: FreeType: load error %d (font = %s)", error, Name); + } + else + esyslog("ERROR: FreeType: initialization error %d (font = %s)", error, Name); +} -cFont::cFont(const void *Data) +cFreetypeFont::~cFreetypeFont() { - SetData(Data); + FT_Done_Face(face); + FT_Done_FreeType(library); } -void cFont::SetData(const void *Data) +int cFreetypeFont::Kerning(cGlyph *Glyph, uint PrevSym) const { - if (Data) { - height = ((tCharData *)Data)->height; - for (int i = 0; i < NUMCHARS; i++) - data[i] = (tCharData *)&((tPixelData *)Data)[(i < 32 ? 0 : i - 32) * (height + 2)]; + int kerning = 0; + if (Glyph && PrevSym) { + kerning = Glyph->GetKerningCache(PrevSym); + if (kerning == KERNING_UNKNOWN) { + FT_Vector delta; + FT_UInt glyph_index = FT_Get_Char_Index(face, Glyph->CharCode()); + FT_UInt glyph_index_prev = FT_Get_Char_Index(face, PrevSym); + FT_Get_Kerning(face, glyph_index_prev, glyph_index, FT_KERNING_DEFAULT, &delta); + kerning = delta.x / 64; + Glyph->SetKerningCache(PrevSym, kerning); + } } - else - height = 0; + return kerning; } -int cFont::Width(const char *s) const +cGlyph* cFreetypeFont::Glyph(uint CharCode, bool AntiAliased) const { - int w = 0; - while (s && *s) - w += Width(*s++); - return w; + // Lookup in cache: + cList<cGlyph> *glyphCache = AntiAliased ? &glyphCacheAntiAliased : &glyphCacheMonochrome; + for (cGlyph *g = glyphCache->First(); g; g = glyphCache->Next(g)) { + if (g->CharCode() == CharCode) + return g; + } + + FT_UInt glyph_index = FT_Get_Char_Index(face, CharCode); + + // Load glyph image into the slot (erase previous one): + int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (error) + esyslog("ERROR: FreeType: error during FT_Load_Glyph"); + else { +#if ((FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 7) || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 2 && FREETYPE_PATCH <= 1))// TODO workaround for bug? which one? + if (AntiAliased || CharCode == 32) +#else + if (AntiAliased) +#endif + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + else + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); + if (error) + esyslog("ERROR: FreeType: error during FT_Render_Glyph %d, %d\n", CharCode, glyph_index); + else { //new bitmap + cGlyph *Glyph = new cGlyph(CharCode, face->glyph); + glyphCache->Add(Glyph); + return Glyph; + } + } + return NULL; } -int cFont::Height(const char *s) const +int cFreetypeFont::Width(uint c) const { - int h = 0; - if (s && *s) - h = height; // all characters have the same height! - return h; + cGlyph *g = Glyph(c, Setup.AntiAlias); + return g ? g->AdvanceX() : 0; } -bool cFont::SetCode(const char *Code) +int cFreetypeFont::Width(const char *s) const { - for (int i = 0; i < eDvbCodeSize; i++) { - if (strcmp(Code, FontCode[i]) == 0) { - SetCode(eDvbCode(i)); - return true; - } - } - return false; + int w = 0; + if (s) { + uint prevSym = 0; + while (*s) { + int sl = Utf8CharLen(s); + uint sym = Utf8CharGet(s, sl); + s += sl; + cGlyph *g = Glyph(sym, Setup.AntiAlias); + if (g) + w += g->AdvanceX() + Kerning(g, prevSym); + prevSym = sym; + } + } + return w; } -void cFont::SetCode(eDvbCode Code) +void cFreetypeFont::DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const { - if (code != Code) { - code = Code; - for (int i = 0; i < eDvbFontSize; i++) { - if (fonts[i]) - fonts[i]->SetData(FontData[code][i]); - } + if (s && height) { // checking height to make sure we actually have a valid font + bool AntiAliased = Setup.AntiAlias && Bitmap->Bpp() >= 8; + tIndex fg = Bitmap->Index(ColorFg); + uint prevSym = 0; + while (*s) { + int sl = Utf8CharLen(s); + uint sym = Utf8CharGet(s, sl); + s += sl; + cGlyph *g = Glyph(sym, AntiAliased); + int kerning = Kerning(g, prevSym); + prevSym = sym; + uchar *buffer = g->Bitmap(); + int symWidth = g->Width(); + if (Width && x + symWidth + g->Left() + kerning - 1 > Width) + break; // we don't draw partial characters + if (x + symWidth + g->Left() + kerning > 0) { + for (int row = 0; row < g->Rows(); row++) { + for (int pitch = 0; pitch < g->Pitch(); pitch++) { + uchar bt = *(buffer + (row * g->Pitch() + pitch)); + if (AntiAliased) { + if (bt > 0x00) { + int px = x + pitch + g->Left() + kerning; + int py = y + row + (height - Bottom() - g->Top()); + if (bt == 0xFF) + Bitmap->SetIndex(px, py, fg); + else { + tColor bg = (ColorBg != clrTransparent) ? ColorBg : Bitmap->GetColor(px, py); + Bitmap->SetIndex(px, py, Bitmap->Index(Bitmap->Blend(ColorFg, bg, bt))); + } + } + } + else { //monochrome rendering + for (int col = 0; col < 8 && col + pitch * 8 <= symWidth; col++) { + if (bt & 0x80) + Bitmap->SetIndex(x + col + pitch * 8 + g->Left() + kerning, y + row + (height - Bottom() - g->Top()), fg); + bt <<= 1; + } + } + } + } + } + x += g->AdvanceX() + kerning; + if (x > Bitmap->Width() - 1) + break; + } } } -void cFont::SetFont(eDvbFont Font, const void *Data) +// --- cFont ----------------------------------------------------------------- + +cFont *cFont::fonts[eDvbFontSize] = { NULL }; + +void cFont::SetFont(eDvbFont Font, const char *Name, int CharHeight) { delete fonts[Font]; - fonts[Font] = new cFont(Data ? Data : FontData[code][Font]); + fonts[Font] = new cFreetypeFont(*Name == '/' ? Name : *AddDirectory(FONTDIR, Name), CharHeight); } const cFont *cFont::GetFont(eDvbFont Font) @@ -131,8 +302,13 @@ const cFont *cFont::GetFont(eDvbFont Font) Font = fontOsd; else if (Setup.UseSmallFont == 2) Font = fontSml; - if (!fonts[Font]) - SetFont(Font); + if (!fonts[Font]) { + switch (Font) { + case fontOsd: SetFont(Font, AddDirectory(FONTDIR, Setup.FontOsd), Setup.FontOsdSize); break; + case fontSml: SetFont(Font, AddDirectory(FONTDIR, Setup.FontSml), Setup.FontSmlSize); break; + case fontFix: SetFont(Font, AddDirectory(FONTDIR, Setup.FontFix), Setup.FontFixSize); break; + } + } return fonts[Font]; } @@ -176,16 +352,18 @@ void cTextWrapper::Set(const char *Text, const cFont *Font, int Width) stripspace(text); // strips trailing newlines for (char *p = text; *p; ) { - if (*p == '\n') { + int sl = Utf8CharLen(p); + uint sym = Utf8CharGet(p, sl); + if (sym == '\n') { lines++; w = 0; Blank = Delim = NULL; p++; continue; } - else if (isspace(*p)) + else if (sl == 1 && isspace(sym)) Blank = p; - int cw = Font->Width(*p); + int cw = Font->Width(sym); if (w + cw > Width) { if (Blank) { *Blank = '\n'; @@ -214,7 +392,7 @@ void cTextWrapper::Set(const char *Text, const cFont *Font, int Width) Delim = p; Blank = NULL; } - p++; + p += sl; } } |