Bump to version 1.22.1
[platform/upstream/busybox.git] / miscutils / fbsplash.c
index 51ba472..12a77b7 100644 (file)
@@ -50,6 +50,10 @@ struct globals {
        struct fb_var_screeninfo scr_var;
        struct fb_fix_screeninfo scr_fix;
        unsigned bytes_per_pixel;
+       // cached (8 - scr_var.COLOR.length):
+       unsigned red_shift;
+       unsigned green_shift;
+       unsigned blue_shift;
 };
 #define G (*ptr_to_globals)
 #define INIT_G() do { \
@@ -74,6 +78,43 @@ struct globals {
 #define DEBUG_MESSAGE(...) ((void)0)
 #endif
 
+/**
+ * Configure palette for RGB:332
+ */
+static void fb_setpal(int fd)
+{
+       struct fb_cmap cmap;
+       /* fb colors are 16 bit */
+       unsigned short red[256], green[256], blue[256];
+       unsigned i;
+
+       /* RGB:332 */
+       for (i = 0; i < 256; i++) {
+               /* Color is encoded in pixel value as rrrgggbb.
+                * 3-bit color is mapped to 16-bit one as:
+                * 000 -> 00000000 00000000
+                * 001 -> 00100100 10010010
+                * ...
+                * 011 -> 01101101 10110110
+                * 100 -> 10010010 01001001
+                * ...
+                * 111 -> 11111111 11111111
+                */
+               red[i]   = (( i >> 5       ) * 0x9249) >> 2; // rrr * 00 10010010 01001001 >> 2
+               green[i] = (((i >> 2) & 0x7) * 0x9249) >> 2; // ggg * 00 10010010 01001001 >> 2
+               /* 2-bit color is easier: */
+               blue[i]  =  ( i       & 0x3) * 0x5555; // bb * 01010101 01010101
+       }
+
+       cmap.start = 0;
+       cmap.len   = 256;
+       cmap.red   = red;
+       cmap.green = green;
+       cmap.blue  = blue;
+       cmap.transp = 0;
+
+       xioctl(fd, FBIOPUTCMAP, &cmap);
+}
 
 /**
  * Open and initialize the framebuffer device
@@ -87,13 +128,29 @@ static void fb_open(const char *strfb_device)
        xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var);
        xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix);
 
-       if (G.scr_var.bits_per_pixel < 16 || G.scr_var.bits_per_pixel > 32)
+       switch (G.scr_var.bits_per_pixel) {
+       case 8:
+               fb_setpal(fbfd);
+               break;
+
+       case 16:
+       case 24:
+       case 32:
+               break;
+
+       default:
                bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel);
+               break;
+       }
+
+       G.red_shift   = 8 - G.scr_var.red.length;
+       G.green_shift = 8 - G.scr_var.green.length;
+       G.blue_shift  = 8 - G.scr_var.blue.length;
        G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) >> 3;
 
        // map the device in memory
        G.addr = mmap(NULL,
-                       G.scr_var.xres * G.scr_var.yres * G.bytes_per_pixel,
+                       G.scr_var.yres * G.scr_fix.line_length,
                        PROT_WRITE, MAP_SHARED, fbfd, 0);
        if (G.addr == MAP_FAILED)
                bb_perror_msg_and_die("mmap");
@@ -105,15 +162,31 @@ static void fb_open(const char *strfb_device)
 
 
 /**
- * Return pixel value of the passed RGB color
+ * Return pixel value of the passed RGB color.
+ * This is performance critical fn.
  */
 static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
 {
+       /* We assume that the r,g,b values are <= 255 */
+
+       if (G.bytes_per_pixel == 1) {
+               r = r        & 0xe0; // 3-bit red
+               g = (g >> 3) & 0x1c; // 3-bit green
+               b =  b >> 6;         // 2-bit blue
+               return r + g + b;
+       }
        if (G.bytes_per_pixel == 2) {
-               r >>= 3;  // 5-bit red
-               g >>= 2;  // 6-bit green
-               b >>= 3;  // 5-bit blue
-               return b + (g << 5) + (r << (5+6));
+               // ARM PL110 on Integrator/CP has RGBA5551 bit arrangement.
+               // We want to support bit locations like that.
+               //
+               // First shift out unused bits
+               r = r >> G.red_shift;
+               g = g >> G.green_shift;
+               b = b >> G.blue_shift;
+               // Then shift the remaining bits to their offset
+               return (r << G.scr_var.red.offset) +
+                       (g << G.scr_var.green.offset) +
+                       (b << G.scr_var.blue.offset);
        }
        // RGB 888
        return b + (g << 8) + (r << 16);
@@ -125,6 +198,9 @@ static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
 static void fb_write_pixel(unsigned char *addr, unsigned pixel)
 {
        switch (G.bytes_per_pixel) {
+       case 1:
+               *addr = pixel;
+               break;
        case 2:
                *(uint16_t *)addr = pixel;
                break;
@@ -154,8 +230,8 @@ static void fb_drawrectangle(void)
        thispix = fb_pixel_value(nred, ngreen, nblue);
 
        // horizontal lines
-       ptr1 = G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * G.bytes_per_pixel;
-       ptr2 = G.addr + ((G.nbar_posy + G.nbar_height - 1) * G.scr_var.xres + G.nbar_posx) * G.bytes_per_pixel;
+       ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+       ptr2 = G.addr + (G.nbar_posy + G.nbar_height - 1) * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
        cnt = G.nbar_width - 1;
        do {
                fb_write_pixel(ptr1, thispix);
@@ -165,14 +241,14 @@ static void fb_drawrectangle(void)
        } while (--cnt >= 0);
 
        // vertical lines
-       ptr1 = G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * G.bytes_per_pixel;
-       ptr2 = G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
+       ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+       ptr2 = G.addr + G.nbar_posy * G.scr_fix.line_length + (G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
        cnt = G.nbar_height - 1;
        do {
                fb_write_pixel(ptr1, thispix);
                fb_write_pixel(ptr2, thispix);
-               ptr1 += G.scr_var.xres * G.bytes_per_pixel;
-               ptr2 += G.scr_var.xres * G.bytes_per_pixel;
+               ptr1 += G.scr_fix.line_length;
+               ptr2 += G.scr_fix.line_length;
        } while (--cnt >= 0);
 }
 
@@ -195,7 +271,7 @@ static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
        cnt1 = ny2pos - ny1pos;
        nypos = ny1pos;
        do {
-               ptr = G.addr + (nypos * G.scr_var.xres + nx1pos) * G.bytes_per_pixel;
+               ptr = G.addr + nypos * G.scr_fix.line_length + nx1pos * G.bytes_per_pixel;
                cnt2 = nx2pos - nx1pos;
                do {
                        fb_write_pixel(ptr, thispix);
@@ -213,14 +289,15 @@ static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
  */
 static void fb_drawprogressbar(unsigned percent)
 {
-       int i, left_x, top_y, width, height;
+       int left_x, top_y, pos_x;
+       unsigned width, height;
 
        // outer box
        left_x = G.nbar_posx;
        top_y = G.nbar_posy;
        width = G.nbar_width - 1;
        height = G.nbar_height - 1;
-       if ((height | width) < 0)
+       if ((int)(height | width) < 0)
                return;
        // NB: "width" of 1 actually makes rect with width of 2!
        fb_drawrectangle();
@@ -230,30 +307,36 @@ static void fb_drawprogressbar(unsigned percent)
        top_y++;
        width -= 2;
        height -= 2;
-       if ((height | width) < 0)
+       if ((int)(height | width) < 0)
                return;
-       fb_drawfullrectangle(
-                       left_x, top_y,
-                                       left_x + width, top_y + height,
-                       G.nbar_colr, G.nbar_colg, G.nbar_colb);
 
+       pos_x = left_x;
        if (percent > 0) {
+               int i, y;
+
                // actual progress bar
-               width = width * percent / 100;
+               pos_x += (unsigned)(width * percent) / 100;
+
+               y = top_y;
                i = height;
                if (height == 0)
                        height++; // divide by 0 is bad
                while (i >= 0) {
                        // draw one-line thick "rectangle"
                        // top line will have gray lvl 200, bottom one 100
-                       unsigned gray_level = 100 + i*100/height;
+                       unsigned gray_level = 100 + (unsigned)i*100 / height;
                        fb_drawfullrectangle(
-                                       left_x, top_y, left_x + width, top_y,
+                                       left_x, y, pos_x, y,
                                        gray_level, gray_level, gray_level);
-                       top_y++;
+                       y++;
                        i--;
                }
        }
+
+       fb_drawfullrectangle(
+                       pos_x, top_y,
+                       left_x + width, top_y + height,
+                       G.nbar_colr, G.nbar_colg, G.nbar_colb);
 }