rockbox/apps/plugins/lua/rocklib_img.c
William Wilgus 948984309a lua move rocklib_img to its own separate loadable module
allows rocklib_img to be excluded if needed

stops rocklib_aux from generating redundant prototypes for
lcd_mono_bitmap[_part]

Change-Id: Ie208ad71ab5f9a7deb026dc01a5b0a0631a0d29c
2018-10-22 14:00:58 -04:00

1432 lines
40 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 int 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;
int ret = 0;
if (!lua_isfunction(L, -1))
return ret; /* error */
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)
{
ret |= 1;
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))
{
ret |= 2;
src = FB_SCALARPACK((unsigned) lua_tointeger(L, -1));
data_set(ss->elem, ss->x, ss->y, &src);
}
lua_pop(L, 2);
return ret; /* 0 signals iterator to stop */
} /* custom_transform */
/* Pre defined pixel manipulations through rli_copy */
static int 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);
return 1;
} /* blit_transform */
static int 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);
return 1;
} /* invert_transform */
static int 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);
return 1;
} /* 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;
int (*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
{
if(!(*rli_trans)(L, ds, NULL, 0, &clr))
break; /* Custom op can quit early */
} 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;
int (*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
{
if(!(*rli_trans)(L, &ds, &ss, op, &clr))
break; /* Custom op can quit early */
} 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)
#ifdef HAVE_LCD_BITMAP
RB_WRAP(lcd_framebuffer)
{
rli_wrap(L, rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT);
return 1;
}
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 = luaL_checkint(L, 5);
int y = luaL_checkint(L, 6);
int width = luaL_checkint(L, 7);
int height = luaL_checkint(L, 8);
int screen = luaL_optint(L, 9, SCREEN_MAIN);
rb->screens[screen]->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 = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3);
int width = luaL_checkint(L, 4);
int height = luaL_checkint(L, 5);
int screen = luaL_optint(L, 6, SCREEN_MAIN);
rb->screens[screen]->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 = luaL_checkint(L, 5);
int y = luaL_checkint(L, 6);
int width = luaL_checkint(L, 7);
int height = luaL_checkint(L, 8);
int screen = luaL_optint(L, 9, SCREEN_MAIN);
rb->screens[screen]->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 = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3);
int width = luaL_checkint(L, 4);
int height = luaL_checkint(L, 5);
int screen = luaL_optint(L, 6, SCREEN_MAIN);
rb->screens[screen]->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;
}
#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 = luaL_checkint(L, 5);
int y = luaL_checkint(L, 6);
int width = luaL_checkint(L, 7);
int height = luaL_checkint(L, 8);
int screen = luaL_optint(L, 9, SCREEN_MAIN);
rb->screens[screen]->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 = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3);
int width = luaL_checkint(L, 4);
int height = luaL_checkint(L, 5);
int screen = luaL_optint(L, 6, SCREEN_MAIN);
rb->screens[screen]->transparent_bitmap(src->data, x, y, width, height);
return 0;
}
#endif /* LCD_DEPTH == 16 */
#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 */
#ifdef HAVE_LCD_BITMAP
R(lcd_framebuffer),
R(lcd_mono_bitmap_part),
R(lcd_mono_bitmap),
#if LCD_DEPTH > 1
R(lcd_get_backdrop),
R(lcd_bitmap_part),
R(lcd_bitmap),
#endif
#if LCD_DEPTH == 16
R(lcd_bitmap_transparent_part),
R(lcd_bitmap_transparent),
#endif
#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, LUA_ROCKLIBNAME, rocklib_img);
luaL_register(L, NULL, rli_lib);
return 1;
}