rendered paste bodydiff --git a/apps/plugins/pictureflow.c b/apps/plugins/pictureflow.cindex 272a691..c0a6dd7 100644--- a/apps/plugins/pictureflow.c+++ b/apps/plugins/pictureflow.c@@ -56,6 +56,10 @@ const struct button_mapping *plugin_contexts[] #define PICTUREFLOW_PREV_ALBUM PLA_LEFT #define PICTUREFLOW_PREV_ALBUM_REPEAT PLA_LEFT_REPEAT #endif+#define PICTUREFLOW_NEXT_TRACK PLA_DOWN+#define PICTUREFLOW_NEXT_TRACK_REPEAT PLA_DOWN_REPEAT+#define PICTUREFLOW_PREV_TRACK PLA_UP+#define PICTUREFLOW_PREV_TRACK_REPEAT PLA_UP_REPEAT #define PICTUREFLOW_MENU PLA_MENU #define PICTUREFLOW_QUIT PLA_QUIT #define PICTUREFLOW_SELECT_ALBUM PLA_FIRE@@ -105,6 +109,10 @@ const struct button_mapping *plugin_contexts[] #define MAX_ALBUMS 1024 #define AVG_ALBUM_NAME_LENGTH 20 +#define MAX_TRACKS 50+#define AVG_TRACK_NAME_LENGTH 20++ #define UNIQBUF_SIZE (64*1024) #define EMPTY_SLIDE CACHE_PREFIX "/emptyslide.pfraw"@@ -122,6 +130,7 @@ struct slide_data { int angle; PFreal cx; PFreal cy;+ PFreal distance; }; struct slide_cache {@@ -135,6 +144,11 @@ struct album_data { long seek; }; +struct track_data {+ int name_idx;+ long seek;+};+ struct rect { int left; int right;@@ -162,13 +176,13 @@ struct config_data { int spacing_between_slides; int extra_spacing_for_center_slide; int show_slides;+ int zoom; }; /** below we allocate the memory we want to use **/ static fb_data *buffer; /* for now it always points to the lcd framebuffer */ static PFreal rays[BUFFER_WIDTH];-static bool animation_is_active; /* an animation is currently running */ static struct slide_data center_slide; static struct slide_data left_slides[MAX_SLIDES_COUNT]; static struct slide_data right_slides[MAX_SLIDES_COUNT];@@ -178,7 +192,6 @@ static int target; static int fade; static int center_index; /* index of the slide that is in the center */ static int itilt;-static int zoom; static PFreal offsetX; static PFreal offsetY; static bool show_fps; /* show fps in the main screen */@@ -207,161 +220,68 @@ static struct album_data album[MAX_ALBUMS]; static char album_names[MAX_ALBUMS*AVG_ALBUM_NAME_LENGTH]; static int album_count; +static char track_names[MAX_TRACKS * AVG_TRACK_NAME_LENGTH];+static struct track_data tracks[MAX_TRACKS];+static int track_count;+static int track_index;+static int selected_track;+static int selected_track_pulse;+ static fb_data *input_bmp_buffer; static fb_data *output_bmp_buffer; static int input_hid; static int output_hid; static struct config_data config; +static int old_drawmode;+ static bool thread_is_running; +static int cover_animation_keyframe;+static int extra_fade; -PFreal sinTable[] = { /* 10 */- 3, 9, 15, 21, 28, 34, 40, 47,- 53, 59, 65, 72, 78, 84, 90, 97,- 103, 109, 115, 122, 128, 134, 140, 147,- 153, 159, 165, 171, 178, 184, 190, 196,- 202, 209, 215, 221, 227, 233, 239, 245,- 251, 257, 264, 270, 276, 282, 288, 294,- 300, 306, 312, 318, 324, 330, 336, 342,- 347, 353, 359, 365, 371, 377, 383, 388,- 394, 400, 406, 412, 417, 423, 429, 434,- 440, 446, 451, 457, 463, 468, 474, 479,- 485, 491, 496, 501, 507, 512, 518, 523,- 529, 534, 539, 545, 550, 555, 561, 566,- 571, 576, 581, 587, 592, 597, 602, 607,- 612, 617, 622, 627, 632, 637, 642, 647,- 652, 656, 661, 666, 671, 675, 680, 685,- 690, 694, 699, 703, 708, 712, 717, 721,- 726, 730, 735, 739, 743, 748, 752, 756,- 760, 765, 769, 773, 777, 781, 785, 789,- 793, 797, 801, 805, 809, 813, 816, 820,- 824, 828, 831, 835, 839, 842, 846, 849,- 853, 856, 860, 863, 866, 870, 873, 876,- 879, 883, 886, 889, 892, 895, 898, 901,- 904, 907, 910, 913, 916, 918, 921, 924,- 927, 929, 932, 934, 937, 939, 942, 944,- 947, 949, 951, 954, 956, 958, 960, 963,- 965, 967, 969, 971, 973, 975, 977, 978,- 980, 982, 984, 986, 987, 989, 990, 992,- 994, 995, 997, 998, 999, 1001, 1002, 1003,- 1004, 1006, 1007, 1008, 1009, 1010, 1011, 1012,- 1013, 1014, 1015, 1015, 1016, 1017, 1018, 1018,- 1019, 1019, 1020, 1020, 1021, 1021, 1022, 1022,- 1022, 1023, 1023, 1023, 1023, 1023, 1023, 1023,- 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1022,- 1022, 1022, 1021, 1021, 1020, 1020, 1019, 1019,- 1018, 1018, 1017, 1016, 1015, 1015, 1014, 1013,- 1012, 1011, 1010, 1009, 1008, 1007, 1006, 1004,- 1003, 1002, 1001, 999, 998, 997, 995, 994,- 992, 990, 989, 987, 986, 984, 982, 980,- 978, 977, 975, 973, 971, 969, 967, 965,- 963, 960, 958, 956, 954, 951, 949, 947,- 944, 942, 939, 937, 934, 932, 929, 927,- 924, 921, 918, 916, 913, 910, 907, 904,- 901, 898, 895, 892, 889, 886, 883, 879,- 876, 873, 870, 866, 863, 860, 856, 853,- 849, 846, 842, 839, 835, 831, 828, 824,- 820, 816, 813, 809, 805, 801, 797, 793,- 789, 785, 781, 777, 773, 769, 765, 760,- 756, 752, 748, 743, 739, 735, 730, 726,- 721, 717, 712, 708, 703, 699, 694, 690,- 685, 680, 675, 671, 666, 661, 656, 652,- 647, 642, 637, 632, 627, 622, 617, 612,- 607, 602, 597, 592, 587, 581, 576, 571,- 566, 561, 555, 550, 545, 539, 534, 529,- 523, 518, 512, 507, 501, 496, 491, 485,- 479, 474, 468, 463, 457, 451, 446, 440,- 434, 429, 423, 417, 412, 406, 400, 394,- 388, 383, 377, 371, 365, 359, 353, 347,- 342, 336, 330, 324, 318, 312, 306, 300,- 294, 288, 282, 276, 270, 264, 257, 251,- 245, 239, 233, 227, 221, 215, 209, 202,- 196, 190, 184, 178, 171, 165, 159, 153,- 147, 140, 134, 128, 122, 115, 109, 103,- 97, 90, 84, 78, 72, 65, 59, 53,- 47, 40, 34, 28, 21, 15, 9, 3,- -4, -10, -16, -22, -29, -35, -41, -48,- -54, -60, -66, -73, -79, -85, -91, -98,- -104, -110, -116, -123, -129, -135, -141, -148,- -154, -160, -166, -172, -179, -185, -191, -197,- -203, -210, -216, -222, -228, -234, -240, -246,- -252, -258, -265, -271, -277, -283, -289, -295,- -301, -307, -313, -319, -325, -331, -337, -343,- -348, -354, -360, -366, -372, -378, -384, -389,- -395, -401, -407, -413, -418, -424, -430, -435,- -441, -447, -452, -458, -464, -469, -475, -480,- -486, -492, -497, -502, -508, -513, -519, -524,- -530, -535, -540, -546, -551, -556, -562, -567,- -572, -577, -582, -588, -593, -598, -603, -608,- -613, -618, -623, -628, -633, -638, -643, -648,- -653, -657, -662, -667, -672, -676, -681, -686,- -691, -695, -700, -704, -709, -713, -718, -722,- -727, -731, -736, -740, -744, -749, -753, -757,- -761, -766, -770, -774, -778, -782, -786, -790,- -794, -798, -802, -806, -810, -814, -817, -821,- -825, -829, -832, -836, -840, -843, -847, -850,- -854, -857, -861, -864, -867, -871, -874, -877,- -880, -884, -887, -890, -893, -896, -899, -902,- -905, -908, -911, -914, -917, -919, -922, -925,- -928, -930, -933, -935, -938, -940, -943, -945,- -948, -950, -952, -955, -957, -959, -961, -964,- -966, -968, -970, -972, -974, -976, -978, -979,- -981, -983, -985, -987, -988, -990, -991, -993,- -995, -996, -998, -999, -1000, -1002, -1003, -1004,- -1005, -1007, -1008, -1009, -1010, -1011, -1012, -1013,- -1014, -1015, -1016, -1016, -1017, -1018, -1019, -1019,- -1020, -1020, -1021, -1021, -1022, -1022, -1023, -1023,- -1023, -1024, -1024, -1024, -1024, -1024, -1024, -1024,- -1024, -1024, -1024, -1024, -1024, -1024, -1024, -1023,- -1023, -1023, -1022, -1022, -1021, -1021, -1020, -1020,- -1019, -1019, -1018, -1017, -1016, -1016, -1015, -1014,- -1013, -1012, -1011, -1010, -1009, -1008, -1007, -1005,- -1004, -1003, -1002, -1000, -999, -998, -996, -995,- -993, -991, -990, -988, -987, -985, -983, -981,- -979, -978, -976, -974, -972, -970, -968, -966,- -964, -961, -959, -957, -955, -952, -950, -948,- -945, -943, -940, -938, -935, -933, -930, -928,- -925, -922, -919, -917, -914, -911, -908, -905,- -902, -899, -896, -893, -890, -887, -884, -880,- -877, -874, -871, -867, -864, -861, -857, -854,- -850, -847, -843, -840, -836, -832, -829, -825,- -821, -817, -814, -810, -806, -802, -798, -794,- -790, -786, -782, -778, -774, -770, -766, -761,- -757, -753, -749, -744, -740, -736, -731, -727,- -722, -718, -713, -709, -704, -700, -695, -691,- -686, -681, -676, -672, -667, -662, -657, -653,- -648, -643, -638, -633, -628, -623, -618, -613,- -608, -603, -598, -593, -588, -582, -577, -572,- -567, -562, -556, -551, -546, -540, -535, -530,- -524, -519, -513, -508, -502, -497, -492, -486,- -480, -475, -469, -464, -458, -452, -447, -441,- -435, -430, -424, -418, -413, -407, -401, -395,- -389, -384, -378, -372, -366, -360, -354, -348,- -343, -337, -331, -325, -319, -313, -307, -301,- -295, -289, -283, -277, -271, -265, -258, -252,- -246, -240, -234, -228, -222, -216, -210, -203,- -197, -191, -185, -179, -172, -166, -160, -154,- -148, -141, -135, -129, -123, -116, -110, -104,- -98, -91, -85, -79, -73, -66, -60, -54,- -48, -41, -35, -29, -22, -16, -10, -4,-};+static int albumtxt_x = 0;+static int albumtxt_dir = -1;+static int prev_center_index = -1; +static int start_index_track_list = 0;+static int track_list_visible_entries = 0;+static int track_scroll_index = 0;+static int track_scroll_dir = 1; -/** code */+/*+ Proposals for transitions:++ pf_idle -> pf_scrolling : NEXT_ALBUM/PREV_ALBUM pressed+ -> pf_cover_in -> pf_show_tracks : SELECT_ALBUM clicked++ pf_scrolling -> pf_idle : NEXT_ALBUM/PREV_ALBUM released++ pf_show_tracks -> pf_cover_out -> pf_idle : SELECT_ALBUM pressed++ TODO:+ pf_show_tracks -> pf_cover_out -> pf_idle : MENU_PRESSED pressed+ pf_show_tracks -> play_track() -> exit() : SELECT_ALBUM pressed + pf_idle, pf_scrolling -> show_menu(): MENU_PRESSED+*/+enum pf_states { pf_idle = 0, pf_scrolling, pf_cover_in, pf_show_tracks, pf_cover_out };++static int pf_state;++/** code */ bool create_bmp(struct bitmap* input_bmp, char *target_path, bool resize); int load_surface(int); -/* There are some precision issues when not using (long long) which in turn- takes very long to compute... I guess the best solution would be to optimize- the computations so it only requires a single long */ static inline PFreal fmul(PFreal a, PFreal b) {- return ((long long) (a)) * ((long long) (b)) >> PFREAL_SHIFT;+ return (a*b) >> PFREAL_SHIFT; } +/* There are some precision issues when not using (long long) which in turn+ takes very long to compute... I guess the best solution would be to optimize+ the computations so it only requires a single long */ static inline PFreal fdiv(PFreal num, PFreal den) { long long p = (long long) (num) << (PFREAL_SHIFT * 2);@@ -394,21 +314,37 @@ 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[] = {+ 3, 103, 202, 300, 394, 485, 571, 652,+ 726, 793, 853, 904, 947, 980, 1004, 1019,+ 1023, 1018, 1003, 978, 944, 901, 849, 789,+ 721, 647, 566, 479, 388, 294, 196, 97,+ -4, -104, -203, -301, -395, -486, -572, -653,+ -727, -794, -854, -905, -948, -981, -1005, -1020,+ -1024, -1019, -1004, -979, -945, -902, -850, -790,+ -722, -648, -567, -480, -389, -295, -197, -98,+ 3+}; -inline PFreal fsin(int iangle)+static inline PFreal fsin(int iangle) {- while (iangle < 0)+ while(iangle < 0) iangle += IANGLE_MAX;- return sinTable[iangle & IANGLE_MASK];+ iangle &= IANGLE_MASK;++ int i = (iangle >> 4);+ PFreal p = sin_tab[i];+ PFreal q = sin_tab[(i+1)];+ PFreal g = (q - p);+ return p + g * (iangle-i*16)/16; } -inline PFreal fcos(int iangle)+static inline PFreal fcos(int iangle) {- /* quarter phase shift */ return fsin(iangle + (IANGLE_MAX >> 2)); } - /** Create an index of all albums from the database. Also store the album names so we can access them later.@@ -444,11 +380,68 @@ int create_album_index(void) /** Return a pointer to the album name of the given slide_index */-char* get_album_name(int slide_index)+char* get_album_name(const int slide_index) { return album_names + album[slide_index].name_idx; } +/**+ Return a pointer to the track name of the active album+ create_track_index has to be called first.+ */+char* get_track_name(const int track_index)+{+ if ( track_index < track_count )+ return track_names + tracks[track_index].name_idx;+ return 0;+}++/**+ Create the track index of the given slide_index.+ */+int create_track_index(const int slide_index)+{+ if ( slide_index == track_index ) {+ return -1;+ }++ if (!rb->tagcache_search(&tcs, tag_title))+ return -1;++ int ret = 0;++ rb->tagcache_search_add_filter(&tcs, tag_album, album[slide_index].seek);+ track_count=0;+ int l, old_l = 0;+ tracks[0].name_idx = 0;++ while (rb->tagcache_get_next(&tcs) && track_count < MAX_TRACKS)+ {+ l = rb->strlen(tcs.result) + 1;+ if ( track_count > 0 )+ tracks[track_count].name_idx = tracks[track_count-1].name_idx + old_l;++ if ( (tracks[track_count].name_idx + l) > MAX_TRACKS * AVG_TRACK_NAME_LENGTH )+ {+ /* not enough memory */+ ret = ERROR_BUFFER_FULL;+ break;+ }+ rb->strcpy(track_names + tracks[track_count].name_idx, tcs.result);+ tracks[track_count].seek = tcs.result_seek;+ old_l = l;+ track_count++;+ }++ rb->tagcache_search_finish(&tcs);+ track_index = slide_index;++ if (ret != 0)+ return ret;+ else+ return (track_count > 0) ? 0 : -1;+}+ /** Determine filename of the album art for the given slide_index and@@ -456,7 +449,7 @@ char* get_album_name(int slide_index) The algorithm looks for the first track of the given album uses find_albumart to find the filename. */-bool get_albumart_for_index_from_db(int slide_index, char *buf, int buflen)+bool get_albumart_for_index_from_db(const int slide_index, char *buf, int buflen) { if ( slide_index == -1 ) {@@ -536,6 +529,9 @@ void draw_progressbar(int step) rb->yield(); } +/**+ Allocate temporary buffers+ */ bool allocate_buffers(void) { int input_size = MAX_IMG_WIDTH * MAX_IMG_HEIGHT * sizeof( fb_data );@@ -566,6 +562,9 @@ bool allocate_buffers(void) } +/**+ Free the temporary buffers+ */ bool free_buffers(void) { rb->bufclose(input_hid);@@ -586,6 +585,7 @@ bool create_albumart_cache(bool force) int i, slides = 0; struct bitmap input_bmp; + config.avg_album_width = 0; for (i=0; i < album_count; i++) { draw_progressbar(i);@@ -597,19 +597,23 @@ bool create_albumart_cache(bool force) sizeof(fb_data)*MAX_IMG_WIDTH*MAX_IMG_HEIGHT, FORMAT_NATIVE); if (ret <= 0) {- rb->splash(HZ, "couldn't read bmp");+ rb->splash(HZ, "Could not read bmp"); continue; /* skip missing/broken files */ } rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw", i); if (!create_bmp(&input_bmp, tmp_path_name, false)) {- rb->splash(HZ, "couldn't write bmp");+ rb->splash(HZ, "Could not write bmp"); } config.avg_album_width += input_bmp.width; slides++; if ( rb->button_get(false) == PICTUREFLOW_MENU ) return false; }+ if ( slides == 0 ) {+ rb->splash(2*HZ, "No albums found");+ return false;+ } config.avg_album_width /= slides; if ( config.avg_album_width == 0 ) { rb->splash(HZ, "album size is 0");@@ -624,7 +628,7 @@ bool create_albumart_cache(bool force) Return the index on the stack of slide_index. Return -1 if slide_index is not on the stack. */-int slide_stack_get_index(int slide_index)+static inline int slide_stack_get_index(const int slide_index) { int i = slide_cache_stack_index + 1; while (i--) {@@ -639,7 +643,7 @@ int slide_stack_get_index(int slide_index) slide_index as high as possible (so second if center_index is on the stack). */-void slide_stack_push(int slide_index)+void slide_stack_push(const int slide_index) { rb->mutex_lock(&slide_cache_stack_lock); @@ -701,7 +705,7 @@ void slide_stack_push(int slide_index) /** Pop the topmost item from the stack and decrease the stack size */-inline int slide_stack_pop(void)+static inline int slide_stack_pop(void) { rb->mutex_lock(&slide_cache_stack_lock); int result;@@ -718,7 +722,7 @@ inline int slide_stack_pop(void) Load the slide into the cache. Thus we have to queue the loading request in our thread while discarding the oldest slide. */-void request_surface(int slide_index)+static inline void request_surface(const int slide_index) { slide_stack_push(slide_index); rb->queue_post(&thread_q, EV_WAKEUP, 0);@@ -914,7 +918,7 @@ bool create_bmp(struct bitmap *input_bmp, char *target_path, bool resize) /** Load the surface for the given slide_index into the cache at cache_index. */-static bool load_and_prepare_surface(int slide_index, int cache_index)+static inline bool load_and_prepare_surface(const int slide_index, const int cache_index) { rb->snprintf(tmp_path_name, sizeof(tmp_path_name), CACHE_PREFIX "/%d.pfraw", slide_index); @@ -937,7 +941,7 @@ static bool load_and_prepare_surface(int slide_index, int cache_index) Load the surface from a bmp and overwrite the oldest slide in the cache if necessary. */-int load_surface(int slide_index)+int load_surface(const int slide_index) { long oldest_tick = *rb->current_tick; int oldest_slide = 0;@@ -966,7 +970,7 @@ int load_surface(int slide_index) /** Get a slide from the buffer */-struct bitmap *get_slide(int hid)+static inline struct bitmap *get_slide(const int hid) { if (hid < 0) return NULL;@@ -984,7 +988,7 @@ struct bitmap *get_slide(int hid) /** Return the requested surface */-struct bitmap *surface(int slide_index)+static inline struct bitmap *surface(const int slide_index) { if (slide_index < 0) return 0;@@ -1011,6 +1015,7 @@ void reset_slides(void) center_slide.angle = 0; center_slide.cx = 0; center_slide.cy = 0;+ center_slide.distance = 0; center_slide.slide_index = center_index; int i;@@ -1020,6 +1025,7 @@ void reset_slides(void) si->cx = -(offsetX + config.spacing_between_slides * i * PFREAL_ONE); si->cy = offsetY; si->slide_index = center_index - 1 - i;+ si->distance = 0; } for (i = 0; i < config.show_slides; i++) {@@ -1028,6 +1034,7 @@ void reset_slides(void) si->cx = offsetX + config.spacing_between_slides * i * PFREAL_ONE; si->cy = offsetY; si->slide_index = center_index + 1 + i;+ si->distance = 0; } } @@ -1058,10 +1065,32 @@ void recalc_table(void) /**+ Fade the given color by spreading the fb_data (ushort)+ to an uint, multiply and compress the result back to a ushort.+ */+#if (LCD_PIXELFORMAT == RGB565SWAPPED)+static inline fb_data fade_color(fb_data c, unsigned int a)+{+ c = swap16(c);+ unsigned int p = ((((c|(c<<16)) & 0x07e0f81f) * a) >> 5) & 0x07e0f81f;+ return swap16( (fb_data) (p | ( p >> 16 )) );+}+#else+static inline fb_data fade_color(fb_data c, unsigned int a)+{+ unsigned int p = ((((c|(c<<16)) & 0x07e0f81f) * a) >> 5) & 0x07e0f81f;+ return (p | ( p >> 16 ));+}+#endif+++++/** Render a single slide */ void render_slide(struct slide_data *slide, struct rect *result_rect,- int alpha, int col1, int col2)+ const int alpha, int col1, int col2) { rb->memset(result_rect, 0, sizeof(struct rect)); struct bitmap *bmp = surface(slide->slide_index);@@ -1070,11 +1099,11 @@ void render_slide(struct slide_data *slide, struct rect *result_rect, } fb_data *src = (fb_data *)bmp->data; - int sw = bmp->height;- int sh = bmp->width;+ const int sw = bmp->height;+ const int sh = bmp->width; - int h = LCD_HEIGHT;- int w = LCD_WIDTH;+ const int h = LCD_HEIGHT;+ const int w = LCD_WIDTH; if (col1 > col2) { int c = col2;@@ -1087,13 +1116,15 @@ void render_slide(struct slide_data *slide, struct rect *result_rect, col1 = fmin(col1, w - 1); col2 = fmin(col2, w - 1); - int distance = h * 100 / zoom;+ int distance = (h + slide->distance) * 100 / config.zoom;+ if (distance < 100 ) distance = 100; // clamp distances PFreal sdx = fcos(slide->angle); PFreal sdy = fsin(slide->angle); PFreal xs = slide->cx - bmp->width * sdx / 4; PFreal ys = slide->cy - bmp->width * sdy / 4; PFreal dist = distance * PFREAL_ONE; + const int alpha4 = alpha >> 3; int xi = fmax((PFreal) 0, ((w * PFREAL_ONE / 2) +@@ -1123,7 +1154,7 @@ void render_slide(struct slide_data *slide, struct rect *result_rect, PFreal hitdist = fdiv(hitx - slide->cx, sdx); - int column = sw / 2 + (hitdist >> PFREAL_SHIFT);+ const int column = (sw >> 1) + (hitdist >> PFREAL_SHIFT); if (column >= sw) break; @@ -1135,19 +1166,19 @@ void render_slide(struct slide_data *slide, struct rect *result_rect, result_rect->left = x; flag = true; - int y1 = h / 2;+ int y1 = (h >> 1); int y2 = y1 + 1; fb_data *pixel1 = &buffer[y1 * BUFFER_WIDTH + x]; fb_data *pixel2 = &buffer[y2 * BUFFER_WIDTH + x];- int pixelstep = pixel2 - pixel1;+ const int pixelstep = pixel2 - pixel1; - int center = (sh / 2);+ int center = (sh >> 1); int dy = dist / h;- int p1 = center * PFREAL_ONE - dy / 2;- int p2 = center * PFREAL_ONE + dy / 2;-+ int p1 = center * PFREAL_ONE - (dy >> 2);+ int p2 = center * PFREAL_ONE + (dy >> 2); const fb_data *ptr = &src[column * bmp->width];+ if (alpha == 256) while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) { *pixel1 = ptr[p1 >> PFREAL_SHIFT];@@ -1160,18 +1191,8 @@ void render_slide(struct slide_data *slide, struct rect *result_rect, pixel2 += pixelstep; } else while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {- fb_data c1 = ptr[p1 >> PFREAL_SHIFT];- fb_data c2 = ptr[p2 >> PFREAL_SHIFT];-- int r1 = RGB_UNPACK_RED(c1) * alpha / 256;- int g1 = RGB_UNPACK_GREEN(c1) * alpha / 256;- int b1 = RGB_UNPACK_BLUE(c1) * alpha / 256;- int r2 = RGB_UNPACK_RED(c2) * alpha / 256;- int g2 = RGB_UNPACK_GREEN(c2) * alpha / 256;- int b2 = RGB_UNPACK_BLUE(c2) * alpha / 256;-- *pixel1 = LCD_RGBPACK(r1, g1, b1);- *pixel2 = LCD_RGBPACK(r2, g2, b2);+ *pixel1 = fade_color(ptr[p1 >> PFREAL_SHIFT], alpha4);+ *pixel2 = fade_color(ptr[p2 >> PFREAL_SHIFT], alpha4); p1 -= dy; p2 += dy; y1--;@@ -1192,7 +1213,7 @@ void render_slide(struct slide_data *slide, struct rect *result_rect, /** Jump the the given slide_index */-static inline void set_current_slide(int slide_index)+static inline void set_current_slide(const int slide_index) { step = 0; center_index = fbound(slide_index, 0, number_of_slides - 1);@@ -1206,10 +1227,8 @@ static inline void set_current_slide(int slide_index) */ void start_animation(void) {- if (!animation_is_active) {- step = (target < center_slide.slide_index) ? -1 : 1;- animation_is_active = true;- }+ step = (target < center_slide.slide_index) ? -1 : 1;+ pf_state = pf_scrolling; } /**@@ -1217,11 +1236,14 @@ void start_animation(void) */ void show_previous_slide(void) {- if (step >= 0) {+ if (step == 0) { if (center_index > 0) { target = center_index - 1; start_animation(); }+ } else if ( step > 0 ) {+ target = center_index;+ start_animation(); } else { target = fmax(0, center_index - 2); }@@ -1233,11 +1255,14 @@ void show_previous_slide(void) */ void show_next_slide(void) {- if (step <= 0) {+ if (step == 0) { if (center_index < number_of_slides - 1) { target = center_index + 1; start_animation(); }+ } else if ( step < 0 ) {+ target = center_index;+ start_animation(); } else { target = fmin(center_index + 2, number_of_slides - 1); }@@ -1247,7 +1272,7 @@ void show_next_slide(void) /** Return true if the rect has size 0 */-bool is_empty_rect(struct rect *r)+static inline bool is_empty_rect(struct rect *r) { return ((r->left == 0) && (r->right == 0) && (r->top == 0) && (r->bottom == 0));@@ -1257,7 +1282,7 @@ bool is_empty_rect(struct rect *r) /** Render the slides. Updates only the offscreen buffer. */-void render(void)+void render_all_slides(void) { rb->lcd_set_background(LCD_RGBPACK(0,0,0)); rb->lcd_clear_display(); /* TODO: Optimizes this by e.g. invalidating rects */@@ -1266,6 +1291,7 @@ void render(void) int nright = config.show_slides; struct rect r;+ r.left = LCD_WIDTH; r.top = 0; r.bottom = 0; r.right = 0; render_slide(¢er_slide, &r, 256, -1, -1); #ifdef DEBUG_DRAW rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);@@ -1277,6 +1303,8 @@ void render(void) /* no animation, boring plain rendering */ for (index = 0; index < nleft - 1; index++) { int alpha = (index < nleft - 2) ? 256 : 128;+ alpha -= extra_fade;+ if (alpha < 0 ) alpha = 0; render_slide(&left_slides[index], &r, alpha, 0, c1 - 1); if (!is_empty_rect(&r)) { #ifdef DEBUG_DRAW@@ -1287,6 +1315,8 @@ void render(void) } for (index = 0; index < nright - 1; index++) { int alpha = (index < nright - 2) ? 256 : 128;+ alpha -= extra_fade;+ if (alpha < 0 ) alpha = 0; render_slide(&right_slides[index], &r, alpha, c2 + 1, BUFFER_WIDTH); if (!is_empty_rect(&r)) {@@ -1341,10 +1371,8 @@ void render(void) /** Updates the animation effect. Call this periodically from a timer. */-void update_animation(void)+void update_scroll_animation(void) {- if (!animation_is_active)- return; if (step == 0) return; @@ -1394,7 +1422,7 @@ void update_animation(void) if (center_index == target) { reset_slides();- animation_is_active = false;+ pf_state = pf_idle; step = 0; fade = 256; return;@@ -1456,9 +1484,13 @@ void cleanup(void *parameter) } if ( empty_slide_hid != - 1) rb->bufclose(empty_slide_hid);+ rb->lcd_set_drawmode(old_drawmode); } -+/**+ Create the "?" slide, that is shown while loading+ or when no cover was found.+ */ int create_empty_slide(bool force) { if ( force || ! rb->file_exists( EMPTY_SLIDE ) ) {@@ -1483,7 +1515,7 @@ int create_empty_slide(bool force) int settings_menu(void) { int selection = 0; - MENUITEM_STRINGLIST(settings_menu,"PictureFlow Settings",NULL,"Show FPS", "Spacing", "Center margin", "Number of slides", "Rebuild cache");+ MENUITEM_STRINGLIST(settings_menu,"PictureFlow Settings",NULL,"Show FPS", "Spacing", "Center margin", "Number of slides", "Zoom", "Rebuild cache"); do { selection=rb->do_menu(&settings_menu,&selection);@@ -1514,6 +1546,13 @@ int settings_menu(void) { break; case 4:+ rb->set_int("Number of slides", "", 1, &(config.zoom),+ NULL, 1, 10, 300, NULL );+ recalc_table();+ reset_slides();+ break;++ case 5: rb->remove(CACHE_PREFIX "/ready"); rb->remove(EMPTY_SLIDE); rb->splash(HZ, "Cache will be rebuilt on next restart");@@ -1561,21 +1600,43 @@ int main_menu(void) } } -bool read_pfconfig(void)+/**+ Fill the config struct with some defaults+ */+void set_default_config(void) {- /* defaults */ config.spacing_between_slides = 40; config.extra_spacing_for_center_slide = 0; config.show_slides = 3;+ config.avg_album_width = 0;+ config.zoom = 100;+}++/**+ Read the config file.+ For now, the size has to match.+ Later a version number might be appropiate.+ */+bool read_pfconfig(void)+{+ set_default_config();+ /* defaults */ int fh = rb->open( CONFIG_FILE, O_RDONLY ); if ( fh < 0 ) { /* no config yet */ return true; } int ret = rb->read(fh, &config, sizeof(struct config_data)); rb->close(fh);- return ( ret == sizeof(struct config_data) );+ if ( ret != sizeof(struct config_data) ) {+ set_default_config();+ rb->splash(2*HZ, "Config invalid. Using defaults");+ }+ return true; } +/**+ Write the config file+ */ bool write_pfconfig(void) { int fh = rb->creat( CONFIG_FILE );@@ -1586,6 +1647,194 @@ bool write_pfconfig(void) } /**+ Animation step for zooming into the current cover+ */+void update_cover_in_animation(void)+{+ cover_animation_keyframe++;+ if( cover_animation_keyframe < 20 ) {+ center_slide.distance-=5;+ center_slide.angle+=1;+ extra_fade += 13;+ }+ else if( cover_animation_keyframe < 35 ) {+ center_slide.angle+=16;+ }+ else {+ cover_animation_keyframe = 0;+ selected_track = 0;+ pf_state = pf_show_tracks;+ }+}++/**+ Animation step for zooming out the current cover+ */+void update_cover_out_animation(void)+{+ cover_animation_keyframe++;+ if( cover_animation_keyframe <= 15 ) {+ center_slide.angle-=16;+ }+ else if( cover_animation_keyframe < 35 ) {+ center_slide.distance+=5;+ center_slide.angle-=1;+ extra_fade -= 13;+ }+ else {+ cover_animation_keyframe = 0;+ pf_state = pf_idle;+ }+}++/**+ Draw a blue gradient at y with height h+ */+static inline void draw_gradient(int y, int h)+{+ static int r, inc, c;+ inc = (100 << 8) / h;+ c = 0;+ selected_track_pulse = (selected_track_pulse+1) % 10;+ int c2 = selected_track_pulse - 5;+ for (r=0; r<h; r++) {+ rb->lcd_set_foreground(LCD_RGBPACK(c2+80-(c >> 9), c2+100-(c >> 9), c2+250-(c >> 8)));+ rb->lcd_hline(0, LCD_WIDTH, r+y);+ if ( r > h/2 )+ c-=inc;+ else+ c+=inc;+ }+}+++/**+ Reset the track list after a album change+ */+void reset_track_list(void)+{+ int albumtxt_w, albumtxt_h;+ const char* albumtxt = get_album_name(center_index);+ rb->lcd_getstringsize(albumtxt, &albumtxt_w, &albumtxt_h);+ const int height = LCD_HEIGHT-albumtxt_h-10;+ track_list_visible_entries = fmin( height/albumtxt_h , track_count );+ start_index_track_list = 0;+ track_scroll_index = 0;+ track_scroll_dir = 1;+ selected_track = 0;+}++/**+ Display the list of tracks+ */+void show_track_list(void)+{+ rb->lcd_clear_display();+ if ( center_slide.slide_index != track_index ) {+ create_track_index(center_slide.slide_index);+ reset_track_list();+ }+ static int titletxt_w, titletxt_h, titletxt_y, titletxt_x, i, color;+ titletxt_y = 0;+ int track_i;+ for (i=0; i < track_list_visible_entries; i++) {+ track_i = i+start_index_track_list;+ rb->lcd_getstringsize(get_track_name(track_i), &titletxt_w, &titletxt_h);+ titletxt_x = (LCD_WIDTH-titletxt_w)/2;+ if ( track_i == selected_track ) {+ draw_gradient(titletxt_y, titletxt_h);+ rb->lcd_set_foreground(LCD_RGBPACK(255,255,255));+ if (titletxt_w > LCD_WIDTH ) {+ if ( titletxt_w + track_scroll_index <= LCD_WIDTH ) track_scroll_dir = 1;+ else if ( track_scroll_index >= 0 ) track_scroll_dir = -1;+ track_scroll_index += track_scroll_dir*2;+ titletxt_x = track_scroll_index;+ }+ rb->lcd_putsxy(titletxt_x,titletxt_y,get_track_name(track_i));+ }+ else {+ color = 250 - (abs(selected_track - track_i) * 200 / track_count);+ rb->lcd_set_foreground(LCD_RGBPACK(color,color,color));+ rb->lcd_putsxy(titletxt_x,titletxt_y,get_track_name(track_i));+ }+ titletxt_y += titletxt_h;+ }+}++void select_next_track(void)+{+ if ( selected_track < track_count - 1 ) {+ selected_track++;+ track_scroll_index = 0;+ track_scroll_dir = 1;+ if (selected_track==(track_list_visible_entries+start_index_track_list)) start_index_track_list++;+ }+}++void select_prev_track(void)+{+ if (selected_track > 0 ) {+ if (selected_track==start_index_track_list) start_index_track_list--;+ track_scroll_index = 0;+ track_scroll_dir = 1;+ selected_track--;+ }+}++/**+ Draw the current album name+ */+void draw_album_text(void)+{+ int albumtxt_w, albumtxt_h;+ int albumtxt_y = 0;++ char *albumtxt;+ int c;+ /* Draw album text */+ if ( pf_state == pf_scrolling ) {+ c = ((slide_frame & 0xffff )/ 255);+ if (step < 0) c = 255-c;+ if (c > 128 ) { /* half way to next slide .. still not perfect! */+ albumtxt = get_album_name(center_index+step);+ c = (c-128)*2;+ }+ else {+ albumtxt = get_album_name(center_index);+ c = (128-c)*2;+ }+ }+ else {+ c= 255;+ albumtxt = get_album_name(center_index);+ }++ rb->lcd_set_foreground(LCD_RGBPACK(c,c,c));+ rb->lcd_getstringsize(albumtxt, &albumtxt_w, &albumtxt_h);+ if (center_index != prev_center_index) {+ albumtxt_x = 0;+ albumtxt_dir = -1;+ prev_center_index = center_index;+ }+ albumtxt_y = LCD_HEIGHT-albumtxt_h-10;++ if (albumtxt_w > LCD_WIDTH ) {+ rb->lcd_putsxy(albumtxt_x, albumtxt_y , albumtxt);+ if ( pf_state == pf_idle || pf_state == pf_show_tracks ) {+ if ( albumtxt_w + albumtxt_x <= LCD_WIDTH ) albumtxt_dir = 1;+ else if ( albumtxt_x >= 0 ) albumtxt_dir = -1;+ albumtxt_x += albumtxt_dir;+ }+ }+ else {+ rb->lcd_putsxy((LCD_WIDTH - albumtxt_w) /2, albumtxt_y , albumtxt);+ }+++}+++/** Main function that also contain the main plasma algorithm. */@@ -1596,7 +1845,7 @@ int main(void) if ( ! rb->dir_exists( CACHE_PREFIX ) ) { if ( rb->mkdir( CACHE_PREFIX ) < 0 ) {- rb->splash(HZ, "Could not create directory");+ rb->splash(HZ, "Could not create directory " CACHE_PREFIX ); return PLUGIN_ERROR; } }@@ -1652,8 +1901,11 @@ int main(void) slide_cache_stack_index = min_slide_cache-1; slide_cache_in_use = 0; buffer = rb->lcd_framebuffer;- animation_is_active = false;- zoom = 100;++ pf_state = pf_idle;++ track_index = -1;+ extra_fade = 0; center_index = 0; slide_frame = 0; step = 0;@@ -1665,7 +1917,6 @@ int main(void) reset_slides(); char fpstxt[10];- char *albumtxt; int button; #ifdef HAVE_ADJUSTABLE_CPU_FREQ@@ -1676,58 +1927,65 @@ int main(void) long current_update; long update_interval = 100; int fps = 0;- int albumtxt_w, albumtxt_h;- int albumtxt_x = 0, albumtxt_y = 0;- int albumtxt_dir = -1;- int c;- int prev_center_index = -1; + bool instant_update;+ old_drawmode = rb->lcd_get_drawmode();+ rb->lcd_set_drawmode(DRMODE_FG); while (true) { current_update = *rb->current_tick; frames++;- update_animation();- render(); + /* Initial rendering */+ instant_update = false;++ /* Handle states */+ switch ( pf_state ) {+ case pf_scrolling:+ update_scroll_animation();+ render_all_slides();+ instant_update = true;+ break;+ case pf_cover_in:+ update_cover_in_animation();+ render_all_slides();+ instant_update = true;+ break;+ case pf_cover_out:+ update_cover_out_animation();+ render_all_slides();+ instant_update = true;+ break;+ case pf_show_tracks:+ show_track_list();+ break;+ case pf_idle:+ render_all_slides();+ break;+ }++ /* Calculate FPS */ if (current_update - last_update > update_interval) { fps = frames * HZ / (current_update - last_update); last_update = current_update; frames = 0; } + /* Draw FPS */ if (show_fps) { rb->lcd_set_foreground(LCD_RGBPACK(255, 0, 0)); rb->snprintf(fpstxt, sizeof(fpstxt), "FPS: %d", fps); rb->lcd_putsxy(0, 0, fpstxt); } - albumtxt = get_album_name(center_index);- if ( animation_is_active ) {- c = ((slide_frame & 0xffff )/ 256);- if (step > 0) c = 255-c;- }- else c= 255;- rb->lcd_set_foreground(LCD_RGBPACK(c,c,c));- rb->lcd_getstringsize(albumtxt, &albumtxt_w, &albumtxt_h);- if (center_index != prev_center_index) {- albumtxt_x = 0;- albumtxt_dir = -1;- albumtxt_y = LCD_HEIGHT-albumtxt_h-10;- prev_center_index = center_index;- }- if (albumtxt_w > LCD_WIDTH && ! animation_is_active ) {- rb->lcd_putsxy(albumtxt_x, albumtxt_y , albumtxt);- if ( albumtxt_w + albumtxt_x <= LCD_WIDTH ) albumtxt_dir = 1;- else if ( albumtxt_x >= 0 ) albumtxt_dir = -1;- albumtxt_x += albumtxt_dir;- }- else {- rb->lcd_putsxy((LCD_WIDTH - albumtxt_w) /2, albumtxt_y , albumtxt);- }+ draw_album_text(); ++ /* Copy offscreen buffer to LCD and give time to other threads */ rb->lcd_update(); rb->yield(); - button = pluginlib_getaction(rb, animation_is_active ? 0 : HZ/16,+ /*/ Handle buttons */+ button = pluginlib_getaction(rb, instant_update ? 0 : HZ/16, plugin_contexts, NB_ACTION_CONTEXTS); switch (button) {@@ -1735,19 +1993,46 @@ int main(void) return PLUGIN_OK; case PICTUREFLOW_MENU:- ret = main_menu();- if ( ret == -1 ) return PLUGIN_OK;- if ( ret != 0 ) return i;+ if ( pf_state == pf_idle || pf_state == pf_scrolling ) {+ ret = main_menu();+ if ( ret == -1 ) return PLUGIN_OK;+ if ( ret != 0 ) return i;+ rb->lcd_set_drawmode(DRMODE_FG);+ }+ else {+ pf_state = pf_cover_out;+ } break; case PICTUREFLOW_NEXT_ALBUM: case PICTUREFLOW_NEXT_ALBUM_REPEAT:- show_next_slide();+ if ( pf_state == pf_idle || pf_state == pf_scrolling )+ show_next_slide(); break; case PICTUREFLOW_PREV_ALBUM: case PICTUREFLOW_PREV_ALBUM_REPEAT:- show_previous_slide();+ if ( pf_state == pf_idle || pf_state == pf_scrolling )+ show_previous_slide();+ break;++ case PICTUREFLOW_NEXT_TRACK:+ case PICTUREFLOW_NEXT_TRACK_REPEAT:+ if ( pf_state == pf_show_tracks )+ select_next_track();+ break;++ case PICTUREFLOW_PREV_TRACK:+ case PICTUREFLOW_PREV_TRACK_REPEAT:+ if ( pf_state == pf_show_tracks )+ select_prev_track();+ break;++ case PICTUREFLOW_SELECT_ALBUM:+ if ( pf_state == pf_idle )+ pf_state = pf_cover_in;+ if ( pf_state == pf_show_tracks )+ pf_state = pf_cover_out; break; default: