xref: /illumos-gate/usr/src/common/font/font.c (revision fa9eb22261c7b7a35c7a6957acb3c2b26c39e15b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2017 Toomas Soome <tsoome@me.com>
29  */
30 
31 /*
32  * Generic font related data and functions shared by early boot console
33  * in dboot, kernel startup and full kernel.
34  */
35 #include <sys/types.h>
36 #include <sys/systm.h>
37 #include <sys/tem_impl.h>
38 #include <sys/rgb.h>
39 #include <sys/font.h>
40 #include <sys/sysmacros.h>
41 
42 /*
43  * To simplify my life, I am "temporarily" collecting the commonly used
44  * color bits here. The bits shared between loader, dboot, early boot, tem.
45  * This data would need some sort of API, but I am in no condition to figure
46  * something out right now.
47  */
48 
49 /* ANSI color to sun color translation. */
50 /* BEGIN CSTYLED */
51 /*                                         Bk  Rd  Gr  Br  Bl  Mg  Cy  Wh */
52 const uint8_t dim_xlate[XLATE_NCOLORS] = {  1,  5,  3,  7,  2,  6,  4,  8 };
53 const uint8_t brt_xlate[XLATE_NCOLORS] = {  9, 13, 11, 15, 10, 14, 12,  0 };
54 
55 const uint8_t solaris_color_to_pc_color[16] = {
56 	pc_brt_white,		/*  0 - brt_white	*/
57 	pc_black,		/*  1 - black		*/
58 	pc_blue,		/*  2 - blue		*/
59 	pc_green,		/*  3 - green		*/
60 	pc_cyan,		/*  4 - cyan		*/
61 	pc_red,			/*  5 - red		*/
62 	pc_magenta,		/*  6 - magenta		*/
63 	pc_brown,		/*  7 - brown		*/
64 	pc_white,		/*  8 - white		*/
65 	pc_grey,		/*  9 - grey		*/
66 	pc_brt_blue,		/* 10 - brt_blue	*/
67 	pc_brt_green,		/* 11 - brt_green	*/
68 	pc_brt_cyan,		/* 12 - brt_cyan	*/
69 	pc_brt_red,		/* 13 - brt_red		*/
70 	pc_brt_magenta,		/* 14 - brt_magenta	*/
71 	pc_yellow		/* 15 - yellow		*/
72 };
73 
74 const uint8_t pc_color_to_solaris_color[16] = {
75 	sun_black,		/*  0 - black		*/
76 	sun_blue,		/*  1 - blue		*/
77 	sun_green,		/*  2 - green		*/
78 	sun_cyan,		/*  3 - cyan		*/
79 	sun_red,		/*  4 - red		*/
80 	sun_magenta,		/*  5 - magenta		*/
81 	sun_brown,		/*  6 - brown		*/
82 	sun_white,		/*  7 - white		*/
83 	sun_grey,		/*  8 - grey		*/
84 	sun_brt_blue,		/*  9 - brt_blue	*/
85 	sun_brt_green,		/* 10 - brt_green	*/
86 	sun_brt_cyan,		/* 11 - brt_cyan	*/
87 	sun_brt_red,		/* 12 - brt_red		*/
88 	sun_brt_magenta,	/* 13 - brt_magenta	*/
89 	sun_yellow,		/* 14 - yellow		*/
90 	sun_brt_white		/* 15 - brt_white	*/
91 };
92 
93 /* 4-bit to 24-bit color translation. */
94 const text_cmap_t cmap4_to_24 = {
95 /* 0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
96   Wh+  Bk   Bl   Gr   Cy   Rd   Mg   Br   Wh   Bk+  Bl+  Gr+  Cy+  Rd+  Mg+  Yw */
97   .red = {
98  0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff
99 },
100   .green = {
101  0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff
102 },
103   .blue = {
104  0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00
105 }
106 };
107 /* END CSTYLED */
108 
109 /* RGB configuration from boot loader */
110 rgb_t rgb_info = {
111 	.red = { .size = 8, .pos = 16 },
112 	.green = { .size = 8, .pos = 8 },
113 	.blue = { .size = 8, .pos = 0 }
114 };
115 
116 /*
117  * Map r, g, b to RGB value.
118  */
119 uint32_t
rgb_to_color(const rgb_t * rgb,uint32_t a,uint32_t r,uint32_t g,uint32_t b)120 rgb_to_color(const rgb_t *rgb, uint32_t a, uint32_t r, uint32_t g, uint32_t b)
121 {
122 	uint32_t color;
123 	int pos, size;
124 
125 	color = 0;
126 	if (a != 0) {
127 		if (rgb->red.pos != 0 &&
128 		    rgb->green.pos != 0 &&
129 		    rgb->blue.pos != 0) {
130 			pos = 0;
131 			size = MIN(rgb->red.pos,
132 			    MIN(rgb->green.pos, rgb->blue.pos));
133 		} else {
134 			pos = 24;
135 			size = (rgb->red.size + rgb->green.size +
136 			    rgb->blue.size) / 3;
137 		}
138 		color = ((a * ((1 << size) - 1)) / 0xff) << pos;
139 	}
140 
141 	pos = rgb->red.pos;
142 	size = rgb->red.size;
143 	color |= ((r * ((1 << size) - 1)) / 0xff) << pos;
144 
145 	pos = rgb->green.pos;
146 	size = rgb->green.size;
147 	color |= (((g * ((1 << size) - 1)) / 0xff) << pos);
148 
149 	pos = rgb->blue.pos;
150 	size = rgb->blue.size;
151 	color |= (((b * ((1 << size) - 1)) / 0xff) << pos);
152 
153 	return (color);
154 }
155 
156 uint32_t
rgb_color_map(const rgb_t * rgb,uint8_t index,uint8_t alpha)157 rgb_color_map(const rgb_t *rgb, uint8_t index, uint8_t alpha)
158 {
159 	uint32_t color, code, gray, level;
160 
161 	if (index < 16) {
162 		color = rgb_to_color(rgb, alpha, cmap4_to_24.red[index],
163 		    cmap4_to_24.green[index], cmap4_to_24.blue[index]);
164 		return (color);
165 	}
166 
167 	/* 6x6x6 color cube */
168 	if (index > 15 && index < 232) {
169 		uint32_t red, green, blue;
170 
171 		for (red = 0; red < 6; red++) {
172 			for (green = 0; green < 6; green++) {
173 				for (blue = 0; blue < 6; blue++) {
174 					code = 16 + (red * 36) +
175 					    (green * 6) + blue;
176 					if (code != index)
177 						continue;
178 					red = red ? (red * 40 + 55) : 0;
179 					green = green ? (green * 40 + 55) : 0;
180 					blue = blue ? (blue * 40 + 55) : 0;
181 					color = rgb_to_color(rgb, alpha,
182 					    red, green, blue);
183 					return (color);
184 				}
185 			}
186 		}
187 	}
188 
189 	/* colors 232-255 are a grayscale ramp */
190 	for (gray = 0; gray < 24; gray++) {
191 		level = (gray * 10) + 8;
192 		code = 232 + gray;
193 		if (code == index)
194 			break;
195 	}
196 	return (rgb_to_color(rgb, alpha, level, level, level));
197 }
198 /*
199  * Fonts are statically linked with this module. At some point an
200  * RFE might be desireable to allow dynamic font loading.  The
201  * original intention to facilitate dynamic fonts can be seen
202  * by examining the data structures and set_font().  As much of
203  * the original code is retained but modified to be suited for
204  * traversing a list of static fonts.
205  */
206 
207 /*
208  * Must be sorted by font size in descending order
209  */
210 font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
211 
212 /*
213  * Reset font flags to FONT_AUTO.
214  */
215 void
reset_font_flags(void)216 reset_font_flags(void)
217 {
218 	struct fontlist *fl;
219 
220 	STAILQ_FOREACH(fl, &fonts, font_next) {
221 		fl->font_flags = FONT_AUTO;
222 	}
223 }
224 
225 __weak_symbol bitmap_data_t *
gfx_get_font(void)226 gfx_get_font(void)
227 {
228 	return (NULL);
229 }
230 
231 bitmap_data_t *
set_font(short * rows,short * cols,short h,short w)232 set_font(short *rows, short *cols, short h, short w)
233 {
234 	bitmap_data_t *font = NULL;
235 	struct fontlist	*fl;
236 	unsigned height = h;
237 	unsigned width = w;
238 
239 	/*
240 	 * First check for manually loaded font.
241 	 */
242 	STAILQ_FOREACH(fl, &fonts, font_next) {
243 		if (fl->font_flags == FONT_MANUAL ||
244 		    fl->font_flags == FONT_BOOT) {
245 			font = fl->font_data;
246 			if (font->font == NULL && fl->font_load != NULL &&
247 			    fl->font_name != NULL) {
248 				font = fl->font_load(fl->font_name);
249 			}
250 			if (font == NULL || font->font == NULL)
251 				font = NULL;
252 			break;
253 		}
254 	}
255 
256 	if (font == NULL)
257 		font = gfx_get_font();
258 
259 	if (font != NULL) {
260 		*rows = (height - BORDER_PIXELS) / font->height;
261 		*cols = (width - BORDER_PIXELS) / font->width;
262 		return (font);
263 	}
264 
265 	/*
266 	 * Find best font for these dimensions, or use default
267 	 *
268 	 * A 1 pixel border is the absolute minimum we could have
269 	 * as a border around the text window (BORDER_PIXELS = 2),
270 	 * however a slightly larger border not only looks better
271 	 * but for the fonts currently statically built into the
272 	 * emulator causes much better font selection for the
273 	 * normal range of screen resolutions.
274 	 */
275 	STAILQ_FOREACH(fl, &fonts, font_next) {
276 		font = fl->font_data;
277 		if ((((*rows * font->height) + BORDER_PIXELS) <= height) &&
278 		    (((*cols * font->width) + BORDER_PIXELS) <= width)) {
279 			if (font->font == NULL ||
280 			    fl->font_flags == FONT_RELOAD) {
281 				if (fl->font_load != NULL &&
282 				    fl->font_name != NULL) {
283 					font = fl->font_load(fl->font_name);
284 				}
285 				if (font == NULL)
286 					continue;
287 			}
288 			*rows = (height - BORDER_PIXELS) / font->height;
289 			*cols = (width - BORDER_PIXELS) / font->width;
290 			break;
291 		}
292 		font = NULL;
293 	}
294 
295 	if (font == NULL) {
296 		/*
297 		 * We have fonts sorted smallest last, try it before
298 		 * falling back to builtin.
299 		 */
300 		fl = STAILQ_LAST(&fonts, fontlist, font_next);
301 		if (fl != NULL && fl->font_load != NULL &&
302 		    fl->font_name != NULL) {
303 			font = fl->font_load(fl->font_name);
304 		}
305 		if (font == NULL)
306 			font = &DEFAULT_FONT_DATA;
307 
308 		*rows = (height - BORDER_PIXELS) / font->height;
309 		*cols = (width - BORDER_PIXELS) / font->width;
310 	}
311 
312 	return (font);
313 }
314 
315 /* Binary search for the glyph. Return 0 if not found. */
316 static uint16_t
font_bisearch(const struct font_map * map,uint32_t len,uint32_t src)317 font_bisearch(const struct font_map *map, uint32_t len, uint32_t src)
318 {
319 	unsigned min, mid, max;
320 
321 	min = 0;
322 	max = len - 1;
323 
324 	/* Empty font map. */
325 	if (len == 0)
326 		return (0);
327 	/* Character below minimal entry. */
328 	if (src < map[0].font_src)
329 		return (0);
330 	/* Optimization: ASCII characters occur very often. */
331 	if (src <= map[0].font_src + map[0].font_len)
332 		return (src - map[0].font_src + map[0].font_dst);
333 	/* Character above maximum entry. */
334 	if (src > map[max].font_src + map[max].font_len)
335 		return (0);
336 
337 	/* Binary search. */
338 	while (max >= min) {
339 		mid = (min + max) / 2;
340 		if (src < map[mid].font_src)
341 			max = mid - 1;
342 		else if (src > map[mid].font_src + map[mid].font_len)
343 			min = mid + 1;
344 		else
345 			return (src - map[mid].font_src + map[mid].font_dst);
346 	}
347 
348 	return (0);
349 }
350 
351 /*
352  * Return glyph bitmap. If glyph is not found, we will return bitmap
353  * for the first (offset 0) glyph.
354  */
355 const uint8_t *
font_lookup(const struct font * vf,uint32_t c)356 font_lookup(const struct font *vf, uint32_t c)
357 {
358 	uint32_t src;
359 	uint16_t dst;
360 	size_t stride;
361 
362 	src = TEM_CHAR(c);
363 
364 	/* Substitute bold with normal if not found. */
365 	if (TEM_CHAR_ATTR(c) & TEM_ATTR_BOLD) {
366 		dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
367 		    vf->vf_map_count[VFNT_MAP_BOLD], src);
368 		if (dst != 0)
369 			goto found;
370 	}
371 	dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
372 	    vf->vf_map_count[VFNT_MAP_NORMAL], src);
373 
374 found:
375 	stride = howmany(vf->vf_width, 8) * vf->vf_height;
376 	return (&vf->vf_bytes[dst * stride]);
377 }
378 
379 /*
380  * bit_to_pix4 is for 4-bit frame buffers.  It will write one output byte
381  * for each 2 bits of input bitmap.  It inverts the input bits before
382  * doing the output translation, for reverse video.
383  *
384  * Assuming foreground is 0001 and background is 0000...
385  * An input data byte of 0x53 will output the bit pattern
386  * 00000001 00000001 00000000 00010001.
387  */
388 
389 void
font_bit_to_pix4(struct font * f,uint8_t * dest,uint32_t c,uint32_t fg_color,uint32_t bg_color)390 font_bit_to_pix4(
391     struct font *f,
392     uint8_t *dest,
393     uint32_t c,
394     uint32_t fg_color,
395     uint32_t bg_color)
396 {
397 	uint32_t row;
398 	int	byte;
399 	int	i;
400 	const uint8_t *cp, *ul;
401 	uint8_t	data;
402 	uint8_t	nibblett;
403 	int	bytes_wide;
404 
405 	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
406 		ul = font_lookup(f, 0x0332);	/* combining low line */
407 	else
408 		ul = NULL;
409 
410 	cp = font_lookup(f, c);
411 	bytes_wide = (f->vf_width + 7) / 8;
412 
413 	for (row = 0; row < f->vf_height; row++) {
414 		for (byte = 0; byte < bytes_wide; byte++) {
415 			if (ul == NULL)
416 				data = *cp++;
417 			else
418 				data = *cp++ | *ul++;
419 			for (i = 0; i < 4; i++) {
420 				nibblett = (data >> ((3-i) * 2)) & 0x3;
421 				switch (nibblett) {
422 				case 0x0:
423 					*dest++ = bg_color << 4 | bg_color;
424 					break;
425 				case 0x1:
426 					*dest++ = bg_color << 4 | fg_color;
427 					break;
428 				case 0x2:
429 					*dest++ = fg_color << 4 | bg_color;
430 					break;
431 				case 0x3:
432 					*dest++ = fg_color << 4 | fg_color;
433 					break;
434 				}
435 			}
436 		}
437 	}
438 }
439 
440 /*
441  * bit_to_pix8 is for 8-bit frame buffers.  It will write one output byte
442  * for each bit of input bitmap.  It inverts the input bits before
443  * doing the output translation, for reverse video.
444  *
445  * Assuming foreground is 00000001 and background is 00000000...
446  * An input data byte of 0x53 will output the bit pattern
447  * 0000000 000000001 00000000 00000001 00000000 00000000 00000001 00000001.
448  */
449 
450 void
font_bit_to_pix8(struct font * f,uint8_t * dest,uint32_t c,uint32_t fg_color,uint32_t bg_color)451 font_bit_to_pix8(
452     struct font *f,
453     uint8_t *dest,
454     uint32_t c,
455     uint32_t fg_color,
456     uint32_t bg_color)
457 {
458 	uint32_t row;
459 	int	byte;
460 	int	i;
461 	const uint8_t *cp, *ul;
462 	uint8_t	data;
463 	int	bytes_wide;
464 	uint8_t	mask;
465 	int	bitsleft, nbits;
466 
467 	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
468 		ul = font_lookup(f, 0x0332);	/* combining low line */
469 	else
470 		ul = NULL;
471 
472 	cp = font_lookup(f, c);
473 	bytes_wide = (f->vf_width + 7) / 8;
474 
475 	for (row = 0; row < f->vf_height; row++) {
476 		bitsleft = f->vf_width;
477 		for (byte = 0; byte < bytes_wide; byte++) {
478 			if (ul == NULL)
479 				data = *cp++;
480 			else
481 				data = *cp++ | *ul++;
482 			mask = 0x80;
483 			nbits = MIN(8, bitsleft);
484 			bitsleft -= nbits;
485 			for (i = 0; i < nbits; i++) {
486 				*dest++ = (data & mask ? fg_color: bg_color);
487 				mask = mask >> 1;
488 			}
489 		}
490 	}
491 }
492 
493 /*
494  * bit_to_pix16 is for 16-bit frame buffers.  It will write two output bytes
495  * for each bit of input bitmap.  It inverts the input bits before
496  * doing the output translation, for reverse video.
497  *
498  * Assuming foreground is 11111111 11111111
499  * and background is 00000000 00000000
500  * An input data byte of 0x53 will output the bit pattern
501  *
502  * 00000000 00000000
503  * 11111111 11111111
504  * 00000000 00000000
505  * 11111111 11111111
506  * 00000000 00000000
507  * 00000000 00000000
508  * 11111111 11111111
509  * 11111111 11111111
510  *
511  */
512 
513 void
font_bit_to_pix16(struct font * f,uint16_t * dest,uint32_t c,uint32_t fg_color16,uint32_t bg_color16)514 font_bit_to_pix16(
515     struct font *f,
516     uint16_t *dest,
517     uint32_t c,
518     uint32_t fg_color16,
519     uint32_t bg_color16)
520 {
521 	uint32_t row;
522 	int	byte;
523 	int	i;
524 	const uint8_t *cp, *ul;
525 	uint16_t data, d;
526 	int	bytes_wide;
527 	int	bitsleft, nbits;
528 
529 	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
530 		ul = font_lookup(f, 0x0332);	/* combining low line */
531 	else
532 		ul = NULL;
533 
534 	cp = font_lookup(f, c);
535 	bytes_wide = (f->vf_width + 7) / 8;
536 
537 	for (row = 0; row < f->vf_height; row++) {
538 		bitsleft = f->vf_width;
539 		for (byte = 0; byte < bytes_wide; byte++) {
540 			if (ul == NULL)
541 				data = *cp++;
542 			else
543 				data = *cp++ | *ul++;
544 			nbits = MIN(8, bitsleft);
545 			bitsleft -= nbits;
546 			for (i = 0; i < nbits; i++) {
547 				d = ((data << i) & 0x80 ?
548 				    fg_color16 : bg_color16);
549 				*dest++ = d;
550 			}
551 		}
552 	}
553 }
554 
555 /*
556  * bit_to_pix24 is for 24-bit frame buffers.  It will write three output bytes
557  * for each bit of input bitmap.  It inverts the input bits before
558  * doing the output translation, for reverse video.
559  *
560  * Assuming foreground is 11111111 11111111 11111111
561  * and background is 00000000 00000000 00000000
562  * An input data byte of 0x53 will output the bit pattern
563  *
564  * 00000000 00000000 00000000
565  * 11111111 11111111 11111111
566  * 00000000 00000000 00000000
567  * 11111111 11111111 11111111
568  * 00000000 00000000 00000000
569  * 00000000 00000000 00000000
570  * 11111111 11111111 11111111
571  * 11111111 11111111 11111111
572  *
573  */
574 
575 void
font_bit_to_pix24(struct font * f,uint8_t * dest,uint32_t c,uint32_t fg_color32,uint32_t bg_color32)576 font_bit_to_pix24(
577     struct font *f,
578     uint8_t *dest,
579     uint32_t c,
580     uint32_t fg_color32,
581     uint32_t bg_color32)
582 {
583 	uint32_t row;
584 	int	byte;
585 	int	i;
586 	const uint8_t *cp, *ul;
587 	uint32_t data, d;
588 	int	bytes_wide;
589 	int	bitsleft, nbits;
590 
591 	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
592 		ul = font_lookup(f, 0x0332);	/* combining low line */
593 	else
594 		ul = NULL;
595 
596 	cp = font_lookup(f, c);
597 	bytes_wide = (f->vf_width + 7) / 8;
598 
599 	for (row = 0; row < f->vf_height; row++) {
600 		bitsleft = f->vf_width;
601 		for (byte = 0; byte < bytes_wide; byte++) {
602 			if (ul == NULL)
603 				data = *cp++;
604 			else
605 				data = *cp++ | *ul++;
606 
607 			nbits = MIN(8, bitsleft);
608 			bitsleft -= nbits;
609 			for (i = 0; i < nbits; i++) {
610 				d = ((data << i) & 0x80 ?
611 				    fg_color32 : bg_color32);
612 				*dest++ = d & 0xff;
613 				*dest++ = (d >> 8) & 0xff;
614 				*dest++ = (d >> 16) & 0xff;
615 			}
616 		}
617 	}
618 }
619 
620 /*
621  * bit_to_pix32 is for 32-bit frame buffers.  It will write four output bytes
622  * for each bit of input bitmap.  It inverts the input bits before
623  * doing the output translation, for reverse video.  Note that each
624  * 24-bit RGB value is finally stored in a 32-bit unsigned int, with the
625  * high-order byte set to zero.
626  *
627  * Assuming foreground is 00000000 11111111 11111111 11111111
628  * and background is 00000000 00000000 00000000 00000000
629  * An input data byte of 0x53 will output the bit pattern
630  *
631  * 00000000 00000000 00000000 00000000
632  * 00000000 11111111 11111111 11111111
633  * 00000000 00000000 00000000 00000000
634  * 00000000 11111111 11111111 11111111
635  * 00000000 00000000 00000000 00000000
636  * 00000000 00000000 00000000 00000000
637  * 00000000 11111111 11111111 11111111
638  * 00000000 11111111 11111111 11111111
639  *
640  */
641 
642 void
font_bit_to_pix32(struct font * f,uint32_t * dest,uint32_t c,uint32_t fg_color32,uint32_t bg_color32)643 font_bit_to_pix32(
644     struct font *f,
645     uint32_t *dest,
646     uint32_t c,
647     uint32_t fg_color32,
648     uint32_t bg_color32)
649 {
650 	uint32_t row;
651 	int	byte;
652 	int	i;
653 	const uint8_t *cp, *ul;
654 	uint32_t data;
655 	int	bytes_wide;
656 	int	bitsleft, nbits;
657 
658 	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
659 		ul = font_lookup(f, 0x0332);	/* combining low line */
660 	else
661 		ul = NULL;
662 
663 	cp = font_lookup(f, c);
664 	bytes_wide = (f->vf_width + 7) / 8;
665 
666 	for (row = 0; row < f->vf_height; row++) {
667 		bitsleft = f->vf_width;
668 		for (byte = 0; byte < bytes_wide; byte++) {
669 			if (ul == NULL)
670 				data = *cp++;
671 			else
672 				data = *cp++ | *ul++;
673 			nbits = MIN(8, bitsleft);
674 			bitsleft -= nbits;
675 			for (i = 0; i < nbits; i++) {
676 				*dest++ = ((data << i) & 0x80 ?
677 				    fg_color32 : bg_color32);
678 			}
679 		}
680 	}
681 }
682