1 /* aafont.c - generic library for drawing anti-aliased fonts
2    Copyright (C) 1996-2017 Paul Sheer
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307, USA.
18  */
19 
20 #include <config.h>
21 #ifdef HAVE_STDLIB_H
22 #include <stdlib.h>
23 #endif
24 #ifdef HAVE_STRING_H
25 #include <string.h>
26 #endif
27 #include <stdio.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include "aafont.h"
31 #include "mad.h"
32 
33 
34 
35 /*
36  * list management
37  * ---------------
38  *
39  * Situation is a list of aa fonts:
40  * Each font is uniquified by a fg colour, a bg colour and a fontid.
41  * Each font has 65536 pixmap glyphs.
42  * The 65536 pixmaps are divided into 256 blocks.
43  * Each block is allocated on demand only.
44  * We *ImageString* only i.e. no DrawString - since requires knowledge
45  *                             of the background which is complicated.
46  */
47 
48 Display *aa_display;
49 int aa_depth;
50 Window aa_root;
51 Visual *aa_visual;
52 
53 int option_rgb_order = RedFirst;
54 int option_interchar_spacing = 0;
55 
XAaInit(Display * display,Visual * visual,int depth,Window root)56 void XAaInit (Display * display, Visual * visual, int depth, Window root)
57 {
58     aa_display = display;
59     aa_depth = depth;
60     aa_root = root;
61     aa_visual = visual;
62 }
63 
64 struct aa_glyph_cache {
65     Pixmap pixmap;
66     int width;
67 };
68 
69 struct aa_font_cache {
70     XFontStruct *font_struct;
71     GC gc;
72     unsigned long fg;
73     unsigned long bg;
74     struct aa_glyph_cache *glyph[256];
75     int num_pixmaps;
76     struct aa_font_cache *next;
77 } *font_cache_list = 0;
78 
aa_insert(void)79 static void aa_insert (void)
80 {
81     struct aa_font_cache *p;
82     p = malloc (sizeof (*font_cache_list));
83     memset (p, 0, sizeof (*font_cache_list));
84     if (!font_cache_list) {
85 	font_cache_list = p;
86     } else {
87 	p->next = font_cache_list;
88 	font_cache_list = p;
89     }
90 }
91 
aa_free(struct aa_font_cache * f)92 static void aa_free (struct aa_font_cache *f)
93 {
94     int i, j;
95     XFreeFontInfo (0, f->font_struct, 0);
96     for (i = 0; i < 256; i++) {
97 	if (f->glyph[i]) {
98 	    for (j = 0; j < 256; j++)
99 		if (f->glyph[i][j].pixmap)
100 		    XFreePixmap (aa_display, f->glyph[i][j].pixmap);
101 	    memset (f->glyph[i], 0, 256 * sizeof (struct aa_glyph_cache));
102 	    free (f->glyph[i]);
103 	}
104     }
105     memset (f, 0, sizeof (*f));
106     free (f);
107 }
108 
109 /* passing fg == bg == 0 finds any fid */
aa_find(Font fid,unsigned long fg,unsigned long bg)110 static struct aa_font_cache *aa_find (Font fid, unsigned long fg, unsigned long bg)
111 {
112     struct aa_font_cache *p;
113     for (p = font_cache_list; p; p = p->next)
114 	if (fid && p->font_struct->fid == fid && p->fg == fg && p->bg == bg)
115 	    return p;
116     return 0;
117 }
118 
119 /* returns zero on not found */
_aa_remove(Font fid)120 static int _aa_remove (Font fid)
121 {
122     struct aa_font_cache *p, *q = 0;
123     for (p = font_cache_list; p; p = p->next) {
124 	if (fid && p->font_struct->fid == fid) {
125 	    if (p == font_cache_list) {
126 		struct aa_font_cache *t;
127 		t = font_cache_list->next;
128 		aa_free (font_cache_list);
129 		font_cache_list = t;
130 		return 1;
131 	    } else {
132 		q->next = p->next;
133 		aa_free (p);
134 		return 1;
135 	    }
136 	}
137 	q = p;
138     }
139     return 0;
140 }
141 
aa_remove(Font fid)142 static void aa_remove (Font fid)
143 {
144     while (_aa_remove (fid));
145 }
146 
147 
148 /* fifth level */
149 /* 5 by 9/3 guassian convolution */
aa_convolve(int i,int j,unsigned char * source,int source_bytes_per_line,int byte_order,int bytes_per_pixel,int rgb_order,int red_shift,int green_shift,int blue_shift,int red_mask,int green_mask,int blue_mask)150 static unsigned long aa_convolve (int i, int j, unsigned char *source, int source_bytes_per_line,
151 				  int byte_order, int bytes_per_pixel, int rgb_order, int red_shift,
152 				  int green_shift, int blue_shift, int red_mask, int green_mask,
153 				  int blue_mask)
154 {
155     unsigned long red, green, blue;
156 #include "conv.c"
157     red /= (256 * 3);
158     green /= (256 * 3);
159     blue /= (256 * 3);
160     return (red << red_shift) | (green << green_shift) | (blue << blue_shift);
161 }
162 
163 /* fourth level */
aa_shrink_pixmap(struct aa_font_cache * f,Pixmap pixmap,int width,int height,int * width_return)164 static Pixmap aa_shrink_pixmap (struct aa_font_cache *f, Pixmap pixmap, int width, int height,
165 				int *width_return)
166 {
167     XImage *image, *shrunk;
168     int i, j, w, h, bytes_per_pixel;
169     int red_shift, green_shift, blue_shift;
170     unsigned long red_mask, green_mask, blue_mask;
171 
172 /* create an image to put the enlarged glyph into - make it slightly large to hold
173    the diameter of the 5x9 guassian as well as a one pixel enlargement and rounding error */
174     image =
175 	XCreateImage (aa_display, aa_visual, aa_depth, ZPixmap, 0, 0, width + 4 + X_ENLARGEMENT + option_interchar_spacing * 3,
176 		      height + 8 + Y_ENLARGEMENT, 8, 0);
177     bytes_per_pixel = image->bytes_per_line / image->width;
178     image->data = (char *) malloc (image->bytes_per_line * image->height);
179 
180     for (i = 0; i < width + 4 + X_ENLARGEMENT + option_interchar_spacing; i++)
181 	XPutPixel (image, i, 0, f->bg);
182     for (j = 0; j < height + 8 + Y_ENLARGEMENT; j++)
183 	memcpy (image->data + image->bytes_per_line * j, image->data, image->bytes_per_line);
184 
185 /* create an image to put the reduced glyph into. round w and h up */
186     *width_return = w = SHRINK_WIDTH (width);
187     h = SHRINK_HEIGHT (height);
188     shrunk = XCreateImage (aa_display, aa_visual, aa_depth, ZPixmap, 0, 0, w, h, 8, 0);
189     shrunk->data = (char *) malloc (shrunk->bytes_per_line * h);
190 
191     for (red_mask = image->red_mask, red_shift = 0; red_shift < 32 && !(red_mask & 1);
192 	 red_shift++, red_mask >>= 1);
193     for (green_mask = image->green_mask, green_shift = 0; green_shift < 32 && !(green_mask & 1);
194 	 green_shift++, green_mask >>= 1);
195     for (blue_mask = image->blue_mask, blue_shift = 0; blue_shift < 32 && !(blue_mask & 1);
196 	 blue_shift++, blue_mask >>= 1);
197 
198     XGetSubImage (aa_display, pixmap, 0, 0, width, height,
199 		  image->red_mask | image->green_mask | image->blue_mask, ZPixmap, image, 2, 4);
200 
201     for (i = 0; i < w; i++) {
202 	for (j = 0; j < h; j++) {
203 	    unsigned long pixel;
204 	    pixel =
205 		aa_convolve (i * 3, j * 3,
206 			     (unsigned char *) image->data + bytes_per_pixel * 2 +
207 			     image->bytes_per_line * 4, image->bytes_per_line, image->byte_order,
208 			     bytes_per_pixel, option_rgb_order, red_shift, green_shift, blue_shift,
209 			     red_mask, green_mask, blue_mask);
210 	    XPutPixel (shrunk, i, j, pixel);
211 	}
212     }
213 
214     pixmap = XCreatePixmap (aa_display, aa_root, w, h, aa_depth);
215     XPutImage (aa_display, pixmap, f->gc, shrunk, 0, 0, 0, 0, w, h);
216     free (image->data);
217     image->data = 0;
218     XDestroyImage (image);
219     free (shrunk->data);
220     shrunk->data = 0;
221     XDestroyImage (shrunk);
222     return pixmap;
223 }
224 
225 /* third level */
aa_create_pixmap(struct aa_font_cache * f,int j,int i,int * width)226 static Pixmap aa_create_pixmap (struct aa_font_cache *f, int j, int i, int *width)
227 {
228     Pixmap w, r;
229     int direction, ascent, descent, height;
230     XCharStruct ch;
231     XChar2b c;
232     c.byte1 = j;
233     c.byte2 = i;
234     XTextExtents16 (f->font_struct, &c, 1, &direction, &ascent, &descent, &ch);
235     height = f->font_struct->ascent + f->font_struct->descent;
236     w = XCreatePixmap (aa_display, aa_root, ch.width, height, aa_depth);
237 /* cheapest way to clear the background */
238     XDrawImageString (aa_display, w, f->gc, 0, f->font_struct->ascent, "     ", 5);
239 /* needed to clear the background if the function fails on non-existing chars */
240     XDrawImageString16 (aa_display, w, f->gc, 0, f->font_struct->ascent, &c, 1);
241     r = aa_shrink_pixmap (f, w, ch.width, height, width);
242     XFreePixmap (aa_display, w);
243     return r;
244 }
245 
246 /* second level */
aa_create_pixmap_(struct aa_font_cache * f,int j,int i)247 static void aa_create_pixmap_ (struct aa_font_cache *f, int j, int i)
248 {
249     if (!f->glyph[j]) {
250 	f->glyph[j] = malloc (sizeof (struct aa_glyph_cache) * 256);
251 	memset (f->glyph[j], 0, sizeof (struct aa_glyph_cache) * 256);
252     }
253     if (!f->glyph[j][i].pixmap)
254 	f->glyph[j][i].pixmap = aa_create_pixmap (f, j, i, &f->glyph[j][i].width);
255 }
256 
257 /* top level */
aa_create_pixmaps(struct aa_font_cache * f,XChar2b * wc,unsigned char * c,int n)258 static void aa_create_pixmaps (struct aa_font_cache *f, XChar2b * wc, unsigned char *c, int n)
259 {
260     int i;
261     if (aa_visual->class != TrueColor) {
262 	fprintf
263 	    (stderr,
264 	     "%s:%d: Can't do anti-aliasing without TrueColor visual.\nTry setting your X server to non-8-bits-per-pixel display.\n",
265 	     __FILE__, __LINE__);
266 	exit (1);
267     }
268     if (wc) {
269 	for (i = 0; i < n; i++)
270 	    aa_create_pixmap_ (f, wc[i].byte1, wc[i].byte2);
271     } else {
272 	for (i = 0; i < n; i++)
273 	    aa_create_pixmap_ (f, 0, c[i]);
274     }
275 }
276 
_XAaDrawImageStringWC(Display * display,Drawable d,GC gc,int x,int y,char * s,XChar2b * wc,int length)277 int _XAaDrawImageStringWC (Display * display, Drawable d, GC gc, int x, int y, char *s,
278 			   XChar2b * wc, int length)
279 {
280     int i, x_start = x;
281     struct aa_font_cache *f;
282     XGCValues values_return;
283     XGetGCValues (display, gc, GCForeground | GCBackground | GCFont, &values_return);
284     f = aa_find (values_return.font, values_return.foreground, values_return.background);
285     if (!f) {
286 	aa_insert ();
287 	f = font_cache_list;
288 	f->font_struct = XQueryFont (display, values_return.font);
289 	f->gc = gc;
290 	f->fg = values_return.foreground;
291 	f->bg = values_return.background;
292 	aa_display = display;
293     }
294     aa_create_pixmaps (f, wc, (unsigned char *) s, length);
295     if (wc) {
296 	for (i = 0; i < length; i++) {
297 	    int width = f->glyph[wc[i].byte1][wc[i].byte2].width;
298 	    int height = SHRINK_HEIGHT (f->font_struct->ascent + f->font_struct->descent);
299 	    XCopyArea (display, f->glyph[wc[i].byte1][wc[i].byte2].pixmap, d, gc, 0, 0, width,
300 		       height, x, y - f->font_struct->ascent / 3);
301 	    x += width;
302 	}
303     } else {
304 	for (i = 0; i < length; i++) {
305 	    int width = f->glyph[0][(unsigned char) s[i]].width;
306 	    int height = SHRINK_HEIGHT (f->font_struct->ascent + f->font_struct->descent);
307 	    XCopyArea (display, f->glyph[0][(unsigned char) s[i]].pixmap, d, gc, 0, 0, width,
308 		       height, x, y - f->font_struct->ascent / 3);
309 	    x += width;
310 	}
311     }
312     return x - x_start;
313 }
314 
XAaTextWidth(XFontStruct * font_struct,char * s,int length)315 int XAaTextWidth (XFontStruct * font_struct, char *s, int length)
316 {
317     int w = 0, i;
318     int direction, ascent, descent;
319     for (i = 0; i < length; i++) {
320 	XCharStruct ch;
321 	XTextExtents (font_struct, s + i, 1, &direction, &ascent, &descent, &ch);
322 	w += SHRINK_WIDTH (ch.width);
323     }
324     return w;
325 }
326 
XAaTextWidth16(XFontStruct * font_struct,XChar2b * s,int length)327 int XAaTextWidth16 (XFontStruct * font_struct, XChar2b * s, int length)
328 {
329     int w = 0, i;
330     int direction, ascent, descent;
331     for (i = 0; i < length; i++) {
332 	XCharStruct ch;
333 	XTextExtents16 (font_struct, s + i, 1, &direction, &ascent, &descent, &ch);
334 	w += SHRINK_WIDTH (ch.width);
335     }
336     return w;
337 }
338 
XAaDrawImageString16(Display * display,Drawable d,GC gc,int x,int y,XChar2b * wc,int length)339 int XAaDrawImageString16 (Display * display, Drawable d, GC gc, int x, int y, XChar2b * wc,
340 			  int length)
341 {
342     return _XAaDrawImageStringWC (display, d, gc, x, y, 0, wc, length);
343 }
344 
XAaDrawImageString(Display * display,Drawable d,GC gc,int x,int y,char * s,int length)345 int XAaDrawImageString (Display * display, Drawable d, GC gc, int x, int y, char *s, int length)
346 {
347     return _XAaDrawImageStringWC (display, d, gc, x, y, s, 0, length);
348 }
349 
XAaFree(Font fid)350 void XAaFree (Font fid)
351 {
352     aa_remove (fid);
353 }
354 
355