/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Grayscale framework * Core functions * * This is a generic framework to use grayscale display within Rockbox * plugins. It obviously does not work for the player. * * Copyright (C) 2004 Jens Arnold * * 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. * ****************************************************************************/ #ifndef SIMULATOR /* not for simulator by now */ #include "plugin.h" #ifdef HAVE_LCD_BITMAP /* and also not for the Player */ #include "gray.h" /* Global variables */ struct plugin_api *_gray_rb = NULL; /* global api struct pointer */ _tGraybuf *_graybuf = NULL; /* pointer to grayscale buffer */ short _gray_random_buffer; /* buffer for random number generator */ /* Prototypes */ static void _timer_isr(void); /* timer interrupt handler: display next bitplane */ static void _timer_isr(void) { _gray_rb->lcd_blit(_graybuf->data + MULU16(_graybuf->plane_size, _graybuf->cur_plane), _graybuf->x, _graybuf->by, _graybuf->width, _graybuf->bheight, _graybuf->width); if (++_graybuf->cur_plane >= _graybuf->depth) _graybuf->cur_plane = 0; if (_graybuf->flags & _GRAY_DEFERRED_UPDATE) /* lcd_update() requested? */ { int x1 = MAX(_graybuf->x, 0); int x2 = MIN(_graybuf->x + _graybuf->width, LCD_WIDTH); int y1 = MAX(_graybuf->by << 3, 0); int y2 = MIN((_graybuf->by << 3) + _graybuf->height, LCD_HEIGHT); if (y1 > 0) /* refresh part above overlay, full width */ _gray_rb->lcd_update_rect(0, 0, LCD_WIDTH, y1); if (y2 < LCD_HEIGHT) /* refresh part below overlay, full width */ _gray_rb->lcd_update_rect(0, y2, LCD_WIDTH, LCD_HEIGHT - y2); if (x1 > 0) /* refresh part to the left of overlay */ _gray_rb->lcd_update_rect(0, y1, x1, y2 - y1); if (x2 < LCD_WIDTH) /* refresh part to the right of overlay */ _gray_rb->lcd_update_rect(x2, y1, LCD_WIDTH - x2, y2 - y1); _graybuf->flags &= ~_GRAY_DEFERRED_UPDATE; /* clear request */ } } /*--------------------------------------------------------------------------- Initialize the framework ---------------------------------------------------------------------------- Every framework needs such a function, and it has to be called as the very first one */ void gray_init(struct plugin_api* newrb) { _gray_rb = newrb; } /*--------------------------------------------------------------------------- Prepare the grayscale display buffer ---------------------------------------------------------------------------- arguments: gbuf = pointer to the memory area to use (e.g. plugin buffer) gbuf_size = max usable size of the buffer width = width in pixels (1..112) bheight = height in 8-pixel units (1..8) depth = desired number of shades - 1 (1..32) result: = depth if there was enough memory < depth if there wasn't enough memory. The number of displayable shades is smaller than desired, but it still works = 0 if there wasn't even enough memory for 1 bitplane (black & white) You can request any depth from 1 to 32, not just powers of 2. The routine performs "graceful degradation" if the memory is not sufficient for the desired depth. As long as there is at least enough memory for 1 bitplane, it creates as many bitplanes as fit into memory, although 1 bitplane will only deliver black & white display. If you need info about the memory taken by the grayscale buffer, supply an int* as the last parameter. This int will then contain the number of bytes used. The total memory needed can be calculated as follows: total_mem = sizeof(_tGraybuf) (= 64 bytes currently) + sizeof(long) (= 4 bytes) + (width * bheight + sizeof(long)) * depth + 0..3 (longword alignment of grayscale display buffer) */ int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width, int bheight, int depth, int *buf_taken) { int possible_depth, plane_size; int i, j, align; if ((unsigned) width > LCD_WIDTH || (unsigned) bheight > (LCD_HEIGHT/8) || depth < 1) return 0; /* the buffer has to be long aligned */ align = 3 - (((unsigned long)gbuf + 3) & 3); gbuf += align; gbuf_size -= align; plane_size = MULU16(width, bheight); possible_depth = (gbuf_size - sizeof(_tGraybuf) - sizeof(long)) / (plane_size + sizeof(long)); if (possible_depth < 1) return 0; depth = MIN(depth, 32); depth = MIN(depth, possible_depth); _graybuf = (_tGraybuf *) gbuf; /* global pointer to buffer structure */ _graybuf->x = 0; _graybuf->by = 0; _graybuf->width = width; _graybuf->height = bheight << 3; _graybuf->bheight = bheight; _graybuf->plane_size = plane_size; _graybuf->depth = depth; _graybuf->cur_plane = 0; _graybuf->flags = 0; _graybuf->bitpattern = (unsigned long *) (gbuf + sizeof(_tGraybuf)); _graybuf->data = (unsigned char *) (_graybuf->bitpattern + depth + 1); i = depth - 1; j = 8; while (i != 0) { i >>= 1; j--; } _graybuf->randmask = 0xFFu >> j; /* initial state is all white */ _gray_rb->memset(_graybuf->data, 0, MULU16(depth, plane_size)); /* Precalculate the bit patterns for all possible pixel values */ for (i = 0; i <= depth; i++) { unsigned long pattern = 0; int value = 0; for (j = 0; j < depth; j++) { pattern <<= 1; value += i; if (value >= depth) value -= depth; /* "white" bit */ else pattern |= 1; /* "black" bit */ } /* now the lower bits contain the pattern */ _graybuf->bitpattern[i] = pattern; } _graybuf->fg_pattern = _graybuf->bitpattern[0]; /* black */ _graybuf->bg_pattern = _graybuf->bitpattern[depth]; /* white */ _graybuf->drawmode = GRAY_DRAW_SOLID; _graybuf->curfont = FONT_SYSFIXED; if (buf_taken) /* caller requested info about space taken */ { *buf_taken = sizeof(_tGraybuf) + sizeof(long) + MULU16(plane_size + sizeof(long), depth) + align; } return depth; } /*--------------------------------------------------------------------------- Release the grayscale display buffer ---------------------------------------------------------------------------- Switches the grayscale overlay off at first if it is still running, then sets the pointer to NULL. DO CALL either this function or at least gray_show_display(false) before you exit, otherwise nasty things may happen. */ void gray_release_buffer(void) { gray_show_display(false); _graybuf = NULL; } /*--------------------------------------------------------------------------- Switch the grayscale overlay on or off ---------------------------------------------------------------------------- enable = true: the grayscale overlay is switched on if initialized = false: the grayscale overlay is switched off and the regular lcd content is restored DO NOT call lcd_update() or any other api function that directly accesses the lcd while the grayscale overlay is running! If you need to do lcd_update() to update something outside the grayscale overlay area, use gray_deferred_update() instead. Other functions to avoid are: lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(), lcd_set_invert_display(), lcd_set_flip(), lcd_roll() The grayscale display consumes ~50 % CPU power (for a full screen overlay, less if the overlay is smaller) when switched on. You can switch the overlay on and off as many times as you want. */ void gray_show_display(bool enable) { if (enable) { _graybuf->flags |= _GRAY_RUNNING; _gray_rb->plugin_register_timer(FREQ / 67, 1, _timer_isr); } else { _gray_rb->plugin_unregister_timer(); _graybuf->flags &= ~_GRAY_RUNNING; _gray_rb->lcd_update(); /* restore whatever there was before */ } } #endif // #ifdef HAVE_LCD_BITMAP #endif // #ifndef SIMULATOR