Index: apps/recorder/bmp.c =================================================================== --- apps/recorder/bmp.c (revision 16727) +++ apps/recorder/bmp.c (working copy) @@ -163,6 +163,49 @@ int read_bmp_file(const char* filename, return ret; } +#ifdef HAVE_ALBUMART +int get_bmp_size(int fd, struct bitmap *bmp) +{ + struct bmp_header bmph; + + int ret = read(fd, &bmph, sizeof(struct bmp_header)); + + if (ret < 0) + return ret * 10 - 2; + + if (ret != sizeof(struct bmp_header)) { + DEBUGF("get_bmp_size: can't read BMP header."); + return -3; + } + + bmp->width = readlong(&bmph.width); + bmp->height = readlong(&bmph.height); + + /* Top-down BMP file */ + if (bmp->height < 0) + bmp->height = -bmp->height; + + if (-1 == lseek(fd, -ret, SEEK_CUR)) + return 0; + + return bmp_total_size(bmp); +} +#endif /* HAVE_ALBUMART */ + +static struct mutex bmpbuf_mutex; + +void bmp_init(void) +{ + mutex_init(&bmpbuf_mutex); +} + +#define LIMIT_WIDTH (1280) +#if (LCD_WIDTH < LIMIT_WIDTH) +#define MAX_WIDTH LIMIT_WIDTH +#else +#define MAX_WIDTH LCD_WIDTH +#endif +static uint32_t bmpbuf[MAX_WIDTH]; /* Buffer for one line */ /****************************************************************************** * read_bmp_fd() * @@ -183,7 +226,6 @@ int read_bmp_fd(int fd, int rowstart, rowstop, rowstep; unsigned char *bitmap = bm->data; - uint32_t bmpbuf[LCD_WIDTH]; /* Buffer for one line */ uint32_t palette[256]; #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) bool transparent = false; @@ -225,9 +267,9 @@ int read_bmp_fd(int fd, } width = readlong(&bmph.width); - if (width > LCD_WIDTH) { + if (width > MAX_WIDTH) { DEBUGF("read_bmp_fd: Bitmap too wide (%d pixels, max is %d)\n", - width, LCD_WIDTH); + width, MAX_WIDTH); return -4; } @@ -370,6 +412,8 @@ int read_bmp_fd(int fd, union rgb_union *qp; union rgb_union q0, q1; + mutex_lock(&bmpbuf_mutex); + /* read one row */ ret = read(fd, bmpbuf, padded_width); if (ret != padded_width) { @@ -577,6 +621,8 @@ int read_bmp_fd(int fd, if (brightness(*qp++) < 128) *p |= mask; } + mutex_unlock(&bmpbuf_mutex); + } DEBUGF("totalsize: %d\n", totalsize); Index: apps/recorder/bmp.h =================================================================== --- apps/recorder/bmp.h (revision 16727) +++ apps/recorder/bmp.h (working copy) @@ -39,4 +39,36 @@ int read_bmp_fd(int fd, struct bitmap *bm, int maxsize, int format); + +#ifdef HAVE_ALBUMART +static inline int bmp_total_size(struct bitmap *bmp) +{ + int width = bmp->width, height = bmp->height; + int dst_width, dst_height; + +#if LCD_DEPTH == 2 +#if LCD_PIXELFORMAT == HORIZONTAL_PACKING + dst_width = (width + 3) >> 2; + dst_height = height; +#elif LCD_PIXELFORMAT == VERTICAL_PACKING + dst_width = width; + dst_height = (height + 3) >> 2; +#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED + dst_width = width; + dst_height = (height + 7) >> 3; +#endif /* LCD_PIXELFORMAT */ +#elif LCD_DEPTH == 16 + dst_width = width; + dst_height = height; +#endif /* LCD_DEPTH */ + + return dst_width * dst_height * sizeof(fb_data); +} + +int get_total_size(struct bitmap *bmp); + +int get_bmp_size(int fd, + struct bitmap *bmp); +#endif /* HAVE_ALBUMART */ + #endif Index: apps/recorder/albumart.c =================================================================== --- apps/recorder/albumart.c (revision 16727) +++ apps/recorder/albumart.c (working copy) @@ -28,7 +28,9 @@ #include "debug.h" #include "misc.h" #include "settings.h" +#include "bmp.h" +#define SCALE 1000 /* Strip filename from a full path * @@ -292,3 +294,48 @@ void draw_album_art(struct gui_wps *gwps gwps->display->set_drawmode(DRMODE_SOLID); } } + +int get_albumart_size(struct bitmap *dst_bmp, struct bitmap *src_bmp) +{ + struct wps_data *data = gui_wps[0].data; + short aa_max_width = data->albumart_max_width; + short aa_max_height = data->albumart_max_height; + const unsigned char aa_xscale = data->albumart_xscale; + const unsigned char aa_yscale = data->albumart_yscale; + const short width = src_bmp->width; + const short height = src_bmp->height; + + if (aa_max_width < 0 || aa_max_height < 0 || width <= 0 || height <= 0) + return 0; + + dst_bmp->width = width; + dst_bmp->height = height; + + if (aa_max_width == 0) + aa_max_width = width; + if (aa_max_height == 0) + aa_max_height = height; + + if (((aa_xscale & WPS_ALBUMART_DECREASE) && (aa_max_width < width)) || + ((aa_xscale & WPS_ALBUMART_INCREASE) && (aa_max_width > width)) || + ((aa_yscale & WPS_ALBUMART_DECREASE) && (aa_max_height < height)) || + ((aa_yscale & WPS_ALBUMART_INCREASE) && (aa_max_height > height))) { + + /* need to resize */ + const unsigned long factx = aa_max_width * SCALE / width; + const unsigned long facty = aa_max_height * SCALE / height; + + if (factx == facty) { + dst_bmp->width = aa_max_width; + dst_bmp->height = aa_max_height; + } else if (factx < facty) { + dst_bmp->width = aa_max_width; + dst_bmp->height = (height * factx + SCALE/2) / SCALE; + } else { + dst_bmp->width = (width * facty + SCALE/2) / SCALE; + dst_bmp->height = aa_max_height; + } + } + + return bmp_total_size(dst_bmp); +} Index: apps/recorder/albumart.h =================================================================== --- apps/recorder/albumart.h (revision 16727) +++ apps/recorder/albumart.h (working copy) @@ -38,6 +38,8 @@ void draw_album_art(struct gui_wps *gwps bool search_albumart_files(const struct mp3entry *id3, const char *size_string, char *buf, int buflen); +int get_albumart_size(struct bitmap *dst_bmp, struct bitmap *src_bmp); + #endif /* HAVE_ALBUMART */ #endif /* _ALBUMART_H_ */ Index: apps/gui/gwps.c =================================================================== --- apps/gui/gwps.c (revision 16727) +++ apps/gui/gwps.c (working copy) @@ -783,6 +783,9 @@ void gui_sync_wps_screen_init(void) void gui_sync_wps_init(void) { int i; +#ifdef HAVE_LCD_BITMAP + bmp_init(); +#endif FOR_NB_SCREENS(i) { wps_data_init(&wps_datas[i]); Index: apps/gui/wps_parser.c =================================================================== --- apps/gui/wps_parser.c (revision 16727) +++ apps/gui/wps_parser.c (working copy) @@ -647,13 +647,6 @@ static int parse_albumart_load(const cha { const char *_pos, *newline; bool parsing; - const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT | - WPS_ALBUMART_ALIGN_CENTER | - WPS_ALBUMART_ALIGN_RIGHT; - const short yalign_mask = WPS_ALBUMART_ALIGN_TOP | - WPS_ALBUMART_ALIGN_CENTER | - WPS_ALBUMART_ALIGN_BOTTOM; - (void)token; /* silence warning */ /* reset albumart info in wps */ @@ -662,8 +655,10 @@ static int parse_albumart_load(const cha wps_data->albumart_max_height = -1; wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ + wps_data->albumart_xscale = WPS_ALBUMART_SIZING; /* default */ + wps_data->albumart_yscale = WPS_ALBUMART_SIZING; /* default */ - /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */ + /* format: %Cl|x|y|[[l|c|r][d|i|s|n]mwidth]|[[t|c|b][d|i|s|n]mheight]| */ newline = strchr(wps_bufptr, '\n'); @@ -698,35 +693,32 @@ static int parse_albumart_load(const cha case 'l': case 'L': case '+': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_LEFT; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT; break; case 'c': case 'C': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_CENTER; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; break; case 'r': case 'R': case '-': - wps_data->albumart_xalign = - (wps_data->albumart_xalign & xalign_mask) | - WPS_ALBUMART_ALIGN_RIGHT; + wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT; break; case 'd': case 'D': - wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE; + wps_data->albumart_xscale = WPS_ALBUMART_DECREASE; break; case 'i': case 'I': - wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE; + wps_data->albumart_xscale = WPS_ALBUMART_INCREASE; break; case 's': case 'S': - wps_data->albumart_xalign |= - (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + wps_data->albumart_xscale = WPS_ALBUMART_SIZING; + break; + case 'n': + case 'N': + wps_data->albumart_xscale = WPS_ALBUMART_NOSIZING; break; default: parsing = false; @@ -758,35 +750,32 @@ static int parse_albumart_load(const cha case 't': case 'T': case '-': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_TOP; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP; break; case 'c': case 'C': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_CENTER; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; break; case 'b': case 'B': case '+': - wps_data->albumart_yalign = - (wps_data->albumart_yalign & yalign_mask) | - WPS_ALBUMART_ALIGN_BOTTOM; + wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM; break; case 'd': case 'D': - wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE; + wps_data->albumart_yscale = WPS_ALBUMART_DECREASE; break; case 'i': case 'I': - wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE; + wps_data->albumart_yscale = WPS_ALBUMART_INCREASE; break; case 's': case 'S': - wps_data->albumart_yalign |= - (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE); + wps_data->albumart_yscale = WPS_ALBUMART_SIZING; + break; + case 'n': + case 'N': + wps_data->albumart_yscale = WPS_ALBUMART_NOSIZING; break; default: parsing = false; Index: apps/gui/gwps.h =================================================================== --- apps/gui/gwps.h (revision 16727) +++ apps/gui/gwps.h (working copy) @@ -46,13 +46,15 @@ #define WPS_ALBUMART_CHECK 1 /* WPS contains AA conditional tag */ #define WPS_ALBUMART_LOAD 2 /* WPS contains AA tag */ -#define WPS_ALBUMART_ALIGN_RIGHT WPS_ALIGN_RIGHT /* x align: right */ -#define WPS_ALBUMART_ALIGN_CENTER WPS_ALIGN_CENTER /* x/y align: center */ -#define WPS_ALBUMART_ALIGN_LEFT WPS_ALIGN_LEFT /* x align: left */ -#define WPS_ALBUMART_ALIGN_TOP WPS_ALIGN_RIGHT /* y align: top */ -#define WPS_ALBUMART_ALIGN_BOTTOM WPS_ALIGN_LEFT /* y align: bottom */ -#define WPS_ALBUMART_INCREASE 8 /* increase if smaller */ -#define WPS_ALBUMART_DECREASE 16 /* decrease if larger */ +#define WPS_ALBUMART_ALIGN_RIGHT 1 /* x align: right */ +#define WPS_ALBUMART_ALIGN_CENTER 2 /* x/y align: center */ +#define WPS_ALBUMART_ALIGN_LEFT 4 /* x align: left */ +#define WPS_ALBUMART_ALIGN_TOP 1 /* y align: top */ +#define WPS_ALBUMART_ALIGN_BOTTOM 4 /* y align: bottom */ +#define WPS_ALBUMART_NOSIZING 1 /* no sizing */ +#define WPS_ALBUMART_INCREASE 2 /* increase if smaller */ +#define WPS_ALBUMART_DECREASE 4 /* decrease if larger */ +#define WPS_ALBUMART_SIZING (WPS_ALBUMART_INCREASE | WPS_ALBUMART_DECREASE) #endif /* HAVE_ALBUMART */ @@ -341,10 +343,14 @@ struct wps_data unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ short albumart_x; short albumart_y; - unsigned short albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT, - + .._INCREASE, + .._DECREASE */ - unsigned short albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM, - + .._INCREASE, + .._DECREASE */ + unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */ + unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ + /* + * A bit redundant IMO, but needed if we want to have scaling without + * keeping the original aspect ratio - kugel. + */ + unsigned char albumart_xscale; /* WPS_ALBUMART_INCREASE, _DECREASE, _NOSIZING */ + unsigned char albumart_yscale; /* WPS_ALBUMART_INCREASE, _DECREASE, _NOSIZING */ short albumart_max_width; short albumart_max_height; @@ -464,4 +470,6 @@ void gui_sync_wps_screen_init(void); bool gui_sync_wps_uses_albumart(void); #endif +void bmp_init(void); + #endif Index: apps/SOURCES =================================================================== --- apps/SOURCES (revision 16727) +++ apps/SOURCES (working copy) @@ -85,6 +85,7 @@ recorder/keyboard.c recorder/peakmeter.c #ifdef HAVE_ALBUMART recorder/albumart.c +plugins/lib/bmp_smooth_scale.c #endif #ifdef HAVE_LCD_COLOR gui/color_picker.c Index: apps/buffering.c =================================================================== --- apps/buffering.c (revision 16727) +++ apps/buffering.c (working copy) @@ -49,6 +49,8 @@ #include "pcmbuf.h" #include "buffer.h" #include "bmp.h" +#include "albumart.h" +#include "plugins/lib/bmp.h" #ifdef SIMULATOR #define ata_disk_is_active() 1 @@ -832,18 +834,56 @@ static bool fill_buffer(void) Return value is the total size (struct + data). */ static int load_bitmap(const int fd) { - int rc; - struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx]; + struct bitmap *aa_bmp, orig_bmp; + int orig_sz, aa_sz, free; /* bytes */ + + orig_sz = get_bmp_size(fd, &orig_bmp); + + if (orig_sz <= 0) + return orig_sz; + + aa_bmp = (struct bitmap *)&buffer[buf_widx]; + free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx) + - sizeof(struct bitmap); + + aa_sz = get_albumart_size(aa_bmp, &orig_bmp); + + if (aa_sz > free) + return 0; + /* FIXME: alignment may be needed for the data buffer. */ - bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; + aa_bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; + if (aa_bmp->width == orig_bmp.width && aa_bmp->height == orig_bmp.height) { #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) - bmp->maskdata = NULL; + aa_bmp->maskdata = NULL; #endif + aa_sz = read_bmp_fd(fd, aa_bmp, free, FORMAT_ANY|FORMAT_DITHER); + } else { + int ex_free = free - aa_sz; + + if (orig_sz > ex_free) + return 0; + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + orig_bmp.maskdata = NULL; +#endif + /* FIXME: alignment may be needed for the data buffer. */ + orig_bmp.data = &buffer[buf_widx + sizeof(struct bitmap) + aa_sz]; + + orig_sz = read_bmp_fd(fd, &orig_bmp, ex_free, FORMAT_ANY|FORMAT_DITHER); + + if (orig_sz <= 0) + return orig_sz; + +#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) + aa_bmp->maskdata = orig_bmp.maskdata; + aa_bmp->format = orig_bmp.format; +#endif + smooth_resize_bitmap(&orig_bmp, aa_bmp); + } - int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx); - rc = read_bmp_fd(fd, bmp, free, FORMAT_ANY|FORMAT_DITHER); - return rc + (rc > 0 ? sizeof(struct bitmap) : 0); + return aa_sz + (aa_sz > 0 ? sizeof(struct bitmap) : 0); } #endif