diff options
author | William Wilgus <me.theuser@yahoo.com> | 2018-07-23 02:50:22 +0200 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2018-07-23 05:13:32 +0200 |
commit | af35d1991633f33965801dcb153a9860c4432816 (patch) | |
tree | b6accfcf2914ff7f565d60b9338dfa7998e27c68 /apps/plugins/lua/rocklib.c | |
parent | ef210b5fe4f72a102dcaa1f752d3022d62da8bc4 (diff) | |
download | rockbox-af35d1991633f33965801dcb153a9860c4432816.tar.gz rockbox-af35d1991633f33965801dcb153a9860c4432816.zip |
Rocklua -- Extend / Fix rliImage
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.
This caused a few issues on 1 & 2-bit devices:
Greatly Oversized data arrays for bitmaps
Improper handling of native image data
Framebuffer data was near unusable without jumping through hoops
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.
Not to mention the advantage of a unified interface for the end programer
-------------------------------------------------------------------
Adds a sane way to access each pixel of image data
Adds:
--------------------------------------------------------------------
img:clear([color],[x1],[y1],[x2],[y2])
(set whole image or a portion to a particular value)
--------------------------------------------------------------------
img:invert([x1],[y1],[x2],[y2])
(inverts whole image or a portion)
--------------------------------------------------------------------
img:marshal([x1],[y1],[x2],[y2],[funct])
(calls funct for each point defined by rect of x1,y1 x2,y2
returns value and allows setting value of each point return
nil to terminate early)
--------------------------------------------------------------------
img:points([x1],[y1],[x2],[y2],[dx],[dy])
(returns iterator function that steps delta-x and delta-y pixels each call
returns value of pixel each call but doesn't allow setting to a new value
compare to lua pairs method)
--------------------------------------------------------------------
img:copy(src,[x1],[y1],[x2],[y2],[w],[h],[clip][operation][clr/funct])
(copies all or part of an image -- straight copy or special ops
optionally calls funct for each point defined by rect of
x1, y1, w, h and x2, y2, w, h for dest and src images
returns value of dst and src and allows setting value of
each point return nil to terminate early)
--------------------------------------------------------------------
img:line(x1, y1, x2, y2, color)
--------------------------------------------------------------------
img:ellipse(x1, y1, x2, y2, color, [fillcolor]
--------------------------------------------------------------------
Fixed handling of 2-bit vertical integrated screens
Added direct element access for saving / restoring native image etc.
Added more data to tostring() handler and a way to access individual items
Added equals method to see if two variables reference the same image address
(doesn't check if two separate images contain the same 'picture')
Optimized get and set routines
Fixed out of bound x coord access shifting to next line
Added lua include files to expose new functionality
Finished image saving routine
Static allocation of set_viewport struct faster + saves ram over dynamic
Cleaned up code
Fixed pixel get/set for 1/2 bit devices
Fixed handling for 24-bit devices (32?)
-------------------------------------------------------------------------
Example lua script to follow on forums
-------------------------------------------------------------------------
Change-Id: I8a9ff0ff72aacf4b1662767ccb2b312fc355239c
Diffstat (limited to 'apps/plugins/lua/rocklib.c')
-rw-r--r-- | apps/plugins/lua/rocklib.c | 1254 |
1 files changed, 1166 insertions, 88 deletions
diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c index 2268063d3f..ba8a576e8c 100644 --- a/apps/plugins/lua/rocklib.c +++ b/apps/plugins/lua/rocklib.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * | 9 | * |
10 | * Copyright (C) 2008 Dan Everton (safetydan) | 10 | * Copyright (C) 2008 Dan Everton (safetydan) |
11 | * Copyright (C) 2009 Maurus Cuelenaere | 11 | * Copyright (C) 2009 Maurus Cuelenaere |
12 | * Copyright (C) 2017 William Wilgus | ||
12 | * | 13 | * |
13 | * This program is free software; you can redistribute it and/or | 14 | * This program is free software; you can redistribute it and/or |
14 | * modify it under the terms of the GNU General Public License | 15 | * modify it under the terms of the GNU General Public License |
@@ -47,127 +48,1215 @@ | |||
47 | 48 | ||
48 | 49 | ||
49 | /* | 50 | /* |
50 | * ----------------------------- | 51 | * ----------------------------------------------------------------------------- |
51 | * | 52 | * |
52 | * Rockbox Lua image wrapper | 53 | * Rockbox Lua image wrapper |
53 | * | 54 | * |
54 | * ----------------------------- | 55 | * Some devices(1-bit / 2-bit displays) have packed bit formats that |
56 | * need to be unpacked in order to work on them at a pixel level. | ||
57 | * | ||
58 | * The internal formats of these devices do not follow the same paradigm | ||
59 | * for image sizes either; We still display the actual width and height to | ||
60 | * the user but store stride based on the native values | ||
61 | * | ||
62 | * Conversion between native addressing and per pixel addressing | ||
63 | * incurs extra overhead but it is much faster to do it | ||
64 | * on the 'C' side rather than in lua. | ||
65 | * | ||
66 | * ----------------------------------------------------------------------------- | ||
55 | */ | 67 | */ |
56 | 68 | ||
69 | #ifdef HAVE_LCD_BITMAP | ||
70 | #define RLI_EXTENDED | ||
71 | #endif | ||
72 | |||
57 | #define ROCKLUA_IMAGE "rb.image" | 73 | #define ROCKLUA_IMAGE "rb.image" |
74 | #define ERR_IDX_RANGE "index out of range" | ||
75 | #define ERR_DATA_OVF "data overflow" | ||
76 | |||
77 | /* mark for RLI to LUA Interface functions (luaState *L) is the only argument */ | ||
78 | #define RLI_LUA static int | ||
79 | |||
80 | #ifndef ABS | ||
81 | #define ABS(a)(((a) < 0) ? - (a) :(a)) | ||
82 | #endif | ||
83 | |||
84 | #ifndef LCD_BLACK | ||
85 | #define LCD_BLACK 0x0 | ||
86 | #endif | ||
87 | |||
58 | struct rocklua_image | 88 | struct rocklua_image |
59 | { | 89 | { |
60 | int width; | 90 | int width; |
61 | int height; | 91 | int height; |
92 | int stride; | ||
93 | size_t elems; | ||
62 | fb_data *data; | 94 | fb_data *data; |
63 | fb_data dummy[1][1]; | 95 | fb_data dummy[1]; |
64 | }; | 96 | }; |
65 | 97 | ||
66 | static void rli_wrap(lua_State *L, fb_data *src, int width, int height) | 98 | /* holds iterator data for rlimages */ |
99 | struct rli_iter_d | ||
67 | { | 100 | { |
68 | struct rocklua_image *a = (struct rocklua_image *)lua_newuserdata(L, sizeof(struct rocklua_image)); | 101 | struct rocklua_image *img; |
102 | fb_data *elem; | ||
103 | int x , y; | ||
104 | int x1, y1; | ||
105 | int x2, y2; | ||
106 | int dx, dy; | ||
107 | }; | ||
69 | 108 | ||
70 | luaL_getmetatable(L, ROCKLUA_IMAGE); | 109 | /* __tostring information enums */ |
71 | lua_setmetatable(L, -2); | 110 | enum rli_info {RLI_INFO_ALL = 0, RLI_INFO_TYPE, RLI_INFO_WIDTH, |
111 | RLI_INFO_HEIGHT, RLI_INFO_ELEMS, RLI_INFO_BYTES, | ||
112 | RLI_INFO_DEPTH, RLI_INFO_FORMAT, RLI_INFO_ADDRESS}; | ||
72 | 113 | ||
73 | a->width = width; | 114 | #ifdef HAVE_LCD_COLOR |
74 | a->height = height; | 115 | |
75 | a->data = src; | 116 | static inline fb_data invert_color(fb_data rgb) |
117 | { | ||
118 | uint8_t r = 0xFFU - FB_UNPACK_RED(rgb); | ||
119 | uint8_t g = 0xFFU - FB_UNPACK_RED(rgb); | ||
120 | uint8_t b = 0xFFU - FB_UNPACK_RED(rgb); | ||
121 | |||
122 | return FB_RGBPACK(r, g, b); | ||
76 | } | 123 | } |
124 | #else /* !HAVE_LCD_COLOR */ | ||
125 | |||
126 | #define invert_color(c) (~c) | ||
127 | |||
128 | #endif /* HAVE_LCD_COLOR */ | ||
129 | |||
130 | |||
131 | #if (LCD_DEPTH > 2) /* no native to pixel mapping needed */ | ||
77 | 132 | ||
78 | static fb_data* rli_alloc(lua_State *L, int width, int height) | 133 | #define pixel_to_fb(x, y, o, n) {(void) x; (void) y; do { } while (0);} |
134 | #define pixel_to_native(x, y, xn, yn) {*xn = x; *yn = y;} | ||
135 | #define init_pixelmask(x, y, m, p) do { } while (0) | ||
136 | |||
137 | |||
138 | #else /* some devices need x | y coords shifted to match native format */ | ||
139 | |||
140 | static fb_data x_shift = FB_SCALARPACK(0); | ||
141 | static fb_data y_shift = FB_SCALARPACK(0); | ||
142 | static fb_data xy_mask = FB_SCALARPACK(0); | ||
143 | static const fb_data *pixelmask = NULL; | ||
144 | |||
145 | /* conversion between packed native formats and individual pixel addressing */ | ||
146 | static inline void init_pixelmask(fb_data *x_shift, fb_data *y_shift, | ||
147 | fb_data *xy_mask, const fb_data **pixelmask) | ||
148 | { | ||
149 | |||
150 | #if(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 1 | ||
151 | static const fb_data pixelmask_v1[8] = | ||
152 | {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; | ||
153 | *pixelmask = pixelmask_v1; | ||
154 | |||
155 | (void) x_shift; | ||
156 | *y_shift = 3U; | ||
157 | *xy_mask = ((1 << (*y_shift)) - 1); | ||
158 | #elif(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 2 | ||
159 | static const fb_data pixelmask_v2[4] = {0x03, 0x0C, 0x30, 0xC0}; | ||
160 | *pixelmask = pixelmask_v2; | ||
161 | |||
162 | (void) x_shift; | ||
163 | *y_shift = 2U; | ||
164 | *xy_mask = ((1 << (*y_shift)) - 1); | ||
165 | #elif(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2 | ||
166 | static const fb_data pixelmask_vi2[8] = | ||
167 | {0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080}; | ||
168 | *pixelmask = pixelmask_vi2; | ||
169 | |||
170 | (void) x_shift; | ||
171 | *y_shift = 3U; | ||
172 | *xy_mask = ((1 << (*y_shift)) - 1); | ||
173 | #elif(LCD_PIXELFORMAT == HORIZONTAL_PACKING) && LCD_DEPTH == 2 | ||
174 | /* MSB on left */ | ||
175 | static const fb_data pixelmask_h2[4] = {0x03, 0x0C, 0x30, 0xC0}; | ||
176 | *pixelmask = pixelmask_h2; | ||
177 | |||
178 | (void) y_shift; | ||
179 | *x_shift = 2U; | ||
180 | *xy_mask = ((1 << (*x_shift)) - 1); | ||
181 | #else | ||
182 | #warning Unknown Pixel Format | ||
183 | #endif /* LCD_PIXELFORMAT */ | ||
184 | |||
185 | } /* init_pixelmask */ | ||
186 | |||
187 | static inline void pixel_to_native(int x, int y, int *x_native, int *y_native) | ||
188 | { | ||
189 | *x_native = ((x - 1) >> x_shift) + 1; | ||
190 | *y_native = ((y - 1) >> y_shift) + 1; | ||
191 | } /* pixel_to_native */ | ||
192 | |||
193 | static inline fb_data set_masked_pixel(fb_data old, | ||
194 | fb_data new, | ||
195 | fb_data mask, | ||
196 | int bit_n) | ||
79 | { | 197 | { |
80 | size_t nbytes = sizeof(struct rocklua_image) + ((width*height) - 1) * sizeof(fb_data); | 198 | /*equivalent of: (old & (~mask)) | ((new << bit_n) & mask);*/ |
81 | struct rocklua_image *a = (struct rocklua_image *)lua_newuserdata(L, nbytes); | 199 | return old ^ ((old ^ (new << bit_n)) & mask); |
200 | |||
201 | } /* set_masked_pixel */ | ||
202 | |||
203 | static inline fb_data get_masked_pixel(fb_data val, fb_data mask, int bit_n) | ||
204 | { | ||
205 | val = val & mask; | ||
206 | return val >> bit_n; | ||
207 | } /* get_masked_pixel */ | ||
208 | |||
209 | /* conversion between packed native formats and individual pixel addressing */ | ||
210 | static void pixel_to_fb(int x, int y, fb_data *oldv, fb_data *newv) | ||
211 | { | ||
212 | fb_data mask; | ||
213 | int bit_n; | ||
214 | |||
215 | #if(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2 | ||
216 | (void) x; | ||
217 | const uint16_t greymap_vi2[4] = {0x0000, 0x0001, 0x0100, 0x0101}; | ||
218 | |||
219 | bit_n = (y - 1) & xy_mask; | ||
220 | mask = pixelmask[bit_n]; | ||
221 | |||
222 | *newv = greymap_vi2[*newv &(0x3)]; /* [0-3] => greymap */ | ||
223 | *newv = set_masked_pixel(*oldv, *newv, mask, bit_n); | ||
224 | |||
225 | *oldv = get_masked_pixel(*oldv, mask, bit_n); | ||
226 | |||
227 | if((*oldv) > 1) /* greymap => [0-3] */ | ||
228 | *oldv = ((*oldv) & 0x1U) + 2U; /* 2, 3 */ | ||
229 | else | ||
230 | *oldv &= 1U; /* 0, 1 */ | ||
231 | |||
232 | #elif(LCD_DEPTH <= 2) | ||
233 | if(y_shift) | ||
234 | bit_n = (y - 1) & xy_mask; | ||
235 | else if(x_shift) | ||
236 | bit_n = xy_mask - ((x - 1) & xy_mask); /*MSB on left*/ | ||
237 | |||
238 | if(y_shift || x_shift) | ||
239 | { | ||
240 | mask = pixelmask[bit_n]; | ||
241 | bit_n *= LCD_DEPTH; | ||
242 | |||
243 | *newv = set_masked_pixel(*oldv, *newv, mask, bit_n); | ||
244 | |||
245 | *oldv = get_masked_pixel(*oldv, mask, bit_n); | ||
246 | } | ||
247 | #else | ||
248 | #error Unknown Pixel Format | ||
249 | #endif /* LCD_PIXELFORMAT == VERTICAL_INTERLEAVED && LCD_DEPTH == 2 */ | ||
250 | } /* pixel_to_fb */ | ||
251 | |||
252 | #endif /* (LCD_DEPTH > 2) no native to pixel mapping needed */ | ||
253 | |||
254 | /* Internal worker functions for image data array *****************************/ | ||
255 | |||
256 | static inline void swap_int(bool swap, int *v1, int *v2) | ||
257 | { | ||
258 | if(swap) | ||
259 | { | ||
260 | int val = *v1; | ||
261 | *v1 = *v2; | ||
262 | *v2 = val; | ||
263 | } | ||
264 | } /* swap_int */ | ||
265 | |||
266 | /* Throws error if x or y are out of bounds notifies user which narg indice | ||
267 | the out of bound variable originated */ | ||
268 | static void bounds_check_xy(lua_State *L, struct rocklua_image *img, | ||
269 | int nargx, int x, int nargy, int y) | ||
270 | { | ||
271 | luaL_argcheck(L, x <= img->width && x > 0, nargx, ERR_IDX_RANGE); | ||
272 | luaL_argcheck(L, y <= img->height && y > 0, nargy, ERR_IDX_RANGE); | ||
273 | } /* bounds_check_xy */ | ||
274 | |||
275 | static struct rocklua_image* rli_checktype(lua_State *L, int arg) | ||
276 | { | ||
277 | void *ud = luaL_checkudata(L, arg, ROCKLUA_IMAGE); | ||
278 | |||
279 | luaL_argcheck(L, ud != NULL, arg, "'" ROCKLUA_IMAGE "' expected"); | ||
280 | |||
281 | return (struct rocklua_image*) ud; | ||
282 | } /* rli_checktype */ | ||
283 | |||
284 | static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data, | ||
285 | int width, int height) | ||
286 | { | ||
287 | /* rliimage is pushed on the stack it is up to you to pop it */ | ||
288 | struct rocklua_image *img; | ||
289 | |||
290 | const size_t sz_header = sizeof(struct rocklua_image); | ||
291 | size_t sz_data = 0; | ||
292 | size_t n_elems; | ||
293 | |||
294 | int w_native; | ||
295 | int h_native; | ||
296 | |||
297 | pixel_to_native(width, height, &w_native, &h_native); | ||
298 | |||
299 | n_elems = (size_t)(w_native * h_native); | ||
300 | |||
301 | if(alloc_data) /* if this a new image we need space for image data */ | ||
302 | sz_data = n_elems * sizeof(fb_data); | ||
303 | |||
304 | /* newuserdata pushes the userdata onto the stack */ | ||
305 | img = (struct rocklua_image *) lua_newuserdata(L, sz_header + sz_data); | ||
82 | 306 | ||
83 | luaL_getmetatable(L, ROCKLUA_IMAGE); | 307 | luaL_getmetatable(L, ROCKLUA_IMAGE); |
84 | lua_setmetatable(L, -2); | 308 | lua_setmetatable(L, -2); |
85 | 309 | ||
86 | a->width = width; | 310 | /* apparent w/h is stored but behind the scenes native w/h is used */ |
87 | a->height = height; | 311 | img->width = width; |
88 | a->data = &a->dummy[0][0]; | 312 | img->height = height; |
313 | img->stride = w_native; | ||
314 | img->elems = n_elems; | ||
315 | |||
316 | return img; | ||
317 | } /* alloc_rlimage */ | ||
318 | |||
319 | static inline void rli_wrap(lua_State *L, fb_data *src, int width, int height) | ||
320 | { | ||
321 | /* rliimage is pushed on the stack it is up to you to pop it */ | ||
322 | struct rocklua_image *a = alloc_rlimage(L, false, width, height); | ||
323 | |||
324 | a->data = src; | ||
325 | } /* rli_wrap */ | ||
326 | |||
327 | static inline fb_data* rli_alloc(lua_State *L, int width, int height) | ||
328 | { | ||
329 | /* rliimage is pushed on the stack it is up to you to pop it */ | ||
330 | struct rocklua_image *a = alloc_rlimage(L, true, width, height); | ||
331 | |||
332 | a->data = &a->dummy[0]; /* ref to beginning of alloc'd img data */ | ||
89 | 333 | ||
90 | return a->data; | 334 | return a->data; |
91 | } | 335 | } /* rli_alloc */ |
92 | 336 | ||
93 | static int rli_new(lua_State *L) | 337 | static inline fb_data data_setget(fb_data *elem, int x, int y, fb_data *val) |
94 | { | 338 | { |
95 | int width = luaL_checkint(L, 1); | 339 | fb_data old_val = FB_SCALARPACK(0); |
96 | int height = luaL_checkint(L, 2); | 340 | fb_data new_val; |
341 | |||
342 | if(elem) | ||
343 | { | ||
344 | old_val = *elem; | ||
345 | |||
346 | if(val) | ||
347 | { | ||
348 | new_val = *val; | ||
349 | pixel_to_fb(x, y, &old_val, &new_val); | ||
350 | *elem = new_val; | ||
351 | } | ||
352 | else | ||
353 | pixel_to_fb(x, y, &old_val, &new_val); | ||
354 | } | ||
355 | |||
356 | return old_val; | ||
357 | } /* data_setget */ | ||
358 | |||
359 | static inline fb_data data_set(fb_data *elem, int x, int y, fb_data *new_val) | ||
360 | { | ||
361 | /* get and set share the same underlying function */ | ||
362 | return data_setget(elem, x, y, new_val); | ||
363 | } /* data_set */ | ||
364 | |||
365 | static inline fb_data data_get(fb_data *elem, int x, int y) | ||
366 | { | ||
367 | /* get and set share the same underlying function */ | ||
368 | return data_setget(elem, x, y, NULL); | ||
369 | } /* data_get */ | ||
370 | |||
371 | static fb_data* rli_get_element(struct rocklua_image* img, int x, int y) | ||
372 | { | ||
373 | int stride = img->stride; | ||
374 | size_t elements = img->elems; | ||
375 | fb_data *data = img->data; | ||
376 | |||
377 | pixel_to_native(x, y, &x, &y); | ||
378 | |||
379 | /* row major address */ | ||
380 | size_t data_address = (stride * (y - 1)) + (x - 1); | ||
381 | |||
382 | /* x needs bound between 0 and stride otherwise overflow to prev/next y */ | ||
383 | if(x <= 0 || x > stride || data_address >= elements) | ||
384 | return NULL; /* data overflow */ | ||
385 | |||
386 | return &data[data_address]; /* return element address */ | ||
387 | } /* rli_get_element */ | ||
388 | |||
389 | /* Lua to C Interface for pixel set and get functions */ | ||
390 | static int rli_setget(lua_State *L, bool is_get) | ||
391 | { | ||
392 | /*(set) (dst*, [x1, y1, clr, clip]) */ | ||
393 | /*(get) (dst*, [x1, y1, clip]) */ | ||
394 | struct rocklua_image *a = rli_checktype(L, 1); | ||
395 | int x = luaL_checkint(L, 2); | ||
396 | int y = luaL_checkint(L, 3); | ||
397 | |||
398 | fb_data clr = FB_SCALARPACK(0); /* Arg 4 is color if set element */ | ||
399 | fb_data *p_clr = &clr; | ||
400 | |||
401 | int clip_narg; | ||
402 | |||
403 | if(is_get) /* get element */ | ||
404 | { | ||
405 | p_clr = NULL; | ||
406 | clip_narg = 4; | ||
407 | } | ||
408 | else /* set element */ | ||
409 | { | ||
410 | clr = FB_SCALARPACK((unsigned) luaL_checknumber(L, 4)); | ||
411 | clip_narg = 5; | ||
412 | } | ||
413 | |||
414 | fb_data *element = rli_get_element(a, x, y); | ||
415 | |||
416 | if(!element) | ||
417 | { | ||
418 | if(!luaL_optboolean(L, clip_narg, false)) /* Error if !clip */ | ||
419 | bounds_check_xy(L, a, 2, x, 3, y); | ||
420 | |||
421 | lua_pushnil(L); | ||
422 | return 1; | ||
423 | } | ||
424 | |||
425 | lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(data_setget(element, x, y, p_clr))); | ||
426 | |||
427 | /* returns old value */ | ||
428 | return 1; | ||
429 | } /* rli_setget */ | ||
430 | |||
431 | #ifdef RLI_EXTENDED | ||
432 | static bool init_rli_iter(struct rli_iter_d *d, | ||
433 | struct rocklua_image *img, | ||
434 | int x1, int y1, | ||
435 | int x2, int y2, | ||
436 | int dx, int dy, | ||
437 | bool swx, bool swy) | ||
438 | { | ||
439 | |||
440 | swap_int((swx), &x1, &x2); | ||
441 | swap_int((swy), &y1, &y2); | ||
442 | |||
443 | /* stepping in the correct x direction ? */ | ||
444 | if((dx > 0 && x1 > x2) || (dx < 0 && x1 < x2)) | ||
445 | dx = -dx; | ||
446 | |||
447 | /* stepping in the correct y direction ? */ | ||
448 | if((dy > 0 && y1 > y2) || (dy < 0 && y1 < y2)) | ||
449 | dy = -dy; | ||
450 | |||
451 | d->img = img; | ||
452 | d->x = x1; | ||
453 | d->y = y1; | ||
454 | d->x1 = x1; | ||
455 | d->y1 = y1; | ||
456 | d->x2 = x2; | ||
457 | d->y2 = y2; | ||
458 | d->dx = dx; | ||
459 | d->dy = dy; | ||
460 | d->elem = rli_get_element(img, d->x, d->y); | ||
461 | |||
462 | return(dx != 0 || dy != 0); | ||
463 | } /* init_rli_iter */ | ||
464 | |||
465 | /* steps to the next point(x, y) by delta x/y, stores pointer to element | ||
466 | returns true if x & y haven't reached x2 & y2 | ||
467 | if limit reached - element set to NULL, deltas set to 0 & false returned | ||
468 | */ | ||
469 | static bool next_rli_iter(struct rli_iter_d *d) | ||
470 | { | ||
471 | if((d->dx > 0 && d->x < d->x2) || (d->dx < 0 && d->x > d->x2)) | ||
472 | d->x += d->dx; | ||
473 | else if((d->dy > 0 && d->y < d->y2) || (d->dy < 0 && d->y > d->y2)) | ||
474 | { | ||
475 | d->x = d->x1; /* Reset x*/ | ||
476 | d->y += d->dy; | ||
477 | } | ||
478 | else | ||
479 | { | ||
480 | d->elem = NULL; | ||
481 | d->dx = 0; | ||
482 | d->dy = 0; | ||
483 | return false; | ||
484 | } | ||
485 | |||
486 | d->elem = rli_get_element(d->img, d->x, d->y); | ||
487 | |||
488 | return true; | ||
489 | } /* next_rli_iter */ | ||
490 | |||
491 | static int d_line(struct rocklua_image *img, | ||
492 | int x1, int y1, | ||
493 | int x2, int y2, | ||
494 | fb_data *clr, | ||
495 | bool clip) | ||
496 | { | ||
497 | /* NOTE! clr passed as pointer */ | ||
498 | /* Bresenham midpoint line algorithm */ | ||
499 | fb_data *element; | ||
500 | |||
501 | int r_a = x2 - x1; /* range of x direction called 'a' for now */ | ||
502 | int r_b = y2 - y1; /* range of y direction called 'b' for now */ | ||
503 | |||
504 | int s_a = 1; /* step of a direction */ | ||
505 | int s_b = 1; /* step of b direction */ | ||
506 | |||
507 | int d_err; | ||
508 | int numpixels; | ||
509 | |||
510 | int *a1 = &x1; /* pointer to the x var 'a' */ | ||
511 | int *b1 = &y1; /* pointer to the y var 'b' */ | ||
512 | |||
513 | if(r_a < 0) /* instead of negative range we will switch step instead */ | ||
514 | { | ||
515 | r_a = -r_a; | ||
516 | s_a = -s_a; | ||
517 | } | ||
518 | |||
519 | if(r_b < 0) /* instead of negative range we will switch step instead */ | ||
520 | { | ||
521 | r_b = -r_b; | ||
522 | s_b = -s_b; | ||
523 | } | ||
524 | |||
525 | x2 += s_a; /* add 1 extra point to make the whole line */ | ||
526 | y2 += s_b; /* add 1 extra point */ | ||
527 | |||
528 | if(r_b > r_a) /*if rangeY('b') > rangeX('a') swap their identities */ | ||
529 | { | ||
530 | a1 = &y1; | ||
531 | b1 = &x1; | ||
532 | swap_int((true), &r_a, &r_b); | ||
533 | swap_int((true), &s_a, &s_b); | ||
534 | } | ||
535 | |||
536 | d_err = ((r_b << 1) - r_a) >> 1; /* preload err of 1 step (px centered) */ | ||
537 | |||
538 | numpixels = r_a + 1; | ||
539 | |||
540 | r_a -= r_b; /* pre-subtract 'a' - 'b' */ | ||
541 | |||
542 | for(;numpixels > 0; numpixels--) | ||
543 | { | ||
544 | element = rli_get_element(img, x1, y1); | ||
545 | if(element || clip) | ||
546 | data_set(element, x1, y1, clr); | ||
547 | else | ||
548 | return numpixels + 1; /* Error */ | ||
549 | |||
550 | if(d_err >= 0) /* 0 is our target midpoint(exact point on the line) */ | ||
551 | { | ||
552 | *b1 += s_b; /* whichever axis is in 'b' stepped(-1 or +1) */ | ||
553 | d_err -= r_a; | ||
554 | } | ||
555 | else | ||
556 | d_err += r_b; /* only add 'b' when d_err < 0 */ | ||
557 | |||
558 | *a1 += s_a; /* whichever axis is in 'a' stepped(-1 or +1) */ | ||
559 | } | ||
560 | |||
561 | return 0; | ||
562 | } /* d_line */ | ||
563 | |||
564 | /* ellipse worker function */ | ||
565 | static int d_ellipse_elements(struct rocklua_image * img, | ||
566 | int x1, int y1, | ||
567 | int x2, int y2, | ||
568 | int sx, int sy, | ||
569 | fb_data *clr, | ||
570 | fb_data *fillclr, | ||
571 | bool clip) | ||
572 | { | ||
573 | int ret = 0; | ||
574 | fb_data *element1, *element2, *element3, *element4; | ||
575 | |||
576 | if(fillclr && x1 - sx != x2 + sx) | ||
577 | { | ||
578 | ret |= d_line(img, x1, y1, x2, y1, fillclr, clip); /* I. II.*/ | ||
579 | ret |= d_line(img, x1, y2, x2, y2, fillclr, clip); /* III.IV.*/ | ||
580 | } | ||
581 | |||
582 | x1 -= sx; /* shift x & y */ | ||
583 | y1 -= sy; | ||
584 | x2 += sx; | ||
585 | y2 += sy; | ||
586 | |||
587 | element1 = rli_get_element(img, x2, y1); | ||
588 | element2 = rli_get_element(img, x1, y1); | ||
589 | element3 = rli_get_element(img, x1, y2); | ||
590 | element4 = rli_get_element(img, x2, y2); | ||
591 | |||
592 | if(clip || (element1 && element2 && element3 && element4)) | ||
593 | { | ||
594 | data_set(element1, x2, y1, clr); /* I. Quadrant +x +y */ | ||
595 | data_set(element2, x1, y1, clr); /* II. Quadrant -x +y */ | ||
596 | data_set(element3, x1, y2, clr); /* III. Quadrant -x -y */ | ||
597 | data_set(element4, x2, y2, clr); /* IV. Quadrant +x -y */ | ||
598 | } | ||
599 | else | ||
600 | ret = 2; /* ERROR */ | ||
601 | |||
602 | return ret; | ||
603 | } /* d_ellipse_elements */ | ||
604 | |||
605 | static int d_ellipse(struct rocklua_image *img, | ||
606 | int x1, int y1, | ||
607 | int x2, int y2, | ||
608 | fb_data *clr, | ||
609 | fb_data *fillclr, | ||
610 | bool clip) | ||
611 | { | ||
612 | /* NOTE! clr and fillclr passed as pointers */ | ||
613 | /* Rasterizing algorithm derivative of work by Alois Zing */ | ||
614 | #if LCD_WIDTH > 1024 || LCD_HEIGHT > 1024 | ||
615 | /* Prevents overflow on large screens */ | ||
616 | double dx, dy, err, e2; | ||
617 | #else | ||
618 | long dx, dy, err, e2; | ||
619 | #endif | ||
620 | |||
621 | int ret = 0; | ||
622 | |||
623 | int a = ABS(x2 - x1); /* diameter */ | ||
624 | int b = ABS(y2 - y1); /* diameter */ | ||
625 | |||
626 | if(a == 0 || b == 0 || !clr) | ||
627 | return ret; /* not an error but nothing to display */ | ||
628 | |||
629 | int b1 = (b & 1); | ||
630 | b = b - (1 - b1); | ||
631 | |||
632 | int a2 = (a * a); | ||
633 | int b2 = (b * b); | ||
634 | |||
635 | dx = ((1 - a) * b2) >> 1; /* error increment */ | ||
636 | dy = (b1 * a2) >> 1; /* error increment */ | ||
637 | |||
638 | err = dx + dy + b1 * a2; /* error of 1.step */ | ||
639 | |||
640 | /* if called with swapped points .. exchange them */ | ||
641 | swap_int((x1 > x2), &x1, &x2); | ||
642 | swap_int((y1 > y2), &y1, &y2); | ||
643 | |||
644 | y1 += (b + 1) >> 1; | ||
645 | y2 = y1 - b1; | ||
646 | |||
647 | do | ||
648 | { | ||
649 | ret = d_ellipse_elements(img, x1, y1, x2, y2, 0, 0, clr, fillclr, clip); | ||
650 | |||
651 | e2 = err; | ||
652 | |||
653 | /* using division because you can't use bit shift on doubles.. */ | ||
654 | if(e2 <= (dy / 2)) /* target midpoint - y step */ | ||
655 | { | ||
656 | y1++; | ||
657 | y2--; | ||
658 | dy += a2; | ||
659 | err += dy; | ||
660 | } | ||
661 | |||
662 | if(e2 >= (dx / 2) || err > (dy / 2)) /* target midpoint - x step */ | ||
663 | { | ||
664 | x1++; | ||
665 | x2--; | ||
666 | dx += b2; | ||
667 | err += dx; | ||
668 | } | ||
669 | |||
670 | } while(ret == 0 && x1 <= x2); | ||
671 | |||
672 | while (ret == 0 && y1 - y2 < b) /* early stop of flat ellipse a=1 finish tip */ | ||
673 | { | ||
674 | ret = d_ellipse_elements(img, x1, y1, x2, y2, 1, 0, clr, fillclr, clip); | ||
675 | |||
676 | y1++; | ||
677 | y2--; | ||
678 | } | ||
679 | |||
680 | return ret; | ||
681 | } /* d_ellipse */ | ||
682 | |||
683 | /* Lua to C Interface for line and ellipse */ | ||
684 | static int rli_line_ellipse(lua_State *L, bool is_ellipse) | ||
685 | { | ||
686 | struct rocklua_image *a = rli_checktype(L, 1); | ||
687 | |||
688 | int x1 = luaL_checkint(L, 2); | ||
689 | int y1 = luaL_checkint(L, 3); | ||
690 | int x2 = luaL_optint(L, 4, x1); | ||
691 | int y2 = luaL_optint(L, 5, y1); | ||
692 | |||
693 | fb_data clr = FB_SCALARPACK((unsigned) luaL_checknumber(L, 6)); | ||
694 | |||
695 | fb_data fillclr; /* fill color is index 7 if is_ellipse */ | ||
696 | fb_data *p_fillclr = NULL; | ||
697 | |||
698 | bool clip; | ||
699 | int clip_narg; | ||
700 | |||
701 | if(is_ellipse) | ||
702 | clip_narg = 8; | ||
703 | else | ||
704 | clip_narg = 7; | ||
705 | |||
706 | clip = luaL_optboolean(L, clip_narg, false); | ||
707 | |||
708 | if(!clip) | ||
709 | { | ||
710 | bounds_check_xy(L, a, 2, x1, 3, y1); | ||
711 | bounds_check_xy(L, a, 4, x2, 5, y2); | ||
712 | } | ||
713 | |||
714 | if(is_ellipse) | ||
715 | { | ||
716 | if(!lua_isnoneornil(L, 7)) | ||
717 | { | ||
718 | fillclr = FB_SCALARPACK((unsigned) luaL_checkint(L, 7)); | ||
719 | p_fillclr = &fillclr; | ||
720 | } | ||
721 | |||
722 | luaL_argcheck(L, d_ellipse(a, x1, y1, x2, y2, &clr, p_fillclr, clip) == 0, | ||
723 | 1, ERR_DATA_OVF); | ||
724 | } | ||
725 | else | ||
726 | luaL_argcheck(L, d_line(a, x1, y1, x2, y2, &clr, clip) == 0, | ||
727 | 1, ERR_DATA_OVF); | ||
728 | |||
729 | return 0; | ||
730 | } /* rli_line_ellipse */ | ||
731 | |||
732 | /* Pushes lua function from Stack at position narg to top of stack | ||
733 | and puts a reference in the global registry for later use */ | ||
734 | static inline int register_luafunc(lua_State *L, int narg_funct) | ||
735 | { | ||
736 | lua_pushvalue(L, narg_funct); /* lua function */ | ||
737 | int lf_ref = luaL_ref(L, LUA_REGISTRYINDEX); | ||
738 | lua_settop(L, 0); /* clear C stack for use by lua function */ | ||
739 | return lf_ref; | ||
740 | } /* register_luafunc */ | ||
741 | |||
742 | /* User defined pixel manipulations through rli_copy, rli_marshal */ | ||
743 | static int custom_transform(lua_State *L, | ||
744 | struct rli_iter_d *ds, | ||
745 | struct rli_iter_d *ss, | ||
746 | int op, | ||
747 | fb_data *color) | ||
748 | { | ||
749 | (void) color; | ||
750 | |||
751 | fb_data dst; | ||
752 | fb_data src; | ||
753 | |||
754 | unsigned int params = 3; | ||
755 | int ret = 0; | ||
756 | |||
757 | lua_rawgeti(L, LUA_REGISTRYINDEX, op); | ||
758 | |||
759 | dst = data_get(ds->elem, ds->x, ds->y); | ||
760 | lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(dst)); | ||
761 | lua_pushnumber(L, ds->x); | ||
762 | lua_pushnumber(L, ds->y); | ||
763 | |||
764 | if(ss) /* Allows src to be omitted */ | ||
765 | { | ||
766 | params += 3; | ||
767 | src = data_get(ss->elem, ss->x, ss->y); | ||
768 | lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(src)); | ||
769 | lua_pushnumber(L, ss->x); | ||
770 | lua_pushnumber(L, ss->y); | ||
771 | } | ||
772 | |||
773 | lua_call(L, params, 2); /* call custom function w/ n-params & 2 ret */ | ||
774 | |||
775 | if(!lua_isnoneornil(L, -2)) | ||
776 | { | ||
777 | ret = 1; | ||
778 | dst = FB_SCALARPACK((unsigned) luaL_checknumber(L, -2)); | ||
779 | data_set(ds->elem, ds->x, ds->y, &dst); | ||
780 | } | ||
781 | |||
782 | if(!lua_isnoneornil(L, -1) && ss) | ||
783 | { | ||
784 | ret |= 2; | ||
785 | src = FB_SCALARPACK((unsigned) luaL_checknumber(L, -1)); | ||
786 | data_set(ss->elem, ss->x, ss->y, &src); | ||
787 | } | ||
788 | |||
789 | lua_pop(L, 2); | ||
790 | return ret; /* 0 signals iterator to stop */ | ||
791 | } /* custom_transform */ | ||
792 | |||
793 | /* Pre defined pixel manipulations through rli_copy */ | ||
794 | static int blit_transform(lua_State *L, | ||
795 | struct rli_iter_d *ds, | ||
796 | struct rli_iter_d *ss, | ||
797 | int op, | ||
798 | fb_data *color) | ||
799 | { | ||
800 | (void) L; | ||
801 | unsigned clr = FB_UNPACK_SCALAR_LCD(*color); | ||
802 | unsigned dst = FB_UNPACK_SCALAR_LCD(data_get(ds->elem, ds->x, ds->y)); | ||
803 | unsigned src; | ||
804 | |||
805 | /* Reuse 0 - 7 for src / clr blits*/ | ||
806 | if(op >= 30 && op <= 37) | ||
807 | { | ||
808 | op -= 30; | ||
809 | src = clr; | ||
810 | } | ||
811 | else | ||
812 | src = FB_UNPACK_SCALAR_LCD(data_get(ss->elem, ss->x, ss->y)); | ||
813 | |||
814 | switch(op) | ||
815 | { | ||
816 | default: | ||
817 | /* case 30: */ | ||
818 | case 0: { dst = src; break; }/* copyS/C */ | ||
819 | /* case 31: */ | ||
820 | case 1: { dst = src | dst; break; }/* DorS/C */ | ||
821 | /* case 32: */ | ||
822 | case 2: { dst = src ^ dst; break; }/* DxorS/C */ | ||
823 | /* case 33: */ | ||
824 | case 3: { dst = ~(src | dst); break; }/* nDorS/C */ | ||
825 | /* case 34: */ | ||
826 | case 4: { dst = (~src) | dst; break; }/* DornS/C */ | ||
827 | /* case 35: */ | ||
828 | case 5: { dst = src & dst; break; }/* DandS/C */ | ||
829 | /* case 36: */ | ||
830 | case 6: { dst = src & (~dst); break; }/* nDandS/C */ | ||
831 | /* case 37: */ | ||
832 | case 7: { dst = ~src; break; }/* notS/C */ | ||
833 | |||
834 | /* mask blits */ | ||
835 | case 8: { if(src != 0) { dst = clr; } break; }/* Sand */ | ||
836 | case 9: { if(src == 0) { dst = clr; } break; }/* Snot */ | ||
837 | |||
838 | case 10: { dst = src | clr; break; }/* SorC */ | ||
839 | case 11: { dst = src ^ clr; break; }/* SxorC */ | ||
840 | case 12: { dst = ~(src | clr); break; }/* nSorC */ | ||
841 | case 13: { dst = src | (~clr); break; }/* SornC */ | ||
842 | case 14: { dst = src & clr; break; }/* SandC */ | ||
843 | case 15: { dst = (~src) & clr; break; }/* nSandC */ | ||
844 | case 16: { dst |= (~src) | clr; break; }/* DornSorC */ | ||
845 | case 17: { dst ^= (src & (dst ^ clr)); break; }/* DxorSandDxorC */ | ||
846 | |||
847 | case 18: { if(src != clr) { dst = src; } break; } | ||
848 | case 19: { if(src == clr) { dst = src; } break; } | ||
849 | case 20: { if(src > clr) { dst = src; } break; } | ||
850 | case 21: { if(src < clr) { dst = src; } break; } | ||
851 | |||
852 | case 22: { if(dst != clr) { dst = src; } break; } | ||
853 | case 23: { if(dst == clr) { dst = src; } break; } | ||
854 | case 24: { if(dst > clr) { dst = src; } break; } | ||
855 | case 25: { if(dst < clr) { dst = src; } break; } | ||
856 | |||
857 | case 26: { if(dst != src) { dst = clr; } break; } | ||
858 | case 27: { if(dst == src) { dst = clr; } break; } | ||
859 | case 28: { if(dst > src) { dst = clr; } break; } | ||
860 | case 29: { if(dst < src) { dst = clr; } break; } | ||
861 | |||
862 | }/*switch op*/ | ||
863 | fb_data data = FB_SCALARPACK(dst); | ||
864 | data_set(ds->elem, ds->x, ds->y, &data); | ||
865 | return 1; | ||
866 | } /* blit_transform */ | ||
867 | |||
868 | static int invert_transform(lua_State *L, | ||
869 | struct rli_iter_d *ds, | ||
870 | struct rli_iter_d *ss, | ||
871 | int op, | ||
872 | fb_data *color) | ||
873 | { | ||
874 | (void) L; | ||
875 | (void) color; | ||
876 | (void) op; | ||
877 | (void) ss; | ||
878 | |||
879 | fb_data val = invert_color(data_get(ds->elem, ds->x, ds->y)); | ||
880 | data_set(ds->elem, ds->x, ds->y, &val); | ||
881 | |||
882 | return 1; | ||
883 | } /* invert_transform */ | ||
884 | #endif /* RLI_EXTENDED */ | ||
885 | |||
886 | /* RLI to LUA Interface functions *********************************************/ | ||
887 | RLI_LUA rli_new(lua_State *L) | ||
888 | { /* [width, height] */ | ||
889 | int width = luaL_optint(L, 1, LCD_WIDTH); | ||
890 | int height = luaL_optint(L, 2, LCD_HEIGHT); | ||
891 | |||
892 | luaL_argcheck(L, width > 0, 1, ERR_IDX_RANGE); | ||
893 | luaL_argcheck(L, height > 0, 2, ERR_IDX_RANGE); | ||
97 | 894 | ||
98 | rli_alloc(L, width, height); | 895 | rli_alloc(L, width, height); |
99 | 896 | ||
100 | return 1; | 897 | return 1; |
101 | } | 898 | } |
102 | 899 | ||
103 | static struct rocklua_image* rli_checktype(lua_State *L, int arg) | 900 | RLI_LUA rli_set(lua_State *L) |
104 | { | 901 | { |
105 | void *ud = luaL_checkudata(L, arg, ROCKLUA_IMAGE); | 902 | /*(set) (dst*, [x1, y1, clr, clip]) */ |
106 | luaL_argcheck(L, ud != NULL, arg, "'" ROCKLUA_IMAGE "' expected"); | 903 | /* get and set share the same underlying function */ |
107 | return (struct rocklua_image*) ud; | 904 | return rli_setget(L, false); |
905 | } | ||
906 | |||
907 | RLI_LUA rli_get(lua_State *L) | ||
908 | { | ||
909 | /*(get) (dst*, [x1, y1, clip]) */ | ||
910 | /* get and set share the same underlying function */ | ||
911 | return rli_setget(L, true); | ||
108 | } | 912 | } |
109 | 913 | ||
110 | static int rli_width(lua_State *L) | 914 | RLI_LUA rli_height(lua_State *L) |
915 | { | ||
916 | struct rocklua_image *a = rli_checktype(L, 1); | ||
917 | lua_pushnumber(L, a->height); | ||
918 | return 1; | ||
919 | } | ||
920 | |||
921 | RLI_LUA rli_width(lua_State *L) | ||
111 | { | 922 | { |
112 | struct rocklua_image *a = rli_checktype(L, 1); | 923 | struct rocklua_image *a = rli_checktype(L, 1); |
113 | lua_pushnumber(L, a->width); | 924 | lua_pushnumber(L, a->width); |
114 | return 1; | 925 | return 1; |
115 | } | 926 | } |
116 | 927 | ||
117 | static int rli_height(lua_State *L) | 928 | RLI_LUA rli_equal(lua_State *L) |
118 | { | 929 | { |
119 | struct rocklua_image *a = rli_checktype(L, 1); | 930 | struct rocklua_image *a = rli_checktype(L, 1); |
120 | lua_pushnumber(L, a->height); | 931 | struct rocklua_image *b = rli_checktype(L, 2); |
932 | lua_pushboolean(L, a->data == b->data); | ||
121 | return 1; | 933 | return 1; |
122 | } | 934 | } |
123 | 935 | ||
124 | static fb_data* rli_element(lua_State *L) | 936 | RLI_LUA rli_size(lua_State *L) |
125 | { | 937 | { |
126 | struct rocklua_image *a = rli_checktype(L, 1); | 938 | struct rocklua_image *a = rli_checktype(L, 1); |
127 | int x = luaL_checkint(L, 2); | 939 | lua_pushnumber(L, a->elems); |
128 | int y = luaL_checkint(L, 3); | 940 | return 1; |
941 | } | ||
942 | |||
943 | RLI_LUA rli_raw(lua_State *L) | ||
944 | { | ||
945 | /*val = (img*, index, [new_val]) */ | ||
946 | struct rocklua_image *a = rli_checktype(L, 1); | ||
947 | |||
948 | size_t i = (unsigned) luaL_checkint(L, 2); | ||
949 | |||
950 | fb_data val; | ||
951 | |||
952 | luaL_argcheck(L, i > 0 && i <= (a->elems), 2, ERR_IDX_RANGE); | ||
129 | 953 | ||
130 | luaL_argcheck(L, 1 <= x && x <= a->width, 2, | 954 | lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(a->data[i-1])); |
131 | "index out of range"); | ||
132 | luaL_argcheck(L, 1 <= y && y <= a->height, 3, | ||
133 | "index out of range"); | ||
134 | 955 | ||
135 | /* return element address */ | 956 | if(!lua_isnoneornil(L, 3)) |
136 | return &a->data[a->width * (y - 1) + (x - 1)]; | 957 | { |
958 | val = FB_SCALARPACK((unsigned) luaL_checknumber(L, 3)); | ||
959 | a->data[i-1] = val; | ||
960 | } | ||
961 | |||
962 | return 1; | ||
137 | } | 963 | } |
138 | 964 | ||
139 | static int rli_set(lua_State *L) | 965 | RLI_LUA rli_tostring(lua_State *L) |
140 | { | 966 | { |
141 | fb_data newvalue = FB_SCALARPACK((unsigned)luaL_checknumber(L, 4)); | 967 | /* (img, [infoitem]) */ |
142 | *rli_element(L) = newvalue; | 968 | struct rocklua_image *a = rli_checktype(L, 1); |
143 | return 0; | 969 | |
970 | int item = (unsigned) luaL_optint(L, 2, 0); | ||
971 | size_t bytes = a->elems * sizeof(fb_data); | ||
972 | |||
973 | switch(item) | ||
974 | { | ||
975 | default: | ||
976 | case RLI_INFO_ALL: | ||
977 | { | ||
978 | lua_pushfstring(L, | ||
979 | ROCKLUA_IMAGE ": %dx%d, %d elements, %d bytes, %d-bit depth, %d pack", | ||
980 | a->width, a->height, a->elems, bytes, LCD_DEPTH, LCD_PIXELFORMAT); | ||
981 | break; | ||
982 | } | ||
983 | case RLI_INFO_TYPE: { lua_pushfstring(L, ROCKLUA_IMAGE ); break; } | ||
984 | case RLI_INFO_WIDTH: { lua_pushfstring(L, "%d", a->width ); break; } | ||
985 | case RLI_INFO_HEIGHT: { lua_pushfstring(L, "%d", a->height ); break; } | ||
986 | case RLI_INFO_ELEMS: { lua_pushfstring(L, "%d", a->elems ); break; } | ||
987 | case RLI_INFO_BYTES: { lua_pushfstring(L, "%d", bytes ); break; } | ||
988 | case RLI_INFO_DEPTH: { lua_pushfstring(L, "%d", LCD_DEPTH ); break; } | ||
989 | case RLI_INFO_FORMAT: { lua_pushfstring(L, "%d", LCD_PIXELFORMAT); break; } | ||
990 | case RLI_INFO_ADDRESS: { lua_pushfstring(L, "%p", a->data); break; } | ||
991 | } | ||
992 | |||
993 | return 1; | ||
144 | } | 994 | } |
145 | 995 | ||
146 | static int rli_get(lua_State *L) | 996 | #ifdef RLI_EXTENDED |
997 | RLI_LUA rli_ellipse(lua_State *L) | ||
147 | { | 998 | { |
148 | lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(*rli_element(L))); | 999 | /* (dst*, x1, y1, x2, y2, [clr, fillclr, clip]) */ |
1000 | /* line and ellipse share the same init function */ | ||
1001 | return rli_line_ellipse(L, true); | ||
1002 | } | ||
1003 | |||
1004 | RLI_LUA rli_line(lua_State *L) | ||
1005 | { | ||
1006 | /* (dst*, x1, y1, [x2, y2, clr, clip]) */ | ||
1007 | /* line and ellipse share the same init function */ | ||
1008 | return rli_line_ellipse(L, false); | ||
1009 | } | ||
1010 | |||
1011 | RLI_LUA rli_iterator(lua_State *L) { | ||
1012 | /* see rli_iterator_factory */ | ||
1013 | struct rli_iter_d *ds; | ||
1014 | ds = (struct rli_iter_d *) lua_touserdata(L, lua_upvalueindex(1)); | ||
1015 | |||
1016 | if(ds->dx != 0 || ds->dy != 0) | ||
1017 | { | ||
1018 | lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(data_get(ds->elem, ds->x, ds->y))); | ||
1019 | |||
1020 | lua_pushinteger(L, ds->x); | ||
1021 | lua_pushinteger(L, ds->y); | ||
1022 | |||
1023 | next_rli_iter(ds); /* load next element */ | ||
1024 | |||
1025 | return 3; | ||
1026 | } | ||
1027 | return 0; /* nothing left to do */ | ||
1028 | } | ||
1029 | |||
1030 | RLI_LUA rli_iterator_factory(lua_State *L) { | ||
1031 | /* (src*, [x1, y1, x2, y2, dx, dy]) */ | ||
1032 | struct rocklua_image *a = rli_checktype(L, 1); /*image we wish to iterate*/ | ||
1033 | |||
1034 | struct rli_iter_d *ds; | ||
1035 | |||
1036 | int x1 = luaL_optint(L, 2, 1); | ||
1037 | int y1 = luaL_optint(L, 3, 1); | ||
1038 | int x2 = luaL_optint(L, 4, a->width - x1 + 1); | ||
1039 | int y2 = luaL_optint(L, 5, a->height - y1 + 1); | ||
1040 | int dx = luaL_optint(L, 6, 1); | ||
1041 | int dy = luaL_optint(L, 7, 1); | ||
1042 | |||
1043 | /* create new iter + pushed onto stack */ | ||
1044 | ds = (struct rli_iter_d *) lua_newuserdata(L, sizeof(struct rli_iter_d)); | ||
1045 | |||
1046 | init_rli_iter(ds, a, x1, y1, x2, y2, dx, dy, false, false); | ||
1047 | |||
1048 | /* returns the iter function with embedded iter data(up values) */ | ||
1049 | lua_pushcclosure(L, &rli_iterator, 1); | ||
1050 | |||
149 | return 1; | 1051 | return 1; |
150 | } | 1052 | } |
151 | 1053 | ||
152 | static int rli_tostring(lua_State *L) | 1054 | RLI_LUA rli_marshal(lua_State *L) /* also invert */ |
153 | { | 1055 | { |
1056 | /* (img*, [x1, y1, x2, y2, dx, dy, clip, function]) */ | ||
154 | struct rocklua_image *a = rli_checktype(L, 1); | 1057 | struct rocklua_image *a = rli_checktype(L, 1); |
155 | lua_pushfstring(L, ROCKLUA_IMAGE ": %dx%d", a->width, a->height); | 1058 | |
156 | return 1; | 1059 | struct rli_iter_d ds; |
1060 | |||
1061 | int x1 = luaL_optint(L, 2, 1); | ||
1062 | int y1 = luaL_optint(L, 3, 1); | ||
1063 | int x2 = luaL_optint(L, 4, a->width); | ||
1064 | int y2 = luaL_optint(L, 5, a->height); | ||
1065 | int dx = luaL_optint(L, 6, 1); | ||
1066 | int dy = luaL_optint(L, 7, 1); | ||
1067 | bool clip = luaL_optboolean(L, 8, false); | ||
1068 | |||
1069 | int lf_ref = LUA_NOREF; /* de-ref'd without consequence */ | ||
1070 | |||
1071 | int (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *); | ||
1072 | rli_trans = invert_transform; /* default transformation */ | ||
1073 | |||
1074 | if(!clip) | ||
1075 | { | ||
1076 | bounds_check_xy(L, a, 2, x1, 3, y1); | ||
1077 | bounds_check_xy(L, a, 4, x2, 5, y2); | ||
1078 | } | ||
1079 | |||
1080 | init_rli_iter(&ds, a, x1, y1, x2, y2, dx, dy, false, false); | ||
1081 | |||
1082 | if(lua_isfunction(L, 9)) /* custom function */ | ||
1083 | { | ||
1084 | rli_trans = custom_transform; | ||
1085 | lf_ref = register_luafunc(L, 9); | ||
1086 | } | ||
1087 | |||
1088 | do | ||
1089 | { | ||
1090 | luaL_argcheck(L, clip || (ds.elem != NULL), 1, ERR_DATA_OVF); | ||
1091 | |||
1092 | if(!(*rli_trans)(L, &ds, NULL, lf_ref, NULL)) | ||
1093 | break; /* Custom op can quit early */ | ||
1094 | |||
1095 | } while(next_rli_iter(&ds)); | ||
1096 | |||
1097 | luaL_unref(L, LUA_REGISTRYINDEX, lf_ref); /* de-reference custom function */ | ||
1098 | return 0; | ||
1099 | } | ||
1100 | |||
1101 | RLI_LUA rli_copy(lua_State *L) | ||
1102 | { | ||
1103 | /* (dst*, src*, [d_x, d_y, s_x, s_y, x_off, y_off, clip, [op, funct/clr]]) */ | ||
1104 | struct rocklua_image *d = rli_checktype(L, 1); /*dst*/ | ||
1105 | struct rocklua_image *s = rli_checktype(L, 2); /*src*/ | ||
1106 | |||
1107 | struct rli_iter_d ds; /*dst*/ | ||
1108 | struct rli_iter_d ss; /*src*/ | ||
1109 | |||
1110 | /* copy whole image if possible */ | ||
1111 | if(s->elems == d->elems && s->width == d->width && lua_gettop(L) < 3) | ||
1112 | { | ||
1113 | memcpy(d->data, s->data, d->elems * sizeof(fb_data)); | ||
1114 | return 0; | ||
1115 | } | ||
1116 | |||
1117 | int d_x = luaL_optint(L, 3, 1); | ||
1118 | int d_y = luaL_optint(L, 4, 1); | ||
1119 | int s_x = luaL_optint(L, 5, 1); | ||
1120 | int s_y = luaL_optint(L, 6, 1); | ||
1121 | |||
1122 | int w = MIN(d->width - d_x, s->width - s_x); | ||
1123 | int h = MIN(d->height - d_y, s->height - s_y); | ||
1124 | |||
1125 | int x_off = luaL_optint(L, 7, w); | ||
1126 | int y_off = luaL_optint(L, 8, h); | ||
1127 | |||
1128 | bool clip = luaL_optboolean(L, 9, false); | ||
1129 | int op = luaL_optint(L, 10, 0); | ||
1130 | fb_data clr = FB_SCALARPACK(0); /* 11 is custom function | color */ | ||
1131 | |||
1132 | bool d_swx = (x_off < 0); /* dest swap */ | ||
1133 | bool d_swy = (y_off < 0); | ||
1134 | bool s_swx = false; /* src swap */ | ||
1135 | bool s_swy = false; | ||
1136 | |||
1137 | int (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *); | ||
1138 | rli_trans = blit_transform; /* default transformation */ | ||
1139 | |||
1140 | int lf_ref = LUA_NOREF; /* de-ref'd without consequence */ | ||
1141 | |||
1142 | if(!clip) /* Out of bounds is not allowed */ | ||
1143 | { | ||
1144 | bounds_check_xy(L, d, 3, d_x, 4, d_y); | ||
1145 | bounds_check_xy(L, s, 5, s_x, 6, s_y); | ||
1146 | } | ||
1147 | else if (w < 0 || h < 0) /* not an error but nothing to display */ | ||
1148 | return 0; | ||
1149 | |||
1150 | w = MIN(w, ABS(x_off)); | ||
1151 | h = MIN(h, ABS(y_off)); | ||
1152 | |||
1153 | /* if(s->data == d->data) need to care about fill direction */ | ||
1154 | if(d_x > s_x) | ||
1155 | { | ||
1156 | d_swx = !d_swx; | ||
1157 | s_swx = !s_swx; | ||
1158 | } | ||
1159 | |||
1160 | if(d_y > s_y) | ||
1161 | { | ||
1162 | d_swy = !d_swy; | ||
1163 | s_swy = !s_swy; | ||
1164 | } | ||
1165 | |||
1166 | init_rli_iter(&ds, d, d_x, d_y, d_x + w, d_y + h, 1, 1, d_swx, d_swy); | ||
1167 | |||
1168 | init_rli_iter(&ss, s, s_x, s_y, s_x + w, s_y + h, 1, 1, s_swx, s_swy); | ||
1169 | |||
1170 | if (op == 0xFF && lua_isfunction(L, 11)) /* custom function specified.. */ | ||
1171 | { | ||
1172 | rli_trans = custom_transform; | ||
1173 | lf_ref = register_luafunc(L, 11); | ||
1174 | op = lf_ref; | ||
1175 | } | ||
1176 | else | ||
1177 | clr = FB_SCALARPACK((unsigned) luaL_optnumber(L, 11, LCD_BLACK)); | ||
1178 | |||
1179 | do | ||
1180 | { | ||
1181 | if(!clip) | ||
1182 | { | ||
1183 | luaL_argcheck(L, ss.elem != NULL, 2, ERR_DATA_OVF); | ||
1184 | luaL_argcheck(L, ds.elem != NULL, 1, ERR_DATA_OVF); | ||
1185 | } | ||
1186 | |||
1187 | if(!(*rli_trans)(L, &ds, &ss, op, &clr)) | ||
1188 | break; /* Custom op can quit early */ | ||
1189 | |||
1190 | } while(next_rli_iter(&ds) && next_rli_iter(&ss)); | ||
1191 | |||
1192 | luaL_unref(L, LUA_REGISTRYINDEX, lf_ref); /* de-reference custom function */ | ||
1193 | return 0; | ||
1194 | } | ||
1195 | |||
1196 | RLI_LUA rli_clear(lua_State *L) | ||
1197 | { | ||
1198 | /* (dst*, [color, x1, y1, x2, y2, clip]) */ | ||
1199 | struct rocklua_image *a = rli_checktype(L, 1); | ||
1200 | |||
1201 | struct rli_iter_d ds; | ||
1202 | |||
1203 | fb_data clr = FB_SCALARPACK((unsigned) luaL_optnumber(L, 2, LCD_BLACK)); | ||
1204 | int x1 = luaL_optint(L, 3, 1); | ||
1205 | int y1 = luaL_optint(L, 4, 1); | ||
1206 | int x2 = luaL_optint(L, 5, a->width); | ||
1207 | int y2 = luaL_optint(L, 6, a->height); | ||
1208 | bool clip = luaL_optboolean(L, 7, false); | ||
1209 | |||
1210 | if(!clip) | ||
1211 | { | ||
1212 | bounds_check_xy(L, a, 3, x1, 4, y1); | ||
1213 | bounds_check_xy(L, a, 5, x2, 6, y2); | ||
1214 | } | ||
1215 | |||
1216 | init_rli_iter(&ds, a, x1, y1, x2, y2, 1, 1, false, false); | ||
1217 | |||
1218 | do | ||
1219 | { | ||
1220 | luaL_argcheck(L, clip || (ds.elem != NULL), 1, ERR_DATA_OVF); | ||
1221 | |||
1222 | data_set(ds.elem, ds.x, ds.y, &clr); | ||
1223 | |||
1224 | } while(next_rli_iter(&ds)); | ||
1225 | |||
1226 | return 0; | ||
157 | } | 1227 | } |
1228 | #endif /* RLI_EXTENDED */ | ||
158 | 1229 | ||
1230 | /* Rli Image methods exported to lua */ | ||
159 | static const struct luaL_reg rli_lib [] = | 1231 | static const struct luaL_reg rli_lib [] = |
160 | { | 1232 | { |
161 | {"__tostring", rli_tostring}, | 1233 | {"__tostring", rli_tostring}, |
162 | {"set", rli_set}, | 1234 | {"_data", rli_raw}, |
163 | {"get", rli_get}, | 1235 | {"__len", rli_size}, |
164 | {"width", rli_width}, | 1236 | {"__eq", rli_equal}, |
165 | {"height", rli_height}, | 1237 | {"width", rli_width}, |
1238 | {"height", rli_height}, | ||
1239 | {"set", rli_set}, | ||
1240 | {"get", rli_get}, | ||
1241 | |||
1242 | #ifdef RLI_EXTENDED | ||
1243 | {"copy", rli_copy}, | ||
1244 | {"clear", rli_clear}, | ||
1245 | {"invert", rli_marshal}, | ||
1246 | {"marshal", rli_marshal}, | ||
1247 | {"points", rli_iterator_factory}, | ||
1248 | {"line", rli_line}, | ||
1249 | {"ellipse", rli_ellipse}, | ||
1250 | #endif /* RLI_EXTENDED */ | ||
166 | {NULL, NULL} | 1251 | {NULL, NULL} |
167 | }; | 1252 | }; |
168 | 1253 | ||
169 | static inline void rli_init(lua_State *L) | 1254 | static inline void rli_init(lua_State *L) |
170 | { | 1255 | { |
1256 | /* some devices need x | y coords shifted to match native format */ | ||
1257 | /* conversion between packed native formats and individual pixel addressing */ | ||
1258 | init_pixelmask(&x_shift, &y_shift, &xy_mask, &pixelmask); | ||
1259 | |||
171 | luaL_newmetatable(L, ROCKLUA_IMAGE); | 1260 | luaL_newmetatable(L, ROCKLUA_IMAGE); |
172 | 1261 | ||
173 | lua_pushstring(L, "__index"); | 1262 | lua_pushstring(L, "__index"); |
@@ -189,57 +1278,45 @@ static inline void rli_init(lua_State *L) | |||
189 | #define SIMPLE_VOID_WRAPPER(func) RB_WRAP(func) { (void)L; func(); return 0; } | 1278 | #define SIMPLE_VOID_WRAPPER(func) RB_WRAP(func) { (void)L; func(); return 0; } |
190 | 1279 | ||
191 | /* Helper function for opt_viewport */ | 1280 | /* Helper function for opt_viewport */ |
192 | static void check_tablevalue(lua_State *L, const char* key, int tablepos, void* res, bool unsigned_val) | 1281 | static void check_tablevalue(lua_State *L, |
1282 | const char* key, | ||
1283 | int tablepos, | ||
1284 | void* res, | ||
1285 | bool is_unsigned) | ||
193 | { | 1286 | { |
194 | lua_getfield(L, tablepos, key); /* Find table[key] */ | 1287 | lua_getfield(L, tablepos, key); /* Find table[key] */ |
195 | 1288 | ||
196 | if(!lua_isnoneornil(L, -1)) | 1289 | int val = luaL_optint(L, -1, 0); |
197 | { | 1290 | |
198 | if(unsigned_val) | 1291 | if(is_unsigned) |
199 | *(unsigned*)res = luaL_checkint(L, -1); | 1292 | *(unsigned*)res = (unsigned) val; |
200 | else | 1293 | else |
201 | *(int*)res = luaL_checkint(L, -1); | 1294 | *(int*)res = val; |
202 | } | ||
203 | 1295 | ||
204 | lua_pop(L, 1); /* Pop the value off the stack */ | 1296 | lua_pop(L, 1); /* Pop the value off the stack */ |
205 | } | 1297 | } |
206 | 1298 | ||
207 | static struct viewport* opt_viewport(lua_State *L, int narg, struct viewport* alt) | 1299 | static inline struct viewport* opt_viewport(lua_State *L, |
1300 | int narg, | ||
1301 | struct viewport* vp, | ||
1302 | struct viewport* alt) | ||
208 | { | 1303 | { |
209 | if(lua_isnoneornil(L, narg)) | 1304 | if(lua_isnoneornil(L, narg)) |
210 | return alt; | 1305 | return alt; |
211 | 1306 | ||
212 | int tablepos = lua_gettop(L); | ||
213 | struct viewport *vp; | ||
214 | |||
215 | lua_getfield(L, tablepos, "vp"); /* get table['vp'] */ | ||
216 | if(lua_isnoneornil(L, -1)) | ||
217 | { | ||
218 | lua_pop(L, 1); /* Pop nil off stack */ | ||
219 | |||
220 | vp = (struct viewport*) lua_newuserdata(L, sizeof(struct viewport)); /* Allocate memory and push it as udata on the stack */ | ||
221 | memset(vp, 0, sizeof(struct viewport)); /* Init viewport values to 0 */ | ||
222 | lua_setfield(L, tablepos, "vp"); /* table['vp'] = vp (pops value off the stack) */ | ||
223 | } | ||
224 | else | ||
225 | { | ||
226 | vp = (struct viewport*) lua_touserdata(L, -1); /* Reuse viewport struct */ | ||
227 | lua_pop(L, 1); /* We don't need the value on stack */ | ||
228 | } | ||
229 | |||
230 | luaL_checktype(L, narg, LUA_TTABLE); | 1307 | luaL_checktype(L, narg, LUA_TTABLE); |
231 | 1308 | ||
232 | check_tablevalue(L, "x", tablepos, &vp->x, false); | 1309 | check_tablevalue(L, "x", narg, &vp->x, false); |
233 | check_tablevalue(L, "y", tablepos, &vp->y, false); | 1310 | check_tablevalue(L, "y", narg, &vp->y, false); |
234 | check_tablevalue(L, "width", tablepos, &vp->width, false); | 1311 | check_tablevalue(L, "width", narg, &vp->width, false); |
235 | check_tablevalue(L, "height", tablepos, &vp->height, false); | 1312 | check_tablevalue(L, "height", narg, &vp->height, false); |
236 | #ifdef HAVE_LCD_BITMAP | 1313 | #ifdef HAVE_LCD_BITMAP |
237 | check_tablevalue(L, "font", tablepos, &vp->font, false); | 1314 | check_tablevalue(L, "font", narg, &vp->font, false); |
238 | check_tablevalue(L, "drawmode", tablepos, &vp->drawmode, false); | 1315 | check_tablevalue(L, "drawmode", narg, &vp->drawmode, false); |
239 | #endif | 1316 | #endif |
240 | #if LCD_DEPTH > 1 | 1317 | #if LCD_DEPTH > 1 |
241 | check_tablevalue(L, "fg_pattern", tablepos, &vp->fg_pattern, true); | 1318 | check_tablevalue(L, "fg_pattern", narg, &vp->fg_pattern, true); |
242 | check_tablevalue(L, "bg_pattern", tablepos, &vp->bg_pattern, true); | 1319 | check_tablevalue(L, "bg_pattern", narg, &vp->bg_pattern, true); |
243 | #endif | 1320 | #endif |
244 | 1321 | ||
245 | return vp; | 1322 | return vp; |
@@ -247,9 +1324,9 @@ static struct viewport* opt_viewport(lua_State *L, int narg, struct viewport* al | |||
247 | 1324 | ||
248 | RB_WRAP(set_viewport) | 1325 | RB_WRAP(set_viewport) |
249 | { | 1326 | { |
250 | struct viewport *vp = opt_viewport(L, 1, NULL); | 1327 | static struct viewport vp; |
251 | int screen = luaL_optint(L, 2, SCREEN_MAIN); | 1328 | int screen = luaL_optint(L, 2, SCREEN_MAIN); |
252 | rb->screens[screen]->set_viewport(vp); | 1329 | rb->screens[screen]->set_viewport(opt_viewport(L, 1, &vp, NULL)); |
253 | return 0; | 1330 | return 0; |
254 | } | 1331 | } |
255 | 1332 | ||
@@ -801,3 +1878,4 @@ LUALIB_API int luaopen_rock(lua_State *L) | |||
801 | 1878 | ||
802 | return 1; | 1879 | return 1; |
803 | } | 1880 | } |
1881 | |||