diff options
author | William Wilgus <me.theuser@yahoo.com> | 2019-08-04 09:48:09 -0500 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2019-08-04 16:57:02 +0200 |
commit | f85df30e3e7da55c6880a321b3d8dc737f7af5b2 (patch) | |
tree | c778d743feef43162f53df9c1b642912822f664e | |
parent | 4209c097705ce474f955bf1d73ca6ccf1ac6786a (diff) | |
download | rockbox-f85df30e3e7da55c6880a321b3d8dc737f7af5b2.tar.gz rockbox-f85df30e3e7da55c6880a321b3d8dc737f7af5b2.zip |
lua add rlimg.lua example script split large includes to separate files
Change-Id: I67cac5bc4ce5525ab30abf9443f6cc1a33190512
-rw-r--r-- | apps/plugins/lua/include_lua/draw.lua | 216 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/draw_floodfill.lua | 95 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/draw_poly.lua | 103 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/draw_text.lua | 121 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/image.lua | 184 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/image_save.lua | 215 | ||||
-rw-r--r-- | apps/plugins/lua/lua.make | 4 | ||||
-rwxr-xr-x | apps/plugins/lua_scripts/rlimg.lua | 919 |
8 files changed, 1464 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 |
429 | end -- _draw functions | 231 | end -- _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) | ||
25 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
26 | do | ||
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 | ||
95 | end | ||
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 | |||
29 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
30 | |||
31 | local _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 | ||
102 | end | ||
103 | return _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 | |||
25 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
26 | |||
27 | do | ||
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 | ||
121 | end | ||
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 | ||
27 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
28 | |||
29 | do | ||
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) | ||
215 | end | ||
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)) | |||
16 | OTHER_SRC += $(LUA_SRC) | 16 | OTHER_SRC += $(LUA_SRC) |
17 | 17 | ||
18 | LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua | 18 | LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua |
19 | LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua draw.lua \ | 19 | LUA_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 | ||
diff --git a/apps/plugins/lua_scripts/rlimg.lua b/apps/plugins/lua_scripts/rlimg.lua new file mode 100755 index 0000000000..801d0c5fac --- /dev/null +++ b/apps/plugins/lua_scripts/rlimg.lua | |||
@@ -0,0 +1,919 @@ | |||
1 | --[[ | ||
2 | __________ __ ___. | ||
3 | Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | \/ \/ \/ \/ \/ | ||
8 | $Id$ | ||
9 | Example Lua RBIMAGE script | ||
10 | Copyright (C) 2009 by Maurus Cuelenaere -- some prior work | ||
11 | Copyright (C) 2017 William Wilgus | ||
12 | This program is free software; you can redistribute it and/or | ||
13 | modify it under the terms of the GNU General Public License | ||
14 | as published by the Free Software Foundation; either version 2 | ||
15 | of the License, or (at your option) any later version. | ||
16 | This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | KIND, either express or implied. | ||
18 | ]]-- | ||
19 | |||
20 | require("actions") -- Contains rb.actions & rb.contexts | ||
21 | -- require("buttons") -- Contains rb.buttons -- not needed for this example | ||
22 | |||
23 | local _math = require("math_ex") -- missing math sine cosine, sqrt, clamp functions | ||
24 | local _timer = require("timer") | ||
25 | local _clr = require("color") -- clrset, clrinc provides device independent colors | ||
26 | local _lcd = require("lcd") -- lcd helper functions | ||
27 | local _print = require("print") -- advanced text printing | ||
28 | local _img = require("image") -- image manipulation save, rotate, resize, tile, new, load | ||
29 | local _img_save = require("image_save") | ||
30 | local _blit = require("blit") -- handy list of blit operations | ||
31 | local _draw = require("draw") -- draw all the things (primitives) | ||
32 | local _draw_floodfill = require("draw_floodfill") | ||
33 | local _draw_text = require("draw_text") | ||
34 | |||
35 | --local scrpath = rb.current_path()" | ||
36 | |||
37 | --package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path | ||
38 | |||
39 | require("printmenu") --menu | ||
40 | |||
41 | --[[ RBIMAGE library functions | ||
42 | NOTE!! on x, y coordinates + width & height | ||
43 | ---------------------------------------------- | ||
44 | When making a new image you specify the width and height say (10, 20). | ||
45 | Intutively (at least to me) (0,0) (offset addressing) would reference the first | ||
46 | pixel (top left) and the last pixel (bottom, right) would be (w-1, h-1) or (9, 19) | ||
47 | but the original rbimage library (like lua) uses 1-based arrays (ordinal addressing) | ||
48 | for the image data so the first pixel is (1,1) and the last pixel is (w, h) | ||
49 | this presents a bit of a problem when interfacing with the internal rockbox | ||
50 | functions as you must remeber to convert all lua coordinates to 0 based coordinates. | ||
51 | I have opted to not change this in the name of compatibility with old scripts | ||
52 | |||
53 | NOTE2!! on width & height versus offset_x & offset_y :copy() | ||
54 | ------------------------------------------------------------------------ | ||
55 | The only place where RBIMAGE deviates from (ordinal addressing) is in the blit | ||
56 | / copy function sx, sy and dx, dy still refer to the same pixel as all the | ||
57 | other functions but offset_x, and offset_y are zero based | ||
58 | Example (assuming same sized images) | ||
59 | dst:copy(src, 1,1, 1,1, 0, 0) | ||
60 | would copy the first pixel from src to the first pixel of dst | ||
61 | dst:copy(src, 1,1, 1,1, dst:width()-1, dst:height()-1) would copy all of src to dst | ||
62 | this might be a bit confusing but makes reversing images (negative offsets) easier. | ||
63 | Since offsets are auto calculated if empty or out of range you could call it as | ||
64 | dst:copy(src, 1,1, 1,1, dst:width(), dst:height()) or even | ||
65 | dst:copy(src, 1,1, 1,1) if you prefered and get the same result | ||
66 | ]] | ||
67 | |||
68 | --'CONSTANTS' (in lua there really is no such thing as all vars are mutable) | ||
69 | -------------------------------------------------------- | ||
70 | --colors for fg/bg ------------------------ | ||
71 | local WHITE = _clr.set(-1, 255, 255, 255) | ||
72 | local BLACK = _clr.set(0, 0, 0, 0) | ||
73 | local RED = _clr.set(WHITE, 255) | ||
74 | local GREEN = _clr.set(WHITE, 0, 255) | ||
75 | local BLUE = _clr.set(WHITE, 0, 0, 255) | ||
76 | ------------------------------------------- | ||
77 | local clrs | ||
78 | local CANCEL_BUTTON = rb.actions.PLA_CANCEL | ||
79 | |||
80 | -- EXAMPLES ---------------------------------------------------------------------- EXAMPLES--------------------------------------------------------------------- | ||
81 | function my_blit(dst_val, dx, dy, src_val, sx, sy) | ||
82 | -- user defined blit operation | ||
83 | --this function gets called for every pixel you specify | ||
84 | --you may change pixels in both the source and dest image | ||
85 | --return nil to stop early | ||
86 | |||
87 | if _lcd.DEPTH < 2 then | ||
88 | return src_val | ||
89 | end | ||
90 | |||
91 | if dst_val == WHITE and bit.band(dx, 1) == 1 and bit.band(dy, 1) == 1 then | ||
92 | return BLACK; | ||
93 | elseif dst_val == WHITE then | ||
94 | return src_val | ||
95 | end | ||
96 | |||
97 | return dst_val | ||
98 | end | ||
99 | |||
100 | function create_logo() | ||
101 | --[[creates a small logo from data array]] | ||
102 | |||
103 | -- moves scope of white and black from global to local | ||
104 | local WHITE = WHITE | ||
105 | local BLACK = BLACK | ||
106 | |||
107 | --[[small array with 16 bits of data per element (16-bits in y direction) | ||
108 | while the number of elements (32) is the x direction. | ||
109 | in other words 16 rows X 32 columns, totally abritrary actually | ||
110 | you could easily rotate or flip it by changing the for loops below ]] | ||
111 | local logo = {0xFFFF, 0xFFFF, 0x8001, 0x8001, 0x9E7F, 0x9E7F, 0x9E7F, 0x9E7F, | ||
112 | 0x9E3F, 0x9E3F, 0x804F, 0x804F, 0xC0E1, 0xC0F1, 0xFFFD, 0xFFFF, | ||
113 | 0xFFFF, 0xFFFF, 0x8001, 0x8001, 0xFF01, 0xFF01, 0xFEFB, 0xFEFB, | ||
114 | 0xFDFD, 0xFDFD, 0xFDFD, 0xFCF9, 0xFE03, 0xFE03, 0xFFFF, 0xFFFF} | ||
115 | |||
116 | local img, img1, img2, img3 | ||
117 | |||
118 | img = _img.new(_lcd.W, _lcd.H) | ||
119 | img:clear(BLACK) --[[always clear an image after you create it if you | ||
120 | intend to do any thing besides copy something | ||
121 | entirely to it as there is no guarantee what | ||
122 | state the data within is in, it could be all | ||
123 | 0's or it could be every digit of PI ]] | ||
124 | |||
125 | -- check for an error condition bail if error | ||
126 | if(not img or not logo) then | ||
127 | return nil | ||
128 | end | ||
129 | |||
130 | local logosz = table.getn(logo) | ||
131 | local bits = 16 -- each element contains 16 pixels | ||
132 | local data = 0 | ||
133 | |||
134 | for i=1, math.min(logosz, _lcd.W) do | ||
135 | for j=0, math.min(bits, _lcd.H) do | ||
136 | |||
137 | if bit.band(bit.lshift(1, bits - j), logo[i]) > 0 then | ||
138 | data = WHITE | ||
139 | else | ||
140 | data = BLACK | ||
141 | end | ||
142 | -- Set the pixel at position i, j+1 to the copied data | ||
143 | img:set(i, j + 1, data) | ||
144 | end | ||
145 | end | ||
146 | |||
147 | -- make a new image the size of our generated logo | ||
148 | img1 = _img.new(logosz + 1, bits + 1) | ||
149 | |||
150 | -- img.copy(dest, source, dx, dy, sx, sy, [w, h]) | ||
151 | img1:copy(img, 1, 1, 1, 1) | ||
152 | |||
153 | --[[lua does auto garbage collection, but it is still | ||
154 | a good idea to set large items to nil when done anyways]] | ||
155 | img = nil | ||
156 | |||
157 | local sl -- new image size | ||
158 | if _lcd.W < _lcd.H then | ||
159 | sl = _lcd.W / 3 | ||
160 | else | ||
161 | sl = _lcd.H / 3 | ||
162 | end | ||
163 | |||
164 | -- make sl always even by subtracting 1 if needed | ||
165 | sl = bit.band(sl, bit.bnot(1)) | ||
166 | if sl < 16 then | ||
167 | sl = 16 | ||
168 | end | ||
169 | |||
170 | img2 = _img.new(sl, sl) | ||
171 | --img2:clear(BLACK) -- doesn't need cleared since we copy to it entirely | ||
172 | |||
173 | --[[we are going to resize the image since the data supplied is 32 x 16 | ||
174 | pixels its really tiny on most screens]] | ||
175 | _img.resize(img2, img1) | ||
176 | |||
177 | img1 = nil | ||
178 | |||
179 | img3 = _img.new(sl, sl) | ||
180 | img3:clear(BLACK) | ||
181 | |||
182 | if IS_COLOR_TARGET == true then | ||
183 | local c_yellow = _clr.set(WHITE, 0xFC, 0xC0, 0x00) | ||
184 | local c_grey = _clr.set(WHITE, 0xD4, 0xE3, 0xF3) | ||
185 | local c_dkgrey = _clr.set(WHITE, 0xB4, 0xC3, 0xD3) | ||
186 | -- if dest pixel == source pixel make dest pixel yellow | ||
187 | img3:copy(img2, 1, 1, 1, 1, nil, nil, false, _blit.BDEQS, c_yellow) | ||
188 | -- xor src pixel to dest pixel if both are 0 or both are 1 dest = 0 | ||
189 | img2:copy(img3, 1, 1, 2, 1, nil, nil, false, _blit.BXOR) | ||
190 | -- if dest pixel color > src pixel color copy grey to dest | ||
191 | img2:copy(img3, 1, 1, 1, 1, nil, nil, false, _blit.BDGTS, c_grey) | ||
192 | -- set img3 to grey | ||
193 | img3:clear(c_dkgrey) | ||
194 | end | ||
195 | |||
196 | -- make a WHITE square in the middle | ||
197 | |||
198 | img3:clear(WHITE, 4,4, img3:width() - 3, img3:height() - 3) | ||
199 | |||
200 | img3:copy(img2, 1, 1, 1, 1, nil, nil, false, _blit.CUSTOM, my_blit) | ||
201 | img2 = nil | ||
202 | _img_save(img3, "pix.bmp") | ||
203 | return img3 | ||
204 | end -- copy_logo | ||
205 | |||
206 | -- draws an olive erm ball and returns it | ||
207 | function create_ball() | ||
208 | local sl -- image size | ||
209 | if _lcd.W < _lcd.H then | ||
210 | sl = _lcd.W / 5 | ||
211 | else | ||
212 | sl = _lcd.H / 5 | ||
213 | end | ||
214 | |||
215 | -- make sl always even by subtracting 1 if needed | ||
216 | sl = bit.band(sl, bit.bnot(1)) | ||
217 | if sl < 16 then | ||
218 | sl = 16 | ||
219 | end | ||
220 | local img = _img.new(sl, sl) | ||
221 | img:clear(0) | ||
222 | _draw.circle_filled(img, sl/2, sl/2, sl/2 - 1, _clr.set(-1, 255), _clr.set(0, 100)) | ||
223 | _draw.circle_filled(img, sl/3, sl/3, sl/10, _clr.set(-1, 255, 0, 0)) | ||
224 | return img | ||
225 | end | ||
226 | |||
227 | -- bounces img around on screen | ||
228 | function bounce_image(img) | ||
229 | local timer = _timer() -- creates a new timer -- saves index | ||
230 | local wait | ||
231 | -- make a copy of the current screen for later | ||
232 | local screen_img = _lcd:duplicate() | ||
233 | |||
234 | local img_sqy = _img.new(img:width() + img:width() / 8, img:height()) | ||
235 | local img_sqx = _img.new(img:width(), img:height() + img:height() / 8) | ||
236 | _img.resize(img_sqy, img) | ||
237 | _img.resize(img_sqx, img) | ||
238 | |||
239 | -- moves definition of CANCEL_BUTTON from global to local | ||
240 | local CANCEL_BUTTON = CANCEL_BUTTON | ||
241 | -------------------------------------------------------- | ||
242 | local imgn = img | ||
243 | local hold = 0 | ||
244 | local sx = 1 -- starting x | ||
245 | local sy = 1 -- starting y | ||
246 | |||
247 | local ex = _lcd.W - img:width() - 2 | ||
248 | local ey = _lcd.H - img:width() - 2 | ||
249 | |||
250 | -- threshold resets speed, inverts image | ||
251 | local tx = ex / 5 | ||
252 | local ty = ey / 5 | ||
253 | |||
254 | local last_x = sx | ||
255 | local last_y = sy | ||
256 | |||
257 | local x = sx | ||
258 | local y = sy | ||
259 | |||
260 | local dx = 1 | ||
261 | local dy = 1 | ||
262 | -- negative width\height cause the image to be drawn from the opposite end | ||
263 | local fx = _lcd.W | ||
264 | local fy = _lcd.H | ||
265 | |||
266 | local function invert_images() | ||
267 | img:invert(); | ||
268 | img_sqx:invert() | ||
269 | img_sqy:invert() | ||
270 | end | ||
271 | |||
272 | local loops = (_lcd.W * _lcd.H) / 2 | ||
273 | while (loops > 0) do | ||
274 | |||
275 | if IS_COLOR_TARGET then | ||
276 | if bit.band(loops, 128) == 128 then | ||
277 | _lcd:copy(imgn, x, y, 1, 1, fx, fy, false, _blit.BOR) | ||
278 | _lcd:copy(screen_img, x, y, x, y, imgn:width(), imgn:height(), | ||
279 | false, _blit.BDEQC, imgn:get(1,1)) | ||
280 | else | ||
281 | _lcd:copy(imgn, x, y, 1, 1, fx, fy, false, _blit.BSNEC, imgn:get(1,1)) | ||
282 | end | ||
283 | else | ||
284 | local blitop | ||
285 | |||
286 | if imgn:get(1,1) ~= 0 then | ||
287 | blitop = _blit.BSNOT | ||
288 | else | ||
289 | blitop = _blit.BXOR | ||
290 | end | ||
291 | |||
292 | _lcd:copy(imgn, x, y, 1, 1, fx, fy, false, blitop, WHITE) | ||
293 | end | ||
294 | |||
295 | if hold < 1 then | ||
296 | imgn = img | ||
297 | else | ||
298 | hold = hold - 1 | ||
299 | end | ||
300 | _lcd:update() | ||
301 | |||
302 | x = x + dx | ||
303 | y = y + dy | ||
304 | |||
305 | if y >= ey or y <= sy then | ||
306 | dy = (-dy) | ||
307 | fy = (-fy) | ||
308 | imgn = img_sqy | ||
309 | hold = 3 | ||
310 | if dx < 0 and y < 10 then | ||
311 | dx = dx - 5 | ||
312 | end | ||
313 | if dx > tx then | ||
314 | dx = 5; | ||
315 | dy = 5; | ||
316 | invert_images() | ||
317 | end | ||
318 | end | ||
319 | |||
320 | if x >= ex or x <= sx then | ||
321 | dx = (-dx) | ||
322 | fx = (-fx) | ||
323 | imgn = img_sqx | ||
324 | hold = 3 | ||
325 | if dy < 0 and x < 10 then | ||
326 | dy = dy - 5 | ||
327 | end | ||
328 | if dy > ty then | ||
329 | dx = 5; | ||
330 | dy = 5; | ||
331 | invert_images() | ||
332 | end | ||
333 | end | ||
334 | |||
335 | x = _math.clamp(x, sx, ex) | ||
336 | y = _math.clamp(y, sy, ey) | ||
337 | |||
338 | -- copy original image back to screen | ||
339 | _lcd:copy(screen_img) | ||
340 | |||
341 | loops = loops -1 | ||
342 | |||
343 | wait = timer:check(true) --checks timer and updates last time | ||
344 | if wait >= 5 then | ||
345 | wait = 0 | ||
346 | elseif wait < 5 then | ||
347 | wait = 5 - wait | ||
348 | end | ||
349 | -- 0 = timeout immediately | ||
350 | -- ( -1 would be never timeout, and >0 is amount of 'ticks' before timeout) | ||
351 | if rb.get_plugin_action(wait) == CANCEL_BUTTON then | ||
352 | break; | ||
353 | end | ||
354 | end | ||
355 | |||
356 | timer:stop() -- destroys timer, also returns time since last time | ||
357 | |||
358 | -- leave the screen how we found it | ||
359 | _lcd:copy(screen_img) | ||
360 | end -- image_bounce | ||
361 | |||
362 | -- draws a gradient using available colors | ||
363 | function draw_gradient(img, x, y, w, h, direction, clrs) | ||
364 | x = x or 1 | ||
365 | y = y or 1 | ||
366 | w = w or img:width() - x | ||
367 | h = h or img:height() - y | ||
368 | local zstep = 0 | ||
369 | local step = 1 | ||
370 | if IS_COLOR_TARGET == true then -- Only do this when we're on a color target | ||
371 | local z = 1 | ||
372 | local c = 1 | ||
373 | clrs = clrs or {255,255,255} | ||
374 | local function gradient_rgb(p, c1, c2) | ||
375 | -- tried squares of difference but blends were very abrupt | ||
376 | local r = c1.r + (p * (c2.r - c1.r) / 10500) | ||
377 | local g = c1.g + (p * (c2.g - c1.g) / 10000) | ||
378 | local b = c1.b + (p * (c2.b - c1.b) / 09999) | ||
379 | return _clr.set(nil, r, g, b) | ||
380 | end | ||
381 | local function check_z() | ||
382 | if z > 10000 and c < #clrs - 1 then | ||
383 | z = 1 | ||
384 | c = c + 1 | ||
385 | elseif z > 10000 then | ||
386 | z = 10000 | ||
387 | end | ||
388 | end | ||
389 | if direction == "V" then | ||
390 | zstep = math.max(1, (10000 / ((w - 1) / (#clrs - 1)) + bit.band(#clrs, 1))) | ||
391 | for i=x, w + x do | ||
392 | check_z() | ||
393 | _draw.vline(img, i, y, h, gradient_rgb(z, clrs[c], clrs[c + 1])) | ||
394 | z = z + zstep | ||
395 | end | ||
396 | else | ||
397 | zstep = math.max(1, (10000 / ((h - 1) / (#clrs - 1)) + bit.band(#clrs, 1))) | ||
398 | for j=y, h + y do | ||
399 | check_z() | ||
400 | _draw.hline(img, x, j, w, gradient_rgb(z, clrs[c], clrs[c + 1])) | ||
401 | z = z + zstep | ||
402 | end | ||
403 | end | ||
404 | else -- not a color target but might be greyscale | ||
405 | local clr = _clr.set(-1) | ||
406 | for i=x, w + x do | ||
407 | for j=y, h + y do | ||
408 | -- Set the pixel at position i, j to the specified color | ||
409 | img:set(i, j, clr) | ||
410 | end | ||
411 | clr = _clr.inc(clr, 1) | ||
412 | end | ||
413 | end | ||
414 | --rb.sleep(1000) | ||
415 | end -- draw_gradient | ||
416 | |||
417 | function twist(img) | ||
418 | --[[ local fg | ||
419 | if rb.lcd_get_foreground then | ||
420 | fg = rb.lcd_get_foreground() | ||
421 | end]] | ||
422 | |||
423 | local ims = {} | ||
424 | ims.strip = _img.tile(img, img:width(), _lcd.H + img:height()) | ||
425 | ims.y_init = {img:height(), 1} | ||
426 | ims.y_finl = {1, img:height()} | ||
427 | ims.y_inc = {-2, 4} | ||
428 | ims.y_pos = nil | ||
429 | |||
430 | -- together define the width of the overall object | ||
431 | local x_off=(_lcd.W/2) | ||
432 | local m = _lcd.W | ||
433 | local z = -m | ||
434 | local zi = 1 | ||
435 | |||
436 | -- angles of rotation for each leaf | ||
437 | local ANGLES = {0, 45, 90, 135, 180, 225, 270, 315} | ||
438 | local elems = #ANGLES | ||
439 | local _XCOORD = {} | ||
440 | |||
441 | -- color alternates each leaf | ||
442 | local colors = { WHITE, _clr.set(0, 0, 0, 0) } | ||
443 | local c = 0 | ||
444 | |||
445 | -- calculated position of each point in the sine wave(s) | ||
446 | local xs, xe | ||
447 | |||
448 | --[[--Profiling code | ||
449 | local timer = _timer.start()]] | ||
450 | |||
451 | for rot = 0, 6000, 4 do | ||
452 | _lcd:clear(BLACK) | ||
453 | |||
454 | for y=1, _lcd.H do | ||
455 | |||
456 | local function get_sines() | ||
457 | local sc = m + z | ||
458 | if sc == 0 then | ||
459 | sc = 1 -- prevent divide by 0 | ||
460 | elseif sc + z > _lcd.W then | ||
461 | zi = -1 | ||
462 | colors[2] = _clr.inc(colors[2], 1, 0, 50, 0) | ||
463 | elseif sc + z < -(_lcd.W) then | ||
464 | zi = 1 | ||
465 | colors[1] = _clr.inc(colors[1], -1, 0, 10, 0) | ||
466 | end | ||
467 | if colors[2] == colors[1] then | ||
468 | colors[2] = _clr.inc(colors[2], 1) | ||
469 | end | ||
470 | for j = 1, elems do | ||
471 | _XCOORD[j] = _math.d_sin(y + ANGLES[j] + rot) / sc + x_off | ||
472 | end | ||
473 | _XCOORD[0] = _XCOORD[elems] -- loop table for efficient wrap | ||
474 | end | ||
475 | |||
476 | get_sines() | ||
477 | for k = 1, elems do | ||
478 | xs = _XCOORD[k] | ||
479 | xe = _XCOORD[(k+1) % elems] | ||
480 | if xs < 1 or xe > _lcd.W then | ||
481 | while xs < 1 or xe > _lcd.W do | ||
482 | m = m + 1 -- shift m in order to scale object min/max | ||
483 | get_sines() | ||
484 | xs = _XCOORD[k] | ||
485 | xe = _XCOORD[(k+1) % elems] | ||
486 | end | ||
487 | end | ||
488 | c = (c % 2) + 1 | ||
489 | if xs < xe then | ||
490 | -- defines the direction of leaves & fills them | ||
491 | |||
492 | _lcd:set(xs, y, colors[c]) | ||
493 | _lcd:set(xe, y, colors[c]) | ||
494 | _lcd:line(xs + 1, y, xe - 1, y, colors[3 - c], true) | ||
495 | end | ||
496 | end | ||
497 | |||
498 | end | ||
499 | |||
500 | do -- stripes and shifts image strips; blits it into the colors(1) leaves | ||
501 | local y | ||
502 | local y_col = 1 | ||
503 | local w = ims.strip:width() - 1 | ||
504 | local h = ims.strip:height() - 1 | ||
505 | if ims.y_pos ~= nil then | ||
506 | for i = 1, #ims.y_pos do | ||
507 | ims.y_pos[i] = ims.y_pos[i] + ims.y_inc[i] | ||
508 | |||
509 | if (ims.y_inc[i] > 0 and ims.y_pos[i] > ims.y_finl[i]) | ||
510 | or (ims.y_inc[i] < 0 and ims.y_pos[i] < ims.y_finl[i]) then | ||
511 | ims.y_pos[i] = ims.y_init[i] | ||
512 | end | ||
513 | end | ||
514 | else | ||
515 | ims.y_pos = {ims.y_init[1], ims.y_init[2]} | ||
516 | end | ||
517 | |||
518 | for ix = 1, _lcd.W, w do | ||
519 | y_col = y_col + 1 | ||
520 | y = ims.y_pos[(y_col % 2) + 1] | ||
521 | if _lcd.DEPTH > 1 then | ||
522 | _lcd:copy(ims.strip, ix, 1, 1, y, w, h, false, _blit.BDEQC, colors[1]) | ||
523 | else | ||
524 | _lcd:copy(ims.strip, ix, 1, 1, y, w, h, false, _blit.BSAND) | ||
525 | end | ||
526 | end | ||
527 | end | ||
528 | |||
529 | _lcd:update() | ||
530 | z = z + zi | ||
531 | |||
532 | if rb.get_plugin_action(0) == CANCEL_BUTTON then | ||
533 | break | ||
534 | end | ||
535 | collectgarbage("step") | ||
536 | end | ||
537 | --[[--Profiling code | ||
538 | _print.f("%d", _timer.stop(timer)) | ||
539 | rb.sleep(rb.HZ * 10)]] | ||
540 | end -- twist | ||
541 | |||
542 | function draw_target(img) | ||
543 | |||
544 | local clr = _clr.set(0, 0, 0, 0) | ||
545 | |||
546 | -- make a copy of original screen for restoration | ||
547 | local screen_img = _lcd:duplicate() | ||
548 | |||
549 | rad_m = math.min(_lcd.W, _lcd.H) | ||
550 | |||
551 | for s = -_lcd.W /4, 16 do | ||
552 | img:copy(screen_img) | ||
553 | s = math.max(1, math.abs(s)) | ||
554 | for r = 1, rad_m /2 - 10, s do | ||
555 | clr = _clr.inc(clr, 1, r * 5, r * 10, r * 20) | ||
556 | _draw.circle(img, _lcd.CX, _lcd.CY, r, clr) | ||
557 | end | ||
558 | |||
559 | _lcd:update() | ||
560 | if rb.get_plugin_action( 20) == CANCEL_BUTTON then | ||
561 | z = 16; | ||
562 | break; | ||
563 | end | ||
564 | end | ||
565 | |||
566 | end -- draw_target | ||
567 | |||
568 | function draw_sweep(img, cx, cy, radius, color) | ||
569 | local timer = _timer() --creates a new timer saves index | ||
570 | local wait | ||
571 | local x | ||
572 | local y | ||
573 | --make a copy of original screen for restoration | ||
574 | local screen_img = _lcd:duplicate() | ||
575 | _draw.circle(img, cx, cy, radius, color) | ||
576 | for d = 630, 270, - 5 do | ||
577 | if d % 45 == 0 then | ||
578 | img:copy(screen_img) | ||
579 | _draw.circle(img, cx, cy, radius, color) | ||
580 | l = 0 | ||
581 | end | ||
582 | x = cx + radius * _math.d_cos(d) / 10000 | ||
583 | y = cy + radius * _math.d_sin(d) / 10000 | ||
584 | |||
585 | |||
586 | _draw.line(img, cx, cy, x, y, color) | ||
587 | l = l + 1 | ||
588 | if l > 1 then | ||
589 | x1 = cx + (radius - 1) * _math.d_cos(d + 1) / 10000 | ||
590 | y1 = cy + (radius - 1) * _math.d_sin(d + 1) / 10000 | ||
591 | _draw_floodfill(img, x1, y1, BLACK, color) | ||
592 | end | ||
593 | _lcd:update() | ||
594 | wait = timer:check(true) --checks timer and updates last time | ||
595 | if wait >= 50 then | ||
596 | wait = 0 | ||
597 | elseif wait < 50 then | ||
598 | wait = 50 - wait | ||
599 | end | ||
600 | if rb.get_plugin_action( wait) == CANCEL_BUTTON then | ||
601 | break | ||
602 | end | ||
603 | end | ||
604 | timer:stop() --destroys timer, also returns time since last time | ||
605 | screen_img = nil | ||
606 | end -- draw_sweep | ||
607 | |||
608 | function rotate_image(img) | ||
609 | local blitop = _blit.BOR | ||
610 | local i = 1 | ||
611 | local d = 0 | ||
612 | local ximg | ||
613 | local x, y, w, h, xr, yr | ||
614 | |||
615 | ximg = _img.rotate(img, 45) -- image will be largest at this point | ||
616 | w = ximg:width() | ||
617 | h = ximg:height() | ||
618 | xr = (_lcd.W - w) / 2 | ||
619 | yr = (_lcd.H - h) / 2 | ||
620 | --make a copy of original screen for restoration | ||
621 | local screen_img -- = _lcd:duplicate() | ||
622 | screen_img =_img.new(w, h) | ||
623 | screen_img :copy(_LCD, 1, 1, xr, yr, w, h) | ||
624 | --_print.f("CW") | ||
625 | |||
626 | --[[--Profiling code | ||
627 | local timer = _timer.start()]] | ||
628 | |||
629 | while d >= 0 do | ||
630 | ximg = _img.rotate(img, d) | ||
631 | w = ximg:width() | ||
632 | h = ximg:height() | ||
633 | x = (_lcd.W - w) / 2 | ||
634 | y = (_lcd.H - h) / 2 | ||
635 | |||
636 | -- copy our rotated image onto the background | ||
637 | _lcd:copy(ximg, x, y, 1, 1, w, h, false, blitop) | ||
638 | _lcd:update() | ||
639 | --restore the portion of the background we destroyed | ||
640 | _lcd:copy(screen_img, xr, yr, 1, 1) | ||
641 | |||
642 | if d > 0 and d % 360 == 0 then | ||
643 | _lcd:copy(ximg, x, y, 1, 1, w, h) | ||
644 | _lcd:update() | ||
645 | if i == 1 then i = 0 end | ||
646 | if d == 1440 or i < 0 then | ||
647 | if i < 0 then | ||
648 | i = i - 5 | ||
649 | else | ||
650 | i = - 5 | ||
651 | end | ||
652 | blitop = _blit.BXOR | ||
653 | --_print.f("CCW") | ||
654 | --rb.sleep(rb.HZ) | ||
655 | else | ||
656 | i = i + 5 | ||
657 | --_print.f("CW") | ||
658 | --rb.sleep(rb.HZ) | ||
659 | end | ||
660 | |||
661 | end | ||
662 | d = d + i | ||
663 | |||
664 | if rb.get_plugin_action(0) == CANCEL_BUTTON then | ||
665 | break; | ||
666 | end | ||
667 | end | ||
668 | |||
669 | _lcd:copy(ximg, x, y, 1, 1, w, h) | ||
670 | --[[-- Profiling code | ||
671 | _print.f("%d", _timer.stop(timer)) | ||
672 | rb.sleep(rb.HZ * 10)]] | ||
673 | end -- rotate_image | ||
674 | |||
675 | -- shows blitting with a mask | ||
676 | function blit_mask(dst) | ||
677 | local timer = _timer() | ||
678 | local r = math.min(_lcd.CX, _lcd.CY) / 5 | ||
679 | |||
680 | local bmask = _img.new(_lcd.W, _lcd.H) | ||
681 | bmask:clear(0) | ||
682 | |||
683 | _draw.circle_filled(bmask, _lcd.CX, _lcd.CY, r, WHITE) | ||
684 | local color = _clr.set(0, 0, 0 ,0) | ||
685 | for z = 0, 100 do | ||
686 | z = z + timer:check(true) | ||
687 | color = _clr.inc(color, 1, z * 5, z * 10, z * 20) | ||
688 | dst:copy(bmask, 1, 1, 1, 1, nil, nil, false, _blit.BSAND, color) | ||
689 | _lcd:update() | ||
690 | |||
691 | if rb.get_plugin_action(0) == CANCEL_BUTTON then | ||
692 | break | ||
693 | end | ||
694 | end | ||
695 | end -- blit_mask | ||
696 | |||
697 | -- draws an X on the screen | ||
698 | function draw_x() | ||
699 | _draw.line(_LCD, 1, 1, _lcd.W, _lcd.H, WHITE) | ||
700 | _draw.line(_LCD, _lcd.W, 1, 1, _lcd.H, WHITE) | ||
701 | |||
702 | _draw.hline(_LCD, 10, _lcd.CY , _lcd.W - 21, WHITE) | ||
703 | |||
704 | _draw.vline(_LCD, _lcd.CX, 20 , _lcd.H - 41, WHITE) | ||
705 | |||
706 | _draw.rect(_LCD, _lcd.CX - 17, _lcd.CY - 17, 34, 34, WHITE) | ||
707 | _lcd:update() | ||
708 | rb.sleep(100) | ||
709 | end -- draw_x | ||
710 | |||
711 | --fills an image with random colors | ||
712 | function random_img(img) | ||
713 | local min = _clr.set(0, 0, 0, 0) | ||
714 | local max = _clr.set(-1, 255, 255, 255) | ||
715 | math.randomseed(rb.current_tick()) | ||
716 | for x = 1, img:width() do | ||
717 | for y = 1, img:height() do | ||
718 | img:set(x, y, math.random(min, max)) | ||
719 | end | ||
720 | end | ||
721 | end -- random_img | ||
722 | |||
723 | function rainbow_img(img) | ||
724 | --draw a gradient using available colors | ||
725 | if IS_COLOR_TARGET == true then | ||
726 | --R O Y G B I V | ||
727 | clrs = { | ||
728 | {r = 255, g = 255, b = 255}, -- white | ||
729 | {r = 000, g = 000, b = 000}, -- black | ||
730 | {r = 200, g = 000, b = 000}, -- red | ||
731 | {r = 255, g = 000, b = 000}, -- red | ||
732 | {r = 255, g = 100, b = 033}, -- orange | ||
733 | {r = 255, g = 127, b = 000}, -- orange | ||
734 | {r = 255, g = 200, b = 033}, -- yellow | ||
735 | {r = 255, g = 255, b = 000}, -- yellow | ||
736 | {r = 050, g = 255, b = 000}, -- green | ||
737 | {r = 000, g = 125, b = 125}, -- green | ||
738 | {r = 000, g = 000, b = 255}, -- blue | ||
739 | {r = 033, g = 025, b = 200}, -- indigo | ||
740 | {r = 075, g = 000, b = 150}, -- indigo | ||
741 | {r = 127, g = 000, b = 150}, -- violet | ||
742 | {r = 150, g = 000, b = 255}, -- violet | ||
743 | {r = 255, g = 255, b = 255}, -- white | ||
744 | {r = 000, g = 000, b = 000}, -- black | ||
745 | } | ||
746 | else | ||
747 | end | ||
748 | draw_gradient(img, 1, 1, nil, nil, "V", clrs) | ||
749 | end -- rainbow_img | ||
750 | |||
751 | -- draws a rounded rectangle with text | ||
752 | function rock_lua() | ||
753 | local res, w, h = _lcd:text_extent("W", rb.FONT_UI) | ||
754 | w = _lcd.W - 10 | ||
755 | h = h + 4 | ||
756 | _draw.rounded_rect_filled(_LCD, 5, 5, w, h, 15, WHITE) | ||
757 | _draw_text(_LCD, 5, 5, w, h, nil, BLACK, "ROCKlua!") | ||
758 | _lcd:update() | ||
759 | rb.sleep(100) | ||
760 | end -- rock_lua | ||
761 | |||
762 | -- draws a rounded rectangle with long text | ||
763 | function long_text() | ||
764 | local txt = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" | ||
765 | local res, w, h = _lcd:text_extent(txt, rb.FONT_UI) | ||
766 | local resp, wp, hp = _lcd:text_extent(" ", rb.FONT_UI) | ||
767 | local wait = 0 | ||
768 | w = w + wp * 3 | ||
769 | h = h + 4 | ||
770 | local img = _img.new(w + 1, h) | ||
771 | img:clear(BLACK) | ||
772 | _draw.rounded_rect_filled(img, 1, 1, w, h, 15, WHITE) | ||
773 | _draw_text(img, 1, 2, nil, nil, nil, BLACK, txt) | ||
774 | |||
775 | for p = -w + 1, w - 1 do | ||
776 | wait = 0 | ||
777 | p = math.abs(p) | ||
778 | _lcd:copy(img, 1, _lcd.H - h, w - p, 1) | ||
779 | _lcd:update() | ||
780 | if p == 0 or w - p == 1 then wait = 100; rb.sleep(50) end | ||
781 | if rb.get_plugin_action(wait) == CANCEL_BUTTON then | ||
782 | break | ||
783 | end | ||
784 | end | ||
785 | |||
786 | end -- long_text | ||
787 | |||
788 | -- creates or loads an image to use as logo | ||
789 | function get_logo() | ||
790 | local logo_img = _img.load("/pix.bmp") --loads the previously saved image (if we saved it before) | ||
791 | |||
792 | --create a logo if an image wasn't saved previously | ||
793 | if(not logo_img) then | ||
794 | logo_img = create_logo() | ||
795 | end | ||
796 | |||
797 | --fallback | ||
798 | if(not logo_img) then | ||
799 | -- Load a BMP file in the variable backdrop | ||
800 | local base = "/.rockbox/icons/" | ||
801 | local backdrop = _img.load("/image.bmp") --user supplied image | ||
802 | or _img.load(base .. "tango_small_viewers.bmp") | ||
803 | or _img.load(base .. "tango_small_viewers_mono.bmp") | ||
804 | or _img.load(base .. "tango_small_mono.bmp") | ||
805 | or _img.load(base .. "tango_icons.16x16.bmp") | ||
806 | or _img.load(base .. "tango_icons_viewers.16x16.bmp") | ||
807 | or _img.load(base .. "viewers.bmp") | ||
808 | or _img.load(base .. "viewers.6x8x1.bmp") | ||
809 | or _img.load(base .. "viewers.6x8x2.bmp") | ||
810 | or _img.load(base .. "viewers.6x8x10.bmp") | ||
811 | or _img.load(base .. "viewers.6x8x16.bmp") | ||
812 | logo_img = backdrop | ||
813 | end | ||
814 | return logo_img | ||
815 | end -- get_logo | ||
816 | |||
817 | -- uses print_table to display a menu | ||
818 | function main_menu() | ||
819 | |||
820 | local mt = { | ||
821 | [1] = "Rocklua RLI Example", | ||
822 | [2] = "The X", | ||
823 | [3] = "Blit Mask", | ||
824 | [4] = "Target", | ||
825 | [5] = "Sweep", | ||
826 | [6] = "Bouncing Ball (olive)", | ||
827 | [7] = "The Twist", | ||
828 | [8] = "Image Rotation", | ||
829 | [9] = "Long Text", | ||
830 | [10] = "Rainbow Image", | ||
831 | [11] = "Random Image", | ||
832 | [12] = "Clear Screen", | ||
833 | [13] = "Save Screen", | ||
834 | [14] = "Exit" | ||
835 | } | ||
836 | local ft = { | ||
837 | [0] = exit_now, --if user cancels do this function | ||
838 | [1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot | ||
839 | [2] = function(THE_X) draw_x() end, | ||
840 | [3] = function(BLITM) blit_mask(_lcd()) end, | ||
841 | [4] = function(TARGT) draw_target(_lcd()) end, | ||
842 | [5] = function(SWEEP) | ||
843 | local r = math.min(_lcd.CX, _lcd.CY) - 20 | ||
844 | draw_sweep(_lcd(), _lcd.CX, _lcd.CY, r, _clr.set(-1, 0, 255, 0)) | ||
845 | end, | ||
846 | [6] = function(BOUNC) bounce_image(create_ball()) end, | ||
847 | [7] = function(TWIST) twist(get_logo()) end, | ||
848 | [8] = function(ROTAT) rotate_image(get_logo()) end, | ||
849 | [9] = long_text, | ||
850 | [10] = function(RAINB) | ||
851 | rainbow_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ) | ||
852 | end, | ||
853 | [11] = function(RANDM) | ||
854 | random_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ) | ||
855 | end, | ||
856 | [12] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end, | ||
857 | [13] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end, | ||
858 | [14] = function(EXIT_) return true end | ||
859 | } | ||
860 | |||
861 | if _lcd.DEPTH < 2 then | ||
862 | table.remove(mt, 10) | ||
863 | table.remove(ft, 10) | ||
864 | end | ||
865 | |||
866 | print_menu(mt, ft) | ||
867 | |||
868 | end | ||
869 | -------------------------------------------------------------------------------- | ||
870 | function exit_now() | ||
871 | _lcd:update() | ||
872 | _lcd:splashf(rb.HZ * 5, "ran for %d seconds", _timer.stop("main") / rb.HZ) | ||
873 | os.exit() | ||
874 | end -- exit_now | ||
875 | -------------------------------------------------------------------------------- | ||
876 | |||
877 | --MAIN PROGRAM------------------------------------------------------------------ | ||
878 | _timer("main") -- keep track of how long the program ran | ||
879 | |||
880 | -- Clear the screen | ||
881 | _lcd:clear(BLACK) | ||
882 | |||
883 | if _lcd.DEPTH > 1 then | ||
884 | --draw a gradient using available colors | ||
885 | if IS_COLOR_TARGET == true then | ||
886 | clrs = { | ||
887 | {r = 255, g = 000, b = 000}, -- red | ||
888 | {r = 000, g = 255, b = 000}, -- green | ||
889 | {r = 000, g = 000, b = 255}, -- blue | ||
890 | } | ||
891 | else | ||
892 | end | ||
893 | local w = bit.rshift(_lcd.W, 2) | ||
894 | local h = bit.rshift(_lcd.H, 2) | ||
895 | draw_gradient(_lcd(), (_lcd.W - w)/2 - 1, (_lcd.H - h)/3 - 1, w, h, "H", clrs) | ||
896 | _lcd:update() | ||
897 | rb.sleep(rb.HZ) | ||
898 | end | ||
899 | |||
900 | do | ||
901 | local img = _img.load("/rocklua.bmp") | ||
902 | if not img then | ||
903 | rock_lua() | ||
904 | else | ||
905 | _lcd:image(img) | ||
906 | end | ||
907 | end | ||
908 | _lcd:update() | ||
909 | rb.sleep(rb.HZ / 2) | ||
910 | |||
911 | if rb.cpu_boost then rb.cpu_boost(true) end | ||
912 | |||
913 | main_menu() | ||
914 | |||
915 | if rb.cpu_boost then rb.cpu_boost(false) end | ||
916 | |||
917 | exit_now() | ||
918 | |||
919 | -- For a reference list of all functions available, see apps/plugins/lua/rocklib.c (and apps/plugin.h) | ||