diff options
Diffstat (limited to 'fb-gui.c')
-rw-r--r-- | fb-gui.c | 615 |
1 files changed, 528 insertions, 87 deletions
@@ -1,73 +1,277 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <math.h> +#include <wchar.h> +#include <sys/ioctl.h> #include <linux/fb.h> +#include <fontconfig/fontconfig.h> +#include <fontconfig/fcfreetype.h> + #include "fbtools.h" -#include "fs.h" +#include "dither.h" #include "fb-gui.h" /* public */ int visible = 1; -/* private */ -static struct fs_font *f; -static char *x11_font = "10x20"; - static int ys = 3; static int xs = 10; /* ---------------------------------------------------------------------- */ -/* clear screen (areas) */ +/* shadow framebuffer -- internals */ -void fb_clear_mem(void) +static float p_gamma = 1; +static unsigned short p_red[256], p_green[256], p_blue[256]; +static struct fb_cmap p_cmap = { 0, 256, p_red, p_green, p_blue }; + +static int32_t s_lut_red[256], s_lut_green[256], s_lut_blue[256]; + +static unsigned char **shadow; +static unsigned int *sdirty,swidth,sheight; + +static unsigned short calc_gamma(int n, int max) { - if (visible) - fb_memset(fb_mem,0,fb_fix.smem_len); + int ret = 65535.0 * pow((float)n/(max), 1 / p_gamma); + if (ret > 65535) ret = 65535; + if (ret < 0) ret = 0; + return ret; } -void fb_clear_screen(void) +static void +linear_palette(int bit) { - if (visible) - fb_memset(fb_mem,0,fb_fix.line_length * fb_var.yres); + int i, size = 256 >> (8 - bit); + + for (i = 0; i < size; i++) + p_red[i] = p_green[i] = p_blue[i] = calc_gamma(i,size); } -static void fb_clear_rect(int x1, int x2, int y1,int y2) +static void +dither_palette(int r, int g, int b) { - unsigned char *ptr; - int y,h; + int rs, gs, bs, i; + + rs = 256 / (r - 1); + gs = 256 / (g - 1); + bs = 256 / (b - 1); + for (i = 0; i < 256; i++) { + p_red[i] = calc_gamma(rs * ((i / (g * b)) % r), 255); + p_green[i] = calc_gamma(gs * ((i / b) % g), 255); + p_blue[i] = calc_gamma(bs * ((i) % b), 255); + } +} + +static void shadow_lut_init_one(int32_t *lut, int bits, int shift) +{ + int i; + + if (bits > 8) + for (i = 0; i < 256; i++) + lut[i] = (i << (bits + shift - 8)); + else + for (i = 0; i < 256; i++) + lut[i] = (i >> (8 - bits)) << shift; +} + +static void shadow_lut_init(int depth) +{ + if (fb_var.red.length && + fb_var.green.length && + fb_var.blue.length) { + /* fb_var.{red|green|blue} looks sane, use it */ + shadow_lut_init_one(s_lut_red, fb_var.red.length, fb_var.red.offset); + shadow_lut_init_one(s_lut_green, fb_var.green.length, fb_var.green.offset); + shadow_lut_init_one(s_lut_blue, fb_var.blue.length, fb_var.blue.offset); + } else { + /* fallback */ + int i; + switch (depth) { + case 15: + for (i = 0; i < 256; i++) { + s_lut_red[i] = (i & 0xf8) << 7; /* bits -rrrrr-- -------- */ + s_lut_green[i] = (i & 0xf8) << 2; /* bits ------gg ggg----- */ + s_lut_blue[i] = (i & 0xf8) >> 3; /* bits -------- ---bbbbb */ + } + break; + case 16: + for (i = 0; i < 256; i++) { + s_lut_red[i] = (i & 0xf8) << 8; /* bits rrrrr--- -------- */ + s_lut_green[i] = (i & 0xfc) << 3; /* bits -----ggg ggg----- */ + s_lut_blue[i] = (i & 0xf8) >> 3; /* bits -------- ---bbbbb */ + } + break; + case 24: + for (i = 0; i < 256; i++) { + s_lut_red[i] = i << 16; /* byte -r-- */ + s_lut_green[i] = i << 8; /* byte --g- */ + s_lut_blue[i] = i; /* byte ---b */ + } + break; + } + } +} + +static void shadow_render_line(int line, unsigned char *dest, char unsigned *buffer) +{ + unsigned char *ptr = (void*)dest; + unsigned short *ptr2 = (void*)dest; + unsigned long *ptr4 = (void*)dest; + int x; + + switch (fb_var.bits_per_pixel) { + case 8: + dither_line(buffer, ptr, line, swidth); + break; + case 15: + case 16: + for (x = 0; x < swidth; x++) { + ptr2[x] = s_lut_red[buffer[x*3]] | + s_lut_green[buffer[x*3+1]] | + s_lut_blue[buffer[x*3+2]]; + } + break; + case 24: + for (x = 0; x < swidth; x++) { + ptr[3*x+2] = buffer[3*x+0]; + ptr[3*x+1] = buffer[3*x+1]; + ptr[3*x+0] = buffer[3*x+2]; + } + break; + case 32: + for (x = 0; x < swidth; x++) { + ptr4[x] = s_lut_red[buffer[x*3]] | + s_lut_green[buffer[x*3+1]] | + s_lut_blue[buffer[x*3+2]]; + } + break; + } +} + +/* ---------------------------------------------------------------------- */ +/* shadow framebuffer -- management interface */ + +void shadow_render(void) +{ + unsigned int offset = 0; + int i; if (!visible) return; + for (i = 0; i < sheight; i++, offset += fb_fix.line_length) { + if (0 == sdirty[i]) + continue; + shadow_render_line(i, fb_mem + offset, shadow[i]); + sdirty[i] = 0; + } +} + +void shadow_clear_lines(int first, int last) +{ + int i; + + for (i = first; i <= last; i++) { + memset(shadow[i],0,3*swidth); + sdirty[i]++; + } +} + +void shadow_clear(void) +{ + shadow_clear_lines(0, sheight-1); +} + +void shadow_set_palette(int fd) +{ + if (fb_fix.visual != FB_VISUAL_DIRECTCOLOR && fb_var.bits_per_pixel != 8) + return; + if (-1 == ioctl(fd,FBIOPUTCMAP,&p_cmap)) { + perror("ioctl FBIOPUTCMAP"); + exit(1); + } +} - if (x2 < x1) - h = x2, x2 = x1, x1 = h; - if (y2 < y1) - h = y2, y2 = y1, y1 = h; - ptr = fb_mem; - ptr += y1 * fb_fix.line_length; - ptr += x1 * fs_bpp; +void shadow_init(void) +{ + int i; - for (y = y1; y <= y2; y++) { - fb_memset(ptr, 0, (x2 - x1 + 1) * fs_bpp); - ptr += fb_fix.line_length; + /* init shadow fb */ + swidth = fb_var.xres; + sheight = fb_var.yres; + shadow = malloc(sizeof(unsigned char*) * sheight); + sdirty = malloc(sizeof(unsigned int) * sheight); + for (i = 0; i < sheight; i++) + shadow[i] = malloc(swidth*3); + shadow_clear(); + + /* init rendering */ + switch (fb_var.bits_per_pixel) { + case 8: + dither_palette(8, 8, 4); + init_dither(8, 8, 4, 2); + dither_line = dither_line_color; + break; + case 15: + case 16: + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR) + linear_palette(5); + if (fb_var.green.length == 5) { + shadow_lut_init(15); + } else { + shadow_lut_init(16); + } + break; + case 24: + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR) + linear_palette(8); + break; + case 32: + if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR) + linear_palette(8); + shadow_lut_init(24); + break; + default: + fprintf(stderr, "Oops: %i bit/pixel ???\n", + fb_var.bits_per_pixel); + exit(1); } } +void shadow_fini(void) +{ + int i; + + if (!shadow) + return; + for (i = 0; i < sheight; i++) + free(shadow[i]); + free(shadow); + free(sdirty); +} + /* ---------------------------------------------------------------------- */ -/* draw lines */ +/* shadow framebuffer -- drawing interface */ -static void fb_setpixel(int x, int y, unsigned int color) +static void shadow_setpixel(int x, int y) { - unsigned char *ptr; + unsigned char *dest = shadow[y] + 3*x; - ptr = fb_mem; - ptr += y * fb_fix.line_length; - ptr += x * fs_bpp; - fs_setpixel(ptr, color); + if (x < 0) + return; + if (x >= swidth) + return; + if (y < 0) + return; + if (y >= sheight) + return; + *(dest++) = 255; + *(dest++) = 255; + *(dest++) = 255; + sdirty[y]++; } -static void fb_line(int x1, int x2, int y1,int y2) +void shadow_draw_line(int x1, int x2, int y1,int y2) { int x,y,h; float inc; @@ -81,84 +285,237 @@ static void fb_line(int x1, int x2, int y1,int y2) inc = (float)(x2-x1)/(float)(y2-y1); for (y = y1; y <= y2; y++) { x = x1 + inc * (y - y1); - fb_setpixel(x,y,fs_white); + shadow_setpixel(x,y); } } else { inc = (float)(y2-y1)/(float)(x2-x1); for (x = x1; x <= x2; x++) { y = y1 + inc * (x - x1); - fb_setpixel(x,y,fs_white); + shadow_setpixel(x,y); } } } -static void fb_rect(int x1, int x2, int y1,int y2) +void shadow_draw_rect(int x1, int x2, int y1,int y2) { - fb_line(x1, x2, y1, y1); - fb_line(x1, x2, y2, y2); - fb_line(x1, x1, y1, y2); - fb_line(x2, x2, y1, y2); + shadow_draw_line(x1, x2, y1, y1); + shadow_draw_line(x1, x2, y2, y2); + shadow_draw_line(x1, x1, y1, y2); + shadow_draw_line(x2, x2, y1, y2); +} + +void shadow_draw_rgbdata(int x, int y, int pixels, unsigned char *rgb) +{ + unsigned char *dest = shadow[y] + 3*x; + + memcpy(dest,rgb,3*pixels); + sdirty[y]++; } -/* ---------------------------------------------------------------------- */ -/* text stuff */ -void fb_text_init1(char *font) +void shadow_darkify(int x1, int x2, int y1,int y2, int percent) { - char *fonts[2] = { font, NULL }; + unsigned char *ptr; + int x,y,h; - if (NULL == f) - f = fs_consolefont(font ? fonts : NULL); -#ifndef X_DISPLAY_MISSING - if (NULL == f && 0 == fs_connect(NULL)) - f = fs_open(font ? font : x11_font); -#endif - if (NULL == f) { - fprintf(stderr,"no font available\n"); - exit(1); + if (x2 < x1) + h = x2, x2 = x1, x1 = h; + if (y2 < y1) + h = y2, y2 = y1, y1 = h; + + for (y = y1; y <= y2; y++) { + if (y < 0) + continue; + if (y >= sheight) + continue; + sdirty[y]++; + ptr = shadow[y]; + for (x = x1; x <= x2; x++) { + if (x < 0) + continue; + if (x >= swidth) + continue; + ptr[3*x+0] = ptr[3*x+0] * percent / 100; + ptr[3*x+1] = ptr[3*x+1] * percent / 100; + ptr[3*x+2] = ptr[3*x+2] * percent / 100; + } } } -void fb_text_init2(void) +void shadow_reverse(int x1, int x2, int y1,int y2) { - fs_init_fb(255); + unsigned char *ptr; + int x,y,h; + + if (x2 < x1) + h = x2, x2 = x1, x1 = h; + if (y2 < y1) + h = y2, y2 = y1, y1 = h; + + for (y = y1; y <= y2; y++) { + if (y < 0) + continue; + if (y >= sheight) + continue; + sdirty[y]++; + ptr = shadow[y]; + for (x = x1; x <= x2; x++) { + if (x < 0) + continue; + if (x >= swidth) + continue; + ptr[3*x+0] = 255-ptr[3*x+0]; + ptr[3*x+1] = 255-ptr[3*x+1]; + ptr[3*x+2] = 255-ptr[3*x+2]; + } + } } -int fb_font_width(void) +/* ---------------------------------------------------------------------- */ +/* shadow framebuffer -- text rendering */ + +static void shadow_draw_glyph(FT_Bitmap *bitmap, int sx, int sy) { - return f->width; + unsigned char *src,*dst; + unsigned int bit; + int x,y; + + src = bitmap->buffer; + for (y = 0; y < bitmap->rows; y++, src += bitmap->pitch) { + if (sy+y < 0) + continue; + if (sy+y >= sheight) + continue; + sdirty[sy+y]++; + dst = shadow[sy+y] + sx*3; + switch (bitmap->pixel_mode) { + case FT_PIXEL_MODE_MONO: + for (x = 0; x < bitmap->width; x++, dst += 3) { + if (sx+x < 0) + continue; + if (sx+x >= swidth) + continue; + bit = (1 << (7-(x&7))); + if (bit & (src[x >> 3])) { + dst[0] = 255; + dst[1] = 255; + dst[2] = 255; + } + } + break; + case FT_PIXEL_MODE_GRAY: + for (x = 0; x < bitmap->width; x++, dst += 3) { + if (sx+x < 0) + continue; + if (sx+x >= swidth) + continue; + if (src[x]) { + dst[0] += (255-dst[0]) * src[x] / 255; + dst[1] += (255-dst[1]) * src[x] / 255; + dst[2] += (255-dst[2]) * src[x] / 255; + } + } + break; + } + } } -void fb_status_line(unsigned char *msg) +struct glyph { + FT_Glyph glyph; + int pos; +}; + +int shadow_draw_string(FT_Face face, int x, int y, wchar_t *str, int align) { - int y; + struct glyph *glyphs; + FT_UInt gi,pgi; + FT_Vector delta,pen; + FT_Glyph image; + FT_BitmapGlyph bit; + size_t len; + int i,ng,pos,kerning; + + len = wcslen(str); + glyphs = malloc(sizeof(*glyphs) * len); + memset(glyphs,0,sizeof(*glyphs) * len); + + kerning = FT_HAS_KERNING(face); + pgi = 0; - if (!visible) - return; - y = fb_var.yres - f->height - ys; - fb_memset(fb_mem + fb_fix.line_length * y, 0, - fb_fix.line_length * (f->height+ys)); - fb_line(0, fb_var.xres, y, y); - fs_puts(f, 0, y+ys, msg); + for (ng = 0, pos = 0, i = 0; str[i] != 0; i++) { + gi = FT_Get_Char_Index(face, str[i]); + if (kerning && pgi && gi) { + FT_Get_Kerning(face,pgi,gi,FT_KERNING_DEFAULT,&delta); + pos += delta.x; + } + glyphs[ng].pos = pos; + if (0 != FT_Load_Glyph(face, gi, FT_LOAD_DEFAULT)) + continue; + if (0 != FT_Get_Glyph(face->glyph, &glyphs[ng].glyph)) + continue; + pos += face->glyph->advance.x; + pgi = gi; + ng++; + } + + for (i = 0; i < ng; i++) { + pen.x = (x << 6) + glyphs[i].pos; + pen.y = (sheight - y) << 6; + switch(align) { + case -1: /* left */ + break; + case 0: /* center */ + pen.x -= pos/2; + break; + case 1: /* right */ + pen.x -= pos; + break; + } + image = glyphs[i].glyph; + if (0 != FT_Glyph_To_Bitmap(&image,FT_RENDER_MODE_NORMAL,&pen,0)) + continue; + bit = (FT_BitmapGlyph)image; + shadow_draw_glyph(&bit->bitmap, bit->left, sheight - bit->top); + FT_Done_Glyph(image); + } + + for (i = 0; i < ng; i++) { + FT_Done_Glyph(glyphs[i].glyph); + } + free(glyphs); + + return pos >> 6; } -void fb_edit_line(unsigned char *str, int pos) +void shadow_draw_string_cursor(FT_Face face, int x, int y, wchar_t *str, int pos) { - int x,y; - - if (!visible) - return; - y = fb_var.yres - f->height - ys; - x = pos * f->width; - fb_memset(fb_mem + fb_fix.line_length * y, 0, - fb_fix.line_length * (f->height+ys)); - fb_line(0, fb_var.xres, y, y); - fs_puts(f, 0, y+ys, str); - fb_line(x, x + f->width, fb_var.yres-1, fb_var.yres-1); - fb_line(x, x + f->width, fb_var.yres-2, fb_var.yres-2); + wchar_t save; + int len, left, width, y1, y2; + + len = wcslen(str); + if (pos >= len) { + left = shadow_draw_string(face, x, y, str, -1); + width = shadow_draw_string(face, x+left, y, L" ", -1); + } else { + save = str[pos]; + str[pos] = 0; + left = shadow_draw_string(face, x, y, str, -1); + str[pos] = save; + + save = str[pos+1]; + str[pos+1] = 0; + width = shadow_draw_string(face, x+left, y, str+pos, -1); + str[pos+1] = save; + + shadow_draw_string(face, x+left+width, y, str+pos+1, -1); + } + + y2 = y - (face->size->metrics.descender >> 6) -1; + y1 = y2 - (face->size->metrics.height >> 6) +1; + shadow_reverse(left,left+width,y1,y2); } -void fb_text_box(int x, int y, char *lines[], unsigned int count) +void shadow_draw_text_box(FT_Face face, int x, int y, int percent, wchar_t *lines[], unsigned int count) { unsigned int i,len,max, x1, x2, y1, y2; @@ -167,23 +524,107 @@ void fb_text_box(int x, int y, char *lines[], unsigned int count) max = 0; for (i = 0; i < count; i++) { - len = strlen(lines[i]); + len = wcslen(lines[i]); if (max < len) max = len; } + + FT_Load_Glyph(face, FT_Get_Char_Index(face, 'm'), FT_LOAD_DEFAULT); x1 = x; - x2 = x + max * f->width; + x2 = x + max * (face->glyph->advance.x >> 6); y1 = y; - y2 = y + count * f->height; + y2 = y + count * (face->size->metrics.height >> 6); x += xs; x2 += 2*xs; y += ys; y2 += 2*ys; + y += (face->size->metrics.height >> 6); + y += (face->size->metrics.descender >> 6); - fb_clear_rect(x1, x2, y1, y2); - fb_rect(x1, x2, y1, y2); + shadow_darkify(x1, x2, y1, y2, percent); + shadow_draw_rect(x1, x2, y1, y2); for (i = 0; i < count; i++) { - fs_puts(f,x,y,lines[i]); - y += f->height; + shadow_draw_string(face, x, y, lines[i], -1); + y += (face->size->metrics.height >> 6); } } +/* ---------------------------------------------------------------------- */ +/* fontconfig + freetype font rendering */ + +static FT_Library freetype; + +void font_init(void) +{ + int rc; + + FcInit(); + rc = FT_Init_FreeType(&freetype); + if (rc) { + fprintf(stderr,"FT_Init_FreeType() failed\n"); + exit(1); + } +} + +FT_Face font_open(char *fcname) +{ + FcResult result = 0; + FT_Face face = NULL; + FcPattern *pattern,*match; + char *fontname,*h; + FcChar8 *filename; + double pixelsize; + int rc; + + /* parse + match font name */ + pattern = FcNameParse(fcname); + FcConfigSubstitute(NULL, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + match = FcFontMatch (0, pattern, &result); + FcPatternDestroy(pattern); + if (FcResultMatch != result) + return NULL; + fontname = FcNameUnparse(match); + h = strchr(fontname, ':'); + if (h) + *h = 0; + + /* try get the face directly */ + result = FcPatternGetFTFace(match, FC_FT_FACE, 0, &face); + if (FcResultMatch == result) { + fprintf(stderr,"using \"%s\", face=%p\n",fontname,face); + return face; + } + + /* failing that use the filename */ + result = FcPatternGetString (match, FC_FILE, 0, &filename); + if (FcResultMatch == result) { + result = FcPatternGetDouble(match, FC_PIXEL_SIZE, 0, &pixelsize); + if (FcResultMatch != result) + pixelsize = 16; + fprintf(stderr,"using \"%s\", pixelsize=%.2lf file=%s\n", + fontname,pixelsize,filename); + rc = FT_New_Face (freetype, filename, 0, &face); + if (rc) + return NULL; + FT_Set_Pixel_Sizes(face, 0, (int)pixelsize); + return face; + } + + /* oops, didn't work */ + return NULL; +} + +/* ---------------------------------------------------------------------- */ +/* clear screen (areas) */ + +void fb_clear_mem(void) +{ + if (visible) + fb_memset(fb_mem,0,fb_fix.smem_len); +} + +void fb_clear_screen(void) +{ + if (visible) + fb_memset(fb_mem,0,fb_fix.line_length * fb_var.yres); +} |