rendered paste bodydiff --git a/apps/plugin.c b/apps/plugin.cindex 9067097..9b3e679 100644--- a/apps/plugin.c+++ b/apps/plugin.c@@ -532,6 +532,24 @@ static const struct plugin_api rockbox_api = { remote_backlight_set_timeout_plugged, #endif #endif /* HAVE_REMOTE_LCD */++#if (CONFIG_CODEC == SWCODEC)+ bufopen,+ bufalloc,+ bufclose,+ bufseek,+ bufadvance,+ bufread,+ bufgetdata,+ bufgettail,+ bufcuttail,++ buf_get_offset,+ buf_handle_offset,+ buf_request_buffer_handle,+ buf_set_base_handle,+ buf_used,+#endif }; int plugin_load(const char* plugin, void* parameter)diff --git a/apps/plugin.h b/apps/plugin.hindex 9105932..825b0a0 100644--- a/apps/plugin.h+++ b/apps/plugin.h@@ -78,6 +78,7 @@ #include "list.h" #include "tree.h" #include "color_picker.h"+#include "buffering.h" #ifdef HAVE_REMOTE_LCD #include "lcd-remote.h"@@ -655,6 +656,24 @@ struct plugin_api { void (*remote_backlight_set_timeout_plugged)(int index); #endif #endif /* HAVE_REMOTE_LCD */++#if (CONFIG_CODEC == SWCODEC)+ int (*bufopen)(const char *file, size_t offset, enum data_type type);+ int (*bufalloc)(const void *src, size_t size, enum data_type type);+ bool (*bufclose)(int handle_id);+ int (*bufseek)(int handle_id, size_t newpos);+ int (*bufadvance)(int handle_id, off_t offset);+ ssize_t (*bufread)(int handle_id, size_t size, void *dest);+ ssize_t (*bufgetdata)(int handle_id, size_t size, void **data);+ ssize_t (*bufgettail)(int handle_id, size_t size, void **data);+ ssize_t (*bufcuttail)(int handle_id, size_t size);++ ssize_t (*buf_get_offset)(int handle_id, void *ptr);+ ssize_t (*buf_handle_offset)(int handle_id);+ void (*buf_request_buffer_handle)(int handle_id);+ void (*buf_set_base_handle)(int handle_id);+ size_t (*buf_used)(void);+#endif }; /* plugin header */diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIESindex 709c5be..57cab53 100644--- a/apps/plugins/CATEGORIES+++ b/apps/plugins/CATEGORIES@@ -47,6 +47,7 @@ mpegplayer,viewers nim,games oscilloscope,demos pacbox,games+pictureflow,demos plasma,demos pong,games properties,viewersdiff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCESindex 727e5a8..b24fef1 100644--- a/apps/plugins/SOURCES+++ b/apps/plugins/SOURCES@@ -20,6 +20,8 @@ stopwatch.c vbrfix.c viewer.c +pictureflow.c+ #ifdef OLYMPUS_MROBE_500 /* remove these once the plugins before it are compileable */ jpeg.cdiff --git a/apps/plugins/pictureflow.c b/apps/plugins/pictureflow.cnew file mode 100644index 0000000..aeb34bd--- /dev/null+++ b/apps/plugins/pictureflow.c@@ -0,0 +1,1215 @@++/***************************************************************************+* __________ __ ___.+* Open \______ \ ____ ____ | | _\_ |__ _______ ___+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \+* \/ \/ \/ \/ \/+*+* Copyright (C) 2007 Jonas Hurrelmann (j@outpo.st)+* Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) (original Qt Version)+*+* PictureFlow Demo+*+* All files in this archive are subject to the GNU General Public License.+* See the file COPYING in the source tree root for full license agreement.+*+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY+* KIND, either express or implied.+*+****************************************************************************/++#include "plugin.h"+#include "helper.h"+#include "lib/bmp.h"+++#ifdef HAVE_LCD_BITMAP /* and also not for the Player */++PLUGIN_HEADER+/******************************* Globals ***********************************/+static struct plugin_api *rb; /* global api struct pointer */++/* Key assignement */++#if (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)+#define PICTUREFLOW_QUIT BUTTON_MENU+#define PICTUREFLOW_SELECT_ALBUM BUTTON_SELECT+#define PICTUREFLOW_NEXT_ALBUM BUTTON_SCROLL_FWD+#define PICTUREFLOW_PREV_ALBUM BUTTON_SCROLL_BACK+#endif+++#if CONFIG_KEYPAD == GIGABEAT_PAD+#define PICTUREFLOW_QUIT BUTTON_MENU+#define PICTUREFLOW_SELECT_ALBUM BUTTON_SELECT+#define PICTUREFLOW_NEXT_ALBUM BUTTON_RIGHT+#define PICTUREFLOW_PREV_ALBUM BUTTON_LEFT+#endif+++++// for fixed-point arithmetic, we need minimum 32-bit long+// long long (64-bit) might be useful for multiplication and division+#define PFreal long+#define PFREAL_SHIFT 10+#define PFREAL_FACTOR (1 << PFREAL_SHIFT)+#define PFREAL_ONE (1 << PFREAL_SHIFT)+#define PFREAL_HALF (PFREAL_ONE >> 1)+++#define fmin(a,b) (((a) < (b)) ? (a) : (b))+#define fmax(a,b) (((a) > (b)) ? (a) : (b))+#define fbound(min,val,max) (fmax((min),fmin((max),(val))))++// 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;+}++static inline PFreal fdiv(PFreal num, PFreal den)+{+ long long p = (long long) (num) << (PFREAL_SHIFT * 2);+ long long q = p / (long long) den;+ long long r = q >> PFREAL_SHIFT;++ return r;+}++/*+#define fmul(a,b) ( ((a)*(b)) >> PFREAL_SHIFT )+#define fdiv(n,m) ( ((n)<< PFREAL_SHIFT ) / m )++#define fconv(a, q1, q2) (((q2)>(q1)) ? (a)<<((q2)-(q1)) : (a)>>((q1)-(q2)))+#define tofloat(a, q) ( (float)(a) / (float)(1<<(q)) )+*/++/*+static inline PFreal fmul(PFreal a, PFreal b)+{+ return (a*b) >> PFREAL_SHIFT;+}++static inline PFreal fdiv(PFreal n, PFreal m)+{+ return (n<<(PFREAL_SHIFT))/m;+}+*/+++#define IANGLE_MAX 1024+#define IANGLE_MASK 1023++/**+ Precomupted sin-table+*/+static const PFreal sinTable[IANGLE_MAX] = { // 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+};++inline PFreal fsin(int iangle)+{+ while (iangle < 0)+ iangle += IANGLE_MAX;+ return sinTable[iangle & IANGLE_MASK];+}++inline PFreal fcos(int iangle)+{+ // quarter phase shift+ return fsin(iangle + (IANGLE_MAX >> 2));+}+++#define IMG_WIDTH 100+#define IMG_HEIGHT 100+#define SLIDE_WIDTH 100+#define SLIDE_HEIGHT 100+#define BUFFER_WIDTH LCD_WIDTH+#define BUFFER_HEIGHT LCD_HEIGHT++// TODO: it would be nice if we could actually make use of much more memory +// this would speed up the whole process but we hit the PLUGIN_RAM (512k) at about+// 11 (where one slide is 100*200*2 ~ 40kb).+// It seems we need to do our own malloc and grab memory from the audio buffer.+// Given the 40kb per slide, we would get 500 slides into 20MB.+// The question is, how much audio buffer we can allocate until we have a noticable +// negative effect.+#define SLIDE_CACHE_SIZE 11+#define LEFT_SLIDES_COUNT 3+#define RIGHT_SLIDES_COUNT 3+++#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200++++struct slide_data {+ int slide_index;+ int angle;+ PFreal cx;+ PFreal cy;+};+++struct rect {+ int left;+ int right;+ int top;+ int bottom;+};++struct load_slide_event_data {+ int slide_index;+ int cache_index;+};++/** below we allocate the memory we want to use **/++static fb_data *buffer;+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[LEFT_SLIDES_COUNT]; +static struct slide_data right_slides[RIGHT_SLIDES_COUNT];+static int slide_frame;+static int step;+static int target;+static int fade;+static int center_index; // index of the slide that is in the center+static int itilt;+static int spacing; // spacing between slides+static int zoom;+static PFreal offsetX;+static PFreal offsetY;+static bool show_fps; // show fps in the main screen+static int number_of_slides;+++static int slide_cache_map[SLIDE_CACHE_SIZE+1]; // map cached slides to an index+static int slide_cache_hid[SLIDE_CACHE_SIZE+1]; // map cached slides to a handle ID+static long slide_cache_touched[SLIDE_CACHE_SIZE+1]; // map cached slides to an index+static int slide_cache_in_use;++/* use long for aligning */+unsigned long thread_stack[THREAD_STACK_SIZE / sizeof(long)];+static int slide_cache_stack[SLIDE_CACHE_SIZE]; // queue (as array) for scheduling load_surface+static int slide_cache_stack_index;+struct mutex slide_cache_stack_lock;++struct thread_entry *thread_id;+struct event_queue thread_q;++#define EV_EXIT 9999+#define EV_WAKEUP 1337++int load_surface(int);++/**+ 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)+{+ int i = slide_cache_stack_index + 1;+ while (i--) {+ if ( slide_cache_stack[i] == slide_index ) return i;+ };+ return -1;+}++/**+ Push the slide_index on the stack so the image will be loaded.+ The algorithm tries to keep the center_index on top and the+ slide_index as high as possible (so second if center_index is + on the stack).+ */+void slide_stack_push(int slide_index)+{+ rb->mutex_lock(&slide_cache_stack_lock);++ if ( slide_cache_stack_index == -1 ) {+ // empty stack, no checks at all+ slide_cache_stack[ ++slide_cache_stack_index ] = slide_index;+ rb->mutex_unlock(&slide_cache_stack_lock);+ return;+ }+ + int i = slide_stack_get_index( slide_index );++ if ( i == slide_cache_stack_index ) { + // slide_index is on top, so we do not change anything+ rb->mutex_unlock(&slide_cache_stack_lock);+ return;+ }+ + if ( i >= 0 ) {+ // slide_index is already on the stack, but not on top+ int tmp = slide_cache_stack[ slide_cache_stack_index ];+ if ( tmp == center_index ) { + // the center_index is on top of the stack so do not touch that+ if ( slide_cache_stack_index > 0 ) { + // but maybe it is possible to swap the given slide_index to the second place+ tmp = slide_cache_stack[ slide_cache_stack_index -1 ];+ slide_cache_stack[ slide_cache_stack_index - 1 ] = slide_cache_stack[ i ];+ slide_cache_stack[ i ] = tmp;+ }+ }+ else { + // if the center_index is not on top (i.e. already loaded) bring the slide_index to the top+ slide_cache_stack[ slide_cache_stack_index ] = slide_cache_stack[ i ];+ slide_cache_stack[ i ] = tmp;+ }+ }+ else {+ // slide_index is not on the stack+ if ( slide_cache_stack_index >= SLIDE_CACHE_SIZE-1 ) { + // if we exceeded the stack size, clear the first half of the stack+ slide_cache_stack_index = SLIDE_CACHE_SIZE/2;+ for (i = 0; i <= slide_cache_stack_index ; i++)+ slide_cache_stack[ i ] = slide_cache_stack[ i + slide_cache_stack_index ];+ }+ if ( slide_cache_stack[ slide_cache_stack_index ] == center_index ) {+ // if the center_index is on top leave it there+ slide_cache_stack[ slide_cache_stack_index ] = slide_index;+ slide_cache_stack[ ++slide_cache_stack_index ] = center_index;+ }+ else {+ // usual stack case: push the slide_index on top+ slide_cache_stack[ ++slide_cache_stack_index ] = slide_index;+ }+ }+ rb->mutex_unlock(&slide_cache_stack_lock);+}++/**+ Pop the topmost item from the stack and decrease the stack size+ */+inline int slide_stack_pop(void)+{+ rb->mutex_lock(&slide_cache_stack_lock);+ int result;+ if ( slide_cache_stack_index >= 0 )+ result = slide_cache_stack[ slide_cache_stack_index-- ];+ else + result = -1;+ rb->mutex_unlock(&slide_cache_stack_lock);+ return result;+}++/**+ 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)+{+ slide_stack_push(slide_index);+ rb->queue_post(&thread_q, EV_WAKEUP, 0);+}+++/**+ Thread used for loading and preparing bitmaps in the background+ */+void thread(void)+{+ long sleep_time = 5 * HZ;+ struct queue_event ev;+ while (1) {+ rb->queue_wait_w_tmo(&thread_q, &ev, sleep_time);+ switch (ev.id) {+ case EV_EXIT:+ return;+ case EV_WAKEUP:+ // we just woke up+ break;+ }+ int slide_index;+ while ( (slide_index = slide_stack_pop()) != -1 ) {+ load_surface( slide_index );+ rb->sleep( HZ ); // FIXME: optimize if we are satisified :)+ }+ }+}+++/**+ End the thread by posting the EV_EXIT event+ */+void end_pf_thread(void)+{+ rb->queue_post(&thread_q, EV_EXIT, 0);+ rb->thread_wait(thread_id);+ /* remove the thread's queue from the broadcast list */+ rb->queue_delete(&thread_q);+}+++/**+ Create the thread an setup the event queue+ */+bool create_pf_thread(void)+{+ rb->queue_init(&thread_q, true); /* put the thread's queue in the bcast list */+ if ((thread_id = rb->create_thread(+ thread, + thread_stack,+ sizeof(thread_stack), + 0,+ "Picture load thread" + IF_PRIO(, PRIORITY_BACKGROUND)+ IF_COP(, CPU)+ )+ ) == NULL) {+ return false;+ }+ return true;+}+++static fb_data tmp_bmp_buffer[IMG_WIDTH * IMG_HEIGHT]; // static buffer for reading the bitmaps++/**+ Read the slide at the given index into the cache+ TODO: This could be *much* more efficient+ It probably would make sense to store the files in raw format + We would save the expensive 24->16bpp conversion and the computation of the + reflection.+ */+static bool read_bmp(int slide_index, int cache_index)+{+ struct bitmap tmp_bmp;+ int ret;+ tmp_bmp.data = (char *) &tmp_bmp_buffer;+ char path[MAX_PATH];+ // TODO: map slide_index to the actual album filename+ rb->snprintf(path, sizeof(path), "/pics/cover%d.bmp", slide_index+1);+ ret = rb->read_bmp_file(path, &tmp_bmp, sizeof(tmp_bmp_buffer), FORMAT_NATIVE);++ int hid = rb->bufalloc(NULL, sizeof(struct bitmap) + (tmp_bmp.width+1) * tmp_bmp.height*2 * sizeof(fb_data), TYPE_BITMAP);+ if (hid < 0)+ return false;++ struct bitmap *bmp;+ rb->bufgetdata(hid, 0, (void *)&bmp);+ bmp->width = tmp_bmp.width;+ bmp->height = tmp_bmp.height;+ bmp->format = tmp_bmp.format;+ bmp->data = (char *)(bmp + sizeof(struct bitmap));+ slide_cache_hid[cache_index] = hid;++ if (ret > 0) {+ int h = bmp->height;+ int w = bmp->width;+ fb_data *result = (fb_data *)bmp->data;+ // slightly larger, to accomodate for the reflection+ int hs = h * 2;+ int hofs = w / 3;+ rb->memset(result, 0, sizeof(fb_data) * w * hs);+ int x, y;+ // transpose the image, this is to speed-up the rendering+ // because we process one column at a time+ // (and much better and faster to work row-wise, i.e in one scanline)+ for (x = 0; x < w; x++)+ for (y = 0; y < h; y++)+ result[bmp->width * 2 * x + (hofs + y)] =+ tmp_bmp_buffer[y * bmp->width + x];++ // create the reflection+ int ht = hs - h - hofs;+ int hte = ht;+ for (x = 0; x < w; x++) {+ for (y = 0; y < ht; y++) {+ fb_data color = tmp_bmp_buffer[x + bmp->width * (bmp->height - y - 1)];+ int r = RGB_UNPACK_RED(color) * (hte - y) / hte * 3 / 5;+ int g = RGB_UNPACK_GREEN(color) * (hte - y) / hte * 3 / 5;+ int b = RGB_UNPACK_BLUE(color) * (hte - y) / hte * 3 / 5;+ result[h + hofs + y + bmp->width * x * 2] =+ LCD_RGBPACK(r, g, b);+ }+ }+ if ( cache_index < SLIDE_CACHE_SIZE ) {+ slide_cache_map[cache_index] = slide_index;+ slide_cache_touched[cache_index] = *rb->current_tick;+ }+ return true;+ }+ return false;+}++++/**+ Load the surface from a bmp and store overwrite the oldest slide in the cache+ */+int load_surface(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;+ read_bmp(slide_index, slide_cache_in_use++);+ }+ else {+ for (i = 0; i < SLIDE_CACHE_SIZE; i++) { // look for oldest slide+ if (slide_cache_touched[i] < oldest_tick) {+ oldest_slide = i;+ oldest_tick = slide_cache_touched[i];+ }+ }+ slide_cache_hid[oldest_slide] = -1;+ rb->bufclose(slide_cache_hid[oldest_slide]);+ read_bmp(slide_index, oldest_slide);+ }+ return oldest_slide;+}+++/**+ Get a slide from the buffer+ */+struct bitmap *get_slide(int hid)+{+ if (hid < 0)+ return NULL;++ struct bitmap *bmp;++ ssize_t ret = rb->bufgetdata(hid, 0, (void *)&bmp);+ if (ret < 0)+ return NULL;++ return bmp;+}+++/**+ Return the requested surface+*/+struct bitmap *surface(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 ( slide_cache_map[i] == slide_index ) { + // We have already loaded our slide, so touch it and return it.+ slide_cache_touched[i] = *rb->current_tick;+ return get_slide(slide_cache_hid[i]);+ }+ }+ request_surface(slide_index);+ return get_slide(slide_cache_hid[SLIDE_CACHE_SIZE]);+}++/**+ adjust slides so that they are in "steady state" position+ */+void reset_slides(void)+{+ center_slide.angle = 0;+ center_slide.cx = 0;+ center_slide.cy = 0;+ center_slide.slide_index = center_index;++ int i;+ for (i = 0; i < LEFT_SLIDES_COUNT; i++) {+ struct slide_data *si = &left_slides[i];+ si->angle = itilt;+ si->cx = -(offsetX + spacing * i * PFREAL_ONE);+ si->cy = offsetY;+ si->slide_index = center_index - 1 - i;+ }++ for (i = 0; i < RIGHT_SLIDES_COUNT; i++) {+ struct slide_data *si = &right_slides[i];+ si->angle = -itilt;+ si->cx = offsetX + spacing * i * PFREAL_ONE;+ si->cy = offsetY;+ si->slide_index = center_index + 1 + i;+ }+}+++/**+ Updates look-up table and other stuff necessary for the rendering.+ Call this when the viewport size or slide dimension is changed.+ */+void recalc_table(void)+{+ int w = (BUFFER_WIDTH + 1) / 2;+ int h = (BUFFER_HEIGHT + 1) / 2;+ int i;+ for (i = 0; i < w; i++) {+ PFreal gg = (PFREAL_HALF + i * PFREAL_ONE) / (2 * h);+ rays[w - i - 1] = -gg;+ rays[w + i] = gg;+ }+++ itilt = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted++ offsetX = SLIDE_WIDTH / 2 * (PFREAL_ONE - fcos(itilt));+ offsetY = SLIDE_WIDTH / 2 * fsin(itilt);+ offsetX += SLIDE_WIDTH * PFREAL_ONE;+ offsetY += SLIDE_WIDTH * PFREAL_ONE / 4;+ spacing = 40;+}++++/**+ render a single slide+*/+void render_slide(struct slide_data *slide, struct rect *result_rect,+ int alpha, int col1, int col2)+{+ rb->memset(result_rect, 0, sizeof(struct rect));+ struct bitmap *bmp = surface(slide->slide_index);+ if (!bmp) {+ return;+ }+ fb_data *src = (fb_data *)bmp->data;++ int sw = bmp->height;+ int sh = bmp->width * 2;++ int h = LCD_HEIGHT;+ int w = LCD_WIDTH;++ if (col1 > col2) {+ int c = col2;+ col2 = col1;+ col1 = c;+ }++ col1 = (col1 >= 0) ? col1 : 0;+ col2 = (col2 >= 0) ? col2 : w - 1;+ col1 = fmin(col1, w - 1);+ col2 = fmin(col2, w - 1);++ int distance = h * 100 / zoom;+ PFreal sdx = fcos(slide->angle);+ PFreal sdy = fsin(slide->angle);+ PFreal xs = slide->cx - bmp->width * sdx / 2;+ PFreal ys = slide->cy - bmp->width * sdy / 2;+ PFreal dist = distance * PFREAL_ONE;+++ int xi = fmax((PFreal) 0,+ ((w * PFREAL_ONE / 2) ++ fdiv(xs * h, dist + ys)) >> PFREAL_SHIFT);+ if (xi >= w) {+ return;+ }++ bool flag = false;+ result_rect->left = xi;+ int x;+ for (x = fmax(xi, col1); x <= col2; x++) {+ PFreal hity = 0;+ PFreal fk = rays[x];+ if (sdy) {+ fk = fk - fdiv(sdx, sdy);+ hity = -fdiv(( rays[x] * distance + - slide->cx + + slide->cy * sdx / sdy), fk);+ }++ dist = distance * PFREAL_ONE + hity;+ if (dist < 0)+ continue;++ PFreal hitx = fmul(dist, rays[x]);++ PFreal hitdist = fdiv(hitx - slide->cx, sdx);++ int column = sw / 2 + (hitdist >> PFREAL_SHIFT);+ if (column >= sw)+ break;++ if (column < 0)+ continue;++ result_rect->right = x;+ if (!flag)+ result_rect->left = x;+ flag = true;++ int y1 = h / 2;+ int y2 = y1 + 1;+ fb_data *pixel1 = &buffer[y1 * BUFFER_WIDTH + x];+ fb_data *pixel2 = &buffer[y2 * BUFFER_WIDTH + x];+ int pixelstep = pixel2 - pixel1;++ int center = (sh / 2);+ int dy = dist / h;+ int p1 = center * PFREAL_ONE - dy / 2;+ int p2 = center * PFREAL_ONE + dy / 2;+++ const fb_data *ptr = &src[column * bmp->width * 2];+ if (alpha == 256)+ while ((y1 >= 0) && (y2 < h) && (p1 >= 0)) {+ *pixel1 = ptr[p1 >> PFREAL_SHIFT];+ *pixel2 = ptr[p2 >> PFREAL_SHIFT];+ p1 -= dy;+ p2 += dy;+ y1--;+ y2++;+ pixel1 -= pixelstep;+ 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);+ p1 -= dy;+ p2 += dy;+ y1--;+ y2++;+ pixel1 -= pixelstep;+ pixel2 += pixelstep;+ }+ }+ // let the music play...+ rb->yield();++ result_rect->top = 0;+ result_rect->bottom = h - 1;+ return;+}+++static inline void set_current_slide(int index)+{+ step = 0;+ center_index = fbound(index, 0, number_of_slides - 1);+ target = center_index;+ slide_frame = index << 16;+ reset_slides();+}++void start_animation(void)+{+ if (!animation_is_active) {+ step = (target < center_slide.slide_index) ? -1 : 1;+ animation_is_active = true;+ }+}++void show_previous_slide(void)+{+ if (step >= 0) {+ if (center_index > 0) {+ target = center_index - 1;+ start_animation();+ }+ } else {+ target = fmax(0, center_index - 2);+ }+}++void show_next_slide(void)+{+ if (step <= 0) {+ if (center_index < number_of_slides - 1) {+ target = center_index + 1;+ start_animation();+ }+ } else {+ target = fmin(center_index + 2, number_of_slides - 1);+ }+}++++/**+ Return true if the rect has size 0+*/+bool is_empty_rect(struct rect *r)+{+ return ((r->left == 0) && (r->right == 0) && (r->top == 0)+ && (r->bottom == 0));+}+++/**+ Render the slides. Updates only the offscreen buffer.+*/+void render(void)+{+ rb->lcd_set_background(LCD_RGBPACK(0,0,0));+ rb->lcd_clear_display(); // TODO: Optimizes this by e.g. invalidating rects++ int nleft = LEFT_SLIDES_COUNT;+ int nright = RIGHT_SLIDES_COUNT;++ struct rect r;+ 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);+#endif+ int c1 = r.left;+ int c2 = r.right;+ int index;+ if (step == 0) {+ // no animation, boring plain rendering+ for (index = 0; index < nleft - 1; index++) {+ int alpha = (index < nleft - 2) ? 256 : 128;+ render_slide(&left_slides[index], &r, alpha, 0, c1 - 1);+ if (!is_empty_rect(&r)) {+#ifdef DEBUG_DRAW+ rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);+#endif+ c1 = r.left;+ }+ }+ for (index = 0; index < nright - 1; index++) {+ int alpha = (index < nright - 2) ? 256 : 128;+ render_slide(&right_slides[index], &r, alpha, c2 + 1,+ BUFFER_WIDTH);+ if (!is_empty_rect(&r)) {+#ifdef DEBUG_DRAW+ rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);+#endif+ c2 = r.right;+ }+ }+ } else {+ // the first and last slide must fade in/fade out+ for (index = 0; index < nleft; index++) {+ int alpha = 256;+ if (index == nleft - 1)+ alpha = (step > 0) ? 0 : 128 - fade / 2;+ if (index == nleft - 2)+ alpha = (step > 0) ? 128 - fade / 2 : 256 - fade / 2;+ if (index == nleft - 3)+ alpha = (step > 0) ? 256 - fade / 2 : 256;+ render_slide(&left_slides[index], &r, alpha, 0, c1 - 1);+ + if (!is_empty_rect(&r)) {+#ifdef DEBUG_DRAW+ rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);+#endif+ c1 = r.left;+ }+ }+ for (index = 0; index < nright; index++) {+ int alpha = (index < nright - 2) ? 256 : 128;+ if (index == nright - 1)+ alpha = (step > 0) ? fade / 2 : 0;+ if (index == nright - 2)+ alpha = (step > 0) ? 128 + fade / 2 : fade / 2;+ if (index == nright - 3)+ alpha = (step > 0) ? 256 : 128 + fade / 2;+ render_slide(&right_slides[index], &r, alpha, c2 + 1,+ BUFFER_WIDTH);+ if (!is_empty_rect(&r)) {+#ifdef DEBUG_DRAW+ rb->lcd_drawrect(r.left, r.top, r.right - r.left, r.bottom - r.top);+#endif+ c2 = r.right;+ }+ }+ }+}+++/**+ Updates the animation effect. Call this periodically from a timer.+*/+void update_animation(void)+{+ if (!animation_is_active)+ return;+ if (step == 0)+ return;++ int speed = 16384;+ int i;++ // deaccelerate when approaching the target+ if (true) {+ const int max = 2 * 65536;++ int fi = slide_frame;+ fi -= (target << 16);+ if (fi < 0)+ fi = -fi;+ fi = fmin(fi, max);++ int ia = IANGLE_MAX * (fi - max / 2) / (max * 2);+ speed = 512 + 16384 * (PFREAL_ONE + fsin(ia)) / PFREAL_ONE;+ }++ slide_frame += speed * step;++ int index = slide_frame >> 16;+ int pos = slide_frame & 0xffff;+ int neg = 65536 - pos;+ int tick = (step < 0) ? neg : pos;+ PFreal ftick = (tick * PFREAL_ONE) >> 16;++ // the leftmost and rightmost slide must fade away+ fade = pos / 256;++ if (step < 0)+ index++;+ if (center_index != index) {+ center_index = index;+ slide_frame = index << 16;+ center_slide.slide_index = center_index;+ for (i = 0; i < LEFT_SLIDES_COUNT; i++)+ left_slides[i].slide_index = center_index - 1 - i;+ for (i = 0; i < RIGHT_SLIDES_COUNT; i++)+ right_slides[i].slide_index = center_index + 1 + i;+ }++ center_slide.angle = (step * tick * itilt) >> 16;+ center_slide.cx = -step * fmul(offsetX, ftick);+ center_slide.cy = fmul(offsetY, ftick);++ if (center_index == target) {+ reset_slides();+ animation_is_active = false;+ step = 0;+ fade = 256;+ return;+ }++ for (i = 0; i < LEFT_SLIDES_COUNT; i++) {+ struct slide_data *si = &left_slides[i];+ si->angle = itilt;+ si->cx =+ -(offsetX + spacing * i * PFREAL_ONE + step * spacing * ftick);+ si->cy = offsetY;+ }++ for (i = 0; i < RIGHT_SLIDES_COUNT; i++) {+ struct slide_data *si = &right_slides[i];+ si->angle = -itilt;+ si->cx =+ offsetX + spacing * i * PFREAL_ONE - step * spacing * ftick;+ si->cy = offsetY;+ }++ if (step > 0) {+ PFreal ftick = (neg * PFREAL_ONE) >> 16;+ right_slides[0].angle = -(neg * itilt) >> 16;+ right_slides[0].cx = fmul(offsetX, ftick);+ right_slides[0].cy = fmul(offsetY, ftick);+ } else {+ PFreal ftick = (pos * PFREAL_ONE) >> 16;+ left_slides[0].angle = (pos * itilt) >> 16;+ left_slides[0].cx = -fmul(offsetX, ftick);+ left_slides[0].cy = fmul(offsetY, ftick);+ }++ // must change direction ?+ if (target < index)+ if (step > 0)+ step = -1;+ if (target > index)+ if (step < 0)+ step = 1;+}+++/**+ Cleanup the plugin+*/+void cleanup(void *parameter)+{+ (void) parameter;+#ifdef HAVE_ADJUSTABLE_CPU_FREQ+ rb->cpu_boost(false);+#endif+ /* Turn on backlight timeout (revert to settings) */+ backlight_use_settings(rb); /* backlight control in lib/helper.c */++ int i;+ for (i = 0; i < SLIDE_CACHE_SIZE; i++) {+ rb->bufclose(slide_cache_hid[i]);+ }+}++++/**+ Main function that also contain the main plasma+ algorithm.+ */+int main(void)+{+ if (!create_pf_thread()) {+ rb->splash(HZ, "Cannot create thread!");+ return PLUGIN_ERROR;+ }++ int i;++ // initialize+ for (i = 0; i < SLIDE_CACHE_SIZE; i++) {+ slide_cache_hid[i] = -1;+ slide_cache_touched[i] = 0;+ }+ slide_cache_in_use = 0;+ buffer = rb->lcd_framebuffer;+ animation_is_active = false;+ zoom = 100;+ center_index = 0;+ slide_frame = 0;+ step = 0;+ target = 0;+ fade = 256;+ show_fps = true;+ number_of_slides = 100; // this is the album count+/*+ for (i = 0; i < SLIDE_CACHE_SIZE; i++) {+ if (!read_bmp(i,i)) {+ rb->splash(HZ, "error reading bmp");+ return PLUGIN_ERROR;+ }+ }+ */+ read_bmp(-1,SLIDE_CACHE_SIZE); // empty slide hack... FIXME: make this prettier+ + recalc_table();+ reset_slides();++ char fpstxt[10];+ char albumtxt[30];+ int button;++#ifdef HAVE_ADJUSTABLE_CPU_FREQ+ rb->cpu_boost(true);+#endif+ int frames = 0;+ long last_update = *rb->current_tick;+ long current_update;+ long update_interval = 100;+ int fps = 0;+ int albumtxt_w, albumtxt_h;+ while (true) {+ current_update = *rb->current_tick;+ frames++;+ update_animation();+ render();++ if (current_update - last_update > update_interval) {+ fps = frames * HZ / (current_update - last_update);+ last_update = current_update;+ frames = 0;+ }++ 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);+ }++ rb->snprintf(albumtxt, sizeof(albumtxt), "Index is %d", center_index); // replace with album title+ rb->lcd_set_foreground(LCD_RGBPACK(255, 255, 255));+ rb->lcd_getstringsize(albumtxt, &albumtxt_w, &albumtxt_h);+ rb->lcd_putsxy((LCD_WIDTH - albumtxt_w) /2, LCD_HEIGHT-albumtxt_h-10, albumtxt);++++ rb->lcd_update();+ rb->yield();++ button = rb->button_get(false); //always paint -> sucks our battery+ //button = rb->button_get(!animation_is_active); // we need to wake up here if our thread yells+ switch (button) {+ case PICTUREFLOW_QUIT:+ cleanup(NULL);+ return PLUGIN_OK;+ break;++ case PICTUREFLOW_NEXT_ALBUM:+ case (PICTUREFLOW_NEXT_ALBUM | BUTTON_REPEAT):+ show_next_slide();+ break;++ case PICTUREFLOW_PREV_ALBUM:+ case (PICTUREFLOW_PREV_ALBUM | BUTTON_REPEAT):+ show_previous_slide();+ break;++ default:+ if (rb->default_event_handler_ex(button, cleanup, NULL)+ == SYS_USB_CONNECTED)+ return PLUGIN_USB_CONNECTED;+ break;+ }+ //rb->sleep(HZ/100);+ }+}++/*************************** Plugin entry point ****************************/++enum plugin_status plugin_start(struct plugin_api *api, void *parameter)+{+ int ret;++ rb = api; // copy to global api pointer+ (void) parameter;+#if LCD_DEPTH > 1+ rb->lcd_set_backdrop(NULL);+#endif+ /* Turn off backlight timeout */+ backlight_force_on(rb); /* backlight control in lib/helper.c */++ ret = main();+ end_pf_thread();+ return ret;+}++#endif /* #ifdef HAVE_LCD_BITMAP */