summaryrefslogtreecommitdiff
path: root/apps/plugins/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lib')
-rw-r--r--apps/plugins/lib/SOURCES1
-rw-r--r--apps/plugins/lib/osd.c470
-rw-r--r--apps/plugins/lib/osd.h94
3 files changed, 565 insertions, 0 deletions
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index 4b33901..d49605f 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -52,6 +52,7 @@ pluginlib_jpeg_load.c
#endif
checkbox.c
+osd.c
picture.c
xlcd_core.c
xlcd_draw.c
diff --git a/apps/plugins/lib/osd.c b/apps/plugins/lib/osd.c
new file mode 100644
index 0000000..ff0533a
--- /dev/null
+++ b/apps/plugins/lib/osd.c
@@ -0,0 +1,470 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Floating on-screen display
+*
+* Copyright (C) 2012 Michael Sevakis
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#include "plugin.h"
+#include "osd.h"
+
+#if 1
+#undef DEBUGF
+#define DEBUGF(...)
+#endif
+
+/* At this time: assumes use of the default viewport for normal drawing */
+
+/* If multiple OSD's are wanted, could convert to caller-allocated */
+static struct osd
+{
+ enum osd_status
+ {
+ OSD_DISABLED = 0, /* Disabled entirely */
+ OSD_HIDDEN, /* Hidden from view */
+ OSD_VISIBLE, /* Visible on screen */
+ OSD_ERASED, /* Erased in preparation for regular drawing */
+ } status; /* View status */
+ struct viewport vp; /* Clipping viewport */
+ struct bitmap lcd_bitmap; /* The main LCD fb bitmap */
+ struct bitmap back_bitmap; /* The OSD backbuffer fb bitmap */
+ int maxwidth; /* How wide may it be at most? */
+ int maxheight; /* How high may it be at most? */
+ long timeout; /* Current popup stay duration */
+ long hide_tick; /* Tick when it should be hidden */
+ osd_draw_cb_fn_t draw_cb; /* Draw update callback */
+} osd;
+
+/* Framebuffer allocation macros */
+#if LCD_DEPTH == 1
+# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
+# define LCD_WIDTH2BYTES(w) (((w)+7)/8)
+# define LCD_BYTES2WIDTH(b) ((b)*8)
+# elif LCD_PIXELFORMAT == VERTICAL_PACKING
+# define LCD_HEIGHT2BYTES(h) (((h)+7)/8)
+# define LCD_BYTES2HEIGHT(b) ((b)*8)
+# else
+# error Unknown 1-bit format; please define macros
+# endif /* LCD_PIXELFORMAT */
+#elif LCD_DEPTH == 2
+# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
+# define LCD_WIDTH2BYTES(w) (((w)+3)/4)
+# define LCD_BYTES2WIDTH(b) ((b)*4)
+# elif LCD_PIXELFORMAT == VERTICAL_PACKING
+# define LCD_HEIGHT2BYTES(h) (((h)+3)/4)
+# define LCD_BYTES2HEIGHT(b) ((b)*4)
+# elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
+# define LCD_WIDTH2BYTES(w) ((w)*2)
+# define LCD_BYTES2WIDTH(b) ((b)/2)
+# define LCD_HEIGHT2BYTES(h) (((h)+7)/8*2)
+# define LCD_BYTES2HEIGHT(b) ((b)/2*8)
+# else
+# error Unknown 2-bit format; please define macros
+# endif /* LCD_PIXELFORMAT */
+#elif LCD_DEPTH == 16
+# define LCD_WIDTH2BYTES(w) ((w)*2)
+# define LCD_BYTES2WIDTH(b) ((b)/2)
+#else
+# error Unknown LCD depth; please define macros
+#endif /* LCD_DEPTH */
+/* Set defaults if not defined different yet. */
+#ifndef LCD_WIDTH2BYTES
+# define LCD_WIDTH2BYTES(w) (w)
+#endif
+#ifndef LCD_BYTES2WIDTH
+# define LCD_BYTES2WIDTH(b) (b)
+#endif
+#ifndef LCD_HEIGHT2BYTES
+# define LCD_HEIGHT2BYTES(h) (h)
+#endif
+#ifndef LCD_BYTES2HEIGHT
+# define LCD_BYTES2HEIGHT(b) (b)
+#endif
+
+/* Create a bitmap framebuffer from a buffer */
+static fb_data * buf_to_fb_bitmap(void *buf, size_t bufsize,
+ int *width, int *height)
+{
+ /* Used as dest, the LCD functions cannot deal with alternate
+ strides as of now - the stride guides the calulations. If
+ that is no longer the case, then width or height can be
+ used instead (and less memory needed for a small surface!).
+ */
+ DEBUGF("buf: %p bufsize: %lu\n", buf, (unsigned long)bufsize);
+
+#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+ int h = LCD_BYTES2HEIGHT(LCD_HEIGHT2BYTES(LCD_HEIGHT));
+ int w = bufsize / LCD_HEIGHT2BYTES(h);
+
+ if (w == 0)
+ {
+ DEBUGF("OSD: not enough buffer\n");
+ return NULL; /* not enough buffer */
+ }
+#else
+ int w = LCD_BYTES2WIDTH(LCD_WIDTH2BYTES(LCD_WIDTH));
+ int h = bufsize / LCD_WIDTH2BYTES(w);
+
+ if (h == 0)
+ {
+ DEBUGF("OSD: not enough buffer\n");
+ return NULL; /* not enough buffer */
+ }
+#endif
+
+ DEBUGF("fbw:%d fbh:%d\n", w, h);
+
+ *width = w;
+ *height = h;
+
+ return (fb_data *)buf;
+}
+
+static inline void osd_set_vp_pos(int x, int y, int width, int height)
+{
+ osd.vp.x = x;
+ osd.vp.y = y;
+ osd.vp.width = width;
+ osd.vp.height = height;
+}
+
+/* Sync the backbuffer to the on-screen image */
+static void osd_lcd_update_back_buffer(void)
+{
+ rb->lcd_set_framebuffer((fb_data *)osd.back_bitmap.data);
+ rb->lcd_bmp_part(&osd.lcd_bitmap, osd.vp.x, osd.vp.y,
+ 0, 0, osd.vp.width, osd.vp.height);
+ /* Assume it was on default framebuffer for now */
+ rb->lcd_set_framebuffer(NULL);
+}
+
+/* Erase the OSD to restore the framebuffer */
+static void osd_lcd_erase(void)
+{
+ rb->lcd_bmp_part(&osd.back_bitmap, 0, 0, osd.vp.x, osd.vp.y,
+ osd.vp.width, osd.vp.height);
+}
+
+/* Draw the OSD image portion using the callback */
+static void osd_lcd_draw_rect(int x, int y, int width, int height)
+{
+ rb->lcd_set_viewport(&osd.vp);
+ osd.draw_cb(x, y, width, height);
+ rb->lcd_set_viewport(NULL);
+}
+
+/* Draw the OSD image using the callback */
+static void osd_lcd_draw(void)
+{
+ osd_lcd_draw_rect(0, 0, osd.vp.width, osd.vp.height);
+}
+
+
+/** Public APIs **/
+
+/* Initialized the OSD and set its backbuffer */
+bool osd_init(void *backbuf, size_t backbuf_size,
+ osd_draw_cb_fn_t draw_cb)
+{
+ osd_show(OSD_HIDE);
+
+ osd.status = OSD_DISABLED; /* Disabled unless all is okay */
+ osd_set_vp_pos(0, 0, 0, 0);
+ osd.maxwidth = osd.maxheight = 0;
+ osd.timeout = 0;
+
+ if (!draw_cb)
+ return false;
+
+ if (!backbuf)
+ return false;
+
+ ALIGN_BUFFER(backbuf, backbuf_size, FB_DATA_SZ);
+
+ if (!backbuf_size)
+ return false;
+
+ rb->viewport_set_fullscreen(&osd.vp, SCREEN_MAIN);
+
+ fb_data *backfb = buf_to_fb_bitmap(backbuf, backbuf_size,
+ &osd.maxwidth, &osd.maxheight);
+
+ if (!backfb)
+ return false;
+
+ osd.draw_cb = draw_cb;
+
+ /* LCD framebuffer bitmap */
+ osd.lcd_bitmap.width = LCD_BYTES2WIDTH(LCD_WIDTH2BYTES(LCD_WIDTH));
+ osd.lcd_bitmap.height = LCD_BYTES2HEIGHT(LCD_HEIGHT2BYTES(LCD_HEIGHT));
+#if LCD_DEPTH > 1
+ osd.lcd_bitmap.format = FORMAT_NATIVE;
+ osd.lcd_bitmap.maskdata = NULL;
+#endif
+#ifdef HAVE_LCD_COLOR
+ osd.lcd_bitmap.alpha_offset = 0;
+#endif
+ osd.lcd_bitmap.data = (void *)rb->lcd_framebuffer;
+
+ /* Backbuffer bitmap */
+ osd.back_bitmap.width = osd.maxwidth;
+ osd.back_bitmap.height = osd.maxheight;
+#if LCD_DEPTH > 1
+ osd.back_bitmap.format = FORMAT_NATIVE;
+ osd.back_bitmap.maskdata = NULL;
+#endif
+#ifdef HAVE_LCD_COLOR
+ osd.back_bitmap.alpha_offset = 0;
+#endif
+ osd.back_bitmap.data = (void *)backfb;
+
+ DEBUGF("FB:%p BB:%p\n", osd.lcd_bitmap.data, osd.back_bitmap.data);
+
+ /* Set the default position to the whole thing */
+ osd_set_vp_pos(0, 0, osd.maxwidth, osd.maxheight);
+
+ osd.status = OSD_HIDDEN; /* Ready when you are */
+ return true;
+}
+
+/* Show/Hide the OSD on screen */
+bool osd_show(unsigned flags)
+{
+ if (flags & OSD_SHOW)
+ {
+ switch (osd.status)
+ {
+ case OSD_DISABLED:
+ break; /* No change */
+
+ case OSD_HIDDEN:
+ osd_lcd_update_back_buffer();
+ osd.status = OSD_VISIBLE;
+ osd_update();
+ osd.hide_tick = *rb->current_tick + osd.timeout;
+ break;
+
+ case OSD_VISIBLE:
+ if (flags & OSD_UPDATENOW)
+ osd_update();
+ /* Fall-through */
+ case OSD_ERASED:
+ osd.hide_tick = *rb->current_tick + osd.timeout;
+ return true;
+ }
+ }
+ else
+ {
+ switch (osd.status)
+ {
+ case OSD_DISABLED:
+ case OSD_HIDDEN:
+ break;
+
+ case OSD_VISIBLE:
+ osd_lcd_erase();
+ rb->lcd_update_rect(osd.vp.x, osd.vp.y, osd.vp.width,
+ osd.vp.height);
+ /* Fall-through */
+ case OSD_ERASED:
+ osd.status = OSD_HIDDEN;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Redraw the entire OSD */
+bool osd_update(void)
+{
+ if (osd.status != OSD_VISIBLE)
+ return false;
+
+ osd_lcd_draw();
+
+ rb->lcd_update_rect(osd.vp.x, osd.vp.y, osd.vp.width,
+ osd.vp.height);
+
+ return true;
+}
+
+/* Redraw part of the OSD (viewport-relative coordinates) */
+bool osd_update_rect(int x, int y, int width, int height)
+{
+ if (osd.status != OSD_VISIBLE)
+ return false;
+
+ osd_lcd_draw_rect(x, y, width, height);
+
+ if (x + width > osd.vp.width)
+ width = osd.vp.width - x;
+
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+ if (width <= 0)
+ return false;
+
+ if (y + height > osd.vp.height)
+ height = osd.vp.height - y;
+
+ if (y < 0)
+ {
+ height += y;
+ y = 0;
+ }
+
+ if (height <= 0)
+ return false;
+
+ rb->lcd_update_rect(osd.vp.x + x, osd.vp.y + y, width, height);
+
+ return true;
+}
+
+/* Set a new screen location and size (screen coordinates) */
+bool osd_update_pos(int x, int y, int width, int height)
+{
+ if (osd.status == OSD_DISABLED)
+ return false;
+
+ if (width < 0)
+ width = 0;
+ else if (width > osd.maxwidth)
+ width = osd.maxwidth;
+
+ if (height < 0)
+ height = 0;
+ else if (height > osd.maxheight)
+ height = osd.maxheight;
+
+ if (x == osd.vp.x && y == osd.vp.y &&
+ width == osd.vp.width && height == osd.vp.height)
+ return false; /* No change */
+
+ if (osd.status != OSD_VISIBLE)
+ {
+ /* Not visible - just update pos */
+ osd_set_vp_pos(x, y, width, height);
+ return false;
+ }
+
+ /* Visible area has changed */
+ osd_lcd_erase();
+
+ /* Update the smallest rectangle that encloses both the old and new
+ regions to make the change free of flicker (they may overlap) */
+ int xu = MIN(osd.vp.x, x);
+ int yu = MIN(osd.vp.y, y);
+ int wu = MAX(osd.vp.x + osd.vp.width, x + width) - xu;
+ int hu = MAX(osd.vp.y + osd.vp.height, y + height) - yu;
+
+ osd_set_vp_pos(x, y, width, height);
+ osd_lcd_update_back_buffer();
+ osd_lcd_draw();
+
+ rb->lcd_update_rect(xu, yu, wu, hu);
+ return true;
+}
+
+/* Call periodically to have the OSD timeout and hide itself */
+void osd_monitor_timeout(void)
+{
+ if (osd.status <= OSD_HIDDEN)
+ return; /* Already hidden/disabled */
+
+ if (osd.timeout > 0 && TIME_AFTER(*rb->current_tick, osd.hide_tick))
+ osd_show(OSD_HIDE);
+}
+
+/* Set the OSD timeout value. <= 0 = never timeout */
+void osd_set_timeout(long timeout)
+{
+ if (osd.status == OSD_DISABLED)
+ return;
+
+ osd.timeout = timeout;
+ osd_monitor_timeout();
+}
+
+/* Use the OSD viewport context */
+struct viewport * osd_get_viewport(void)
+{
+ return &osd.vp;
+}
+
+/* Get the maximum dimensions calculated by osd_init() */
+void osd_get_max_dims(int *maxwidth, int *maxheight)
+{
+ if (maxwidth)
+ *maxwidth = osd.maxwidth;
+
+ if (maxheight)
+ *maxheight = osd.maxheight;
+}
+
+/* Is the OSD enabled? */
+bool osd_enabled(void)
+{
+ return osd.status != OSD_DISABLED;
+}
+
+
+/** LCD update substitutes **/
+
+/* Prepare LCD framebuffer for regular drawing */
+void osd_lcd_update_prepare(void)
+{
+ if (osd.status == OSD_VISIBLE)
+ {
+ osd.status = OSD_ERASED;
+ osd_lcd_erase();
+ }
+}
+
+/* Update the whole screen */
+void osd_lcd_update(void)
+{
+ if (osd.status == OSD_ERASED)
+ {
+ /* Save the screen image underneath and restore the OSD image */
+ osd.status = OSD_VISIBLE;
+ osd_lcd_update_back_buffer();
+ osd_lcd_draw();
+ }
+
+ rb->lcd_update();
+}
+
+/* Update a part of the screen */
+void osd_lcd_update_rect(int x, int y, int width, int height)
+{
+ if (osd.status == OSD_ERASED)
+ {
+ /* Save the screen image underneath and restore the OSD image */
+ osd.status = OSD_VISIBLE;
+ osd_lcd_update_back_buffer();
+ osd_lcd_draw();
+ }
+
+ rb->lcd_update_rect(x, y, width, height);
+}
diff --git a/apps/plugins/lib/osd.h b/apps/plugins/lib/osd.h
new file mode 100644
index 0000000..89441ae
--- /dev/null
+++ b/apps/plugins/lib/osd.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Floating on-screen display
+*
+* Copyright (C) 2012 Michael Sevakis
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#ifndef OSD_H
+#define OSD_H
+
+/* At this time: assumes use of the default viewport for normal drawing */
+
+/* Callback implemented by user. Paramters are OSD vp-relative coordinates */
+typedef void (* osd_draw_cb_fn_t)(int x, int y, int width, int height);
+
+/* Initialize the OSD, set its backbuffer, update callback and enable it if
+ * the call succeeded. */
+bool osd_init(void *backbuf, size_t backbuf_size,
+ osd_draw_cb_fn_t draw_cb);
+
+enum
+{
+ OSD_HIDE = 0x0, /* Hide from view */
+ OSD_SHOW = 0x1, /* Bring into view, updating if needed */
+ OSD_UPDATENOW = 0x2, /* Force update even if nothing changed */
+};
+
+/* Show/Hide the OSD on screen returning previous status */
+bool osd_show(unsigned flags);
+
+/* Redraw the entire OSD returning true if screen update occurred */
+bool osd_update(void);
+
+/* Redraw part of the OSD (OSD viewport-relative coordinates) returning true
+ if any screen update occurred */
+bool osd_update_rect(int x, int y, int width, int height);
+
+/* Set a new screen location and size (screen coordinates) */
+bool osd_update_pos(int x, int y, int width, int height);
+
+/* Call periodically to have the OSD timeout and hide itself */
+void osd_monitor_timeout(void);
+
+/* Set the OSD timeout value. 'timeout' <= 0 == never timeout */
+void osd_set_timeout(long timeout);
+
+/* Use the OSD viewport context */
+struct viewport * osd_get_viewport(void);
+
+/* Get the maximum buffer dimensions calculated by osd_init() */
+void osd_get_max_dims(int *maxwidth, int *maxheight);
+
+/* Is the OSD enabled? */
+bool osd_enabled(void);
+
+/** Functions that must be used in lieu of regular LCD functions for this
+ ** to work.
+ **
+ ** To be efficient, as much drawing as possible should be combined between
+ ** *prepare and *update.
+ **
+ ** osd_lcd_update_prepare();
+ ** <draw stuff using lcd_* routines>
+ ** osd_lcd_update[_rect]();
+ **
+ ** TODO: Make it work seamlessly with greylib and regular LCD functions.
+ **/
+
+/* Prepare LCD frambuffer for regular drawing - call before any other LCD
+ function */
+void osd_lcd_update_prepare(void);
+
+/* Update the whole screen and restore OSD if it is visible */
+void osd_lcd_update(void);
+
+/* Update a part of the screen and restore OSD if it is visible */
+void osd_lcd_update_rect(int x, int y, int width, int height);
+
+#endif /* OSD_H */