summaryrefslogtreecommitdiff
path: root/apps/plugins/lua
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lua')
-rw-r--r--apps/plugins/lua/include_lua/blit.lua85
-rw-r--r--apps/plugins/lua/include_lua/color.lua117
-rw-r--r--apps/plugins/lua/include_lua/draw.lua469
-rw-r--r--apps/plugins/lua/include_lua/image.lua392
-rw-r--r--apps/plugins/lua/include_lua/lcd.lua154
-rw-r--r--apps/plugins/lua/include_lua/math_ex.lua159
-rw-r--r--apps/plugins/lua/include_lua/print.lua378
-rw-r--r--apps/plugins/lua/include_lua/timer.lua115
-rw-r--r--apps/plugins/lua/lua.make9
-rw-r--r--apps/plugins/lua/rocklib.c1254
10 files changed, 3043 insertions, 89 deletions
diff --git a/apps/plugins/lua/include_lua/blit.lua b/apps/plugins/lua/include_lua/blit.lua
new file mode 100644
index 0000000000..6c5ea377e4
--- /dev/null
+++ b/apps/plugins/lua/include_lua/blit.lua
@@ -0,0 +1,85 @@
1--[[ Lua Blit Operations
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[
25copy(dst, src, [dx, dy, sx, sy, offset_x, offset_y, clip, _blit.OP, clr/customfunct])
26 blit allows you to copy a [portion of a] source image to a dest image applying
27 a transformation operation to the pixels as they are copied
28 offsets are auto calculated if left empty or out of range
29 blit will default to copy if operation is empty or out of range
30
31it is slightly faster to use the number directly and you don't really
32 need to define all (any) of these if you don't use them but I put them
33 here for easier use of the blit function
34]]
35if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
36
37local _blit ={} do
38
39 _blit.CUSTOM = 0xFF --user defined blit function func(dst_val, x, y, src_val, x, y)
40 _blit.BCOPY = 0x0 --copy (use :copy() instead it is slightly faster
41 _blit.BOR = 0x1 --OR source and dest pixels
42 _blit.BXOR = 0x2 --XOR source and dest pixels
43 _blit.BNOR = 0x3 --(NOT) (source OR dest pixels)
44 _blit.BSNOR = 0x4 --(NOT source) OR dest pixels
45 _blit.BAND = 0x5 --AND source and dest pixels
46 _blit.BNAND = 0x6 --(NOT) AND source and dest pixels
47 _blit.BNOT = 0x7 --NOT source and dest pixels
48 --blit functions for masks
49 _blit.BSAND = 0x8 --copy color to dest if source pixel <> 0
50 _blit.BSNOT = 0x9 --copy color to dest if source pixel == 0
51 --blit functions for masks with colors
52 _blit.BSORC = 0xA --copy source pixel or color
53 _blit.BSXORC = 0xB --copy source pixel xor color
54 _blit.BNSORC = 0xC --copy ~(src_val | clr)
55 _blit.BSORNC = 0xD --copy src_val | (~clr)
56 _blit.BSANDC = 0xE --copy src_val & clr;
57 _blit.BNSANDC = 0xF --copy (~src_val) & clr
58 _blit.BDORNSORC = 0x10 --copy dst | (~src_val) | clr
59 _blit.BXORSADXORC = 0x11 --copy dst ^ (src_val & (dst_val ^ clr))
60
61 _blit.BSNEC = 0x12 --copy source pixel if source <> color
62 _blit.BSEQC = 0x13 --copy source pixel if source == color
63 _blit.BSGTC = 0x14 --copy source pixel if source > color
64 _blit.BSLTC = 0x15 --copy source pixel if source < color
65 _blit.BDNEC = 0x16 --copy source pixel if dest <> color
66 _blit.BDEQC = 0x17 --copy source pixel if dest == color
67 _blit.BDGTC = 0x18 --copy source pixel if dest > color
68 _blit.BDLTC = 0x19 --copy source pixel if dest < color
69 _blit.BDNES = 0x1A --copy color to dest if dest <> source pixel
70 _blit.BDEQS = 0x1B --copy color to dest if dest == source pixel
71 _blit.BDGTS = 0x1C --copy color to dest if dest > source pixel
72 _blit.BDLTS = 0x1D --copy color to dest if dest < source pixel
73 --Source unused for these blits
74 _blit.BCOPYC = 0x1E --copy color
75 _blit.BORC = 0x1F --OR dest and color
76 _blit.BXORC = 0x20 --XOR dest and color
77 _blit.BNDORC = 0x21 --~(dst_val | clr)
78 _blit.BDORNC = 0x22 --dst_val | (~clr)
79 _blit.BANDC = 0x23 --AND dest and color
80 _blit.BNDANDC = 0x24 --copy (~dst_val) & clr
81 _blit.BDLTS = 0x25 --dest NOT color
82end -- _blit operations
83
84return _blit
85
diff --git a/apps/plugins/lua/include_lua/color.lua b/apps/plugins/lua/include_lua/color.lua
new file mode 100644
index 0000000000..ed2e4f865e
--- /dev/null
+++ b/apps/plugins/lua/include_lua/color.lua
@@ -0,0 +1,117 @@
1--[[ Lua Color functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _clr.inc
27 _clr.set
28
29-- Exposed Constants
30 IS_COLOR_TARGET
31
32]]
33if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
34
35IS_COLOR_TARGET = false
36-- Only true when we're on a color target, i.e. when LCD_RGBPACK is available
37if rb.lcd_rgbpack ~= _NIL then
38 IS_COLOR_TARGET = true
39end
40
41local _clr = {} do
42
43 -- Internal Constants
44 local _NIL = nil -- _NIL placeholder
45
46 local maxstate = (bit.lshift(1, rb.LCD_DEPTH) - 1)
47
48 if rb.LCD_DEPTH > 24 then -- no alpha channels
49 maxstate = (bit.lshift(1, 24) - 1)
50 end
51
52 local function init(v)
53 return v or 0
54 end
55
56 -- clamps value to >= min and <= max rolls over to opposite
57 local function clamp_roll(val, min, max)
58 if min > max then
59 local swap = min
60 min, max = max, swap
61 end
62
63 if val < min then
64 val = max
65 elseif val > max then
66 val = min
67 end
68
69 return val
70 end
71
72 -- sets color -- monochrome / greyscale use 'set' -- color targets 'r,b,g'
73 -- on monochrome/ greyscale targets:
74 -- '-1' sets the highest 'color' state & 0 is the minimum 'color' state
75 local function clrset(set, r, g, b)
76 local color = set or 0
77
78 if IS_COLOR_TARGET then
79 if (r ~= _NIL or g ~= _NIL or b ~= _NIL) then
80 r, g, b = init(r), init(g), init(b)
81 color = rb.lcd_rgbpack(r, g, b)
82 end
83 end
84
85 return clamp_roll(color, 0, maxstate)
86 end -- clrset
87
88 -- de/increments current color by 'inc' -- optionally color targets by 'r,g,b'
89 local function clrinc(current, inc, r, g, b)
90 local color = 0
91 current = current or color
92 inc = inc or 1
93
94 if IS_COLOR_TARGET then
95 local ru, gu, bu = rb.lcd_rgbunpack(current);
96 if (r ~= _NIL or g ~= _NIL or b ~= _NIL) then
97 r, g, b = init(r), init(g), init(b)
98 ru = ru + r; gu = gu + g; bu = bu + b
99 color = rb.lcd_rgbpack(ru, gu, bu)
100 else
101 ru = ru + inc; gu = gu + inc; bu = bu + inc
102 color = rb.lcd_rgbpack(ru, gu, bu)
103 end
104 else
105 color = current + inc
106 end
107
108 return clamp_roll(color, 0, maxstate)
109 end -- clrinc
110
111 -- expose functions to the outside through _clr table
112 _clr.set = clrset
113 _clr.inc = clrinc
114end -- color functions
115
116return _clr
117
diff --git a/apps/plugins/lua/include_lua/draw.lua b/apps/plugins/lua/include_lua/draw.lua
new file mode 100644
index 0000000000..4409ab7838
--- /dev/null
+++ b/apps/plugins/lua/include_lua/draw.lua
@@ -0,0 +1,469 @@
1--[[ Lua Drawing functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _draw.circle
27 _draw.circle_filled
28 _draw.ellipse
29 _draw.ellipse_filled
30 _draw.ellipse_rect_filled
31 _draw.ellipse_rect
32 _draw.flood_fill
33 _draw.hline
34 _draw.image
35 _draw.line
36 _draw.polygon
37 _draw.polyline
38 _draw.rect
39 _draw.rect_filled
40 _draw.rounded_rect
41 _draw.rounded_rect_filled
42 _draw.text
43 _draw.vline
44
45]]
46
47--[[ bClip allows drawing out of bounds without raising an error it is slower
48 than having a correctly bounded figure, but can be helpful in some cases..
49]]
50
51if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
52
53local _draw = {} do
54
55 -- Internal Constants
56 local _LCD = rb.lcd_framebuffer()
57 local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT
58 local BSAND = 8 -- blits color to dst if src <> 0
59 local _NIL = nil -- nil placeholder
60
61 local function set_viewport(vp)
62 if not vp then rb.set_viewport() return end
63 if rb.LCD_DEPTH == 2 then -- invert 2-bit screens
64 --vp.drawmode = bit.bxor(vp.drawmode, 4)
65 vp.fg_pattern = 3 - vp.fg_pattern
66 vp.bg_pattern = 3 - vp.bg_pattern
67 end
68 rb.set_viewport(vp)
69 end
70
71 -- line
72 local function line(img, x1, y1, x2, y2, color, bClip)
73 img:line(x1, y1, x2, y2, color, bClip)
74 end
75
76 -- horizontal line; x, y define start point; length in horizontal direction
77 local function hline(img, x, y , length, color, bClip)
78 img:line(x, y, x + length, _NIL, color, bClip)
79 end
80
81 -- vertical line; x, y define start point; length in vertical direction
82 local function vline(img, x, y , length, color, bClip)
83 img:line(x, y, _NIL, y + length, color, bClip)
84 end
85
86 -- draws a non-filled figure based on points in t-points
87 local function polyline(img, x, y, t_points, color, bClosed, bClip)
88 if #t_points < 2 then error("not enough points", 3) end
89
90 local pt_first_last
91
92 if bClosed then
93 pt_first_last = t_points[1]
94 else
95 pt_first_last = t_points[#t_points]
96 end
97
98 for i = 1, #t_points, 1 do
99 local pt1 = t_points[i]
100
101 local pt2 = t_points[i + 1] or pt_first_last-- first and last point
102
103 img:line(pt1[1] + x, pt1[2] + y, pt2[1]+x, pt2[2]+y, color, bClip)
104 end
105
106 end
107
108 -- rectangle
109 local function rect(img, x, y, width, height, color, bClip)
110 if width == 0 or height == 0 then return end
111
112 local ppt = {{0, 0}, {width, 0}, {width, height}, {0, height}}
113 polyline(img, x, y, ppt, color, true, bClip)
114 --[[
115 vline(img, x, y, height, color, bClip);
116 vline(img, x + width, y, height, color, bClip);
117 hline(img, x, y, width, color, bClip);
118 hline(img, x, y + height, width, color, bClip);]]
119 end
120
121 -- filled rect, fillcolor is color if left empty
122 local function rect_filled(img, x, y, width, height, color, fillcolor, bClip)
123 if width == 0 or height == 0 then return end
124
125 if not fillcolor then
126 img:clear(color, x, y, x + width, y + height, bClip)
127 else
128 img:clear(fillcolor, x, y, x + width, y + height, bClip)
129 rect(img, x, y, width, height, color, bClip)
130 end
131 end
132
133 -- circle cx,cy define center point
134 local function circle(img, cx, cy, radius, color, bClip)
135 local r = radius
136 img:ellipse(cx - r, cy - r, cx + r, cy + r, color, _NIL, bClip)
137 end
138
139 -- filled circle cx,cy define center, fillcolor is color if left empty
140 local function circle_filled(img, cx, cy, radius, color, fillcolor, bClip)
141 fillcolor = fillcolor or color
142 local r = radius
143 img:ellipse(cx - r, cy - r, cx + r, cy + r, color, fillcolor, bClip)
144 end
145
146 -- ellipse that fits into defined rect
147 local function ellipse_rect(img, x1, y1, x2, y2, color, bClip)
148 img:ellipse(x1, y1, x2, y2, color, _NIL, bClip)
149 end
150
151 --ellipse that fits into defined rect, fillcolor is color if left empty
152 local function ellipse_rect_filled(img, x1, y1, x2, y2, color, fillcolor, bClip)
153 if not fillcolor then fillcolor = color end
154
155 img:ellipse(x1, y1, x2, y2, color, fillcolor, bClip)
156 end
157
158 -- ellipse cx, cy define center point; a, b the major/minor axis
159 local function ellipse(img, cx, cy, a, b, color, bClip)
160 img:ellipse(cx - a, cy - b, cx + a, cy + b, color, _NIL, bClip)
161 end
162
163 -- filled ellipse cx, cy define center point; a, b the major/minor axis
164 -- fillcolor is color if left empty
165 local function ellipse_filled(img, cx, cy, a, b, color, fillcolor, bClip)
166 if not fillcolor then fillcolor = color end
167
168 img:ellipse(cx - a, cy - b, cx + a, cy + b, color, fillcolor, bClip)
169 end
170
171 -- rounded rectangle
172 local function rounded_rect(img, x, y, w, h, radius, color, bClip)
173 local c_img
174
175 local function blit(dx, dy, sx, sy, ox, oy)
176 img:copy(c_img, dx, dy, sx, sy, ox, oy, bClip, BSAND, color)
177 end
178
179 if w == 0 or h == 0 then return end
180
181 -- limit the radius of the circle otherwise it will overtake the rect
182 radius = math.min(w / 2, radius)
183 radius = math.min(h / 2, radius)
184
185 local r = radius
186
187 c_img = rb.new_image(r * 2 + 1, r * 2 + 1)
188 c_img:clear(0)
189 circle(c_img, r + 1, r + 1, r, 0xFFFFFF)
190
191 -- copy 4 pieces of circle to their respective corners
192 blit(x, y, _NIL, _NIL, r + 1, r + 1) --TL
193 blit(x + w - r - 2, y, r, _NIL, r + 1, r + 1) --TR
194 blit(x , y + h - r - 2, _NIL, r, r + 1, _NIL) --BL
195 blit(x + w - r - 2, y + h - r - 2, r, r, r + 1, r + 1)--BR
196 c_img = _NIL
197
198 vline(img, x, y + r, h - r * 2, color, bClip);
199 vline(img, x + w - 1, y + r, h - r * 2, color, bClip);
200 hline(img, x + r, y, w - r * 2, color, bClip);
201 hline(img, x + r, y + h - 1, w - r * 2, color, bClip);
202 end
203
204 -- rounded rectangle fillcolor is color if left empty
205 local function rounded_rect_filled(img, x, y, w, h, radius, color, fillcolor, bClip)
206 local c_img
207
208 local function blit(dx, dy, sx, sy, ox, oy)
209 img:copy(c_img, dx, dy, sx, sy, ox, oy, bClip, BSAND, fillcolor)
210 end
211
212 if w == 0 or h == 0 then return end
213
214 if not fillcolor then fillcolor = color end
215
216 -- limit the radius of the circle otherwise it will overtake the rect
217 radius = math.min(w / 2, radius)
218 radius = math.min(h / 2, radius)
219
220 local r = radius
221
222 c_img = rb.new_image(r * 2 + 1, r * 2 + 1)
223 c_img:clear(0)
224 circle_filled(c_img, r + 1, r + 1, r, fillcolor)
225
226 -- copy 4 pieces of circle to their respective corners
227 blit(x, y, _NIL, _NIL, r + 1, r + 1) --TL
228 blit(x + w - r - 2, y, r, _NIL, r + 1, r + 1) --TR
229 blit(x, y + h - r - 2, _NIL, r, r + 1, _NIL) --BL
230 blit(x + w - r - 2, y + h - r - 2, r, r, r + 1, r + 1) --BR
231 c_img = _NIL
232
233 -- finish filling areas circles didn't cover
234 img:clear(fillcolor, x + r, y, x + w - r, y + h - 1, bClip)
235 img:clear(fillcolor, x, y + r, x + r, y + h - r, bClip)
236 img:clear(fillcolor, x + w - r, y + r, x + w - 1, y + h - r - 1, bClip)
237
238 if fillcolor ~= color then
239 rounded_rect(img, x, y, w, h, r, color, bClip)
240 end
241 end
242
243 -- draws an image at xy coord in dest image
244 local function image(dst, src, x, y, bClip)
245 if not src then --make sure an image was passed, otherwise bail
246 rb.splash(rb.HZ, "No Image!")
247 return _NIL
248 end
249
250 dst:copy(src, x, y, 1, 1, _NIL, _NIL, bClip)
251 end
252
253 -- floods an area of targetclr with fillclr x, y specifies the start seed
254 function flood_fill(img, x, y, targetclr, fillclr)
255 -- scanline 4-way flood algorithm
256 -- ^
257 -- <--------x--->
258 -- v
259 -- check that target color doesn't = fill and the first point is target color
260 if targetclr == fillclr or targetclr ~= img:get(x,y, true) then return end
261 local max_w = img:width()
262 local max_h = img:height()
263
264 local qpt = {} -- FIFO queue
265 -- rather than moving elements around in our FIFO queue
266 -- for each read; increment 'qhead' by 2
267 -- set both elements to nil and let the
268 -- garbage collector worry about it
269 -- for each write; increment 'qtail' by 2
270 -- x coordinates are in odd indices while
271 -- y coordinates are in even indices
272
273 local qtail = 0
274 local iter_n; -- North iteration
275 local iter_s; -- South iteration
276
277 local function check_ns(val, x, y)
278 if targetclr == val then
279 if targetclr == iter_n() then
280 qtail = qtail + 2
281 qpt[qtail - 1] = x
282 qpt[qtail] = (y - 1)
283 end
284
285 if targetclr == iter_s() then
286 qtail = qtail + 2
287 qpt[qtail - 1] = x
288 qpt[qtail] = (y + 1)
289 end
290 return fillclr
291 end
292 return _NIL -- signal marshal to stop
293 end
294
295 local function seed_pt(x, y)
296 -- will never hit max_w * max_h^2 but make sure not to end early
297 for qhead = 2, max_w * max_h * max_w * max_h, 2 do
298
299 if targetclr == img:get(x, y, true) then
300 iter_n = img:points(x, y - 1, 1, y - 1)
301 iter_s = img:points(x, y + 1, 1, y + 1)
302 img:marshal(x, y, 1, y, _NIL, _NIL, true, check_ns)
303
304 iter_n = img:points(x + 1, y - 1, max_w, y - 1)
305 iter_s = img:points(x + 1, y + 1, max_w, y + 1)
306 img:marshal(x + 1, y, max_w, y, _NIL, _NIL, true, check_ns)
307 end
308
309 x = qpt[qhead - 1]
310 qpt[qhead - 1] = _NIL
311
312 if not x then break end
313
314 y = qpt[qhead]
315 qpt[qhead] = _NIL
316 end
317 end
318
319 seed_pt(x, y) -- Begin
320 end -- flood_fill
321
322 -- draws a closed figure based on points in t_points
323 local function polygon(img, x, y, t_points, color, fillcolor, bClip)
324 if #t_points < 2 then error("not enough points", 3) end
325
326 if fillcolor then
327 local x_min, x_max = 0, 0
328 local y_min, y_max = 0, 0
329 local w, h = 0, 0
330 -- find boundries of polygon
331 for i = 1, #t_points, 1 do
332 local pt = t_points[i]
333 if pt[1] < x_min then x_min = pt[1] end
334 if pt[1] > x_max then x_max = pt[1] end
335 if pt[2] < y_min then y_min = pt[2] end
336 if pt[2] > y_max then y_max = pt[2] end
337 end
338 w = math.abs(x_max) + math.abs(x_min)
339 h = math.abs(y_max) + math.abs(y_min)
340 x_min = x_min - 2 -- leave a border to use flood_fill
341 y_min = y_min - 2
342
343 local fill_img = rb.new_image(w + 3, h + 3)
344 fill_img:clear(0xFFFFFF)
345
346 for i = 1, #t_points, 1 do
347 local pt1 = t_points[i]
348 local pt2 = t_points[i + 1] or t_points[1]-- first and last point
349 fill_img:line(pt1[1] - x_min, pt1[2] - y_min,
350 pt2[1]- x_min, pt2[2] - y_min, 0)
351
352 end
353 flood_fill(fill_img, fill_img:width(), fill_img:height() , 1, 0)
354 img:copy(fill_img, x - 1, y - 1, _NIL, _NIL, _NIL, _NIL, bClip, BSAND, fillcolor)
355 end
356
357 polyline(img, x, y, t_points, color, true, bClip)
358 end
359
360 -- draw text onto image if width/height are supplied text is centered
361 local function text(img, x, y, width, height, font, color, text)
362 font = font or rb.FONT_UI
363
364 local opts = {x = 0, y = 0, width = LCD_W - 1, height = LCD_H - 1,
365 font = font, drawmode = 3, fg_pattern = 0xFFFFFF, bg_pattern = 0}
366 set_viewport(opts)
367
368 local res, w, h = rb.font_getstringsize(text, font)
369
370 if not width then
371 width = 0
372 else
373 width = (width - w) / 2
374 end
375
376 if not height then
377 height = 0
378 else
379 height = (height - h) / 2
380 end
381
382 -- make a copy of the current screen for later
383 local screen_img = rb.new_image(LCD_W, LCD_H)
384 screen_img:copy(_LCD)
385
386 -- check if the screen buffer is supplied image if so set img to the copy
387 if img == _LCD then
388 img = screen_img
389 end
390
391 -- we will be printing the text to the screen then blitting into img
392 rb.lcd_clear_display()
393
394 local function blit(dx, dy)
395 img:copy(_LCD, dx, dy, _NIL, _NIL, _NIL, _NIL, false, BSAND, color)
396 end
397
398 if w > LCD_W then -- text is too long for the screen do it in chunks
399 local l = 1
400 local resp, wp, hp
401 local lenr = text:len()
402
403 while lenr > 1 do
404 l = lenr
405 resp, wp, hp = rb.font_getstringsize(text:sub(1, l), font)
406
407 while wp >= LCD_W and l > 1 do
408 l = l - 1
409 resp, wp, hp = rb.font_getstringsize(text:sub( 1, l), font)
410 end
411
412 rb.lcd_putsxy(0, 0, text:sub(1, l))
413 text = text:sub(l)
414
415 if x + width > img:width() or y + height > img:height() then
416 break
417 end
418
419 -- using the mask we made blit color into img
420 blit(x + width, y + height)
421 x = x + wp
422 rb.lcd_clear_display()
423 lenr = text:len()
424 end
425 else --w <= LCD_W
426 rb.lcd_putsxy(0, 0, text)
427
428 -- using the mask we made blit color into img
429 blit(x + width, y + height)
430 end
431
432 _LCD:copy(screen_img) -- restore screen
433 set_viewport() -- set viewport default
434 return res, w, h
435 end
436
437 -- expose functions to the outside through _draw table
438 _draw.image = image
439 _draw.text = text
440 _draw.line = line
441 _draw.hline = hline
442 _draw.vline = vline
443 _draw.polygon = polygon
444 _draw.polyline = polyline
445 _draw.rect = rect
446 _draw.circle = circle
447 _draw.ellipse = ellipse
448 _draw.flood_fill = flood_fill
449 _draw.ellipse_rect = ellipse_rect
450 _draw.rounded_rect = rounded_rect
451 -- filled functions use color as fillcolor if fillcolor is left empty...
452 _draw.rect_filled = rect_filled
453 _draw.circle_filled = circle_filled
454 _draw.ellipse_filled = ellipse_filled
455 _draw.ellipse_rect_filled = ellipse_rect_filled
456 _draw.rounded_rect_filled = rounded_rect_filled
457
458 -- adds the above _draw functions into the metatable for RLI_IMAGE
459 local ex = getmetatable(rb.lcd_framebuffer())
460 for k, v in pairs(_draw) do
461 if ex[k] == _NIL then
462 ex[k] = v
463 end
464 end
465
466end -- _draw functions
467
468return _draw
469
diff --git a/apps/plugins/lua/include_lua/image.lua b/apps/plugins/lua/include_lua/image.lua
new file mode 100644
index 0000000000..c30e8ab556
--- /dev/null
+++ b/apps/plugins/lua/include_lua/image.lua
@@ -0,0 +1,392 @@
1--[[ Lua Image functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _img.save
27 _img.search
28 _img.rotate
29 _img.resize
30 _img.tile
31 _img.new
32 _img.load
33
34-- Exposed Constants
35 _img.RLI_INFO_ALL
36 _img.RLI_INFO_TYPE
37 _img.RLI_INFO_WIDTH
38 _img.RLI_INFO_HEIGHT
39 _img.RLI_INFO_ELEMS
40 _img.RLI_INFO_BYTES
41 _img.RLI_INFO_DEPTH
42 _img.RLI_INFO_FORMAT
43 _img.RLI_INFO_ADDRESS
44
45]]
46
47--[[Other rbimage Functions:
48--------------------------------------------------------------------------------
49img:_len() or #img -- returns number of pixels in image
50
51img:__tostring([item]) or tostring(img) -- returns data about the image item = 0
52 is the same as tostring(img) otherwise
53 item = 1 is the first item in list
54 item = 7 is the 7th item
55 item = 8 is the data address in hex
56 -- See Constants _img.RLI_INFO_....
57
58img:_data(element) -- returns/sets raw pixel data
59 NOTE!! this data is defined by the target and targets with
60 different color depth, bit packing, etc will not be
61 compatible with the same image's data on another target
62]]
63--------------------------------------------------------------------------------
64if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
65
66local _img = {} do
67
68 -- internal constants
69 local _NIL = nil -- _NIL placeholder
70 local _math = require("math_ex") -- math functions needed
71 local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT
72
73 -- returns new image -of- img sized to fit w/h tiling to fit if needed
74 local function tile(img, w, h)
75 local hs , ws = img:height(), img:width()
76 local t_img = rb.new_image(w, h)
77
78 for x = 1, w, ws do t_img:copy(img, x, 1, 1, 1) end
79 for y = hs, h, hs do t_img:copy(t_img, 1, y, 1, 1, w, hs) end
80 return t_img
81 end
82
83 -- resizes src to size of dst
84 local function resize(dst, src)
85 -- simple nearest neighbor resize derived from rockbox - pluginlib_bmp.c
86 -- pretty rough results highly recommend building one more suited..
87 local dw, dh = dst:width(), dst:height()
88
89 local xstep = (bit.lshift(src:width(),8) / (dw)) + 1
90 local ystep = (bit.lshift(src:height(),8) / (dh))
91
92 local xpos, ypos = 0, 0
93 local src_x, src_y
94
95 -- walk the dest get src pixel
96 function rsz_trans(val, x, y)
97 if x == 1 then
98 src_y = bit.rshift(ypos,8) + 1
99 xpos = xstep - bit.rshift(xstep,4) + 1
100 ypos = ypos + ystep;
101 end
102 src_x = bit.rshift(xpos,8) + 1
103 xpos = xpos + xstep
104 return (src:get(src_x, src_y, true) or 0)
105 end
106 --/* (dst*, [x1, y1, x2, y2, dx, dy, clip, function]) */
107 dst:marshal(1, 1, dw, dh, _NIL, _NIL, false, rsz_trans)
108 end
109
110 -- returns new image -of- img rotated in whole degrees 0 - 360
111 local function rotate(img, degrees)
112 -- we do this backwards as if dest was the unrotated object
113 degrees = 360 - degrees
114 local c, s = _math.d_cos(degrees), _math.d_sin(degrees)
115
116 -- get the center of the source image
117 local s_xctr, s_yctr = img:width() / 2, img:height() / 2
118
119 -- get the the new center of the dest image at rotation angle
120 local d_xctr = ((math.abs(s_xctr * c) + math.abs(s_yctr * s))/ 10000) + 1
121 local d_yctr = ((math.abs(s_xctr * s) + math.abs(s_yctr * c))/ 10000) + 1
122
123 -- calculate size of rect new image will occupy
124 local dw, dh = d_xctr * 2 - 1, d_yctr * 2 - 1
125
126 local r_img = rb.new_image(dw, dh)
127 -- r_img:clear() -- doesn't need cleared as we walk every pixel
128
129 --[[rotation works on origin of 0,0 we need to offset to the center of the
130 image and then place the upper left back at the origin (0, 0)]]
131 --[[0,0|-----| ^< |-------| v> 0,0|-------|
132 | | | 0,0 | | |
133 |_____| |_______| |_______| ]]
134
135 -- walk the dest get translated src pixel, oversamples src to fill gaps
136 function rot_trans(val, x, y)
137 -- move center x/y to the origin
138 local xtran = x - d_xctr;
139 local ytran = y - d_yctr;
140
141 -- rotate about the center of the image by x degrees
142 local yrot = ((xtran * s) + (ytran * c)) / 10000 + s_yctr
143 local xrot = ((xtran * c) - (ytran * s)) / 10000 + s_xctr
144 -- upper left of src image back to origin, copy src pixel
145 return img:get(xrot, yrot, true) or 0
146 end
147 r_img:marshal(1, 1, dw, dh, _NIL, _NIL, false, rot_trans)
148 return r_img
149 end
150
151 -- saves img to file: name
152 local function save(img, name)
153 -- bmp saving derived from rockbox - screendump.c
154 -- bitdepth is limited by the device
155 -- eg. device displays greyscale, rgb images are saved greyscale
156 local file
157
158 local fbuffer = {} -- concat buffer for file writes, reused
159
160 local function dump_fbuffer(thresh)
161 if #fbuffer >= thresh then
162 file:write(table.concat(fbuffer))
163 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
164 end
165 end
166
167 local function s_bytesLE(bits, value)
168 -- bits must be multiples of 8 (sizeof byte)
169 local byte
170 local result = ""
171 for b = 1, bit.rshift(bits, 3) do
172 if value > 0 then
173 byte = value % 256
174 value = (value - byte) / 256
175 result = result .. string.char(byte)
176 else
177 result = result .. string.char(0)
178 end
179 end
180 return result
181 end
182
183 local function s_bytesBE(bits, value)
184 -- bits must be multiples of 8 (sizeof byte)
185 local byte
186 local result = ""
187 for b = 1, bit.rshift(bits, 3) do
188 if value > 0 then
189 byte = value % 256
190 value = (value - byte) / 256
191 result = string.char(byte) .. result
192 else
193 result = string.char(0) .. result
194 end
195 end
196 return result
197 end
198
199 local function c_cmp(color, shift)
200 -- [RR][GG][BB]
201 return bit.band(bit.rshift(color, shift), 0xFF)
202 end
203
204 local cmp = {["r"] = function(c) return c_cmp(c, 16) end,
205 ["g"] = function(c) return c_cmp(c, 08) end,
206 ["b"] = function(c) return c_cmp(c, 00) end}
207
208 local function bmp_color(color)
209 return s_bytesLE(8, cmp.b(color))..
210 s_bytesLE(8, cmp.g(color))..
211 s_bytesLE(8, cmp.r(color))..
212 s_bytesLE(8, 0) .. ""
213 end -- c_cmp(color, c.r))
214
215 local function bmp_color_mix(c1, c2, num, den)
216 -- mixes c1 and c2 as ratio of numerator / denominator
217 -- used 2x each save results
218 local bc1, gc1, rc1 = cmp.b(c1), cmp.g(c1), cmp.r(c1)
219
220 return s_bytesLE(8, cmp.b(c2) - bc1 * num / den + bc1)..
221 s_bytesLE(8, cmp.g(c2) - gc1 * num / den + gc1)..
222 s_bytesLE(8, cmp.r(c2) - rc1 * num / den + rc1)..
223 s_bytesLE(8, 0) .. ""
224 end
225
226 local w, h = img:width(), img:height()
227 local depth = tonumber(img:__tostring(6)) -- RLI_INFO_DEPTH = 0x6
228 local format = tonumber(img:__tostring(7)) -- RLI_INFO_FORMAT = 0x7
229
230 local bpp, bypl -- bits per pixel, bytes per line
231 -- bypl, pad rows to a multiple of 4 bytes
232 if depth <= 4 then
233 bpp = 8 -- 256 color image
234 bypl = (w + 3)
235 elseif depth <= 16 then
236 bpp = 16
237 bypl = (w * 2 + 3)
238 else
239 bpp = 24
240 bypl = (w * 3 + 3)
241 end
242
243 local linebytes = bit.band(bypl, bit.bnot(3))
244
245 local bytesperpixel = bit.rshift(bpp, 3)
246 local headersz = 54
247 local imgszpad = h * linebytes
248
249 local compression, n_colors = 0, 0
250 local h_ppm, v_ppm = 0x00000EC4, 0x00000EC4 --Pixels Per Meter ~ 96 dpi
251
252 if depth == 16 then
253 compression = 3 -- BITFIELDS
254 n_colors = 3
255 elseif depth <= 8 then
256 n_colors = bit.lshift(1, depth)
257 end
258
259 headersz = headersz + (4 * n_colors)
260
261 file = io.open('/' .. name, "w+") -- overwrite, rb ignores the 'b' flag
262
263 if not file then
264 rb.splash(rb.HZ, "Error opening /" .. name)
265 return
266 end
267 -- create a bitmap header 'rope' with image details -- concatenated at end
268 local bmpheader = fbuffer
269
270 bmpheader[01] = "BM"
271 bmpheader[02] = s_bytesLE(32, headersz + imgszpad)
272 bmpheader[03] = "\0\0\0\0" -- WORD reserved 1 & 2
273 bmpheader[04] = s_bytesLE(32, headersz) -- BITMAPCOREHEADER size
274 bmpheader[05] = s_bytesLE(32, 40) -- BITMAPINFOHEADER size
275
276 bmpheader[06] = s_bytesLE(32, w)
277 bmpheader[07] = s_bytesLE(32, h)
278 bmpheader[08] = "\1\0" -- WORD color planes ALWAYS 1
279 bmpheader[09] = s_bytesLE(16, bpp) -- bits/pixel
280 bmpheader[10] = s_bytesLE(32, compression)
281 bmpheader[11] = s_bytesLE(32, imgszpad)
282 bmpheader[12] = s_bytesLE(32, h_ppm) -- biXPelsPerMeter
283 bmpheader[13] = s_bytesLE(32, v_ppm) -- biYPelsPerMeter
284 bmpheader[14] = s_bytesLE(32, n_colors)
285 bmpheader[15] = s_bytesLE(32, n_colors)
286
287 -- Color Table (#n_colors entries)
288 if depth == 1 then -- assuming positive display
289 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
290 bmpheader[#bmpheader + 1] = bmp_color(0x0)
291 elseif depth == 2 then
292 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
293 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 1, 3)
294 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 2, 3)
295 bmpheader[#bmpheader + 1] = bmp_color(0x0)
296 elseif depth == 16 then
297 -- red bitfield mask
298 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000F800)
299 -- green bitfield mask
300 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000007E0)
301 -- blue bitfield mask
302 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
303 end
304
305 dump_fbuffer(0) -- write the header to the file now
306
307 local imgdata = fbuffer
308 -- pad rows to a multiple of 4 bytes
309 local bytesleft = linebytes - (bytesperpixel * w)
310 local t_data = {}
311 local fs_bytes_E = s_bytesLE -- default save in Little Endian
312
313 if format == 3553 then -- RGB565SWAPPED
314 fs_bytes_E = s_bytesBE -- Saves in Big Endian
315 end
316
317 -- Bitmap lines start at bottom unless biHeight is negative
318 for point in img:points(1, h, w + bytesleft, 1) do
319 imgdata[#imgdata + 1] = fs_bytes_E(bpp, point or 0)
320 dump_fbuffer(31) -- buffered write, increase # for performance
321 end
322
323 dump_fbuffer(0) --write leftovers to file
324
325 file:close()
326 end -- save(img, name)
327
328 --searches an image for target color
329 local function search(img, x1, y1, x2, y2, targetclr, variation, stepx, stepy)
330
331 if variation > 128 then variation = 128 end
332 if variation < -128 then variation = -128 end
333
334 local targeth = targetclr + variation
335 local targetl = targetclr - variation
336
337 if targeth < targetl then
338 local swap = targeth
339 targeth = targetl
340 targetl = swap
341 end
342
343 for point, x, y in img:points(x1, y1, x2, y2, stepx, stepy) do
344 if point >= targetl and point <= targeth then
345 return point, x, y
346 end
347 end
348 return nil, nil, nil
349 end
350
351 --[[ we won't be extending these into RLI_IMAGE]]
352 -- creates a new rbimage size w x h
353 local function new(w, h)
354 return rb.new_image(w, h)
355 end
356
357 -- returns new image -of- file: name (_NIL if error)
358 local function load(name)
359 return rb.read_bmp_file("/" .. name)
360 end
361
362 -- expose tostring constants to outside through _img table
363 _img.RLI_INFO_ALL = 0x0
364 _img.RLI_INFO_TYPE = 0x1
365 _img.RLI_INFO_WIDTH = 0x2
366 _img.RLI_INFO_HEIGHT = 0x3
367 _img.RLI_INFO_ELEMS = 0x4
368 _img.RLI_INFO_BYTES = 0x5
369 _img.RLI_INFO_DEPTH = 0x6
370 _img.RLI_INFO_FORMAT = 0x7
371 _img.RLI_INFO_ADDRESS = 0x8
372
373 -- expose functions to the outside through _img table
374 _img.save = save
375 _img.search = search
376 _img.rotate = rotate
377 _img.resize = resize
378 _img.tile = tile
379
380 -- adds the above _img functions into the metatable for RLI_IMAGE
381 local ex = getmetatable(rb.lcd_framebuffer())
382 for k, v in pairs(_img) do
383 if ex[k] == _NIL then ex[k] = v end
384 end
385 -- not exposed through RLI_IMAGE
386 _img.new = new
387 _img.load = load
388
389end -- _img functions
390
391return _img
392
diff --git a/apps/plugins/lua/include_lua/lcd.lua b/apps/plugins/lua/include_lua/lcd.lua
new file mode 100644
index 0000000000..bbf0f240aa
--- /dev/null
+++ b/apps/plugins/lua/include_lua/lcd.lua
@@ -0,0 +1,154 @@
1--[[ Lua LCD Wrapper functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _lcd.clear
27 _lcd.duplicate
28 _lcd.image
29 _lcd.set_viewport
30 _lcd.splashf
31 _lcd.text_extent
32 _lcd.update
33 _lcd.update_rect
34
35-- Exposed Constants
36 _lcd.CX
37 _lcd.CY
38 _lcd.DEPTH
39 _lcd.W
40 _lcd.H
41
42 _lcd
43 _LCD
44
45]]
46if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
47
48_LCD = rb.lcd_framebuffer()
49
50local _lcd = {} do
51
52 --internal constants
53 local _NIL = nil -- _NIL placeholder
54 local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT
55
56 -- clamps value to >= min and <= max
57 local function clamp(val, min, max)
58 -- Warning doesn't check if min < max
59 if val < min then
60 return min
61 elseif val < max then
62 return val
63 end
64 return max
65 end
66
67 -- return a copy of lcd screen
68 local function duplicate(t, screen_img)
69 screen_img = screen_img or rb.new_image()
70 screen_img:copy(rb.lcd_framebuffer())
71 return screen_img
72 end
73
74 -- updates screen in specified rectangle
75 local function update_rect(t, x, y, w, h)
76 rb.lcd_update_rect(x - 1, y - 1,
77 clamp(x + w, 1, LCD_W) - 1,
78 clamp(y + h, 1, LCD_H) - 1)
79 end
80
81 -- clears lcd, optional.. ([color, x1, y1, x2, y2, clip])
82 local function clear(t, clr, ...)
83 if clr == _NIL and ... == _NIL then
84 rb.lcd_clear_display()
85 else
86 rb.lcd_scroll_stop() --rb really doesn't like bg change while scroll
87 _LCD:clear(clr, ...)
88 end
89 end
90
91 -- loads an image to the screen
92 local function image(t, src, x, y)
93 if not src then --make sure an image was passed, otherwise bail
94 rb.splash(rb.HZ, "No Image!")
95 return _NIL
96 end
97 _LCD:copy(src,x,y,1,1)
98 end
99
100 -- Formattable version of splash
101 local function splashf(t, timeout, ...)
102 rb.splash(timeout, string.format(...))
103 end
104
105 -- Gets size of text
106 local function text_extent(t, msg, font)
107 font = font or rb.FONT_UI
108
109 return rb.font_getstringsize(msg, font)
110 end
111
112 -- Sets viewport size
113 local function set_viewport(t, vp)
114 if not vp then rb.set_viewport() return end
115 if rb.LCD_DEPTH == 2 then -- invert 2-bit screens
116 --vp.drawmode = bit.bxor(vp.drawmode, 4)
117 vp.fg_pattern = 3 - vp.fg_pattern
118 vp.bg_pattern = 3 - vp.bg_pattern
119 end
120 rb.set_viewport(vp)
121 end
122
123 -- allows the use of _lcd() as a identifier for the screen
124 local function index(k, v)
125 return function(x, ...)
126 _LCD[v](_LCD, ...)
127 end
128 end
129
130 -- allows the use of _lcd() as a identifier for the screen
131 local function call()
132 return rb.lcd_framebuffer()
133 end
134
135 --expose functions to the outside through _lcd table
136 _lcd.text_extent = text_extent
137 _lcd.set_viewport = set_viewport
138 _lcd.duplicate = duplicate
139 _lcd.update = rb.lcd_update
140 _lcd.update_rect = update_rect
141 _lcd.clear = clear
142 _lcd.splashf = splashf
143 _lcd.image = image
144 _lcd.DEPTH = rb.LCD_DEPTH
145 _lcd.W = rb.LCD_WIDTH
146 _lcd.H = rb.LCD_HEIGHT
147 _lcd.CX = (rb.LCD_WIDTH / 2)
148 _lcd.CY = (rb.LCD_HEIGHT / 2)
149 _lcd = setmetatable(_lcd,{__index = index, __call = call})
150
151end -- _lcd functions
152
153return _lcd
154
diff --git a/apps/plugins/lua/include_lua/math_ex.lua b/apps/plugins/lua/include_lua/math_ex.lua
new file mode 100644
index 0000000000..bd4cc58765
--- /dev/null
+++ b/apps/plugins/lua/include_lua/math_ex.lua
@@ -0,0 +1,159 @@
1--[[ Lua Missing Math functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _math.clamp
27 _math.clamp_roll
28 _math.d_sin
29 _math.d_cos
30 _math.d_tan
31 _math.i_sqrt
32
33]]
34
35local _math = {} do
36
37 -- internal constants
38 local _NIL = nil -- _NIL placeholder
39
40 -- clamps value to >= min and <= max
41 local function clamp(iVal, iMin, iMax)
42 if iMin > iMax then
43 local swap = iMin
44 iMin, iMax = iMax, swap
45 end
46
47 if iVal < iMin then
48 return iMin
49 elseif iVal < iMax then
50 return iVal
51 end
52
53 return iMax
54 end
55
56 -- clamps value to >= min and <= max rolls over to opposite
57 local function clamp_roll(iVal, iMin, iMax)
58 if iMin > iMax then
59 local swap = iMin
60 iMin, iMax = iMax, swap
61 end
62
63 if iVal < iMin then
64 iVal = iMax
65 elseif iVal > iMax then
66 iVal = iMin
67 end
68
69 return iVal
70 end
71
72 local function i_sqrt(n)
73 -- Newtons square root approximation
74 if n < 2 then return n end
75 local g = n / 2
76 local l = 1
77
78 for c = 1, 25 do -- if l,g haven't converged after 25 iterations quit
79
80 l = (n / g + g)/ 2
81 g = (n / l + l)/ 2
82
83 if g == l then return g end
84 end
85
86 -- check for period-two cycle between g and l
87 if g - l == 1 then
88 return l
89 elseif l - g == 1 then
90 return g
91 end
92
93 return _NIL
94 end
95
96 local function d_sin(iDeg, bExtraPrecision)
97 --[[ values are returned multiplied by 10000
98 II | I 180-90 | 90-0
99 ---(--)--- -------(--)-------
100 III | IV 180-270 | 270-360
101
102 sine is only positive in quadrants I , II => 0 - 180 degrees
103 sine 180-360 degrees is a reflection of sine 0-180
104 Bhaskara I's sine approximation formula isn't overly accurate
105 but it is close enough for rough image work.
106 ]]
107 local sign, x
108 -- no negative angles -10 degrees = 350 degrees
109 if iDeg < 0 then
110 x = 360 + (iDeg % 360)
111 else --keep rotation in 0-360 range
112 x = iDeg % 360
113 end
114
115 -- reflect II & I onto III & IV
116 if x > 180 then
117 sign = -1
118 x = x % 180
119 else
120 sign = 1
121 end
122
123 local x1 = x * (180 - x)
124
125 if bExtraPrecision then -- ~halves the largest errors
126 if x <= 22 or x >= 158 then
127 return sign * 39818 * x1 / (40497 - x1)
128 elseif (x >= 40 and x <= 56) or (x > 124 and x < 140) then
129 return sign * 40002 * x1 / (40450 - x1)
130 elseif (x > 31 and x < 71) or (x > 109 and x < 150) then
131 return sign * 40009 * x1 / (40470 - x1)
132 end
133 end
134
135 --multiplied by 10000 so no decimal in results (RB LUA is integer only)
136 return sign * 40000 * x1 / (40497 - x1)
137 end
138
139 local function d_cos(iDeg, bExtraPrecision)
140 --cos is just sine shifed by 90 degrees CCW
141 return d_sin(90 - iDeg, bExtraPrecision)
142 end
143
144 local function d_tan(iDeg, bExtraPrecision)
145 --tan = sin0 / cos0
146 return (d_sin(iDeg, bExtraPrecision) * 10000 / d_sin(90 - iDeg, bExtraPrecision))
147 end
148
149 --expose functions to the outside through _math table
150 _math.clamp = clamp
151 _math.clamp_roll = clamp_roll
152 _math.i_sqrt = i_sqrt
153 _math.d_sin = d_sin
154 _math.d_cos = d_cos
155 _math.d_tan = d_tan
156end -- missing math functions
157
158return _math
159
diff --git a/apps/plugins/lua/include_lua/print.lua b/apps/plugins/lua/include_lua/print.lua
new file mode 100644
index 0000000000..9b21dafb9d
--- /dev/null
+++ b/apps/plugins/lua/include_lua/print.lua
@@ -0,0 +1,378 @@
1--[[ Lua Print functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _print.clear
27 _print.f
28 _print.opt
29 _print.opt.area
30 _print.opt.autoupdate
31 _print.opt.color
32 _print.opt.column
33 _print.opt.defaults
34 _print.opt.get
35 _print.opt.justify
36 _print.opt.line
37 _print.opt.overflow
38 _print.opt.sel_line
39 _print.opt.set
40
41]]
42
43if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
44
45local _print = {} do
46
47 -- internal constants
48 local _clr = require("color") -- _clr functions required
49
50 local _NIL = nil -- _NIL placeholder
51 local _LCD = rb.lcd_framebuffer()
52 local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT
53 local WHITE = _clr.set(-1, 255, 255, 255)
54 local BLACK = _clr.set(0, 0, 0, 0)
55 local DRMODE_SOLID = 3
56 local col_buf, s_lines = {}, {}
57 local _p_opts = _NIL
58
59-- print internal helper functions
60--------------------------------------------------------------------------------
61 -- clamps value to >= min and <= max
62 local function clamp(val, min, max)
63 -- Warning doesn't check if min < max
64 if val < min then
65 return min
66 elseif val < max then
67 return val
68 end
69 return max
70 end
71
72 -- updates screen in specified rectangle
73 local function update_rect(x, y, w, h)
74 rb.lcd_update_rect(x - 1, y - 1,
75 clamp(x + w, 1, LCD_W) - 1,
76 clamp(y + h, 1, LCD_H) - 1)
77 end
78
79 -- Gets size of text
80 local function text_extent(msg, font)
81 font = font or rb.FONT_UI
82 -- res, w, h
83 return rb.font_getstringsize(msg, font)
84 end
85
86 -- Sets viewport size
87 local function set_viewport(vp)
88 if not vp then rb.set_viewport() return end
89
90 if rb.LCD_DEPTH == 2 then -- invert 2-bit screens
91 --vp.drawmode = bit.bxor(vp.drawmode, 4)
92 vp.fg_pattern = 3 - vp.fg_pattern
93 vp.bg_pattern = 3 - vp.bg_pattern
94 end
95 rb.set_viewport(vp)
96 end
97
98 -- shallow copy of table
99 function table_clone(t)
100 local copy = {}
101 for k, v in pairs(t) do
102 copy[k] = v
103 end
104 return copy
105 end
106
107 -- Updates a single line on the screen
108 local function update_line(enabled, opts, line, h)
109 if enabled ~= true then return end
110 local o = opts or _p_opts
111 update_rect(o.x, o.y + line * h + 1, o.width, h)
112 end
113
114 -- Clears a single line on the screen
115 local function clear_line(opts, line, h)
116 local o = opts or _p_opts
117 _LCD:clear(o.bg_pattern, o.x, o.y + line * h + 1,
118 o.x + o.width, line * h + h + o.y)
119 end
120
121 -- Sets the maximum number of lines on the screen
122 local function max_lines(opts)
123 local h = opts.height
124 local _, _, th = text_extent("W", opts.font)
125 return h / th
126 end
127
128 --saves the items displayed for side to side scroll
129 local function col_buf_insert(msg, line, _p_opts)
130 --if _p_opts.line <= 1 then col_buf = {} end
131 if not col_buf[line] then
132 table.insert(col_buf, line, msg) end
133 end
134
135 --replaces / strips escape characters
136 local function check_escapes(o, msg)
137 local tabsz = 2
138 local tabstr = string.rep(" ", tabsz)
139
140 local function repl(esc)
141 local ret = ""
142 if esc:sub(1,1) == "\t" then ret = string.rep(tabstr, esc:len()) end
143 return ret
144 end
145
146 msg = msg:gsub("(%c+)", repl)
147
148 local res, w, h = text_extent(msg, o.font)
149 return w, h, msg
150 end
151--------------------------------------------------------------------------------
152
153 -- set defaults for print view
154 local function set_defaults()
155 _p_opts = { x = 1,
156 y = 1,
157 width = LCD_W - 1,
158 height = LCD_H - 1,
159 font = rb.FONT_UI,
160 drawmode = DRMODE_SOLID,
161 fg_pattern = WHITE,
162 bg_pattern = BLACK,
163 sel_pattern = WHITE,
164 line = 1,
165 max_line = _NIL,
166 col = 0,
167 ovfl = "auto",
168 justify = "left",
169 autoupdate = true,
170 }
171 _p_opts.max_line = max_lines(_p_opts)
172
173 s_lines, col_buf = {}, {}
174 return _p_opts
175 end
176
177 -- returns table with settings for print
178 -- if bByRef is _NIL or false then a copy is returned
179 local function get_settings(bByRef)
180 _p_opts = _p_opts or set_defaults()
181 if not bByRef then return table_clone(_p_opts) end
182 return _p_opts
183 end
184
185 -- sets the settings for print with your passed table
186 local function set_settings(t_opts)
187 _p_opts = t_opts or set_defaults()
188 if t_opts then
189 _p_opts.max_line = max_lines(_p_opts)
190 col_buf = {}
191 end
192 end
193
194 -- sets colors for print
195 local function set_color(fgclr, bgclr, selclr)
196 local o = get_settings(true)
197
198 if fgclr ~= _NIL then
199 o.fg_pattern, o.sel_pattern = fgclr, fgclr
200 end
201 o.sel_pattern = selclr or o.sel_pattern
202 o.bg_pattern = bgclr or o.bg_pattern
203 end
204
205 -- helper function sets up colors/marker for selected items
206 local function show_selected(iLine, msg)
207 local o = get_settings() -- using a copy of opts so changes revert
208
209 if s_lines[iLine] == true then
210 if not rb.lcd_set_background then
211 o.drawmode = bit.bxor(o.drawmode, 4)
212 else
213 o.fg_pattern = o.bg_pattern
214 o.bg_pattern = o.sel_pattern
215 end
216 -- alternative selection method
217 --msg = "> " .. msg
218 end
219 set_viewport(o)
220 o = _NIL
221 end
222
223 -- sets line explicitly or increments line if line is _NIL
224 local function set_line(iLine)
225 local o = get_settings(true)
226
227 o.line = iLine or o.line + 1
228
229 if(o.line < 1 or o.line > o.max_line) then
230 o.line = 1
231 end
232 end
233
234 -- clears the set print area
235 local function clear()
236 local o = get_settings(true)
237 _LCD:clear(o.bg_pattern, o.x, o.y, o.x + o.width, o.y + o.height)
238 if o.autoupdate == true then rb.lcd_update() end
239 set_line(1)
240 for i=1, #col_buf do col_buf[i] = _NIL end
241 s_lines = {}
242 collectgarbage("collect")
243 end
244
245 -- screen update after each call to print.f
246 local function set_update(bAutoUpdate)
247 local o = get_settings(true)
248 o.autoupdate = bAutoUpdate or false
249 end
250
251 -- sets print area
252 local function set_area(x, y, w, h)
253 local o = get_settings(true)
254 o.x, o.y = clamp(x, 1, LCD_W), clamp(y, 1, LCD_H)
255 o.width, o.height = clamp(w, 1, LCD_W - o.x), clamp(h, 1, LCD_H - o.y)
256 o.max_line = max_lines(_p_opts)
257
258 clear()
259 return o.line, o.max_line
260 end
261
262 -- when string is longer than print width scroll -- "auto", "manual", "none"
263 local function set_overflow(str_mode)
264 -- "auto", "manual", "none"
265 local str_mode = str_mode or "auto"
266 local o = get_settings(true)
267 o.ovfl = str_mode:lower()
268 col_buf = {}
269 end
270
271 -- aligns text to: "left", "center", "right"
272 local function set_justify(str_mode)
273 -- "left", "center", "right"
274 local str_mode = str_mode or "left"
275 local o = get_settings(true)
276 o.justify = str_mode:lower()
277 end
278
279 -- selects line
280 local function select_line(iLine)
281 s_lines[iLine] = true
282 end
283
284 -- Internal print function
285 local function print_internal(t_opts, x, w, h, msg)
286
287 local o = t_opts
288 if o.justify == "center" then
289 x = x + (o.width - w) / 2
290 elseif o.justify == "right" then
291 x = x + (o.width - w)
292 end
293
294 local line = o.line - 1 -- rb is 0-based lua is 1-based
295 if(o.ovfl == "auto" and w >= o.width) then -- -o.x
296 rb.lcd_puts_scroll(0, line, msg)
297 else
298 rb.lcd_putsxy(x, line * h, msg)
299 if o.ovfl == "manual" then --save msg for later side scroll
300 col_buf_insert(msg, o.line, o)
301 end
302 end
303
304 --only update the line we changed
305 update_line(o.autoupdate, o, line, h)
306
307 set_line(_NIL) -- increments line counter
308 end
309
310 -- Helper function that acts mostly like a normal printf() would
311 local function printf(...)
312 local o = get_settings(true)
313 local w, h, msg
314 local line = o.line - 1 -- rb is 0-based lua is 1-based
315 if not (...) or (...) == "\n" then -- handles blank line / single '\n'
316 local res, w, h = text_extent(" ", o.font)
317
318 clear_line(o, line, h)
319 update_line(o.autoupdate, o, line, h)
320
321 if (...) then set_line(_NIL) end
322
323 return o.line, o.max_line, o.width, h
324 end
325
326 msg = string.format(...)
327
328 show_selected(o.line, msg)
329
330 w, h, msg = check_escapes(o, msg)
331
332 print_internal(o, o.col, w, h, msg)
333
334 return o.line, o.max_line, w, h
335 end
336
337 -- x > 0 scrolls right x < 0 scrolls left
338 local function set_column(x)
339 local o = get_settings()
340 if o.ovfl ~= "manual" then return end -- no buffer stored to scroll
341 local res, w, h, str, line
342
343 for key, value in pairs(col_buf) do
344 line = key - 1 -- rb is 0-based lua is 1-based
345 o.line = key
346
347 if value then
348 show_selected(key, value)
349 res, w, h = text_extent(value, o.font)
350 clear_line(o, line, h)
351
352 print_internal(o, x + o.col, w, h, value)
353 update_line(o.autoupdate, o, line, h)
354 end
355 end
356 o = _NIL
357 end
358
359 --expose functions to the outside through _print table
360 _print.opt = {}
361 _print.opt.column = set_column
362 _print.opt.color = set_color
363 _print.opt.area = set_area
364 _print.opt.set = set_settings
365 _print.opt.get = get_settings
366 _print.opt.defaults = set_defaults
367 _print.opt.overflow = set_overflow
368 _print.opt.justify = set_justify
369 _print.opt.sel_line = select_line
370 _print.opt.line = set_line
371 _print.opt.autoupdate = set_update
372 _print.clear = clear
373 _print.f = printf
374
375end --_print functions
376
377return _print
378
diff --git a/apps/plugins/lua/include_lua/timer.lua b/apps/plugins/lua/include_lua/timer.lua
new file mode 100644
index 0000000000..18b973cbbd
--- /dev/null
+++ b/apps/plugins/lua/include_lua/timer.lua
@@ -0,0 +1,115 @@
1--[[ Lua Timer functions
2/***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2017 William Wilgus
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22]]
23
24--[[ Exposed Functions
25
26 _timer.active
27 _timer.check
28 _timer.split
29 _timer.start
30 _timer.stop
31
32]]
33
34local _timer = {} do
35
36 --internal constants
37 local _NIL = nil -- _NIL placeholder
38
39 -- newer versions of lua use table.unpack
40 local unpack = unpack or table.unpack
41
42 --stores time elapsed at call to split; only vaid for unique timers
43 local function split(index)
44 if type(index) ~= "table" then return end
45 index[#index + 1] = rb.current_tick() - _timer[index]
46 end
47
48 -- starts a new timer, if index is not specified a unique index is returned
49 -- numeric or string indices are valid to use directly for permanent timers
50 -- in this case its up to you to make sure you keep the index unique
51 local function start(index)
52 if index == _NIL then
53 ---if you have _timer.start create timer it returns a unique Id which
54 -- then has the same methods of _timer :start :stop :check :split
55 index = setmetatable({}, {__index = _timer})
56 end
57 if _timer[index] == _NIL then
58 _timer[index] = rb.current_tick()
59 end
60 return index
61 end
62
63 -- returns time elapsed in centiseconds, assigning bCheckonly keeps timer active
64 local function stop(index, bCheckonly)
65
66 local time_end = rb.current_tick()
67 index = index or 0
68 if not _timer[index] then
69 return 0
70 else
71 local time_start = _timer[index]
72 if not bCheckonly then _timer[index] = _NIL end -- destroy timer
73 if type(index) ~= "table" then
74 return time_end - time_start
75 else
76 return time_end - time_start, unpack(index)
77 end
78 end
79 end
80
81 -- returns time elapsed in centiseconds, assigning to bUpdate.. updates timer
82 local function check(index, bUpdate)
83 local elapsed = stop(index, true)
84 if bUpdate ~= _NIL and index then
85 _timer[index] = rb.current_tick()
86 end
87 return elapsed
88 end
89
90 -- returns table of active timers
91 local function active()
92 local t_active = {}
93 local n = 0
94 for k,v in pairs(_timer) do
95 if type(_timer[k]) ~= "function" then
96 n = n + 1
97 t_active[n]=(k)
98 end
99 end
100 return n, t_active
101 end
102
103 -- expose functions to the outside through _timer table
104 _timer.active = active
105 _timer.check = check
106 _timer.split = split
107 _timer.start = start
108 _timer.stop = stop
109
110 -- allows a call to _timer.start() by just calling _timer()
111 _timer = setmetatable(_timer,{__call = function(t, i) return start(i) end})
112end -- timer functions
113
114return _timer
115
diff --git a/apps/plugins/lua/lua.make b/apps/plugins/lua/lua.make
index 2365150c1e..e36ace4759 100644
--- a/apps/plugins/lua/lua.make
+++ b/apps/plugins/lua/lua.make
@@ -15,6 +15,9 @@ LUA_OBJ := $(call c2obj, $(LUA_SRC))
15 15
16OTHER_SRC += $(LUA_SRC) 16OTHER_SRC += $(LUA_SRC)
17 17
18LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua
19LUA_INCLUDELIST = blit color draw image lcd math_ex print timer
20
18ifndef APP_TYPE 21ifndef APP_TYPE
19ifneq (,$(strip $(foreach tgt,RECORDER ONDIO,$(findstring $(tgt),$(TARGET))))) 22ifneq (,$(strip $(foreach tgt,RECORDER ONDIO,$(findstring $(tgt),$(TARGET)))))
20 ### lowmem targets 23 ### lowmem targets
@@ -30,7 +33,7 @@ else
30 ROCKS += $(LUA_BUILDDIR)/lua.rock 33 ROCKS += $(LUA_BUILDDIR)/lua.rock
31endif 34endif
32 35
33$(LUA_BUILDDIR)/lua.rock: $(LUA_OBJ) $(TLSFLIB) $(LUA_BUILDDIR)/actions.lua $(LUA_BUILDDIR)/buttons.lua $(LUA_BUILDDIR)/rocklib_aux.o 36$(LUA_BUILDDIR)/lua.rock: $(LUA_OBJ) $(TLSFLIB) $(LUA_BUILDDIR)/actions.lua $(LUA_BUILDDIR)/buttons.lua $(LUA_BUILDDIR)/rocklib_aux.o $(LUA_INCLUDELIST)
34 37
35$(LUA_BUILDDIR)/actions.lua: $(LUA_OBJ) $(LUA_SRCDIR)/action_helper.pl 38$(LUA_BUILDDIR)/actions.lua: $(LUA_OBJ) $(LUA_SRCDIR)/action_helper.pl
36 $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -E $(APPSDIR)/plugins/lib/pluginlib_actions.h | $(LUA_SRCDIR)/action_helper.pl > $(LUA_BUILDDIR)/actions.lua 39 $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -E $(APPSDIR)/plugins/lib/pluginlib_actions.h | $(LUA_SRCDIR)/action_helper.pl > $(LUA_BUILDDIR)/actions.lua
@@ -46,6 +49,9 @@ $(LUA_BUILDDIR)/rocklib_aux.c: $(APPSDIR)/plugin.h $(LUA_OBJ) $(LUA_SRCDIR)/rock
46$(LUA_BUILDDIR)/rocklib_aux.o: $(LUA_BUILDDIR)/rocklib_aux.c 49$(LUA_BUILDDIR)/rocklib_aux.o: $(LUA_BUILDDIR)/rocklib_aux.c
47 $(call PRINTS,CC $(<F))$(CC) $(INCLUDES) $(PLUGINFLAGS) -I $(LUA_SRCDIR) -c $< -o $@ 50 $(call PRINTS,CC $(<F))$(CC) $(INCLUDES) $(PLUGINFLAGS) -I $(LUA_SRCDIR) -c $< -o $@
48 51
52$(LUA_INCLUDELIST): %: $(LUA_INCLUDEDIR)/%.lua
53 $(call PRINTS,CP $(subst $(LUA_INCLUDEDIR)/,,$<))cp $< $(LUA_BUILDDIR)/$@.lua
54
49$(LUA_BUILDDIR)/lua.refmap: $(LUA_OBJ) $(TLSFLIB) 55$(LUA_BUILDDIR)/lua.refmap: $(LUA_OBJ) $(TLSFLIB)
50 56
51$(LUA_OUTLDS): $(PLUGIN_LDS) $(LUA_BUILDDIR)/lua.refmap 57$(LUA_OUTLDS): $(PLUGIN_LDS) $(LUA_BUILDDIR)/lua.refmap
@@ -58,3 +64,4 @@ $(LUA_BUILDDIR)/lua.ovl: $(LUA_OBJ) $(TLSFLIB) $(LUA_OUTLDS)
58 $(filter %.a, $+) \ 64 $(filter %.a, $+) \
59 -lgcc $(LUA_OVLFLAGS) 65 -lgcc $(LUA_OVLFLAGS)
60 $(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@) 66 $(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@)
67
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
58struct rocklua_image 88struct 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
66static void rli_wrap(lua_State *L, fb_data *src, int width, int height) 98/* holds iterator data for rlimages */
99struct 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); 110enum 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; 116static 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
78static 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
140static fb_data x_shift = FB_SCALARPACK(0);
141static fb_data y_shift = FB_SCALARPACK(0);
142static fb_data xy_mask = FB_SCALARPACK(0);
143static const fb_data *pixelmask = NULL;
144
145/* conversion between packed native formats and individual pixel addressing */
146static 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
187static 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
193static 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
203static 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 */
210static 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
256static 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 */
268static 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
275static 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
284static 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
319static 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
327static 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
93static int rli_new(lua_State *L) 337static 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
359static 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
365static 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
371static 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 */
390static 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
432static 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*/
469static 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
491static 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 */
565static 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
605static 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 */
684static 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 */
734static 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 */
743static 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 */
794static 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
868static 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 *********************************************/
887RLI_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
103static struct rocklua_image* rli_checktype(lua_State *L, int arg) 900RLI_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
907RLI_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
110static int rli_width(lua_State *L) 914RLI_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
921RLI_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
117static int rli_height(lua_State *L) 928RLI_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
124static fb_data* rli_element(lua_State *L) 936RLI_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
943RLI_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
139static int rli_set(lua_State *L) 965RLI_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
146static int rli_get(lua_State *L) 996#ifdef RLI_EXTENDED
997RLI_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
1004RLI_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
1011RLI_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
1030RLI_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
152static int rli_tostring(lua_State *L) 1054RLI_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
1101RLI_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
1196RLI_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 */
159static const struct luaL_reg rli_lib [] = 1231static 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
169static inline void rli_init(lua_State *L) 1254static 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 */
192static void check_tablevalue(lua_State *L, const char* key, int tablepos, void* res, bool unsigned_val) 1281static 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
207static struct viewport* opt_viewport(lua_State *L, int narg, struct viewport* alt) 1299static 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
248RB_WRAP(set_viewport) 1325RB_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