diff --git a/apps/plugins/pictureflow.c b/apps/plugins/pictureflow.c index 5613ce8..4023055 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,9 +117,9 @@ 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 64 /* probably more than can be loaded */ -#define MAX_SLIDES_COUNT 10 +#define MAX_SLIDES_COUNT 7 #define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200 #define CACHE_PREFIX PLUGIN_DEMOS_DIR "/pictureflow" @@ -128,7 +129,6 @@ typedef fb_data pix_t; /* maximum number of albums */ #define MAX_ALBUMS 1024 -#define AVG_ALBUM_NAME_LENGTH 20 #define MAX_TRACKS 50 #define AVG_TRACK_NAME_LENGTH 20 @@ -161,7 +161,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 { @@ -196,7 +197,7 @@ const struct picture logos[]={ {pictureflow_logo, BMPWIDTH_pictureflow_logo, BMPHEIGHT_pictureflow_logo}, }; -enum show_album_name_values { album_name_hide = 0, album_name_bottom , +enum show_album_name_values { album_name_hide = 0, album_name_bottom, album_name_top }; static char* show_album_name_conf[] = { @@ -216,7 +217,8 @@ static int zoom = 100; static bool show_fps = false; static bool resize = true; static int cache_version = 0; -static int show_album_name = (LCD_HEIGHT > 100) ? album_name_top : album_name_bottom; +static int show_album_name = (LCD_HEIGHT > 100) + ? album_name_top : album_name_bottom; static struct configdata config[] = { @@ -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]; @@ -254,7 +256,8 @@ static PFreal offsetY; static int number_of_slides; static struct slide_cache cache[SLIDE_CACHE_SIZE]; -static int slide_cache_in_use; +static int cache_free; +static int cache_used; /* use long for aligning */ unsigned long thread_stack[THREAD_STACK_SIZE / sizeof(long)]; @@ -268,11 +271,10 @@ static int empty_slide_hid; unsigned int thread_id; struct event_queue thread_q; -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 +400,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 +474,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, @@ -562,11 +564,14 @@ void init_reflect_table(void) */ int create_album_index(void) { + plugin_buf_size -= UNIQBUF_SIZE * sizeof(long); + long *uniqbuf = plugin_buf + plugin_buf_size; rb->memset(&tcs, 0, sizeof(struct tagcache_search) ); 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,18 +579,19 @@ 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++; } rb->tagcache_search_finish(&tcs); - + plugin_buf_size += UNIQBUF_SIZE * sizeof(long); return (album_count > 0) ? 0 : ERROR_NO_ALBUMS; } @@ -940,7 +946,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 */ @@ -1031,6 +1036,94 @@ bool save_pfraw(char* filename, struct bitmap *bm) } +/* + * The following functions implement the linked-list-in-array used to manage + * the LRU cache of slides, and the list of free cache slots. + */ + + +/** + Pop the given item from the linked list starting at *head, returning the next + item, or -1 if the list is now empty. +*/ +static inline int lla_pop_item (int *head, int i) +{ + int prev = cache[i].prev; + int next = cache[i].next; + if (i == next) + { + *head = -1; + return -1; + } + else if (i == *head) + *head = next; + cache[next].prev = prev; + cache[prev].next = next; + return next; +} + + +/** + Pop the head item from the list starting at *head, returning the index of the + item, or -1 if the list is already empty. +*/ +static inline int lla_pop_head (int *head) +{ + int i = *head; + if (i != -1) + lla_pop_item(head, i); + return i; +} + + +/** + Insert the item at index i at the end of the list starting at *head. +*/ +static inline void lla_insert_tail (int *head, int i) +{ + if (*head == -1) + { + *head = i; + cache[i].next = i; + cache[i].prev = i; + } else { + int next = *head; + int prev = cache[next].prev; + cache[next].prev = i; + cache[prev].next = i; + cache[i].next = next; + cache[i].prev = prev; + } +} + + +/** + Demote the item at index i to the tail of the list starting at *head. +*/ +static inline void lla_demote (int *head, int i) +{ + if (*head != i) + { + lla_pop_item(head, i); + lla_insert_tail(head, i); + } +} + + +/** + Free the used slide at index i, and its buffer, and move it to the free + slides list, returning the next slide. +*/ +static inline int free_slide(int i) +{ + if (cache[i].hid != empty_slide_hid) + buf_free(cache[i].hid); + int ret = lla_pop_item(&cache_used, i); + lla_insert_tail(&cache_free, i); + return ret; +} + + /** Read the pfraw image given as filename and return the hid of the buffer */ @@ -1038,38 +1131,46 @@ 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) { - rb->close( fh ); - return -1; - } + int oldest = cache_used, hid; + do { + hid = buf_alloc(size); + if (hid) + break; + do { + if (cache[oldest].hid == empty_slide_hid) + oldest = cache[oldest].next; + else + { + oldest = free_slide(oldest); + break; + } + } while (cache_used != oldest); + } while (cache_used != oldest); - struct bitmap *bm; - if (rb->bufgetdata(hid, 0, (void *)&bm) < size) { + if (!hid) { rb->close( fh ); - return -1; + return 0; } + 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; @@ -1087,14 +1188,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 +1207,32 @@ 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; - } - load_and_prepare_surface(slide_index, oldest_slide); + if (cache_free == -1) + free_slide(cache_used); + i = lla_pop_head(&cache_free); + if (load_and_prepare_surface(slide_index, i)) + { + lla_insert_tail(&cache_used, i); + return i; + } else { + lla_insert_tail(&cache_free, i); + return -1; } - 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 +1241,23 @@ 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 ((i = cache_used) != -1) + { + do { + if (cache[i].index == slide_index) + { + lla_demote(&cache_used, i); + return get_slide(cache[i].hid); + } + i = cache[i].next; + } while (i != cache_used); } request_surface(slide_index); return get_slide(empty_slide_hid); @@ -1293,13 +1384,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 +1420,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 +1502,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 +1743,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 +1772,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 +2135,7 @@ void draw_album_text(void) int main(void) { int ret; - + rb->lcd_setfont(FONT_UI); draw_splashscreen(); @@ -2068,6 +2150,7 @@ int main(void) init_reflect_table(); + ALIGN_BUFFER(plugin_buf, plugin_buf_size, 4); ret = create_album_index(); if (ret == ERROR_BUFFER_FULL) { rb->splash(HZ, "Not enough memory for album names"); @@ -2076,7 +2159,8 @@ int main(void) rb->splash(HZ, "No albums found. Please enable database"); return PLUGIN_ERROR; } - + + ALIGN_BUFFER(plugin_buf, plugin_buf_size, 4); number_of_slides = album_count; if ((cache_version != CACHE_VERSION) && !create_albumart_cache()) { rb->splash(HZ, "Could not create album art cache"); @@ -2090,6 +2174,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((void *)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; @@ -2098,20 +2204,17 @@ int main(void) int i; /* 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; - } - slide_cache_stack_index = min_slide_cache-1; - 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 + for (i = 0; i < SLIDE_CACHE_SIZE; i++) { + cache[i].hid = 0; + cache[i].index = 0; + cache[i].next = i + 1; + cache[i].prev = i - 1; + } + cache[0].prev = i - 1; + cache[i - 1].next = 0; + cache_free = 0; + cache_used = -1; + slide_cache_stack_index = 0; buffer = LCD_BUF; pf_state = pf_idle; @@ -2289,7 +2392,6 @@ int main(void) enum plugin_status plugin_start(const void *parameter) { int ret; - (void) parameter; #if LCD_DEPTH > 1 rb->lcd_set_backdrop(NULL); @@ -2300,7 +2402,6 @@ enum plugin_status plugin_start(const void *parameter) rb->cpu_boost(true); #endif plugin_buf = rb->plugin_get_buffer(&plugin_buf_size); - ALIGN_BUFFER(plugin_buf, plugin_buf_size, 4); ret = main(); #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(false);