summaryrefslogtreecommitdiff
path: root/apps/plugins/lib/osd.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-12-02 01:09:44 -0500
committerMichael Sevakis <jethead71@rockbox.org>2012-12-05 02:03:58 +0100
commit0f8aedbe9492a5226fddd4ad27dae21b8a39e1b4 (patch)
treefa337438065d277926a254af74d3843b060b37eb /apps/plugins/lib/osd.c
parente1ea08417bca57c607df6686472fa0a86f38b6d1 (diff)
downloadrockbox-0f8aedbe9492a5226fddd4ad27dae21b8a39e1b4.tar.gz
rockbox-0f8aedbe9492a5226fddd4ad27dae21b8a39e1b4.zip
Add a true waveform display to the oscilloscope plugin.
* Adds some additional niftyness like a floating popup display that is implemented in an OSD library for use by other plugins. * Speed changes are now gradual for both views and follow a curve derived from some fiddling around to get a nice feel. * Refined a few behavioral things overall. It needs a bit of help from a direct PCM channel callback so it may capture PCM for waveform display. Also need a few other core routines to help out for the OSD. Messes with some keymaps. Some targets need keymaps to access the different views. Some devices can't support the additional view because it requires a large buffer ( > 1 s) for samples. If the plugin buffer is small, they can still use the popup display since the plugin is also much smaller in that case. Slow speed waveform needs some refining so it draws gradually like a real oscilloscope but I'll stick with what it is, for the moment. Change-Id: Ieb5b7922a2238264e9b19a58cb437739194eb036 Reviewed-on: http://gerrit.rockbox.org/245 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested-by: Michael Sevakis <jethead71@rockbox.org>
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}