f373c97c90
Instead of putting "#ifdef LCD_STRIDEFORMAT" at every usage of the macro it's simpler to have config.h define LCD_STRIDEFORMAT to the default of horizontal stride when the target leaves it unspecified. Change-Id: Ib187012aad65ac678dbd837b1464a83bad722411
879 lines
24 KiB
C
879 lines
24 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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 "grey.h"
|
|
#include "osd.h"
|
|
|
|
#if 1
|
|
#undef DEBUGF
|
|
#define DEBUGF(...)
|
|
#endif
|
|
|
|
#if defined(SIMULATOR) && LCD_DEPTH < 4
|
|
/* Sim isn't using --ffunction-sections thus greylib references will happen
|
|
here even if not using this with greylib on a grayscale display, which
|
|
demands that a struct _grey_info exist. */
|
|
#ifndef _WIN32
|
|
__attribute__((weak))
|
|
#endif /* _WIN32 */
|
|
struct _grey_info _grey_info;
|
|
#endif /* defined(SIMULATOR) && LCD_DEPTH < 4 */
|
|
|
|
/* At this time: assumes use of the default viewport for normal drawing */
|
|
|
|
/* If multiple OSD's are wanted, could convert to caller-allocated */
|
|
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 frame_buffer_t framebuf; /* Holds framebuffer reference */
|
|
int lcd_bitmap_stride; /* Stride of LCD bitmap */
|
|
void *lcd_bitmap_data; /* Backbuffer framebuffer data */
|
|
int back_bitmap_stride; /* Stride of backbuffer bitmap */
|
|
void *back_bitmap_data; /* LCD framebuffer data */
|
|
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 */
|
|
/* Functions to factilitate interface compatibility of OSD types */
|
|
void * (*init_buffers)(struct osd *osd, unsigned flags, void *buf,
|
|
size_t *bufsize);
|
|
void (*set_viewport_pos)(struct viewport *vp, int x, int y, int width,
|
|
int height);
|
|
void (*lcd_update)(void);
|
|
void (*lcd_update_rect)(int x, int y, int width, int height);
|
|
struct viewport *(*lcd_set_viewport)(struct viewport *vp);
|
|
void (*lcd_set_framebuffer)(void *buf);
|
|
void (*lcd_framebuffer_set_pos)(int x, int y, int width, int height);
|
|
void (*lcd_bitmap_part)(const void *src, int src_x, int src_y,
|
|
int stride, int x, int y, int width, int height);
|
|
};
|
|
|
|
static struct osd native_osd;
|
|
#if LCD_DEPTH < 4
|
|
static struct osd grey_osd;
|
|
#endif
|
|
|
|
/* Framebuffer allocation macros */
|
|
#if LCD_DEPTH == 1
|
|
# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
|
|
# define _OSD_WIDTH2BYTES(w) (((w)+7)/8)
|
|
# define _OSD_BYTES2WIDTH(b) ((b)*8)
|
|
# elif LCD_PIXELFORMAT == VERTICAL_PACKING
|
|
# define _OSD_HEIGHT2BYTES(h) (((h)+7)/8)
|
|
# define _OSD_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 _OSD_WIDTH2BYTES(w) (((w)+3)/4)
|
|
# define _OSD_BYTES2WIDTH(b) ((b)*4)
|
|
# elif LCD_PIXELFORMAT == VERTICAL_PACKING
|
|
# define _OSD_HEIGHT2BYTES(h) (((h)+3)/4)
|
|
# define _OSD_BYTES2HEIGHT(b) ((b)*4)
|
|
# elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
|
|
# define _OSD_HEIGHT2BYTES(h) (((h)+7)/8*2)
|
|
# define _OSD_BYTES2HEIGHT(b) ((b)/2*8)
|
|
# else
|
|
# error Unknown 2-bit format; please define macros
|
|
# endif /* LCD_PIXELFORMAT */
|
|
#elif LCD_DEPTH == 16
|
|
# if LCD_STRIDEFORMAT == VERTICAL_STRIDE
|
|
# define _OSD_HEIGHT2BYTES(h) ((h)*2)
|
|
# define _OSD_BYTES2HEIGHT(b) ((b)/2)
|
|
# else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
|
|
# define _OSD_WIDTH2BYTES(w) ((w)*2)
|
|
# define _OSD_BYTES2WIDTH(b) ((b)/2)
|
|
# endif /* end stride type selection */
|
|
#elif LCD_DEPTH == 24
|
|
# define _OSD_WIDTH2BYTES(w) ((w)*3)
|
|
# define _OSD_BYTES2WIDTH(b) ((b)/3)
|
|
#elif LCD_DEPTH == 32
|
|
# define _OSD_WIDTH2BYTES(w) ((w)*4)
|
|
# define _OSD_BYTES2WIDTH(b) ((b)/4)
|
|
#else /* other LCD depth */
|
|
# error Unknown LCD depth; please define macros
|
|
#endif /* LCD_DEPTH */
|
|
/* Set defaults if not defined differently */
|
|
#ifndef _OSD_WIDTH2BYTES
|
|
# define _OSD_WIDTH2BYTES(w) (w)
|
|
#endif
|
|
#ifndef _OSD_BYTES2WIDTH
|
|
# define _OSD_BYTES2WIDTH(b) (b)
|
|
#endif
|
|
#ifndef _OSD_HEIGHT2BYTES
|
|
# define _OSD_HEIGHT2BYTES(h) (h)
|
|
#endif
|
|
#ifndef _OSD_BYTES2HEIGHT
|
|
# define _OSD_BYTES2HEIGHT(b) (b)
|
|
#endif
|
|
#ifndef _OSD_BUFSIZE
|
|
# define _OSD_BUFSIZE(w, h) (_OSD_WIDTH2BYTES(w)*_OSD_HEIGHT2BYTES(h))
|
|
#endif
|
|
|
|
static void _osd_destroy(struct osd *osd);
|
|
static bool _osd_show(struct osd *osd, unsigned flags);
|
|
|
|
|
|
/** Native LCD routines **/
|
|
|
|
/* Create a bitmap framebuffer from a buffer */
|
|
static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
|
|
void *buf, size_t *bufsize)
|
|
{
|
|
/* 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!).
|
|
IOW: crappiness means one dimension is non-negotiable.
|
|
*/
|
|
DEBUGF("OSD: in(buf=%p bufsize=%lu)\n", buf,
|
|
(unsigned long)*bufsize);
|
|
|
|
rb->viewport_set_fullscreen(&osd->vp, SCREEN_MAIN);
|
|
|
|
#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
|
|
int colbytes = _OSD_HEIGHT2BYTES(LCD_HEIGHT);
|
|
int bytecols = *bufsize / colbytes;
|
|
int w = _OSD_BYTES2WIDTH(bytecols);
|
|
int h = _OSD_BYTES2HEIGHT(colbytes);
|
|
|
|
if (flags & OSD_INIT_MAJOR_HEIGHT)
|
|
{
|
|
if (w == 0 || ((flags & OSD_INIT_MINOR_MIN) && w < osd->maxwidth))
|
|
{
|
|
DEBUGF("OSD: not enough buffer\n");
|
|
return NULL; /* not enough buffer */
|
|
}
|
|
|
|
if ((flags & OSD_INIT_MINOR_MAX) && w > osd->maxwidth)
|
|
w = osd->maxwidth;
|
|
}
|
|
else /* OSD_INIT_MAJOR_WIDTH implied */
|
|
{
|
|
if (w == 0 || w < osd->maxwidth)
|
|
{
|
|
DEBUGF("OSD: not enough buffer\n");
|
|
return NULL; /* not enough buffer */
|
|
}
|
|
else if (w > osd->maxwidth)
|
|
{
|
|
w = osd->maxwidth;
|
|
}
|
|
}
|
|
|
|
w = _OSD_BYTES2WIDTH(_OSD_WIDTH2BYTES(w));
|
|
osd->lcd_bitmap_stride = _OSD_BYTES2HEIGHT(_OSD_HEIGHT2BYTES(LCD_HEIGHT));
|
|
osd->back_bitmap_stride = h;
|
|
#else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
|
|
int rowbytes = _OSD_WIDTH2BYTES(LCD_WIDTH);
|
|
int byterows = *bufsize / rowbytes;
|
|
int w = _OSD_BYTES2WIDTH(rowbytes);
|
|
int h = _OSD_BYTES2HEIGHT(byterows);
|
|
|
|
if (flags & OSD_INIT_MAJOR_HEIGHT)
|
|
{
|
|
if (h == 0 || h < osd->maxheight)
|
|
{
|
|
DEBUGF("OSD: not enough buffer\n");
|
|
return NULL;
|
|
}
|
|
else if (h > osd->maxheight)
|
|
{
|
|
h = osd->maxheight;
|
|
}
|
|
}
|
|
else /* OSD_INIT_MAJOR_WIDTH implied */
|
|
{
|
|
if (h == 0 || ((flags & OSD_INIT_MINOR_MIN) && h < osd->maxheight))
|
|
{
|
|
DEBUGF("OSD: not enough buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
if ((flags & OSD_INIT_MINOR_MAX) && h > osd->maxheight)
|
|
h = osd->maxheight;
|
|
}
|
|
|
|
h = _OSD_BYTES2HEIGHT(_OSD_HEIGHT2BYTES(h));
|
|
osd->lcd_bitmap_stride = _OSD_BYTES2WIDTH(_OSD_WIDTH2BYTES(LCD_WIDTH));
|
|
osd->back_bitmap_stride = w;
|
|
#endif /* end stride type selection */
|
|
|
|
/* vp is currently initialized to the default framebuffer */
|
|
osd->lcd_bitmap_data = osd->vp.buffer->data;
|
|
osd->back_bitmap_data = buf;
|
|
|
|
osd->maxwidth = w;
|
|
osd->maxheight = h;
|
|
*bufsize = _OSD_BUFSIZE(w, h);
|
|
|
|
DEBUGF("OSD: addr(fb=%p bb=%p)\n", osd->lcd_bitmap_data,
|
|
osd->back_bitmap_data);
|
|
DEBUGF("OSD: w=%d h=%d bufsz=%lu\n", w, h, (unsigned long)*bufsize);
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* Set viewport coordinates */
|
|
static void _osd_lcd_viewport_set_pos(
|
|
struct viewport *vp, int x, int y, int width, int height)
|
|
{
|
|
vp->x = x;
|
|
vp->y = y;
|
|
vp->width = width;
|
|
vp->height = height;
|
|
}
|
|
|
|
|
|
#if LCD_DEPTH < 4
|
|
/** Greylib LCD routines **/
|
|
|
|
/* Create a greylib bitmap framebuffer from a buffer */
|
|
static void * _osd_grey_init_buffers(struct osd *osd, unsigned flags,
|
|
void *buf, size_t *bufsize)
|
|
{
|
|
int w, h;
|
|
|
|
DEBUGF("OSD (grey): in(buf=%p bufsize=%lu)\n", buf,
|
|
(unsigned long)*bufsize);
|
|
|
|
grey_viewport_set_fullscreen(&osd->vp, SCREEN_MAIN);
|
|
|
|
if (flags & OSD_INIT_MAJOR_HEIGHT)
|
|
{
|
|
h = osd->maxheight;
|
|
w = *bufsize / h;
|
|
|
|
if (w == 0 || ((flags & OSD_INIT_MINOR_MIN) && w < osd->maxwidth))
|
|
{
|
|
DEBUGF("OSD (grey): Not enough buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
if ((flags & OSD_INIT_MINOR_MAX) && w > osd->maxwidth)
|
|
w = osd->maxwidth;
|
|
}
|
|
else /* OSD_INIT_MAJOR_WIDTH implied */
|
|
{
|
|
w = osd->maxwidth;
|
|
h = *bufsize / w;
|
|
|
|
if (h == 0 || ((flags & OSD_INIT_MINOR_MIN) && h < osd->maxheight))
|
|
{
|
|
DEBUGF("OSD (grey): Not enough buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
if ((flags & OSD_INIT_MINOR_MAX) && h > osd->maxheight)
|
|
h = osd->maxheight;
|
|
}
|
|
|
|
/* Have to peek into _grey_info a bit */
|
|
osd->lcd_bitmap_stride = _grey_info.width;
|
|
osd->lcd_bitmap_data = _grey_info.buffer;
|
|
osd->back_bitmap_stride = w;
|
|
osd->back_bitmap_data = buf;
|
|
|
|
osd->maxwidth = w;
|
|
osd->maxheight = h;
|
|
*bufsize = w * h;
|
|
|
|
DEBUGF("OSD (grey): addr(fb=%p bb=%p)\n", osd->lcd_bitmap_data,
|
|
osd->back_bitmap_data);
|
|
DEBUGF("OSD (grey): w=%d h=%d bufsz=%lu\n", w, h, (unsigned long)*bufsize);
|
|
|
|
return buf;
|
|
}
|
|
#endif /* LCD_DEPTH < 4*/
|
|
|
|
|
|
/** Common LCD routines **/
|
|
|
|
/* Draw the OSD image portion using the callback */
|
|
static void _osd_draw_osd_rect(struct osd *osd, int x, int y,
|
|
int width, int height)
|
|
{
|
|
osd->lcd_set_viewport(&osd->vp);
|
|
osd->draw_cb(x, y, width, height);
|
|
osd->lcd_set_viewport(NULL);
|
|
}
|
|
|
|
/* Draw the OSD image using the callback */
|
|
static void _osd_draw_osd(struct osd *osd)
|
|
{
|
|
_osd_draw_osd_rect(osd, 0, 0, osd->vp.width, osd->vp.height);
|
|
}
|
|
|
|
static void _osd_update_viewport(struct osd *osd)
|
|
{
|
|
osd->lcd_update_rect(osd->vp.x, osd->vp.y, osd->vp.width,
|
|
osd->vp.height);
|
|
}
|
|
|
|
/* Sync the backbuffer to the framebuffer image */
|
|
static void _osd_update_back_buffer(struct osd *osd)
|
|
{
|
|
/* Assume it's starting with default viewport for now */
|
|
osd->lcd_set_framebuffer(osd->back_bitmap_data);
|
|
#if LCD_DEPTH < 4
|
|
if (osd->lcd_framebuffer_set_pos)
|
|
osd->lcd_framebuffer_set_pos(0, 0, osd->maxwidth, osd->maxheight);
|
|
#endif /* LCD_DEPTH < 4 */
|
|
osd->lcd_bitmap_part(osd->lcd_bitmap_data, osd->vp.x, osd->vp.y,
|
|
osd->lcd_bitmap_stride, 0, 0, osd->vp.width,
|
|
osd->vp.height);
|
|
/* Assume it was on default framebuffer for now */
|
|
osd->lcd_set_framebuffer(NULL);
|
|
}
|
|
|
|
/* Erase the OSD to restore the framebuffer image */
|
|
static void _osd_erase_osd(struct osd *osd)
|
|
{
|
|
osd->lcd_bitmap_part(osd->back_bitmap_data, 0, 0, osd->back_bitmap_stride,
|
|
osd->vp.x, osd->vp.y, osd->vp.width, osd->vp.height);
|
|
}
|
|
|
|
/* Initialized the OSD and set its backbuffer */
|
|
static bool _osd_init(struct osd *osd, unsigned flags, void *backbuf,
|
|
size_t backbuf_size, osd_draw_cb_fn_t draw_cb,
|
|
int *width, int *height, size_t *bufused)
|
|
{
|
|
_osd_destroy(osd);
|
|
|
|
if (!draw_cb)
|
|
return false;
|
|
|
|
if (!backbuf)
|
|
return false;
|
|
|
|
void *backbuf_orig = backbuf; /* Save in case of ptr advance */
|
|
ALIGN_BUFFER(backbuf, backbuf_size, MAX(FB_DATA_SZ, 4));
|
|
|
|
if (!backbuf_size)
|
|
return false;
|
|
|
|
if (flags & OSD_INIT_MAJOR_HEIGHT)
|
|
{
|
|
if (!height || *height <= 0)
|
|
return false;
|
|
|
|
if ((flags & (OSD_INIT_MINOR_MIN | OSD_INIT_MINOR_MAX)) &&
|
|
(!width || *width <= 0))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!width || *width <= 0)
|
|
return false;
|
|
|
|
if ((flags & (OSD_INIT_MINOR_MIN | OSD_INIT_MINOR_MAX)) &&
|
|
(!height || *height <= 0))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Store requested sizes in max(width|height) */
|
|
if (width)
|
|
osd->maxwidth = *width;
|
|
else
|
|
osd->maxwidth = LCD_WIDTH;
|
|
|
|
if (height)
|
|
osd->maxheight = *height;
|
|
else
|
|
osd->maxheight = LCD_HEIGHT;
|
|
|
|
if (!osd->init_buffers(osd, flags, backbuf, &backbuf_size))
|
|
{
|
|
osd->maxwidth = osd->maxheight = 0;
|
|
return false;
|
|
}
|
|
|
|
osd->draw_cb = draw_cb;
|
|
|
|
if (bufused)
|
|
*bufused = backbuf_size + (backbuf_orig - backbuf);
|
|
|
|
if (width)
|
|
*width = osd->maxwidth;
|
|
|
|
if (height)
|
|
*height = osd->maxheight;
|
|
|
|
/* Set the default position to the whole thing */
|
|
osd->set_viewport_pos(&osd->vp, 0, 0, osd->maxwidth, osd->maxheight);
|
|
|
|
osd->status = OSD_HIDDEN; /* Ready when you are */
|
|
|
|
return true;
|
|
}
|
|
|
|
static void _osd_destroy(struct osd *osd)
|
|
{
|
|
_osd_show(osd, OSD_HIDE);
|
|
|
|
/* Set to essential defaults */
|
|
osd->status = OSD_DISABLED;
|
|
osd->set_viewport_pos(&osd->vp, 0, 0, 0, 0);
|
|
osd->maxwidth = osd->maxheight = 0;
|
|
osd->timeout = 0;
|
|
}
|
|
|
|
/* Redraw the entire OSD */
|
|
static bool _osd_update(struct osd *osd)
|
|
{
|
|
if (osd->status != OSD_VISIBLE)
|
|
return false;
|
|
|
|
_osd_draw_osd(osd);
|
|
_osd_update_viewport(osd);
|
|
return true;
|
|
}
|
|
|
|
/* Show/Hide the OSD on screen */
|
|
static bool _osd_show(struct osd *osd, unsigned flags)
|
|
{
|
|
if (flags & OSD_SHOW)
|
|
{
|
|
switch (osd->status)
|
|
{
|
|
case OSD_DISABLED:
|
|
break; /* No change */
|
|
|
|
case OSD_HIDDEN:
|
|
_osd_update_back_buffer(osd);
|
|
osd->status = OSD_VISIBLE;
|
|
_osd_update(osd);
|
|
osd->hide_tick = *rb->current_tick + osd->timeout;
|
|
break;
|
|
|
|
case OSD_VISIBLE:
|
|
if (flags & OSD_UPDATENOW)
|
|
_osd_update(osd);
|
|
/* 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_erase_osd(osd);
|
|
_osd_update_viewport(osd);
|
|
/* Fall-through */
|
|
case OSD_ERASED:
|
|
osd->status = OSD_HIDDEN;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Redraw part of the OSD (viewport-relative coordinates) */
|
|
static bool _osd_update_rect(struct osd *osd, int x, int y, int width,
|
|
int height)
|
|
{
|
|
if (osd->status != OSD_VISIBLE)
|
|
return false;
|
|
|
|
_osd_draw_osd_rect(osd, x, y, width, height);
|
|
|
|
int vp_x = osd->vp.x;
|
|
int vp_w = osd->vp.width;
|
|
|
|
if (x + width > vp_w)
|
|
width = vp_w - x;
|
|
|
|
if (x < 0)
|
|
{
|
|
width += x;
|
|
x = 0;
|
|
}
|
|
|
|
if (width <= 0)
|
|
return false;
|
|
|
|
int vp_y = osd->vp.y;
|
|
int vp_h = osd->vp.height;
|
|
|
|
if (y + height > vp_h)
|
|
height = vp_h - y;
|
|
|
|
if (y < 0)
|
|
{
|
|
height += y;
|
|
y = 0;
|
|
}
|
|
|
|
if (height <= 0)
|
|
return false;
|
|
|
|
osd->lcd_update_rect(vp_x + x, vp_y + y, width, height);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Set a new screen location and size (screen coordinates) */
|
|
static bool _osd_update_pos(struct osd *osd, 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;
|
|
|
|
int vp_x = osd->vp.x;
|
|
int vp_y = osd->vp.y;
|
|
int vp_w = osd->vp.width;
|
|
int vp_h = osd->vp.height;
|
|
|
|
if (x == vp_x && y == vp_y && width == vp_w && height == vp_h)
|
|
return false; /* No change */
|
|
|
|
if (osd->status != OSD_VISIBLE)
|
|
{
|
|
/* Not visible - just update pos */
|
|
osd->set_viewport_pos(&osd->vp, x, y, width, height);
|
|
return false;
|
|
}
|
|
|
|
/* Visible area has changed */
|
|
_osd_erase_osd(osd);
|
|
|
|
/* 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(vp_x, x);
|
|
int yu = MIN(vp_y, y);
|
|
int wu = MAX(vp_x + vp_w, x + width) - xu;
|
|
int hu = MAX(vp_y + vp_h, y + height) - yu;
|
|
|
|
osd->set_viewport_pos(&osd->vp, x, y, width, height);
|
|
_osd_update_back_buffer(osd);
|
|
_osd_draw_osd(osd);
|
|
osd->lcd_update_rect(xu, yu, wu, hu);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Call periodically to have the OSD timeout and hide itself */
|
|
static void _osd_monitor_timeout(struct osd *osd)
|
|
{
|
|
if (osd->status <= OSD_HIDDEN)
|
|
return; /* Already hidden/disabled */
|
|
|
|
if (osd->timeout > 0 && TIME_AFTER(*rb->current_tick, osd->hide_tick))
|
|
_osd_show(osd, OSD_HIDE);
|
|
}
|
|
|
|
/* Set the OSD timeout value. <= 0 = never timeout */
|
|
static void _osd_set_timeout(struct osd *osd, long timeout)
|
|
{
|
|
if (osd->status == OSD_DISABLED)
|
|
return;
|
|
|
|
osd->timeout = timeout;
|
|
_osd_monitor_timeout(osd);
|
|
}
|
|
|
|
/* Use the OSD viewport context */
|
|
static inline struct viewport * _osd_get_viewport(struct osd *osd)
|
|
{
|
|
return &osd->vp;
|
|
}
|
|
|
|
/* Get the maximum dimensions calculated by osd_init() */
|
|
static void _osd_get_max_dims(struct osd *osd,
|
|
int *maxwidth, int *maxheight)
|
|
{
|
|
if (maxwidth)
|
|
*maxwidth = osd->maxwidth;
|
|
|
|
if (maxheight)
|
|
*maxheight = osd->maxheight;
|
|
}
|
|
|
|
/* Is the OSD enabled? */
|
|
static inline bool _osd_enabled(struct osd *osd)
|
|
{
|
|
return osd->status != OSD_DISABLED;
|
|
}
|
|
|
|
|
|
/** LCD update substitutes **/
|
|
|
|
/* Prepare LCD framebuffer for regular drawing */
|
|
static inline void _osd_lcd_update_prepare(struct osd *osd)
|
|
{
|
|
if (osd->status == OSD_VISIBLE)
|
|
{
|
|
osd->status = OSD_ERASED;
|
|
_osd_erase_osd(osd);
|
|
}
|
|
}
|
|
|
|
/* Update the whole screen */
|
|
static inline void _osd_lcd_update(struct osd *osd)
|
|
{
|
|
if (osd->status == OSD_ERASED)
|
|
{
|
|
/* Save the screen image underneath and restore the OSD image */
|
|
osd->status = OSD_VISIBLE;
|
|
_osd_update_back_buffer(osd);
|
|
_osd_draw_osd(osd);
|
|
}
|
|
|
|
osd->lcd_update();
|
|
}
|
|
|
|
/* Update a part of the screen */
|
|
static void _osd_lcd_update_rect(struct osd *osd,
|
|
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_update_back_buffer(osd);
|
|
_osd_draw_osd(osd);
|
|
}
|
|
|
|
osd->lcd_update_rect(x, y, width, height);
|
|
}
|
|
|
|
static void _osd_lcd_viewport_set_buffer(void *buffer)
|
|
{
|
|
if (buffer)
|
|
{
|
|
native_osd.framebuf.data = buffer;
|
|
native_osd.framebuf.elems = native_osd.maxheight * native_osd.maxwidth;
|
|
native_osd.framebuf.get_address_fn = NULL; /*Default iterator*/
|
|
|
|
if (buffer == native_osd.back_bitmap_data)
|
|
native_osd.framebuf.stride = (native_osd.back_bitmap_stride);
|
|
else
|
|
native_osd.framebuf.stride = (native_osd.lcd_bitmap_stride);
|
|
|
|
rb->viewport_set_buffer(NULL, &native_osd.framebuf, SCREEN_MAIN);
|
|
}
|
|
else
|
|
rb->viewport_set_buffer(NULL, NULL, SCREEN_MAIN);
|
|
}
|
|
|
|
/* Native LCD, public */
|
|
bool osd_init(unsigned flags, void *backbuf, size_t backbuf_size,
|
|
osd_draw_cb_fn_t draw_cb, int *width, int *height,
|
|
size_t *bufused)
|
|
{
|
|
native_osd.init_buffers = _osd_lcd_init_buffers;
|
|
native_osd.set_viewport_pos = _osd_lcd_viewport_set_pos;
|
|
native_osd.lcd_update = rb->lcd_update;
|
|
native_osd.lcd_update_rect = rb->lcd_update_rect;
|
|
native_osd.lcd_set_viewport = rb->lcd_set_viewport;
|
|
native_osd.lcd_set_framebuffer = (void *)_osd_lcd_viewport_set_buffer;
|
|
#if LCD_DEPTH < 4
|
|
native_osd.lcd_framebuffer_set_pos = NULL;
|
|
#endif /* LCD_DEPTH < 4 */
|
|
native_osd.lcd_bitmap_part = (void *)rb->lcd_bitmap_part;
|
|
|
|
return _osd_init(&native_osd, flags, backbuf, backbuf_size, draw_cb,
|
|
width, height, bufused);
|
|
}
|
|
|
|
void osd_destroy(void)
|
|
{
|
|
return _osd_destroy(&native_osd);
|
|
}
|
|
|
|
bool osd_show(unsigned flags)
|
|
{
|
|
return _osd_show(&native_osd, flags);
|
|
}
|
|
|
|
bool osd_update(void)
|
|
{
|
|
return _osd_update(&native_osd);
|
|
}
|
|
|
|
bool osd_update_rect(int x, int y, int width, int height)
|
|
{
|
|
return _osd_update_rect(&native_osd, x, y, width, height);
|
|
}
|
|
|
|
bool osd_update_pos(int x, int y, int width, int height)
|
|
{
|
|
return _osd_update_pos(&native_osd, x, y, width, height);
|
|
}
|
|
|
|
void osd_monitor_timeout(void)
|
|
{
|
|
_osd_monitor_timeout(&native_osd);
|
|
}
|
|
|
|
void osd_set_timeout(long timeout)
|
|
{
|
|
_osd_set_timeout(&native_osd, timeout);
|
|
}
|
|
|
|
struct viewport * osd_get_viewport(void)
|
|
{
|
|
return _osd_get_viewport(&native_osd);
|
|
}
|
|
|
|
void osd_get_max_dims(int *maxwidth, int *maxheight)
|
|
{
|
|
_osd_get_max_dims(&native_osd, maxwidth, maxheight);
|
|
}
|
|
|
|
bool osd_enabled(void)
|
|
{
|
|
return _osd_enabled(&native_osd);
|
|
}
|
|
|
|
void osd_lcd_update_prepare(void)
|
|
{
|
|
_osd_lcd_update_prepare(&native_osd);
|
|
}
|
|
|
|
|
|
void osd_lcd_update(void)
|
|
{
|
|
_osd_lcd_update(&native_osd);
|
|
}
|
|
|
|
void osd_lcd_update_rect(int x, int y, int width, int height)
|
|
{
|
|
_osd_lcd_update_rect(&native_osd, x, y, width, height);
|
|
}
|
|
|
|
#if LCD_DEPTH < 4
|
|
/* Greylib LCD, public */
|
|
bool osd_grey_init(unsigned flags, void *backbuf, size_t backbuf_size,
|
|
osd_draw_cb_fn_t draw_cb, int *width, int *height,
|
|
size_t *bufused)
|
|
{
|
|
grey_osd.init_buffers = _osd_grey_init_buffers;
|
|
grey_osd.set_viewport_pos = grey_viewport_set_pos;
|
|
grey_osd.lcd_update = grey_update;
|
|
grey_osd.lcd_update_rect = grey_update_rect;
|
|
grey_osd.lcd_set_viewport = grey_set_viewport;
|
|
grey_osd.lcd_set_framebuffer = (void *)grey_set_framebuffer;
|
|
grey_osd.lcd_framebuffer_set_pos = grey_framebuffer_set_pos;
|
|
grey_osd.lcd_bitmap_part = (void *)grey_gray_bitmap_part;
|
|
|
|
return _osd_init(&grey_osd, flags, backbuf, backbuf_size, draw_cb,
|
|
width, height, bufused);
|
|
}
|
|
|
|
void osd_grey_destroy(void)
|
|
{
|
|
return _osd_destroy(&grey_osd);
|
|
}
|
|
|
|
bool osd_grey_show(unsigned flags)
|
|
{
|
|
return _osd_show(&grey_osd, flags);
|
|
}
|
|
|
|
bool osd_grey_update(void)
|
|
{
|
|
return _osd_update(&grey_osd);
|
|
}
|
|
|
|
bool osd_grey_update_rect(int x, int y, int width, int height)
|
|
{
|
|
return _osd_update_rect(&grey_osd, x, y, width, height);
|
|
}
|
|
|
|
bool osd_grey_update_pos(int x, int y, int width, int height)
|
|
{
|
|
return _osd_update_pos(&grey_osd, x, y, width, height);
|
|
}
|
|
|
|
void osd_grey_monitor_timeout(void)
|
|
{
|
|
_osd_monitor_timeout(&grey_osd);
|
|
}
|
|
|
|
void osd_grey_set_timeout(long timeout)
|
|
{
|
|
_osd_set_timeout(&grey_osd, timeout);
|
|
}
|
|
|
|
struct viewport * osd_grey_get_viewport(void)
|
|
{
|
|
return _osd_get_viewport(&grey_osd);
|
|
}
|
|
|
|
void osd_grey_get_max_dims(int *maxwidth, int *maxheight)
|
|
{
|
|
_osd_get_max_dims(&grey_osd, maxwidth, maxheight);
|
|
}
|
|
|
|
bool osd_grey_enabled(void)
|
|
{
|
|
return _osd_enabled(&grey_osd);
|
|
}
|
|
|
|
void osd_grey_lcd_update_prepare(void)
|
|
{
|
|
_osd_lcd_update_prepare(&grey_osd);
|
|
}
|
|
|
|
void osd_grey_lcd_update(void)
|
|
{
|
|
_osd_lcd_update(&grey_osd);
|
|
}
|
|
|
|
void osd_grey_lcd_update_rect(int x, int y, int width, int height)
|
|
{
|
|
_osd_lcd_update_rect(&grey_osd, x, y, width, height);
|
|
}
|
|
#endif /* LCD_DEPTH < 4 */
|