/* * Copyright (C) 1997 and 1998 WIDE Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: font.c,v 1.26 2000/12/04 09:21:27 nishida Exp $ */ #include "mgp.h" #ifdef VFLIB #define VF_DESCENT 8 int vfcachesize = 3000; /*XXX*/ #define VFHASH_SIZE 255 static char *vf_curname; static int vfont_fd = -1; static int vfcinitdone; static int vfcachecnt; static struct vfont vfclru; static struct vfont vfcache[VFHASH_SIZE]; #define VFCACHE_HASH(w, h, c) (((c ^ w) ^ h) % VFHASH_SIZE) int vfcachehit = 0; int vfcachemiss = 0; static void initrevbitmap __P((void)); /* * revbitmap: reverse the MSB and LSB of a byte value. * example: revbitmap[0x80] == 0x01, revbitmap[0x55] == 0xaa * leftbitmap: the index of leftmost bit in a byte value. (MSB is on the left) * example: leftbitmap[0x80] == 0, leftbitmap[0x19] == 3 * rightbitmap: the index of rightmost bit in a byte value. * example: rightbitmap[0x80] == 0, rightbitmap[0x19] == 7 */ static u_char *revbitmap = NULL; static char *leftbitmap = NULL; static char *rightbitmap = NULL; static short ctable[128] = { 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x212a, 0x216d, 0x2174, 0x2170, 0x2173, 0x2175, 0x216c, 0x214a, 0x214b, 0x2176, 0x215c, 0x2124, 0x215d, 0x2125, 0x213f, 0x2330, 0x2331, 0x2332, 0x2333, 0x2334, 0x2335, 0x2336, 0x2337, 0x2338, 0x2339, 0x2127, 0x2128, 0x2152, 0x2161, 0x2153, 0x2129, 0x2177, 0x2341, 0x2342, 0x2343, 0x2344, 0x2345, 0x2346, 0x2347, 0x2348, 0x2349, 0x234a, 0x234b, 0x234c, 0x234d, 0x234e, 0x234f, 0x2350, 0x2351, 0x2352, 0x2353, 0x2354, 0x2355, 0x2356, 0x2357, 0x2358, 0x2359, 0x235a, 0x214e, 0x2140, 0x214f, 0x2130, 0x2132, 0x212e, 0x2361, 0x2362, 0x2363, 0x2364, 0x2365, 0x2366, 0x2367, 0x2368, 0x2369, 0x236a, 0x236b, 0x236c, 0x236d, 0x236e, 0x236f, 0x2370, 0x2371, 0x2372, 0x2373, 0x2374, 0x2375, 0x2376, 0x2377, 0x2378, 0x2379, 0x237a, 0x2150, 0x2143, 0x2151, 0x2141, 0x2121, }; static void initrevbitmap() { int i; int j; u_char x; int right; int left; revbitmap = (u_char *)malloc(256 * sizeof(u_char)); leftbitmap= (char *)malloc(256 * sizeof(char)); rightbitmap= (char *)malloc(256 * sizeof(char)); if (!revbitmap || !leftbitmap || !rightbitmap) { fprintf(stderr, "initrevbitmap: malloc failed\n"); cleanup(-1); } for (i = 0; i < 256; i++) { right = left = -1; x = 0x00; for (j = 0; j < 8; j++) { if (i & (0x80 >> j)) { x |= (1 << j); right = j; if (left == -1) left = j; } } revbitmap[i] = x; rightbitmap[i] = right; leftbitmap[i] = left; } } #define VFC_INSHASH(vfc) { \ struct vfont *h, *n; \ h = &vfcache[VFCACHE_HASH(vfc->code, vfc->width, vfc->height)]; \ n = h->next; vfc->next = n; n->prev = vfc; \ h->next = vfc; vfc->prev = h; \ } #define VFC_DELHASH(vfc) { \ struct vfont *n, *p; \ n = vfc->next; p = vfc->prev; \ n->prev = p; p->next = n; \ } #define VFC_INSLRU(vfc) { \ struct vfont *p; \ p = vfclru.lruprev; vfc->lruprev = p; p->lrunext = vfc; \ vfclru.lruprev = vfc; vfc->lrunext = &vfclru; \ } #define VFC_DELLRU(vfc) { \ struct vfont *n, *p; \ n = vfc->lrunext; p = vfc->lruprev; \ n->lruprev = p; p->lrunext = n; \ } static void vfc_init() { int i; struct vfont *vfc; vfc = vfcache; for (vfc = vfcache, i = 0; i < VFHASH_SIZE; vfc++, i++) vfc->next = vfc->prev = vfc; vfclru.lrunext = vfclru.lruprev = &vfclru; vfcinitdone++; } static void vfc_free(vfc) struct vfont *vfc; { VFC_DELHASH(vfc); VFC_DELLRU(vfc); free(vfc->dbitmap); free(vfc); vfcachecnt--; } static struct vfont * vfc_lookup(code, width, height, fontname) u_int code, width, height; char *fontname; { struct vfont *vfc, *hvfc; hvfc = &vfcache[VFCACHE_HASH(code, width, height)]; for (vfc = hvfc->next; vfc != hvfc; vfc = vfc->next) { if (vfc->code == code && vfc->width == width && vfc->height == height && vfc->fontname == fontname) { vfcachehit++; VFC_DELLRU(vfc); VFC_INSLRU(vfc); return vfc; } } vfcachemiss++; return NULL; } static struct vfont * vfc_alloc(code, width, height, fontname) u_int code, width, height; char *fontname; { u_int rast = (width + 7) / 8; u_int rast2 = (width * 2 + 7) / 8; u_char *lbitmap; u_char *dbitmap; u_int char_len; struct vfont *vfc; u_int max_x, min_x; u_int ix, iy; u_char data, *bitmap; u_char *s; u_int bsize; u_int vcode; XImage *xim; bsize = rast * height; if (code < 128) vcode = ctable[code]; else vcode = code; vfc = (struct vfont *)malloc(sizeof(*vfc)); if (vfc == NULL) { fprintf(stderr, "vfc_alloc: malloc failed\n"); cleanup(-1); } vfc->code = code; vfc->width = width; vfc->height = height; vfc->fontname = vf_curname; vfc->ref = 0; bitmap = (u_char *)malloc(bsize); if (bitmap == NULL) { fprintf(stderr, "vfc_alloc: malloc failed\n"); cleanup(-1); } #if 1 /* ANTIALIAS */ /*XXX: TODO: cleanup, optimization */ lbitmap = (u_char *)malloc(rast2 * height * 2); dbitmap = (u_char *)malloc(width * height); if (lbitmap == NULL || dbitmap == NULL) { fprintf(stderr, "vfc_alloc: malloc failed\n"); cleanup(-1); } memset(lbitmap, 0, rast2 * height * 2); VF_GetBitmap(vcode, vfont_fd, width * 2, height * 2, rast2, 0, lbitmap); { int i, j, x1, x2, y, b1, b2; u_char *s1, *s2, *d, *z; static u_char dp[] = { 0, 1, 1, 2 }; z = dbitmap; d = bitmap; for (j = 0; j < height; j++) { s1 = lbitmap + rast2 * (j * 2); s2 = s1 + rast2; y = 0; for (i = 0, b1 = 7, b2 = 6; i < width; i++, b1--, b2 -= 2) { if (b1 < 0) { *d++ = y; y = 0; b1 = 7; } if (b2 < 0) { s1++, s2++; b2 = 6; } x1 = (*s1 >> b2) & 0x03; x2 = (*s2 >> b2) & 0x03; if (x1 | x2) y |= 1 << b1; *z++ = dp[x1] + dp[x2]; } *d++ = y; for (i += 2; i < rast * 2; i++) *d++ = 0; } } free(lbitmap); vfc->dbitmap = dbitmap; #else memset(bitmap, 0, bsize); if (!(mgp_flag & FL_OUTLINE)) { VF_GetBitmap(vcode, vfont_fd, width, height, rast, 0, bitmap); } else { /* OR */ u_long *vfdata; if ((vfdata = VF_GetOutline(vcode, vfont_fd)) == NULL) { fprintf(stderr, "Failed to get outline : %04x\n", vcode); } else { VF_DrawOutline(vfdata, vfont_fd, width, height, rast, 0, bitmap); VF_FreeOutline(vfdata, vfont_fd); } } #endif /*ANTIALIAS*/ if (!revbitmap) initrevbitmap(); /* compute right/left boundary */ max_x = -1; min_x = -1; for (ix = 0; ix < rast; ix++) { data = 0x00; s = bitmap + ix; for (iy = 0, s = bitmap + ix; iy < height; iy++, s += rast) data |= *s; if (data) { max_x = ix * 8 + rightbitmap[data]; if (min_x == -1) min_x = ix * 8 + leftbitmap[data]; } } /* XXX: ".," ... */ if (code >= 0x2121 && code <= 0x2125 && max_x < width / 2) max_x += width / 2; #if 0 /* remove soon */ xim = XCreateImage(display, visual, 1, XYBitmap, 0, NULL, width, height, 8, 0); if (xim == NULL) { fprintf(stderr, "vfc_alloc: XCreateImage failed\n"); cleanup(-1); } if (xim->bitmap_bit_order == LSBFirst) { /* convert the bit order of image bitmap */ for (iy = 0, s = bitmap; iy < height; iy++) { for (ix = 0; ix < rast; ix++, s++) *s = revbitmap[*s]; } } xim->data = (char *)bitmap; vfc->image = xim; #endif /* horizontal gap of 1 dot is included by default */ char_len = max_x - min_x + 2; vfc->xoff = min_x; vfc->charlen = vfc->xmax = char_len; vfc->descent = height / VF_DESCENT; vfc->ascent = vfc->height - vfc->descent; VFC_INSHASH(vfc); VFC_INSLRU(vfc); vfcachecnt++; return vfc; } struct vfont * vfc_get(code, width, height, force) u_int code, width, height; int force; { struct vfont *vfc, *nvfc; /* sanity check */ if (vf_curname == NULL) return NULL; if (!vfcinitdone) vfc_init(); vfc = vfc_lookup(code, width, height, vf_curname); if (vfc == NULL) { if (vfcachecnt >= vfcachesize) { if (!force) return NULL; vfc = vfclru.lrunext; while (vfcachecnt >= vfcachesize) { if (vfc == &vfclru) break; nvfc = vfc->lrunext; if (vfc->ref == 0) vfc_free(vfc); vfc = nvfc; } } vfc = vfc_alloc(code, width, height, vf_curname); } #if 0 fprintf(stderr, "vfc_get: cache=%d, hit=%d, miss=%d\n", vfcachecnt, vfcachehit, vfcachemiss); #endif return vfc; } void vfc_setfont(fontname) char *fontname; { static char *prev_vfcap; if ((mgp_flag & FL_NOVFLIB)) { return; } if (prev_vfcap != vfcap_name) { /* * Because the string specified by %vfcap is stored in * allocated area and never freed, string compare is not * needed here. */ if (prev_vfcap) VF_Deinit(); if (VF_Init(vfcap_name) < 0) { fprintf(stderr, "Error VF_Init\n"); cleanup(-1); } prev_vfcap = vfcap_name; if (prev_vfcap == NULL) prev_vfcap = ""; /* XXX: just in case */ } #if 0 if (vfont_fd >= 0) VF_CloseFont(vfont_fd); #endif vfont_fd = VF_OpenFont(fontname); vf_curname = fontname; if (vfont_fd < 0) { fprintf(stderr, "Can't open vffont %s (vfcap=%s)\n", fontname, vfcap_name); cleanup(-1); } } XImage * vfc_image(vfc, fore, back, xim, bx, by) struct vfont *vfc; long fore, back; XImage *xim; u_int bx, by; { int x, y; int maxx; static XColor col[5]; u_char d, *s; u_long p; XColor *bcol = NULL, *bc; u_int charlen; /* offset the base position */ by -= vfc->ascent; charlen = vfc->charlen; if (vfc->xoff + charlen > vfc->width) charlen = vfc->width - vfc->xoff; if (back == -1) { u_long r, g, b; int dots; dots = charlen * vfc->height; bcol = malloc(sizeof(XColor) * dots); if (bcol == NULL) return NULL; bc = bcol; for (y = 0; y < vfc->height; y++) { for (x = 0; x < charlen; x++, bc++) { bc->pixel = XGetPixel(xim, bx+x, by+y); bc->flags = DoRed|DoGreen|DoBlue; } } XQueryColors(display, colormap, bcol, dots); r = g = b = 0; for (y = 0; y < vfc->height; y++) { for (x = 0; x < charlen; x++) { r += bcol[x].red; g += bcol[x].green; b += bcol[x].blue; } } r /= dots; g /= dots; b /= dots; bc = &col[0]; if (bc->red == r && bc->green == g && bc->blue == b) bc->pixel = back; else { bc->pixel = ~back; bc->red = r; bc->green = g; bc->blue = b; } } if (fore != col[4].pixel || back != col[0].pixel) { col[4].pixel = fore; col[4].flags = DoRed|DoGreen|DoBlue; if (back != -1) { col[3].pixel = back; col[3].flags = DoRed|DoGreen|DoBlue; XQueryColors(display, colormap, &col[3], 2); col[0] = col[3]; } else { XQueryColor(display, colormap, &col[4]); } for (x = 3; x > 0; x--) { col[x].red = (col[4].red *x + col[0].red *(4-x)) /4; col[x].green = (col[4].green*x + col[0].green*(4-x)) /4; col[x].blue = (col[4].blue *x + col[0].blue *(4-x)) /4; if (!XAllocColor(display, colormap, &col[x])) { if (verbose) printf("vfc_image: cannot allocate color for level %d (using %d)\n", x, x + 1); col[x].pixel = col[x + 1].pixel; } else regist_alloc_colors(&font_clr, &col[x].pixel, 1); } } /* XXX: need optimization */ s = vfc->dbitmap; bc = bcol; maxx = bx + charlen; for (y = by; y < vfc->height + by; y++) { s += vfc->xoff; for (x = bx; x < maxx; x++) { if ((d = *s++)) { p = col[d].pixel; XPutPixel(xim, x, y, p); } } s += vfc->width - (vfc->xoff + charlen); } if (mgp_flag & FL_GLYPHEDGE) { /* * for debugging treatment of font metrics, for 16bpp displays */ /* pixmap bounding box */ for (y = 0; y < vfc->height; y++) { XPutPixel(xim, bx, by + y, 0xffff); XPutPixel(xim, bx + charlen - 1, by + y, 0xffff); } for (x = 0; x < charlen; x++) { XPutPixel(xim, bx + x, by, 0xffff); XPutPixel(xim, bx + x, by + vfc->height - 1, 0xffff); } /* origin */ XPutPixel(xim, bx, by, 0xaaaa); /* baseline */ for (x = 0; x < vfc->width; x++) XPutPixel(xim, bx + vfc->xoff + x, by, 0xaaaa); } if (bcol) free(bcol); return xim; } #endif /* VFLIB */