diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c index d33bd8ccac..d15dbba888 100644 --- a/apps/gui/gwps-common.c +++ b/apps/gui/gwps-common.c @@ -216,12 +216,12 @@ bool wps_data_preload_tags(struct wps_data *data, char *buf, } /* load the image */ - ret = read_bmp_file(imgname, &data->img[n].w, - &data->img[n].h, data->img_buf_ptr, - data->img_buf_free); + data->img[n].bm.data = data->img_buf_ptr; + ret = read_bmp_file(imgname, &data->img[n].bm, + data->img_buf_free, + FORMAT_ANY); if (ret > 0) { - data->img[n].ptr = data->img_buf_ptr; data->img_buf_ptr += ret; data->img_buf_free -= ret; data->img[n].loaded = true; @@ -785,7 +785,7 @@ static void clear_image_pos(struct gui_wps *gwps, int n) struct wps_data *data = gwps->data; gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); gwps->display->fillrect(data->img[n].x, data->img[n].y, - data->img[n].w, data->img[n].h); + data->img[n].bm.width, data->img[n].bm.height); gwps->display->set_drawmode(DRMODE_SOLID); } #endif @@ -827,12 +827,13 @@ static const char* skip_conditional(struct gui_wps *gwps, const char* fmt, if(n >= 'A' && n <= 'Z') n = n - 'A' + 26; if(last_x != data->img[n].x || last_y != data->img[n].y - || last_w != data->img[n].w || last_h != data->img[n].h) + || last_w != data->img[n].bm.width + || last_h != data->img[n].bm.height) { last_x = data->img[n].x; last_y = data->img[n].y; - last_w = data->img[n].w; - last_h = data->img[n].h; + last_w = data->img[n].bm.width; + last_h = data->img[n].bm.height; clear_image_pos(gwps,n); } } @@ -1243,9 +1244,19 @@ static void wps_draw_image(struct gui_wps *gwps, int n) else display->set_drawmode(DRMODE_SOLID); - display->mono_bitmap(data->img[n].ptr, data->img[n].x, - data->img[n].y, data->img[n].w, - data->img[n].h); +#if LCD_DEPTH > 1 + if(data->img[n].bm.format == FORMAT_MONO) { +#endif + display->mono_bitmap(data->img[n].bm.data, data->img[n].x, + data->img[n].y, data->img[n].bm.width, + data->img[n].bm.height); +#if LCD_DEPTH > 1 + } else { + display->bitmap((fb_data *)data->img[n].bm.data, data->img[n].x, + data->img[n].y, data->img[n].bm.width, + data->img[n].bm.height); + } +#endif } static void wps_display_images(struct gui_wps *gwps, bool always) { diff --git a/apps/gui/gwps.h b/apps/gui/gwps.h index eb976c9220..8cd4c8f63e 100644 --- a/apps/gui/gwps.h +++ b/apps/gui/gwps.h @@ -255,11 +255,9 @@ extern bool keys_locked; #ifdef HAVE_LCD_BITMAP struct gui_img{ - unsigned char* ptr; /* pointer */ + struct bitmap bm; int x; /* x-pos */ int y; /* y-pos */ - int w; /* width */ - int h; /* height */ bool loaded; /* load state */ bool display; /* is to be displayed */ bool always_display; /* not using the preload/display mechanism */ @@ -274,7 +272,7 @@ struct align_pos { #ifdef HAVE_LCD_BITMAP #define MAX_IMAGES (26*2) /* a-z and A-Z */ -#define IMG_BUFSIZE (LCD_HEIGHT * LCD_WIDTH * MAX_IMAGES/10) / 8 +#define IMG_BUFSIZE (LCD_HEIGHT * LCD_WIDTH * MAX_IMAGES/10) #define WPS_MAX_LINES (LCD_HEIGHT/5+1) #define FORMAT_BUFFER_SIZE 3072 #else diff --git a/apps/plugin.h b/apps/plugin.h index 747c9e099a..10ae180df7 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -97,12 +97,12 @@ #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 3 +#define PLUGIN_API_VERSION 4 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 1 +#define PLUGIN_MIN_API_VERSION 2 /* plugin return codes */ enum plugin_status { @@ -420,8 +420,8 @@ struct plugin_api { bool (*peak_meter_get_use_dbfs)(void); #endif #ifdef HAVE_LCD_BITMAP - int (*read_bmp_file)(char* filename, int *get_width, int *get_height, - char *bitmap, int maxsize); + int (*read_bmp_file)(char* filename, struct bitmap *bm, int maxsize, + int format); void (*screen_dump_set_hook)(void (*hook)(int fh)); #endif int (*show_logo)(void); diff --git a/apps/recorder/bmp.c b/apps/recorder/bmp.c index d2be8c9172..61d7430bb6 100644 --- a/apps/recorder/bmp.c +++ b/apps/recorder/bmp.c @@ -26,11 +26,13 @@ #include #include - +#include #include "debug.h" #include "lcd.h" #include "file.h" -#include "autoconf.h" +#include "config.h" +#include "bmp.h" +#include "lcd.h" #ifdef __GNUC__ #define STRUCT_PACKED __attribute__((packed)) @@ -59,6 +61,12 @@ struct Fileheader { unsigned long ClrImportant; /* important color count */ } STRUCT_PACKED; +struct rgb_quad { /* Little endian */ + unsigned char blue; + unsigned char green; + unsigned char red; + unsigned char reserved; +} STRUCT_PACKED; #ifdef ROCKBOX_LITTLE_ENDIAN #define readshort(x) *(x) @@ -79,6 +87,19 @@ static long readlong(long *value) { #endif +unsigned char brightness(struct rgb_quad color) +{ + return (3 * (unsigned int)color.red + 6 * (unsigned int)color.green + + (unsigned int)color.blue) / 10; +} + +/* Function to get a pixel from a line. (Tomas: maybe a better way?) */ +inline int getpix(int px, unsigned char *bmpbuf) { + int a = (px / 8); + int b = (7 - (px % 8)); + + return (bmpbuf[a] & (1 << b)) != 0; +} /****************************************************************************** @@ -88,16 +109,26 @@ static long readlong(long *value) { * *****************************************************************************/ int read_bmp_file(char* filename, - int *get_width, /* in pixels */ - int *get_height, /* in pixels */ - char *bitmap, - int maxsize) /* Maximum amount of bytes to write to bitmap */ + struct bitmap *bm, + int maxsize, + int format) { struct Fileheader fh; - int bitmap_width, bitmap_height, PaddedWidth, PaddedHeight; + int width, height, PaddedWidth, PaddedHeight; int fd, row, col, ret; - char bmpbuf[(LCD_WIDTH / 8 + 3) & ~3]; /* Buffer for one line */ + struct rgb_quad palette[256]; + int invert_pixel = 0; + int numcolors; + int depth; + int totalsize; + char *bitmap = bm->data; + + unsigned char bmpbuf[LCD_WIDTH*sizeof(struct rgb_quad)]; /* Buffer for one line */ +#if LCD_DEPTH == 1 + (void)format; +#endif + fd = open(filename, O_RDONLY); /* Exit if file opening failed */ @@ -119,14 +150,6 @@ int read_bmp_file(char* filename, return -3; } - /* Exit if not monochrome */ - if (readshort(&fh.BitCount) != 1) { - DEBUGF("error - Bitmap must be in 1 bit per pixel format. " - "This one is: %d\n", readshort(&fh.BitCount)); - close(fd); - return -4; - } - /* Exit if too wide */ if (readlong(&fh.Width) > LCD_WIDTH) { DEBUGF("error - Bitmap is too wide (%d pixels, max is %d)\n", @@ -144,28 +167,80 @@ int read_bmp_file(char* filename, } /* Calculate image size */ - bitmap_height = readlong(&fh.Height); - bitmap_width = readlong(&fh.Width); - /* Paddedwidth is for BMP files. */ - PaddedWidth = ((bitmap_width + 31) & (~0x1f)) / 8; + height = readlong(&fh.Height); + width = readlong(&fh.Width); + depth = readshort(&fh.BitCount); + + /* 4-byte boundary aligned */ + PaddedWidth = (width * depth / 8 + 3) & ~3; + +#if LCD_DEPTH > 1 + if(format == FORMAT_ANY) { + if(depth == 1) + format = FORMAT_MONO; + else + format = FORMAT_NATIVE; + } +#endif + /* PaddedHeight is for rockbox format. */ - PaddedHeight = (bitmap_height + 7) / 8; + if(format == FORMAT_MONO) { + PaddedHeight = (height + 7) / 8; + totalsize = PaddedHeight * width; + } else { +#if LCD_DEPTH == 2 + PaddedHeight = height/4; +#else + PaddedHeight = height; +#endif + totalsize = PaddedHeight * width * sizeof(fb_data); + } /* Check if this fits the buffer */ - if ((PaddedHeight * bitmap_width) > maxsize) { + + if (totalsize > maxsize) { DEBUGF("error - Bitmap is too large to fit the supplied buffer: " - "%d bytes.\n", (PaddedHeight * bitmap_width)); + "%d bytes.\n", (PaddedHeight * width)); close(fd); return -7; } + if (depth <= 8) + { + numcolors = readlong(&fh.ClrUsed); + if (numcolors == 0) + numcolors = 1 << depth; + + if(read(fd, palette, numcolors * sizeof(struct rgb_quad)) + != numcolors * (int)sizeof(struct rgb_quad)) + { + DEBUGF("error - Can't read bitmap's color palette\n"); + close(fd); + return -8; + } + } + + /* Use the darker palette color as foreground on mono bitmaps */ + if(readshort(&fh.BitCount) == 1) { + if(brightness(palette[0]) > brightness(palette[1])) + invert_pixel = 1; + } + /* Search to the beginning of the image data */ lseek(fd, (off_t)readlong(&fh.OffBits), SEEK_SET); +#if LCD_DEPTH == 2 + if(format == FORMAT_NATIVE) + memset(bitmap, 0, width * height / 4); +#endif + +#if LCD_DEPTH > 1 + fb_data *dest = (fb_data *)bitmap; +#endif + /* loop to read rows and put them to buffer */ - for (row = 0; row < bitmap_height; row++) { - int bitsel = 1 << ((bitmap_height - row - 1) % 8); - int bytesel = bitmap_width * ((bitmap_height - row - 1) / 8); + for (row = 0; row < height; row++) { + unsigned char *p; /* read one row */ ret = read(fd, bmpbuf, PaddedWidth); @@ -173,24 +248,115 @@ int read_bmp_file(char* filename, DEBUGF("error reading image, read returned: %d expected was: " "%d\n", ret, PaddedWidth); close(fd); - return -8; + return -9; } - /* loop though the pixels in this line. */ - for (col = 0; col < bitmap_width; col++) { - ret = (bmpbuf[col/8] & (1 << (7 - (col % 8)))) != 0; - if (ret == 1) - bitmap[bytesel + col] &= ~bitsel; - else - bitmap[bytesel + col] |= bitsel; + switch(depth) { + case 1: +#if LCD_DEPTH > 1 + if(format == FORMAT_MONO) { +#endif + /* Mono -> Mono */ + for (col = 0; col < width; col++) { + ret = getpix(col, bmpbuf) ^ invert_pixel; + if (ret == 1) { + bitmap[width * ((height - row - 1) / 8) + col] + &= ~ 1 << ((height - row - 1) % 8); + } else { + bitmap[width * ((height - row - 1) / 8) + col] + |= 1 << ((height - row - 1) % 8); + } + } +#if LCD_DEPTH == 2 + } else { + /* Mono -> 2gray (iriver H1xx) */ + for (col = 0; col < width; col++) { + ret = brightness(palette[getpix(col, bmpbuf)]); + + if (ret > 96) { + bitmap[width * ((height - row - 1) / 8) + col] + &= ~ 1 << ((height - row - 1) % 8); + } else { + bitmap[width * ((height - row - 1) / 8) + col] + |= 1 << ((height - row - 1) % 8); + } + } + } +#elif LCD_DEPTH == 16 + } else { + /* Mono -> RGB16 */ + for (col = 0; col < width; col++) { + ret = getpix(col, bmpbuf); + unsigned short rgb = (((palette[ret].red >> 3) << 11) | + ((palette[ret].green >> 2) << 5) | + ((palette[ret].blue >> 3))); + dest[width * (height - row - 1) + col] = rgb; + } + } +#endif + break; + + case 24: + p = bmpbuf; +#if LCD_DEPTH > 1 + if(format == FORMAT_MONO) { +#endif + /* RGB24 -> mono */ + for (col = 0; col < width; col++) { + struct rgb_quad rgb; + rgb.red = p[2]; + rgb.green = p[1]; + rgb.blue = p[0]; + ret = brightness(rgb); + if (ret > 96) { + bitmap[width * ((height - row - 1) / 8) + col] + &= ~ 1 << ((height - row - 1) % 8); + } else { + bitmap[width * ((height - row - 1) / 8) + col] + |= 1 << ((height - row - 1) % 8); + } + p += 3; + } +#if LCD_DEPTH == 2 + } else { + /* RGB24 -> 2gray (iriver H1xx) */ + for (col = 0; col < width; col++) { + struct rgb_quad rgb; + rgb.red = p[2]; + rgb.green = p[1]; + rgb.blue = p[0]; + ret = brightness(rgb); + + dest[((height - row - 1)/4) * width + col] |= + (~ret & 0xC0) >> (2 * (~(height - row - 1) & 3)); + p += 3; + } + } +#elif LCD_DEPTH == 16 + } else { + /* RGB24 -> RGB16 */ + for (col = 0; col < width; col++) { + unsigned short rgb = (((p[2] >> 3) << 11) | + ((p[1] >> 2) << 5) | + ((p[0] >> 3))); + dest[width * (height - row - 1) + col] = rgb; + p += 3; + } + } +#endif + break; } } close(fd); /* returning image size: */ - *get_width = bitmap_width; - *get_height = bitmap_height; + bm->width = width; + bm->height = height; +#if LCD_DEPTH > 1 + bm->format = format; +#endif - return (PaddedHeight * bitmap_width); /* return the used buffer size. */ +DEBUGF("totalsize: %d\n", totalsize); + return totalsize; /* return the used buffer size. */ } diff --git a/apps/recorder/bmp.h b/apps/recorder/bmp.h index 5414ad893a..3bd8da74b7 100644 --- a/apps/recorder/bmp.h +++ b/apps/recorder/bmp.h @@ -16,6 +16,12 @@ * KIND, either express or implied. * ****************************************************************************/ +#ifndef _BMP_H_ +#define _BMP_H_ + +#include "config.h" +#include "lcd.h" + /********************************************************************* * read_bmp_file() * @@ -24,7 +30,7 @@ * **********************************************/ int read_bmp_file(char* filename, - int *get_width, /* in pixels */ - int *get_height, /* in pixels */ - char *bitmap, - int maxsize);/* Maximum amount of bytes to write to bitmap */ + struct bitmap *bm, + int maxsize, + int format); +#endif diff --git a/apps/screen_access.c b/apps/screen_access.c index dfe2b13a46..8f7f5c5aae 100644 --- a/apps/screen_access.c +++ b/apps/screen_access.c @@ -120,6 +120,7 @@ void screen_init(struct screen * screen, enum screen_type screen_type) screen->mono_bitmap=&lcd_mono_bitmap; screen->set_drawmode=&lcd_set_drawmode; #if LCD_DEPTH > 1 + screen->bitmap=&lcd_bitmap; screen->set_background=&lcd_set_background; #endif screen->update_rect=&lcd_update_rect; diff --git a/apps/screen_access.h b/apps/screen_access.h index 1c694e8d31..af52527fc0 100644 --- a/apps/screen_access.h +++ b/apps/screen_access.h @@ -82,6 +82,8 @@ struct screen int style, int offset); void (*mono_bitmap)(const unsigned char *src, int x, int y, int width, int height); + void (*bitmap)(const fb_data *src, + int x, int y, int width, int height); void (*set_drawmode)(int mode); #if (LCD_DEPTH > 1) || (LCD_REMOTE_DEPTH > 1) void (*set_background)(unsigned background); diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index 2779bd9c95..393e91f93e 100644 --- a/firmware/export/lcd.h +++ b/firmware/export/lcd.h @@ -193,6 +193,23 @@ extern fb_data lcd_framebuffer[LCD_HEIGHT][LCD_WIDTH]; extern void lcd_enable(bool on); #endif +/* Bitmap formats */ +enum +{ + FORMAT_MONO, + FORMAT_NATIVE, + FORMAT_ANY /* For passing to read_bmp_file() */ +}; + +struct bitmap { + int width; + int height; +#if LCD_DEPTH > 1 + int format; +#endif + unsigned char *data; +}; + extern void lcd_set_invert_display(bool yesno); extern void lcd_set_flip(bool yesno); extern void lcd_roll(int pixels);