diff options
Diffstat (limited to 'nx-X11/lib/Xft/xftglyphs.c')
-rw-r--r-- | nx-X11/lib/Xft/xftglyphs.c | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/nx-X11/lib/Xft/xftglyphs.c b/nx-X11/lib/Xft/xftglyphs.c new file mode 100644 index 000000000..c1ee42142 --- /dev/null +++ b/nx-X11/lib/Xft/xftglyphs.c @@ -0,0 +1,825 @@ +/* + * $Id: xftglyphs.c,v 1.4 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" +#include <freetype/ftoutln.h> + +static const int filters[3][3] = { + /* red */ +#if 0 +{ 65538*4/7,65538*2/7,65538*1/7 }, + /* green */ +{ 65536*1/4, 65536*2/4, 65537*1/4 }, + /* blue */ +{ 65538*1/7,65538*2/7,65538*4/7 }, +#endif +{ 65538*9/13,65538*3/13,65538*1/13 }, + /* green */ +{ 65538*1/6, 65538*4/6, 65538*1/6 }, + /* blue */ +{ 65538*1/13,65538*3/13,65538*9/13 }, +}; + +/* + * Validate the memory info for a font + */ + +static void +_XftFontValidateMemory (Display *dpy, XftFont *public) +{ + XftFontInt *font = (XftFontInt *) public; + unsigned long glyph_memory; + FT_UInt glyphindex; + XftGlyph *xftg; + + glyph_memory = 0; + for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++) + { + xftg = font->glyphs[glyphindex]; + if (xftg) + { + glyph_memory += xftg->glyph_memory; + } + } + if (glyph_memory != font->glyph_memory) + printf ("Font glyph cache incorrect has %ld bytes, should have %ld\n", + font->glyph_memory, glyph_memory); +} + +void +XftFontLoadGlyphs (Display *dpy, + XftFont *pub, + FcBool need_bitmaps, + _Xconst FT_UInt *glyphs, + int nglyph) +{ + XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True); + XftFontInt *font = (XftFontInt *) pub; + FT_Error error; + FT_UInt glyphindex; + FT_GlyphSlot glyphslot; + XftGlyph *xftg; + Glyph glyph; + unsigned char bufLocal[4096]; + unsigned char *bufBitmap = bufLocal; + int bufSize = sizeof (bufLocal); + int size, pitch; + unsigned char bufLocalRgba[4096]; + unsigned char *bufBitmapRgba = bufLocalRgba; + int bufSizeRgba = sizeof (bufLocalRgba); + int sizergba, pitchrgba, widthrgba; + int width; + int height; + int left, right, top, bottom; + int hmul = 1; + int vmul = 1; + FT_Bitmap ftbit; + FT_Matrix matrix; + FT_Vector vector; + Bool subpixel = False; + FT_Face face; + + if (!info) + return; + + face = XftLockFace (&font->public); + + if (!face) + return; + + matrix.xx = matrix.yy = 0x10000L; + matrix.xy = matrix.yx = 0; + + if (font->info.antialias) + { + switch (font->info.rgba) { + case FC_RGBA_RGB: + case FC_RGBA_BGR: + matrix.xx *= 3; + subpixel = True; + hmul = 3; + break; + case FC_RGBA_VRGB: + case FC_RGBA_VBGR: + matrix.yy *= 3; + vmul = 3; + subpixel = True; + break; + } + } + + while (nglyph--) + { + glyphindex = *glyphs++; + xftg = font->glyphs[glyphindex]; + if (!xftg) + continue; + + if (XftDebug() & XFT_DBG_CACHE) + _XftFontValidateMemory (dpy, pub); + /* + * Check to see if this glyph has just been loaded, + * this happens when drawing the same glyph twice + * in a single string + */ + if (xftg->glyph_memory) + continue; + + error = FT_Load_Glyph (face, glyphindex, font->info.load_flags); + if (error) + { + /* + * If anti-aliasing or transforming glyphs and + * no outline version exists, fallback to the + * bitmap and let things look bad instead of + * missing the glyph + */ + if (font->info.load_flags & FT_LOAD_NO_BITMAP) + error = FT_Load_Glyph (face, glyphindex, + font->info.load_flags & ~FT_LOAD_NO_BITMAP); + if (error) + continue; + } + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define TRUNC(x) ((x) >> 6) +#define ROUND(x) (((x)+32) & -64) + + glyphslot = face->glyph; + +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + /* + * Embolden if required + */ + if (font->info.embolden) FT_GlyphSlot_Embolden(glyphslot); +#endif + + /* + * Compute glyph metrics from FreeType information + */ + if(font->info.transform && glyphslot->format != ft_glyph_format_bitmap) + { + /* + * calculate the true width by transforming all four corners. + */ + int xc, yc; + left = right = top = bottom = 0; + for(xc = 0; xc <= 1; xc ++) { + for(yc = 0; yc <= 1; yc++) { + vector.x = glyphslot->metrics.horiBearingX + xc * glyphslot->metrics.width; + vector.y = glyphslot->metrics.horiBearingY - yc * glyphslot->metrics.height; + FT_Vector_Transform(&vector, &font->info.matrix); + if (XftDebug() & XFT_DBG_GLYPH) + printf("Trans %d %d: %d %d\n", (int) xc, (int) yc, + (int) vector.x, (int) vector.y); + if(xc == 0 && yc == 0) { + left = right = vector.x; + top = bottom = vector.y; + } else { + if(left > vector.x) left = vector.x; + if(right < vector.x) right = vector.x; + if(bottom > vector.y) bottom = vector.y; + if(top < vector.y) top = vector.y; + } + + } + } + left = FLOOR(left); + right = CEIL(right); + bottom = FLOOR(bottom); + top = CEIL(top); + + } else { + left = FLOOR( glyphslot->metrics.horiBearingX ); + right = CEIL( glyphslot->metrics.horiBearingX + glyphslot->metrics.width ); + + top = CEIL( glyphslot->metrics.horiBearingY ); + bottom = FLOOR( glyphslot->metrics.horiBearingY - glyphslot->metrics.height ); + } + + width = TRUNC(right - left); + height = TRUNC( top - bottom ); + + /* + * Clip charcell glyphs to the bounding box + * XXX transformed? + */ + if (font->info.spacing >= FC_CHARCELL && !font->info.transform) + { + if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT) + { + if (TRUNC(bottom) > font->public.max_advance_width) + { + int adjust; + + adjust = bottom - (font->public.max_advance_width << 6); + if (adjust > top) + adjust = top; + top -= adjust; + bottom -= adjust; + height = font->public.max_advance_width; + } + } + else + { + if (TRUNC(right) > font->public.max_advance_width) + { + int adjust; + + adjust = right - (font->public.max_advance_width << 6); + if (adjust > left) + adjust = left; + left -= adjust; + right -= adjust; + width = font->public.max_advance_width; + } + } + } + + if (font->info.antialias) + pitch = (width * hmul + 3) & ~3; + else + pitch = ((width + 31) & ~31) >> 3; + + size = pitch * height * vmul; + + xftg->metrics.width = width; + xftg->metrics.height = height; + xftg->metrics.x = -TRUNC(left); + xftg->metrics.y = TRUNC(top); + + if (font->info.spacing >= FC_MONO) + { + if (font->info.transform) + { + if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT) + { + vector.x = 0; + vector.y = -face->size->metrics.max_advance; + } + else + { + vector.x = face->size->metrics.max_advance; + vector.y = 0; + } + FT_Vector_Transform (&vector, &font->info.matrix); + xftg->metrics.xOff = vector.x >> 6; + xftg->metrics.yOff = -(vector.y >> 6); + } + else + { + if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT) + { + xftg->metrics.xOff = 0; + xftg->metrics.yOff = -font->public.max_advance_width; + } + else + { + xftg->metrics.xOff = font->public.max_advance_width; + xftg->metrics.yOff = 0; + } + } + } + else + { + xftg->metrics.xOff = TRUNC(ROUND(glyphslot->advance.x)); + xftg->metrics.yOff = -TRUNC(ROUND(glyphslot->advance.y)); + } + + /* + * If the glyph is relatively large (> 1% of server memory), + * don't send it until necessary + */ + if (!need_bitmaps && size > info->max_glyph_memory / 100) + continue; + + /* + * Make sure there's enough buffer space for the glyph + */ + if (size > bufSize) + { + if (bufBitmap != bufLocal) + free (bufBitmap); + bufBitmap = (unsigned char *) malloc (size); + if (!bufBitmap) + continue; + bufSize = size; + } + memset (bufBitmap, 0, size); + + /* + * Rasterize into the local buffer + */ + switch (glyphslot->format) { + case ft_glyph_format_outline: + ftbit.width = width * hmul; + ftbit.rows = height * vmul; + ftbit.pitch = pitch; + if (font->info.antialias) + ftbit.pixel_mode = ft_pixel_mode_grays; + else + ftbit.pixel_mode = ft_pixel_mode_mono; + + ftbit.buffer = bufBitmap; + + if (subpixel) + FT_Outline_Transform (&glyphslot->outline, &matrix); + + FT_Outline_Translate ( &glyphslot->outline, -left*hmul, -bottom*vmul ); + + FT_Outline_Get_Bitmap( _XftFTlibrary, &glyphslot->outline, &ftbit ); + break; + case ft_glyph_format_bitmap: + if (font->info.antialias) + { + unsigned char *srcLine, *dstLine; + int height; + int x; + int h, v; + + srcLine = glyphslot->bitmap.buffer; + dstLine = bufBitmap; + height = glyphslot->bitmap.rows; + while (height--) + { + for (x = 0; x < glyphslot->bitmap.width; x++) + { + /* always MSB bitmaps */ + unsigned char a = ((srcLine[x >> 3] & (0x80 >> (x & 7))) ? + 0xff : 0x00); + if (subpixel) + { + for (v = 0; v < vmul; v++) + for (h = 0; h < hmul; h++) + dstLine[v * pitch + x*hmul + h] = a; + } + else + dstLine[x] = a; + } + dstLine += pitch * vmul; + srcLine += glyphslot->bitmap.pitch; + } + } + else + { + unsigned char *srcLine, *dstLine; + int h, bytes; + + srcLine = glyphslot->bitmap.buffer; + dstLine = bufBitmap; + h = glyphslot->bitmap.rows; + bytes = (glyphslot->bitmap.width + 7) >> 3; + while (h--) + { + memcpy (dstLine, srcLine, bytes); + dstLine += pitch; + srcLine += glyphslot->bitmap.pitch; + } + } + break; + default: + if (XftDebug() & XFT_DBG_GLYPH) + printf ("glyph %d is not in a usable format\n", + (int) glyphindex); + continue; + } + + if (XftDebug() & XFT_DBG_GLYPH) + { + printf ("glyph %d:\n", (int) glyphindex); + printf (" xywh (%d %d %d %d), trans (%d %d %d %d) wh (%d %d)\n", + (int) glyphslot->metrics.horiBearingX, + (int) glyphslot->metrics.horiBearingY, + (int) glyphslot->metrics.width, + (int) glyphslot->metrics.height, + left, right, top, bottom, + width, height); + if (XftDebug() & XFT_DBG_GLYPHV) + { + int x, y; + unsigned char *line; + + line = bufBitmap; + for (y = 0; y < height * vmul; y++) + { + if (font->info.antialias) + { + static char den[] = { " .:;=+*#" }; + for (x = 0; x < pitch; x++) + printf ("%c", den[line[x] >> 5]); + } + else + { + for (x = 0; x < pitch * 8; x++) + { + printf ("%c", line[x>>3] & (1 << (x & 7)) ? '#' : ' '); + } + } + printf ("|\n"); + line += pitch; + } + printf ("\n"); + } + } + + /* + * Use the glyph index as the wire encoding; it + * might be more efficient for some locales to map + * these by first usage to smaller values, but that + * would require persistently storing the map when + * glyphs were freed. + */ + glyph = (Glyph) glyphindex; + + if (subpixel) + { + int x, y; + unsigned char *in_line, *out_line, *in; + unsigned int *out; + unsigned int red, green, blue; + int rf, gf, bf; + int s; + int o, os; + + /* + * Filter the glyph to soften the color fringes + */ + widthrgba = width; + pitchrgba = (widthrgba * 4 + 3) & ~3; + sizergba = pitchrgba * height; + + os = 1; + switch (font->info.rgba) { + case FC_RGBA_VRGB: + os = pitch; + case FC_RGBA_RGB: + default: + rf = 0; + gf = 1; + bf = 2; + break; + case FC_RGBA_VBGR: + os = pitch; + case FC_RGBA_BGR: + bf = 0; + gf = 1; + rf = 2; + break; + } + if (sizergba > bufSizeRgba) + { + if (bufBitmapRgba != bufLocalRgba) + free (bufBitmapRgba); + bufBitmapRgba = (unsigned char *) malloc (sizergba); + if (!bufBitmapRgba) + continue; + bufSizeRgba = sizergba; + } + memset (bufBitmapRgba, 0, sizergba); + in_line = bufBitmap; + out_line = bufBitmapRgba; + for (y = 0; y < height; y++) + { + in = in_line; + out = (unsigned int *) out_line; + in_line += pitch * vmul; + out_line += pitchrgba; + for (x = 0; x < width * hmul; x += hmul) + { + red = green = blue = 0; + o = 0; + for (s = 0; s < 3; s++) + { + red += filters[rf][s]*in[x+o]; + green += filters[gf][s]*in[x+o]; + blue += filters[bf][s]*in[x+o]; + o += os; + } + red = red / 65536; + green = green / 65536; + blue = blue / 65536; + *out++ = (green << 24) | (red << 16) | (green << 8) | blue; + } + } + + xftg->glyph_memory = sizergba + sizeof (XftGlyph); + if (font->format) + { + if (!font->glyphset) + font->glyphset = XRenderCreateGlyphSet (dpy, font->format); + if (ImageByteOrder (dpy) != XftNativeByteOrder ()) + XftSwapCARD32 ((CARD32 *) bufBitmapRgba, sizergba >> 2); + XRenderAddGlyphs (dpy, font->glyphset, &glyph, + &xftg->metrics, 1, + (char *) bufBitmapRgba, sizergba); + } + else + { + if (sizergba) + { + xftg->bitmap = malloc (sizergba); + if (xftg->bitmap) + memcpy (xftg->bitmap, bufBitmapRgba, sizergba); + } + else + xftg->bitmap = 0; + } + } + else + { + xftg->glyph_memory = size + sizeof (XftGlyph); + if (font->format) + { + /* + * swap bit order around; FreeType is always MSBFirst + */ + if (!font->info.antialias) + { + if (BitmapBitOrder (dpy) != MSBFirst) + { + unsigned char *line; + unsigned char c; + int i; + + line = (unsigned char *) bufBitmap; + i = size; + while (i--) + { + c = *line; + c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55); + c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33); + c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f); + *line++ = c; + } + } + } + if (!font->glyphset) + font->glyphset = XRenderCreateGlyphSet (dpy, font->format); + XRenderAddGlyphs (dpy, font->glyphset, &glyph, + &xftg->metrics, 1, + (char *) bufBitmap, size); + } + else + { + if (size) + { + xftg->bitmap = malloc (size); + if (xftg->bitmap) + memcpy (xftg->bitmap, bufBitmap, size); + } + else + xftg->bitmap = 0; + } + } + font->glyph_memory += xftg->glyph_memory; + info->glyph_memory += xftg->glyph_memory; + if (XftDebug() & XFT_DBG_CACHE) + _XftFontValidateMemory (dpy, pub); + if (XftDebug() & XFT_DBG_CACHEV) + printf ("Caching glyph 0x%x size %ld\n", glyphindex, + xftg->glyph_memory); + } + if (bufBitmap != bufLocal) + free (bufBitmap); + if (bufBitmapRgba != bufLocalRgba) + free (bufBitmapRgba); + XftUnlockFace (&font->public); +} + +void +XftFontUnloadGlyphs (Display *dpy, + XftFont *pub, + _Xconst FT_UInt *glyphs, + int nglyph) +{ + XftDisplayInfo *info = _XftDisplayInfoGet (dpy, False); + XftFontInt *font = (XftFontInt *) pub; + XftGlyph *xftg; + FT_UInt glyphindex; + Glyph glyphBuf[1024]; + int nused; + + nused = 0; + while (nglyph--) + { + glyphindex = *glyphs++; + xftg = font->glyphs[glyphindex]; + if (!xftg) + continue; + if (xftg->glyph_memory) + { + if (font->format) + { + if (font->glyphset) + { + glyphBuf[nused++] = (Glyph) glyphindex; + if (nused == sizeof (glyphBuf) / sizeof (glyphBuf[0])) + { + XRenderFreeGlyphs (dpy, font->glyphset, glyphBuf, nused); + nused = 0; + } + } + } + else + { + if (xftg->bitmap) + free (xftg->bitmap); + } + font->glyph_memory -= xftg->glyph_memory; + if (info) + info->glyph_memory -= xftg->glyph_memory; + } + free (xftg); + XftMemFree (XFT_MEM_GLYPH, sizeof (XftGlyph)); + font->glyphs[glyphindex] = 0; + } + if (font->glyphset && nused) + XRenderFreeGlyphs (dpy, font->glyphset, glyphBuf, nused); +} + +FcBool +XftFontCheckGlyph (Display *dpy, + XftFont *pub, + FcBool need_bitmaps, + FT_UInt glyph, + FT_UInt *missing, + int *nmissing) +{ + XftFontInt *font = (XftFontInt *) pub; + XftGlyph *xftg; + int n; + + if (glyph >= font->num_glyphs) + return FcFalse; + xftg = font->glyphs[glyph]; + if (!xftg || (need_bitmaps && !xftg->glyph_memory)) + { + if (!xftg) + { + xftg = (XftGlyph *) malloc (sizeof (XftGlyph)); + if (!xftg) + return FcFalse; + XftMemAlloc (XFT_MEM_GLYPH, sizeof (XftGlyph)); + xftg->bitmap = 0; + xftg->glyph_memory = 0; + font->glyphs[glyph] = xftg; + } + n = *nmissing; + missing[n++] = glyph; + if (n == XFT_NMISSING) + { + XftFontLoadGlyphs (dpy, pub, need_bitmaps, missing, n); + n = 0; + } + *nmissing = n; + return FcTrue; + } + else + return FcFalse; +} + +FcBool +XftCharExists (Display *dpy, + XftFont *pub, + FcChar32 ucs4) +{ + if (pub->charset) + return FcCharSetHasChar (pub->charset, ucs4); + return FcFalse; +} + +#define Missing ((FT_UInt) ~0) + +FT_UInt +XftCharIndex (Display *dpy, + XftFont *pub, + FcChar32 ucs4) +{ + XftFontInt *font = (XftFontInt *) pub; + FcChar32 ent, offset; + FT_Face face; + + if (!font->hash_value) + return 0; + + ent = ucs4 % font->hash_value; + offset = 0; + while (font->hash_table[ent].ucs4 != ucs4) + { + if (font->hash_table[ent].ucs4 == (FcChar32) ~0) + { + if (!XftCharExists (dpy, pub, ucs4)) + return 0; + face = XftLockFace (pub); + if (!face) + return 0; + font->hash_table[ent].ucs4 = ucs4; + font->hash_table[ent].glyph = FcFreeTypeCharIndex (face, ucs4); + XftUnlockFace (pub); + break; + } + if (!offset) + { + offset = ucs4 % font->rehash_value; + if (!offset) + offset = 1; + } + ent = ent + offset; + if (ent >= font->hash_value) + ent -= font->hash_value; + } + return font->hash_table[ent].glyph; +} + +/* + * Pick a random glyph from the font and remove it from the cache + */ +void +_XftFontUncacheGlyph (Display *dpy, XftFont *pub) +{ + XftFontInt *font = (XftFontInt *) pub; + unsigned long glyph_memory; + FT_UInt glyphindex; + XftGlyph *xftg; + + if (!font->glyph_memory) + return; + if (font->use_free_glyphs) + { + glyph_memory = rand() % font->glyph_memory; + } + else + { + if (font->glyphset) + { + XRenderFreeGlyphSet (dpy, font->glyphset); + font->glyphset = 0; + } + glyph_memory = 0; + } + + if (XftDebug() & XFT_DBG_CACHE) + _XftFontValidateMemory (dpy, pub); + for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++) + { + xftg = font->glyphs[glyphindex]; + if (xftg) + { + if (xftg->glyph_memory > glyph_memory) + { + if (XftDebug() & XFT_DBG_CACHEV) + printf ("Uncaching glyph 0x%x size %ld\n", + glyphindex, xftg->glyph_memory); + XftFontUnloadGlyphs (dpy, pub, &glyphindex, 1); + if (!font->use_free_glyphs) + continue; + break; + } + glyph_memory -= xftg->glyph_memory; + } + } + if (XftDebug() & XFT_DBG_CACHE) + _XftFontValidateMemory (dpy, pub); +} + +void +_XftFontManageMemory (Display *dpy, XftFont *pub) +{ + XftFontInt *font = (XftFontInt *) pub; + + if (font->max_glyph_memory) + { + if (XftDebug() & XFT_DBG_CACHE) + { + if (font->glyph_memory > font->max_glyph_memory) + printf ("Reduce memory for font 0x%lx from %ld to %ld\n", + font->glyphset ? font->glyphset : (unsigned long) font, + font->glyph_memory, font->max_glyph_memory); + } + while (font->glyph_memory > font->max_glyph_memory) + _XftFontUncacheGlyph (dpy, pub); + } + _XftDisplayManageMemory (dpy); +} |