summaryrefslogtreecommitdiff
path: root/apps/plugins/lib/osd.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lib/osd.c')
-rw-r--r--apps/plugins/lib/osd.c470
1 files changed, 470 insertions, 0 deletions
diff --git a/apps/plugins/lib/osd.c b/apps/plugins/lib/osd.c
new file mode 100644
index 0000000000..ff0533a898
--- /dev/null
+++ b/apps/plugins/lib/osd.c
@@ -0,0 +1,470 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Floating on-screen display
11*
12* Copyright (C) 2012 Michael Sevakis
13*
14* This program is free software; you can redistribute it and/or
15* modify it under the terms of the GNU General Public License
16* as published by the Free Software Foundation; either version 2
17* of the License, or (at your option) any later version.
18*
19* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20* KIND, either express or implied.
21*
22****************************************************************************/
23#include "plugin.h"
24#include "osd.h"
25
26#if 1
27#undef DEBUGF
28#define DEBUGF(...)
29#endif
30
31/* At this time: assumes use of the default viewport for normal drawing */
32
33/* If multiple OSD's are wanted, could convert to caller-allocated */
34static struct osd
35{
36 enum osd_status
37 {
38 OSD_DISABLED = 0, /* Disabled entirely */
39 OSD_HIDDEN, /* Hidden from view */
40 OSD_VISIBLE, /* Visible on screen */
41 OSD_ERASED, /* Erased in preparation for regular drawing */
42 } status; /* View status */
43 struct viewport vp; /* Clipping viewport */
44 struct bitmap lcd_bitmap; /* The main LCD fb bitmap */
45 struct bitmap back_bitmap; /* The OSD backbuffer fb bitmap */
46 int maxwidth; /* How wide may it be at most? */
47 int maxheight; /* How high may it be at most? */
48 long timeout; /* Current popup stay duration */
49 long hide_tick; /* Tick when it should be hidden */
50 osd_draw_cb_fn_t draw_cb; /* Draw update callback */
51} osd;
52
53/* Framebuffer allocation macros */
54#if LCD_DEPTH == 1
55# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
56# define LCD_WIDTH2BYTES(w) (((w)+7)/8)
57# define LCD_BYTES2WIDTH(b) ((b)*8)
58# elif LCD_PIXELFORMAT == VERTICAL_PACKING
59# define LCD_HEIGHT2BYTES(h) (((h)+7)/8)
60# define LCD_BYTES2HEIGHT(b) ((b)*8)
61# else
62# error Unknown 1-bit format; please define macros
63# endif /* LCD_PIXELFORMAT */
64#elif LCD_DEPTH == 2
65# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
66# define LCD_WIDTH2BYTES(w) (((w)+3)/4)
67# define LCD_BYTES2WIDTH(b) ((b)*4)
68# elif LCD_PIXELFORMAT == VERTICAL_PACKING
69# define LCD_HEIGHT2BYTES(h) (((h)+3)/4)
70# define LCD_BYTES2HEIGHT(b) ((b)*4)
71# elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
72# define LCD_WIDTH2BYTES(w) ((w)*2)
73# define LCD_BYTES2WIDTH(b) ((b)/2)
74# define LCD_HEIGHT2BYTES(h) (((h)+7)/8*2)
75# define LCD_BYTES2HEIGHT(b) ((b)/2*8)
76# else
77# error Unknown 2-bit format; please define macros
78# endif /* LCD_PIXELFORMAT */
79#elif LCD_DEPTH == 16
80# define LCD_WIDTH2BYTES(w) ((w)*2)
81# define LCD_BYTES2WIDTH(b) ((b)/2)
82#else
83# error Unknown LCD depth; please define macros
84#endif /* LCD_DEPTH */
85/* Set defaults if not defined different yet. */
86#ifndef LCD_WIDTH2BYTES
87# define LCD_WIDTH2BYTES(w) (w)
88#endif
89#ifndef LCD_BYTES2WIDTH
90# define LCD_BYTES2WIDTH(b) (b)
91#endif
92#ifndef LCD_HEIGHT2BYTES
93# define LCD_HEIGHT2BYTES(h) (h)
94#endif
95#ifndef LCD_BYTES2HEIGHT
96# define LCD_BYTES2HEIGHT(b) (b)
97#endif
98
99/* Create a bitmap framebuffer from a buffer */
100static fb_data * buf_to_fb_bitmap(void *buf, size_t bufsize,
101 int *width, int *height)
102{
103 /* Used as dest, the LCD functions cannot deal with alternate
104 strides as of now - the stride guides the calulations. If
105 that is no longer the case, then width or height can be
106 used instead (and less memory needed for a small surface!).
107 */
108 DEBUGF("buf: %p bufsize: %lu\n", buf, (unsigned long)bufsize);
109
110#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
111 int h = LCD_BYTES2HEIGHT(LCD_HEIGHT2BYTES(LCD_HEIGHT));
112 int w = bufsize / LCD_HEIGHT2BYTES(h);
113
114 if (w == 0)
115 {
116 DEBUGF("OSD: not enough buffer\n");
117 return NULL; /* not enough buffer */
118 }
119#else
120 int w = LCD_BYTES2WIDTH(LCD_WIDTH2BYTES(LCD_WIDTH));
121 int h = bufsize / LCD_WIDTH2BYTES(w);
122
123 if (h == 0)
124 {
125 DEBUGF("OSD: not enough buffer\n");
126 return NULL; /* not enough buffer */
127 }
128#endif
129
130 DEBUGF("fbw:%d fbh:%d\n", w, h);
131
132 *width = w;
133 *height = h;
134
135 return (fb_data *)buf;
136}
137
138static inline void osd_set_vp_pos(int x, int y, int width, int height)
139{
140 osd.vp.x = x;
141 osd.vp.y = y;
142 osd.vp.width = width;
143 osd.vp.height = height;
144}
145
146/* Sync the backbuffer to the on-screen image */
147static void osd_lcd_update_back_buffer(void)
148{
149 rb->lcd_set_framebuffer((fb_data *)osd.back_bitmap.data);
150 rb->lcd_bmp_part(&osd.lcd_bitmap, osd.vp.x, osd.vp.y,
151 0, 0, osd.vp.width, osd.vp.height);
152 /* Assume it was on default framebuffer for now */
153 rb->lcd_set_framebuffer(NULL);
154}
155
156/* Erase the OSD to restore the framebuffer */
157static void osd_lcd_erase(void)
158{
159 rb->lcd_bmp_part(&osd.back_bitmap, 0, 0, osd.vp.x, osd.vp.y,
160 osd.vp.width, osd.vp.height);
161}
162
163/* Draw the OSD image portion using the callback */
164static void osd_lcd_draw_rect(int x, int y, int width, int height)
165{
166 rb->lcd_set_viewport(&osd.vp);
167 osd.draw_cb(x, y, width, height);
168 rb->lcd_set_viewport(NULL);
169}
170
171/* Draw the OSD image using the callback */
172static void osd_lcd_draw(void)
173{
174 osd_lcd_draw_rect(0, 0, osd.vp.width, osd.vp.height);
175}
176
177
178/** Public APIs **/
179
180/* Initialized the OSD and set its backbuffer */
181bool osd_init(void *backbuf, size_t backbuf_size,
182 osd_draw_cb_fn_t draw_cb)
183{
184 osd_show(OSD_HIDE);
185
186 osd.status = OSD_DISABLED; /* Disabled unless all is okay */
187 osd_set_vp_pos(0, 0, 0, 0);
188 osd.maxwidth = osd.maxheight = 0;
189 osd.timeout = 0;
190
191 if (!draw_cb)
192 return false;
193
194 if (!backbuf)
195 return false;
196
197 ALIGN_BUFFER(backbuf, backbuf_size, FB_DATA_SZ);
198
199 if (!backbuf_size)
200 return false;
201
202 rb->viewport_set_fullscreen(&osd.vp, SCREEN_MAIN);
203
204 fb_data *backfb = buf_to_fb_bitmap(backbuf, backbuf_size,
205 &osd.maxwidth, &osd.maxheight);
206
207 if (!backfb)
208 return false;
209
210 osd.draw_cb = draw_cb;
211
212 /* LCD framebuffer bitmap */
213 osd.lcd_bitmap.width = LCD_BYTES2WIDTH(LCD_WIDTH2BYTES(LCD_WIDTH));
214 osd.lcd_bitmap.height = LCD_BYTES2HEIGHT(LCD_HEIGHT2BYTES(LCD_HEIGHT));
215#if LCD_DEPTH > 1
216 osd.lcd_bitmap.format = FORMAT_NATIVE;
217 osd.lcd_bitmap.maskdata = NULL;
218#endif
219#ifdef HAVE_LCD_COLOR
220 osd.lcd_bitmap.alpha_offset = 0;
221#endif
222 osd.lcd_bitmap.data = (void *)rb->lcd_framebuffer;
223
224 /* Backbuffer bitmap */
225 osd.back_bitmap.width = osd.maxwidth;
226 osd.back_bitmap.height = osd.maxheight;
227#if LCD_DEPTH > 1
228 osd.back_bitmap.format = FORMAT_NATIVE;
229 osd.back_bitmap.maskdata = NULL;
230#endif
231#ifdef HAVE_LCD_COLOR
232 osd.back_bitmap.alpha_offset = 0;
233#endif
234 osd.back_bitmap.data = (void *)backfb;
235
236 DEBUGF("FB:%p BB:%p\n", osd.lcd_bitmap.data, osd.back_bitmap.data);
237
238 /* Set the default position to the whole thing */
239 osd_set_vp_pos(0, 0, osd.maxwidth, osd.maxheight);
240
241 osd.status = OSD_HIDDEN; /* Ready when you are */
242 return true;
243}
244
245/* Show/Hide the OSD on screen */
246bool osd_show(unsigned flags)
247{
248 if (flags & OSD_SHOW)
249 {
250 switch (osd.status)
251 {
252 case OSD_DISABLED:
253 break; /* No change */
254
255 case OSD_HIDDEN:
256 osd_lcd_update_back_buffer();
257 osd.status = OSD_VISIBLE;
258 osd_update();
259 osd.hide_tick = *rb->current_tick + osd.timeout;
260 break;
261
262 case OSD_VISIBLE:
263 if (flags & OSD_UPDATENOW)
264 osd_update();
265 /* Fall-through */
266 case OSD_ERASED:
267 osd.hide_tick = *rb->current_tick + osd.timeout;
268 return true;
269 }
270 }
271 else
272 {
273 switch (osd.status)
274 {
275 case OSD_DISABLED:
276 case OSD_HIDDEN:
277 break;
278
279 case OSD_VISIBLE:
280 osd_lcd_erase();
281 rb->lcd_update_rect(osd.vp.x, osd.vp.y, osd.vp.width,
282 osd.vp.height);
283 /* Fall-through */
284 case OSD_ERASED:
285 osd.status = OSD_HIDDEN;
286 return true;
287 }
288 }
289
290 return false;
291}
292
293/* Redraw the entire OSD */
294bool osd_update(void)
295{
296 if (osd.status != OSD_VISIBLE)
297 return false;
298
299 osd_lcd_draw();
300
301 rb->lcd_update_rect(osd.vp.x, osd.vp.y, osd.vp.width,
302 osd.vp.height);
303
304 return true;
305}
306
307/* Redraw part of the OSD (viewport-relative coordinates) */
308bool osd_update_rect(int x, int y, int width, int height)
309{
310 if (osd.status != OSD_VISIBLE)
311 return false;
312
313 osd_lcd_draw_rect(x, y, width, height);
314
315 if (x + width > osd.vp.width)
316 width = osd.vp.width - x;
317
318 if (x < 0)
319 {
320 width += x;
321 x = 0;
322 }
323
324 if (width <= 0)
325 return false;
326
327 if (y + height > osd.vp.height)
328 height = osd.vp.height - y;
329
330 if (y < 0)
331 {
332 height += y;
333 y = 0;
334 }
335
336 if (height <= 0)
337 return false;
338
339 rb->lcd_update_rect(osd.vp.x + x, osd.vp.y + y, width, height);
340
341 return true;
342}
343
344/* Set a new screen location and size (screen coordinates) */
345bool osd_update_pos(int x, int y, int width, int height)
346{
347 if (osd.status == OSD_DISABLED)
348 return false;
349
350 if (width < 0)
351 width = 0;
352 else if (width > osd.maxwidth)
353 width = osd.maxwidth;
354
355 if (height < 0)
356 height = 0;
357 else if (height > osd.maxheight)
358 height = osd.maxheight;
359
360 if (x == osd.vp.x && y == osd.vp.y &&
361 width == osd.vp.width && height == osd.vp.height)
362 return false; /* No change */
363
364 if (osd.status != OSD_VISIBLE)
365 {
366 /* Not visible - just update pos */
367 osd_set_vp_pos(x, y, width, height);
368 return false;
369 }
370
371 /* Visible area has changed */
372 osd_lcd_erase();
373
374 /* Update the smallest rectangle that encloses both the old and new
375 regions to make the change free of flicker (they may overlap) */
376 int xu = MIN(osd.vp.x, x);
377 int yu = MIN(osd.vp.y, y);
378 int wu = MAX(osd.vp.x + osd.vp.width, x + width) - xu;
379 int hu = MAX(osd.vp.y + osd.vp.height, y + height) - yu;
380
381 osd_set_vp_pos(x, y, width, height);
382 osd_lcd_update_back_buffer();
383 osd_lcd_draw();
384
385 rb->lcd_update_rect(xu, yu, wu, hu);
386 return true;
387}
388
389/* Call periodically to have the OSD timeout and hide itself */
390void osd_monitor_timeout(void)
391{
392 if (osd.status <= OSD_HIDDEN)
393 return; /* Already hidden/disabled */
394
395 if (osd.timeout > 0 && TIME_AFTER(*rb->current_tick, osd.hide_tick))
396 osd_show(OSD_HIDE);
397}
398
399/* Set the OSD timeout value. <= 0 = never timeout */
400void osd_set_timeout(long timeout)
401{
402 if (osd.status == OSD_DISABLED)
403 return;
404
405 osd.timeout = timeout;
406 osd_monitor_timeout();
407}
408
409/* Use the OSD viewport context */
410struct viewport * osd_get_viewport(void)
411{
412 return &osd.vp;
413}
414
415/* Get the maximum dimensions calculated by osd_init() */
416void osd_get_max_dims(int *maxwidth, int *maxheight)
417{
418 if (maxwidth)
419 *maxwidth = osd.maxwidth;
420
421 if (maxheight)
422 *maxheight = osd.maxheight;
423}
424
425/* Is the OSD enabled? */
426bool osd_enabled(void)
427{
428 return osd.status != OSD_DISABLED;
429}
430
431
432/** LCD update substitutes **/
433
434/* Prepare LCD framebuffer for regular drawing */
435void osd_lcd_update_prepare(void)
436{
437 if (osd.status == OSD_VISIBLE)
438 {
439 osd.status = OSD_ERASED;
440 osd_lcd_erase();
441 }
442}
443
444/* Update the whole screen */
445void osd_lcd_update(void)
446{
447 if (osd.status == OSD_ERASED)
448 {
449 /* Save the screen image underneath and restore the OSD image */
450 osd.status = OSD_VISIBLE;
451 osd_lcd_update_back_buffer();
452 osd_lcd_draw();
453 }
454
455 rb->lcd_update();
456}
457
458/* Update a part of the screen */
459void osd_lcd_update_rect(int x, int y, int width, int height)
460{
461 if (osd.status == OSD_ERASED)
462 {
463 /* Save the screen image underneath and restore the OSD image */
464 osd.status = OSD_VISIBLE;
465 osd_lcd_update_back_buffer();
466 osd_lcd_draw();
467 }
468
469 rb->lcd_update_rect(x, y, width, height);
470}