diff --git a/apps/plugins/pictureflow.c b/apps/plugins/pictureflow.c index 5613ce8..37ebb75 100644 --- a/apps/plugins/pictureflow.c +++ b/apps/plugins/pictureflow.c @@ -32,6 +32,7 @@ #include "pluginbitmaps/pictureflow_logo.h" #include "lib/grey.h" #include "lib/feature_wrappers.h" +#include "lib/buffer_alloc.h" PLUGIN_HEADER @@ -116,7 +117,7 @@ typedef fb_data pix_t; #define DISPLAY_LEFT_R (PFREAL_HALF - LCD_WIDTH * PFREAL_HALF) #define MAXSLIDE_LEFT_R (PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF) -#define SLIDE_CACHE_SIZE 100 +#define SLIDE_CACHE_SIZE 16 /* probably more than can be loaded */ #define MAX_SLIDES_COUNT 10 @@ -161,7 +162,8 @@ struct slide_data { struct slide_cache { int index; /* index of the cached slide */ int hid; /* handle ID of the cached slide */ - long touched; /* last time the slide was touched */ + short next; /* "next" slide, with LRU last */ + short prev; /* "previous" slide */ }; struct album_data { @@ -239,7 +241,7 @@ static struct configdata config[] = /** below we allocate the memory we want to use **/ static pix_t *buffer; /* for now it always points to the lcd framebuffer */ -static uint16_t reflect_table[REFLECT_HEIGHT]; +static uint8_t reflect_table[REFLECT_HEIGHT]; static struct slide_data center_slide; static struct slide_data left_slides[MAX_SLIDES_COUNT]; static struct slide_data right_slides[MAX_SLIDES_COUNT]; @@ -255,6 +257,7 @@ static int number_of_slides; static struct slide_cache cache[SLIDE_CACHE_SIZE]; static int slide_cache_in_use; +static int newest_cached_slot; /* use long for aligning */ unsigned long thread_stack[THREAD_STACK_SIZE / sizeof(long)]; @@ -272,7 +275,7 @@ static long uniqbuf[UNIQBUF_SIZE]; static struct tagcache_search tcs; static struct album_data album[MAX_ALBUMS]; -static char album_names[MAX_ALBUMS*AVG_ALBUM_NAME_LENGTH]; +static char *album_names; static int album_count; static char track_names[MAX_TRACKS * AVG_TRACK_NAME_LENGTH]; @@ -398,7 +401,7 @@ static inline int clz(uint32_t v) } #else static const char clz_lut[16] = { 4, 3, 2, 2, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0 }; + 0, 0, 0, 0, 0, 0, 0, 0 }; /* This clz is based on the log2(n) implementation at * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup * It is not any faster than the one above, but trades 16B in the lookup table @@ -472,7 +475,7 @@ static inline PFreal fdiv(PFreal n, PFreal m) #endif /* warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! */ -static const PFreal sin_tab[] = { +static const short sin_tab[] = { 0, 100, 200, 297, 392, 483, 569, 650, 724, 792, 851, 903, 946, 980, 1004, 1019, 1024, 1019, 1004, 980, 946, 903, 851, 792, @@ -566,7 +569,8 @@ int create_album_index(void) album_count = 0; rb->tagcache_search(&tcs, tag_album); rb->tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE); - int l, old_l = 0; + unsigned int l, old_l = 0; + album_names = plugin_buf; album[0].name_idx = 0; while (rb->tagcache_get_next(&tcs) && album_count < MAX_ALBUMS) { @@ -574,12 +578,13 @@ int create_album_index(void) if ( album_count > 0 ) album[album_count].name_idx = album[album_count-1].name_idx + old_l; - if ( (album[album_count].name_idx + l) > - MAX_ALBUMS*AVG_ALBUM_NAME_LENGTH ) + if ( l > plugin_buf_size ) /* not enough memory */ return ERROR_BUFFER_FULL; - rb->strcpy(album_names + album[album_count].name_idx, tcs.result); + rb->strcpy(plugin_buf, tcs.result); + plugin_buf_size -= l; + plugin_buf = l + (char *)plugin_buf; album[album_count].seek = tcs.result_seek; old_l = l; album_count++; @@ -940,7 +945,6 @@ static inline void request_surface(const int slide_index) rb->queue_post(&thread_q, EV_WAKEUP, 0); } - /** Thread used for loading and preparing bitmaps in the background */ @@ -1038,43 +1042,79 @@ int read_pfraw(char* filename) { struct pfraw_header bmph; int fh = rb->open(filename, O_RDONLY); - rb->read(fh, &bmph, sizeof(struct pfraw_header)); - if( fh < 0 ) { + if( fh < 0 ) return empty_slide_hid; - } + else + rb->read(fh, &bmph, sizeof(struct pfraw_header)); int size = sizeof(struct bitmap) + sizeof( pix_t ) * bmph.width * bmph.height; - int hid = rb->bufalloc(NULL, size, TYPE_BITMAP); - if (hid < 0) { + int hid = buf_alloc(size); + if (!hid) { rb->close( fh ); - return -1; + return 0; } - struct bitmap *bm; - if (rb->bufgetdata(hid, 0, (void *)&bm) < size) { - rb->close( fh ); - return -1; - } + struct dim *bm = buf_get_data(hid); bm->width = bmph.width; bm->height = bmph.height; -#if LCD_DEPTH > 1 - bm->format = FORMAT_NATIVE; -#endif - bm->data = ((unsigned char *)bm + sizeof(struct bitmap)); + pix_t *data = (pix_t*)(sizeof(struct dim) + (char *)bm); int y; for( y = 0; y < bm->height; y++ ) { - pix_t *d = (pix_t*)( bm->data ) + (y*bm->width); - rb->read( fh, d , sizeof( pix_t ) * bm->width ); + rb->read( fh, data , sizeof( pix_t ) * bm->width ); + data += bm->width; } rb->close( fh ); return hid; } +/** + Unlink an item from the cache linked-list. + */ +static inline void cache_unlink(int i) +{ + int next = cache[i].next; + int prev = cache[i].prev; + cache[prev].next = next; + cache[next].prev = prev; +} + +/** + Delete an item from the cache linked-list. + */ +static inline void cache_delete(int i) +{ + if (cache[i].hid && cache[i].hid != empty_slide_hid) + buf_free(cache[i].hid); + cache_unlink(i); + cache[i].hid = 0; + slide_cache_in_use--; +} + +/** + Insert item i before item next in cache linked-list. + */ +static inline void cache_insert(int i, int next) +{ + int prev = cache[next].prev; + cache[i].prev = prev; + cache[prev].next = i; + cache[i].next = next; + cache[next].prev = i; +} + +/** + Add new item i before item next in cache linked-list. + */ +static inline void cache_add(int i, int next) +{ + cache_insert(i, next); + slide_cache_in_use++; +} /** Load the surface for the given slide_index into the cache at cache_index. @@ -1087,14 +1127,13 @@ static inline bool load_and_prepare_surface(const int slide_index, slide_index); int hid = read_pfraw(tmp_path_name); - if (hid < 0) + if (!hid) return false; cache[cache_index].hid = hid; if ( cache_index < SLIDE_CACHE_SIZE ) { cache[cache_index].index = slide_index; - cache[cache_index].touched = *rb->current_tick; } return true; @@ -1107,43 +1146,58 @@ static inline bool load_and_prepare_surface(const int slide_index, */ int load_surface(const int slide_index) { - long oldest_tick = *rb->current_tick; - int oldest_slide = 0; - int i; - if ( slide_cache_in_use < SLIDE_CACHE_SIZE ) { /* initial fill */ - oldest_slide = slide_cache_in_use; - load_and_prepare_surface(slide_index, slide_cache_in_use++); - } - else { - for (i = 0; i < SLIDE_CACHE_SIZE; i++) { /* look for oldest slide */ - if (cache[i].touched < oldest_tick) { - oldest_slide = i; - oldest_tick = cache[i].touched; - } - } - if (cache[oldest_slide].hid != empty_slide_hid) { - rb->bufclose(cache[oldest_slide].hid); - cache[oldest_slide].hid = -1; + int oldest_slide, i; + bool success; + if (!slide_cache_in_use) + { + if(load_and_prepare_surface(slide_index, 0)) + { + newest_cached_slot = 0; + cache[0].next = 0; + cache[0].prev = 0; + slide_cache_in_use = 1; + return 0; + } else + return -1; + } else { + oldest_slide = newest_cached_slot; + int prev = cache[oldest_slide].prev; + for (i = 0; i < SLIDE_CACHE_SIZE && cache[i].hid; i++); + if (i == SLIDE_CACHE_SIZE) + { + i = prev; + oldest_slide = prev; + prev = cache[oldest_slide].prev; + cache_delete(oldest_slide); } - load_and_prepare_surface(slide_index, oldest_slide); + do { + if (!(success = load_and_prepare_surface(slide_index,i))) + { + oldest_slide = prev; + prev = cache[oldest_slide].prev; + cache_delete(oldest_slide); + } + } while (slide_cache_in_use > 1 && !success); + if (!success) + return -1; + cache_add(i, newest_cached_slot); + newest_cached_slot = i; + return i; } - return oldest_slide; } /** Get a slide from the buffer */ -static inline struct bitmap *get_slide(const int hid) +static inline struct dim *get_slide(const int hid) { - if (hid < 0) + if (!hid) return NULL; - struct bitmap *bmp; + struct dim *bmp; - ssize_t ret = rb->bufgetdata(hid, 0, (void *)&bmp); - if (ret < 0) - return NULL; + bmp = buf_get_data(hid); return bmp; } @@ -1152,21 +1206,28 @@ static inline struct bitmap *get_slide(const int hid) /** Return the requested surface */ -static inline struct bitmap *surface(const int slide_index) +static inline struct dim *surface(const int slide_index) { if (slide_index < 0) return 0; if (slide_index >= number_of_slides) return 0; - - int i; - for (i = 0; i < slide_cache_in_use; i++) { - /* maybe do the inverse mapping => implies dynamic allocation? */ - if ( cache[i].index == slide_index ) { - /* We have already loaded our slide, so touch it and return it. */ - cache[i].touched = *rb->current_tick; - return get_slide(cache[i].hid); - } + if (slide_cache_in_use) + { + int i = newest_cached_slot; + do { + if ( cache[i].hid && cache[i].index == slide_index ) { + /* We have already loaded our slide, so touch it and return it. */ + if (i != newest_cached_slot) + { + cache_unlink(i); + cache_insert(i, newest_cached_slot); + newest_cached_slot = i; + } + return get_slide(cache[i].hid); + } + i = cache[i].next; + } while (i != newest_cached_slot); } request_surface(slide_index); return get_slide(empty_slide_hid); @@ -1293,13 +1354,13 @@ static inline pix_t fade_color(pix_t c, unsigned int a) */ void render_slide(struct slide_data *slide, const int alpha) { - struct bitmap *bmp = surface(slide->slide_index); + struct dim *bmp = surface(slide->slide_index); if (!bmp) { return; } if (slide->angle > 255 || slide->angle < -255) return; - pix_t *src = (pix_t *)bmp->data; + pix_t *src = (pix_t*)(sizeof(struct dim) + (char *)bmp); const int sw = bmp->width; const int sh = bmp->height; @@ -1329,7 +1390,7 @@ void render_slide(struct slide_data *slide, const int alpha) xsnum = CAM_DIST * (slide->cx - xp) - fmuln(xp, zo, PFREAL_SHIFT - 2, 0); xsden = fmuln(xp, sinr, PFREAL_SHIFT - 2, 0) - CAM_DIST * cosr; xs = fdiv(xsnum, xsden); - + xsnumi = -CAM_DIST_R - zo; xsdeni = sinr; int x; @@ -1411,7 +1472,7 @@ void render_slide(struct slide_data *slide, const int alpha) xs = fdiv(xsnum, xsden); } else xs += PFREAL_ONE; - + } /* let the music play... */ rb->yield(); @@ -1652,12 +1713,6 @@ void cleanup(void *parameter) /* Turn on backlight timeout (revert to settings) */ backlight_use_settings(); /* backlight control in lib/helper.c */ - int i; - for (i = 0; i < slide_cache_in_use; i++) { - rb->bufclose(cache[i].hid); - } - if ( empty_slide_hid != - 1) - rb->bufclose(empty_slide_hid); #ifdef USEGSLIB grey_release(); #endif @@ -1687,9 +1742,6 @@ int create_empty_slide(bool force) return false; } - empty_slide_hid = read_pfraw( EMPTY_SLIDE ); - if (empty_slide_hid == -1 ) return false; - return true; } @@ -2053,7 +2105,7 @@ void draw_album_text(void) int main(void) { int ret; - + rb->lcd_setfont(FONT_UI); draw_splashscreen(); @@ -2076,7 +2128,7 @@ int main(void) rb->splash(HZ, "No albums found. Please enable database"); return PLUGIN_ERROR; } - + number_of_slides = album_count; if ((cache_version != CACHE_VERSION) && !create_albumart_cache()) { rb->splash(HZ, "Could not create album art cache"); @@ -2090,6 +2142,28 @@ int main(void) cache_version = CACHE_VERSION; configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION); + +#ifdef USEGSLIB + long grey_buf_used; + if (!grey_init(plugin_buf, plugin_buf_size, GREY_BUFFERED|GREY_ON_COP, + LCD_WIDTH, LCD_HEIGHT, &grey_buf_used)) + { + rb->splash(HZ, "Greylib init failed!"); + return PLUGIN_ERROR; + } + grey_setfont(FONT_UI); + plugin_buf_size -= grey_buf_used; + plugin_buf = (void*)(grey_buf_used + (char*)plugin_buf); +#endif + buf_init(plugin_buf, plugin_buf_size); + + empty_slide_hid = read_pfraw( EMPTY_SLIDE ); + if (!(empty_slide_hid = read_pfraw( EMPTY_SLIDE ) )) + { + rb->splash(HZ, "Unable to load empty slide image"); + return PLUGIN_ERROR; + } + if (!create_pf_thread()) { rb->splash(HZ, "Cannot create thread!"); return PLUGIN_ERROR; @@ -2100,18 +2174,13 @@ int main(void) /* initialize */ int min_slide_cache = fmin(number_of_slides, SLIDE_CACHE_SIZE); for (i = 0; i < min_slide_cache; i++) { - cache[i].hid = -1; - cache[i].touched = 0; - slide_cache_stack[i] = SLIDE_CACHE_SIZE-i-1; + cache[i].hid = 0; + cache[i].next = -1; + cache[i].prev = -1; } - slide_cache_stack_index = min_slide_cache-1; + newest_cached_slot = -1; + slide_cache_stack_index = 0; slide_cache_in_use = 0; -#ifdef USEGSLIB - if (!grey_init(plugin_buf, plugin_buf_size, GREY_BUFFERED|GREY_ON_COP, - LCD_WIDTH, LCD_HEIGHT, NULL)) - rb->splash(HZ, "Greylib init failed!"); - grey_setfont(FONT_UI); -#endif buffer = LCD_BUF; pf_state = pf_idle;