Logo Search packages:      
Sourcecode: fbi version File versions  Download package

fb-gui.c

#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 "dither.h"
#include "fb-gui.h"

/* public */
int visible = 1;

static int ys =  3;
static int xs = 10;

/* ---------------------------------------------------------------------- */
/* shadow framebuffer -- internals                                        */

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)
{
    int ret = 65535.0 * pow((float)n/(max), 1 / p_gamma);
    if (ret > 65535) ret = 65535;
    if (ret <     0) ret =     0;
    return ret;
}

static void
linear_palette(int bit)
{
    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
dither_palette(int r, int g, int b)
{
    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_dirty(void)
{
    int i;

    for (i = 0; i < sheight; i++)
      sdirty[i]++;
}

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);
    }
}

void shadow_init(void)
{
    int i;

    /* 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);
}

/* ---------------------------------------------------------------------- */
/* shadow framebuffer -- drawing interface                                */

static void shadow_setpixel(int x, int y)
{
    unsigned char *dest = shadow[y] + 3*x;

    if (x < 0)
      return;
    if (x >= swidth)
      return;
    if (y < 0)
      return;
    if (y >= sheight)
      return;
    *(dest++) = 255;
    *(dest++) = 255;
    *(dest++) = 255;
    sdirty[y]++;
}

void shadow_draw_line(int x1, int x2, int y1,int y2)
{
    int x,y,h;
    float inc;

    if (x2 < x1)
      h = x2, x2 = x1, x1 = h;
    if (y2 < y1)
      h = y2, y2 = y1, y1 = h;

    if (x2 - x1 < y2 - y1) {
      inc = (float)(x2-x1)/(float)(y2-y1);
      for (y = y1; y <= y2; y++) {
          x = x1 + inc * (y - y1);
          shadow_setpixel(x,y);
      }
    } else {
      inc = (float)(y2-y1)/(float)(x2-x1);
      for (x = x1; x <= x2; x++) {
          y = y1 + inc * (x - x1);
          shadow_setpixel(x,y);
      }
    }
}

void shadow_draw_rect(int x1, int x2, int y1,int 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]++;
}


void shadow_darkify(int x1, int x2, int y1,int y2, int percent)
{
    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] = 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 shadow_reverse(int x1, int x2, int y1,int y2)
{
    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];
      }
    }
}

/* ---------------------------------------------------------------------- */
/* shadow framebuffer -- text rendering                                   */

static void shadow_draw_glyph(FT_Bitmap *bitmap, int sx, int sy)
{
    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;
      }
    }
}

struct glyph {
    FT_Glyph  glyph;
    int       pos;
};

int shadow_draw_string(FT_Face face, int x, int y, wchar_t *str, int align)
{
    struct glyph   *glyphs;
    FT_UInt        gi,pgi;
    FT_Vector      delta,pen;
    FT_Glyph       image;
    FT_BitmapGlyph bit;
    size_t         len;
    int            i,ng,pos;
    int            kerning,scalable;

    len = wcslen(str);
    glyphs = malloc(sizeof(*glyphs) * len);
    memset(glyphs,0,sizeof(*glyphs) * len);

    kerning  = FT_HAS_KERNING(face);
    scalable = FT_IS_SCALABLE(face);
    pgi = 0;
    
    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++;
    }

    switch(align) {
    case -1: /* left */
      break;
    case 0: /* center */
      x -= pos >> 7;
      break;
    case 1: /* right */
      x -= pos >> 6;
      break;
    }
    pen.x = 0;
    pen.y = 0;
    for (i = 0; i < ng; i++) {
      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,
                    x + bit->left + (glyphs[i].pos >> 6),
                    y - bit->top);
      if (image != glyphs[i].glyph)
          FT_Done_Glyph(image);
    }

    for (i = 0; i < ng; i++)
      FT_Done_Glyph(glyphs[i].glyph);
    free(glyphs);

    return pos >> 6;
}

void shadow_draw_string_cursor(FT_Face face, int x, int y, wchar_t *str, int pos)
{
    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 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;

    if (!visible)
      return;

    max = 0;
    for (i = 0; i < count; i++) {
      len = wcslen(lines[i]);
      if (max < len)
          max = len;
    }

    FT_Load_Glyph(face, FT_Get_Char_Index(face, 'x'), FT_LOAD_DEFAULT);
    x1 = x;
    x2 = x + max * (face->glyph->advance.x >> 6);
    y1 = y;
    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);
    
    shadow_darkify(x1, x2, y1, y2, percent);
    shadow_draw_rect(x1, x2, y1, y2);
    for (i = 0; i < count; i++) {
      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);
}

Generated by  Doxygen 1.6.0   Back to index