df8233e4ab
Some of the lcd functions had support for multiple screens but this wasn't very safe since the screen number wasn't bounded within the screens[] array This adds support for all the lcd functions along with checking that screen# is bounded properly, adds around 600 bytes to devices with a remote screen devices without a remote screen lock to SCREEN_MAIN Change-Id: I618bbc7b3919c7b0ff375fb2d71949d7cab43c87
1692 lines
45 KiB
C
1692 lines
45 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2008 Dan Everton (safetydan)
|
|
* Copyright (C) 2009 Maurus Cuelenaere
|
|
* Copyright (C) 2017 William Wilgus
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define lrockimg_c
|
|
#define LUA_LIB
|
|
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
#include "rocklib.h"
|
|
#include "rocklib_img.h"
|
|
|
|
/*
|
|
* -----------------------------------------------------------------------------
|
|
*
|
|
* Rockbox Lua image wrapper
|
|
*
|
|
* Some devices(1-bit / 2-bit displays) have packed bit formats that
|
|
* need to be unpacked in order to work on them at a pixel level.
|
|
*
|
|
* The internal formats of these devices do not follow the same paradigm
|
|
* for image sizes either; We still display the actual width and height to
|
|
* the user but store stride based on the native values
|
|
*
|
|
* Conversion between native addressing and per pixel addressing
|
|
* incurs extra overhead but it is much faster to do it
|
|
* on the 'C' side rather than in lua.
|
|
*
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define ROCKLUA_IMAGE LUA_ROCKLIBNAME ".image"
|
|
|
|
/* mark for RLI to LUA Interface functions (luaState *L) is the only argument */
|
|
#define RLI_LUA static int
|
|
|
|
#ifndef ABS
|
|
#define ABS(a)(((a) < 0) ? - (a) :(a))
|
|
#endif
|
|
|
|
struct rocklua_image
|
|
{
|
|
int width;
|
|
int height;
|
|
int stride;
|
|
size_t elems;
|
|
fb_data *data;
|
|
fb_data dummy[1][1];
|
|
};
|
|
|
|
/* holds iterator data for rlimages */
|
|
struct rli_iter_d
|
|
{
|
|
int x , y;
|
|
int x1, y1;
|
|
int x2, y2;
|
|
int dx, dy;
|
|
fb_data *elem;
|
|
struct rocklua_image *img;
|
|
};
|
|
|
|
/* __tostring information enums */
|
|
enum rli_info {RLI_INFO_ALL = 0, RLI_INFO_TYPE, RLI_INFO_WIDTH,
|
|
RLI_INFO_HEIGHT, RLI_INFO_ELEMS, RLI_INFO_BYTES,
|
|
RLI_INFO_DEPTH, RLI_INFO_FORMAT, RLI_INFO_ADDRESS};
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
static inline fb_data invert_color(fb_data rgb)
|
|
{
|
|
uint8_t r = 0xFFU - FB_UNPACK_RED(rgb);
|
|
uint8_t g = 0xFFU - FB_UNPACK_GREEN(rgb);
|
|
uint8_t b = 0xFFU - FB_UNPACK_BLUE(rgb);
|
|
|
|
return FB_RGBPACK(r, g, b);
|
|
}
|
|
#else /* !HAVE_LCD_COLOR */
|
|
|
|
#define invert_color(c) (~c)
|
|
|
|
#endif /* HAVE_LCD_COLOR */
|
|
|
|
|
|
#if (LCD_DEPTH > 2) /* no native to pixel mapping needed */
|
|
|
|
#define pixel_to_fb(x, y, o, n) {(void) x; (void) y; do { } while (0);}
|
|
#define pixel_to_native(x, y, xn, yn) {*xn = x; *yn = y;}
|
|
#define init_pixelmask(x, y, m, p) do { } while (0)
|
|
|
|
|
|
#else /* some devices need x | y coords shifted to match native format */
|
|
|
|
static fb_data x_shift = FB_SCALARPACK(0);
|
|
static fb_data y_shift = FB_SCALARPACK(0);
|
|
static fb_data xy_mask = FB_SCALARPACK(0);
|
|
static const fb_data *pixelmask = NULL;
|
|
|
|
/* conversion between packed native formats and individual pixel addressing */
|
|
static inline void init_pixelmask(fb_data *x_shift, fb_data *y_shift,
|
|
fb_data *xy_mask, const fb_data **pixelmask)
|
|
{
|
|
|
|
#if(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 1
|
|
static const fb_data pixelmask_v1[8] =
|
|
{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
|
*pixelmask = pixelmask_v1;
|
|
|
|
(void) x_shift;
|
|
*y_shift = 3U;
|
|
*xy_mask = ((1 << (*y_shift)) - 1);
|
|
#elif(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 2
|
|
static const fb_data pixelmask_v2[4] = {0x03, 0x0C, 0x30, 0xC0};
|
|
*pixelmask = pixelmask_v2;
|
|
|
|
(void) x_shift;
|
|
*y_shift = 2U;
|
|
*xy_mask = ((1 << (*y_shift)) - 1);
|
|
#elif(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2
|
|
static const fb_data pixelmask_vi2[8] =
|
|
{0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080};
|
|
*pixelmask = pixelmask_vi2;
|
|
|
|
(void) x_shift;
|
|
*y_shift = 3U;
|
|
*xy_mask = ((1 << (*y_shift)) - 1);
|
|
#elif(LCD_PIXELFORMAT == HORIZONTAL_PACKING) && LCD_DEPTH == 2
|
|
/* MSB on left */
|
|
static const fb_data pixelmask_h2[4] = {0x03, 0x0C, 0x30, 0xC0};
|
|
*pixelmask = pixelmask_h2;
|
|
|
|
(void) y_shift;
|
|
*x_shift = 2U;
|
|
*xy_mask = ((1 << (*x_shift)) - 1);
|
|
#else
|
|
#warning Unknown Pixel Format
|
|
#endif /* LCD_PIXELFORMAT */
|
|
|
|
} /* init_pixelmask */
|
|
|
|
static inline void pixel_to_native(int x, int y, int *x_native, int *y_native)
|
|
{
|
|
*x_native = ((x - 1) >> x_shift) + 1;
|
|
*y_native = ((y - 1) >> y_shift) + 1;
|
|
} /* pixel_to_native */
|
|
|
|
static inline fb_data set_masked_pixel(fb_data old,
|
|
fb_data new,
|
|
fb_data mask,
|
|
int bit_n)
|
|
{
|
|
/*equivalent of: (old & (~mask)) | ((new << bit_n) & mask);*/
|
|
return old ^ ((old ^ (new << bit_n)) & mask);
|
|
|
|
} /* set_masked_pixel */
|
|
|
|
static inline fb_data get_masked_pixel(fb_data val, fb_data mask, int bit_n)
|
|
{
|
|
val = val & mask;
|
|
return val >> bit_n;
|
|
} /* get_masked_pixel */
|
|
|
|
/* conversion between packed native formats and individual pixel addressing */
|
|
static void pixel_to_fb(int x, int y, fb_data *oldv, fb_data *newv)
|
|
{
|
|
fb_data mask;
|
|
int bit_n;
|
|
|
|
#if(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2
|
|
(void) x;
|
|
const uint16_t greymap_vi2[4] = {0x0000, 0x0001, 0x0100, 0x0101};
|
|
|
|
bit_n = (y - 1) & xy_mask;
|
|
mask = pixelmask[bit_n];
|
|
|
|
*newv = greymap_vi2[*newv &(0x3)]; /* [0-3] => greymap */
|
|
*newv = set_masked_pixel(*oldv, *newv, mask, bit_n);
|
|
|
|
*oldv = get_masked_pixel(*oldv, mask, bit_n);
|
|
|
|
if((*oldv) > 1) /* greymap => [0-3] */
|
|
*oldv = ((*oldv) & 0x1U) + 2U; /* 2, 3 */
|
|
else
|
|
*oldv &= 1U; /* 0, 1 */
|
|
|
|
#elif(LCD_DEPTH <= 2)
|
|
if(y_shift)
|
|
bit_n = (y - 1) & xy_mask;
|
|
else if(x_shift)
|
|
bit_n = xy_mask - ((x - 1) & xy_mask); /*MSB on left*/
|
|
|
|
if(y_shift || x_shift)
|
|
{
|
|
mask = pixelmask[bit_n];
|
|
bit_n *= LCD_DEPTH;
|
|
|
|
*newv = set_masked_pixel(*oldv, *newv, mask, bit_n);
|
|
|
|
*oldv = get_masked_pixel(*oldv, mask, bit_n);
|
|
}
|
|
#else
|
|
#error Unknown Pixel Format
|
|
#endif /* LCD_PIXELFORMAT == VERTICAL_INTERLEAVED && LCD_DEPTH == 2 */
|
|
} /* pixel_to_fb */
|
|
|
|
#endif /* (LCD_DEPTH > 2) no native to pixel mapping needed */
|
|
|
|
/* Internal worker functions for image data array *****************************/
|
|
|
|
static inline void swap_int(bool swap, int *v1, int *v2)
|
|
{
|
|
if(swap)
|
|
{
|
|
int val = *v1;
|
|
*v1 = *v2;
|
|
*v2 = val;
|
|
}
|
|
} /* swap_int */
|
|
|
|
/* Throws error if x or y are out of bounds notifies user which narg indice
|
|
the out of bound variable originated */
|
|
static void bounds_check_xy(lua_State *L, struct rocklua_image *img,
|
|
int nargx, int x, int nargy, int y)
|
|
{
|
|
int narg;
|
|
|
|
if(x > img->width || x < 1)
|
|
narg = nargx;
|
|
else if(y <= img->height && y > 0)
|
|
return; /* note -- return if no error */
|
|
else
|
|
narg = nargy;
|
|
|
|
luaL_argerror(L, narg, ERR_IDX_RANGE);
|
|
} /* bounds_check_xy */
|
|
|
|
static struct rocklua_image* rli_checktype(lua_State *L, int arg)
|
|
{
|
|
#if 0
|
|
return (struct rocklua_image*) luaL_checkudata(L, arg, ROCKLUA_IMAGE);
|
|
#else /* cache result */
|
|
static struct rocklua_image* last = NULL;
|
|
void *ud = lua_touserdata(L, arg);
|
|
|
|
if(ud != NULL)
|
|
{
|
|
if(ud == last)
|
|
return last;
|
|
else if (lua_getmetatable(L, arg))
|
|
{ /* does it have a metatable? */
|
|
luaL_getmetatable(L, ROCKLUA_IMAGE); /* get correct metatable */
|
|
if (lua_rawequal(L, -1, -2))
|
|
{ /* does it have the correct mt? */
|
|
lua_pop(L, 2); /* remove both metatables */
|
|
last = (struct rocklua_image*) ud;
|
|
return last;
|
|
}
|
|
}
|
|
}
|
|
|
|
luaL_typerror(L, arg, ROCKLUA_IMAGE); /* else error */
|
|
return NULL; /* to avoid warnings */
|
|
#endif
|
|
} /* rli_checktype */
|
|
|
|
static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data,
|
|
int width, int height)
|
|
{
|
|
/* rliimage is pushed on the stack it is up to you to pop it */
|
|
struct rocklua_image *img;
|
|
|
|
const size_t sz_header = sizeof(struct rocklua_image);
|
|
size_t sz_data = 0;
|
|
size_t n_elems;
|
|
|
|
int w_native;
|
|
int h_native;
|
|
|
|
pixel_to_native(width, height, &w_native, &h_native);
|
|
|
|
n_elems = (size_t)(w_native * h_native);
|
|
|
|
if(alloc_data) /* if this a new image we need space for image data */
|
|
sz_data = n_elems * sizeof(fb_data);
|
|
|
|
/* newuserdata pushes the userdata onto the stack */
|
|
img = (struct rocklua_image *) lua_newuserdata(L, sz_header + sz_data);
|
|
|
|
luaL_getmetatable(L, ROCKLUA_IMAGE);
|
|
lua_setmetatable(L, -2);
|
|
|
|
/* apparent w/h is stored but behind the scenes native w/h is used */
|
|
img->width = width;
|
|
img->height = height;
|
|
img->stride = STRIDE_MAIN(w_native, h_native);
|
|
img->elems = n_elems;
|
|
|
|
return img;
|
|
} /* alloc_rlimage */
|
|
|
|
static inline void rli_wrap(lua_State *L, fb_data *src, int width, int height)
|
|
{
|
|
/* rliimage is pushed on the stack it is up to you to pop it */
|
|
struct rocklua_image *a = alloc_rlimage(L, false, width, height);
|
|
|
|
a->data = src;
|
|
} /* rli_wrap */
|
|
|
|
static inline fb_data* rli_alloc(lua_State *L, int width, int height)
|
|
{
|
|
/* rliimage is pushed on the stack it is up to you to pop it */
|
|
struct rocklua_image *a = alloc_rlimage(L, true, width, height);
|
|
|
|
a->data = &a->dummy[0][0]; /* ref to beginning of alloc'd img data */
|
|
|
|
return a->data;
|
|
} /* rli_alloc */
|
|
|
|
static inline fb_data data_set(fb_data *elem, int x, int y, fb_data *val)
|
|
{
|
|
fb_data old_val;
|
|
fb_data new_val;
|
|
|
|
if(elem)
|
|
{
|
|
old_val = *elem;
|
|
|
|
if(val)
|
|
{
|
|
new_val = *val;
|
|
pixel_to_fb(x, y, &old_val, &new_val);
|
|
*elem = new_val;
|
|
}
|
|
else
|
|
pixel_to_fb(x, y, &old_val, &new_val);
|
|
}
|
|
else
|
|
old_val = FB_SCALARPACK(0);
|
|
|
|
return old_val;
|
|
} /* data_set */
|
|
|
|
static inline fb_data data_get(fb_data *elem, int x, int y)
|
|
{
|
|
return data_set(elem, x, y, NULL);
|
|
} /* data_get */
|
|
|
|
static inline fb_data* rli_get_element(struct rocklua_image* img, int x, int y)
|
|
{
|
|
int stride = img->stride;
|
|
size_t elements = img->elems;
|
|
fb_data *data = img->data;
|
|
|
|
pixel_to_native(x, y, &x, &y);
|
|
|
|
#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
|
|
/* column major address */
|
|
size_t data_address = (stride * (x - 1)) + (y - 1);
|
|
|
|
/* y needs bound between 0 and stride otherwise overflow to prev/next x */
|
|
if(y <= 0 || y > stride || data_address >= elements)
|
|
return NULL; /* data overflow */
|
|
#else
|
|
/* row major address */
|
|
size_t data_address = (stride * (y - 1)) + (x - 1);
|
|
|
|
/* x needs bound between 0 and stride otherwise overflow to prev/next y */
|
|
if(x <= 0 || x > stride || data_address >= elements)
|
|
return NULL; /* data overflow */
|
|
#endif
|
|
|
|
return &data[data_address]; /* return element address */
|
|
} /* rli_get_element */
|
|
|
|
/* Lua to C Interface for pixel set and get functions */
|
|
static int rli_setget(lua_State *L, bool is_get, int narg_clip)
|
|
{
|
|
/*(set) (dst*, [x1, y1, clr, clip]) */
|
|
/*(get) (dst*, [x1, y1, clip]) */
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
int x = lua_tointeger(L, 2);
|
|
int y = lua_tointeger(L, 3);
|
|
|
|
fb_data clr; /* Arg 4 is color if set element */
|
|
|
|
fb_data *element = rli_get_element(a, x, y);
|
|
|
|
if(!element)
|
|
{
|
|
if(!lua_toboolean(L, narg_clip)) /* Error if !clip */
|
|
bounds_check_xy(L, a, 2, x, 3, y);
|
|
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
if(is_get) /* get element */
|
|
lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(data_get(element, x, y)));
|
|
else /* set element */
|
|
{
|
|
clr = FB_SCALARPACK((unsigned) lua_tointeger(L, 4));
|
|
lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(data_set(element, x, y, &clr)));
|
|
}
|
|
|
|
/* returns old value */
|
|
return 1;
|
|
} /* rli_setget */
|
|
|
|
#ifdef RLI_EXTENDED
|
|
static bool rli_iter_init(struct rli_iter_d *d,
|
|
struct rocklua_image *img,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
int dx, int dy,
|
|
bool swx, bool swy)
|
|
{
|
|
|
|
swap_int((swx), &x1, &x2);
|
|
swap_int((swy), &y1, &y2);
|
|
|
|
/* stepping in the correct x direction ? */
|
|
if((dx ^ (x2 - x1)) < 0)
|
|
dx = -dx;
|
|
|
|
/* stepping in the correct y direction ? */
|
|
if((dy ^ (y2 - y1)) < 0)
|
|
dy = -dy;
|
|
|
|
d->img = img;
|
|
d->x = x1;
|
|
d->y = y1;
|
|
d->x1 = x1;
|
|
d->y1 = y1;
|
|
d->x2 = x2;
|
|
d->y2 = y2;
|
|
d->dx = dx;
|
|
d->dy = dy;
|
|
d->elem = rli_get_element(img, d->x, d->y);
|
|
|
|
return(dx != 0 || dy != 0);
|
|
} /* rli_iter_init */
|
|
|
|
static struct rli_iter_d * rli_iter_create(lua_State *L)
|
|
{
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
int x1 = luaL_optint(L, 2, 1);
|
|
int y1 = luaL_optint(L, 3, 1);
|
|
int x2 = luaL_optint(L, 4, a->width);
|
|
int y2 = luaL_optint(L, 5, a->height);
|
|
int dx = luaL_optint(L, 6, 1);
|
|
int dy = luaL_optint(L, 7, 1);
|
|
bool clip = lua_toboolean(L, 8);
|
|
|
|
if(!clip)
|
|
{
|
|
bounds_check_xy(L, a, 2, x1, 3, y1);
|
|
bounds_check_xy(L, a, 4, x2, 5, y2);
|
|
}
|
|
|
|
struct rli_iter_d *ds;
|
|
/* create new iter + pushed onto stack */
|
|
ds = (struct rli_iter_d *) lua_newuserdata(L, sizeof(struct rli_iter_d));
|
|
|
|
rli_iter_init(ds, a, x1, y1, x2, y2, dx, dy, false, false);
|
|
|
|
return ds;
|
|
}
|
|
|
|
/* steps to the next point(x, y) by delta x/y, stores pointer to element
|
|
returns true if x & y haven't reached x2 & y2
|
|
if limit reached - element set to NULL, deltas set to 0 & false returned
|
|
*/
|
|
static bool next_rli_iter(struct rli_iter_d *d)
|
|
{
|
|
if((d->dx > 0 && d->x < d->x2) || (d->dx < 0 && d->x > d->x2))
|
|
d->x += d->dx;
|
|
else if((d->dy > 0 && d->y < d->y2) || (d->dy < 0 && d->y > d->y2))
|
|
{
|
|
d->x = d->x1; /* Reset x*/
|
|
d->y += d->dy;
|
|
}
|
|
else
|
|
{
|
|
d->elem = NULL;
|
|
d->dx = 0;
|
|
d->dy = 0;
|
|
return false;
|
|
}
|
|
|
|
d->elem = rli_get_element(d->img, d->x, d->y);
|
|
|
|
return true;
|
|
} /* next_rli_iter */
|
|
|
|
static void d_line(struct rocklua_image *img,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
fb_data *clr)
|
|
{
|
|
/* NOTE! clr passed as pointer */
|
|
/* Bresenham midpoint line algorithm */
|
|
fb_data *element;
|
|
|
|
int r_a = x2 - x1; /* range of x direction called 'a' for now */
|
|
int r_b = y2 - y1; /* range of y direction called 'b' for now */
|
|
|
|
int s_a = (r_a > 0) - (r_a < 0); /* step of a direction -1, 0, 1 */
|
|
int s_b = (r_b > 0) - (r_b < 0); /* step of b direction -1, 0, 1 */
|
|
|
|
int d_err;
|
|
int numpixels;
|
|
|
|
int *a1 = &x1; /* pointer to the x var 'a' */
|
|
int *b1 = &y1; /* pointer to the y var 'b' */
|
|
|
|
r_a = ABS(r_a);/* instead of negative range we switch step */
|
|
r_b = ABS(r_b);/* instead of negative range we switch step */
|
|
|
|
if(r_b > r_a) /*if rangeY('b') > rangeX('a') swap their identities */
|
|
{
|
|
a1 = &y1; /* pointer to the y var 'a' */
|
|
b1 = &x1; /* pointer to the x var 'b' */
|
|
swap_int((true), &r_a, &r_b);
|
|
swap_int((true), &s_a, &s_b);
|
|
}
|
|
|
|
d_err = ((r_b << 1) - r_a) >> 1; /* preload err of 1 step (px centered) */
|
|
|
|
/* add 1 extra point to make the whole line */
|
|
numpixels = r_a + 1;
|
|
|
|
r_a -= r_b; /* pre-subtract 'a' - 'b' */
|
|
|
|
for(; numpixels > 0; numpixels--)
|
|
{
|
|
element = rli_get_element(img, x1, y1);
|
|
data_set(element, x1, y1, clr);
|
|
|
|
if(d_err >= 0) /* 0 is our target midpoint(exact point on the line) */
|
|
{
|
|
*b1 += s_b; /* whichever axis is in 'b' stepped(-1 or +1) */
|
|
d_err -= r_a;
|
|
}
|
|
else
|
|
d_err += r_b; /* only add 'b' when d_err < 0 */
|
|
|
|
*a1 += s_a; /* whichever axis is in 'a' stepped(-1 or +1) */
|
|
}
|
|
|
|
} /* d_line */
|
|
|
|
/* ellipse worker function */
|
|
static void d_ellipse_elements(struct rocklua_image * img,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
fb_data *clr,
|
|
fb_data *fillclr)
|
|
{
|
|
fb_data *element;
|
|
|
|
if(fillclr)
|
|
{
|
|
d_line(img, x1, y1, x2, y1, fillclr); /* I. II.*/
|
|
d_line(img, x1, y2, x2, y2, fillclr); /* III.IV.*/
|
|
}
|
|
|
|
element = rli_get_element(img, x2, y1);
|
|
data_set(element, x2, y1, clr); /* I. Quadrant +x +y */
|
|
|
|
element = rli_get_element(img, x1, y1);
|
|
data_set(element, x1, y1, clr); /* II. Quadrant -x +y */
|
|
|
|
element = rli_get_element(img, x1, y2);
|
|
data_set(element, x1, y2, clr); /* III. Quadrant -x -y */
|
|
|
|
element = rli_get_element(img, x2, y2);
|
|
data_set(element, x2, y2, clr); /* IV. Quadrant +x -y */
|
|
|
|
} /* d_ellipse_elements */
|
|
|
|
static void d_ellipse(struct rocklua_image *img,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
fb_data *clr,
|
|
fb_data *fillclr)
|
|
{
|
|
/* NOTE! clr and fillclr passed as pointers */
|
|
/* Rasterizing algorithm derivative of work by Alois Zingl */
|
|
#if (LCD_WIDTH > 1024 || LCD_HEIGHT > 1024) && defined(INT64_MAX)
|
|
/* Prevents overflow on large screens */
|
|
int64_t dx, dy, err, e1;
|
|
#else
|
|
int32_t dx, dy, err, e1;
|
|
#endif
|
|
/* if called with swapped points .. exchange them */
|
|
swap_int((x1 > x2), &x1, &x2);
|
|
swap_int((y1 > y2), &y1, &y2);
|
|
|
|
int a = x2 - x1; /* diameter */
|
|
int b = y2 - y1; /* diameter */
|
|
|
|
if(a == 0 || b == 0)
|
|
return; /* not an error but nothing to display */
|
|
|
|
int b1 = (b & 1);
|
|
b = b - (1 - b1);
|
|
|
|
int a2 = (a * a);
|
|
int b2 = (b * b);
|
|
|
|
dx = ((1 - a) * b2) >> 1; /* error increment */
|
|
dy = (b1 * a2) >> 1; /* error increment */
|
|
|
|
err = dx + dy + b1 * a2; /* error of 1.step */
|
|
|
|
y1 += (b + 1) >> 1;
|
|
y2 = y1 - b1;
|
|
|
|
do
|
|
{
|
|
d_ellipse_elements(img, x1, y1, x2, y2, clr, fillclr);
|
|
|
|
e1 = err;
|
|
|
|
if(e1 <= (dy >> 1)) /* target midpoint - y step */
|
|
{
|
|
y1++;
|
|
y2--;
|
|
dy += a2;
|
|
err += dy;
|
|
}
|
|
|
|
if(e1 >= (dx >> 1) || err > (dy >> 1)) /* target midpoint - x step */
|
|
{
|
|
x1++;
|
|
x2--;
|
|
dx += b2;
|
|
err += dx;
|
|
}
|
|
|
|
} while(x1 <= x2);
|
|
|
|
if (fillclr && x1 - x2 <= 2)
|
|
fillclr = clr;
|
|
|
|
while (y1 - y2 < b) /* early stop of flat ellipse a=1 finish tip */
|
|
{
|
|
d_ellipse_elements(img, x1, y1, x2, y2, clr, fillclr);
|
|
|
|
y1++;
|
|
y2--;
|
|
}
|
|
|
|
} /* d_ellipse */
|
|
|
|
/* Lua to C Interface for line and ellipse */
|
|
static int rli_line_ellipse(lua_State *L, bool is_ellipse, int narg_clip)
|
|
{
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
|
|
int x1 = luaL_checkint(L, 2);
|
|
int y1 = luaL_checkint(L, 3);
|
|
int x2 = luaL_optint(L, 4, x1);
|
|
int y2 = luaL_optint(L, 5, y1);
|
|
|
|
fb_data clr = FB_SCALARPACK((unsigned) lua_tointeger(L, 6));
|
|
|
|
fb_data fillclr; /* fill color is index 7 if is_ellipse */
|
|
fb_data *p_fillclr = NULL;
|
|
|
|
bool clip = lua_toboolean(L, narg_clip);
|
|
|
|
if(!clip)
|
|
{
|
|
bounds_check_xy(L, a, 2, x1, 3, y1);
|
|
bounds_check_xy(L, a, 4, x2, 5, y2);
|
|
}
|
|
|
|
if(is_ellipse)
|
|
{
|
|
if(lua_type(L, 7) == LUA_TNUMBER)
|
|
{
|
|
fillclr = FB_SCALARPACK((unsigned) lua_tointeger(L, 7));
|
|
p_fillclr = &fillclr;
|
|
}
|
|
|
|
d_ellipse(a, x1, y1, x2, y2, &clr, p_fillclr);
|
|
}
|
|
else
|
|
d_line(a, x1, y1, x2, y2, &clr);
|
|
|
|
return 0;
|
|
} /* rli_line_ellipse */
|
|
|
|
static inline int rli_pushpixel(lua_State *L, fb_data color, int x, int y)
|
|
{
|
|
lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(color));
|
|
lua_pushinteger(L, x);
|
|
lua_pushinteger(L, y);
|
|
return 3;
|
|
}
|
|
|
|
/* User defined pixel manipulations through rli_copy, rli_marshal */
|
|
static void custom_transform(lua_State *L,
|
|
struct rli_iter_d *ds,
|
|
struct rli_iter_d *ss,
|
|
int op,
|
|
fb_data *color)
|
|
{
|
|
(void) color;
|
|
(void) op;
|
|
fb_data dst;
|
|
fb_data src;
|
|
|
|
int params;
|
|
bool done = true;
|
|
|
|
if (true)/*(lua_isfunction(L, -1))*/
|
|
{
|
|
lua_pushvalue(L, -1); /* make a copy of the lua function */
|
|
|
|
dst = data_get(ds->elem, ds->x, ds->y);
|
|
params = rli_pushpixel(L, dst, ds->x, ds->y);
|
|
|
|
if(ss) /* Allows src to be omitted */
|
|
{
|
|
src = data_get(ss->elem, ss->x, ss->y);
|
|
params += rli_pushpixel(L, src, ss->x, ss->y);
|
|
}
|
|
|
|
lua_call(L, params, 2); /* call custom function w/ n-params & 2 ret */
|
|
|
|
if(lua_type(L, -2) == LUA_TNUMBER)
|
|
{
|
|
done = false;
|
|
dst = FB_SCALARPACK((unsigned) lua_tointeger(L, -2));
|
|
data_set(ds->elem, ds->x, ds->y, &dst);
|
|
}
|
|
|
|
if(ss && (lua_type(L, -1) == LUA_TNUMBER))
|
|
{
|
|
done = false;
|
|
src = FB_SCALARPACK((unsigned) lua_tointeger(L, -1));
|
|
data_set(ss->elem, ss->x, ss->y, &src);
|
|
}
|
|
|
|
lua_pop(L, 2);
|
|
}
|
|
|
|
if(done) /* signal iter to stop */
|
|
{
|
|
ds->dx = 0;
|
|
ds->dy = 0;
|
|
}
|
|
} /* custom_transform */
|
|
|
|
/* Pre defined pixel manipulations through rli_copy */
|
|
static void blit_transform(lua_State *L,
|
|
struct rli_iter_d *ds,
|
|
struct rli_iter_d *ss,
|
|
int op,
|
|
fb_data *color)
|
|
{
|
|
(void) L;
|
|
unsigned clr = FB_UNPACK_SCALAR_LCD(*color);
|
|
unsigned dst = FB_UNPACK_SCALAR_LCD(data_get(ds->elem, ds->x, ds->y));
|
|
unsigned src;
|
|
|
|
/* Reuse 0 - 7 for src / clr blits*/
|
|
if(op >= 30 && op <= 37)
|
|
{
|
|
op -= 30;
|
|
src = clr;
|
|
}
|
|
else
|
|
src = FB_UNPACK_SCALAR_LCD(data_get(ss->elem, ss->x, ss->y));
|
|
|
|
switch(op)
|
|
{
|
|
default:
|
|
/* case 30: */
|
|
case 0: { dst = src; break; }/* copyS/C */
|
|
/* case 31: */
|
|
case 1: { dst = src | dst; break; }/* DorS/C */
|
|
/* case 32: */
|
|
case 2: { dst = src ^ dst; break; }/* DxorS/C */
|
|
/* case 33: */
|
|
case 3: { dst = ~(src | dst); break; }/* nDorS/C */
|
|
/* case 34: */
|
|
case 4: { dst = (~src) | dst; break; }/* DornS/C */
|
|
/* case 35: */
|
|
case 5: { dst = src & dst; break; }/* DandS/C */
|
|
/* case 36: */
|
|
case 6: { dst = src & (~dst); break; }/* nDandS/C */
|
|
/* case 37: */
|
|
case 7: { dst = ~src; break; }/* notS/C */
|
|
|
|
/* mask blits */
|
|
case 8: { if(src != 0) { dst = clr; } break; }/* Sand */
|
|
case 9: { if(src == 0) { dst = clr; } break; }/* Snot */
|
|
|
|
case 10: { dst = src | clr; break; }/* SorC */
|
|
case 11: { dst = src ^ clr; break; }/* SxorC */
|
|
case 12: { dst = ~(src | clr); break; }/* nSorC */
|
|
case 13: { dst = src | (~clr); break; }/* SornC */
|
|
case 14: { dst = src & clr; break; }/* SandC */
|
|
case 15: { dst = (~src) & clr; break; }/* nSandC */
|
|
case 16: { dst |= (~src) | clr; break; }/* DornSorC */
|
|
case 17: { dst ^= (src & (dst ^ clr)); break; }/* DxorSandDxorC */
|
|
|
|
case 18: { if(src != clr) { dst = src; } break; }
|
|
case 19: { if(src == clr) { dst = src; } break; }
|
|
case 20: { if(src > clr) { dst = src; } break; }
|
|
case 21: { if(src < clr) { dst = src; } break; }
|
|
|
|
case 22: { if(dst != clr) { dst = src; } break; }
|
|
case 23: { if(dst == clr) { dst = src; } break; }
|
|
case 24: { if(dst > clr) { dst = src; } break; }
|
|
case 25: { if(dst < clr) { dst = src; } break; }
|
|
|
|
case 26: { if(dst != src) { dst = clr; } break; }
|
|
case 27: { if(dst == src) { dst = clr; } break; }
|
|
case 28: { if(dst > src) { dst = clr; } break; }
|
|
case 29: { if(dst < src) { dst = clr; } break; }
|
|
#if 0
|
|
/* src unneeded */
|
|
case 30: { dst = clr; break; }/* copyC */
|
|
case 31: { dst = clr | dst; break; }/* DorC */
|
|
case 32: { dst = clr ^ dst; break; }/* DxorC */
|
|
case 33: { dst = ~(clr | dst); break; }/* nDorC */
|
|
case 34: { dst = (~clr) | dst; break; }/* DornC */
|
|
case 35: { dst = clr & dst; break; }/* DandC */
|
|
case 36: { dst = clr & (~dst); break; }/* nDandC */
|
|
case 37: { dst = ~clr; break; }/* notC */
|
|
#endif
|
|
|
|
}/*switch op*/
|
|
fb_data val = FB_SCALARPACK(dst);
|
|
data_set(ds->elem, ds->x, ds->y, &val);
|
|
} /* blit_transform */
|
|
|
|
static void invert_transform(lua_State *L,
|
|
struct rli_iter_d *ds,
|
|
struct rli_iter_d *ss,
|
|
int op,
|
|
fb_data *color)
|
|
{
|
|
(void) L;
|
|
(void) color;
|
|
(void) op;
|
|
(void) ss;
|
|
|
|
fb_data val = invert_color(data_get(ds->elem, ds->x, ds->y));
|
|
data_set(ds->elem, ds->x, ds->y, &val);
|
|
} /* invert_transform */
|
|
|
|
static void clear_transform(lua_State *L,
|
|
struct rli_iter_d *ds,
|
|
struct rli_iter_d *ss,
|
|
int op,
|
|
fb_data *color)
|
|
{
|
|
(void) L;
|
|
(void) op;
|
|
(void) ss;
|
|
|
|
data_set(ds->elem, ds->x, ds->y, color);
|
|
} /* clear_transform */
|
|
|
|
#endif /* RLI_EXTENDED */
|
|
|
|
/* RLI to LUA Interface functions *********************************************/
|
|
RLI_LUA rli_new(lua_State *L)
|
|
{ /* [width, height] */
|
|
int width = luaL_optint(L, 1, LCD_WIDTH);
|
|
int height = luaL_optint(L, 2, LCD_HEIGHT);
|
|
|
|
luaL_argcheck(L, width > 0 && height > 0, (width <= 0) ? 1 : 2, ERR_IDX_RANGE);
|
|
|
|
rli_alloc(L, width, height);
|
|
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_set(lua_State *L)
|
|
{
|
|
/*(set) (dst*, [x1, y1, clr, clip]) */
|
|
return rli_setget(L, false, 5);
|
|
}
|
|
|
|
RLI_LUA rli_get(lua_State *L)
|
|
{
|
|
/*(get) (dst*, [x1, y1, clip]) */
|
|
return rli_setget(L, true, 4);
|
|
}
|
|
|
|
RLI_LUA rli_equal(lua_State *L)
|
|
{
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
struct rocklua_image *b = rli_checktype(L, 2);
|
|
lua_pushboolean(L, a->data == b->data);
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_height(lua_State *L)
|
|
{
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
lua_pushinteger(L, a->height);
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_width(lua_State *L)
|
|
{
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
lua_pushinteger(L, a->width);
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_size(lua_State *L)
|
|
{
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
lua_pushinteger(L, a->elems);
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_raw(lua_State *L)
|
|
{
|
|
/*val = (img*, index, [new_val]) */
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
|
|
size_t i = (unsigned) lua_tointeger(L, 2);
|
|
|
|
fb_data val;
|
|
|
|
luaL_argcheck(L, i > 0 && i <= (a->elems), 2, ERR_IDX_RANGE);
|
|
|
|
lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(a->data[i-1]));
|
|
|
|
if(lua_type(L, 3) == LUA_TNUMBER)
|
|
{
|
|
val = FB_SCALARPACK((unsigned) lua_tointeger(L, 3));
|
|
a->data[i-1] = val;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_tostring(lua_State *L)
|
|
{
|
|
/* (img, [infoitem]) */
|
|
struct rocklua_image *a = rli_checktype(L, 1);
|
|
|
|
int item = lua_tointeger(L, 2);
|
|
size_t bytes = a->elems * sizeof(fb_data);
|
|
|
|
switch(item)
|
|
{
|
|
default:
|
|
case RLI_INFO_ALL:
|
|
{
|
|
lua_pushfstring(L,
|
|
ROCKLUA_IMAGE ": %dx%d, %d elements, %d bytes, %d-bit depth, %d pack",
|
|
a->width, a->height, a->elems, bytes, LCD_DEPTH, LCD_PIXELFORMAT);
|
|
break;
|
|
}
|
|
case RLI_INFO_TYPE: { lua_pushfstring(L, ROCKLUA_IMAGE); break; }
|
|
case RLI_INFO_WIDTH: { lua_pushinteger(L, a->width); break; }
|
|
case RLI_INFO_HEIGHT: { lua_pushinteger(L, a->height); break; }
|
|
case RLI_INFO_ELEMS: { lua_pushinteger(L, a->elems); break; }
|
|
case RLI_INFO_BYTES: { lua_pushinteger(L, bytes); break; }
|
|
case RLI_INFO_DEPTH: { lua_pushinteger(L, LCD_DEPTH ); break; }
|
|
case RLI_INFO_FORMAT: { lua_pushinteger(L, LCD_PIXELFORMAT); break; }
|
|
case RLI_INFO_ADDRESS: { lua_pushfstring(L, "%p", a->data); break; }
|
|
}
|
|
|
|
/* lua_pushstring(L, lua_tostring(L, -1)); */
|
|
lua_tostring(L, -1); /* converts item at index to string */
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef RLI_EXTENDED
|
|
RLI_LUA rli_ellipse(lua_State *L)
|
|
{
|
|
/* (dst*, x1, y1, x2, y2, [clr, fillclr, clip]) */
|
|
/* line and ellipse share the same init function */
|
|
return rli_line_ellipse(L, true, 8);
|
|
}
|
|
|
|
RLI_LUA rli_line(lua_State *L)
|
|
{
|
|
/* (dst*, x1, y1, [x2, y2, clr, clip]) */
|
|
/* line and ellipse share the same init function */
|
|
return rli_line_ellipse(L, false, 7);
|
|
}
|
|
|
|
RLI_LUA rli_iterator(lua_State *L) {
|
|
/* see rli_iterator_factory */
|
|
int params = 0;
|
|
struct rli_iter_d *ds;
|
|
ds = (struct rli_iter_d *) lua_touserdata(L, lua_upvalueindex(1));
|
|
|
|
if(ds->dx != 0 || ds->dy != 0)
|
|
{
|
|
params = rli_pushpixel(L, data_get(ds->elem, ds->x, ds->y), ds->x, ds->y);
|
|
|
|
next_rli_iter(ds); /* load next element */
|
|
}
|
|
return params; /* nothing left to do */
|
|
}
|
|
|
|
RLI_LUA rli_iterator_factory(lua_State *L)
|
|
{
|
|
/* (points) (img*, [x1, y1, x2, y2, dx, dy, clip]) */
|
|
/* (indices 1-8 are used by rli_iter_create) */
|
|
|
|
/* create new iter + pushed onto stack */
|
|
rli_iter_create(L);
|
|
|
|
/* returns the iter function with embedded iter data(up values) */
|
|
lua_pushcclosure(L, &rli_iterator, 1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
RLI_LUA rli_marshal(lua_State *L) /* also invert, clear */
|
|
{
|
|
/* (marshal/invert/clear) (img*, [x1, y1, x2, y2, dx, dy, clip, function]) */
|
|
/* (indices 1-8 are used by rli_iter_create) */
|
|
fb_data clr;
|
|
|
|
void (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *);
|
|
int ltype = lua_type (L, 9);
|
|
|
|
/* create new iter + pushed onto stack */
|
|
struct rli_iter_d *ds = rli_iter_create(L);
|
|
|
|
if (ltype == LUA_TNUMBER)
|
|
{
|
|
clr = FB_SCALARPACK((unsigned) lua_tointeger(L, 9));
|
|
rli_trans = clear_transform;
|
|
}
|
|
else if(ltype == LUA_TFUNCTION) /* custom function */
|
|
{
|
|
rli_trans = custom_transform;
|
|
lua_pushvalue(L, 9); /* ensure lua function on top of stack */
|
|
}
|
|
else
|
|
rli_trans = invert_transform; /* default transformation */
|
|
|
|
do
|
|
{
|
|
(*rli_trans)(L, ds, NULL, 0, &clr);
|
|
} while(next_rli_iter(ds));
|
|
|
|
return 0;
|
|
}
|
|
|
|
RLI_LUA rli_copy(lua_State *L)
|
|
{
|
|
/* (dst*, src*, [d_x, d_y, s_x, s_y, x_off, y_off, clip, [op, funct/clr]]) */
|
|
struct rocklua_image *dst = rli_checktype(L, 1); /* dst */
|
|
struct rocklua_image *src = rli_checktype(L, 2); /* src */
|
|
|
|
struct rli_iter_d ds; /* dst */
|
|
struct rli_iter_d ss; /* src */
|
|
|
|
/* copy whole image if possible */
|
|
if(src->elems == dst->elems && src->width == dst->width && lua_gettop(L) < 3)
|
|
{
|
|
rb->memcpy(dst->data, src->data, dst->elems * sizeof(fb_data));
|
|
return 0;
|
|
}
|
|
|
|
int d_x = luaL_optint(L, 3, 1);
|
|
int d_y = luaL_optint(L, 4, 1);
|
|
int s_x = luaL_optint(L, 5, 1);
|
|
int s_y = luaL_optint(L, 6, 1);
|
|
|
|
int w = MIN(dst->width - d_x, src->width - s_x);
|
|
int h = MIN(dst->height - d_y, src->height - s_y);
|
|
|
|
int x_off = luaL_optint(L, 7, w);
|
|
int y_off = luaL_optint(L, 8, h);
|
|
|
|
bool clip = lua_toboolean(L, 9);
|
|
int op; /* 10 is operation for blit */
|
|
fb_data clr; /* 11 is custom function | color */
|
|
|
|
bool d_swx = (x_off < 0); /* dest swap */
|
|
bool d_swy = (y_off < 0);
|
|
bool s_swx = false; /* src swap */
|
|
bool s_swy = false;
|
|
|
|
void (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *);
|
|
|
|
if(!clip) /* Out of bounds is not allowed */
|
|
{
|
|
bounds_check_xy(L, dst, 3, d_x, 4, d_y);
|
|
bounds_check_xy(L, src, 5, s_x, 6, s_y);
|
|
w = MIN(w, ABS(x_off));
|
|
h = MIN(h, ABS(y_off));
|
|
bounds_check_xy(L, dst, 7, d_x + w, 8, d_y + h);
|
|
bounds_check_xy(L, src, 7, s_x + w, 8, s_y + h);
|
|
}
|
|
else
|
|
{
|
|
if (w < 0 || h < 0) /* not an error but nothing to display */
|
|
return 0;
|
|
w = MIN(w, ABS(x_off));
|
|
h = MIN(h, ABS(y_off));
|
|
}
|
|
|
|
/* if src->data == dst->data need to care about fill direction */
|
|
if(d_x > s_x)
|
|
{
|
|
d_swx = !d_swx;
|
|
s_swx = !s_swx;
|
|
}
|
|
|
|
if(d_y > s_y)
|
|
{
|
|
d_swy = !d_swy;
|
|
s_swy = !s_swy;
|
|
}
|
|
|
|
rli_iter_init(&ds, dst, d_x, d_y, d_x + w, d_y + h, 1, 1, d_swx, d_swy);
|
|
|
|
rli_iter_init(&ss, src, s_x, s_y, s_x + w, s_y + h, 1, 1, s_swx, s_swy);
|
|
|
|
if (lua_type(L, 11) == LUA_TFUNCTION) /* custom function supplied.. */
|
|
{
|
|
rli_trans = custom_transform;
|
|
lua_settop(L, 11); /* ensure lua function on top of stack */
|
|
clr = FB_SCALARPACK(0);
|
|
op = 0;
|
|
}
|
|
else
|
|
{
|
|
rli_trans = blit_transform; /* default transformation */
|
|
clr = FB_SCALARPACK((unsigned) lua_tointeger(L, 11));
|
|
op = lua_tointeger(L, 10);
|
|
}
|
|
|
|
do
|
|
{
|
|
(*rli_trans)(L, &ds, &ss, op, &clr);
|
|
} while(next_rli_iter(&ds) && next_rli_iter(&ss));
|
|
|
|
return 0;
|
|
}
|
|
|
|
RLI_LUA rli_clear(lua_State *L)
|
|
{
|
|
/* (clear) (dst*, [color, x1, y1, x2, y2, clip, dx, dy]) */
|
|
lua_settop(L, 9);
|
|
|
|
lua_pushvalue(L, 7); /* clip -- index 8 */
|
|
lua_remove(L, 7);
|
|
|
|
lua_pushinteger(L, lua_tointeger(L, 2)); /*color -- index 9*/
|
|
lua_remove(L, 2);
|
|
|
|
return rli_marshal(L); /* (img*, [x1, y1, x2, y2, dx, dy, clip, function]) */
|
|
}
|
|
#endif /* RLI_EXTENDED */
|
|
|
|
/* Rli Image methods exported to lua */
|
|
static const struct luaL_reg rli_lib [] =
|
|
{
|
|
{"__tostring", rli_tostring},
|
|
{"_data", rli_raw},
|
|
{"__len", rli_size},
|
|
{"__eq", rli_equal},
|
|
{"width", rli_width},
|
|
{"height", rli_height},
|
|
{"set", rli_set},
|
|
{"get", rli_get},
|
|
|
|
#ifdef RLI_EXTENDED
|
|
{"copy", rli_copy},
|
|
{"clear", rli_clear},
|
|
{"invert", rli_marshal},
|
|
{"marshal", rli_marshal},
|
|
{"points", rli_iterator_factory},
|
|
{"line", rli_line},
|
|
{"ellipse", rli_ellipse},
|
|
#endif /* RLI_EXTENDED */
|
|
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/*
|
|
* -----------------------------
|
|
*
|
|
* Rockbox wrappers start here!
|
|
*
|
|
* -----------------------------
|
|
*/
|
|
|
|
#define RB_WRAP(func) static int rock_##func(lua_State UNUSED_ATTR *L)
|
|
|
|
#if defined NB_SCREENS && (NB_SCREENS > 1)
|
|
#define RB_SCREENS(luastate, narg, func, ...) \
|
|
rb->screens[get_screen(luastate, narg)]->func(__VA_ARGS__)
|
|
|
|
static int get_screen(lua_State *L, int narg)
|
|
{
|
|
int screen = luaL_optint(L, narg, SCREEN_MAIN);
|
|
|
|
if(screen < SCREEN_MAIN)
|
|
screen = SCREEN_MAIN;
|
|
else if(screen > NB_SCREENS)
|
|
screen = NB_SCREENS;
|
|
|
|
return screen;
|
|
}
|
|
#else /* only SCREEN_MAIN exists */
|
|
#define RB_SCREENS(luastate, narg, func, ...) \
|
|
rb->screens[SCREEN_MAIN]->func(__VA_ARGS__)
|
|
#endif
|
|
|
|
RB_WRAP(lcd_update)
|
|
{
|
|
RB_SCREENS(L, 1, update);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_clear_display)
|
|
{
|
|
RB_SCREENS(L, 1, clear_display);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_set_drawmode)
|
|
{
|
|
int mode = (int) luaL_checkint(L, 1);
|
|
RB_SCREENS(L, 2, set_drawmode, mode);
|
|
return 0;
|
|
}
|
|
|
|
/* helper function for lcd_puts functions */
|
|
static const unsigned char * lcd_putshelper(lua_State *L, int *x, int *y)
|
|
{
|
|
*x = (int) luaL_checkint(L, 1);
|
|
*y = (int) luaL_checkint(L, 2);
|
|
return luaL_checkstring(L, 3);
|
|
}
|
|
|
|
RB_WRAP(lcd_putsxy)
|
|
{
|
|
int x, y;
|
|
const unsigned char *string = lcd_putshelper(L, &x, &y);
|
|
RB_SCREENS(L, 4, putsxy, x, y, string);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_puts)
|
|
{
|
|
int x, y;
|
|
const unsigned char * string = lcd_putshelper(L, &x, &y);
|
|
RB_SCREENS(L, 4, puts, x, y, string);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_puts_scroll)
|
|
{
|
|
int x, y;
|
|
const unsigned char * string = lcd_putshelper(L, &x, &y);
|
|
bool result = RB_SCREENS(L, 4, puts_scroll, x, y, string);
|
|
lua_pushboolean(L, result);
|
|
return 1;
|
|
}
|
|
|
|
RB_WRAP(lcd_scroll_stop)
|
|
{
|
|
RB_SCREENS(L, 1, scroll_stop);
|
|
return 0;
|
|
}
|
|
|
|
/* Helper function for opt_viewport */
|
|
static int check_tablevalue(lua_State *L, const char* key, int tablepos)
|
|
{
|
|
lua_getfield(L, tablepos, key); /* Find table[key] */
|
|
|
|
int val = lua_tointeger(L, -1);
|
|
|
|
lua_pop(L, 1); /* Pop the value off the stack */
|
|
return val;
|
|
}
|
|
|
|
static inline struct viewport* opt_viewport(lua_State *L,
|
|
int narg,
|
|
struct viewport* vp,
|
|
struct viewport* alt)
|
|
{
|
|
if(lua_isnoneornil(L, narg))
|
|
return alt;
|
|
|
|
luaL_checktype(L, narg, LUA_TTABLE);
|
|
|
|
vp->x = check_tablevalue(L, "x", narg);
|
|
vp->y = check_tablevalue(L, "y", narg);
|
|
vp->width = check_tablevalue(L, "width", narg);
|
|
vp->height = check_tablevalue(L, "height", narg);
|
|
#ifdef HAVE_LCD_BITMAP
|
|
vp->font = check_tablevalue(L, "font", narg);
|
|
vp->drawmode = check_tablevalue(L, "drawmode", narg);
|
|
#endif
|
|
#if LCD_DEPTH > 1
|
|
vp->fg_pattern = (unsigned int) check_tablevalue(L, "fg_pattern", narg);
|
|
vp->bg_pattern = (unsigned int) check_tablevalue(L, "bg_pattern", narg);
|
|
#endif
|
|
|
|
return vp;
|
|
}
|
|
|
|
RB_WRAP(set_viewport)
|
|
{
|
|
static struct viewport vp;
|
|
RB_SCREENS(L, 2, set_viewport, opt_viewport(L, 1, &vp, NULL));
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(clear_viewport)
|
|
{
|
|
RB_SCREENS(L, 1, clear_viewport);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(font_getstringsize)
|
|
{
|
|
const unsigned char* str = luaL_checkstring(L, 1);
|
|
int fontnumber = lua_tointeger(L, 2);
|
|
int w, h, result;
|
|
|
|
if (fontnumber == FONT_UI)
|
|
fontnumber = rb->global_status->font_id[SCREEN_MAIN];
|
|
else
|
|
fontnumber = FONT_SYSFIXED;
|
|
|
|
if lua_isnil(L, 2)
|
|
result = RB_SCREENS(L, 3, getstringsize, str, &w, &h);
|
|
else
|
|
result = rb->font_getstringsize(str, &w, &h, fontnumber);
|
|
|
|
lua_pushinteger(L, result);
|
|
lua_pushinteger(L, w);
|
|
lua_pushinteger(L, h);
|
|
|
|
return 3;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
|
|
RB_WRAP(lcd_framebuffer)
|
|
{
|
|
rli_wrap(L, rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT);
|
|
return 1;
|
|
}
|
|
|
|
RB_WRAP(lcd_setfont)
|
|
{
|
|
int font = (int) luaL_checkint(L, 1);
|
|
RB_SCREENS(L, 2, setfont, font);
|
|
return 0;
|
|
}
|
|
|
|
/* helper function for lcd_xxx_bitmap/rect functions */
|
|
static void get_rect_bounds(lua_State *L, int narg, int *x, int *y, int *w, int* h)
|
|
{
|
|
*x = luaL_checkint(L, narg);
|
|
*y = luaL_checkint(L, narg + 1);
|
|
*w = luaL_checkint(L, narg + 2);
|
|
*h = luaL_checkint(L, narg + 3);
|
|
}
|
|
|
|
RB_WRAP(lcd_mono_bitmap_part)
|
|
{
|
|
struct rocklua_image *src = rli_checktype(L, 1);
|
|
int src_x = luaL_checkint(L, 2);
|
|
int src_y = luaL_checkint(L, 3);
|
|
int stride = luaL_checkint(L, 4);
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 5, &x, &y, &width, &height);
|
|
|
|
RB_SCREENS(L, 9, mono_bitmap_part, (const unsigned char *)src->data,
|
|
src_x, src_y, stride, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_mono_bitmap)
|
|
{
|
|
struct rocklua_image *src = rli_checktype(L, 1);
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 2, &x, &y, &width, &height);
|
|
|
|
RB_SCREENS(L, 6, mono_bitmap, (const unsigned char *)src->data, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
#if LCD_DEPTH > 1
|
|
RB_WRAP(lcd_bitmap_part)
|
|
{
|
|
struct rocklua_image *src = rli_checktype(L, 1);
|
|
int src_x = luaL_checkint(L, 2);
|
|
int src_y = luaL_checkint(L, 3);
|
|
int stride = luaL_checkint(L, 4);
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 5, &x, &y, &width, &height);
|
|
|
|
RB_SCREENS(L, 9, bitmap_part, src->data, src_x, src_y, stride, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_bitmap)
|
|
{
|
|
struct rocklua_image *src = rli_checktype(L, 1);
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 2, &x, &y, &width, &height);
|
|
|
|
RB_SCREENS(L, 6, bitmap, src->data, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_get_backdrop)
|
|
{
|
|
fb_data* backdrop = rb->lcd_get_backdrop();
|
|
if(backdrop == NULL)
|
|
lua_pushnil(L);
|
|
else
|
|
rli_wrap(L, backdrop, LCD_WIDTH, LCD_HEIGHT);
|
|
|
|
return 1;
|
|
}
|
|
|
|
RB_WRAP(lcd_set_foreground)
|
|
{
|
|
unsigned foreground = (unsigned) luaL_checkint(L, 1);
|
|
RB_SCREENS(L, 2, set_foreground, foreground);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_get_foreground)
|
|
{
|
|
unsigned result = RB_SCREENS(L, 1, get_foreground);
|
|
lua_pushinteger(L, result);
|
|
return 1;
|
|
}
|
|
|
|
RB_WRAP(lcd_set_background)
|
|
{
|
|
unsigned background = (unsigned) luaL_checkint(L, 1);
|
|
RB_SCREENS(L, 2, set_background, background);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_get_background)
|
|
{
|
|
unsigned result = RB_SCREENS(L, 1, get_background);
|
|
lua_pushinteger(L, result);
|
|
return 1;
|
|
}
|
|
|
|
#endif /* LCD_DEPTH > 1 */
|
|
|
|
#if LCD_DEPTH == 16
|
|
RB_WRAP(lcd_bitmap_transparent_part)
|
|
{
|
|
struct rocklua_image *src = rli_checktype(L, 1);
|
|
int src_x = luaL_checkint(L, 2);
|
|
int src_y = luaL_checkint(L, 3);
|
|
int stride = luaL_checkint(L, 4);
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 5, &x, &y, &width, &height);
|
|
|
|
RB_SCREENS(L, 9, transparent_bitmap_part, src->data, src_x,
|
|
src_y, stride, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_bitmap_transparent)
|
|
{
|
|
struct rocklua_image *src = rli_checktype(L, 1);
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 2, &x, &y, &width, &height);
|
|
|
|
RB_SCREENS(L, 6, transparent_bitmap, src->data, x, y, width, height);
|
|
return 0;
|
|
}
|
|
#endif /* LCD_DEPTH == 16 */
|
|
|
|
RB_WRAP(lcd_update_rect)
|
|
{
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 1, &x, &y, &width, &height);
|
|
RB_SCREENS(L, 5, update_rect, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_drawrect)
|
|
{
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 1, &x, &y, &width, &height);
|
|
RB_SCREENS(L, 5, drawrect, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_fillrect)
|
|
{
|
|
int x, y, width, height;
|
|
get_rect_bounds(L, 1, &x, &y, &width, &height);
|
|
RB_SCREENS(L, 5, fillrect, x, y, width, height);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_drawline)
|
|
{
|
|
int x1, y1, x2, y2;
|
|
get_rect_bounds(L, 1, &x1, &y1, &x2, &y2);
|
|
RB_SCREENS(L, 5, drawline, x1, y1, x2, y2);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_hline)
|
|
{
|
|
int x1 = (int) luaL_checkint(L, 1);
|
|
int x2 = (int) luaL_checkint(L, 2);
|
|
int y = (int) luaL_checkint(L, 3);
|
|
RB_SCREENS(L, 4, hline, x1, x2, y);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_vline)
|
|
{
|
|
int x = (int) luaL_checkint(L, 1);
|
|
int y1 = (int) luaL_checkint(L, 2);
|
|
int y2 = (int) luaL_checkint(L, 3);
|
|
RB_SCREENS(L, 4, vline, x, y1, y2);
|
|
return 0;
|
|
}
|
|
|
|
RB_WRAP(lcd_drawpixel)
|
|
{
|
|
int x = (int) luaL_checkint(L, 1);
|
|
int y = (int) luaL_checkint(L, 2);
|
|
RB_SCREENS(L, 3, drawpixel, x, y);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* defined(LCD_BITMAP) */
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
RB_WRAP(lcd_rgbpack)
|
|
{
|
|
int r = luaL_checkint(L, 1);
|
|
int g = luaL_checkint(L, 2);
|
|
int b = luaL_checkint(L, 3);
|
|
int result = LCD_RGBPACK(r, g, b);
|
|
lua_pushinteger(L, result);
|
|
return 1;
|
|
}
|
|
|
|
RB_WRAP(lcd_rgbunpack)
|
|
{
|
|
int rgb = luaL_checkint(L, 1);
|
|
lua_pushinteger(L, RGB_UNPACK_RED(rgb));
|
|
lua_pushinteger(L, RGB_UNPACK_GREEN(rgb));
|
|
lua_pushinteger(L, RGB_UNPACK_BLUE(rgb));
|
|
return 3;
|
|
}
|
|
#endif
|
|
|
|
RB_WRAP(read_bmp_file)
|
|
{
|
|
struct bitmap bm;
|
|
const char* filename = luaL_checkstring(L, 1);
|
|
bool dither = luaL_optboolean(L, 2, true);
|
|
bool transparent = luaL_optboolean(L, 3, false);
|
|
int format = FORMAT_NATIVE;
|
|
|
|
if(dither)
|
|
format |= FORMAT_DITHER;
|
|
|
|
if(transparent)
|
|
format |= FORMAT_TRANSPARENT;
|
|
|
|
int result = rb->read_bmp_file(filename, &bm, 0, format | FORMAT_RETURN_SIZE, NULL);
|
|
|
|
if(result > 0)
|
|
{
|
|
bm.data = (unsigned char*) rli_alloc(L, bm.width, bm.height);
|
|
if(rb->read_bmp_file(filename, &bm, result, format, NULL) < 0)
|
|
{
|
|
/* Error occured, drop newly allocated image from stack */
|
|
lua_pop(L, 1);
|
|
lua_pushnil(L);
|
|
}
|
|
}
|
|
else
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define R(NAME) {#NAME, rock_##NAME}
|
|
static const luaL_Reg rocklib_img[] =
|
|
{
|
|
/* Graphics */
|
|
R(lcd_update),
|
|
R(lcd_clear_display),
|
|
R(lcd_set_drawmode),
|
|
R(lcd_putsxy),
|
|
R(lcd_puts),
|
|
R(lcd_puts_scroll),
|
|
R(lcd_scroll_stop),
|
|
R(set_viewport),
|
|
R(clear_viewport),
|
|
R(font_getstringsize),
|
|
#ifdef HAVE_LCD_BITMAP
|
|
R(lcd_framebuffer),
|
|
R(lcd_setfont),
|
|
R(lcd_mono_bitmap_part),
|
|
R(lcd_mono_bitmap),
|
|
#if LCD_DEPTH > 1
|
|
R(lcd_get_backdrop),
|
|
R(lcd_bitmap_part),
|
|
R(lcd_bitmap),
|
|
R(lcd_set_foreground),
|
|
R(lcd_get_foreground),
|
|
R(lcd_set_background),
|
|
R(lcd_get_background),
|
|
#endif
|
|
#if LCD_DEPTH == 16
|
|
R(lcd_bitmap_transparent_part),
|
|
R(lcd_bitmap_transparent),
|
|
#endif
|
|
R(lcd_update_rect),
|
|
R(lcd_drawrect),
|
|
R(lcd_fillrect),
|
|
R(lcd_drawline),
|
|
R(lcd_hline),
|
|
R(lcd_vline),
|
|
R(lcd_drawpixel),
|
|
|
|
#endif /*HAVE_LCD_BITMAP*/
|
|
#ifdef HAVE_LCD_COLOR
|
|
R(lcd_rgbpack),
|
|
R(lcd_rgbunpack),
|
|
#endif
|
|
R(read_bmp_file),
|
|
{"new_image", rli_new},
|
|
|
|
{NULL, NULL}
|
|
};
|
|
#undef R
|
|
|
|
LUALIB_API int luaopen_rock_img(lua_State *L)
|
|
{
|
|
/* some devices need x | y coords shifted to match native format */
|
|
/* conversion between packed native formats and individual pixel addressing */
|
|
init_pixelmask(&x_shift, &y_shift, &xy_mask, &pixelmask);
|
|
|
|
luaL_newmetatable(L, ROCKLUA_IMAGE);
|
|
|
|
lua_pushvalue(L, -1); /* pushes the metatable */
|
|
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
|
|
luaL_register(L, NULL, rli_lib); /*add rli_lib to the image metatable*/
|
|
|
|
luaL_register(L, LUA_ROCKLIBNAME, rocklib_img);
|
|
|
|
return 1;
|
|
}
|
|
|