1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <string.h>
5 #include <math.h>
6 #include <wchar.h>
7 #include <inttypes.h>
8 #include <sys/ioctl.h>
9 #include <linux/fb.h>
10 
11 #include <fontconfig/fontconfig.h>
12 #include <fontconfig/fcfreetype.h>
13 
14 #include "vt.h"
15 #include "fbtools.h"
16 #include "dither.h"
17 #include "fb-gui.h"
18 
19 static int ys =  3;
20 static int xs = 10;
21 
22 /* ---------------------------------------------------------------------- */
23 /* shadow framebuffer -- internals                                        */
24 
25 static int32_t s_lut_transp[256], s_lut_red[256], s_lut_green[256], s_lut_blue[256];
26 
27 static unsigned char **shadow;
28 static unsigned int  *sdirty,swidth,sheight;
29 
shadow_lut_init_one(int32_t * lut,int bits,int shift)30 static void shadow_lut_init_one(int32_t *lut, int bits, int shift)
31 {
32     int i;
33 
34     if (bits > 8)
35 	for (i = 0; i < 256; i++)
36 	    lut[i] = (i << (bits + shift - 8));
37     else
38 	for (i = 0; i < 256; i++)
39 	    lut[i] = (i >> (8 - bits)) << shift;
40 }
41 
shadow_lut_init(gfxstate * gfx)42 static void shadow_lut_init(gfxstate *gfx)
43 {
44     shadow_lut_init_one(s_lut_transp, gfx->tlen, gfx->toff);
45     shadow_lut_init_one(s_lut_red,    gfx->rlen, gfx->roff);
46     shadow_lut_init_one(s_lut_green,  gfx->glen, gfx->goff);
47     shadow_lut_init_one(s_lut_blue,   gfx->blen, gfx->boff);
48 }
49 
shadow_render_line(gfxstate * gfx,int line,unsigned char * dest,char unsigned * buffer)50 static void shadow_render_line(gfxstate *gfx, int line,
51                                unsigned char *dest, char unsigned *buffer)
52 {
53     uint8_t  *ptr  = (void*)dest;
54     uint16_t *ptr2 = (void*)dest;
55     uint32_t *ptr4 = (void*)dest;
56     int x;
57 
58     switch (gfx->bits_per_pixel) {
59     case 8:
60 	dither_line(buffer, ptr, line, swidth);
61 	break;
62     case 15:
63     case 16:
64 	for (x = 0; x < swidth; x++) {
65 	    ptr2[x] = s_lut_red[buffer[x*3]] |
66 		s_lut_green[buffer[x*3+1]] |
67 		s_lut_blue[buffer[x*3+2]];
68 	}
69 	break;
70     case 24:
71 	for (x = 0; x < swidth; x++) {
72 	    ptr[3*x+2] = buffer[3*x+0];
73 	    ptr[3*x+1] = buffer[3*x+1];
74 	    ptr[3*x+0] = buffer[3*x+2];
75 	}
76 	break;
77     case 32:
78 	for (x = 0; x < swidth; x++) {
79 	    ptr4[x] = s_lut_transp[255] |
80 		s_lut_red[buffer[x*3]] |
81 		s_lut_green[buffer[x*3+1]] |
82 		s_lut_blue[buffer[x*3+2]];
83 	}
84 	break;
85     }
86 }
87 
88 /* ---------------------------------------------------------------------- */
89 /* shadow framebuffer -- management interface                             */
90 
shadow_render(gfxstate * gfx)91 void shadow_render(gfxstate *gfx)
92 {
93     unsigned int offset = 0;
94     int i;
95 
96     if (!console_visible)
97 	return;
98     for (i = 0; i < sheight; i++, offset += gfx->stride) {
99 	if (0 == sdirty[i])
100 	    continue;
101 	shadow_render_line(gfx, i, gfx->mem + offset, shadow[i]);
102 	sdirty[i] = 0;
103     }
104     if (gfx->flush_display)
105         gfx->flush_display(false);
106 }
107 
shadow_clear_lines(int first,int last)108 void shadow_clear_lines(int first, int last)
109 {
110     int i;
111 
112     for (i = first; i <= last; i++) {
113 	memset(shadow[i],0,3*swidth);
114 	sdirty[i]++;
115     }
116 }
117 
shadow_clear(void)118 void shadow_clear(void)
119 {
120     shadow_clear_lines(0, sheight-1);
121 }
122 
shadow_set_dirty(void)123 void shadow_set_dirty(void)
124 {
125     int i;
126 
127     for (i = 0; i < sheight; i++)
128 	sdirty[i]++;
129 }
130 
shadow_init(gfxstate * gfx)131 void shadow_init(gfxstate *gfx)
132 {
133     int i;
134 
135     /* init shadow fb */
136     swidth  = gfx->hdisplay;
137     sheight = gfx->vdisplay;
138     shadow  = malloc(sizeof(unsigned char*) * sheight);
139     sdirty  = malloc(sizeof(unsigned int)   * sheight);
140     memset(sdirty,0, sizeof(unsigned int)   * sheight);
141     for (i = 0; i < sheight; i++)
142 	shadow[i] = malloc(swidth*3);
143     shadow_clear();
144 
145     /* init rendering */
146     switch (gfx->bits_per_pixel) {
147     case 8:
148 	init_dither(8, 8, 4, 2);
149 	dither_line = dither_line_color;
150 	break;
151     case 15:
152     case 16:
153     case 24:
154     case 32:
155         shadow_lut_init(gfx);
156 	break;
157     default:
158 	fprintf(stderr, "Oops: %i bit/pixel ???\n",
159 		gfx->bits_per_pixel);
160 	exit(1);
161     }
162 }
163 
shadow_fini(void)164 void shadow_fini(void)
165 {
166     int i;
167 
168     if (!shadow)
169 	return;
170     for (i = 0; i < sheight; i++)
171 	free(shadow[i]);
172     free(shadow);
173     free(sdirty);
174 }
175 
176 /* ---------------------------------------------------------------------- */
177 /* shadow framebuffer -- drawing interface                                */
178 
shadow_setpixel(int x,int y)179 static void shadow_setpixel(int x, int y)
180 {
181     unsigned char *dest = shadow[y] + 3*x;
182 
183     if (x < 0)
184 	return;
185     if (x >= swidth)
186 	return;
187     if (y < 0)
188 	return;
189     if (y >= sheight)
190 	return;
191     *(dest++) = 255;
192     *(dest++) = 255;
193     *(dest++) = 255;
194     sdirty[y]++;
195 }
196 
shadow_draw_line(int x1,int x2,int y1,int y2)197 void shadow_draw_line(int x1, int x2, int y1,int y2)
198 {
199     int x,y,h;
200     float inc;
201 
202     if (x2 < x1)
203 	h = x2, x2 = x1, x1 = h;
204     if (y2 < y1)
205 	h = y2, y2 = y1, y1 = h;
206 
207     if (x2 - x1 < y2 - y1) {
208 	inc = (float)(x2-x1)/(float)(y2-y1);
209 	for (y = y1; y <= y2; y++) {
210 	    x = x1 + inc * (y - y1);
211 	    shadow_setpixel(x,y);
212 	}
213     } else {
214 	inc = (float)(y2-y1)/(float)(x2-x1);
215 	for (x = x1; x <= x2; x++) {
216 	    y = y1 + inc * (x - x1);
217 	    shadow_setpixel(x,y);
218 	}
219     }
220 }
221 
shadow_draw_rect(int x1,int x2,int y1,int y2)222 void shadow_draw_rect(int x1, int x2, int y1,int y2)
223 {
224     shadow_draw_line(x1, x2, y1, y1);
225     shadow_draw_line(x1, x2, y2, y2);
226     shadow_draw_line(x1, x1, y1, y2);
227     shadow_draw_line(x2, x2, y1, y2);
228 }
229 
shadow_draw_rgbdata(int x,int y,int pixels,unsigned char * rgb)230 void shadow_draw_rgbdata(int x, int y, int pixels, unsigned char *rgb)
231 {
232     unsigned char *dest = shadow[y] + 3*x;
233 
234     memcpy(dest,rgb,3*pixels);
235     sdirty[y]++;
236 }
237 
shadow_merge_rgbdata(int x,int y,int pixels,int weight,unsigned char * rgb)238 void shadow_merge_rgbdata(int x, int y, int pixels, int weight,
239 			  unsigned char *rgb)
240 {
241     unsigned char *dest = shadow[y] + 3*x;
242     int i = 3*pixels;
243 
244     weight = weight * 256 / 100;
245 
246     while (i-- > 0)
247 	*(dest++) += *(rgb++) * weight >> 8;
248     sdirty[y]++;
249 }
250 
251 
shadow_darkify(int x1,int x2,int y1,int y2,int percent)252 void shadow_darkify(int x1, int x2, int y1,int y2, int percent)
253 {
254     unsigned char *ptr;
255     int x,y,h;
256 
257     if (x2 < x1)
258 	h = x2, x2 = x1, x1 = h;
259     if (y2 < y1)
260 	h = y2, y2 = y1, y1 = h;
261 
262     if (x1 < 0)
263 	x1 = 0;
264     if (x2 >= swidth)
265 	x2 = swidth;
266 
267     if (y1 < 0)
268 	y1 = 0;
269     if (y2 >= sheight)
270 	y2 = sheight;
271 
272     percent = percent * 256 / 100;
273 
274     for (y = y1; y <= y2; y++) {
275 	sdirty[y]++;
276 	ptr = shadow[y];
277 	ptr += 3*x1;
278 	x = 3*(x2-x1+1);
279 	while (x-- > 0) {
280 	    *ptr = (*ptr * percent) >> 8;
281 	    ptr++;
282 	}
283     }
284 }
285 
shadow_reverse(int x1,int x2,int y1,int y2)286 void shadow_reverse(int x1, int x2, int y1,int y2)
287 {
288     unsigned char *ptr;
289     int x,y,h;
290 
291     if (x2 < x1)
292 	h = x2, x2 = x1, x1 = h;
293     if (y2 < y1)
294 	h = y2, y2 = y1, y1 = h;
295 
296     if (x1 < 0)
297 	x1 = 0;
298     if (x2 >= swidth)
299 	x2 = swidth;
300 
301     if (y1 < 0)
302 	y1 = 0;
303     if (y2 >= sheight)
304 	y2 = sheight;
305 
306     for (y = y1; y <= y2; y++) {
307 	sdirty[y]++;
308 	ptr = shadow[y];
309 	for (x = x1; x <= x2; x++) {
310 	    ptr[3*x+0] = 255-ptr[3*x+0];
311 	    ptr[3*x+1] = 255-ptr[3*x+1];
312 	    ptr[3*x+2] = 255-ptr[3*x+2];
313 	}
314     }
315 }
316 
317 /* ---------------------------------------------------------------------- */
318 /* shadow framebuffer -- text rendering                                   */
319 
shadow_draw_glyph(FT_Bitmap * bitmap,int sx,int sy)320 static void shadow_draw_glyph(FT_Bitmap *bitmap, int sx, int sy)
321 {
322     unsigned char *src,*dst;
323     unsigned int bit;
324     int x,y;
325 
326     src = bitmap->buffer;
327     for (y = 0; y < bitmap->rows; y++, src += bitmap->pitch) {
328 	if (sy+y < 0)
329 	    continue;
330 	if (sy+y >= sheight)
331 	    continue;
332 	sdirty[sy+y]++;
333 	dst = shadow[sy+y] + sx*3;
334 	switch (bitmap->pixel_mode) {
335 	case FT_PIXEL_MODE_MONO:
336 	    for (x = 0; x < bitmap->width; x++, dst += 3) {
337 		if (sx+x < 0)
338 		    continue;
339 		if (sx+x >= swidth)
340 		    continue;
341 		bit = (1 << (7-(x&7)));
342 		if (bit & (src[x >> 3])) {
343 		    dst[0] = 255;
344 		    dst[1] = 255;
345 		    dst[2] = 255;
346 		}
347 	    }
348 	    break;
349 	case FT_PIXEL_MODE_GRAY:
350 	    for (x = 0; x < bitmap->width; x++, dst += 3) {
351 		if (sx+x < 0)
352 		    continue;
353 		if (sx+x >= swidth)
354 		    continue;
355 		if (src[x]) {
356 		    dst[0] += (255-dst[0]) * src[x] / 255;
357 		    dst[1] += (255-dst[1]) * src[x] / 255;
358 		    dst[2] += (255-dst[2]) * src[x] / 255;
359 		}
360 	    }
361 	    break;
362 	}
363     }
364 }
365 
366 struct glyph {
367     FT_Glyph  glyph;
368     int       pos;
369 };
370 
shadow_draw_string(FT_Face face,int x,int y,wchar_t * str,int align)371 int shadow_draw_string(FT_Face face, int x, int y, wchar_t *str, int align)
372 {
373     struct glyph   *glyphs;
374     FT_UInt        gi,pgi;
375     FT_Vector      delta,pen;
376     FT_Glyph       image;
377     FT_BitmapGlyph bit;
378     size_t         len;
379     int            i,ng,pos;
380     int            kerning;
381 
382     len = wcslen(str);
383     glyphs = malloc(sizeof(*glyphs) * len);
384     memset(glyphs,0,sizeof(*glyphs) * len);
385 
386     kerning  = FT_HAS_KERNING(face);
387     pgi = 0;
388 
389     for (ng = 0, pos = 0, i = 0; str[i] != 0; i++) {
390 	gi = FT_Get_Char_Index(face, str[i]);
391 	if (kerning && pgi && gi) {
392 	    FT_Get_Kerning(face,pgi,gi,FT_KERNING_DEFAULT,&delta);
393 	    pos += delta.x;
394 	}
395 	glyphs[ng].pos = pos;
396 	if (0 != FT_Load_Glyph(face, gi, FT_LOAD_DEFAULT))
397 	    continue;
398 	if (0 != FT_Get_Glyph(face->glyph, &glyphs[ng].glyph))
399 	    continue;
400 	pos += face->glyph->advance.x;
401 	pgi = gi;
402 	ng++;
403     }
404 
405     switch(align) {
406     case -1: /* left */
407 	break;
408     case 0: /* center */
409 	x -= pos >> 7;
410 	break;
411     case 1: /* right */
412 	x -= pos >> 6;
413 	break;
414     }
415     pen.x = 0;
416     pen.y = 0;
417     for (i = 0; i < ng; i++) {
418 	image = glyphs[i].glyph;
419 	if (0 != FT_Glyph_To_Bitmap(&image,FT_RENDER_MODE_NORMAL,&pen,0))
420 	    continue;
421 	bit = (FT_BitmapGlyph)image;
422 	shadow_draw_glyph(&bit->bitmap,
423 			  x + bit->left + (glyphs[i].pos >> 6),
424 			  y - bit->top);
425 	if (image != glyphs[i].glyph)
426 	    FT_Done_Glyph(image);
427     }
428 
429     for (i = 0; i < ng; i++)
430 	FT_Done_Glyph(glyphs[i].glyph);
431     free(glyphs);
432 
433     return pos >> 6;
434 }
435 
shadow_draw_string_cursor(FT_Face face,int x,int y,wchar_t * str,int pos)436 void shadow_draw_string_cursor(FT_Face face, int x, int y, wchar_t *str, int pos)
437 {
438     wchar_t save;
439     int len, left, width, y1, y2;
440 
441     len = wcslen(str);
442     if (pos >= len) {
443 	left  = shadow_draw_string(face, x, y, str, -1);
444 	width = shadow_draw_string(face, x+left, y, L" ", -1);
445     } else {
446 	save = str[pos];
447 	str[pos] = 0;
448 	left = shadow_draw_string(face, x, y, str, -1);
449 	str[pos] = save;
450 
451 	save = str[pos+1];
452 	str[pos+1] = 0;
453 	width = shadow_draw_string(face, x+left, y, str+pos, -1);
454 	str[pos+1] = save;
455 
456 	shadow_draw_string(face, x+left+width, y, str+pos+1, -1);
457     }
458 
459     y2 = y  - (face->size->metrics.descender >> 6) -1;
460     y1 = y2 - (face->size->metrics.height    >> 6) +1;
461     shadow_reverse(left,left+width,y1,y2);
462 }
463 
shadow_draw_text_box(FT_Face face,int x,int y,int percent,wchar_t * lines[],unsigned int count)464 void shadow_draw_text_box(FT_Face face, int x, int y, int percent, wchar_t *lines[], unsigned int count)
465 {
466     unsigned int i,len,max, x1, x2, y1, y2;
467 
468     if (!console_visible)
469 	return;
470 
471     max = 0;
472     for (i = 0; i < count; i++) {
473 	len = wcslen(lines[i]);
474 	if (max < len)
475 	    max = len;
476     }
477 
478     FT_Load_Glyph(face, FT_Get_Char_Index(face, 'x'), FT_LOAD_DEFAULT);
479     x1 = x;
480     x2 = x + max * (face->glyph->advance.x >> 6);
481     y1 = y;
482     y2 = y + count * (face->size->metrics.height >> 6);
483 
484     x += xs; x2 += 2*xs;
485     y += ys; y2 += 2*ys;
486     y += (face->size->metrics.height    >> 6);
487     y += (face->size->metrics.descender >> 6);
488 
489     shadow_darkify(x1, x2, y1, y2, percent);
490     shadow_draw_rect(x1, x2, y1, y2);
491     for (i = 0; i < count; i++) {
492 	shadow_draw_string(face, x, y, lines[i], -1);
493 	y += (face->size->metrics.height >> 6);
494     }
495 }
496 
497 /* ---------------------------------------------------------------------- */
498 /* fontconfig + freetype font rendering                                   */
499 
500 static FT_Library freetype;
501 
font_init(void)502 void font_init(void)
503 {
504     int rc;
505 
506     FcInit();
507     rc = FT_Init_FreeType(&freetype);
508     if (rc) {
509 	fprintf(stderr,"FT_Init_FreeType() failed\n");
510 	exit(1);
511     }
512 }
513 
font_open(char * fcname)514 FT_Face font_open(char *fcname)
515 {
516     FcResult    result = 0;
517     FT_Face     face = NULL;
518     FcPattern   *pattern,*match;
519     char        *fontname,*h;
520     FcChar8     *filename;
521     double      pixelsize;
522     int         rc;
523 
524     /* parse + match font name */
525     pattern = FcNameParse(fcname);
526     FcConfigSubstitute(NULL, pattern, FcMatchPattern);
527     FcDefaultSubstitute(pattern);
528     match = FcFontMatch (0, pattern, &result);
529     FcPatternDestroy(pattern);
530     if (FcResultMatch != result)
531 	return NULL;
532     fontname = FcNameUnparse(match);
533     h = strchr(fontname, ':');
534     if (h)
535 	*h = 0;
536 
537     /* try get the face directly */
538     result = FcPatternGetFTFace(match, FC_FT_FACE, 0, &face);
539     if (FcResultMatch == result) {
540 	fprintf(stderr,"using \"%s\", face=%p\n",fontname,face);
541 	return face;
542     }
543 
544     /* failing that use the filename */
545     result = FcPatternGetString (match, FC_FILE, 0, &filename);
546     if (FcResultMatch == result) {
547 	result = FcPatternGetDouble(match, FC_PIXEL_SIZE, 0, &pixelsize);
548 	if (FcResultMatch != result)
549 	    pixelsize = 16;
550 	fprintf(stderr,"using \"%s\", pixelsize=%.2lf file=%s\n",
551 		fontname,pixelsize,filename);
552 	rc = FT_New_Face (freetype, filename, 0, &face);
553 	if (rc)
554 	    return NULL;
555 	FT_Set_Pixel_Sizes(face, 0, (int)pixelsize);
556 	return face;
557     }
558 
559     /* oops, didn't work */
560     return NULL;
561 }
562