/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2005 Kevin Ferrare * * Fire demo plugin * * 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" #ifdef HAVE_LCD_BITMAP #include "pluginlib_actions.h" #include "fixedpoint.h" #ifndef HAVE_LCD_COLOR #include "grey.h" #endif #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64) /* Archos has not enough plugin RAM for full-width fire :( */ #define FIRE_WIDTH 106 #define FIRE_XPOS 3 #else #define FIRE_WIDTH LCD_WIDTH #define FIRE_XPOS 0 #endif PLUGIN_HEADER static const struct plugin_api* rb; /* global api struct pointer */ #ifndef HAVE_LCD_COLOR GREY_INFO_STRUCT static unsigned char draw_buffer[FIRE_WIDTH]; #endif /* Key assignement */ const struct button_mapping* plugin_contexts[]= { generic_increase_decrease, generic_directions, #if defined(HAVE_REMOTE_LCD) remote_directions, #endif generic_actions }; #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0]) #define FIRE_QUIT PLA_QUIT #define FIRE_SWITCH_FLAMES_TYPE PLA_LEFT #define FIRE_SWITCH_FLAMES_MOVING PLA_RIGHT #define FIRE_INCREASE_MULT PLA_INC #define FIRE_DECREASE_MULT PLA_DEC #define MIN_FLAME_VALUE 0 #define COOL_MAX (440/LCD_HEIGHT+2) #ifndef HAVE_LCD_COLOR static unsigned char palette[256]; void color_palette_init(unsigned char* palette) { int i; for(i=0;i<=160;i++)//palette[i]=(3/2)*i palette[i]=(i*3)/2; /* 'regular' fire doesn't exceed this value */ for(;i<=255;i++)//palette[i]=(3/20)*i+216 palette[i]=(i*3+20*217)/20; } #else static fb_data palette[256]; /* * Color palette generation algorithm taken from * the "The Demo Effects Collection" GPL project * Copyright (C) 2002 W.P. van Paassen */ void color_palette_init(fb_data* palette) { int i; for (i = 0; i < 32; i++){ /* black to blue, 32 values*/ palette[i]=LCD_RGBPACK(0, 0, 2*i); /* blue to red, 32 values*/ palette[i + 32]=LCD_RGBPACK(8*i, 0, 64 - 2*i); /* red to yellow, 32 values*/ palette[i + 64]=LCD_RGBPACK(255, 8*i, 0); /* yellow to white, 162 values */ palette[i + 96]=LCD_RGBPACK(255, 255, 0 + 4*i); palette[i + 128]=LCD_RGBPACK(255, 255, 64 + 4*i); palette[i + 160]=LCD_RGBPACK(255, 255, 128 + 4*i); palette[i + 192]=LCD_RGBPACK(255, 255, 192 + i); palette[i + 224]=LCD_RGBPACK(255, 255, 224 + i); } } #endif static void tab_init_rand(unsigned char *tab, unsigned int tab_size, int rand_max) { unsigned char *end = tab + tab_size; while(tab < end) *tab++ = (unsigned char)rb->rand() % rand_max; } struct fire { unsigned char fire[LCD_HEIGHT+3][FIRE_WIDTH]; unsigned char cooling_map[LCD_HEIGHT][FIRE_WIDTH]; int flames_type; bool moving; unsigned int mult; }; /* makes the instance a global variable since it's too big to fit on the target's stack */ static struct fire fire; static inline void fire_convolve(struct fire* fire) { unsigned int pixel_value; unsigned int cooling_value; unsigned char *ptr, *end, *cool; unsigned int mult=fire->mult; rb->yield(); /* Convolve the pixels and handle cooling (to add nice shapes effects later) */ cool = &fire->cooling_map[0][0]; ptr = &fire->fire[0][0]; end = ptr + LCD_HEIGHT*FIRE_WIDTH; switch (fire->flames_type){ case 0: do{ pixel_value = ptr[FIRE_WIDTH-1] /* fire[y+1][x-1] */ + ptr[2*FIRE_WIDTH] /* fire[y+2][x] */ + ptr[FIRE_WIDTH+1] /* fire[y+1][x+1] */ + ptr[3*FIRE_WIDTH]; /* fire[y+3][x] */ pixel_value = FMULU(pixel_value, mult) >> 10; cooling_value = *cool++; if (cooling_value <= pixel_value) pixel_value -= cooling_value; /* else it's too cold, don't frost the pixels !!! */ if (pixel_value > 255) pixel_value = 255; *ptr++ = pixel_value; }while (ptr < end); break; case 1: mult -= 2; do{ pixel_value = ptr[FIRE_WIDTH-1] /* fire[y+1][x-1] */ + ptr[FIRE_WIDTH] /* fire[y+1][x] */ + ptr[FIRE_WIDTH+1] /* fire[y+1][x+1] */ + ptr[2*FIRE_WIDTH]; /* fire[y+2][x] */ pixel_value = FMULU(pixel_value, mult) >> 10; cooling_value = *cool++; if (cooling_value <= pixel_value) pixel_value -= cooling_value; /* else it's too cold, don't frost the pixels !!! */ if (pixel_value > 255) pixel_value = 255; *ptr++ = pixel_value; }while (ptr < end); break; default: /* We should never reach this */ break; } rb->yield(); } static void fire_generate_bottom_seed(struct fire* fire) { unsigned char *ptr, *end; ptr = &fire->fire[LCD_HEIGHT][0]; end = ptr + FIRE_WIDTH; do{ *ptr++ = (MIN_FLAME_VALUE + rb->rand() % (256-MIN_FLAME_VALUE)); }while (ptr < end); } static inline void fire_step(struct fire* fire) { if(fire->moving){ /* Randomize the bottom line */ fire_generate_bottom_seed(fire); /* Add here further effects like fire letters, ball ... */ } fire_convolve(fire); } static void fire_init(struct fire* fire) { fire->mult = 261; fire->flames_type=0; fire->moving=true; rb->memset(&fire->fire[0][0], 0, sizeof(fire->fire)); tab_init_rand(&fire->cooling_map[0][0], LCD_HEIGHT*FIRE_WIDTH, COOL_MAX); fire_generate_bottom_seed(fire); } static inline void fire_draw(struct fire* fire) { int y; unsigned char *src = &fire->fire[0][0]; #ifndef HAVE_LCD_COLOR unsigned char *dest, *end; #else fb_data *dest, *end; #endif for (y = 0; y < LCD_HEIGHT; y++){ #ifndef HAVE_LCD_COLOR dest = draw_buffer; #else dest = rb->lcd_framebuffer + LCD_WIDTH * y + FIRE_XPOS; #endif end = dest + FIRE_WIDTH; do *dest++ = palette[*src++]; while (dest < end); #ifndef HAVE_LCD_COLOR grey_ub_gray_bitmap(draw_buffer, 0, y, FIRE_WIDTH, 1); #endif } #ifdef HAVE_LCD_COLOR rb->lcd_update(); #endif } void cleanup(void *parameter) { (void)parameter; #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(false); #endif #ifndef HAVE_LCD_COLOR grey_release(); #endif /* Turn on backlight timeout (revert to settings) */ backlight_use_settings(rb); /* backlight control in lib/helper.c */ } #ifndef HAVE_LCD_COLOR int init_grey(void) { unsigned char *gbuf; size_t gbuf_size = 0; /* get the remainder of the plugin buffer */ gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size); if (!grey_init(rb, gbuf, gbuf_size, GREY_ON_COP, FIRE_WIDTH, LCD_HEIGHT, NULL)){ rb->splash(HZ, "not enough memory"); return PLUGIN_ERROR; } /* switch on greyscale overlay */ grey_set_position(FIRE_XPOS, 0); grey_show(true); return PLUGIN_OK; } #endif int main(void) { int action; #ifndef HAVE_LCD_COLOR if(init_grey()!=PLUGIN_OK) return(PLUGIN_ERROR); #endif color_palette_init(palette); #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); #endif fire_init(&fire); while (true){ fire_step(&fire); fire_draw(&fire); rb->yield(); action = pluginlib_getaction(rb, 0, plugin_contexts, PLA_ARRAY_COUNT); switch(action){ case FIRE_QUIT: cleanup(NULL); return PLUGIN_OK; case FIRE_INCREASE_MULT: ++fire.mult; break; case FIRE_DECREASE_MULT: if (fire.mult > 0) --fire.mult; break; case FIRE_SWITCH_FLAMES_TYPE: fire.flames_type = (fire.flames_type + 1) % 2; break; case FIRE_SWITCH_FLAMES_MOVING: fire.moving = !fire.moving; break; default: if (rb->default_event_handler_ex(action, cleanup, NULL) == SYS_USB_CONNECTED) return PLUGIN_USB_CONNECTED; } } } /*************************** Plugin entry point ****************************/ enum plugin_status plugin_start(const struct plugin_api* api, const 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(); return ret; } #endif /* #ifdef HAVE_LCD_BITMAP */