diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-18 17:31:55 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-18 17:31:55 (GMT) |
commit | 39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb (patch) | |
tree | 8e5374666c7f0b3017176ec9d6e6b6eae0dcabac /tk8.6/unix/tkUnixRFont.c | |
parent | 066971b1e6e77991d9161bb0216a63ba94ea04f9 (diff) | |
parent | 6b095f3c8521ca7215e6ff5dcbada52b197ef7d0 (diff) | |
download | blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.zip blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.gz blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.bz2 |
Merge commit '6b095f3c8521ca7215e6ff5dcbada52b197ef7d0' as 'tk8.6'
Diffstat (limited to 'tk8.6/unix/tkUnixRFont.c')
-rw-r--r-- | tk8.6/unix/tkUnixRFont.c | 1202 |
1 files changed, 1202 insertions, 0 deletions
diff --git a/tk8.6/unix/tkUnixRFont.c b/tk8.6/unix/tkUnixRFont.c new file mode 100644 index 0000000..36e5462 --- /dev/null +++ b/tk8.6/unix/tkUnixRFont.c @@ -0,0 +1,1202 @@ +/* + * tkUnixRFont.c -- + * + * Alternate implementation of tkUnixFont.c using Xft. + * + * Copyright (c) 2002-2003 Keith Packard + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkUnixInt.h" +#include "tkFont.h" +#include <X11/Xft/Xft.h> +#include <ctype.h> + +#define MAX_CACHED_COLORS 16 + +typedef struct { + XftFont *ftFont; + XftFont *ft0Font; + FcPattern *source; + FcCharSet *charset; + double angle; +} UnixFtFace; + +typedef struct { + XftColor color; + int next; +} UnixFtColorList; + +typedef struct { + TkFont font; /* Stuff used by generic font package. Must be + * first in structure. */ + UnixFtFace *faces; + int nfaces; + FcFontSet *fontset; + FcPattern *pattern; + + Display *display; + int screen; + XftDraw *ftDraw; + int ncolors; + int firstColor; + UnixFtColorList colors[MAX_CACHED_COLORS]; +} UnixFtFont; + +/* + * Used to describe the current clipping box. Can't be passed normally because + * the information isn't retrievable from the GC. + */ + +typedef struct ThreadSpecificData { + Region clipRegion; /* The clipping region, or None. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * Package initialization: + * Nothing to do here except register the fact that we're using Xft in + * the TIP 59 configuration database. + */ + +#ifndef TCL_CFGVAL_ENCODING +#define TCL_CFGVAL_ENCODING "ascii" +#endif + +void +TkpFontPkgInit( + TkMainInfo *mainPtr) /* The application being created. */ +{ + static Tcl_Config cfg[] = { + { "fontsystem", "xft" }, + { 0,0 } + }; + + Tcl_RegisterConfig(mainPtr->interp, "tk", cfg, TCL_CFGVAL_ENCODING); +} + +static XftFont * +GetFont( + UnixFtFont *fontPtr, + FcChar32 ucs4, + double angle) +{ + int i; + + if (ucs4) { + for (i = 0; i < fontPtr->nfaces; i++) { + FcCharSet *charset = fontPtr->faces[i].charset; + + if (charset && FcCharSetHasChar(charset, ucs4)) { + break; + } + } + if (i == fontPtr->nfaces) { + i = 0; + } + } else { + i = 0; + } + if ((angle == 0.0 && !fontPtr->faces[i].ft0Font) || (angle != 0.0 && + (!fontPtr->faces[i].ftFont || fontPtr->faces[i].angle != angle))){ + FcPattern *pat = FcFontRenderPrepare(0, fontPtr->pattern, + fontPtr->faces[i].source); + double s = sin(angle*PI/180.0), c = cos(angle*PI/180.0); + FcMatrix mat; + XftFont *ftFont; + + /* + * Initialize the matrix manually so this can compile with HP-UX cc + * (which does not allow non-constant structure initializers). [Bug + * 2978410] + */ + + mat.xx = mat.yy = c; + mat.xy = -(mat.yx = s); + + if (angle != 0.0) { + FcPatternAddMatrix(pat, FC_MATRIX, &mat); + } + ftFont = XftFontOpenPattern(fontPtr->display, pat); + if (!ftFont) { + /* + * The previous call to XftFontOpenPattern() should not fail, but + * sometimes does anyway. Usual cause appears to be a + * misconfigured fontconfig installation; see [Bug 1090382]. Try a + * fallback: + */ + + ftFont = XftFontOpen(fontPtr->display, fontPtr->screen, + FC_FAMILY, FcTypeString, "sans", + FC_SIZE, FcTypeDouble, 12.0, + FC_MATRIX, FcTypeMatrix, &mat, + NULL); + } + if (!ftFont) { + /* + * The previous call should definitely not fail. Impossible to + * proceed at this point. + */ + + Tcl_Panic("Cannot find a usable font"); + } + + if (angle == 0.0) { + fontPtr->faces[i].ft0Font = ftFont; + } else { + if (fontPtr->faces[i].ftFont) { + XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont); + } + fontPtr->faces[i].ftFont = ftFont; + fontPtr->faces[i].angle = angle; + } + } + return (angle==0.0? fontPtr->faces[i].ft0Font : fontPtr->faces[i].ftFont); +} + +/* + *--------------------------------------------------------------------------- + * + * GetTkFontAttributes -- + * Fill in TkFontAttributes from an XftFont. + */ + +static void +GetTkFontAttributes( + XftFont *ftFont, + TkFontAttributes *faPtr) +{ + const char *family = "Unknown"; + const char *const *familyPtr = &family; + int weight, slant, size, pxsize; + double ptsize; + + (void) XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr); + if (XftPatternGetDouble(ftFont->pattern, XFT_SIZE, 0, + &ptsize) == XftResultMatch) { + size = (int) ptsize; + } else if (XftPatternGetInteger(ftFont->pattern, XFT_PIXEL_SIZE, 0, + &pxsize) == XftResultMatch) { + size = -pxsize; + } else { + size = 12; + } + if (XftPatternGetInteger(ftFont->pattern, XFT_WEIGHT, 0, + &weight) != XftResultMatch) { + weight = XFT_WEIGHT_MEDIUM; + } + if (XftPatternGetInteger(ftFont->pattern, XFT_SLANT, 0, + &slant) != XftResultMatch) { + slant = XFT_SLANT_ROMAN; + } + +#if DEBUG_FONTSEL + printf("family %s size %d weight %d slant %d\n", + family, size, weight, slant); +#endif /* DEBUG_FONTSEL */ + + faPtr->family = Tk_GetUid(family); + faPtr->size = size; + faPtr->weight = (weight > XFT_WEIGHT_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL; + faPtr->slant = (slant > XFT_SLANT_ROMAN) ? TK_FS_ITALIC : TK_FS_ROMAN; + faPtr->underline = 0; + faPtr->overstrike = 0; +} + +/* + *--------------------------------------------------------------------------- + * + * GetTkFontMetrics -- + * Fill in TkFontMetrics from an XftFont. + */ + +static void +GetTkFontMetrics( + XftFont *ftFont, + TkFontMetrics *fmPtr) +{ + int spacing; + + if (XftPatternGetInteger(ftFont->pattern, XFT_SPACING, 0, + &spacing) != XftResultMatch) { + spacing = XFT_PROPORTIONAL; + } + + fmPtr->ascent = ftFont->ascent; + fmPtr->descent = ftFont->descent; + fmPtr->maxWidth = ftFont->max_advance_width; + fmPtr->fixed = spacing != XFT_PROPORTIONAL; +} + +/* + *--------------------------------------------------------------------------- + * + * InitFont -- + * + * Initializes the fields of a UnixFtFont structure. If fontPtr is NULL, + * also allocates a new UnixFtFont. + * + * Results: + * On error, frees fontPtr and returns NULL, otherwise returns fontPtr. + * + *--------------------------------------------------------------------------- + */ + +static UnixFtFont * +InitFont( + Tk_Window tkwin, + FcPattern *pattern, + UnixFtFont *fontPtr) +{ + FcFontSet *set; + FcCharSet *charset; + FcResult result; + XftFont *ftFont; + int i, iWidth; + + if (!fontPtr) { + fontPtr = ckalloc(sizeof(UnixFtFont)); + } + + FcConfigSubstitute(0, pattern, FcMatchPattern); + XftDefaultSubstitute(Tk_Display(tkwin), Tk_ScreenNumber(tkwin), pattern); + + /* + * Generate the list of fonts + */ + + set = FcFontSort(0, pattern, FcTrue, NULL, &result); + if (!set) { + ckfree(fontPtr); + return NULL; + } + + fontPtr->fontset = set; + fontPtr->pattern = pattern; + fontPtr->faces = ckalloc(set->nfont * sizeof(UnixFtFace)); + fontPtr->nfaces = set->nfont; + + /* + * Fill in information about each returned font + */ + + for (i = 0; i < set->nfont; i++) { + fontPtr->faces[i].ftFont = 0; + fontPtr->faces[i].ft0Font = 0; + fontPtr->faces[i].source = set->fonts[i]; + if (FcPatternGetCharSet(set->fonts[i], FC_CHARSET, 0, + &charset) == FcResultMatch) { + fontPtr->faces[i].charset = FcCharSetCopy(charset); + } else { + fontPtr->faces[i].charset = 0; + } + fontPtr->faces[i].angle = 0.0; + } + + fontPtr->display = Tk_Display(tkwin); + fontPtr->screen = Tk_ScreenNumber(tkwin); + fontPtr->ftDraw = 0; + fontPtr->ncolors = 0; + fontPtr->firstColor = -1; + + /* + * Fill in platform-specific fields of TkFont. + */ + + ftFont = GetFont(fontPtr, 0, 0.0); + fontPtr->font.fid = XLoadFont(Tk_Display(tkwin), "fixed"); + GetTkFontAttributes(ftFont, &fontPtr->font.fa); + GetTkFontMetrics(ftFont, &fontPtr->font.fm); + + /* + * Fontconfig can't report any information about the position or thickness + * of underlines or overstrikes. Thus, we use some defaults that are + * hacked around from backup defaults in tkUnixFont.c, which are in turn + * based on recommendations in the X manual. The comments from that file + * leading to these computations were: + * + * If the XA_UNDERLINE_POSITION property does not exist, the X manual + * recommends using half the descent. + * + * If the XA_UNDERLINE_THICKNESS property does not exist, the X + * manual recommends using the width of the stem on a capital letter. + * I don't know of a way to get the stem width of a letter, so guess + * and use 1/3 the width of a capital I. + * + * Note that nothing corresponding to *either* property is reported by + * Fontconfig at all. [Bug 1961455] + */ + + { + TkFont *fPtr = &fontPtr->font; + + fPtr->underlinePos = fPtr->fm.descent / 2; + Tk_MeasureChars((Tk_Font) fPtr, "I", 1, -1, 0, &iWidth); + fPtr->underlineHeight = iWidth / 3; + if (fPtr->underlineHeight == 0) { + fPtr->underlineHeight = 1; + } + if (fPtr->underlineHeight + fPtr->underlinePos > fPtr->fm.descent) { + fPtr->underlineHeight = fPtr->fm.descent - fPtr->underlinePos; + if (fPtr->underlineHeight == 0) { + fPtr->underlinePos--; + fPtr->underlineHeight = 1; + } + } + } + + return fontPtr; +} + +static void +FinishedWithFont( + UnixFtFont *fontPtr) +{ + Display *display = fontPtr->display; + int i; + Tk_ErrorHandler handler = + Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); + + for (i = 0; i < fontPtr->nfaces; i++) { + if (fontPtr->faces[i].ftFont) { + XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont); + } + if (fontPtr->faces[i].ft0Font) { + XftFontClose(fontPtr->display, fontPtr->faces[i].ft0Font); + } + if (fontPtr->faces[i].charset) { + FcCharSetDestroy(fontPtr->faces[i].charset); + } + } + if (fontPtr->faces) { + ckfree(fontPtr->faces); + } + if (fontPtr->pattern) { + FcPatternDestroy(fontPtr->pattern); + } + if (fontPtr->ftDraw) { + XftDrawDestroy(fontPtr->ftDraw); + } + if (fontPtr->font.fid) { + XUnloadFont(fontPtr->display, fontPtr->font.fid); + } + if (fontPtr->fontset) { + FcFontSetDestroy(fontPtr->fontset); + } + Tk_DeleteErrorHandler(handler); +} + +TkFont * +TkpGetNativeFont( + Tk_Window tkwin, /* For display where font will be used. */ + const char *name) /* Platform-specific font name. */ +{ + UnixFtFont *fontPtr; + FcPattern *pattern; +#if DEBUG_FONTSEL + printf("TkpGetNativeFont %s\n", name); +#endif /* DEBUG_FONTSEL */ + + pattern = XftXlfdParse(name, FcFalse, FcFalse); + if (!pattern) { + return NULL; + } + + /* + * Should also try: pattern = FcNameParse(name); but generic/tkFont.c + * expects TkpGetNativeFont() to only work on XLFD names under Unix. + */ + + fontPtr = InitFont(tkwin, pattern, NULL); + if (!fontPtr) { + FcPatternDestroy(pattern); + return NULL; + } + return &fontPtr->font; +} + +TkFont * +TkpGetFontFromAttributes( + TkFont *tkFontPtr, /* If non-NULL, store the information in this + * existing TkFont structure, rather than + * allocating a new structure to hold the + * font; the existing contents of the font + * will be released. If NULL, a new TkFont + * structure is allocated. */ + Tk_Window tkwin, /* For display where font will be used. */ + const TkFontAttributes *faPtr) + /* Set of attributes to match. */ +{ + XftPattern *pattern; + int weight, slant; + UnixFtFont *fontPtr; + +#if DEBUG_FONTSEL + printf("TkpGetFontFromAttributes %s-%d %d %d\n", faPtr->family, + faPtr->size, faPtr->weight, faPtr->slant); +#endif /* DEBUG_FONTSEL */ + pattern = XftPatternCreate(); + if (faPtr->family) { + XftPatternAddString(pattern, XFT_FAMILY, faPtr->family); + } + if (faPtr->size > 0) { + XftPatternAddDouble(pattern, XFT_SIZE, (double)faPtr->size); + } else if (faPtr->size < 0) { + XftPatternAddInteger(pattern, XFT_PIXEL_SIZE, -faPtr->size); + } else { + XftPatternAddDouble(pattern, XFT_SIZE, 12.0); + } + switch (faPtr->weight) { + case TK_FW_NORMAL: + default: + weight = XFT_WEIGHT_MEDIUM; + break; + case TK_FW_BOLD: + weight = XFT_WEIGHT_BOLD; + break; + } + XftPatternAddInteger(pattern, XFT_WEIGHT, weight); + switch (faPtr->slant) { + case TK_FS_ROMAN: + default: + slant = XFT_SLANT_ROMAN; + break; + case TK_FS_ITALIC: + slant = XFT_SLANT_ITALIC; + break; + case TK_FS_OBLIQUE: + slant = XFT_SLANT_OBLIQUE; + break; + } + XftPatternAddInteger(pattern, XFT_SLANT, slant); + + fontPtr = (UnixFtFont *) tkFontPtr; + if (fontPtr != NULL) { + FinishedWithFont(fontPtr); + } + fontPtr = InitFont(tkwin, pattern, fontPtr); + + /* + * Hack to work around issues with weird issues with Xft/Xrender + * connection. For details, see comp.lang.tcl thread starting from + * <adcc99ed-c73e-4efc-bb5d-e57a57a051e8@l35g2000pra.googlegroups.com> + */ + + if (!fontPtr) { + XftPatternAddBool(pattern, XFT_RENDER, FcFalse); + fontPtr = InitFont(tkwin, pattern, fontPtr); + } + + if (!fontPtr) { + FcPatternDestroy(pattern); + return NULL; + } + + fontPtr->font.fa.underline = faPtr->underline; + fontPtr->font.fa.overstrike = faPtr->overstrike; + return &fontPtr->font; +} + +void +TkpDeleteFont( + TkFont *tkFontPtr) /* Token of font to be deleted. */ +{ + UnixFtFont *fontPtr = (UnixFtFont *) tkFontPtr; + + FinishedWithFont(fontPtr); + /* XXX tkUnixFont.c doesn't free tkFontPtr... */ +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFamilies -- + * + * Return information about the font families that are available on the + * display of the given window. + * + * Results: + * Modifies interp's result object to hold a list of all the available + * font families. + * + *--------------------------------------------------------------------------- + */ + +void +TkpGetFontFamilies( + Tcl_Interp *interp, /* Interp to hold result. */ + Tk_Window tkwin) /* For display to query. */ +{ + Tcl_Obj *resultPtr; + XftFontSet *list; + int i; + + resultPtr = Tcl_NewListObj(0, NULL); + + list = XftListFonts(Tk_Display(tkwin), Tk_ScreenNumber(tkwin), + (char *) 0, /* pattern elements */ + XFT_FAMILY, (char*) 0); /* fields */ + for (i = 0; i < list->nfont; i++) { + char *family, **familyPtr = &family; + + if (XftPatternGetString(list->fonts[i], XFT_FAMILY, 0, familyPtr) + == XftResultMatch) { + Tcl_Obj *strPtr = Tcl_NewStringObj(family, -1); + + Tcl_ListObjAppendElement(NULL, resultPtr, strPtr); + } + } + XftFontSetDestroy(list); + + Tcl_SetObjResult(interp, resultPtr); +} + +/* + *------------------------------------------------------------------------- + * + * TkpGetSubFonts -- + * + * Called by [testfont subfonts] in the Tk testing package. + * + * Results: + * Sets interp's result to a list of the faces used by tkfont + * + *------------------------------------------------------------------------- + */ + +void +TkpGetSubFonts( + Tcl_Interp *interp, + Tk_Font tkfont) +{ + Tcl_Obj *objv[3], *listPtr, *resultPtr; + UnixFtFont *fontPtr = (UnixFtFont *) tkfont; + FcPattern *pattern; + const char *family = "Unknown"; + const char *const *familyPtr = &family; + const char *foundry = "Unknown"; + const char *const *foundryPtr = &foundry; + const char *encoding = "Unknown"; + const char *const *encodingPtr = &encoding; + int i; + + resultPtr = Tcl_NewListObj(0, NULL); + + for (i = 0; i < fontPtr->nfaces ; ++i) { + pattern = FcFontRenderPrepare(0, fontPtr->pattern, + fontPtr->faces[i].source); + + XftPatternGetString(pattern, XFT_FAMILY, 0, familyPtr); + XftPatternGetString(pattern, XFT_FOUNDRY, 0, foundryPtr); + XftPatternGetString(pattern, XFT_ENCODING, 0, encodingPtr); + objv[0] = Tcl_NewStringObj(family, -1); + objv[1] = Tcl_NewStringObj(foundry, -1); + objv[2] = Tcl_NewStringObj(encoding, -1); + listPtr = Tcl_NewListObj(3, objv); + Tcl_ListObjAppendElement(NULL, resultPtr, listPtr); + } + Tcl_SetObjResult(interp, resultPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetFontAttrsForChar -- + * + * Retrieve the font attributes of the actual font used to render a given + * character. + * + *---------------------------------------------------------------------- + */ + +void +TkpGetFontAttrsForChar( + Tk_Window tkwin, /* Window on the font's display */ + Tk_Font tkfont, /* Font to query */ + Tcl_UniChar c, /* Character of interest */ + TkFontAttributes *faPtr) /* Output: Font attributes */ +{ + UnixFtFont *fontPtr = (UnixFtFont *) tkfont; + /* Structure describing the logical font */ + FcChar32 ucs4 = (FcChar32) c; + /* UCS-4 character to map */ + XftFont *ftFont = GetFont(fontPtr, ucs4, 0.0); + /* Actual font used to render the character */ + + GetTkFontAttributes(ftFont, faPtr); + faPtr->underline = fontPtr->font.fa.underline; + faPtr->overstrike = fontPtr->font.fa.overstrike; +} + +int +Tk_MeasureChars( + Tk_Font tkfont, /* Font in which characters will be drawn. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. */ + int numBytes, /* Maximum number of bytes to consider from + * source string. */ + int maxLength, /* If >= 0, maxLength specifies the longest + * permissible line length in pixels; don't + * consider any character that would cross + * this x-position. If < 0, then line length + * is unbounded and the flags argument is + * ignored. */ + int flags, /* Various flag bits OR-ed together: + * TK_PARTIAL_OK means include the last char + * which only partially fit on this line. + * TK_WHOLE_WORDS means stop on a word + * boundary, if possible. TK_AT_LEAST_ONE + * means return at least one character even if + * no characters fit. */ + int *lengthPtr) /* Filled with x-location just after the + * terminating character. */ +{ + UnixFtFont *fontPtr = (UnixFtFont *) tkfont; + XftFont *ftFont; + FcChar32 c; + XGlyphInfo extents; + int clen, curX, newX, curByte, newByte, sawNonSpace; + int termByte = 0, termX = 0; +#if DEBUG_FONTSEL + char string[256]; + int len = 0; +#endif /* DEBUG_FONTSEL */ + + curX = 0; + curByte = 0; + sawNonSpace = 0; + while (numBytes > 0) { + Tcl_UniChar unichar; + + clen = Tcl_UtfToUniChar(source, &unichar); + c = (FcChar32) unichar; + + if (clen <= 0) { + /* + * This can't happen (but see #1185640) + */ + + *lengthPtr = curX; + return curByte; + } + + source += clen; + numBytes -= clen; + if (c < 256 && isspace(c)) { /* I18N: ??? */ + if (sawNonSpace) { + termByte = curByte; + termX = curX; + sawNonSpace = 0; + } + } else { + sawNonSpace = 1; + } + +#if DEBUG_FONTSEL + string[len++] = (char) c; +#endif /* DEBUG_FONTSEL */ + ftFont = GetFont(fontPtr, c, 0.0); + + XftTextExtents32(fontPtr->display, ftFont, &c, 1, &extents); + + newX = curX + extents.xOff; + newByte = curByte + clen; + if (maxLength >= 0 && newX > maxLength) { + if (flags & TK_PARTIAL_OK || + (flags & TK_AT_LEAST_ONE && curByte == 0)) { + curX = newX; + curByte = newByte; + } else if (flags & TK_WHOLE_WORDS && termX != 0) { + curX = termX; + curByte = termByte; + } + break; + } + + curX = newX; + curByte = newByte; + } +#if DEBUG_FONTSEL + string[len] = '\0'; + printf("MeasureChars %s length %d bytes %d\n", string, curX, curByte); +#endif /* DEBUG_FONTSEL */ + *lengthPtr = curX; + return curByte; +} + +int +TkpMeasureCharsInContext( + Tk_Font tkfont, + const char *source, + int numBytes, + int rangeStart, + int rangeLength, + int maxLength, + int flags, + int *lengthPtr) +{ + (void) numBytes; /*unused*/ + + return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength, + maxLength, flags, lengthPtr); +} + +/* + *---------------------------------------------------------------------- + * + * LookUpColor -- + * + * Convert a pixel value to an XftColor. This can be slow due to the + * need to call XQueryColor, which involves a server round-trip. To + * avoid that, a least-recently-used cache of up to MAX_CACHED_COLORS + * is kept, in the form of a linked list. The returned color is moved + * to the front of the list, so repeatedly asking for the same one + * should be fast. + * + * Results: + * A pointer to the XftColor structure for the requested color is + * returned. + * + * Side effects: + * The converted color is stored in a cache in the UnixFtFont structure. The cache + * can hold at most MAX_CACHED_COLORS colors. If no more slots are available, the least + * recently used color is replaced with the new one. + *---------------------------------------------------------------------- + */ + +static XftColor * +LookUpColor(Display *display, /* Display to lookup colors on */ + UnixFtFont *fontPtr, /* Font to search for cached colors */ + unsigned long pixel) /* Pixel value to translate to XftColor */ +{ + int i, last = -1, last2 = -1; + XColor xcolor; + + for (i = fontPtr->firstColor; + i >= 0; last2 = last, last = i, i = fontPtr->colors[i].next) { + + if (pixel == fontPtr->colors[i].color.pixel) { + /* + * Color found in cache. Move it to the front of the list and return it. + */ + if (last >= 0) { + fontPtr->colors[last].next = fontPtr->colors[i].next; + fontPtr->colors[i].next = fontPtr->firstColor; + fontPtr->firstColor = i; + } + + return &fontPtr->colors[i].color; + } + } + + /* + * Color wasn't found, so it needs to be added to the cache. + * If a spare slot is available, it can be put there. If not, last + * will now point to the least recently used color, so replace that one. + */ + + if (fontPtr->ncolors < MAX_CACHED_COLORS) { + last2 = -1; + last = fontPtr->ncolors++; + } + + /* + * Translate the pixel value to a color. Needs a server round-trip. + */ + xcolor.pixel = pixel; + XQueryColor(display, DefaultColormap(display, fontPtr->screen), &xcolor); + + fontPtr->colors[last].color.color.red = xcolor.red; + fontPtr->colors[last].color.color.green = xcolor.green; + fontPtr->colors[last].color.color.blue = xcolor.blue; + fontPtr->colors[last].color.color.alpha = 0xffff; + fontPtr->colors[last].color.pixel = pixel; + + /* + * Put at the front of the list. + */ + if (last2 >= 0) { + fontPtr->colors[last2].next = fontPtr->colors[last].next; + } + fontPtr->colors[last].next = fontPtr->firstColor; + fontPtr->firstColor = last; + + return &fontPtr->colors[last].color; +} + +#define NUM_SPEC 1024 + +void +Tk_DrawChars( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context for drawing characters. */ + Tk_Font tkfont, /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + int x, int y) /* Coordinates at which to place origin of + * string when drawing. */ +{ + const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */ + UnixFtFont *fontPtr = (UnixFtFont *) tkfont; + XGCValues values; + XftColor *xftcolor; + int clen, nspec, xStart = x; + XftGlyphFontSpec specs[NUM_SPEC]; + XGlyphInfo metrics; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (fontPtr->ftDraw == 0) { +#if DEBUG_FONTSEL + printf("Switch to drawable 0x%x\n", drawable); +#endif /* DEBUG_FONTSEL */ + fontPtr->ftDraw = XftDrawCreate(display, drawable, + DefaultVisual(display, fontPtr->screen), + DefaultColormap(display, fontPtr->screen)); + } else { + Tk_ErrorHandler handler = + Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); + + XftDrawChange(fontPtr->ftDraw, drawable); + Tk_DeleteErrorHandler(handler); + } + XGetGCValues(display, gc, GCForeground, &values); + xftcolor = LookUpColor(display, fontPtr, values.foreground); + if (tsdPtr->clipRegion != None) { + XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion); + } + nspec = 0; + while (numBytes > 0 && x <= maxCoord && y <= maxCoord) { + XftFont *ftFont; + FcChar32 c; + + clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes); + if (clen <= 0) { + /* + * This should not happen, but it can. + */ + + goto doUnderlineStrikeout; + } + source += clen; + numBytes -= clen; + + ftFont = GetFont(fontPtr, c, 0.0); + if (ftFont) { + specs[nspec].font = ftFont; + specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c); + specs[nspec].x = x; + specs[nspec].y = y; + XftGlyphExtents(fontPtr->display, ftFont, &specs[nspec].glyph, 1, + &metrics); + x += metrics.xOff; + y += metrics.yOff; + nspec++; + if (nspec == NUM_SPEC) { + XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, + specs, nspec); + nspec = 0; + } + } + } + if (nspec) { + XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec); + } + + doUnderlineStrikeout: + if (tsdPtr->clipRegion != None) { + XftDrawSetClip(fontPtr->ftDraw, None); + } + if (fontPtr->font.fa.underline != 0) { + XFillRectangle(display, drawable, gc, xStart, + y + fontPtr->font.underlinePos, (unsigned) (x - xStart), + (unsigned) fontPtr->font.underlineHeight); + } + if (fontPtr->font.fa.overstrike != 0) { + y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10; + XFillRectangle(display, drawable, gc, xStart, y, + (unsigned) (x - xStart), + (unsigned) fontPtr->font.underlineHeight); + } +} + +/* + *--------------------------------------------------------------------------- + * + * TkDrawAngledChars -- + * + * Draw some characters at an angle. This would be simple code, except + * Xft has bugs with cumulative errors in character positioning which are + * caused by trying to perform all calculations internally with integers. + * So we have to do the work ourselves with floating-point math. + * + * Results: + * None. + * + * Side effects: + * Target drawable is updated. + * + *--------------------------------------------------------------------------- + */ + +void +TkDrawAngledChars( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context for drawing characters. */ + Tk_Font tkfont, /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + double x, double y, /* Coordinates at which to place origin of + * string when drawing. */ + double angle) /* What angle to put text at, in degrees. */ +{ + const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */ + const int minCoord = -1000; /* Should be good enough... */ + UnixFtFont *fontPtr = (UnixFtFont *) tkfont; + XGCValues values; + XftColor *xftcolor; + int xStart = x, yStart = y; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); +#ifdef XFT_HAS_FIXED_ROTATED_PLACEMENT + int clen, nglyph; + FT_UInt glyphs[NUM_SPEC]; + XGlyphInfo metrics; + XftFont *currentFtFont; + int originX, originY; + + if (fontPtr->ftDraw == 0) { +#if DEBUG_FONTSEL + printf("Switch to drawable 0x%x\n", drawable); +#endif /* DEBUG_FONTSEL */ + fontPtr->ftDraw = XftDrawCreate(display, drawable, + DefaultVisual(display, fontPtr->screen), + DefaultColormap(display, fontPtr->screen)); + } else { + Tk_ErrorHandler handler = + Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); + + XftDrawChange(fontPtr->ftDraw, drawable); + Tk_DeleteErrorHandler(handler); + } + + XGetGCValues(display, gc, GCForeground, &values); + xftcolor = LookUpColor(display, fontPtr, values.foreground); + if (tsdPtr->clipRegion != None) { + XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion); + } + + nglyph = 0; + currentFtFont = NULL; + originX = originY = 0; /* lint */ + + while (numBytes > 0 && x <= maxCoord && x >= minCoord && y <= maxCoord + && y >= minCoord) { + XftFont *ftFont; + FcChar32 c; + + clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes); + if (clen <= 0) { + /* + * This should not happen, but it can. + */ + + goto doUnderlineStrikeout; + } + source += clen; + numBytes -= clen; + + ftFont = GetFont(fontPtr, c, angle); + if (!ftFont) { + continue; + } + + if (ftFont != currentFtFont || nglyph == NUM_SPEC) { + if (nglyph) { + /* + * We pass multiple glyphs at once to enable the code to + * perform better rendering of sub-pixel inter-glyph spacing. + * If only the current Xft implementation could make use of + * this information... but we'll be ready when it does! + */ + + XftDrawGlyphs(fontPtr->ftDraw, xftcolor, currentFtFont, + originX, originY, glyphs, nglyph); + } + originX = ROUND16(x); + originY = ROUND16(y); + if (nglyph) { + XftGlyphExtents(fontPtr->display, currentFtFont, glyphs, + nglyph, &metrics); + nglyph = 0; + x += metrics.xOff; + y += metrics.yOff; + } + currentFtFont = ftFont; + } + glyphs[nglyph++] = XftCharIndex(fontPtr->display, ftFont, c); + } + if (nglyph) { + XftDrawGlyphs(fontPtr->ftDraw, xftcolor, currentFtFont, + originX, originY, glyphs, nglyph); + } +#else /* !XFT_HAS_FIXED_ROTATED_PLACEMENT */ + int clen, nspec; + XftGlyphFontSpec specs[NUM_SPEC]; + XGlyphInfo metrics; + double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0); + + if (fontPtr->ftDraw == 0) { +#if DEBUG_FONTSEL + printf("Switch to drawable 0x%x\n", drawable); +#endif /* DEBUG_FONTSEL */ + fontPtr->ftDraw = XftDrawCreate(display, drawable, + DefaultVisual(display, fontPtr->screen), + DefaultColormap(display, fontPtr->screen)); + } else { + Tk_ErrorHandler handler = + Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); + + XftDrawChange(fontPtr->ftDraw, drawable); + Tk_DeleteErrorHandler(handler); + } + XGetGCValues(display, gc, GCForeground, &values); + xftcolor = LookUpColor(display, fontPtr, values.foreground); + if (tsdPtr->clipRegion != None) { + XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion); + } + nspec = 0; + while (numBytes > 0 && x <= maxCoord && x >= minCoord + && y <= maxCoord && y >= minCoord) { + XftFont *ftFont, *ft0Font; + FcChar32 c; + + clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes); + if (clen <= 0) { + /* + * This should not happen, but it can. + */ + + goto doUnderlineStrikeout; + } + source += clen; + numBytes -= clen; + + ftFont = GetFont(fontPtr, c, angle); + ft0Font = GetFont(fontPtr, c, 0.0); + if (ftFont && ft0Font) { + specs[nspec].font = ftFont; + specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c); + specs[nspec].x = ROUND16(x); + specs[nspec].y = ROUND16(y); + XftGlyphExtents(fontPtr->display, ft0Font, &specs[nspec].glyph, 1, + &metrics); + x += metrics.xOff*cosA + metrics.yOff*sinA; + y += metrics.yOff*cosA - metrics.xOff*sinA; + nspec++; + if (nspec == NUM_SPEC) { + XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, + specs, nspec); + nspec = 0; + } + } + } + if (nspec) { + XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec); + } +#endif /* XFT_HAS_FIXED_ROTATED_PLACEMENT */ + + doUnderlineStrikeout: + if (tsdPtr->clipRegion != None) { + XftDrawSetClip(fontPtr->ftDraw, None); + } + if (fontPtr->font.fa.underline || fontPtr->font.fa.overstrike) { + XPoint points[5]; + double width = (x - xStart) * cosA + (yStart - y) * sinA; + double barHeight = fontPtr->font.underlineHeight; + double dy = fontPtr->font.underlinePos; + + if (fontPtr->font.fa.underline != 0) { + if (fontPtr->font.underlineHeight == 1) { + dy++; + } + points[0].x = xStart + ROUND16(dy*sinA); + points[0].y = yStart + ROUND16(dy*cosA); + points[1].x = xStart + ROUND16(dy*sinA + width*cosA); + points[1].y = yStart + ROUND16(dy*cosA - width*sinA); + if (fontPtr->font.underlineHeight == 1) { + XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); + } else { + points[2].x = xStart + ROUND16(dy*sinA + width*cosA + + barHeight*sinA); + points[2].y = yStart + ROUND16(dy*cosA - width*sinA + + barHeight*cosA); + points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA); + points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA); + points[4].x = points[0].x; + points[4].y = points[0].y; + XFillPolygon(display, drawable, gc, points, 5, Complex, + CoordModeOrigin); + XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); + } + } + if (fontPtr->font.fa.overstrike != 0) { + dy = -fontPtr->font.fm.descent + - (fontPtr->font.fm.ascent) / 10; + points[0].x = xStart + ROUND16(dy*sinA); + points[0].y = yStart + ROUND16(dy*cosA); + points[1].x = xStart + ROUND16(dy*sinA + width*cosA); + points[1].y = yStart + ROUND16(dy*cosA - width*sinA); + if (fontPtr->font.underlineHeight == 1) { + XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); + } else { + points[2].x = xStart + ROUND16(dy*sinA + width*cosA + + barHeight*sinA); + points[2].y = yStart + ROUND16(dy*cosA - width*sinA + + barHeight*cosA); + points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA); + points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA); + points[4].x = points[0].x; + points[4].y = points[0].y; + XFillPolygon(display, drawable, gc, points, 5, Complex, + CoordModeOrigin); + XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); + } + } + } +} + +void +TkUnixSetXftClipRegion( + TkRegion clipRegion) /* The clipping region to install. */ +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + tsdPtr->clipRegion = (Region) clipRegion; +} + +/* + * Local Variables: + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |