#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "xftint.h"
#include <X11/Xlibint.h>
FT_Library _XftFTlibrary;
#define FT_Matrix_Equal(a,b) ((a)->xx == (b)->xx && \
(a)->yy == (b)->yy && \
(a)->xy == (b)->xy && \
(a)->yx == (b)->yx)
static XftFtFile *_XftFtFiles;
int XftMaxFreeTypeFiles = 5;
static XftFtFile *
_XftGetFile (const FcChar8 *file, int id)
{
XftFtFile *f;
if (!XftInitFtLibrary ())
return 0;
for (f = _XftFtFiles; f; f = f->next)
{
if (!strcmp (f->file, (char *) file) && f->id == id)
{
++f->ref;
if (XftDebug () & XFT_DBG_REF)
printf ("FontFile %s/%d matches existing (%d)\n",
file, id, f->ref);
return f;
}
}
f = malloc (sizeof (XftFtFile) + strlen ((char *) file) + 1);
if (!f)
return 0;
XftMemAlloc (XFT_MEM_FILE, sizeof (XftFtFile) + strlen ((char *) file) + 1);
if (XftDebug () & XFT_DBG_REF)
printf ("FontFile %s/%d matches new\n",
file, id);
f->next = _XftFtFiles;
_XftFtFiles = f;
f->ref = 1;
f->file = (char *) (f+1);
strcpy (f->file, (char *) file);
f->id = id;
f->lock = 0;
f->face = 0;
f->xsize = 0;
f->ysize = 0;
return f;
}
static XftFtFile *
_XftGetFaceFile (FT_Face face)
{
XftFtFile *f;
f = malloc (sizeof (XftFtFile));
if (!f)
return 0;
XftMemAlloc (XFT_MEM_FILE, sizeof(XftFtFile));
f->next = 0;
f->ref = 1;
f->file = 0;
f->id = 0;
f->lock = 0;
f->face = face;
f->xsize = 0;
f->ysize = 0;
return f;
}
static int
_XftNumFiles (void)
{
XftFtFile *f;
int count = 0;
for (f = _XftFtFiles; f; f = f->next)
if (f->face && !f->lock)
++count;
return count;
}
static XftFtFile *
_XftNthFile (int n)
{
XftFtFile *f;
int count = 0;
for (f = _XftFtFiles; f; f = f->next)
if (f->face && !f->lock)
if (count++ == n)
break;
return f;
}
static void
_XftUncacheFiles (void)
{
int n;
XftFtFile *f;
while ((n = _XftNumFiles ()) > XftMaxFreeTypeFiles)
{
f = _XftNthFile (rand () % n);
if (f)
{
if (XftDebug() & XFT_DBG_REF)
printf ("Discard file %s/%d from cache\n",
f->file, f->id);
FT_Done_Face (f->face);
f->face = 0;
}
}
}
static FT_Face
_XftLockFile (XftFtFile *f)
{
++f->lock;
if (!f->face)
{
if (XftDebug() & XFT_DBG_REF)
printf ("Loading file %s/%d\n", f->file, f->id);
if (FT_New_Face (_XftFTlibrary, f->file, f->id, &f->face))
--f->lock;
f->xsize = 0;
f->ysize = 0;
f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0;
_XftUncacheFiles ();
}
return f->face;
}
static void
_XftLockError (char *reason)
{
fprintf (stderr, "Xft: locking error %s\n", reason);
}
static void
_XftUnlockFile (XftFtFile *f)
{
if (--f->lock < 0)
_XftLockError ("too many file unlocks");
}
FcBool
_XftSetFace (XftFtFile *f, FT_F26Dot6 xsize, FT_F26Dot6 ysize, FT_Matrix *matrix)
{
FT_Face face = f->face;
if (f->xsize != xsize || f->ysize != ysize)
{
if (XftDebug() & XFT_DBG_GLYPH)
printf ("Set face size to %dx%d (%dx%d)\n",
(int) (xsize >> 6), (int) (ysize >> 6), (int) xsize, (int) ysize);
if (FT_Set_Char_Size (face, xsize, ysize, 0, 0))
return False;
f->xsize = xsize;
f->ysize = ysize;
}
if (!FT_Matrix_Equal (&f->matrix, matrix))
{
if (XftDebug() & XFT_DBG_GLYPH)
printf ("Set face matrix to (%g,%g,%g,%g)\n",
(double) matrix->xx / 0x10000,
(double) matrix->xy / 0x10000,
(double) matrix->yx / 0x10000,
(double) matrix->yy / 0x10000);
FT_Set_Transform (face, matrix, 0);
f->matrix = *matrix;
}
return True;
}
static void
_XftReleaseFile (XftFtFile *f)
{
XftFtFile **prev;
if (--f->ref != 0)
return;
if (f->lock)
_XftLockError ("Attempt to close locked file");
if (f->file)
{
for (prev = &_XftFtFiles; *prev; prev = &(*prev)->next)
{
if (*prev == f)
{
*prev = f->next;
break;
}
}
if (f->face)
FT_Done_Face (f->face);
}
XftMemFree (XFT_MEM_FILE, sizeof (XftFtFile) + strlen (f->file) + 1);
free (f);
}
static FcChar32
_XftSqrt (FcChar32 a)
{
FcChar32 l, h, m;
l = 2;
h = a/2;
while ((h-l) > 1)
{
m = (h+l) >> 1;
if (m * m < a)
l = m;
else
h = m;
}
return h;
}
static FcBool
_XftIsPrime (FcChar32 i)
{
FcChar32 l, t;
if (i < 2)
return FcFalse;
if ((i & 1) == 0)
{
if (i == 2)
return FcTrue;
return FcFalse;
}
l = _XftSqrt (i) + 1;
for (t = 3; t <= l; t += 2)
if (i % t == 0)
return FcFalse;
return FcTrue;
}
static FcChar32
_XftHashSize (FcChar32 num_unicode)
{
FcChar32 hash = num_unicode + (num_unicode >> 2) + (num_unicode >> 4);
if ((hash & 1) == 0)
hash++;
while (!_XftIsPrime (hash))
hash += 2;
return hash;
}
FT_Face
XftLockFace (XftFont *public)
{
XftFontInt *font = (XftFontInt *) public;
XftFontInfo *fi = &font->info;
FT_Face face;
face = _XftLockFile (fi->file);
if (face && !_XftSetFace (fi->file, fi->xsize, fi->ysize, &fi->matrix))
{
_XftUnlockFile (fi->file);
face = 0;
}
return face;
}
void
XftUnlockFace (XftFont *public)
{
XftFontInt *font = (XftFontInt *) public;
_XftUnlockFile (font->info.file);
}
static FcBool
XftFontInfoFill (Display *dpy, _Xconst FcPattern *pattern, XftFontInfo *fi)
{
XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True);
FcChar8 *filename;
int id;
double dsize;
double aspect;
FcMatrix *font_matrix;
FcBool hinting, vertical_layout, autohint, global_advance;
FcChar32 hash, *hashp;
FT_Face face;
int nhash;
if (!info)
return FcFalse;
switch (FcPatternGetString (pattern, FC_FILE, 0, &filename)) {
case FcResultNoMatch:
filename = 0;
break;
case FcResultMatch:
break;
default:
goto bail0;
}
switch (FcPatternGetInteger (pattern, FC_INDEX, 0, &id)) {
case FcResultNoMatch:
id = 0;
break;
case FcResultMatch:
break;
default:
goto bail0;
}
if (filename)
fi->file = _XftGetFile (filename, id);
else if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &face) == FcResultMatch
&& face)
fi->file = _XftGetFaceFile (face);
else
fi->file = 0;
if (!fi->file)
goto bail0;
if (FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch)
goto bail1;
if (FcPatternGetDouble (pattern, FC_ASPECT, 0, &aspect) != FcResultMatch)
aspect = 1.0;
fi->ysize = (FT_F26Dot6) (dsize * 64.0);
fi->xsize = (FT_F26Dot6) (dsize * aspect * 64.0);
if (XftDebug() & XFT_DBG_OPEN)
printf ("XftFontInfoFill: %s: %d (%g pixels)\n",
(filename ? filename : (FcChar8 *) "<none>"), id, dsize);
switch (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &fi->antialias)) {
case FcResultNoMatch:
fi->antialias = True;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
switch (FcPatternGetInteger (pattern, FC_RGBA, 0, &fi->rgba)) {
case FcResultNoMatch:
fi->rgba = FC_RGBA_UNKNOWN;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
switch (FcPatternGetMatrix (pattern, FC_MATRIX, 0, &font_matrix)) {
case FcResultNoMatch:
fi->matrix.xx = fi->matrix.yy = 0x10000;
fi->matrix.xy = fi->matrix.yx = 0;
break;
case FcResultMatch:
fi->matrix.xx = 0x10000L * font_matrix->xx;
fi->matrix.yy = 0x10000L * font_matrix->yy;
fi->matrix.xy = 0x10000L * font_matrix->xy;
fi->matrix.yx = 0x10000L * font_matrix->yx;
break;
default:
goto bail1;
}
fi->transform = (fi->matrix.xx != 0x10000 || fi->matrix.xy != 0 ||
fi->matrix.yx != 0 || fi->matrix.yy != 0x10000);
if (info->hasRender)
{
switch (FcPatternGetBool (pattern, XFT_RENDER, 0, &fi->render)) {
case FcResultNoMatch:
fi->render = info->hasRender;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
}
else
fi->render = FcFalse;
fi->load_flags = FT_LOAD_DEFAULT;
if (fi->antialias || fi->transform)
fi->load_flags |= FT_LOAD_NO_BITMAP;
switch (FcPatternGetBool (pattern, FC_HINTING, 0, &hinting)) {
case FcResultNoMatch:
hinting = FcTrue;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
if (!hinting)
fi->load_flags |= FT_LOAD_NO_HINTING;
switch (FcPatternGetBool (pattern, FC_VERTICAL_LAYOUT, 0, &vertical_layout)) {
case FcResultNoMatch:
vertical_layout = FcFalse;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
if (vertical_layout)
fi->load_flags |= FT_LOAD_VERTICAL_LAYOUT;
switch (FcPatternGetBool (pattern, FC_AUTOHINT, 0, &autohint)) {
case FcResultNoMatch:
autohint = FcFalse;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
if (autohint)
fi->load_flags |= FT_LOAD_FORCE_AUTOHINT;
switch (FcPatternGetBool (pattern, FC_GLOBAL_ADVANCE, 0, &global_advance)) {
case FcResultNoMatch:
global_advance = FcTrue;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
if (!global_advance)
fi->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
switch (FcPatternGetInteger (pattern, FC_SPACING, 0, &fi->spacing)) {
case FcResultNoMatch:
fi->spacing = FC_PROPORTIONAL;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
switch (FcPatternGetBool (pattern, FC_MINSPACE, 0, &fi->minspace)) {
case FcResultNoMatch:
fi->minspace = FcFalse;
break;
case FcResultMatch:
break;
default:
goto bail1;
}
switch (FcPatternGetInteger (pattern, FC_CHAR_WIDTH, 0, &fi->char_width)) {
case FcResultNoMatch:
fi->char_width = 0;
break;
case FcResultMatch:
if (fi->char_width)
fi->spacing = FC_MONO;
break;
default:
goto bail1;
}
hash = 0;
hashp = (FcChar32 *) fi + 1;
nhash = (sizeof (XftFontInfo) / sizeof (FcChar32)) - 1;
while (nhash--)
hash += *hashp++;
fi->hash = hash;
return FcTrue;
bail1:
_XftReleaseFile (fi->file);
fi->file = 0;
bail0:
return FcFalse;
}
static void
XftFontInfoEmpty (Display *dpy, XftFontInfo *fi)
{
if (fi->file)
_XftReleaseFile (fi->file);
}
XftFontInfo *
XftFontInfoCreate (Display *dpy, _Xconst FcPattern *pattern)
{
XftFontInfo *fi = malloc (sizeof (XftFontInfo));
if (!fi)
return 0;
if (!XftFontInfoFill (dpy, pattern, fi))
{
free (fi);
fi = 0;
}
XftMemAlloc (XFT_MEM_FONT, sizeof (XftFontInfo));
return fi;
}
void
XftFontInfoDestroy (Display *dpy, XftFontInfo *fi)
{
XftFontInfoEmpty (dpy, fi);
XftMemFree (XFT_MEM_FONT, sizeof (XftFontInfo));
free (fi);
}
FcChar32
XftFontInfoHash (_Xconst XftFontInfo *fi)
{
return fi->hash;
}
FcBool
XftFontInfoEqual (_Xconst XftFontInfo *a, _Xconst XftFontInfo *b)
{
return memcmp ((void *) a, (void *) b, sizeof (XftFontInfo)) == 0;
}
XftFont *
XftFontOpenInfo (Display *dpy,
FcPattern *pattern,
XftFontInfo *fi)
{
XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True);
FT_Face face;
XftFont **bucket;
XftFontInt *font;
XRenderPictFormat pf, *format;
FcCharSet *charset;
FcChar32 num_unicode;
FcChar32 hash_value;
FcChar32 rehash_value;
FcBool antialias;
int max_glyph_memory;
int alloc_size;
int ascent, descent, height;
int i;
if (!info)
return 0;
bucket = &info->fontHash[fi->hash % XFT_NUM_FONT_HASH];
for (font = (XftFontInt *) *bucket; font; font = (XftFontInt *) font->hash_next)
if (XftFontInfoEqual (&font->info, fi))
{
if (!font->ref++)
--info->num_unref_fonts;
FcPatternDestroy (pattern);
return &font->public;
}
if (XftDebug () & XFT_DBG_CACHE)
printf ("New font %s/%d size %dx%d\n",
fi->file->file, fi->file->id,
(int) fi->xsize >> 6, (int) fi->ysize >> 6);
if (FcPatternGetInteger (pattern, XFT_MAX_GLYPH_MEMORY, 0,
&max_glyph_memory) != FcResultMatch)
max_glyph_memory = XFT_FONT_MAX_GLYPH_MEMORY;
face = _XftLockFile (fi->file);
if (!face)
goto bail0;
if (!_XftSetFace (fi->file, fi->xsize, fi->ysize, &fi->matrix))
goto bail1;
if (FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset) != FcResultMatch)
charset = FcFreeTypeCharSet (face, FcConfigGetBlanks (0));
antialias = fi->antialias;
if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
antialias = FcFalse;
if (fi->render)
{
if (antialias)
{
switch (fi->rgba) {
case FC_RGBA_RGB:
case FC_RGBA_BGR:
case FC_RGBA_VRGB:
case FC_RGBA_VBGR:
pf.depth = 32;
pf.type = PictTypeDirect;
pf.direct.alpha = 24;
pf.direct.alphaMask = 0xff;
pf.direct.red = 16;
pf.direct.redMask = 0xff;
pf.direct.green = 8;
pf.direct.greenMask = 0xff;
pf.direct.blue = 0;
pf.direct.blueMask = 0xff;
format = XRenderFindFormat(dpy,
PictFormatType|
PictFormatDepth|
PictFormatAlpha|
PictFormatAlphaMask|
PictFormatRed|
PictFormatRedMask|
PictFormatGreen|
PictFormatGreenMask|
PictFormatBlue|
PictFormatBlueMask,
&pf, 0);
break;
default:
pf.depth = 8;
pf.type = PictTypeDirect;
pf.direct.alpha = 0;
pf.direct.alphaMask = 0xff;
format = XRenderFindFormat(dpy,
PictFormatType|
PictFormatDepth|
PictFormatAlpha|
PictFormatAlphaMask,
&pf, 0);
break;
}
}
else
{
pf.depth = 1;
pf.type = PictTypeDirect;
pf.direct.alpha = 0;
pf.direct.alphaMask = 0x1;
format = XRenderFindFormat(dpy,
PictFormatType|
PictFormatDepth|
PictFormatAlpha|
PictFormatAlphaMask,
&pf, 0);
}
if (!format)
goto bail0;
}
else
format = 0;
if (charset)
{
num_unicode = FcCharSetCount (charset);
hash_value = _XftHashSize (num_unicode);
rehash_value = hash_value - 2;
}
else
{
num_unicode = 0;
hash_value = 0;
rehash_value = 0;
}
alloc_size = (sizeof (XftFontInt) +
face->num_glyphs * sizeof (XftGlyph *) +
hash_value * sizeof (XftUcsHash));
font = malloc (alloc_size);
if (!font)
goto bail1;
XftMemAlloc (XFT_MEM_FONT, alloc_size);
if (fi->transform)
{
FT_Vector vector;
vector.x = 0;
vector.y = face->size->metrics.descender;
FT_Vector_Transform (&vector, &fi->matrix);
descent = -(vector.y >> 6);
vector.x = 0;
vector.y = face->size->metrics.ascender;
FT_Vector_Transform (&vector, &fi->matrix);
ascent = vector.y >> 6;
if (fi->minspace)
height = ascent + descent;
else
{
vector.x = 0;
vector.y = face->size->metrics.height;
FT_Vector_Transform (&vector, &fi->matrix);
height = vector.y >> 6;
}
}
else
{
descent = -(face->size->metrics.descender >> 6);
ascent = face->size->metrics.ascender >> 6;
if (fi->minspace)
height = ascent + descent;
else
height = face->size->metrics.height >> 6;
}
font->public.ascent = ascent;
font->public.descent = descent;
font->public.height = height;
if (fi->char_width)
font->public.max_advance_width = fi->char_width;
else
{
if (fi->transform)
{
FT_Vector vector;
vector.x = face->size->metrics.max_advance;
vector.y = 0;
FT_Vector_Transform (&vector, &fi->matrix);
font->public.max_advance_width = vector.x >> 6;
}
else
font->public.max_advance_width = face->size->metrics.max_advance >> 6;
}
font->public.charset = charset;
font->public.pattern = pattern;
font->ref = 1;
font->next = info->fonts;
info->fonts = &font->public;
font->hash_next = *bucket;
*bucket = &font->public;
font->info = *fi;
font->info.antialias = antialias;
font->info.file->ref++;
font->glyphs = (XftGlyph **) (font + 1);
memset (font->glyphs, '\0', face->num_glyphs * sizeof (XftGlyph *));
font->num_glyphs = face->num_glyphs;
font->hash_table = (XftUcsHash *) (font->glyphs + font->num_glyphs);
for (i = 0; i < hash_value; i++)
{
font->hash_table[i].ucs4 = ((FcChar32) ~0);
font->hash_table[i].glyph = 0;
}
font->hash_value = hash_value;
font->rehash_value = rehash_value;
font->glyphset = 0;
font->format = format;
font->glyph_memory = 0;
font->max_glyph_memory = max_glyph_memory;
font->use_free_glyphs = info->use_free_glyphs;
_XftUnlockFile (fi->file);
return &font->public;
bail1:
_XftUnlockFile (fi->file);
bail0:
return 0;
}
XftFont *
XftFontOpenPattern (Display *dpy, FcPattern *pattern)
{
XftFontInfo info;
XftFont *font;
if (!XftFontInfoFill (dpy, pattern, &info))
return 0;
font = XftFontOpenInfo (dpy, pattern, &info);
XftFontInfoEmpty (dpy, &info);
return font;
}
XftFont *
XftFontCopy (Display *dpy, XftFont *public)
{
XftFontInt *font = (XftFontInt *) public;
font->ref++;
return public;
}
static void
XftFontDestroy (Display *dpy, XftFont *public)
{
XftFontInt *font = (XftFontInt *) public;
int i;
XftFontInfoEmpty (dpy, &font->info);
if (font->glyphset)
XRenderFreeGlyphSet (dpy, font->glyphset);
for (i = 0; i < font->num_glyphs; i++)
{
XftGlyph *xftg = font->glyphs[i];
if (xftg)
{
if (xftg->bitmap)
free (xftg->bitmap);
free (xftg);
}
}
XftMemFree (XFT_MEM_FONT, sizeof (XftFontInt) +
font->num_glyphs * sizeof (XftGlyph *) +
font->hash_value * sizeof (XftUcsHash));
free (font);
}
static XftFont *
XftFontFindNthUnref (XftDisplayInfo *info, int n)
{
XftFont *public;
XftFontInt *font;
for (public = info->fonts; public; public = font->next)
{
font = (XftFontInt*) public;
if (!font->ref && !n--)
break;
}
return public;
}
void
XftFontManageMemory (Display *dpy)
{
XftDisplayInfo *info = _XftDisplayInfoGet (dpy, False);
XftFont **prev;
XftFont *public;
XftFontInt *font;
if (!info)
return;
while (info->num_unref_fonts > info->max_unref_fonts)
{
public = XftFontFindNthUnref (info, rand() % info->num_unref_fonts);
font = (XftFontInt *) public;
if (XftDebug () & XFT_DBG_CACHE)
printf ("freeing unreferenced font %s/%d size %dx%d\n",
font->info.file->file, font->info.file->id,
(int) font->info.xsize >> 6, (int) font->info.ysize >> 6);
for (prev = &info->fonts; *prev; prev = &(*(XftFontInt **) prev)->next)
{
if (*prev == public)
{
*prev = font->next;
break;
}
}
for (prev = &info->fontHash[font->info.hash % XFT_NUM_FONT_HASH];
*prev;
prev = &(*(XftFontInt **) prev)->hash_next)
{
if (*prev == public)
{
*prev = font->hash_next;
break;
}
}
XftFontDestroy (dpy, public);
--info->num_unref_fonts;
}
}
void
XftFontClose (Display *dpy, XftFont *public)
{
XftDisplayInfo *info = _XftDisplayInfoGet (dpy, False);
XftFontInt *font = (XftFontInt *) public;
if (--font->ref != 0)
return;
if (info)
{
++info->num_unref_fonts;
XftFontManageMemory (dpy);
}
else
{
XftFontDestroy (dpy, public);
}
}
FcBool
XftInitFtLibrary (void)
{
if (_XftFTlibrary)
return FcTrue;
if (FT_Init_FreeType (&_XftFTlibrary))
return FcFalse;
return FcTrue;
}