summaryrefslogtreecommitdiff
path: root/apps/plugins/lua/include_lua
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2018-07-23 02:50:22 +0200
committerWilliam Wilgus <me.theuser@yahoo.com>2018-07-23 05:13:32 +0200
commitaf35d1991633f33965801dcb153a9860c4432816 (patch)
treeb6accfcf2914ff7f565d60b9338dfa7998e27c68 /apps/plugins/lua/include_lua
parentef210b5fe4f72a102dcaa1f752d3022d62da8bc4 (diff)
downloadrockbox-af35d1991633f33965801dcb153a9860c4432816.tar.gz
rockbox-af35d1991633f33965801dcb153a9860c4432816.zip
Rocklua -- Extend / Fix rliImage
Some devices(1-bit / 2-bit displays) have packed bit formats that need to be unpacked in order to work on them at a pixel level. This caused a few issues on 1 & 2-bit devices: Greatly Oversized data arrays for bitmaps Improper handling of native image data Framebuffer data was near unusable without jumping through hoops Conversion between native addressing and per pixel addressing incurs extra overhead but it is much faster to do it on the 'C' side rather than in lua. Not to mention the advantage of a unified interface for the end programer ------------------------------------------------------------------- Adds a sane way to access each pixel of image data Adds: -------------------------------------------------------------------- img:clear([color],[x1],[y1],[x2],[y2]) (set whole image or a portion to a particular value) -------------------------------------------------------------------- img:invert([x1],[y1],[x2],[y2]) (inverts whole image or a portion) -------------------------------------------------------------------- img:marshal([x1],[y1],[x2],[y2],[funct]) (calls funct for each point defined by rect of x1,y1 x2,y2 returns value and allows setting value of each point return nil to terminate early) -------------------------------------------------------------------- img:points([x1],[y1],[x2],[y2],[dx],[dy]) (returns iterator function that steps delta-x and delta-y pixels each call returns value of pixel each call but doesn't allow setting to a new value compare to lua pairs method) -------------------------------------------------------------------- img:copy(src,[x1],[y1],[x2],[y2],[w],[h],[clip][operation][clr/funct]) (copies all or part of an image -- straight copy or special ops optionally calls funct for each point defined by rect of x1, y1, w, h and x2, y2, w, h for dest and src images returns value of dst and src and allows setting value of each point return nil to terminate early) -------------------------------------------------------------------- img:line(x1, y1, x2, y2, color) -------------------------------------------------------------------- img:ellipse(x1, y1, x2, y2, color, [fillcolor] -------------------------------------------------------------------- Fixed handling of 2-bit vertical integrated screens Added direct element access for saving / restoring native image etc. Added more data to tostring() handler and a way to access individual items Added equals method to see if two variables reference the same image address (doesn't check if two separate images contain the same 'picture') Optimized get and set routines Fixed out of bound x coord access shifting to next line Added lua include files to expose new functionality Finished image saving routine Static allocation of set_viewport struct faster + saves ram over dynamic Cleaned up code Fixed pixel get/set for 1/2 bit devices Fixed handling for 24-bit devices (32?) ------------------------------------------------------------------------- Example lua script to follow on forums ------------------------------------------------------------------------- Change-Id: I8a9ff0ff72aacf4b1662767ccb2b312fc355239c
Diffstat (limited to 'apps/plugins/lua/include_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
8 files changed, 1869 insertions, 0 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