aboutsummaryrefslogtreecommitdiffstats
path: root/fb-gui.c
diff options
context:
space:
mode:
authorkraxel <kraxel>2004-06-07 19:19:20 +0000
committerkraxel <kraxel>2004-06-07 19:19:20 +0000
commit7c0a76f027d5820af7f2bbbe6abb3234383a5d9b (patch)
tree8221d4c482d5518c0de163350fc7fe59ea76d033 /fb-gui.c
parentec23de55331d3597e625ab42f7b50394ae34be1f (diff)
downloadfbida-7c0a76f027d5820af7f2bbbe6abb3234383a5d9b.tar.gz
- fbi: switch over text rendering to freetype2 + fontconfig.
Diffstat (limited to 'fb-gui.c')
-rw-r--r--fb-gui.c615
1 files changed, 528 insertions, 87 deletions
diff --git a/fb-gui.c b/fb-gui.c
index 3c6328c..eb04e46 100644
--- a/fb-gui.c
+++ b/fb-gui.c
@@ -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);
+}