summaryrefslogtreecommitdiff
path: root/font.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2007-06-10 13:02:43 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2007-06-10 13:02:43 +0200
commitc6f8a149574f4e5196f802439e7439406ca82e71 (patch)
tree0b04b012f87d7033abb37aeb986385672b85da18 /font.c
parent32dd727d057a1ba22d403f48306adae10285ac77 (diff)
downloadvdr-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.c352
1 files changed, 265 insertions, 87 deletions
diff --git a/font.c b/font.c
index 5e22fcd2..af8b7cbf 100644
--- a/font.c
+++ b/font.c
@@ -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;
}
}