Add gbs.conf
[profile/ivi/psplash.git] / psplash-fb.c
index 5bb110c..71740cd 100644 (file)
@@ -1,5 +1,5 @@
-/* 
- *  pslash - a lightweight framebuffer splashscreen for embedded devices. 
+/*
+ *  pslash - a lightweight framebuffer splashscreen for embedded devices.
  *
  *  Copyright (c) 2006 Matthew Allum <mallum@o-hand.com>
  *
@@ -25,8 +25,79 @@ psplash_fb_destroy (PSplashFB *fb)
   free(fb);
 }
 
+static int
+attempt_to_change_pixel_format (PSplashFB *fb,
+                                struct fb_var_screeninfo *fb_var)
+{
+  /* By default the framebuffer driver may have set an oversized
+   * yres_virtual to support VT scrolling via the panning interface.
+   *
+   * We don't try and maintain this since it's more likely that we
+   * will fail to increase the bpp if the driver's pre allocated
+   * framebuffer isn't large enough.
+   */
+  fb_var->yres_virtual = fb_var->yres;
+
+  /* First try setting an 8,8,8,0 pixel format so we don't have to do
+   * any conversions while drawing. */
+
+  fb_var->bits_per_pixel = 32;
+
+  fb_var->red.offset = 0;
+  fb_var->red.length = 8;
+
+  fb_var->green.offset = 8;
+  fb_var->green.length = 8;
+
+  fb_var->blue.offset = 16;
+  fb_var->blue.length = 8;
+
+  fb_var->transp.offset = 0;
+  fb_var->transp.length = 0;
+
+  if (ioctl (fb->fd, FBIOPUT_VSCREENINFO, fb_var) == 0)
+    {
+      fprintf(stdout, "Switched to a 32 bpp 8,8,8 frame buffer\n");
+      return 1;
+    }
+  else
+    {
+      fprintf(stderr,
+              "Error, failed to switch to a 32 bpp 8,8,8 frame buffer\n");
+    }
+
+  /* Otherwise try a 16bpp 5,6,5 format */
+
+  fb_var->bits_per_pixel = 16;
+
+  fb_var->red.offset = 11;
+  fb_var->red.length = 5;
+
+  fb_var->green.offset = 5;
+  fb_var->green.length = 6;
+
+  fb_var->blue.offset = 0;
+  fb_var->blue.length = 5;
+
+  fb_var->transp.offset = 0;
+  fb_var->transp.length = 0;
+
+  if (ioctl (fb->fd, FBIOPUT_VSCREENINFO, fb_var) == 0)
+    {
+      fprintf(stdout, "Switched to a 16 bpp 5,6,5 frame buffer\n");
+      return 1;
+    }
+  else
+    {
+      fprintf(stderr,
+              "Error, failed to switch to a 16 bpp 5,6,5 frame buffer\n");
+    }
+
+  return 0;
+}
+
 PSplashFB*
-psplash_fb_new (void)
+psplash_fb_new (int angle)
 {
   struct fb_var_screeninfo fb_var;
   struct fb_fix_screeninfo fb_fix;
@@ -44,10 +115,10 @@ psplash_fb_new (void)
       perror ("Error no memory");
       goto fail;
     }
-  
+
   memset (fb, 0, sizeof(PSplashFB));
 
-  fb->fd = -1;  
+  fb->fd = -1;
 
   if ((fb->fd = open (fbdev, O_RDWR)) < 0)
     {
@@ -55,30 +126,73 @@ psplash_fb_new (void)
       goto fail;
     }
 
-  if (ioctl (fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1
-      || ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
+  if (ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
     {
-      perror ("Error getting framebuffer info");
+      perror ("Error getting variable framebuffer info");
       goto fail;
     }
-  
+
   if (fb_var.bits_per_pixel < 16)
     {
       fprintf(stderr,
-             "Error, no support currently for %i bpp frame buffers\n",
-             fb_var.bits_per_pixel);
+              "Error, no support currently for %i bpp frame buffers\n"
+              "Trying to change pixel format...\n",
+              fb_var.bits_per_pixel);
+      if (!attempt_to_change_pixel_format (fb, &fb_var))
+        goto fail;
+    }
+
+  if (ioctl (fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
+    {
+      perror ("Error getting variable framebuffer info (2)");
+      goto fail;
+    }
+
+  /* NB: It looks like the fbdev concept of fixed vs variable screen info is
+   * broken. The line_length is part of the fixed info but it can be changed
+   * if you set a new pixel format. */
+  if (ioctl (fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
+    {
+      perror ("Error getting fixed framebuffer info");
+      goto fail;
     }
 
-  fb->width  = fb_var.xres;
-  fb->height = fb_var.yres;
+  fb->real_width  = fb->width  = fb_var.xres;
+  fb->real_height = fb->height = fb_var.yres;
   fb->bpp    = fb_var.bits_per_pixel;
   fb->stride = fb_fix.line_length;
   fb->type   = fb_fix.type;
   fb->visual = fb_fix.visual;
 
-  DBG("width: %i, height: %i, bpp: %i, stride: %i", 
-      fb->width, fb->height, fb->bpp, fb->stride);
+  fb->red_offset = fb_var.red.offset;
+  fb->red_length = fb_var.red.length;
+  fb->green_offset = fb_var.green.offset;
+  fb->green_length = fb_var.green.length;
+  fb->blue_offset = fb_var.blue.offset;
+  fb->blue_length = fb_var.blue.length;
+
+  if (fb->red_offset == 11 && fb->red_length == 5 &&
+      fb->green_offset == 5 && fb->green_length == 6 &&
+      fb->blue_offset == 0 && fb->blue_length == 5) {
+         fb->rgbmode = RGB565;
+  } else if (fb->red_offset == 0 && fb->red_length == 5 &&
+      fb->green_offset == 5 && fb->green_length == 6 &&
+      fb->blue_offset == 11 && fb->blue_length == 5) {
+         fb->rgbmode = BGR565;
+  } else if (fb->red_offset == 16 && fb->red_length == 8 &&
+      fb->green_offset == 8 && fb->green_length == 8 &&
+      fb->blue_offset == 0 && fb->blue_length == 8) {
+         fb->rgbmode = RGB888;
+  } else if (fb->red_offset == 0 && fb->red_length == 8 &&
+      fb->green_offset == 8 && fb->green_length == 8 &&
+      fb->blue_offset == 8 && fb->blue_length == 8) {
+         fb->rgbmode = BGR888;
+  } else {
+         fb->rgbmode = GENERIC;
+  }
 
+  DBG("width: %i, height: %i, bpp: %i, stride: %i",
+      fb->width, fb->height, fb->bpp, fb->stride);
 
   fb->base = (char *) mmap ((caddr_t) NULL,
                            /*fb_fix.smem_len */
@@ -86,8 +200,8 @@ psplash_fb_new (void)
                            PROT_READ|PROT_WRITE,
                            MAP_SHARED,
                            fb->fd, 0);
-    
-  if (fb->base == (char *)-1) 
+
+  if (fb->base == (char *)-1)
     {
       perror("Error cannot mmap framebuffer ");
       goto fail;
@@ -113,12 +227,27 @@ psplash_fb_new (void)
 
     ioctl (fb, FBIOGETCMAP, &cmap);
   }
-  
+
   if (!status)
     atexit (bogl_done);
   status = 2;
 #endif
 
+  fb->angle = angle;
+
+  switch (fb->angle)
+    {
+    case 270:
+    case 90:
+      fb->width  = fb->real_height;
+      fb->height = fb->real_width;
+      break;
+    case 180:
+    case 0:
+    default:
+      break;
+    }
+
   return fb;
 
  fail:
@@ -129,10 +258,12 @@ psplash_fb_new (void)
   return NULL;
 }
 
-void
-psplash_fb_plot_pixel (PSplashFB    *fb, 
-                      int          x, 
-                      int          y, 
+#define OFFSET(fb,x,y) (((y) * (fb)->stride) + ((x) * ((fb)->bpp >> 3)))
+
+inline void
+psplash_fb_plot_pixel (PSplashFB    *fb,
+                      int          x,
+                      int          y,
                       uint8        red,
                       uint8        green,
                       uint8        blue)
@@ -142,33 +273,84 @@ psplash_fb_plot_pixel (PSplashFB    *fb,
   if (x < 0 || x > fb->width-1 || y < 0 || y > fb->height-1)
     return;
 
-  off = (y * fb->stride) + (x * (fb->bpp >> 3));
-
-  /* FIXME: handle no RGB orderings */
-  switch (fb->bpp)
+  switch (fb->angle)
     {
-    case 24:
-    case 32:
-      *(fb->data + off)     = red;
-      *(fb->data + off + 1) = green;
-      *(fb->data + off + 2) = blue;
+    case 270:
+      off = OFFSET (fb, fb->height - y - 1, x);
       break;
-    case 16:
-      *(volatile uint16 *) (fb->data + off) 
-       = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
+    case 180:
+      off = OFFSET (fb, fb->width - x - 1, fb->height - y - 1);
       break;
+    case 90:
+      off = OFFSET (fb, y, fb->width - x - 1);
+      break;
+    case 0:
     default:
-      /* depth not supported yet */
+      off = OFFSET (fb, x, y);
       break;
     }
 
+  if (fb->rgbmode == RGB565 || fb->rgbmode == RGB888) {
+    switch (fb->bpp)
+      {
+      case 24:
+      case 32:
+        *(fb->data + off)     = blue;
+        *(fb->data + off + 1) = green;
+        *(fb->data + off + 2) = red;
+        break;
+      case 16:
+        *(volatile uint16_t *) (fb->data + off)
+         = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
+        break;
+      default:
+        /* depth not supported yet */
+        break;
+      }
+  } else if (fb->rgbmode == BGR565 || fb->rgbmode == BGR888) {
+    switch (fb->bpp)
+      {
+      case 24:
+      case 32:
+        *(fb->data + off)     = red;
+        *(fb->data + off + 1) = green;
+        *(fb->data + off + 2) = blue;
+        break;
+      case 16:
+        *(volatile uint16_t *) (fb->data + off)
+         = ((blue >> 3) << 11) | ((green >> 2) << 5) | (red >> 3);
+        break;
+      default:
+        /* depth not supported yet */
+        break;
+      }
+  } else {
+    switch (fb->bpp)
+      {
+      case 32:
+        *(volatile uint32_t *) (fb->data + off)
+         = ((red >> (8 - fb->red_length)) << fb->red_offset) 
+             | ((green >> (8 - fb->green_length)) << fb->green_offset)
+             | ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
+        break;
+      case 16:
+        *(volatile uint16_t *) (fb->data + off)
+         = ((red >> (8 - fb->red_length)) << fb->red_offset) 
+             | ((green >> (8 - fb->green_length)) << fb->green_offset)
+             | ((blue >> (8 - fb->blue_length)) << fb->blue_offset);
+        break;
+      default:
+        /* depth not supported yet */
+        break;
+      }
+  }
 }
 
 void
-psplash_fb_draw_rect (PSplashFB    *fb, 
-                     int          x, 
-                     int          y, 
-                     int          width, 
+psplash_fb_draw_rect (PSplashFB    *fb,
+                     int          x,
+                     int          y,
+                     int          width,
                      int          height,
                      uint8        red,
                      uint8        green,
@@ -176,36 +358,24 @@ psplash_fb_draw_rect (PSplashFB    *fb,
 {
   int dx, dy;
 
-  for (dy=0; dy < height; dy++)   
+  for (dy=0; dy < height; dy++)
     for (dx=0; dx < width; dx++)
-      {
-       /* FIXME: inline this call */
-       psplash_fb_plot_pixel (fb, x+dx, y+dy, red, green, blue); 
-      }
+       psplash_fb_plot_pixel (fb, x+dx, y+dy, red, green, blue);
 }
 
 void
-psplash_fb_draw_image (PSplashFB    *fb, 
-                      int          x, 
-                      int          y, 
-                      int          img_width, 
+psplash_fb_draw_image (PSplashFB    *fb,
+                      int          x,
+                      int          y,
+                      int          img_width,
                       int          img_height,
                       int          img_bytes_per_pixel,
                       uint8       *rle_data)
 {
-  uint8 *p = rle_data;
-  int    dx = 0, dy = 0,  total_len;
+  uint8       *p = rle_data;
+  int          dx = 0, dy = 0,  total_len;
   unsigned int len;
 
-#if 0
-  for (dy=0; dy < img_height; dy++)   
-    for (dx=0; dx < img_width; dx++)
-      {
-       psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2)); 
-       p += img_bytes_per_pixel;
-      }
-#endif
-
   total_len = img_width * img_height * img_bytes_per_pixel;
 
   /* FIXME: Optimise, check for over runs ... */
@@ -221,7 +391,8 @@ psplash_fb_draw_image (PSplashFB    *fb,
 
          do
            {
-             psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2)); 
+             if (img_bytes_per_pixel < 4 || *(p+3))
+               psplash_fb_plot_pixel (fb, x+dx, y+dy, *(p), *(p+1), *(p+2));
              if (++dx >= img_width) { dx=0; dy++; }
            }
          while (--len && (p - rle_data) < total_len);
@@ -234,7 +405,8 @@ psplash_fb_draw_image (PSplashFB    *fb,
 
          do
            {
-             psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2)); 
+             if (img_bytes_per_pixel < 4 || *(p+3))
+               psplash_fb_plot_pixel (fb, x+dx, y+dy, *(p), *(p+1), *(p+2));
              if (++dx >= img_width) { dx=0; dy++; }
              p += img_bytes_per_pixel;
            }
@@ -242,3 +414,111 @@ psplash_fb_draw_image (PSplashFB    *fb,
        }
     }
 }
+
+/* Font rendering code based on BOGL by Ben Pfaff */
+
+static int
+psplash_font_glyph (const PSplashFont *font, wchar_t wc, u_int32_t **bitmap)
+{
+  int mask = font->index_mask;
+  int i;
+
+  for (;;)
+    {
+      for (i = font->offset[wc & mask]; font->index[i]; i += 2)
+       {
+         if ((font->index[i] & ~mask) == (wc & ~mask))
+           {
+             if (bitmap != NULL)
+               *bitmap = &font->content[font->index[i+1]];
+             return font->index[i] & mask;
+           }
+       }
+    }
+  return 0;
+}
+
+void
+psplash_fb_text_size (PSplashFB          *fb,
+                     int                *width,
+                     int                *height,
+                     const PSplashFont  *font,
+                     const char         *text)
+{
+  char   *c = (char*)text;
+  wchar_t wc;
+  int     k, n, w, h, mw;
+
+  n = strlen (text);
+  mw = h = w = 0;
+
+  mbtowc (0, 0, 0);
+  for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
+    {
+      if (*c == '\n')
+       {
+         if (w > mw)
+           mw = 0;
+         h += font->height;
+         continue;
+       }
+
+      w += psplash_font_glyph (font, wc, NULL);
+    }
+
+  *width  = (w > mw) ? w : mw;
+  *height = (h == 0) ? font->height : h;
+}
+
+void
+psplash_fb_draw_text (PSplashFB         *fb,
+                     int                x,
+                     int                y,
+                     uint8              red,
+                     uint8              green,
+                     uint8              blue,
+                     const PSplashFont *font,
+                     const char        *text)
+{
+  int     h, w, k, n, cx, cy, dx, dy;
+  char   *c = (char*)text;
+  wchar_t wc;
+
+  n = strlen (text);
+  h = font->height;
+  dx = dy = 0;
+
+  mbtowc (0, 0, 0);
+  for (; (k = mbtowc (&wc, c, n)) > 0; c += k, n -= k)
+    {
+      u_int32_t *glyph = NULL;
+
+      if (*c == '\n')
+       {
+         dy += h;
+         dx  = 0;
+         continue;
+       }
+
+      w = psplash_font_glyph (font, wc, &glyph);
+
+      if (glyph == NULL)
+       continue;
+
+      for (cy = 0; cy < h; cy++)
+       {
+         u_int32_t g = *glyph++;
+
+         for (cx = 0; cx < w; cx++)
+           {
+             if (g & 0x80000000)
+               psplash_fb_plot_pixel (fb, x+dx+cx, y+dy+cy,
+                                      red, green, blue);
+             g <<= 1;
+           }
+       }
+
+      dx += w;
+    }
+}
+