All pastes #808807 Raw Edit

pictureflow.patch

public diff v1 · immutable
#808807 ·published 2007-12-07 22:36 UTC
rendered paste body
diff --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(&center_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 */