Logo Search packages:      
Sourcecode: ia32-libs-gtk version File versions  Download package

xftfreetype.c

/*
 * $Id: xftfreetype.c,v 1.5 2005/07/03 07:00:57 daniels Exp $
 *
 * Copyright © 2000 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "xftint.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)
/*
 * List of all open files (each face in a file is managed separately)
 */

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;
    f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 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;
    f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 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");
}

#if HAVE_FT_BITMAP_SIZE_Y_PPEM
#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem)
#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem)
#else
#define X_SIZE(face,i) ((face)->available_sizes[i].width << 6)
#define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6)
#endif

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);
      /*
       * Bitmap only faces must match exactly, so find the closest
       * one (height dominant search)
       */
      if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
      {
          int           i, best = 0;

#define xft_abs(a)      ((a) < 0 ? -(a) : (a))
#define dist(a,b) (xft_abs((a)-(b)))

          for (i = 1; i < face->num_fixed_sizes; i++)
          {
            if (dist (ysize, Y_SIZE(face,i)) <
                dist (ysize, Y_SIZE(face, best)) ||
                (dist (ysize, Y_SIZE(face, i)) ==
                 dist (ysize, Y_SIZE(face, best)) &&
                 dist (xsize, X_SIZE(face, i)) <
                 dist (xsize, X_SIZE(face, best))))
            {
                best = i;
            }
          }
          /* 
           * Freetype 2.1.7 and earlier used width/height
           * for matching sizes in the BDF and PCF loaders.
           * This has been fixed for 2.1.8.  Because BDF and PCF
           * files have but a single strike per file, we can
           * simply try both sizes.
           */
          if (
#if HAVE_FT_BITMAP_SIZE_Y_PPEM
            FT_Set_Char_Size (face, face->available_sizes[best].x_ppem,
                          face->available_sizes[best].y_ppem, 0, 0) != 0
            &&
#endif
            FT_Set_Char_Size (face, face->available_sizes[best].width << 6,
                          face->available_sizes[best].height << 6,
                          0, 0) != 0)
          {
            return False;
          }
      }
      else
      {
          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) + (f->file ? strlen (f->file) + 1 : 0));
    free (f);
}

/*
 * Find a prime larger than the minimum reasonable hash size
 */

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)
{
    /* at least 31.25 % extra space */
    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);
    /*
     * Make sure the face is usable at the requested size
     */
    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;
#ifdef FC_HINT_STYLE
    int             hint_style;
#endif
    FcChar32          hash, *hashp;
    FT_Face     face;
    int               nhash;

    if (!info)
      return FcFalse;

    /*
     * Find the associated file
     */
    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;

    /*
     * Compute pixel size
     */
    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);
    /*
     * Get antialias value
     */
    switch (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &fi->antialias)) {
    case FcResultNoMatch:
      fi->antialias = True;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }
    
    /*
     * Get rgba value
     */
    switch (FcPatternGetInteger (pattern, FC_RGBA, 0, &fi->rgba)) {
    case FcResultNoMatch:
      fi->rgba = FC_RGBA_UNKNOWN;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }
    
    /*
     * Get matrix and transform values
     */
    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);
    
    /* 
     * Get render value, set to false if no Render extension present
     */
    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;
    
    /*
     * Compute glyph load flags
     */
    fi->load_flags = FT_LOAD_DEFAULT;

    /* disable bitmaps when anti-aliasing or transforming glyphs */
    if (fi->antialias || fi->transform)
      fi->load_flags |= FT_LOAD_NO_BITMAP;
    
    /* disable hinting if requested */
    switch (FcPatternGetBool (pattern, FC_HINTING, 0, &hinting)) {
    case FcResultNoMatch:
      hinting = FcTrue;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }

#ifdef FC_EMBOLDEN
    switch (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &fi->embolden)) {
    case FcResultNoMatch:
      fi->embolden = FcFalse;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }
#else
    fi->embolden = FcFalse;
#endif
    
#ifdef FC_HINT_STYLE
    switch (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style)) {
    case FcResultNoMatch:
      hint_style = FC_HINT_FULL;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }
#endif

    if (!hinting
#ifdef FC_HINT_STYLE
      || hint_style == FC_HINT_NONE
#endif
      )
    {
      fi->load_flags |= FT_LOAD_NO_HINTING;
    }

    /* Figure out the load target, which modifies the hinting
     * behavior of FreeType based on the intended use of the glyphs.
     */
    if (fi->antialias)
    {
#ifdef FC_HINT_STYLE
#ifdef FT_LOAD_TARGET_LIGHT
      if (FC_HINT_NONE < hint_style && hint_style < FC_HINT_FULL)
      {
          fi->load_flags |= FT_LOAD_TARGET_LIGHT;
      }
      else
#endif
#endif
      {
          /* autohinter will snap stems to integer widths, when
           * the LCD targets are used.
           */
          switch (fi->rgba) {
          case FC_RGBA_RGB:
          case FC_RGBA_BGR:
#ifdef FT_LOAD_TARGET_LCD
            fi->load_flags |= FT_LOAD_TARGET_LCD;
#endif
            break;
          case FC_RGBA_VRGB:
          case FC_RGBA_VBGR:
#ifdef FT_LOAD_TARGET_LCD_V
            fi->load_flags |= FT_LOAD_TARGET_LCD_V;
#endif
            break;
          }
      }
    }
#ifdef FT_LOAD_TARGET_MONO
    else
      fi->load_flags |= FT_LOAD_TARGET_MONO;
#endif
    
    /* set vertical layout if requested */
    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;

    /* force autohinting if requested */
    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;

    /* disable global advance width (for broken DynaLab TT CJK fonts) */
    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;
    
    /*
     * Get requested spacing value
     */
    switch (FcPatternGetInteger (pattern, FC_SPACING, 0, &fi->spacing)) {
    case FcResultNoMatch:
      fi->spacing = FC_PROPORTIONAL;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }
    
    /*
     * Check for minspace
     */

    switch (FcPatternGetBool (pattern, FC_MINSPACE, 0, &fi->minspace)) {
    case FcResultNoMatch:
      fi->minspace = FcFalse;
      break;
    case FcResultMatch:
      break;
    default:
      goto bail1;
    }
    /*
     * Check for fixed pixel spacing 
     */
    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;
    }

    /*
     * Step over hash value in the structure
     */
    hash = 0;
    hashp = (FcChar32 *) fi + 1;
    nhash = (sizeof (XftFontInfo) / sizeof (FcChar32)) - 1;

    while (nhash--)
      hash += *hashp++;
    fi->hash = hash;
    
    /*
     * All done
     */
    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   *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;
    int                 num_glyphs;

    if (!info)
      return 0;
    /*
     * Find a matching previously opened font
     */
    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;
      }

    /*
     * No existing font, create another.  
     */
    
    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;

    /*
     * Get the set of Unicode codepoints covered by the font.
     * If the incoming pattern doesn't provide this data, go
     * off and compute it.  Yes, this is expensive, but it's
     * required to map Unicode to glyph indices.
     */
    if (FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset) == FcResultMatch)
      charset = FcCharSetCopy (charset);
    else
      charset = FcFreeTypeCharSet (face, FcConfigGetBlanks (0));
    
    antialias = fi->antialias;
    if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
      antialias = FcFalse;

    /*
     * Find the appropriate picture format
     */
    if (fi->render)
    {
      if (antialias)
      {
          switch (fi->rgba) {
          case FC_RGBA_RGB:
          case FC_RGBA_BGR:
          case FC_RGBA_VRGB:
          case FC_RGBA_VBGR:
            format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
            break;
          default:
            format = XRenderFindStandardFormat (dpy, PictStandardA8);
            break;
          }
      }
      else
      {
          format = XRenderFindStandardFormat (dpy, PictStandardA1);
      }
      
      if (!format)
          goto bail2;
    }
    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;
    }
    
    /*
     * Sometimes the glyphs are numbered 1..n, other times 0..n-1,
     * accept either numbering scheme by making room in the table
     */
    num_glyphs = face->num_glyphs + 1;
    alloc_size = (sizeof (XftFontInt) + 
              num_glyphs * sizeof (XftGlyph *) +
              hash_value * sizeof (XftUcsHash));
    font = malloc (alloc_size);
    
    if (!font)
      goto bail2;

    XftMemAlloc (XFT_MEM_FONT, alloc_size);

    /*
     * Public fields
     */
    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;
    
    /*
     * Management fields
     */
    font->ref = 1;

    font->next = info->fonts;
    info->fonts = &font->public;
    
    font->hash_next = *bucket;
    *bucket = &font->public;
    
    /*
     * Copy the info over
     */
    font->info = *fi;
    /*
     * reset the antialias field.  It can't
     * be set correctly until the font is opened,
     * which doesn't happen in XftFontInfoFill
     */
    font->info.antialias = antialias;
    /*
     * bump XftFile reference count
     */
    font->info.file->ref++;
    
    /*
     * Per glyph information
     */
    font->glyphs = (XftGlyph **) (font + 1);
    memset (font->glyphs, '\0', num_glyphs * sizeof (XftGlyph *));
    font->num_glyphs = num_glyphs;
    /*
     * Unicode hash table information
     */
    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;
    /*
     * X specific fields
     */
    font->glyphset = 0;
    font->format = format;
    
    /*
     * Glyph memory management fields
     */
    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;
    
bail2:
    FcCharSetDestroy (charset);
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)
{
    XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, False);
    XftFontInt        *font = (XftFontInt *) public;
    int               i;
    
    /* note reduction in memory use */
    if (info)
      info->glyph_memory -= font->glyph_memory;
    /* Clean up the info */
    XftFontInfoEmpty (dpy, &font->info);
    /* Free the glyphset */
    if (font->glyphset)
      XRenderFreeGlyphSet (dpy, font->glyphset);
    /* Free the glyphs */
    for (i = 0; i < font->num_glyphs; i++)
    {
      XftGlyph    *xftg = font->glyphs[i];
      if (xftg)
      {
          if (xftg->bitmap)
            free (xftg->bitmap);
          free (xftg);
      }
    }
    
    /* Free the pattern and the charset */
    FcPatternDestroy (font->public.pattern);
    FcCharSetDestroy (font->public.charset);
    
    /* Finally, free the font structure */
    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);

      /* Unhook from display list */
      for (prev = &info->fonts; *prev; prev = &(*(XftFontInt **) prev)->next)
      {
          if (*prev == public)
          {
            *prev = font->next;
            break;
          }
      }
      /* Unhook from hash list */
      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;
          }
      }
      /* Destroy the font */
      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;
}

Generated by  Doxygen 1.6.0   Back to index