A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 Dan Everton (safetydan)
11 * Copyright (C) 2009 Maurus Cuelenaere
12 * Copyright (C) 2017 William Wilgus
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24#define lrockimg_c
25#define LUA_LIB
26#define ICON_PADDING_S "1"
27
28
29
30#include "lua.h"
31#include "lauxlib.h"
32#include "rocklib.h"
33#include "rocklib_img.h"
34
35/*
36 * -----------------------------------------------------------------------------
37 *
38 * Rockbox Lua image wrapper
39 *
40 * Some devices(1-bit / 2-bit displays) have packed bit formats that
41 * need to be unpacked in order to work on them at a pixel level.
42 *
43 * The internal formats of these devices do not follow the same paradigm
44 * for image sizes either; We still display the actual width and height to
45 * the user but store stride based on the native values
46 *
47 * Conversion between native addressing and per pixel addressing
48 * incurs extra overhead but it is much faster to do it
49 * on the 'C' side rather than in lua.
50 *
51 * -----------------------------------------------------------------------------
52 */
53
54#define ROCKLUA_IMAGE LUA_ROCKLIBNAME ".image"
55
56/* mark for RLI to LUA Interface functions (luaState *L) is the only argument */
57#define RLI_LUA static int
58
59#ifndef ABS
60#define ABS(a)(((a) < 0) ? - (a) :(a))
61#endif
62
63struct rocklua_image
64{
65 int width;
66 int height;
67 int stride;
68 size_t elems;
69 fb_data *data;
70 fb_data dummy[1][1];
71};
72
73/* holds iterator data for rlimages */
74struct rli_iter_d
75{
76 int x , y;
77 int x1, y1;
78 int x2, y2;
79 int dx, dy;
80 fb_data *elem;
81 struct rocklua_image *img;
82};
83
84/* viewport for rliimages to use rb functions */
85static struct viewport img_vp =
86{
87 .x = 0,
88 .y = 0,
89 .width = 0,
90 .height = 0,
91 .font = FONT_UI,
92 .drawmode = DRMODE_SOLID,
93#if LCD_DEPTH > 1
94 .fg_pattern = LCD_WHITE,
95 .bg_pattern = LCD_BLACK,
96#endif
97};
98
99static void *img_address_fn(int x, int y)
100{
101/* Address lookup function
102 * core will use this to get an address from x/y coord
103 * depending on the lcd function core sometimes uses this for
104 * only the first and last address
105 * and handles subsequent address based on stride */
106
107 struct frame_buffer_t *fb = img_vp.buffer;
108/* LCD_STRIDEFORMAT & LCD_NATIVE_STRIDE macros allow Horiz screens to work with RB */
109#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
110 size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
111#else
112 size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
113#endif
114 /* use mod fb->elems to protect from buffer ovfl */
115 return fb->fb_ptr + (element % fb->elems);
116}
117/* sets an image into a vp to be used by rockbox image functions */
118static void img_set_as_vp(struct rocklua_image *img, struct viewport *vp)
119{
120 int w = img->width;
121 int h = img->height;
122 vp->x = 0;
123 vp->y = 0;
124 vp->width = w;
125 vp->height = h;
126
127 static struct frame_buffer_t fb;/* warning passed to external fns */
128 fb.elems = LCD_NBELEMS(w, h); /* recalculate these rb expects num pixels */
129 fb.stride = STRIDE_MAIN(w, h); /* recalculate these */
130 fb.data = img->data;
131 fb.get_address_fn = &img_address_fn;
132 rb->viewport_set_buffer(vp, &fb, SCREEN_MAIN); /* not multiscreen aware yet */
133}
134
135/* __tostring information enums */
136enum rli_info {RLI_INFO_ALL = 0, RLI_INFO_TYPE, RLI_INFO_WIDTH,
137 RLI_INFO_HEIGHT, RLI_INFO_ELEMS, RLI_INFO_BYTES,
138 RLI_INFO_DEPTH, RLI_INFO_FORMAT, RLI_INFO_ADDRESS};
139
140#ifdef HAVE_LCD_COLOR
141
142static inline fb_data invert_color(fb_data rgb)
143{
144 uint8_t r = 0xFFU - FB_UNPACK_RED(rgb);
145 uint8_t g = 0xFFU - FB_UNPACK_GREEN(rgb);
146 uint8_t b = 0xFFU - FB_UNPACK_BLUE(rgb);
147
148 return FB_RGBPACK(r, g, b);
149}
150#else /* !HAVE_LCD_COLOR */
151
152#define invert_color(c) (~c)
153
154#endif /* HAVE_LCD_COLOR */
155
156
157#if (LCD_DEPTH > 2) /* no native to pixel mapping needed */
158
159#define pixel_to_fb(x, y, o, n) {(void) x; (void) y; do { } while (0);}
160#define pixel_to_native(x, y, xn, yn) {*xn = x; *yn = y;}
161#define init_pixelmask(x, y, m, p) do { } while (0)
162
163
164#else /* some devices need x | y coords shifted to match native format */
165
166static fb_data x_shift = FB_SCALARPACK(0);
167static fb_data y_shift = FB_SCALARPACK(0);
168static fb_data xy_mask = FB_SCALARPACK(0);
169static const fb_data *pixelmask = NULL;
170
171/* conversion between packed native formats and individual pixel addressing */
172static inline void init_pixelmask(fb_data *x_shift, fb_data *y_shift,
173 fb_data *xy_mask, const fb_data **pixelmask)
174{
175
176#if(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 1
177 static const fb_data pixelmask_v1[8] =
178 {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
179 *pixelmask = pixelmask_v1;
180
181 (void) x_shift;
182 *y_shift = 3U;
183 *xy_mask = ((1 << (*y_shift)) - 1);
184#elif(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 2
185 static const fb_data pixelmask_v2[4] = {0x03, 0x0C, 0x30, 0xC0};
186 *pixelmask = pixelmask_v2;
187
188 (void) x_shift;
189 *y_shift = 2U;
190 *xy_mask = ((1 << (*y_shift)) - 1);
191#elif(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2
192 static const fb_data pixelmask_vi2[8] =
193 {0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080};
194 *pixelmask = pixelmask_vi2;
195
196 (void) x_shift;
197 *y_shift = 3U;
198 *xy_mask = ((1 << (*y_shift)) - 1);
199#elif(LCD_PIXELFORMAT == HORIZONTAL_PACKING) && LCD_DEPTH == 2
200 /* MSB on left */
201 static const fb_data pixelmask_h2[4] = {0x03, 0x0C, 0x30, 0xC0};
202 *pixelmask = pixelmask_h2;
203
204 (void) y_shift;
205 *x_shift = 2U;
206 *xy_mask = ((1 << (*x_shift)) - 1);
207#else
208 #warning Unknown Pixel Format
209#endif /* LCD_PIXELFORMAT */
210
211} /* init_pixelmask */
212
213static inline void pixel_to_native(int x, int y, int *x_native, int *y_native)
214{
215 *x_native = ((x - 1) >> x_shift) + 1;
216 *y_native = ((y - 1) >> y_shift) + 1;
217} /* pixel_to_native */
218
219static inline fb_data set_masked_pixel(fb_data old,
220 fb_data new,
221 fb_data mask,
222 int bit_n)
223{
224 /*equivalent of: (old & (~mask)) | ((new << bit_n) & mask);*/
225 return old ^ ((old ^ (new << bit_n)) & mask);
226
227} /* set_masked_pixel */
228
229static inline fb_data get_masked_pixel(fb_data val, fb_data mask, int bit_n)
230{
231 val = val & mask;
232 return val >> bit_n;
233} /* get_masked_pixel */
234
235/* conversion between packed native formats and individual pixel addressing */
236static void pixel_to_fb(int x, int y, fb_data *oldv, fb_data *newv)
237{
238 fb_data mask;
239 int bit_n;
240
241#if(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2
242 (void) x;
243 const uint16_t greymap_vi2[4] = {0x0000, 0x0001, 0x0100, 0x0101};
244
245 bit_n = (y - 1) & xy_mask;
246 mask = pixelmask[bit_n];
247
248 *newv = greymap_vi2[*newv &(0x3)]; /* [0-3] => greymap */
249 *newv = set_masked_pixel(*oldv, *newv, mask, bit_n);
250
251 *oldv = get_masked_pixel(*oldv, mask, bit_n);
252
253 if((*oldv) > 1) /* greymap => [0-3] */
254 *oldv = ((*oldv) & 0x1U) + 2U; /* 2, 3 */
255 else
256 *oldv &= 1U; /* 0, 1 */
257
258#elif(LCD_DEPTH <= 2)
259 if(y_shift)
260 bit_n = (y - 1) & xy_mask;
261 else if(x_shift)
262 bit_n = xy_mask - ((x - 1) & xy_mask); /*MSB on left*/
263
264 if(y_shift || x_shift)
265 {
266 mask = pixelmask[bit_n];
267 bit_n *= LCD_DEPTH;
268
269 *newv = set_masked_pixel(*oldv, *newv, mask, bit_n);
270
271 *oldv = get_masked_pixel(*oldv, mask, bit_n);
272 }
273#else
274 #error Unknown Pixel Format
275#endif /* LCD_PIXELFORMAT == VERTICAL_INTERLEAVED && LCD_DEPTH == 2 */
276} /* pixel_to_fb */
277
278#endif /* (LCD_DEPTH > 2) no native to pixel mapping needed */
279
280/* Internal worker functions for image data array *****************************/
281static inline fb_data lua_to_fbscalar(lua_State *L, int narg)
282{
283 lua_Integer luaint = lua_tointeger(L, narg);
284 fb_data val = FB_SCALARPACK((unsigned) luaint);
285 return val;
286}
287
288static inline void swap_int(bool swap, int *v1, int *v2)
289{
290 if(swap)
291 {
292 int val = *v1;
293 *v1 = *v2;
294 *v2 = val;
295 }
296} /* swap_int */
297
298/* Throws error if x or y are out of bounds notifies user which narg indice
299 the out of bound variable originated */
300static void bounds_check_xy(lua_State *L, struct rocklua_image *img,
301 int nargx, int x, int nargy, int y)
302{
303 int narg;
304
305 if(x > img->width || x < 1)
306 narg = nargx;
307 else if(y <= img->height && y > 0)
308 return; /* note -- return if no error */
309 else
310 narg = nargy;
311
312 luaL_argerror(L, narg, ERR_IDX_RANGE);
313} /* bounds_check_xy */
314
315static struct rocklua_image* rli_checktype_opt(lua_State *L, int arg)
316{
317#if 0
318 return (struct rocklua_image*) luaL_checkudata(L, arg, ROCKLUA_IMAGE);
319#else /* cache result */
320 static struct rocklua_image* last = NULL;
321 void *ud = lua_touserdata(L, arg);
322
323 if(ud != NULL)
324 {
325 if(ud == last)
326 return last;
327 else if (lua_getmetatable(L, arg))
328 { /* does it have a metatable? */
329 luaL_getmetatable(L, ROCKLUA_IMAGE); /* get correct metatable */
330 if (lua_rawequal(L, -1, -2))
331 { /* does it have the correct mt? */
332 lua_pop(L, 2); /* remove both metatables */
333 last = (struct rocklua_image*) ud;
334 return last;
335 }
336 }
337 }
338 /* Not a ROCKLUA IMAGE */
339 return NULL;
340#endif
341} /* rli_checktype_opt*/
342
343static struct rocklua_image* rli_checktype(lua_State *L, int arg)
344{
345 struct rocklua_image *img = rli_checktype_opt(L, arg);
346 if (img == NULL)
347 luaL_typerror(L, arg, ROCKLUA_IMAGE);
348 return img;
349} /* rli_checktype */
350
351static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data,
352 int width, int height)
353{
354 /* rliimage is pushed on the stack it is up to you to pop it */
355 struct rocklua_image *img;
356
357 const size_t sz_header = sizeof(struct rocklua_image);
358 size_t sz_data = 0;
359 size_t n_elems;
360
361 int w_native;
362 int h_native;
363
364 pixel_to_native(width, height, &w_native, &h_native);
365
366 n_elems = (size_t)(w_native * h_native);
367
368 if(alloc_data) /* if this a new image we need space for image data */
369 sz_data = n_elems * sizeof(fb_data);
370
371 /* newuserdata pushes the userdata onto the stack */
372 img = (struct rocklua_image *) lua_newuserdata(L, sz_header + sz_data);
373
374 luaL_getmetatable(L, ROCKLUA_IMAGE);
375 lua_setmetatable(L, -2);
376
377 /* apparent w/h is stored but behind the scenes native w/h is used */
378 img->width = width;
379 img->height = height;
380 img->stride = STRIDE_MAIN(w_native, h_native);
381 img->elems = n_elems;
382
383 return img;
384} /* alloc_rlimage */
385
386static inline void rli_wrap(lua_State *L, fb_data *src, int width, int height)
387{
388 /* rliimage is pushed on the stack it is up to you to pop it */
389 struct rocklua_image *a = alloc_rlimage(L, false, width, height);
390
391 a->data = src;
392} /* rli_wrap */
393
394static inline fb_data* rli_alloc(lua_State *L, int width, int height)
395{
396 /* rliimage is pushed on the stack it is up to you to pop it */
397 struct rocklua_image *a = alloc_rlimage(L, true, width, height);
398
399 a->data = &a->dummy[0][0]; /* ref to beginning of alloc'd img data */
400
401 return a->data;
402} /* rli_alloc */
403
404static inline fb_data data_set(fb_data *elem, int x, int y, fb_data *val)
405{
406 fb_data old_val;
407 fb_data new_val;
408
409 if(elem)
410 {
411 old_val = *elem;
412
413 if(val)
414 {
415 new_val = *val;
416 pixel_to_fb(x, y, &old_val, &new_val);
417 *elem = new_val;
418 }
419 else
420 pixel_to_fb(x, y, &old_val, &new_val);
421 }
422 else
423 old_val = FB_SCALARPACK(0);
424
425 return old_val;
426} /* data_set */
427
428static inline fb_data data_get(fb_data *elem, int x, int y)
429{
430 return data_set(elem, x, y, NULL);
431} /* data_get */
432
433static inline fb_data* rli_get_element(struct rocklua_image* img, int x, int y)
434{
435 int stride = img->stride;
436 size_t elements = img->elems;
437 fb_data *data = img->data;
438
439 pixel_to_native(x, y, &x, &y);
440
441#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
442 /* column major address */
443 size_t data_address = (stride * (x - 1)) + (y - 1);
444
445 /* y needs bound between 0 and stride otherwise overflow to prev/next x */
446 if(y <= 0 || y > stride || data_address >= elements)
447 return NULL; /* data overflow */
448#else
449 /* row major address */
450 size_t data_address = (stride * (y - 1)) + (x - 1);
451
452 /* x needs bound between 0 and stride otherwise overflow to prev/next y */
453 if(x <= 0 || x > stride || data_address >= elements)
454 return NULL; /* data overflow */
455#endif
456
457 return &data[data_address]; /* return element address */
458} /* rli_get_element */
459
460/* Lua to C Interface for pixel set and get functions */
461static int rli_setget(lua_State *L, bool is_get, int narg_clip)
462{
463 /*(set) (dst*, [x1, y1, clr, clip]) */
464 /*(get) (dst*, [x1, y1, clip]) */
465 struct rocklua_image *a = rli_checktype(L, 1);
466 int x = lua_tointeger(L, 2);
467 int y = lua_tointeger(L, 3);
468
469 fb_data clr; /* Arg 4 is color if set element */
470
471 fb_data *element = rli_get_element(a, x, y);
472
473 if(!element)
474 {
475 if(!lua_toboolean(L, narg_clip)) /* Error if !clip */
476 bounds_check_xy(L, a, 2, x, 3, y);
477
478 lua_pushnil(L);
479 return 1;
480 }
481
482 if(is_get) /* get element */
483 lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(data_get(element, x, y)));
484 else /* set element */
485 {
486 clr = lua_to_fbscalar(L, 4);
487 lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(data_set(element, x, y, &clr)));
488 }
489
490 /* returns old value */
491 return 1;
492} /* rli_setget */
493
494#ifdef RLI_EXTENDED
495static bool rli_iter_init(struct rli_iter_d *d,
496 struct rocklua_image *img,
497 int x1, int y1,
498 int x2, int y2,
499 int dx, int dy,
500 bool swx, bool swy)
501{
502
503 swap_int((swx), &x1, &x2);
504 swap_int((swy), &y1, &y2);
505
506 /* stepping in the correct x direction ? */
507 if((dx ^ (x2 - x1)) < 0)
508 dx = -dx;
509
510 /* stepping in the correct y direction ? */
511 if((dy ^ (y2 - y1)) < 0)
512 dy = -dy;
513
514 d->img = img;
515 d->x = x1;
516 d->y = y1;
517 d->x1 = x1;
518 d->y1 = y1;
519 d->x2 = x2;
520 d->y2 = y2;
521 d->dx = dx;
522 d->dy = dy;
523 d->elem = rli_get_element(img, d->x, d->y);
524
525 return(dx != 0 || dy != 0);
526} /* rli_iter_init */
527
528static struct rli_iter_d * rli_iter_create(lua_State *L)
529{
530 struct rocklua_image *a = rli_checktype(L, 1);
531 int x1 = luaL_optint(L, 2, 1);
532 int y1 = luaL_optint(L, 3, 1);
533 int x2 = luaL_optint(L, 4, a->width);
534 int y2 = luaL_optint(L, 5, a->height);
535 int dx = luaL_optint(L, 6, 1);
536 int dy = luaL_optint(L, 7, 1);
537 bool clip = lua_toboolean(L, 8);
538
539 if(!clip)
540 {
541 bounds_check_xy(L, a, 2, x1, 3, y1);
542 bounds_check_xy(L, a, 4, x2, 5, y2);
543 }
544
545 struct rli_iter_d *ds;
546 /* create new iter + pushed onto stack */
547 ds = (struct rli_iter_d *) lua_newuserdata(L, sizeof(struct rli_iter_d));
548
549 rli_iter_init(ds, a, x1, y1, x2, y2, dx, dy, false, false);
550
551 return ds;
552}
553
554/* steps to the next point(x, y) by delta x/y, stores pointer to element
555 returns true if x & y haven't reached x2 & y2
556 if limit reached - element set to NULL, deltas set to 0 & false returned
557*/
558static bool next_rli_iter(struct rli_iter_d *d)
559{
560 if((d->dx > 0 && d->x < d->x2) || (d->dx < 0 && d->x > d->x2))
561 d->x += d->dx;
562 else if((d->dy > 0 && d->y < d->y2) || (d->dy < 0 && d->y > d->y2))
563 {
564 d->x = d->x1; /* Reset x*/
565 d->y += d->dy;
566 }
567 else
568 {
569 d->elem = NULL;
570 d->dx = 0;
571 d->dy = 0;
572 return false;
573 }
574
575 d->elem = rli_get_element(d->img, d->x, d->y);
576
577 return true;
578} /* next_rli_iter */
579
580#if 0
581static void d_line(struct rocklua_image *img,
582 int x1, int y1,
583 int x2, int y2,
584 fb_data *clr)
585{
586 /* NOTE! clr passed as pointer */
587 /* Bresenham midpoint line algorithm */
588 fb_data *element;
589
590 int r_a = x2 - x1; /* range of x direction called 'a' for now */
591 int r_b = y2 - y1; /* range of y direction called 'b' for now */
592
593 int s_a = (r_a > 0) - (r_a < 0); /* step of a direction -1, 0, 1 */
594 int s_b = (r_b > 0) - (r_b < 0); /* step of b direction -1, 0, 1 */
595
596 int d_err;
597 int numpixels;
598
599 int *a1 = &x1; /* pointer to the x var 'a' */
600 int *b1 = &y1; /* pointer to the y var 'b' */
601
602 r_a = ABS(r_a);/* instead of negative range we switch step */
603 r_b = ABS(r_b);/* instead of negative range we switch step */
604
605 if(r_b > r_a) /*if rangeY('b') > rangeX('a') swap their identities */
606 {
607 a1 = &y1; /* pointer to the y var 'a' */
608 b1 = &x1; /* pointer to the x var 'b' */
609 swap_int((true), &r_a, &r_b);
610 swap_int((true), &s_a, &s_b);
611 }
612
613 d_err = ((r_b << 1) - r_a) >> 1; /* preload err of 1 step (px centered) */
614
615 /* add 1 extra point to make the whole line */
616 numpixels = r_a + 1;
617
618 r_a -= r_b; /* pre-subtract 'a' - 'b' */
619
620 for(; numpixels > 0; numpixels--)
621 {
622 element = rli_get_element(img, x1, y1);
623 data_set(element, x1, y1, clr);
624
625 if(d_err >= 0) /* 0 is our target midpoint(exact point on the line) */
626 {
627 *b1 += s_b; /* whichever axis is in 'b' stepped(-1 or +1) */
628 d_err -= r_a;
629 }
630 else
631 d_err += r_b; /* only add 'b' when d_err < 0 */
632
633 *a1 += s_a; /* whichever axis is in 'a' stepped(-1 or +1) */
634 }
635
636} /* d_line */
637#endif
638
639
640static void d_line(struct rocklua_image *img,
641 int x1, int y1,
642 int x2, int y2,
643 fb_data *clr)
644{
645 /* NOTE! clr passed as pointer */
646#if LCD_DEPTH == 2
647 img_vp.fg_pattern = 0x55 * (~(*clr) & 3);
648 img_vp.drawmode = DRMODE_FG;
649#elif LCD_DEPTH > 1
650 img_vp.fg_pattern = FB_UNPACK_SCALAR_LCD(*clr);
651 img_vp.drawmode = DRMODE_FG;
652#else /* bit of a hack to make sure lines show properly from lua */
653 /* use rb.lcd_drawline if you want full control */
654
655 img_vp.drawmode = *clr & (DRMODE_SOLID|DRMODE_INVERSEVID);
656 if (img_vp.drawmode != (DRMODE_SOLID|DRMODE_INVERSEVID))
657 img_vp.drawmode = DRMODE_SOLID;
658#endif
659
660 img_set_as_vp(img, &img_vp);
661
662 struct viewport *oldvp = rb->screens[SCREEN_MAIN]->set_viewport(&img_vp);
663
664 rb->lcd_drawline(x1 - 1, y1 - 1 , x2 - 1, y2 - 1);
665
666 rb->screens[SCREEN_MAIN]->set_viewport(oldvp);
667
668} /* d_line */
669
670/* ellipse worker function */
671static void d_ellipse_elements(struct rocklua_image * img,
672 int x1, int y1,
673 int x2, int y2,
674 fb_data *clr,
675 fb_data *fillclr)
676{
677 fb_data *element;
678
679 if(fillclr)
680 {
681 d_line(img, x1, y1, x2, y1, fillclr); /* I. II.*/
682 d_line(img, x1, y2, x2, y2, fillclr); /* III.IV.*/
683 }
684
685 element = rli_get_element(img, x2, y1);
686 data_set(element, x2, y1, clr); /* I. Quadrant +x +y */
687
688 element = rli_get_element(img, x1, y1);
689 data_set(element, x1, y1, clr); /* II. Quadrant -x +y */
690
691 element = rli_get_element(img, x1, y2);
692 data_set(element, x1, y2, clr); /* III. Quadrant -x -y */
693
694 element = rli_get_element(img, x2, y2);
695 data_set(element, x2, y2, clr); /* IV. Quadrant +x -y */
696
697} /* d_ellipse_elements */
698
699static void d_ellipse(struct rocklua_image *img,
700 int x1, int y1,
701 int x2, int y2,
702 fb_data *clr,
703 fb_data *fillclr)
704{
705 /* NOTE! clr and fillclr passed as pointers */
706 /* Rasterizing algorithm derivative of work by Alois Zingl */
707#if (LCD_WIDTH > 1024 || LCD_HEIGHT > 1024) && defined(INT64_MAX)
708 /* Prevents overflow on large screens */
709 int64_t dx, dy, err, e1;
710#else
711 int32_t dx, dy, err, e1;
712#endif
713 /* if called with swapped points .. exchange them */
714 swap_int((x1 > x2), &x1, &x2);
715 swap_int((y1 > y2), &y1, &y2);
716
717 int a = x2 - x1; /* diameter */
718 int b = y2 - y1; /* diameter */
719
720 if(a == 0 || b == 0)
721 return; /* not an error but nothing to display */
722
723 int b1 = (b & 1);
724 b = b - (1 - b1);
725
726 int a2 = (a * a);
727 int b2 = (b * b);
728
729 dx = ((1 - a) * b2) >> 1; /* error increment */
730 dy = (b1 * a2) >> 1; /* error increment */
731
732 err = dx + dy + b1 * a2; /* error of 1.step */
733
734 y1 += (b + 1) >> 1;
735 y2 = y1 - b1;
736
737 do
738 {
739 d_ellipse_elements(img, x1, y1, x2, y2, clr, fillclr);
740
741 e1 = err;
742
743 if(e1 <= (dy >> 1)) /* target midpoint - y step */
744 {
745 y1++;
746 y2--;
747 dy += a2;
748 err += dy;
749 }
750
751 if(e1 >= (dx >> 1) || err > (dy >> 1)) /* target midpoint - x step */
752 {
753 x1++;
754 x2--;
755 dx += b2;
756 err += dx;
757 }
758
759 } while(x1 <= x2);
760
761 if (fillclr && x1 - x2 <= 2)
762 fillclr = clr;
763
764 while (y1 - y2 < b) /* early stop of flat ellipse a=1 finish tip */
765 {
766 d_ellipse_elements(img, x1, y1, x2, y2, clr, fillclr);
767
768 y1++;
769 y2--;
770 }
771
772} /* d_ellipse */
773
774/* Lua to C Interface for line and ellipse */
775static int rli_line_ellipse(lua_State *L, bool is_ellipse, int narg_clip)
776{
777 struct rocklua_image *a = rli_checktype(L, 1);
778
779 int x1 = luaL_checkint(L, 2);
780 int y1 = luaL_checkint(L, 3);
781 int x2 = luaL_optint(L, 4, x1);
782 int y2 = luaL_optint(L, 5, y1);
783
784 fb_data clr = lua_to_fbscalar(L, 6);
785
786 fb_data fillclr; /* fill color is index 7 if is_ellipse */
787 fb_data *p_fillclr = NULL;
788
789 bool clip = lua_toboolean(L, narg_clip);
790
791 if(!clip)
792 {
793 bounds_check_xy(L, a, 2, x1, 3, y1);
794 bounds_check_xy(L, a, 4, x2, 5, y2);
795 }
796
797 if(is_ellipse)
798 {
799 if(lua_type(L, 7) == LUA_TNUMBER)
800 {
801 fillclr = lua_to_fbscalar(L, 7);
802 p_fillclr = &fillclr;
803 }
804
805 d_ellipse(a, x1, y1, x2, y2, &clr, p_fillclr);
806 }
807 else
808 d_line(a, x1, y1, x2, y2, &clr);
809
810 return 0;
811} /* rli_line_ellipse */
812
813static inline int rli_pushpixel(lua_State *L, fb_data color, int x, int y)
814{
815 lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(color));
816 lua_pushinteger(L, x);
817 lua_pushinteger(L, y);
818 return 3;
819}
820
821/* User defined pixel manipulations through rli_copy, rli_marshal */
822static void custom_transform(lua_State *L,
823 struct rli_iter_d *ds,
824 struct rli_iter_d *ss,
825 int op,
826 fb_data *color)
827{
828 (void) color;
829 (void) op;
830 fb_data dst;
831 fb_data src;
832
833 int params;
834 bool done = true;
835
836 if (true)/*(lua_isfunction(L, -1))*/
837 {
838 lua_pushvalue(L, -1); /* make a copy of the lua function */
839
840 dst = data_get(ds->elem, ds->x, ds->y);
841 params = rli_pushpixel(L, dst, ds->x, ds->y);
842
843 if(ss) /* Allows src to be omitted */
844 {
845 src = data_get(ss->elem, ss->x, ss->y);
846 params += rli_pushpixel(L, src, ss->x, ss->y);
847 }
848
849 lua_call(L, params, 2); /* call custom function w/ n-params & 2 ret */
850
851 if(lua_type(L, -2) == LUA_TNUMBER)
852 {
853 done = false;
854 dst = lua_to_fbscalar(L, -2);
855 data_set(ds->elem, ds->x, ds->y, &dst);
856 }
857
858 if(ss && (lua_type(L, -1) == LUA_TNUMBER))
859 {
860 done = false;
861 src = lua_to_fbscalar(L, -1);
862 data_set(ss->elem, ss->x, ss->y, &src);
863 }
864
865 lua_pop(L, 2);
866 }
867
868 if(done) /* signal iter to stop */
869 {
870 ds->dx = 0;
871 ds->dy = 0;
872 }
873} /* custom_transform */
874
875/* Pre defined pixel manipulations through rli_copy */
876static void blit_transform(lua_State *L,
877 struct rli_iter_d *ds,
878 struct rli_iter_d *ss,
879 int op,
880 fb_data *color)
881{
882 (void) L;
883 unsigned clr = FB_UNPACK_SCALAR_LCD(*color);
884 unsigned dst = FB_UNPACK_SCALAR_LCD(data_get(ds->elem, ds->x, ds->y));
885 unsigned src;
886
887 /* Reuse 0 - 7 for src / clr blits*/
888 if(op >= 30 && op <= 37)
889 {
890 op -= 30;
891 src = clr;
892 }
893 else
894 src = FB_UNPACK_SCALAR_LCD(data_get(ss->elem, ss->x, ss->y));
895
896 switch(op)
897 {
898 default:
899 /* case 30: */
900 case 0: { dst = src; break; }/* copyS/C */
901 /* case 31: */
902 case 1: { dst = src | dst; break; }/* DorS/C */
903 /* case 32: */
904 case 2: { dst = src ^ dst; break; }/* DxorS/C */
905 /* case 33: */
906 case 3: { dst = ~(src | dst); break; }/* nDorS/C */
907 /* case 34: */
908 case 4: { dst = (~src) | dst; break; }/* DornS/C */
909 /* case 35: */
910 case 5: { dst = src & dst; break; }/* DandS/C */
911 /* case 36: */
912 case 6: { dst = src & (~dst); break; }/* nDandS/C */
913 /* case 37: */
914 case 7: { dst = ~src; break; }/* notS/C */
915
916 /* mask blits */
917 case 8: { if(src != 0) { dst = clr; } break; }/* Sand */
918 case 9: { if(src == 0) { dst = clr; } break; }/* Snot */
919
920 case 10: { dst = src | clr; break; }/* SorC */
921 case 11: { dst = src ^ clr; break; }/* SxorC */
922 case 12: { dst = ~(src | clr); break; }/* nSorC */
923 case 13: { dst = src | (~clr); break; }/* SornC */
924 case 14: { dst = src & clr; break; }/* SandC */
925 case 15: { dst = (~src) & clr; break; }/* nSandC */
926 case 16: { dst |= (~src) | clr; break; }/* DornSorC */
927 case 17: { dst ^= (src & (dst ^ clr)); break; }/* DxorSandDxorC */
928
929 case 18: { if(src != clr) { dst = src; } break; }
930 case 19: { if(src == clr) { dst = src; } break; }
931 case 20: { if(src > clr) { dst = src; } break; }
932 case 21: { if(src < clr) { dst = src; } break; }
933
934 case 22: { if(dst != clr) { dst = src; } break; }
935 case 23: { if(dst == clr) { dst = src; } break; }
936 case 24: { if(dst > clr) { dst = src; } break; }
937 case 25: { if(dst < clr) { dst = src; } break; }
938
939 case 26: { if(dst != src) { dst = clr; } break; }
940 case 27: { if(dst == src) { dst = clr; } break; }
941 case 28: { if(dst > src) { dst = clr; } break; }
942 case 29: { if(dst < src) { dst = clr; } break; }
943#if 0
944 /* src unneeded */
945 case 30: { dst = clr; break; }/* copyC */
946 case 31: { dst = clr | dst; break; }/* DorC */
947 case 32: { dst = clr ^ dst; break; }/* DxorC */
948 case 33: { dst = ~(clr | dst); break; }/* nDorC */
949 case 34: { dst = (~clr) | dst; break; }/* DornC */
950 case 35: { dst = clr & dst; break; }/* DandC */
951 case 36: { dst = clr & (~dst); break; }/* nDandC */
952 case 37: { dst = ~clr; break; }/* notC */
953#endif
954
955 }/*switch op*/
956 fb_data val = FB_SCALARPACK(dst);
957 data_set(ds->elem, ds->x, ds->y, &val);
958} /* blit_transform */
959
960static void invert_transform(lua_State *L,
961 struct rli_iter_d *ds,
962 struct rli_iter_d *ss,
963 int op,
964 fb_data *color)
965{
966 (void) L;
967 (void) color;
968 (void) op;
969 (void) ss;
970
971 fb_data val = invert_color(data_get(ds->elem, ds->x, ds->y));
972 data_set(ds->elem, ds->x, ds->y, &val);
973} /* invert_transform */
974
975static void clear_transform(lua_State *L,
976 struct rli_iter_d *ds,
977 struct rli_iter_d *ss,
978 int op,
979 fb_data *color)
980{
981 (void) L;
982 (void) op;
983 (void) ss;
984
985 data_set(ds->elem, ds->x, ds->y, color);
986} /* clear_transform */
987
988#endif /* RLI_EXTENDED */
989
990/* RLI to LUA Interface functions *********************************************/
991RLI_LUA rli_new(lua_State *L)
992{ /* [width, height] */
993 int width = luaL_optint(L, 1, LCD_WIDTH);
994 int height = luaL_optint(L, 2, LCD_HEIGHT);
995
996 luaL_argcheck(L, width > 0 && height > 0, (width <= 0) ? 1 : 2, ERR_IDX_RANGE);
997
998 rli_alloc(L, width, height);
999
1000 return 1;
1001}
1002
1003RLI_LUA rli_set(lua_State *L)
1004{
1005 /*(set) (dst*, [x1, y1, clr, clip]) */
1006 return rli_setget(L, false, 5);
1007}
1008
1009RLI_LUA rli_get(lua_State *L)
1010{
1011 /*(get) (dst*, [x1, y1, clip]) */
1012 return rli_setget(L, true, 4);
1013}
1014
1015RLI_LUA rli_equal(lua_State *L)
1016{
1017 struct rocklua_image *a = rli_checktype(L, 1);
1018 struct rocklua_image *b = rli_checktype(L, 2);
1019 lua_pushboolean(L, a->data == b->data);
1020 return 1;
1021}
1022
1023RLI_LUA rli_height(lua_State *L)
1024{
1025 struct rocklua_image *a = rli_checktype(L, 1);
1026 lua_pushinteger(L, a->height);
1027 return 1;
1028}
1029
1030RLI_LUA rli_width(lua_State *L)
1031{
1032 struct rocklua_image *a = rli_checktype(L, 1);
1033 lua_pushinteger(L, a->width);
1034 return 1;
1035}
1036
1037RLI_LUA rli_size(lua_State *L)
1038{
1039 struct rocklua_image *a = rli_checktype(L, 1);
1040 lua_pushinteger(L, a->elems);
1041 return 1;
1042}
1043
1044RLI_LUA rli_raw(lua_State *L)
1045{
1046 /*val = (img*, index, [new_val]) */
1047 struct rocklua_image *a = rli_checktype(L, 1);
1048
1049 size_t i = (unsigned) lua_tointeger(L, 2);
1050
1051 fb_data val;
1052
1053 luaL_argcheck(L, i > 0 && i <= (a->elems), 2, ERR_IDX_RANGE);
1054
1055 lua_pushinteger(L, FB_UNPACK_SCALAR_LCD(a->data[i-1]));
1056
1057 if(lua_type(L, 3) == LUA_TNUMBER)
1058 {
1059 val = lua_to_fbscalar(L, 3);
1060 a->data[i-1] = val;
1061 }
1062
1063 return 1;
1064}
1065
1066RLI_LUA rli_tostring(lua_State *L)
1067{
1068 /* (img, [infoitem]) */
1069 struct rocklua_image *a = rli_checktype(L, 1);
1070
1071 int item = lua_tointeger(L, 2);
1072 size_t bytes = a->elems * sizeof(fb_data);
1073
1074 switch(item)
1075 {
1076 default:
1077 case RLI_INFO_ALL:
1078 {
1079 lua_pushfstring(L,
1080 ROCKLUA_IMAGE ": %dx%d, %d elements, %d bytes, %d-bit depth, %d pack",
1081 a->width, a->height, a->elems, bytes, LCD_DEPTH, LCD_PIXELFORMAT);
1082 break;
1083 }
1084 case RLI_INFO_TYPE: { lua_pushfstring(L, ROCKLUA_IMAGE); break; }
1085 case RLI_INFO_WIDTH: { lua_pushinteger(L, a->width); break; }
1086 case RLI_INFO_HEIGHT: { lua_pushinteger(L, a->height); break; }
1087 case RLI_INFO_ELEMS: { lua_pushinteger(L, a->elems); break; }
1088 case RLI_INFO_BYTES: { lua_pushinteger(L, bytes); break; }
1089 case RLI_INFO_DEPTH: { lua_pushinteger(L, LCD_DEPTH ); break; }
1090 case RLI_INFO_FORMAT: { lua_pushinteger(L, LCD_PIXELFORMAT); break; }
1091 case RLI_INFO_ADDRESS: { lua_pushfstring(L, "%p", a->data); break; }
1092 }
1093
1094 /* lua_pushstring(L, lua_tostring(L, -1)); */
1095 lua_tostring(L, -1); /* converts item at index to string */
1096
1097 return 1;
1098}
1099
1100#ifdef RLI_EXTENDED
1101RLI_LUA rli_ellipse(lua_State *L)
1102{
1103 /* (dst*, x1, y1, x2, y2, [clr, fillclr, clip]) */
1104 /* line and ellipse share the same init function */
1105 return rli_line_ellipse(L, true, 8);
1106}
1107
1108RLI_LUA rli_line(lua_State *L)
1109{
1110 /* (dst*, x1, y1, [x2, y2, clr, clip]) */
1111 /* line and ellipse share the same init function */
1112 return rli_line_ellipse(L, false, 7);
1113}
1114
1115RLI_LUA rli_iterator(lua_State *L) {
1116 /* see rli_iterator_factory */
1117 int params = 0;
1118 struct rli_iter_d *ds;
1119 ds = (struct rli_iter_d *) lua_touserdata(L, lua_upvalueindex(1));
1120
1121 if(ds->dx != 0 || ds->dy != 0)
1122 {
1123 params = rli_pushpixel(L, data_get(ds->elem, ds->x, ds->y), ds->x, ds->y);
1124
1125 next_rli_iter(ds); /* load next element */
1126 }
1127 return params; /* nothing left to do */
1128}
1129
1130RLI_LUA rli_iterator_factory(lua_State *L)
1131{
1132 /* (points) (img*, [x1, y1, x2, y2, dx, dy, clip]) */
1133 /* (indices 1-8 are used by rli_iter_create) */
1134
1135 /* create new iter + pushed onto stack */
1136 rli_iter_create(L);
1137
1138 /* returns the iter function with embedded iter data(up values) */
1139 lua_pushcclosure(L, &rli_iterator, 1);
1140
1141 return 1;
1142}
1143
1144RLI_LUA rli_marshal(lua_State *L) /* also invert, clear */
1145{
1146 /* (marshal/invert/clear) (img*, [x1, y1, x2, y2, dx, dy, clip, function]) */
1147 /* (indices 1-8 are used by rli_iter_create) */
1148 fb_data clr;
1149
1150 void (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *);
1151 int ltype = lua_type (L, 9);
1152
1153 /* create new iter + pushed onto stack */
1154 struct rli_iter_d *ds = rli_iter_create(L);
1155
1156 if (ltype == LUA_TNUMBER)
1157 {
1158 clr = lua_to_fbscalar(L, 9);
1159 rli_trans = clear_transform;
1160 }
1161 else if(ltype == LUA_TFUNCTION) /* custom function */
1162 {
1163 rli_trans = custom_transform;
1164 lua_pushvalue(L, 9); /* ensure lua function on top of stack */
1165 }
1166 else
1167 rli_trans = invert_transform; /* default transformation */
1168
1169 do
1170 {
1171 (*rli_trans)(L, ds, NULL, 0, &clr);
1172 } while(next_rli_iter(ds));
1173
1174 return 0;
1175}
1176
1177RLI_LUA rli_copy(lua_State *L)
1178{
1179 /* (dst*, src*, [d_x, d_y, s_x, s_y, x_off, y_off, clip, [op, funct/clr]]) */
1180 struct rocklua_image *dst = rli_checktype(L, 1); /* dst */
1181 struct rocklua_image *src = rli_checktype(L, 2); /* src */
1182
1183 struct rli_iter_d ds; /* dst */
1184 struct rli_iter_d ss; /* src */
1185
1186 /* copy whole image if possible */
1187 if(src->elems == dst->elems && src->width == dst->width && lua_gettop(L) < 3)
1188 {
1189 rb->memcpy(dst->data, src->data, dst->elems * sizeof(fb_data));
1190 return 0;
1191 }
1192
1193 int d_x = luaL_optint(L, 3, 1);
1194 int d_y = luaL_optint(L, 4, 1);
1195 int s_x = luaL_optint(L, 5, 1);
1196 int s_y = luaL_optint(L, 6, 1);
1197
1198 int w = MIN(dst->width - d_x, src->width - s_x);
1199 int h = MIN(dst->height - d_y, src->height - s_y);
1200
1201 int x_off = luaL_optint(L, 7, w);
1202 int y_off = luaL_optint(L, 8, h);
1203
1204 bool clip = lua_toboolean(L, 9);
1205 int op; /* 10 is operation for blit */
1206 fb_data clr; /* 11 is custom function | color */
1207
1208 bool d_swx = (x_off < 0); /* dest swap */
1209 bool d_swy = (y_off < 0);
1210 bool s_swx = false; /* src swap */
1211 bool s_swy = false;
1212
1213 void (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *);
1214
1215 if(!clip) /* Out of bounds is not allowed */
1216 {
1217 bounds_check_xy(L, dst, 3, d_x, 4, d_y);
1218 bounds_check_xy(L, src, 5, s_x, 6, s_y);
1219 w = MIN(w, ABS(x_off));
1220 h = MIN(h, ABS(y_off));
1221 bounds_check_xy(L, dst, 7, d_x + w, 8, d_y + h);
1222 bounds_check_xy(L, src, 7, s_x + w, 8, s_y + h);
1223 }
1224 else
1225 {
1226 if (w < 0 || h < 0) /* not an error but nothing to display */
1227 return 0;
1228 w = MIN(w, ABS(x_off));
1229 h = MIN(h, ABS(y_off));
1230 }
1231
1232 /* if src->data == dst->data need to care about fill direction */
1233 if(d_x > s_x)
1234 {
1235 d_swx = !d_swx;
1236 s_swx = !s_swx;
1237 }
1238
1239 if(d_y > s_y)
1240 {
1241 d_swy = !d_swy;
1242 s_swy = !s_swy;
1243 }
1244
1245 rli_iter_init(&ds, dst, d_x, d_y, d_x + w, d_y + h, 1, 1, d_swx, d_swy);
1246
1247 rli_iter_init(&ss, src, s_x, s_y, s_x + w, s_y + h, 1, 1, s_swx, s_swy);
1248
1249 if (lua_type(L, 11) == LUA_TFUNCTION) /* custom function supplied.. */
1250 {
1251 rli_trans = custom_transform;
1252 lua_settop(L, 11); /* ensure lua function on top of stack */
1253 clr = FB_SCALARPACK(0);
1254 op = 0;
1255 }
1256 else
1257 {
1258 rli_trans = blit_transform; /* default transformation */
1259 clr = lua_to_fbscalar(L, 11);
1260 op = lua_tointeger(L, 10);
1261 }
1262
1263 do
1264 {
1265 (*rli_trans)(L, &ds, &ss, op, &clr);
1266 } while(next_rli_iter(&ds) && next_rli_iter(&ss));
1267
1268 return 0;
1269}
1270
1271RLI_LUA rli_clear(lua_State *L)
1272{
1273 /* (clear) (dst*, [color, x1, y1, x2, y2, clip, dx, dy]) */
1274 lua_settop(L, 9);
1275
1276 lua_pushvalue(L, 7); /* clip -- index 8 */
1277 lua_remove(L, 7);
1278
1279 lua_pushinteger(L, lua_tointeger(L, 2)); /*color -- index 9*/
1280 lua_remove(L, 2);
1281
1282 return rli_marshal(L); /* (img*, [x1, y1, x2, y2, dx, dy, clip, function]) */
1283}
1284#endif /* RLI_EXTENDED */
1285
1286/* Rli Image methods exported to lua */
1287static const struct luaL_reg rli_lib [] =
1288{
1289 {"__tostring", rli_tostring},
1290 {"_data", rli_raw},
1291 {"__len", rli_size},
1292 {"__eq", rli_equal},
1293 {"width", rli_width},
1294 {"height", rli_height},
1295 {"set", rli_set},
1296 {"get", rli_get},
1297
1298#ifdef RLI_EXTENDED
1299 {"copy", rli_copy},
1300 {"clear", rli_clear},
1301 {"invert", rli_marshal},
1302 {"marshal", rli_marshal},
1303 {"points", rli_iterator_factory},
1304 {"line", rli_line},
1305 {"ellipse", rli_ellipse},
1306#endif /* RLI_EXTENDED */
1307
1308 {NULL, NULL}
1309};
1310
1311/*
1312 * -----------------------------
1313 *
1314 * Rockbox wrappers start here!
1315 *
1316 * -----------------------------
1317 */
1318
1319#define RB_WRAP(func) static int rock_##func(lua_State UNUSED_ATTR *L)
1320
1321#if defined NB_SCREENS && (NB_SCREENS > 1)
1322#define RB_SCREEN_STRUCT(luastate, narg) \
1323 rb->screens[get_screen(luastate, narg)]
1324#define RB_SCREENS(luastate, narg, func, ...) \
1325 rb->screens[get_screen(luastate, narg)]->func(__VA_ARGS__)
1326
1327static int get_screen(lua_State *L, int narg)
1328{
1329 int screen = luaL_optint(L, narg, SCREEN_MAIN);
1330
1331 if(screen < SCREEN_MAIN)
1332 screen = SCREEN_MAIN;
1333 else if(screen > NB_SCREENS)
1334 screen = NB_SCREENS;
1335
1336 return screen;
1337}
1338#else /* only SCREEN_MAIN exists */
1339#define get_screen(a,b) (SCREEN_MAIN)
1340#define RB_SCREEN_STRUCT(luastate, narg) \
1341 rb->screens[SCREEN_MAIN]
1342#define RB_SCREENS(luastate, narg, func, ...) \
1343 rb->screens[SCREEN_MAIN]->func(__VA_ARGS__)
1344#endif
1345
1346RB_WRAP(lcd_update)
1347{
1348 RB_SCREENS(L, 1, update);
1349 return 0;
1350}
1351
1352RB_WRAP(lcd_clear_display)
1353{
1354 RB_SCREENS(L, 1, clear_display);
1355 return 0;
1356}
1357
1358RB_WRAP(lcd_set_drawmode)
1359{
1360 int previous;
1361 int mode = (int) luaL_checkint(L, 1);
1362 if (rli_checktype_opt(L, 2) != NULL) /* is rliimage? */
1363 {
1364 previous = img_vp.drawmode;
1365 img_vp.drawmode = mode;
1366 }
1367 else
1368 {
1369 struct viewport *vp = *(RB_SCREEN_STRUCT(L, 2)->current_viewport);
1370 previous = vp->drawmode;
1371 RB_SCREENS(L, 2, set_drawmode, mode);
1372 }
1373 lua_pushinteger(L, previous);
1374 return 1;
1375}
1376
1377/* helper function for lcd_puts functions */
1378static const unsigned char * lcd_putshelper(lua_State *L, int *x, int *y)
1379{
1380 *x = (int) luaL_checkint(L, 1);
1381 *y = (int) luaL_checkint(L, 2);
1382 return luaL_checkstring(L, 3);
1383}
1384
1385RB_WRAP(lcd_putsxy)
1386{
1387 int x, y;
1388 const unsigned char *string = lcd_putshelper(L, &x, &y);
1389 RB_SCREENS(L, 4, putsxy, x, y, string);
1390 return 0;
1391}
1392
1393RB_WRAP(lcd_puts)
1394{
1395 int x, y;
1396 const unsigned char * string = lcd_putshelper(L, &x, &y);
1397 RB_SCREENS(L, 4, puts, x, y, string);
1398 return 0;
1399}
1400
1401/* Helper function for opt_viewport lcd_put_line */
1402static int check_tablevalue_def(lua_State *L, const char* key, int tablepos, int def)
1403{
1404 lua_getfield(L, tablepos, key); /* Find table[key] */
1405
1406 int val;
1407
1408 if (lua_isboolean(L, -1))
1409 val = lua_toboolean (L, -1); /*True = 1 and False = 0*/
1410 else
1411 val = luaL_optinteger(L, -1, def);
1412
1413 lua_pop(L, 1); /* Pop the value off the stack */
1414 return val;
1415}
1416
1417/* Helper function for opt_viewport lcd_put_line */
1418static int check_tablevalue(lua_State *L, const char* key, int tablepos)
1419{
1420 /* returns 0 if key doesn't exist */
1421 return check_tablevalue_def(L, key, tablepos, 0);
1422}
1423
1424RB_WRAP(lcd_put_line)
1425{
1426 /*x, y, text, t_linedesc, [screen = MAIN]*/
1427
1428#if 0
1429 /* height of the line (in pixels). -1 to inherit the height
1430 * from the font. The text will be centered if the height is larger,
1431 * but the decorations will span the entire height */
1432 int height;
1433 /* multiline support: For some decorations (e.g. gradient) to work
1434 * across multiple lines (e.g. to draw a line selector across 2 lines)
1435 * the line index and line count must be known. For normal, single
1436 * lines specify nlines=1 and line=0 */
1437 /* line count of a group */
1438 int16_t nlines;
1439 /* index of the line in the group */
1440 int16_t line;
1441 /* line text color if STYLE_COLORED is specified, in native
1442 * lcd format (convert with LCD_RGBPACK() if necessary) */
1443 unsigned text_color;
1444 /* line color if STYLE_COLORBAR or STYLE_GRADIENT is specified, in native
1445 * lcd format (convert with LCD_RGBPACK() if necessary) */
1446 unsigned line_color, line_end_color;
1447 /* line decorations, see STYLE_DEFAULT etc. */
1448 enum line_styles style;
1449 /* whether the line can scroll */
1450 bool scroll;
1451 /* height of the line separator (in pixels). 0 to disable drawing
1452 * of the separator */
1453 int8_t separator_height;
1454#endif
1455
1456/*LINE_DESC_DEFINIT { .style = STYLE_DEFAULT, .height = -1, .separator_height = 0, .line = 0, .nlines = 1, .scroll = false }*/
1457
1458 struct line_desc linedes = LINE_DESC_DEFINIT;
1459
1460 bool is_selected = false;
1461 bool show_icons = false;
1462 bool show_cursor = false;
1463
1464 int line_indent = 0;
1465 int item_offset = 0;
1466
1467 int x = (int) luaL_checkint(L, 1);
1468 int y = (int) luaL_checkint(L, 2);
1469
1470 const unsigned char * string = luaL_checkstring(L, 3);
1471 int icon = Icon_NOICON;
1472 const int narg = 4;
1473
1474 if(lua_type(L, narg) == LUA_TTABLE)
1475 {
1476 /* check_tablevalue only returns INTS */
1477 linedes.line = check_tablevalue(L, "line", narg);
1478 linedes.height = check_tablevalue_def(L, "height", narg, -1);
1479 linedes.nlines = check_tablevalue_def(L, "nlines", narg, 1);
1480 linedes.style = check_tablevalue_def(L, "style", narg, STYLE_DEFAULT);
1481 linedes.separator_height = check_tablevalue(L, "separator_height", narg);
1482 linedes.scroll = check_tablevalue(L, "scroll", narg) > 0;
1483
1484 linedes.text_color = check_tablevalue(L, "text_color", narg);
1485 linedes.line_color = check_tablevalue(L, "line_color", narg);
1486 linedes.line_end_color = check_tablevalue(L, "line_end_color", narg);
1487
1488 icon = check_tablevalue_def(L, "icon", narg, Icon_NOICON);
1489 show_icons = check_tablevalue(L, "show_icons", narg) > 0 && icon > Icon_NOICON;
1490 show_cursor = check_tablevalue(L, "show_cursor", narg) > 0;
1491 is_selected = check_tablevalue(L, "selected", narg) > 0;
1492
1493 line_indent = check_tablevalue(L, "indent", narg);
1494 item_offset = check_tablevalue(L, "offset", narg);
1495
1496 }
1497
1498 while (*string == '\t')
1499 {
1500 line_indent++;
1501 string++;
1502 }
1503
1504 /* mask out gradient and colorbar styles for non-color displays */
1505 if (RB_SCREEN_STRUCT(L, 5)->depth < 16)
1506 {
1507 if (linedes.style & (STYLE_COLORBAR|STYLE_GRADIENT))
1508 {
1509 linedes.style &= ~(STYLE_COLORBAR|STYLE_GRADIENT);
1510 linedes.style |= STYLE_INVERT;
1511 }
1512 linedes.style &= ~STYLE_COLORED;
1513 }
1514
1515 if (show_cursor && (show_icons && icon > Icon_NOICON))
1516 RB_SCREENS(L, 5, put_line, x, y, &linedes,
1517 "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t",
1518 line_indent, is_selected ? Icon_Cursor : Icon_NOICON,
1519 icon, item_offset, string);
1520 else if (show_cursor || (show_icons && icon > Icon_NOICON))
1521 RB_SCREENS(L, 5, put_line, x, y, &linedes,
1522 "$*s$"ICON_PADDING_S"I$*t", line_indent,
1523 show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon,
1524 item_offset, string);
1525 else
1526 RB_SCREENS(L, 5, put_line, x, y, &linedes, "$*s$*t", line_indent, item_offset, string);
1527
1528
1529 return 0;
1530}
1531
1532RB_WRAP(lcd_puts_scroll)
1533{
1534 int x, y;
1535 const unsigned char * string = lcd_putshelper(L, &x, &y);
1536 bool result = RB_SCREENS(L, 4, puts_scroll, x, y, string);
1537 lua_pushboolean(L, result);
1538 return 1;
1539}
1540
1541RB_WRAP(lcd_scroll_stop)
1542{
1543 RB_SCREENS(L, 1, scroll_stop);
1544 return 0;
1545}
1546
1547static inline struct viewport* opt_viewport(lua_State *L,
1548 int narg,
1549 bool set_coords,
1550 struct viewport* vp,
1551 struct viewport* alt)
1552{
1553 if(lua_isnoneornil(L, narg))
1554 return alt;
1555
1556 luaL_checktype(L, narg, LUA_TTABLE);
1557 if (set_coords)
1558 {
1559 vp->x = check_tablevalue(L, "x", narg);
1560 vp->y = check_tablevalue(L, "y", narg);
1561 vp->width = check_tablevalue(L, "width", narg);
1562 vp->height = check_tablevalue(L, "height", narg);
1563 }
1564 vp->font = check_tablevalue_def(L, "font", narg, FONT_UI);
1565 vp->drawmode = check_tablevalue_def(L, "drawmode", narg, DRMODE_SOLID);
1566
1567#if LCD_DEPTH == 2
1568 unsigned int fg = check_tablevalue(L, "fg_pattern", narg);
1569 unsigned int bg = check_tablevalue(L, "bg_pattern", narg);
1570 /*invert fg and bg patterns (3-)*/
1571 vp->fg_pattern = 0x55 * (3 - (~(fg) & 3));
1572 vp->bg_pattern = 0x55 * (3 - (~(bg) & 3));
1573#elif LCD_DEPTH > 1
1574 vp->fg_pattern = (unsigned int) check_tablevalue(L, "fg_pattern", narg);
1575 vp->bg_pattern = (unsigned int) check_tablevalue(L, "bg_pattern", narg);
1576#endif
1577
1578 return vp;
1579}
1580
1581RB_WRAP(set_viewport)
1582{
1583 void *ud = rli_checktype_opt(L, 1);
1584 if (ud != NULL)
1585 {
1586 img_set_as_vp((struct rocklua_image*) ud, &img_vp);
1587 RB_SCREENS(L, 3, set_viewport, opt_viewport(L, 2, false, &img_vp, &img_vp));
1588 return 0;
1589 }
1590 static struct viewport vp;
1591 RB_SCREENS(L, 2, set_viewport, opt_viewport(L, 1, true, &vp, NULL));
1592 return 0;
1593}
1594
1595RB_WRAP(clear_viewport)
1596{
1597 RB_SCREENS(L, 1, clear_viewport);
1598 return 0;
1599}
1600
1601RB_WRAP(font_getstringsize)
1602{
1603 const unsigned char* str = luaL_checkstring(L, 1);
1604 int fontnumber = lua_tointeger(L, 2);
1605 int w, h, result;
1606
1607 if (fontnumber == FONT_UI)
1608 fontnumber = rb->global_status->font_id[SCREEN_MAIN];
1609 else
1610 fontnumber = FONT_SYSFIXED;
1611
1612 if lua_isnoneornil(L, 2)
1613 result = RB_SCREENS(L, 3, getstringsize, str, &w, &h);
1614 else
1615 result = rb->font_getstringsize(str, &w, &h, fontnumber);
1616
1617 lua_pushinteger(L, result);
1618 lua_pushinteger(L, w);
1619 lua_pushinteger(L, h);
1620
1621 return 3;
1622}
1623
1624RB_WRAP(lcd_framebuffer)
1625{
1626 int screen = get_screen(L, 1);
1627 static struct viewport vp;
1628 rb->viewport_set_fullscreen(&vp, screen);
1629 rli_wrap(L, vp.buffer->data,
1630 RB_SCREEN_STRUCT(L, 1)->lcdwidth,
1631 RB_SCREEN_STRUCT(L, 1)->lcdheight);
1632 return 1;
1633}
1634
1635RB_WRAP(lcd_setfont)
1636{
1637 int font = (int) luaL_checkint(L, 1);
1638 RB_SCREENS(L, 2, setfont, font);
1639 return 0;
1640}
1641
1642/* helper function for lcd_xxx_bitmap/rect functions */
1643static void checkint_arr(lua_State *L, int *val, int narg, int elems)
1644{
1645 /* fills passed array of integers with lua integers from stack */
1646 for (int i = 0; i < elems; i++)
1647 val[i] = luaL_checkint(L, narg + i);
1648}
1649
1650RB_WRAP(gui_scrollbar_draw)
1651{
1652 enum {x = 0, y, w, h, items, min_shown, max_shown, flags, eCNT};
1653 int v[eCNT];
1654 checkint_arr(L, v, 1, eCNT);
1655
1656 rb->gui_scrollbar_draw(RB_SCREEN_STRUCT(L, 9), v[x], v[y], v[w], v[h],
1657 v[items], v[min_shown], v[max_shown], (unsigned) v[flags]);
1658return 0;
1659}
1660
1661RB_WRAP(lcd_mono_bitmap_part)
1662{
1663 struct rocklua_image *src = rli_checktype(L, 1);
1664 enum {src_x = 0, src_y, stride, x, y, w, h, eCNT};
1665 int v[eCNT];
1666 checkint_arr(L, v, 2, eCNT);;
1667
1668 RB_SCREENS(L, 9, mono_bitmap_part, (const unsigned char *)src->data,
1669 v[src_x], v[src_y], v[stride], v[x], v[y], v[w], v[h]);
1670 return 0;
1671}
1672
1673RB_WRAP(lcd_mono_bitmap)
1674{
1675 struct rocklua_image *src = rli_checktype(L, 1);
1676 enum {x = 0, y, w, h, eCNT};
1677 int v[eCNT];
1678 checkint_arr(L, v, 2, eCNT);
1679
1680 RB_SCREENS(L, 6, mono_bitmap, (const unsigned char *)src->data,
1681 v[x], v[y], v[w], v[h]);
1682 return 0;
1683}
1684
1685#if LCD_DEPTH > 1
1686RB_WRAP(lcd_bitmap_part)
1687{
1688 struct rocklua_image *src = rli_checktype(L, 1);
1689 enum {src_x = 0, src_y, stride, x, y, w, h, eCNT};
1690 int v[eCNT];
1691 checkint_arr(L, v, 2, eCNT);
1692
1693 RB_SCREENS(L, 9, bitmap_part, src->data,
1694 v[src_x], v[src_y], v[stride], v[x], v[y], v[w], v[h]);
1695 return 0;
1696}
1697
1698RB_WRAP(lcd_bitmap)
1699{
1700 struct rocklua_image *src = rli_checktype(L, 1);
1701 enum {x = 0, y, w, h, eCNT};
1702 int v[eCNT];
1703 checkint_arr(L, v, 2, eCNT);
1704
1705 RB_SCREENS(L, 6, bitmap, src->data, v[x], v[y], v[w], v[h]);
1706 return 0;
1707}
1708
1709RB_WRAP(lcd_get_backdrop)
1710{
1711 fb_data* backdrop = rb->lcd_get_backdrop();
1712 if(backdrop == NULL)
1713 lua_pushnil(L);
1714 else
1715 rli_wrap(L, backdrop, LCD_WIDTH, LCD_HEIGHT);
1716
1717 return 1;
1718}
1719
1720RB_WRAP(lcd_set_foreground)
1721{
1722 unsigned foreground = (unsigned) luaL_checkint(L, 1);
1723 RB_SCREENS(L, 2, set_foreground, foreground);
1724 return 0;
1725}
1726
1727RB_WRAP(lcd_get_foreground)
1728{
1729 unsigned result = RB_SCREENS(L, 1, get_foreground);
1730 lua_pushinteger(L, result);
1731 return 1;
1732}
1733
1734RB_WRAP(lcd_set_background)
1735{
1736 unsigned background = (unsigned) luaL_checkint(L, 1);
1737 RB_SCREENS(L, 2, set_background, background);
1738 return 0;
1739}
1740
1741RB_WRAP(lcd_get_background)
1742{
1743 unsigned result = RB_SCREENS(L, 1, get_background);
1744 lua_pushinteger(L, result);
1745 return 1;
1746}
1747#endif /* LCD_DEPTH > 1 */
1748
1749#if (LCD_DEPTH < 4) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
1750RB_WRAP(lcd_blit_grey_phase)
1751{
1752 /* note that by and bheight are in 8-pixel units! */
1753 unsigned char * values = (unsigned char *) luaL_checkstring(L, 1);
1754 unsigned char * phases = (unsigned char *) luaL_checkstring(L, 2);
1755 enum {bx = 0, by, bw, bh, stride, eCNT};
1756 int v[eCNT];
1757 checkint_arr(L, v, 3, eCNT);
1758
1759 rb->lcd_blit_grey_phase(values, phases, v[bx], v[by], v[bw], v[bh], v[stride]);
1760 return 0;
1761}
1762
1763RB_WRAP(lcd_blit_mono)
1764{
1765 /* note that by and bheight are in 8-pixel units! */
1766 const unsigned char * data = (const unsigned char *) luaL_checkstring(L, 1);
1767 enum {x = 0, by, w, bh, stride, eCNT};
1768 int v[eCNT];
1769 checkint_arr(L, v, 3, eCNT);
1770
1771 rb->lcd_blit_mono(data, v[x], v[by], v[w], v[bh], v[stride]);
1772 return 0;
1773}
1774#endif /* LCD_DEPTH < 4 && CONFIG_PLATFORM & PLATFORM_NATIVE */
1775
1776#if LCD_DEPTH == 16
1777RB_WRAP(lcd_bitmap_transparent_part)
1778{
1779 struct rocklua_image *src = rli_checktype(L, 1);
1780 enum {src_x = 0, src_y, stride, x, y, w, h, eCNT};
1781 int v[eCNT];
1782 checkint_arr(L, v, 2, eCNT);
1783
1784 RB_SCREENS(L, 9, transparent_bitmap_part, src->data,
1785 v[src_x], v[src_y], v[stride], v[x], v[y], v[w], v[h]);
1786 return 0;
1787}
1788
1789RB_WRAP(lcd_bitmap_transparent)
1790{
1791 struct rocklua_image *src = rli_checktype(L, 1);
1792 enum {x = 0, y, w, h, eCNT};
1793 int v[eCNT];
1794 checkint_arr(L, v, 2, eCNT);
1795
1796 RB_SCREENS(L, 6, transparent_bitmap, src->data, v[x], v[y], v[w], v[h]);
1797 return 0;
1798}
1799#endif /* LCD_DEPTH == 16 */
1800
1801RB_WRAP(lcd_update_rect)
1802{
1803 enum {x = 0, y, w, h, eCNT};
1804 int v[eCNT];
1805 checkint_arr(L, v, 1, eCNT);
1806
1807 RB_SCREENS(L, 5, update_rect, v[x], v[y], v[w], v[h]);
1808 return 0;
1809}
1810
1811RB_WRAP(lcd_drawrect)
1812{
1813 enum {x = 0, y, w, h, eCNT};
1814 int v[eCNT];
1815 checkint_arr(L, v, 1, eCNT);
1816
1817 RB_SCREENS(L, 5, drawrect, v[x], v[y], v[w], v[h]);
1818 return 0;
1819}
1820
1821RB_WRAP(lcd_fillrect)
1822{
1823 enum {x = 0, y, w, h, eCNT};
1824 int v[eCNT];
1825 checkint_arr(L, v, 1, eCNT);
1826
1827 RB_SCREENS(L, 5, fillrect, v[x], v[y], v[w], v[h]);
1828 return 0;
1829}
1830
1831RB_WRAP(lcd_drawline)
1832{
1833 enum {x1 = 0, y1, x2, y2, eCNT};
1834 int v[eCNT];
1835 checkint_arr(L, v, 1, eCNT);
1836
1837 RB_SCREENS(L, 5, drawline, v[x1], v[y1], v[x2], v[y2]);
1838 return 0;
1839}
1840
1841RB_WRAP(lcd_hline)
1842{
1843 enum {x1 = 0, x2, y, eCNT};
1844 int v[eCNT];
1845 checkint_arr(L, v, 1, eCNT);
1846
1847 RB_SCREENS(L, 4, hline, v[x1], v[x2], v[y]);
1848 return 0;
1849}
1850
1851RB_WRAP(lcd_vline)
1852{
1853 enum {x = 0, y1, y2, eCNT};
1854 int v[eCNT];
1855 checkint_arr(L, v, 1, eCNT);
1856
1857 RB_SCREENS(L, 4, vline, v[x], v[y1], v[y2]);
1858 return 0;
1859}
1860
1861RB_WRAP(lcd_drawpixel)
1862{
1863 int x = (int) luaL_checkint(L, 1);
1864 int y = (int) luaL_checkint(L, 2);
1865 RB_SCREENS(L, 3, drawpixel, x, y);
1866 return 0;
1867}
1868
1869#ifdef HAVE_LCD_COLOR
1870RB_WRAP(lcd_rgbpack)
1871{
1872 enum {r = 0, g, b, eCNT};
1873 int v[eCNT];
1874 checkint_arr(L, v, 1, eCNT);
1875
1876 int result = LCD_RGBPACK(v[r], v[g], v[b]);
1877 lua_pushinteger(L, result);
1878 return 1;
1879}
1880
1881RB_WRAP(lcd_rgbunpack)
1882{
1883 int rgb = luaL_checkint(L, 1);
1884 lua_pushinteger(L, RGB_UNPACK_RED(rgb));
1885 lua_pushinteger(L, RGB_UNPACK_GREEN(rgb));
1886 lua_pushinteger(L, RGB_UNPACK_BLUE(rgb));
1887 return 3;
1888}
1889#endif
1890
1891RB_WRAP(read_bmp_file)
1892{
1893 struct bitmap bm;
1894 const char* filename = luaL_checkstring(L, 1);
1895 bool dither = luaL_optboolean(L, 2, true);
1896 bool transparent = luaL_optboolean(L, 3, false);
1897 int format = FORMAT_NATIVE;
1898
1899 if(dither)
1900 format |= FORMAT_DITHER;
1901
1902 if(transparent)
1903 format |= FORMAT_TRANSPARENT;
1904
1905 int result = rb->read_bmp_file(filename, &bm, 0, format | FORMAT_RETURN_SIZE, NULL);
1906
1907 if(result > 0)
1908 {
1909 bm.data = (unsigned char*) rli_alloc(L, bm.width, bm.height);
1910 if(rb->read_bmp_file(filename, &bm, result, format, NULL) < 0)
1911 {
1912 /* Error occured, drop newly allocated image from stack */
1913 lua_pop(L, 1);
1914 lua_pushnil(L);
1915 }
1916 }
1917 else
1918 lua_pushnil(L);
1919
1920 return 1;
1921}
1922
1923#define R(NAME) {#NAME, rock_##NAME}
1924static const luaL_Reg rocklib_img[] =
1925{
1926 /* Graphics */
1927 R(lcd_update),
1928 R(lcd_clear_display),
1929 R(lcd_set_drawmode),
1930 R(lcd_putsxy),
1931 R(lcd_puts),
1932 R(lcd_put_line),
1933 R(lcd_puts_scroll),
1934 R(lcd_scroll_stop),
1935 R(set_viewport),
1936 R(clear_viewport),
1937 R(font_getstringsize),
1938 R(lcd_framebuffer),
1939 R(lcd_setfont),
1940 R(gui_scrollbar_draw),
1941 R(lcd_mono_bitmap_part),
1942 R(lcd_mono_bitmap),
1943#if LCD_DEPTH > 1
1944 R(lcd_get_backdrop),
1945 R(lcd_bitmap_part),
1946 R(lcd_bitmap),
1947 R(lcd_set_foreground),
1948 R(lcd_get_foreground),
1949 R(lcd_set_background),
1950 R(lcd_get_background),
1951#endif /* LCD_DEPTH > 1 */
1952#if (LCD_DEPTH < 4) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
1953 R(lcd_blit_grey_phase),
1954 R(lcd_blit_mono),
1955#endif /* LCD_DEPTH < 4 && CONFIG_PLATFORM & PLATFORM_NATIVE */
1956#if LCD_DEPTH == 16
1957 R(lcd_bitmap_transparent_part),
1958 R(lcd_bitmap_transparent),
1959#endif
1960 R(lcd_update_rect),
1961 R(lcd_drawrect),
1962 R(lcd_fillrect),
1963 R(lcd_drawline),
1964 R(lcd_hline),
1965 R(lcd_vline),
1966 R(lcd_drawpixel),
1967
1968#ifdef HAVE_LCD_COLOR
1969 R(lcd_rgbpack),
1970 R(lcd_rgbunpack),
1971#endif
1972 R(read_bmp_file),
1973 {"new_image", rli_new},
1974
1975 {NULL, NULL}
1976};
1977#undef R
1978
1979LUALIB_API int luaopen_rock_img(lua_State *L)
1980{
1981 /* some devices need x | y coords shifted to match native format */
1982 /* conversion between packed native formats and individual pixel addressing */
1983 init_pixelmask(&x_shift, &y_shift, &xy_mask, &pixelmask);
1984
1985 luaL_newmetatable(L, ROCKLUA_IMAGE);
1986
1987 lua_pushvalue(L, -1); /* pushes the metatable */
1988 lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
1989 luaL_register(L, NULL, rli_lib); /*add rli_lib to the image metatable*/
1990
1991 luaL_register(L, LUA_ROCKLIBNAME, rocklib_img);
1992
1993 return 1;
1994}
1995