summaryrefslogtreecommitdiff
path: root/apps/plugins/lua
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2019-08-04 09:48:09 -0500
committerWilliam Wilgus <me.theuser@yahoo.com>2019-08-04 16:57:02 +0200
commitf85df30e3e7da55c6880a321b3d8dc737f7af5b2 (patch)
treec778d743feef43162f53df9c1b642912822f664e /apps/plugins/lua
parent4209c097705ce474f955bf1d73ca6ccf1ac6786a (diff)
downloadrockbox-f85df30e3e7da55c6880a321b3d8dc737f7af5b2.tar.gz
rockbox-f85df30e3e7da55c6880a321b3d8dc737f7af5b2.zip
lua add rlimg.lua example script split large includes to separate files
Change-Id: I67cac5bc4ce5525ab30abf9443f6cc1a33190512
Diffstat (limited to 'apps/plugins/lua')
-rw-r--r--apps/plugins/lua/include_lua/draw.lua216
-rw-r--r--apps/plugins/lua/include_lua/draw_floodfill.lua95
-rw-r--r--apps/plugins/lua/include_lua/draw_poly.lua103
-rw-r--r--apps/plugins/lua/include_lua/draw_text.lua121
-rw-r--r--apps/plugins/lua/include_lua/image.lua184
-rw-r--r--apps/plugins/lua/include_lua/image_save.lua215
-rw-r--r--apps/plugins/lua/lua.make4
7 files changed, 545 insertions, 393 deletions
diff --git a/apps/plugins/lua/include_lua/draw.lua b/apps/plugins/lua/include_lua/draw.lua
index 0ee3e93d75..7208b63efb 100644
--- a/apps/plugins/lua/include_lua/draw.lua
+++ b/apps/plugins/lua/include_lua/draw.lua
@@ -29,7 +29,6 @@
29 _draw.ellipse_filled 29 _draw.ellipse_filled
30 _draw.ellipse_rect_filled 30 _draw.ellipse_rect_filled
31 _draw.ellipse_rect 31 _draw.ellipse_rect
32 _draw.flood_fill
33 _draw.hline 32 _draw.hline
34 _draw.image 33 _draw.image
35 _draw.line 34 _draw.line
@@ -39,7 +38,6 @@
39 _draw.rect_filled 38 _draw.rect_filled
40 _draw.rounded_rect 39 _draw.rounded_rect
41 _draw.rounded_rect_filled 40 _draw.rounded_rect_filled
42 _draw.text
43 _draw.vline 41 _draw.vline
44 42
45]] 43]]
@@ -56,21 +54,17 @@ local _draw = {} do
56 setmetatable(_draw, rocklib_image) 54 setmetatable(_draw, rocklib_image)
57 55
58 -- Internal Constants 56 -- Internal Constants
59 local _LCD = rb.lcd_framebuffer()
60 local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT
61 local BSAND = 8 -- blits color to dst if src <> 0 57 local BSAND = 8 -- blits color to dst if src <> 0
62 local _NIL = nil -- nil placeholder 58 local _NIL = nil -- nil placeholder
63 59
64 local _abs = math.abs 60
65 local _clear = rocklib_image.clear 61 local _clear = rocklib_image.clear
66 local _copy = rocklib_image.copy 62 local _copy = rocklib_image.copy
67 local _ellipse = rocklib_image.ellipse 63 local _ellipse = rocklib_image.ellipse
68 local _get = rocklib_image.get 64 local _get = rocklib_image.get
69 local _line = rocklib_image.line 65 local _line = rocklib_image.line
70 local _marshal = rocklib_image.marshal
71 local _min = math.min 66 local _min = math.min
72 local _newimg = rb.new_image 67 local _newimg = rb.new_image
73 local _points = rocklib_image.points
74 68
75 -- line 69 -- line
76 _draw.line = function(img, x1, y1, x2, y2, color, bClip) 70 _draw.line = function(img, x1, y1, x2, y2, color, bClip)
@@ -87,33 +81,22 @@ local _draw = {} do
87 _line(img, x, y, _NIL, y + length, color, bClip) 81 _line(img, x, y, _NIL, y + length, color, bClip)
88 end 82 end
89 83
90 -- draws a non-filled figure based on points in t-points 84 -- draws a non-filled rect based on points in t-points
91 local function polyline(img, x, y, t_points, color, bClosed, bClip) 85 local function polyrect(img, x, y, t_points, color, bClip)
92 if #t_points < 2 then error("not enough points", 3) end 86 local pt_first_last = t_points[1]
93 87 local pt1, pt2
94 local pt_first_last 88 for i = 1, 4, 1 do
95 89 pt1 = t_points[i]
96 if bClosed then 90 pt2 = t_points[i + 1] or pt_first_last-- first and last point
97 pt_first_last = t_points[1]
98 else
99 pt_first_last = t_points[#t_points]
100 end
101
102 for i = 1, #t_points, 1 do
103 local pt1 = t_points[i]
104
105 local pt2 = t_points[i + 1] or pt_first_last-- first and last point
106
107 _line(img, pt1[1] + x, pt1[2] + y, pt2[1] + x, pt2[2] + y, color, bClip) 91 _line(img, pt1[1] + x, pt1[2] + y, pt2[1] + x, pt2[2] + y, color, bClip)
108 end 92 end
109
110 end 93 end
111 94
112 -- rectangle 95 -- rectangle
113 local function rect(img, x, y, width, height, color, bClip) 96 local function rect(img, x, y, width, height, color, bClip)
114 if width == 0 or height == 0 then return end 97 if width == 0 or height == 0 then return end
115 98
116 polyline(img, x, y, {{0, 0}, {width, 0}, {width, height}, {0, height}}, color, true, bClip) 99 polyrect(img, x, y, {{0, 0}, {width, 0}, {width, height}, {0, height}}, color, bClip)
117 100
118 end 101 end
119 102
@@ -240,190 +223,9 @@ local _draw = {} do
240 _copy(dst, src, x, y, 1, 1, _NIL, _NIL, bClip) 223 _copy(dst, src, x, y, 1, 1, _NIL, _NIL, bClip)
241 end 224 end
242 225
243 -- floods an area of targetclr with fillclr x, y specifies the start seed
244 _draw.flood_fill = function(img, x, y, targetclr, fillclr)
245 -- scanline 4-way flood algorithm
246 -- ^
247 -- <--------x--->
248 -- v
249 -- check that target color doesn't = fill and the first point is target color
250 if targetclr == fillclr or targetclr ~= _get(img, x, y, true) then return end
251 local max_w = img:width()
252 local max_h = img:height()
253
254 local qpt = {} -- FIFO queue
255 -- rather than moving elements around in our FIFO queue
256 -- for each read; increment 'qhead' by 2
257 -- set both elements to nil and let the
258 -- garbage collector worry about it
259 -- for each write; increment 'qtail' by 2
260 -- x coordinates are in odd indices while
261 -- y coordinates are in even indices
262
263 local qtail = 0
264
265 local function check_ns(val, x, y)
266 if targetclr == val then
267 y = y - 1
268 if targetclr == _get(img, x, y, true) then -- north
269 qtail = qtail + 2
270 qpt[qtail - 1] = x
271 qpt[qtail] = y
272 end
273 y = y + 2
274 if targetclr == _get(img, x, y, true) then -- south
275 qtail = qtail + 2
276 qpt[qtail - 1] = x
277 qpt[qtail] = y
278 end
279 return fillclr
280 end
281 return _NIL -- signal marshal to stop
282 end
283
284 local function seed_pt(x, y)
285 -- should never hit max but make sure not to end early
286 for qhead = 2, 0x40000000, 2 do
287
288 if targetclr == _get(img, x, y, true) then
289 _marshal(img, x, y, 1, y, _NIL, _NIL, true, check_ns) -- west
290 _marshal(img, x + 1, y, max_w, y, _NIL, _NIL, true, check_ns) -- east
291 end
292
293 x = qpt[qhead - 1]
294 qpt[qhead - 1] = _NIL
295
296 if not x then break end
297
298 y = qpt[qhead]
299 qpt[qhead] = _NIL
300 end
301 end
302
303 seed_pt(x, y) -- Begin
304 end -- flood_fill
305
306 -- draws a closed figure based on points in t_points
307 _draw.polygon = function(img, x, y, t_points, color, fillcolor, bClip)
308 if #t_points < 2 then error("not enough points", 3) end
309
310 if fillcolor then
311 local x_min, x_max = 0, 0
312 local y_min, y_max = 0, 0
313 local w, h = 0, 0
314 -- find boundries of polygon
315 for i = 1, #t_points, 1 do
316 local pt = t_points[i]
317 if pt[1] < x_min then x_min = pt[1] end
318 if pt[1] > x_max then x_max = pt[1] end
319 if pt[2] < y_min then y_min = pt[2] end
320 if pt[2] > y_max then y_max = pt[2] end
321 end
322 w = _abs(x_max) + _abs(x_min)
323 h = _abs(y_max) + _abs(y_min)
324 x_min = x_min - 2 -- leave a border to use flood_fill
325 y_min = y_min - 2
326
327 local fill_img = _newimg(w + 3, h + 3)
328 _clear(fill_img, 0x1)
329
330 for i = 1, #t_points, 1 do
331 local pt1 = t_points[i]
332 local pt2 = t_points[i + 1] or t_points[1]-- first and last point
333 _line(fill_img, pt1[1] - x_min, pt1[2] - y_min,
334 pt2[1]- x_min, pt2[2] - y_min, 0)
335
336 end
337 _draw.flood_fill(fill_img, fill_img:width(), fill_img:height() , 0x1, 0x0)
338 _copy(img, fill_img, x - 1, y - 1, _NIL, _NIL, _NIL, _NIL, bClip, BSAND, fillcolor)
339 end
340
341 polyline(img, x, y, t_points, color, true, bClip)
342 end
343
344 -- draw text onto image if width/height are supplied text is centered
345 _draw.text = function(img, x, y, width, height, font, color, text)
346 font = font or rb.FONT_UI
347
348 local opts = {x = 0, y = 0, width = LCD_W - 1, height = LCD_H - 1,
349 font = font, drawmode = 3, fg_pattern = 0x1, bg_pattern = 0}
350
351 if rb.LCD_DEPTH == 2 then -- invert 2-bit screens
352 --vp.drawmode = bit.bxor(vp.drawmode, 4)
353 opts.fg_pattern = 3 - opts.fg_pattern
354 opts.bg_pattern = 3 - opts.bg_pattern
355 end
356 rb.set_viewport(opts)
357
358 local res, w, h = rb.font_getstringsize(text, font)
359
360 if not width then
361 width = 0
362 else
363 width = (width - w) / 2
364 end
365
366 if not height then
367 height = 0
368 else
369 height = (height - h) / 2
370 end
371
372 -- make a copy of the current screen for later
373 local screen_img = _newimg(LCD_W, LCD_H)
374 _copy(screen_img, _LCD)
375
376 -- check if the screen buffer is supplied image if so set img to the copy
377 if img == _LCD then
378 img = screen_img
379 end
380
381 -- we will be printing the text to the screen then blitting into img
382 rb.lcd_clear_display()
383
384 if w > LCD_W then -- text is too long for the screen do it in chunks
385 local l = 1
386 local resp, wp, hp
387 local lenr = text:len()
388
389 while lenr > 1 do
390 l = lenr
391 resp, wp, hp = rb.font_getstringsize(text:sub(1, l), font)
392
393 while wp >= LCD_W and l > 1 do
394 l = l - 1
395 resp, wp, hp = rb.font_getstringsize(text:sub( 1, l), font)
396 end
397
398 rb.lcd_putsxy(0, 0, text:sub(1, l))
399 text = text:sub(l)
400
401 if x + width > img:width() or y + height > img:height() then
402 break
403 end
404
405 -- using the mask we made blit color into img
406 _copy(img, _LCD, x + width, y + height, _NIL, _NIL, _NIL, _NIL, false, BSAND, color)
407 x = x + wp
408 rb.lcd_clear_display()
409 lenr = text:len()
410 end
411 else --w <= LCD_W
412 rb.lcd_putsxy(0, 0, text)
413
414 -- using the mask we made blit color into img
415 _copy(img, _LCD, x + width, y + height, _NIL, _NIL, _NIL, _NIL, false, BSAND, color)
416 end
417
418 _copy(_LCD, screen_img) -- restore screen
419 rb.set_viewport() -- set viewport default
420 return res, w, h
421 end
422
423 -- expose internal functions to the outside through _draw table 226 -- expose internal functions to the outside through _draw table
424 _draw.hline = hline 227 _draw.hline = hline
425 _draw.vline = vline 228 _draw.vline = vline
426 _draw.polyline = polyline
427 _draw.rect = rect 229 _draw.rect = rect
428 _draw.rounded_rect = rounded_rect 230 _draw.rounded_rect = rounded_rect
429end -- _draw functions 231end -- _draw functions
diff --git a/apps/plugins/lua/include_lua/draw_floodfill.lua b/apps/plugins/lua/include_lua/draw_floodfill.lua
new file mode 100644
index 0000000000..0303e711f2
--- /dev/null
+++ b/apps/plugins/lua/include_lua/draw_floodfill.lua
@@ -0,0 +1,95 @@
1--[[ Lua Floodfill function
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-- floods an area of targetclr with fillclr x, y specifies the start seed
24-- flood_fill(img, x, y, targetclr, fillclr)
25if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
26do
27
28 local rocklib_image = getmetatable(rb.lcd_framebuffer())
29 local _NIL = nil -- nil placeholder
30 local _get = rocklib_image.get
31 local _line = rocklib_image.line
32 local _marshal = rocklib_image.marshal
33
34 return function(img, x, y, targetclr, fillclr)
35 -- scanline 4-way flood algorithm
36 -- ^
37 -- <--------x--->
38 -- v
39 -- check that target color doesn't = fill and the first point is target color
40 if targetclr == fillclr or targetclr ~= _get(img, x, y, true) then return end
41 local max_w = img:width()
42 local max_h = img:height()
43
44 local qpt = {} -- FIFO queue
45 -- rather than moving elements around in our FIFO queue
46 -- for each read; increment 'qhead' by 2
47 -- set both elements to nil and let the
48 -- garbage collector worry about it
49 -- for each write; increment 'qtail' by 2
50 -- x coordinates are in odd indices while
51 -- y coordinates are in even indices
52
53 local qtail = 0
54
55 local function check_ns(val, x, y)
56 if targetclr == val then
57 y = y - 1
58 if targetclr == _get(img, x, y, true) then -- north
59 qtail = qtail + 2
60 qpt[qtail - 1] = x
61 qpt[qtail] = y
62 end
63 y = y + 2
64 if targetclr == _get(img, x, y, true) then -- south
65 qtail = qtail + 2
66 qpt[qtail - 1] = x
67 qpt[qtail] = y
68 end
69 return fillclr
70 end
71 return _NIL -- signal marshal to stop
72 end
73
74 local function seed_pt(x, y)
75 -- should never hit max but make sure not to end early
76 for qhead = 2, 0x40000000, 2 do
77
78 if targetclr == _get(img, x, y, true) then
79 _marshal(img, x, y, 1, y, _NIL, _NIL, true, check_ns) -- west
80 _marshal(img, x + 1, y, max_w, y, _NIL, _NIL, true, check_ns) -- east
81 end
82
83 x = qpt[qhead - 1]
84 qpt[qhead - 1] = _NIL
85
86 if not x then break end
87
88 y = qpt[qhead]
89 qpt[qhead] = _NIL
90 end
91 end
92
93 seed_pt(x, y) -- Begin
94 end -- flood_fill
95end
diff --git a/apps/plugins/lua/include_lua/draw_poly.lua b/apps/plugins/lua/include_lua/draw_poly.lua
new file mode 100644
index 0000000000..fd76a582b1
--- /dev/null
+++ b/apps/plugins/lua/include_lua/draw_poly.lua
@@ -0,0 +1,103 @@
1--[[ Lua Poly 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 _poly.polygon
26 _poly.polyline
27]]
28
29if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
30
31local _poly = {} do
32 local BSAND = 8 -- blits color to dst if src <> 0
33 local _NIL = nil -- nil placeholder
34
35 local _abs = math.abs
36 local _copy = rocklib_image.copy
37 local _line = rocklib_image.line
38 local flood_fill = require("draw_floodfill")
39
40 -- draws a non-filled figure based on points in t-points
41 local function polyline(img, x, y, t_points, color, bClosed, bClip)
42 if #t_points < 2 then error("not enough points", 3) end
43
44 local pt_first_last
45
46 if bClosed then
47 pt_first_last = t_points[1]
48 else
49 pt_first_last = t_points[#t_points]
50 end
51
52 for i = 1, #t_points, 1 do
53 local pt1 = t_points[i]
54
55 local pt2 = t_points[i + 1] or pt_first_last-- first and last point
56
57 _line(img, pt1[1] + x, pt1[2] + y, pt2[1] + x, pt2[2] + y, color, bClip)
58 end
59
60 end
61
62 -- draws a closed figure based on points in t_points
63 _poly.polygon = function(img, x, y, t_points, color, fillcolor, bClip)
64 if #t_points < 2 then error("not enough points", 3) end
65
66 if fillcolor then
67 local x_min, x_max = 0, 0
68 local y_min, y_max = 0, 0
69 local w, h = 0, 0
70 -- find boundries of polygon
71 for i = 1, #t_points, 1 do
72 local pt = t_points[i]
73 if pt[1] < x_min then x_min = pt[1] end
74 if pt[1] > x_max then x_max = pt[1] end
75 if pt[2] < y_min then y_min = pt[2] end
76 if pt[2] > y_max then y_max = pt[2] end
77 end
78 w = _abs(x_max) + _abs(x_min)
79 h = _abs(y_max) + _abs(y_min)
80 x_min = x_min - 2 -- leave a border to use flood_fill
81 y_min = y_min - 2
82
83 local fill_img = _newimg(w + 3, h + 3)
84 _clear(fill_img, 0x1)
85
86 for i = 1, #t_points, 1 do
87 local pt1 = t_points[i]
88 local pt2 = t_points[i + 1] or t_points[1]-- first and last point
89 _line(fill_img, pt1[1] - x_min, pt1[2] - y_min,
90 pt2[1]- x_min, pt2[2] - y_min, 0)
91
92 end
93 flood_fill(fill_img, fill_img:width(), fill_img:height() , 0x1, 0x0)
94 _copy(img, fill_img, x - 1, y - 1, _NIL, _NIL, _NIL, _NIL, bClip, BSAND, fillcolor)
95 end
96
97 polyline(img, x, y, t_points, color, true, bClip)
98 end
99
100 -- expose internal functions to the outside through _poly table
101 _poly.polyline = polyline
102end
103return _poly
diff --git a/apps/plugins/lua/include_lua/draw_text.lua b/apps/plugins/lua/include_lua/draw_text.lua
new file mode 100644
index 0000000000..3722931c2c
--- /dev/null
+++ b/apps/plugins/lua/include_lua/draw_text.lua
@@ -0,0 +1,121 @@
1--[[ Lua Draw Text function
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-- draw text onto image if width/height are supplied text is centered
24
25if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
26
27do
28 -- Internal Constants
29 local rocklib_image = getmetatable(rb.lcd_framebuffer())
30 local _LCD = rb.lcd_framebuffer()
31 local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT
32 local BSAND = 8 -- blits color to dst if src <> 0
33 local _NIL = nil -- nil placeholder
34
35 local _clear = rocklib_image.clear
36 local _copy = rocklib_image.copy
37 local _newimg = rb.new_image
38
39
40 return function(img, x, y, width, height, font, color, text)
41 font = font or rb.FONT_UI
42
43 local opts = {x = 0, y = 0, width = LCD_W - 1, height = LCD_H - 1,
44 font = font, drawmode = 3, fg_pattern = 0x1, bg_pattern = 0}
45
46 if rb.LCD_DEPTH == 2 then -- invert 2-bit screens
47 --vp.drawmode = bit.bxor(vp.drawmode, 4)
48 opts.fg_pattern = 3 - opts.fg_pattern
49 opts.bg_pattern = 3 - opts.bg_pattern
50 end
51 rb.set_viewport(opts)
52
53 local res, w, h = rb.font_getstringsize(text, font)
54
55 if not width then
56 width = 0
57 else
58 width = (width - w) / 2
59 end
60
61 if not height then
62 height = 0
63 else
64 height = (height - h) / 2
65 end
66
67 -- make a copy of the current screen for later
68 --local screen_img = _newimg(LCD_W, LCD_H)
69 local screen_img = _newimg(LCD_W, h * 2)
70 _copy(screen_img, _LCD)
71
72 -- check if the screen buffer is supplied image if so set img to the copy
73 if img == _LCD then
74 img = screen_img
75 end
76
77 -- we will be printing the text to the screen then blitting into img
78 --rb.lcd_clear_display()
79 _clear(_LCD, opts.bg_pattern or 0, 1, 1, LCD_W, h * 2)
80
81 if w > LCD_W then -- text is too long for the screen do it in chunks
82 local l = 1
83 local resp, wp, hp
84 local lenr = text:len()
85
86 while lenr > 1 do
87 l = lenr
88 resp, wp, hp = rb.font_getstringsize(text:sub(1, l), font)
89
90 while wp >= LCD_W and l > 1 do
91 l = l - 1
92 resp, wp, hp = rb.font_getstringsize(text:sub( 1, l), font)
93 end
94
95 rb.lcd_putsxy(0, 0, text:sub(1, l))
96 text = text:sub(l)
97
98 if x + width > img:width() or y + height > img:height() then
99 break
100 end
101
102 -- using the mask we made blit color into img
103 _copy(img, _LCD, x + width, y + height, _NIL, _NIL, _NIL, _NIL, false, BSAND, color)
104 x = x + wp
105 --rb.lcd_clear_display()
106 _clear(_LCD, opts.bg_pattern or 0, 1, 1, LCD_W, h * 2)
107
108 lenr = text:len()
109 end
110 else --w <= LCD_W
111 rb.lcd_putsxy(0, 0, text)
112
113 -- using the mask we made blit color into img
114 _copy(img, _LCD, x + width, y + height, _NIL, _NIL, _NIL, _NIL, false, BSAND, color)
115 end
116
117 _copy(_LCD, screen_img) -- restore screen
118 rb.set_viewport() -- set viewport default
119 return res, w, h
120 end
121end
diff --git a/apps/plugins/lua/include_lua/image.lua b/apps/plugins/lua/include_lua/image.lua
index 2b2a1ce19f..5fd452ea78 100644
--- a/apps/plugins/lua/include_lua/image.lua
+++ b/apps/plugins/lua/include_lua/image.lua
@@ -23,7 +23,6 @@
23 23
24--[[ Exposed Functions 24--[[ Exposed Functions
25 25
26 _img.save
27 _img.search 26 _img.search
28 _img.rotate 27 _img.rotate
29 _img.resize 28 _img.resize
@@ -156,189 +155,6 @@ local _img = {} do
156 return r_img 155 return r_img
157 end 156 end
158 157
159 -- saves img to file: name
160 _img.save = function(img, name)
161 -- bmp saving derived from rockbox - screendump.c
162 -- bitdepth is limited by the device
163 -- eg. device displays greyscale, rgb images are saved greyscale
164 local file
165 local bbuffer = {} -- concat buffer for s_bytes
166 local fbuffer = {} -- concat buffer for file writes, reused
167
168 local function s_bytesLE(bits, value)
169 -- bits must be multiples of 8 (sizeof byte)
170 local byte
171 local nbytes = bit.rshift(bits, 3)
172 for b = 1, nbytes do
173 if value > 0 then
174 byte = value % 256
175 value = (value - byte) / 256
176 else
177 byte = 0
178 end
179 bbuffer[b] = string.char(byte)
180 end
181 return table.concat(bbuffer, _NIL, 1, nbytes)
182 end
183
184 local function s_bytesBE(bits, value)
185 -- bits must be multiples of 8 (sizeof byte)
186 local byte
187 local nbytes = bit.rshift(bits, 3)
188 for b = nbytes, 1, -1 do
189 if value > 0 then
190 byte = value % 256
191 value = (value - byte) / 256
192 else
193 byte = 0
194 end
195 bbuffer[b] = string.char(byte)
196 end
197 return table.concat(bbuffer, _NIL, 1, nbytes)
198 end
199
200 local cmp = {["r"] = function(c) return bit.band(bit.rshift(c, 16), 0xFF) end,
201 ["g"] = function(c) return bit.band(bit.rshift(c, 08), 0xFF) end,
202 ["b"] = function(c) return bit.band(c, 0xFF) end}
203
204 local function bmp_color(color)
205 return s_bytesLE(8, cmp.b(color))..
206 s_bytesLE(8, cmp.g(color))..
207 s_bytesLE(8, cmp.r(color))..
208 s_bytesLE(8, 0) .. ""
209 end -- c_cmp(color, c.r))
210
211 local function bmp_color_mix(c1, c2, num, den)
212 -- mixes c1 and c2 as ratio of numerator / denominator
213 -- used 2x each save results
214 local bc1, gc1, rc1 = cmp.b(c1), cmp.g(c1), cmp.r(c1)
215
216 return s_bytesLE(8, cmp.b(c2) - bc1 * num / den + bc1)..
217 s_bytesLE(8, cmp.g(c2) - gc1 * num / den + gc1)..
218 s_bytesLE(8, cmp.r(c2) - rc1 * num / den + rc1)..
219 s_bytesLE(8, 0) .. ""
220 end
221
222 local w, h = img:width(), img:height()
223 local depth = tonumber(img:__tostring(6)) -- RLI_INFO_DEPTH = 0x6
224 local format = tonumber(img:__tostring(7)) -- RLI_INFO_FORMAT = 0x7
225
226 local bpp, bypl -- bits per pixel, bytes per line
227 -- bypl, pad rows to a multiple of 4 bytes
228 if depth <= 4 then
229 bpp = 8 -- 256 color image
230 bypl = (w + 3)
231 elseif depth <= 16 then
232 bpp = 16
233 bypl = (w * 2 + 3)
234 elseif depth <= 24 then
235 bpp = 24
236 bypl = (w * 3 + 3)
237 else
238 bpp = 32
239 bypl = (w * 4 + 3)
240 end
241
242 local linebytes = bit.band(bypl, bit.bnot(3))
243
244 local bytesperpixel = bit.rshift(bpp, 3)
245 local headersz = 54
246 local imgszpad = h * linebytes
247
248 local compression, n_colors = 0, 0
249 local h_ppm, v_ppm = 0x00000EC4, 0x00000EC4 --Pixels Per Meter ~ 96 dpi
250
251 if depth == 16 then
252 compression = 3 -- BITFIELDS
253 n_colors = 3
254 elseif depth <= 8 then
255 n_colors = bit.lshift(1, depth)
256 end
257
258 headersz = headersz + (4 * n_colors)
259
260 file = io.open('/' .. name, "w+") -- overwrite, rb ignores the 'b' flag
261
262 if not file then
263 rb.splash(rb.HZ, "Error opening /" .. name)
264 return
265 end
266 -- create a bitmap header 'rope' with image details -- concatenated at end
267 local bmpheader = fbuffer
268
269 bmpheader[01] = "BM"
270 bmpheader[02] = s_bytesLE(32, headersz + imgszpad)
271 bmpheader[03] = "\0\0\0\0" -- WORD reserved 1 & 2
272 bmpheader[04] = s_bytesLE(32, headersz) -- BITMAPCOREHEADER size
273 bmpheader[05] = s_bytesLE(32, 40) -- BITMAPINFOHEADER size
274
275 bmpheader[06] = s_bytesLE(32, w)
276 bmpheader[07] = s_bytesLE(32, h)
277 bmpheader[08] = "\1\0" -- WORD color planes ALWAYS 1
278 bmpheader[09] = s_bytesLE(16, bpp) -- bits/pixel
279 bmpheader[10] = s_bytesLE(32, compression)
280 bmpheader[11] = s_bytesLE(32, imgszpad)
281 bmpheader[12] = s_bytesLE(32, h_ppm) -- biXPelsPerMeter
282 bmpheader[13] = s_bytesLE(32, v_ppm) -- biYPelsPerMeter
283 bmpheader[14] = s_bytesLE(32, n_colors)
284 bmpheader[15] = s_bytesLE(32, n_colors)
285
286 -- Color Table (#n_colors entries)
287 if depth == 1 then -- assuming positive display
288 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
289 bmpheader[#bmpheader + 1] = bmp_color(0x0)
290 elseif depth == 2 then
291 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
292 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 1, 3)
293 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 2, 3)
294 bmpheader[#bmpheader + 1] = bmp_color(0x0)
295 elseif depth == 16 then
296 if format == 555 then
297 -- red bitfield mask
298 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x00007C00)
299 -- green bitfield mask
300 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000003E0)
301 -- blue bitfield mask
302 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
303 else --565
304 -- red bitfield mask
305 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000F800)
306 -- green bitfield mask
307 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000007E0)
308 -- blue bitfield mask
309 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
310 end
311 end
312
313 file:write(table.concat(fbuffer))-- write the header to the file now
314 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
315
316 local imgdata = fbuffer
317 -- pad rows to a multiple of 4 bytes
318 local bytesleft = linebytes - (bytesperpixel * w)
319 local t_data = {}
320 local fs_bytes_E = s_bytesLE -- default save in Little Endian
321
322 if format == 3553 then -- RGB565SWAPPED
323 fs_bytes_E = s_bytesBE -- Saves in Big Endian
324 end
325
326 -- Bitmap lines start at bottom unless biHeight is negative
327 for point in _points(img, 1, h, w + bytesleft, 1) do
328 imgdata[#imgdata + 1] = fs_bytes_E(bpp, point or 0)
329
330 if #fbuffer >= 31 then -- buffered write, increase # for performance
331 file:write(table.concat(fbuffer))
332 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
333 end
334
335 end
336 file:write(table.concat(fbuffer)) --write leftovers to file
337 fbuffer = _NIL
338
339 file:close()
340 end -- save(img, name)
341
342 --searches an image for target color 158 --searches an image for target color
343 _img.search = function(img, x1, y1, x2, y2, targetclr, variation, stepx, stepy) 159 _img.search = function(img, x1, y1, x2, y2, targetclr, variation, stepx, stepy)
344 160
diff --git a/apps/plugins/lua/include_lua/image_save.lua b/apps/plugins/lua/include_lua/image_save.lua
new file mode 100644
index 0000000000..4735af46d7
--- /dev/null
+++ b/apps/plugins/lua/include_lua/image_save.lua
@@ -0,0 +1,215 @@
1--[[ Lua Image save
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-- save(img, path/name)
24-- bmp saving derived from rockbox - screendump.c
25-- bitdepth is limited by the device
26-- eg. device displays greyscale, rgb images are saved greyscale
27if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
28
29do
30 local rocklib_image = getmetatable(rb.lcd_framebuffer())
31
32 -- internal constants
33 local _NIL = nil -- _NIL placeholder
34 local _points = rocklib_image.points
35
36 -- saves img to file: name
37 return function(img, name)
38 local file
39 local bbuffer = {} -- concat buffer for s_bytes
40 local fbuffer = {} -- concat buffer for file writes, reused
41
42 local function s_bytesLE(bits, value)
43 -- bits must be multiples of 8 (sizeof byte)
44 local byte
45 local nbytes = bit.rshift(bits, 3)
46 for b = 1, nbytes do
47 if value > 0 then
48 byte = value % 256
49 value = (value - byte) / 256
50 else
51 byte = 0
52 end
53 bbuffer[b] = string.char(byte)
54 end
55 return table.concat(bbuffer, _NIL, 1, nbytes)
56 end
57
58 local function s_bytesBE(bits, value)
59 -- bits must be multiples of 8 (sizeof byte)
60 local byte
61 local nbytes = bit.rshift(bits, 3)
62 for b = nbytes, 1, -1 do
63 if value > 0 then
64 byte = value % 256
65 value = (value - byte) / 256
66 else
67 byte = 0
68 end
69 bbuffer[b] = string.char(byte)
70 end
71 return table.concat(bbuffer, _NIL, 1, nbytes)
72 end
73
74 local cmp = {["r"] = function(c) return bit.band(bit.rshift(c, 16), 0xFF) end,
75 ["g"] = function(c) return bit.band(bit.rshift(c, 08), 0xFF) end,
76 ["b"] = function(c) return bit.band(c, 0xFF) end}
77
78 local function bmp_color(color)
79 return s_bytesLE(8, cmp.b(color))..
80 s_bytesLE(8, cmp.g(color))..
81 s_bytesLE(8, cmp.r(color))..
82 s_bytesLE(8, 0) .. ""
83 end -- c_cmp(color, c.r))
84
85 local function bmp_color_mix(c1, c2, num, den)
86 -- mixes c1 and c2 as ratio of numerator / denominator
87 -- used 2x each save results
88 local bc1, gc1, rc1 = cmp.b(c1), cmp.g(c1), cmp.r(c1)
89
90 return s_bytesLE(8, cmp.b(c2) - bc1 * num / den + bc1)..
91 s_bytesLE(8, cmp.g(c2) - gc1 * num / den + gc1)..
92 s_bytesLE(8, cmp.r(c2) - rc1 * num / den + rc1)..
93 s_bytesLE(8, 0) .. ""
94 end
95
96 local w, h = img:width(), img:height()
97 local depth = tonumber(img:__tostring(6)) -- RLI_INFO_DEPTH = 0x6
98 local format = tonumber(img:__tostring(7)) -- RLI_INFO_FORMAT = 0x7
99
100 local bpp, bypl -- bits per pixel, bytes per line
101 -- bypl, pad rows to a multiple of 4 bytes
102 if depth <= 4 then
103 bpp = 8 -- 256 color image
104 bypl = (w + 3)
105 elseif depth <= 16 then
106 bpp = 16
107 bypl = (w * 2 + 3)
108 elseif depth <= 24 then
109 bpp = 24
110 bypl = (w * 3 + 3)
111 else
112 bpp = 32
113 bypl = (w * 4 + 3)
114 end
115
116 local linebytes = bit.band(bypl, bit.bnot(3))
117
118 local bytesperpixel = bit.rshift(bpp, 3)
119 local headersz = 54
120 local imgszpad = h * linebytes
121
122 local compression, n_colors = 0, 0
123 local h_ppm, v_ppm = 0x00000EC4, 0x00000EC4 --Pixels Per Meter ~ 96 dpi
124
125 if depth == 16 then
126 compression = 3 -- BITFIELDS
127 n_colors = 3
128 elseif depth <= 8 then
129 n_colors = bit.lshift(1, depth)
130 end
131
132 headersz = headersz + (4 * n_colors)
133
134 file = io.open('/' .. name, "w+") -- overwrite, rb ignores the 'b' flag
135
136 if not file then
137 rb.splash(rb.HZ, "Error opening /" .. name)
138 return
139 end
140 -- create a bitmap header 'rope' with image details -- concatenated at end
141 local bmpheader = fbuffer
142
143 bmpheader[01] = "BM"
144 bmpheader[02] = s_bytesLE(32, headersz + imgszpad)
145 bmpheader[03] = "\0\0\0\0" -- WORD reserved 1 & 2
146 bmpheader[04] = s_bytesLE(32, headersz) -- BITMAPCOREHEADER size
147 bmpheader[05] = s_bytesLE(32, 40) -- BITMAPINFOHEADER size
148
149 bmpheader[06] = s_bytesLE(32, w)
150 bmpheader[07] = s_bytesLE(32, h)
151 bmpheader[08] = "\1\0" -- WORD color planes ALWAYS 1
152 bmpheader[09] = s_bytesLE(16, bpp) -- bits/pixel
153 bmpheader[10] = s_bytesLE(32, compression)
154 bmpheader[11] = s_bytesLE(32, imgszpad)
155 bmpheader[12] = s_bytesLE(32, h_ppm) -- biXPelsPerMeter
156 bmpheader[13] = s_bytesLE(32, v_ppm) -- biYPelsPerMeter
157 bmpheader[14] = s_bytesLE(32, n_colors)
158 bmpheader[15] = s_bytesLE(32, n_colors)
159
160 -- Color Table (#n_colors entries)
161 if depth == 1 then -- assuming positive display
162 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
163 bmpheader[#bmpheader + 1] = bmp_color(0x0)
164 elseif depth == 2 then
165 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
166 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 1, 3)
167 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 2, 3)
168 bmpheader[#bmpheader + 1] = bmp_color(0x0)
169 elseif depth == 16 then
170 if format == 555 then
171 -- red bitfield mask
172 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x00007C00)
173 -- green bitfield mask
174 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000003E0)
175 -- blue bitfield mask
176 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
177 else --565
178 -- red bitfield mask
179 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000F800)
180 -- green bitfield mask
181 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000007E0)
182 -- blue bitfield mask
183 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
184 end
185 end
186
187 file:write(table.concat(fbuffer))-- write the header to the file now
188 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
189
190 local imgdata = fbuffer
191 -- pad rows to a multiple of 4 bytes
192 local bytesleft = linebytes - (bytesperpixel * w)
193 local t_data = {}
194 local fs_bytes_E = s_bytesLE -- default save in Little Endian
195
196 if format == 3553 then -- RGB565SWAPPED
197 fs_bytes_E = s_bytesBE -- Saves in Big Endian
198 end
199
200 -- Bitmap lines start at bottom unless biHeight is negative
201 for point in _points(img, 1, h, w + bytesleft, 1) do
202 imgdata[#imgdata + 1] = fs_bytes_E(bpp, point or 0)
203
204 if #fbuffer >= 31 then -- buffered write, increase # for performance
205 file:write(table.concat(fbuffer))
206 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
207 end
208
209 end
210 file:write(table.concat(fbuffer)) --write leftovers to file
211 fbuffer = _NIL
212
213 file:close()
214 end -- save(img, name)
215end
diff --git a/apps/plugins/lua/lua.make b/apps/plugins/lua/lua.make
index d5fb85afbc..388c5d74d3 100644
--- a/apps/plugins/lua/lua.make
+++ b/apps/plugins/lua/lua.make
@@ -16,8 +16,8 @@ LUA_OBJ := $(call c2obj, $(LUA_SRC))
16OTHER_SRC += $(LUA_SRC) 16OTHER_SRC += $(LUA_SRC)
17 17
18LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua 18LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua
19LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua draw.lua \ 19LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua draw.lua draw_floodfill.lua draw_poly.lua \
20 image.lua lcd.lua math_ex.lua print.lua \ 20 draw_text.lua image.lua image_save.lua lcd.lua math_ex.lua print.lua \
21 timer.lua playlist.lua pcm.lua sound.lua \ 21 timer.lua playlist.lua pcm.lua sound.lua \
22 rbcompat.lua printtable.lua) 22 rbcompat.lua printtable.lua)
23 23