1 /**
2  ** buildfnt.c ---- allocate and fill a font descriptor
3  **
4  ** Copyright (c) 1995 Csaba Biegl, 820 Stirrup Dr, Nashville, TN 37221
5  ** [e-mail: csaba@vuse.vanderbilt.edu]
6  **
7  ** This file is part of the GRX graphics library.
8  **
9  ** The GRX graphics library is free software; you can redistribute it
10  ** and/or modify it under some conditions; see the "copying.grx" file
11  ** for details.
12  **
13  ** This library is distributed in the hope that it will be useful,
14  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  **
17  **/
18 
19 #include <string.h>
20 #include <stdio.h>
21 
22 #include "libgrx.h"
23 #include "grfontdv.h"
24 #include "allocate.h"
25 #include "arith.h"
26 #include "memfill.h"
27 
28 /*
29  * amounts for various font conversions
30  */
31 #define BOLDWDT(width)                  (((width)  + 3) / 10)
32 #define ITALWDT(height)                 (((height) + 1) / 7)
33 #define PROPGAP(width)                  (((width)  / 6) ? ((width) / 6) : 1)
34 
35 /*
36  * Font conversion option structure
37  */
38 typedef struct {
39     int     oldhgt;                     /* height of the source font */
40     int     newhgt;                     /* height of the created font */
41     int     boldwdt;                    /* shift and OR in X dir by this much */
42     int     italwdt;                    /* tilt font by this much */
43     int     dofix;                      /* convert to fixed width */
44     int     fixwdt;                     /* width of the fixed cvt font */
45     int     doprop;                     /* convert to proportional width */
46     int     propgap;                    /* zero edge pixels for prop cvt */
47 } conv;
48 
49 #ifdef DEBUG
bdump(char * hd,unsigned char * bmp,int w,int h,int pitch)50 static void bdump(char *hd,unsigned char *bmp,int w,int h,int pitch)
51 {
52 	char *s;
53 	DBGPRINTF(DBG_FONT, ("%s (%dx%d):\n",hd,w,h));
54 	setup_ALLOC();
55 	s = ALLOC((size_t) (w + 1));
56 	if(s == NULL) DBGPRINTF(DBG_FONT, ("no memory to show the char\n"));
57 	else {
58 	    while(--h >= 0) {
59 		int x;
60 		for(x = 0; x < w; x++) s[x] = bmp[x] ? '#' : '.';
61 		s[x] = '\0';
62 		DBGPRINTF(DBG_FONT, ("%s\n", s));
63 		bmp += pitch;
64 	    }
65 	    FREE(s);
66 	}
67 	reset_ALLOC();
68 }
69 #define BDUMP(hd,bmp,w,h,pitch) bdump(hd,bmp,w,h,pitch)
70 #else
71 #define BDUMP(hd,bmp,w,h,pitch)
72 #endif
73 
cvtbitmap(int oldw,int neww,conv * c,char * bmp)74 static int cvtbitmap(int oldw,int neww,conv *c,char *bmp)
75 {
76 	int wofs,i,j,x,y,w,h;
77 	unsigned char *work_base;
78 	setup_ALLOC();
79 	wofs = umax(oldw,neww) + c->boldwdt + c->italwdt + c->propgap;
80 	wofs = umax(wofs,c->fixwdt);
81 	wofs = (wofs + 15) & ~3;
82 	i    = wofs * (umax(c->newhgt,c->oldhgt) + 2);
83 	work_base = ALLOC((size_t)i);
84 	if(work_base) {
85 	    unsigned char *work = work_base;
86 	    memzero(work,i);
87 	    work += wofs;
88 	    w = neww - c->boldwdt - c->italwdt;
89 	    h = c->newhgt;
90 	    if((w == oldw) && (h == c->oldhgt)) {
91 		/* No resizing is needed */
92 		for(y = 0; y < h; y++) {
93 		    i = ((w + 7) & ~7) * y;
94 		    j = wofs * y;
95 		    for(x = 0; x < w; x++,i++,j++) {
96 			work[j] = bmp[i >> 3] & (0x80 >> (i & 7));
97 		    }
98 		}
99 		BDUMP("after unpacking",work,w,h,wofs);
100 	    }
101 	    else {
102 		/* Resize the bitmap */
103 		/* The algorithm first looks for rectangles of '1' pixels */
104 		/* and then maps these onto the coordinate space of the */
105 		/* resized bitmap. This seems to work better than simple */
106 		/* pixel sampling. (No feature loss) */
107 		for(y = 0; y < c->oldhgt; y++) {
108 		    unsigned int pos = 0,ones = 0;
109 		    i = ((oldw + 7) & ~7) * y;
110 		    for(x = 0; x < oldw; x++,i++) {
111 			if(bmp[i >> 3] & (0x80 >> (i & 7))) {
112 			    if(!ones) pos = x;
113 			    ones++;
114 			    if(x != (oldw - 1)) continue;
115 			}
116 			if(ones > 0) {
117 			    unsigned int x1 = urscale(pos,w,oldw);
118 			    unsigned int x2 = urscale((pos + ones),w,oldw);
119 			    unsigned int y1 = urscale(y,h,c->oldhgt);
120 			    unsigned int y2 = urscale((y + 1),h,c->oldhgt);
121 			    do {
122 				unsigned int xx = x1;
123 				j = (y1 * wofs) + xx;
124 				do {
125 				    work[j++] = 1;
126 				} while(++xx < x2);
127 			    } while(++y1 < y2);
128 			    ones = 0;
129 			}
130 		    }
131 		}
132 		BDUMP("after resize",work,w,h,wofs);
133 	    }
134 	    /* bold conversion */
135 	    if(c->boldwdt > 0) {
136 		for(y = 0; y < h; y++) {
137 		    unsigned char *row = &work[wofs * y];
138 		    unsigned char *p1  = &row[w];
139 		    unsigned char *p2  = &row[w + c->boldwdt];
140 		    while(p1 > row) *--p2 |= *--p1;
141 		}
142 		w += c->boldwdt;
143 		BDUMP("after boldify",work,w,h,wofs);
144 	    }
145 	    /* italic conversion */
146 	    if(c->italwdt > 0) {
147 		int ymax = h - 1;
148 		int yrnd = ymax >> 1;
149 		for(y = 0; y < h; y++) {
150 		    int   tilt = ((c->italwdt * (ymax - y)) + yrnd) / ymax;
151 		    unsigned char *row = &work[wofs * y];
152 		    unsigned char *p1  = &row[w];
153 		    unsigned char *p2  = &row[w + tilt];
154 		    while(p1 > row) *--p2 = *--p1;
155 		    while(p2 > row) *--p2 = 0;
156 		}
157 		w += c->italwdt;
158 		BDUMP("after italicize",work,w,h,wofs);
159 	    }
160 	    x = 0;
161 	    /* proportional or fixed width adjustment */
162 	    if(c->dofix || c->doprop) {
163 		int minx = w;
164 		int maxx = 0;
165 		for(y = 0; y < h; y++) {
166 		    i = y * wofs;
167 		    for(j = 0; j < w; j++,i++) {
168 			if(work[i] && (j < minx)) minx = j;
169 			if(work[i] && (j > maxx)) maxx = j;
170 		    }
171 		}
172 		if(minx > maxx) {
173 		    minx = 0;
174 		    maxx = w - 1;
175 		}
176 		if(c->dofix) {
177 		    neww = c->fixwdt;
178 		    w    = maxx - minx + 1;
179 		    x    = (neww - w) >> 1;
180 		}
181 		if(c->doprop) {
182 		    w    = maxx - minx + 1;
183 		    x    = minx - (c->propgap >> 1);
184 		    neww = w + c->propgap;
185 		}
186 	    }
187 	    for(y = 0,i = (neww + 7) >> 3; y < h; y++) {
188 		char  *bp = &bmp[y * i];
189 		unsigned char *wp = &work[y * wofs];
190 		int  xpos = x;
191 		memfill_b(bp,0,i);
192 		for(j = 0; j < neww; j++,xpos++) {
193 		    if(wp[xpos]) bp[j >> 3] |= (0x80 >> (j & 7));
194 		}
195 	    }
196 	    FREE(work_base);
197 	}
198 	reset_ALLOC();
199 	return(neww);
200 }
201 
_GrBuildFont(const GrFontHeader * h,int cvt,int wdt,int hgt,int cmin,int cmax,int (* charwdt)(int chr),int (* bitmap)(int chr,int w,int h,char * buffer),int scaled)202 GrFont *_GrBuildFont(
203     const GrFontHeader *h,
204     int  cvt,
205     int  wdt,
206     int  hgt,
207     int  cmin,
208     int  cmax,
209     int  (*charwdt)(int chr),
210     int  (*bitmap)(int chr,int w,int h,char *buffer),
211     int  scaled
212 ){
213 	GrFont *f    = NULL;
214 	unsigned int  fprop  = h->proportional;
215 	unsigned int  chrcv  = GR_FONTCVT_NONE;
216 	unsigned int  bmpcv  = GR_FONTCVT_NONE;
217 	unsigned long totwdt = 0L;
218 	unsigned long bmplen = 0L;
219 	unsigned int  bmpofs = 0;
220 	unsigned int  numch,i,chr;
221 	char  *bmp = NULL;
222 	conv  cv;
223 	setup_ALLOC();
224 	sttzero(&cv);
225 	if(cvt & GR_FONTCVT_SKIPCHARS) {
226 	    unsigned int lastfntchr = h->minchar + h->numchars - 1;
227 	    cmin = umin(umax(cmin,h->minchar),lastfntchr);
228 	    cmax = umin(umax(cmax,h->minchar),lastfntchr);
229 	    if(cmin > cmax) goto error;
230 	    if((unsigned int)cmin > h->minchar) chrcv = GR_FONTCVT_SKIPCHARS;
231 	    if((unsigned int)cmax < lastfntchr) chrcv = GR_FONTCVT_SKIPCHARS;
232 	}
233 	else {
234 	    cmin = h->minchar;
235 	    cmax = h->minchar + h->numchars - 1;
236 	}
237 	if(cvt & GR_FONTCVT_RESIZE) {
238 	    if((unsigned int)(wdt = imax(2,wdt)) != h->width)  bmpcv=GR_FONTCVT_RESIZE;
239 	    if((unsigned int)(hgt = imax(2,hgt)) != h->height) bmpcv=GR_FONTCVT_RESIZE;
240 	}
241 	else {
242 	    wdt = h->width;
243 	    hgt = h->height;
244 	}
245 	numch = cmax - cmin + 1;
246 	i = sizeof(GrFont) + ((numch - 1) * sizeof(GrFontChrInfo));
247 	f = malloc(i);
248 	if(!f) goto error;
249 	memzero(f,i);
250 	f->h.name   = malloc(strlen(h->name)   + 1);
251 	f->h.family = malloc(strlen(h->family) + 1);
252 	if(!f->h.name || !f->h.family) goto error;
253 	strcpy(f->h.name  ,h->name);
254 	strcpy(f->h.family,h->family);
255 	f->minwidth = 0x7fff;
256 	f->maxwidth = 0;
257 	for(chr = cmin,i = 0; i < numch; chr++,i++) {
258 	    int  oldw = (*charwdt)(chr);
259 	    unsigned int neww = urscale(oldw,wdt,h->width);
260 	    if(oldw < 0) goto error;
261 	    if(f->minwidth > neww) f->minwidth = neww;
262 	    if(f->maxwidth < neww) f->maxwidth = neww;
263 	    f->chrinfo[i].width  = oldw;
264 	    bmplen += ((neww + 7) >> 3) * hgt;
265 	}
266 	cv.oldhgt = scaled ? hgt : h->height;
267 	cv.newhgt = hgt;
268 	if(cvt & GR_FONTCVT_BOLDIFY) {
269 	    cv.boldwdt = BOLDWDT(wdt);
270 	    cv.boldwdt = imin(cv.boldwdt,(f->minwidth - 1));
271 	    cv.boldwdt = imax(cv.boldwdt,0);
272 	    if(cv.boldwdt > 0) bmpcv |= GR_FONTCVT_BOLDIFY;
273 	}
274 	if(cvt & GR_FONTCVT_ITALICIZE) {
275 	    cv.italwdt = ITALWDT(hgt);
276 	    cv.italwdt = imin(cv.italwdt,(f->minwidth - cv.boldwdt - 1));
277 	    cv.italwdt = imax(cv.italwdt,0);
278 	    if(cv.italwdt > 0) bmpcv |= GR_FONTCVT_ITALICIZE;
279 	}
280 	if((cvt & GR_FONTCVT_FIXIFY) && fprop) {
281 	    bmpcv     |= GR_FONTCVT_FIXIFY;
282 	    cv.fixwdt  = f->maxwidth;
283 	    bmplen     = umul32((hgt * ((cv.fixwdt + 7) >> 3)),numch);
284 	    cv.dofix   = TRUE;
285 	    fprop      = FALSE;
286 	}
287 	if((cvt & GR_FONTCVT_PROPORTION) && !fprop) {
288 	    bmpcv     |= GR_FONTCVT_PROPORTION;
289 	    cv.propgap = imax(0,(PROPGAP(wdt) - cv.italwdt));
290 	    bmplen     = umul32((hgt * ((wdt + cv.propgap + 7) >> 3)),numch);
291 	    cv.doprop  = TRUE;
292 	    fprop      = TRUE;
293 	}
294 	if((unsigned long)(unsigned int)bmplen != bmplen) goto error;
295 	f->bitmap = farmalloc(bmplen);
296 	if(!f->bitmap) goto error;
297 	memzero(f->bitmap,(unsigned int)bmplen);
298 	i = f->maxwidth;
299 	if(h->width > (unsigned int)wdt) i = urscale(i,h->width,wdt);
300 	i = ((i + 31) >> 3) * umax(hgt,h->height);
301 	bmp = ALLOC((size_t)i);
302 	if(!bmp) goto error;
303 	f->minwidth = 0x7fff;
304 	f->maxwidth = 0;
305 	for(chr = cmin,i = 0; i < numch; chr++,i++) {
306 	    unsigned int oldw = f->chrinfo[i].width;
307 	    unsigned int neww = imax(urscale(oldw,wdt,h->width),1);
308 	    unsigned int size;
309 	    if(scaled) {
310 		unsigned int raww = neww - cv.boldwdt - cv.italwdt;
311 		if(!(*bitmap)(chr,raww,hgt,bmp)) goto error;
312 		if(bmpcv & ~GR_FONTCVT_RESIZE) neww = cvtbitmap(oldw,raww,&cv,bmp);
313 	    }
314 	    else {
315 		if(!(*bitmap)(chr,oldw,h->height,bmp)) goto error;
316 		if(bmpcv) neww = cvtbitmap(oldw,neww,&cv,bmp);
317 	    }
318 	    if(f->minwidth > neww) f->minwidth = neww;
319 	    if(f->maxwidth < neww) f->maxwidth = neww;
320 	    size = ((neww + 7) >> 3) * hgt;
321 	    memcpy(&f->bitmap[bmpofs],bmp,size);
322 	    f->chrinfo[i].width  = neww;
323 	    f->chrinfo[i].offset = bmpofs;
324 	    bmpofs += size;
325 	    totwdt += neww;
326 	}
327 	f->h.proportional = fprop;
328 	f->h.scalable     = h->scalable;
329 	f->h.preloaded    = FALSE;
330 	f->h.modified     = h->modified | chrcv | bmpcv;
331 	f->h.minchar      = cmin;
332 	f->h.numchars     = numch;
333 	f->h.width        = (cv.dofix || cv.doprop) ? (unsigned int)(totwdt / numch) : wdt;
334 	f->h.height       = hgt;
335 	f->h.baseline     = urscale(h->baseline,hgt,h->height);
336 	f->h.ulpos        = urscale(h->ulpos,   hgt,h->height);
337 	f->h.ulheight     = urscale(h->ulheight,hgt,h->height);
338 	goto done;
339       error:
340 	if(f) {
341 	    if(f->h.name)   free(f->h.name);
342 	    if(f->h.family) free(f->h.family);
343 	    if(f->bitmap)   farfree(f->bitmap);
344 	    free(f);
345 	    f = NULL;
346 	}
347       done:
348 	if (bmp) FREE(bmp);
349 	reset_ALLOC();
350 	return(f);
351 }
352 
353