A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Rocklua -- Extend / Fix rliImage

Some devices(1-bit / 2-bit displays) have packed bit formats that
need to be unpacked in order to work on them at a pixel level.

This caused a few issues on 1 & 2-bit devices:
Greatly Oversized data arrays for bitmaps
Improper handling of native image data
Framebuffer data was near unusable without jumping through hoops

Conversion between native addressing and per pixel addressing
incurs extra overhead but it is much faster to do it
on the 'C' side rather than in lua.

Not to mention the advantage of a unified interface for the end programer

-------------------------------------------------------------------
Adds a sane way to access each pixel of image data
Adds:
--------------------------------------------------------------------
img:clear([color],[x1],[y1],[x2],[y2])
(set whole image or a portion to a particular value)
--------------------------------------------------------------------
img:invert([x1],[y1],[x2],[y2])
(inverts whole image or a portion)
--------------------------------------------------------------------
img:marshal([x1],[y1],[x2],[y2],[funct])
(calls funct for each point defined by rect of x1,y1 x2,y2
returns value and allows setting value of each point return
nil to terminate early)
--------------------------------------------------------------------
img:points([x1],[y1],[x2],[y2],[dx],[dy])
(returns iterator function that steps delta-x and delta-y pixels each call
returns value of pixel each call but doesn't allow setting to a new value
compare to lua pairs method)
--------------------------------------------------------------------
img:copy(src,[x1],[y1],[x2],[y2],[w],[h],[clip][operation][clr/funct])
(copies all or part of an image -- straight copy or special ops
optionally calls funct for each point defined by rect of
x1, y1, w, h and x2, y2, w, h for dest and src images
returns value of dst and src and allows setting value of
each point return nil to terminate early)
--------------------------------------------------------------------
img:line(x1, y1, x2, y2, color)
--------------------------------------------------------------------
img:ellipse(x1, y1, x2, y2, color, [fillcolor]
--------------------------------------------------------------------
Fixed handling of 2-bit vertical integrated screens

Added direct element access for saving / restoring native image etc.

Added more data to tostring() handler and a way to access individual items

Added equals method to see if two variables reference the same image address
(doesn't check if two separate images contain the same 'picture')

Optimized get and set routines

Fixed out of bound x coord access shifting to next line

Added lua include files to expose new functionality

Finished image saving routine

Static allocation of set_viewport struct faster + saves ram over dynamic

Cleaned up code

Fixed pixel get/set for 1/2 bit devices

Fixed handling for 24-bit devices (32?)

-------------------------------------------------------------------------
Example lua script to follow on forums
-------------------------------------------------------------------------

Change-Id: I8a9ff0ff72aacf4b1662767ccb2b312fc355239c

+3043 -89
+85
apps/plugins/lua/include_lua/blit.lua
··· 1 + --[[ Lua Blit Operations 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ 25 + copy(dst, src, [dx, dy, sx, sy, offset_x, offset_y, clip, _blit.OP, clr/customfunct]) 26 + blit allows you to copy a [portion of a] source image to a dest image applying 27 + a transformation operation to the pixels as they are copied 28 + offsets are auto calculated if left empty or out of range 29 + blit will default to copy if operation is empty or out of range 30 + 31 + it is slightly faster to use the number directly and you don't really 32 + need to define all (any) of these if you don't use them but I put them 33 + here for easier use of the blit function 34 + ]] 35 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 36 + 37 + local _blit ={} do 38 + 39 + _blit.CUSTOM = 0xFF --user defined blit function func(dst_val, x, y, src_val, x, y) 40 + _blit.BCOPY = 0x0 --copy (use :copy() instead it is slightly faster 41 + _blit.BOR = 0x1 --OR source and dest pixels 42 + _blit.BXOR = 0x2 --XOR source and dest pixels 43 + _blit.BNOR = 0x3 --(NOT) (source OR dest pixels) 44 + _blit.BSNOR = 0x4 --(NOT source) OR dest pixels 45 + _blit.BAND = 0x5 --AND source and dest pixels 46 + _blit.BNAND = 0x6 --(NOT) AND source and dest pixels 47 + _blit.BNOT = 0x7 --NOT source and dest pixels 48 + --blit functions for masks 49 + _blit.BSAND = 0x8 --copy color to dest if source pixel <> 0 50 + _blit.BSNOT = 0x9 --copy color to dest if source pixel == 0 51 + --blit functions for masks with colors 52 + _blit.BSORC = 0xA --copy source pixel or color 53 + _blit.BSXORC = 0xB --copy source pixel xor color 54 + _blit.BNSORC = 0xC --copy ~(src_val | clr) 55 + _blit.BSORNC = 0xD --copy src_val | (~clr) 56 + _blit.BSANDC = 0xE --copy src_val & clr; 57 + _blit.BNSANDC = 0xF --copy (~src_val) & clr 58 + _blit.BDORNSORC = 0x10 --copy dst | (~src_val) | clr 59 + _blit.BXORSADXORC = 0x11 --copy dst ^ (src_val & (dst_val ^ clr)) 60 + 61 + _blit.BSNEC = 0x12 --copy source pixel if source <> color 62 + _blit.BSEQC = 0x13 --copy source pixel if source == color 63 + _blit.BSGTC = 0x14 --copy source pixel if source > color 64 + _blit.BSLTC = 0x15 --copy source pixel if source < color 65 + _blit.BDNEC = 0x16 --copy source pixel if dest <> color 66 + _blit.BDEQC = 0x17 --copy source pixel if dest == color 67 + _blit.BDGTC = 0x18 --copy source pixel if dest > color 68 + _blit.BDLTC = 0x19 --copy source pixel if dest < color 69 + _blit.BDNES = 0x1A --copy color to dest if dest <> source pixel 70 + _blit.BDEQS = 0x1B --copy color to dest if dest == source pixel 71 + _blit.BDGTS = 0x1C --copy color to dest if dest > source pixel 72 + _blit.BDLTS = 0x1D --copy color to dest if dest < source pixel 73 + --Source unused for these blits 74 + _blit.BCOPYC = 0x1E --copy color 75 + _blit.BORC = 0x1F --OR dest and color 76 + _blit.BXORC = 0x20 --XOR dest and color 77 + _blit.BNDORC = 0x21 --~(dst_val | clr) 78 + _blit.BDORNC = 0x22 --dst_val | (~clr) 79 + _blit.BANDC = 0x23 --AND dest and color 80 + _blit.BNDANDC = 0x24 --copy (~dst_val) & clr 81 + _blit.BDLTS = 0x25 --dest NOT color 82 + end -- _blit operations 83 + 84 + return _blit 85 +
+117
apps/plugins/lua/include_lua/color.lua
··· 1 + --[[ Lua Color functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _clr.inc 27 + _clr.set 28 + 29 + -- Exposed Constants 30 + IS_COLOR_TARGET 31 + 32 + ]] 33 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 34 + 35 + IS_COLOR_TARGET = false 36 + -- Only true when we're on a color target, i.e. when LCD_RGBPACK is available 37 + if rb.lcd_rgbpack ~= _NIL then 38 + IS_COLOR_TARGET = true 39 + end 40 + 41 + local _clr = {} do 42 + 43 + -- Internal Constants 44 + local _NIL = nil -- _NIL placeholder 45 + 46 + local maxstate = (bit.lshift(1, rb.LCD_DEPTH) - 1) 47 + 48 + if rb.LCD_DEPTH > 24 then -- no alpha channels 49 + maxstate = (bit.lshift(1, 24) - 1) 50 + end 51 + 52 + local function init(v) 53 + return v or 0 54 + end 55 + 56 + -- clamps value to >= min and <= max rolls over to opposite 57 + local function clamp_roll(val, min, max) 58 + if min > max then 59 + local swap = min 60 + min, max = max, swap 61 + end 62 + 63 + if val < min then 64 + val = max 65 + elseif val > max then 66 + val = min 67 + end 68 + 69 + return val 70 + end 71 + 72 + -- sets color -- monochrome / greyscale use 'set' -- color targets 'r,b,g' 73 + -- on monochrome/ greyscale targets: 74 + -- '-1' sets the highest 'color' state & 0 is the minimum 'color' state 75 + local function clrset(set, r, g, b) 76 + local color = set or 0 77 + 78 + if IS_COLOR_TARGET then 79 + if (r ~= _NIL or g ~= _NIL or b ~= _NIL) then 80 + r, g, b = init(r), init(g), init(b) 81 + color = rb.lcd_rgbpack(r, g, b) 82 + end 83 + end 84 + 85 + return clamp_roll(color, 0, maxstate) 86 + end -- clrset 87 + 88 + -- de/increments current color by 'inc' -- optionally color targets by 'r,g,b' 89 + local function clrinc(current, inc, r, g, b) 90 + local color = 0 91 + current = current or color 92 + inc = inc or 1 93 + 94 + if IS_COLOR_TARGET then 95 + local ru, gu, bu = rb.lcd_rgbunpack(current); 96 + if (r ~= _NIL or g ~= _NIL or b ~= _NIL) then 97 + r, g, b = init(r), init(g), init(b) 98 + ru = ru + r; gu = gu + g; bu = bu + b 99 + color = rb.lcd_rgbpack(ru, gu, bu) 100 + else 101 + ru = ru + inc; gu = gu + inc; bu = bu + inc 102 + color = rb.lcd_rgbpack(ru, gu, bu) 103 + end 104 + else 105 + color = current + inc 106 + end 107 + 108 + return clamp_roll(color, 0, maxstate) 109 + end -- clrinc 110 + 111 + -- expose functions to the outside through _clr table 112 + _clr.set = clrset 113 + _clr.inc = clrinc 114 + end -- color functions 115 + 116 + return _clr 117 +
+469
apps/plugins/lua/include_lua/draw.lua
··· 1 + --[[ Lua Drawing functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _draw.circle 27 + _draw.circle_filled 28 + _draw.ellipse 29 + _draw.ellipse_filled 30 + _draw.ellipse_rect_filled 31 + _draw.ellipse_rect 32 + _draw.flood_fill 33 + _draw.hline 34 + _draw.image 35 + _draw.line 36 + _draw.polygon 37 + _draw.polyline 38 + _draw.rect 39 + _draw.rect_filled 40 + _draw.rounded_rect 41 + _draw.rounded_rect_filled 42 + _draw.text 43 + _draw.vline 44 + 45 + ]] 46 + 47 + --[[ bClip allows drawing out of bounds without raising an error it is slower 48 + than having a correctly bounded figure, but can be helpful in some cases.. 49 + ]] 50 + 51 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 52 + 53 + local _draw = {} do 54 + 55 + -- Internal Constants 56 + local _LCD = rb.lcd_framebuffer() 57 + local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT 58 + local BSAND = 8 -- blits color to dst if src <> 0 59 + local _NIL = nil -- nil placeholder 60 + 61 + local function set_viewport(vp) 62 + if not vp then rb.set_viewport() return end 63 + if rb.LCD_DEPTH == 2 then -- invert 2-bit screens 64 + --vp.drawmode = bit.bxor(vp.drawmode, 4) 65 + vp.fg_pattern = 3 - vp.fg_pattern 66 + vp.bg_pattern = 3 - vp.bg_pattern 67 + end 68 + rb.set_viewport(vp) 69 + end 70 + 71 + -- line 72 + local function line(img, x1, y1, x2, y2, color, bClip) 73 + img:line(x1, y1, x2, y2, color, bClip) 74 + end 75 + 76 + -- horizontal line; x, y define start point; length in horizontal direction 77 + local function hline(img, x, y , length, color, bClip) 78 + img:line(x, y, x + length, _NIL, color, bClip) 79 + end 80 + 81 + -- vertical line; x, y define start point; length in vertical direction 82 + local function vline(img, x, y , length, color, bClip) 83 + img:line(x, y, _NIL, y + length, color, bClip) 84 + end 85 + 86 + -- draws a non-filled figure based on points in t-points 87 + local function polyline(img, x, y, t_points, color, bClosed, bClip) 88 + if #t_points < 2 then error("not enough points", 3) end 89 + 90 + local pt_first_last 91 + 92 + if bClosed then 93 + pt_first_last = t_points[1] 94 + else 95 + pt_first_last = t_points[#t_points] 96 + end 97 + 98 + for i = 1, #t_points, 1 do 99 + local pt1 = t_points[i] 100 + 101 + local pt2 = t_points[i + 1] or pt_first_last-- first and last point 102 + 103 + img:line(pt1[1] + x, pt1[2] + y, pt2[1]+x, pt2[2]+y, color, bClip) 104 + end 105 + 106 + end 107 + 108 + -- rectangle 109 + local function rect(img, x, y, width, height, color, bClip) 110 + if width == 0 or height == 0 then return end 111 + 112 + local ppt = {{0, 0}, {width, 0}, {width, height}, {0, height}} 113 + polyline(img, x, y, ppt, color, true, bClip) 114 + --[[ 115 + vline(img, x, y, height, color, bClip); 116 + vline(img, x + width, y, height, color, bClip); 117 + hline(img, x, y, width, color, bClip); 118 + hline(img, x, y + height, width, color, bClip);]] 119 + end 120 + 121 + -- filled rect, fillcolor is color if left empty 122 + local function rect_filled(img, x, y, width, height, color, fillcolor, bClip) 123 + if width == 0 or height == 0 then return end 124 + 125 + if not fillcolor then 126 + img:clear(color, x, y, x + width, y + height, bClip) 127 + else 128 + img:clear(fillcolor, x, y, x + width, y + height, bClip) 129 + rect(img, x, y, width, height, color, bClip) 130 + end 131 + end 132 + 133 + -- circle cx,cy define center point 134 + local function circle(img, cx, cy, radius, color, bClip) 135 + local r = radius 136 + img:ellipse(cx - r, cy - r, cx + r, cy + r, color, _NIL, bClip) 137 + end 138 + 139 + -- filled circle cx,cy define center, fillcolor is color if left empty 140 + local function circle_filled(img, cx, cy, radius, color, fillcolor, bClip) 141 + fillcolor = fillcolor or color 142 + local r = radius 143 + img:ellipse(cx - r, cy - r, cx + r, cy + r, color, fillcolor, bClip) 144 + end 145 + 146 + -- ellipse that fits into defined rect 147 + local function ellipse_rect(img, x1, y1, x2, y2, color, bClip) 148 + img:ellipse(x1, y1, x2, y2, color, _NIL, bClip) 149 + end 150 + 151 + --ellipse that fits into defined rect, fillcolor is color if left empty 152 + local function ellipse_rect_filled(img, x1, y1, x2, y2, color, fillcolor, bClip) 153 + if not fillcolor then fillcolor = color end 154 + 155 + img:ellipse(x1, y1, x2, y2, color, fillcolor, bClip) 156 + end 157 + 158 + -- ellipse cx, cy define center point; a, b the major/minor axis 159 + local function ellipse(img, cx, cy, a, b, color, bClip) 160 + img:ellipse(cx - a, cy - b, cx + a, cy + b, color, _NIL, bClip) 161 + end 162 + 163 + -- filled ellipse cx, cy define center point; a, b the major/minor axis 164 + -- fillcolor is color if left empty 165 + local function ellipse_filled(img, cx, cy, a, b, color, fillcolor, bClip) 166 + if not fillcolor then fillcolor = color end 167 + 168 + img:ellipse(cx - a, cy - b, cx + a, cy + b, color, fillcolor, bClip) 169 + end 170 + 171 + -- rounded rectangle 172 + local function rounded_rect(img, x, y, w, h, radius, color, bClip) 173 + local c_img 174 + 175 + local function blit(dx, dy, sx, sy, ox, oy) 176 + img:copy(c_img, dx, dy, sx, sy, ox, oy, bClip, BSAND, color) 177 + end 178 + 179 + if w == 0 or h == 0 then return end 180 + 181 + -- limit the radius of the circle otherwise it will overtake the rect 182 + radius = math.min(w / 2, radius) 183 + radius = math.min(h / 2, radius) 184 + 185 + local r = radius 186 + 187 + c_img = rb.new_image(r * 2 + 1, r * 2 + 1) 188 + c_img:clear(0) 189 + circle(c_img, r + 1, r + 1, r, 0xFFFFFF) 190 + 191 + -- copy 4 pieces of circle to their respective corners 192 + blit(x, y, _NIL, _NIL, r + 1, r + 1) --TL 193 + blit(x + w - r - 2, y, r, _NIL, r + 1, r + 1) --TR 194 + blit(x , y + h - r - 2, _NIL, r, r + 1, _NIL) --BL 195 + blit(x + w - r - 2, y + h - r - 2, r, r, r + 1, r + 1)--BR 196 + c_img = _NIL 197 + 198 + vline(img, x, y + r, h - r * 2, color, bClip); 199 + vline(img, x + w - 1, y + r, h - r * 2, color, bClip); 200 + hline(img, x + r, y, w - r * 2, color, bClip); 201 + hline(img, x + r, y + h - 1, w - r * 2, color, bClip); 202 + end 203 + 204 + -- rounded rectangle fillcolor is color if left empty 205 + local function rounded_rect_filled(img, x, y, w, h, radius, color, fillcolor, bClip) 206 + local c_img 207 + 208 + local function blit(dx, dy, sx, sy, ox, oy) 209 + img:copy(c_img, dx, dy, sx, sy, ox, oy, bClip, BSAND, fillcolor) 210 + end 211 + 212 + if w == 0 or h == 0 then return end 213 + 214 + if not fillcolor then fillcolor = color end 215 + 216 + -- limit the radius of the circle otherwise it will overtake the rect 217 + radius = math.min(w / 2, radius) 218 + radius = math.min(h / 2, radius) 219 + 220 + local r = radius 221 + 222 + c_img = rb.new_image(r * 2 + 1, r * 2 + 1) 223 + c_img:clear(0) 224 + circle_filled(c_img, r + 1, r + 1, r, fillcolor) 225 + 226 + -- copy 4 pieces of circle to their respective corners 227 + blit(x, y, _NIL, _NIL, r + 1, r + 1) --TL 228 + blit(x + w - r - 2, y, r, _NIL, r + 1, r + 1) --TR 229 + blit(x, y + h - r - 2, _NIL, r, r + 1, _NIL) --BL 230 + blit(x + w - r - 2, y + h - r - 2, r, r, r + 1, r + 1) --BR 231 + c_img = _NIL 232 + 233 + -- finish filling areas circles didn't cover 234 + img:clear(fillcolor, x + r, y, x + w - r, y + h - 1, bClip) 235 + img:clear(fillcolor, x, y + r, x + r, y + h - r, bClip) 236 + img:clear(fillcolor, x + w - r, y + r, x + w - 1, y + h - r - 1, bClip) 237 + 238 + if fillcolor ~= color then 239 + rounded_rect(img, x, y, w, h, r, color, bClip) 240 + end 241 + end 242 + 243 + -- draws an image at xy coord in dest image 244 + local function image(dst, src, x, y, bClip) 245 + if not src then --make sure an image was passed, otherwise bail 246 + rb.splash(rb.HZ, "No Image!") 247 + return _NIL 248 + end 249 + 250 + dst:copy(src, x, y, 1, 1, _NIL, _NIL, bClip) 251 + end 252 + 253 + -- floods an area of targetclr with fillclr x, y specifies the start seed 254 + function flood_fill(img, x, y, targetclr, fillclr) 255 + -- scanline 4-way flood algorithm 256 + -- ^ 257 + -- <--------x---> 258 + -- v 259 + -- check that target color doesn't = fill and the first point is target color 260 + if targetclr == fillclr or targetclr ~= img:get(x,y, true) then return end 261 + local max_w = img:width() 262 + local max_h = img:height() 263 + 264 + local qpt = {} -- FIFO queue 265 + -- rather than moving elements around in our FIFO queue 266 + -- for each read; increment 'qhead' by 2 267 + -- set both elements to nil and let the 268 + -- garbage collector worry about it 269 + -- for each write; increment 'qtail' by 2 270 + -- x coordinates are in odd indices while 271 + -- y coordinates are in even indices 272 + 273 + local qtail = 0 274 + local iter_n; -- North iteration 275 + local iter_s; -- South iteration 276 + 277 + local function check_ns(val, x, y) 278 + if targetclr == val then 279 + if targetclr == iter_n() then 280 + qtail = qtail + 2 281 + qpt[qtail - 1] = x 282 + qpt[qtail] = (y - 1) 283 + end 284 + 285 + if targetclr == iter_s() then 286 + qtail = qtail + 2 287 + qpt[qtail - 1] = x 288 + qpt[qtail] = (y + 1) 289 + end 290 + return fillclr 291 + end 292 + return _NIL -- signal marshal to stop 293 + end 294 + 295 + local function seed_pt(x, y) 296 + -- will never hit max_w * max_h^2 but make sure not to end early 297 + for qhead = 2, max_w * max_h * max_w * max_h, 2 do 298 + 299 + if targetclr == img:get(x, y, true) then 300 + iter_n = img:points(x, y - 1, 1, y - 1) 301 + iter_s = img:points(x, y + 1, 1, y + 1) 302 + img:marshal(x, y, 1, y, _NIL, _NIL, true, check_ns) 303 + 304 + iter_n = img:points(x + 1, y - 1, max_w, y - 1) 305 + iter_s = img:points(x + 1, y + 1, max_w, y + 1) 306 + img:marshal(x + 1, y, max_w, y, _NIL, _NIL, true, check_ns) 307 + end 308 + 309 + x = qpt[qhead - 1] 310 + qpt[qhead - 1] = _NIL 311 + 312 + if not x then break end 313 + 314 + y = qpt[qhead] 315 + qpt[qhead] = _NIL 316 + end 317 + end 318 + 319 + seed_pt(x, y) -- Begin 320 + end -- flood_fill 321 + 322 + -- draws a closed figure based on points in t_points 323 + local function polygon(img, x, y, t_points, color, fillcolor, bClip) 324 + if #t_points < 2 then error("not enough points", 3) end 325 + 326 + if fillcolor then 327 + local x_min, x_max = 0, 0 328 + local y_min, y_max = 0, 0 329 + local w, h = 0, 0 330 + -- find boundries of polygon 331 + for i = 1, #t_points, 1 do 332 + local pt = t_points[i] 333 + if pt[1] < x_min then x_min = pt[1] end 334 + if pt[1] > x_max then x_max = pt[1] end 335 + if pt[2] < y_min then y_min = pt[2] end 336 + if pt[2] > y_max then y_max = pt[2] end 337 + end 338 + w = math.abs(x_max) + math.abs(x_min) 339 + h = math.abs(y_max) + math.abs(y_min) 340 + x_min = x_min - 2 -- leave a border to use flood_fill 341 + y_min = y_min - 2 342 + 343 + local fill_img = rb.new_image(w + 3, h + 3) 344 + fill_img:clear(0xFFFFFF) 345 + 346 + for i = 1, #t_points, 1 do 347 + local pt1 = t_points[i] 348 + local pt2 = t_points[i + 1] or t_points[1]-- first and last point 349 + fill_img:line(pt1[1] - x_min, pt1[2] - y_min, 350 + pt2[1]- x_min, pt2[2] - y_min, 0) 351 + 352 + end 353 + flood_fill(fill_img, fill_img:width(), fill_img:height() , 1, 0) 354 + img:copy(fill_img, x - 1, y - 1, _NIL, _NIL, _NIL, _NIL, bClip, BSAND, fillcolor) 355 + end 356 + 357 + polyline(img, x, y, t_points, color, true, bClip) 358 + end 359 + 360 + -- draw text onto image if width/height are supplied text is centered 361 + local function text(img, x, y, width, height, font, color, text) 362 + font = font or rb.FONT_UI 363 + 364 + local opts = {x = 0, y = 0, width = LCD_W - 1, height = LCD_H - 1, 365 + font = font, drawmode = 3, fg_pattern = 0xFFFFFF, bg_pattern = 0} 366 + set_viewport(opts) 367 + 368 + local res, w, h = rb.font_getstringsize(text, font) 369 + 370 + if not width then 371 + width = 0 372 + else 373 + width = (width - w) / 2 374 + end 375 + 376 + if not height then 377 + height = 0 378 + else 379 + height = (height - h) / 2 380 + end 381 + 382 + -- make a copy of the current screen for later 383 + local screen_img = rb.new_image(LCD_W, LCD_H) 384 + screen_img:copy(_LCD) 385 + 386 + -- check if the screen buffer is supplied image if so set img to the copy 387 + if img == _LCD then 388 + img = screen_img 389 + end 390 + 391 + -- we will be printing the text to the screen then blitting into img 392 + rb.lcd_clear_display() 393 + 394 + local function blit(dx, dy) 395 + img:copy(_LCD, dx, dy, _NIL, _NIL, _NIL, _NIL, false, BSAND, color) 396 + end 397 + 398 + if w > LCD_W then -- text is too long for the screen do it in chunks 399 + local l = 1 400 + local resp, wp, hp 401 + local lenr = text:len() 402 + 403 + while lenr > 1 do 404 + l = lenr 405 + resp, wp, hp = rb.font_getstringsize(text:sub(1, l), font) 406 + 407 + while wp >= LCD_W and l > 1 do 408 + l = l - 1 409 + resp, wp, hp = rb.font_getstringsize(text:sub( 1, l), font) 410 + end 411 + 412 + rb.lcd_putsxy(0, 0, text:sub(1, l)) 413 + text = text:sub(l) 414 + 415 + if x + width > img:width() or y + height > img:height() then 416 + break 417 + end 418 + 419 + -- using the mask we made blit color into img 420 + blit(x + width, y + height) 421 + x = x + wp 422 + rb.lcd_clear_display() 423 + lenr = text:len() 424 + end 425 + else --w <= LCD_W 426 + rb.lcd_putsxy(0, 0, text) 427 + 428 + -- using the mask we made blit color into img 429 + blit(x + width, y + height) 430 + end 431 + 432 + _LCD:copy(screen_img) -- restore screen 433 + set_viewport() -- set viewport default 434 + return res, w, h 435 + end 436 + 437 + -- expose functions to the outside through _draw table 438 + _draw.image = image 439 + _draw.text = text 440 + _draw.line = line 441 + _draw.hline = hline 442 + _draw.vline = vline 443 + _draw.polygon = polygon 444 + _draw.polyline = polyline 445 + _draw.rect = rect 446 + _draw.circle = circle 447 + _draw.ellipse = ellipse 448 + _draw.flood_fill = flood_fill 449 + _draw.ellipse_rect = ellipse_rect 450 + _draw.rounded_rect = rounded_rect 451 + -- filled functions use color as fillcolor if fillcolor is left empty... 452 + _draw.rect_filled = rect_filled 453 + _draw.circle_filled = circle_filled 454 + _draw.ellipse_filled = ellipse_filled 455 + _draw.ellipse_rect_filled = ellipse_rect_filled 456 + _draw.rounded_rect_filled = rounded_rect_filled 457 + 458 + -- adds the above _draw functions into the metatable for RLI_IMAGE 459 + local ex = getmetatable(rb.lcd_framebuffer()) 460 + for k, v in pairs(_draw) do 461 + if ex[k] == _NIL then 462 + ex[k] = v 463 + end 464 + end 465 + 466 + end -- _draw functions 467 + 468 + return _draw 469 +
+392
apps/plugins/lua/include_lua/image.lua
··· 1 + --[[ Lua Image functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _img.save 27 + _img.search 28 + _img.rotate 29 + _img.resize 30 + _img.tile 31 + _img.new 32 + _img.load 33 + 34 + -- Exposed Constants 35 + _img.RLI_INFO_ALL 36 + _img.RLI_INFO_TYPE 37 + _img.RLI_INFO_WIDTH 38 + _img.RLI_INFO_HEIGHT 39 + _img.RLI_INFO_ELEMS 40 + _img.RLI_INFO_BYTES 41 + _img.RLI_INFO_DEPTH 42 + _img.RLI_INFO_FORMAT 43 + _img.RLI_INFO_ADDRESS 44 + 45 + ]] 46 + 47 + --[[Other rbimage Functions: 48 + -------------------------------------------------------------------------------- 49 + img:_len() or #img -- returns number of pixels in image 50 + 51 + img:__tostring([item]) or tostring(img) -- returns data about the image item = 0 52 + is the same as tostring(img) otherwise 53 + item = 1 is the first item in list 54 + item = 7 is the 7th item 55 + item = 8 is the data address in hex 56 + -- See Constants _img.RLI_INFO_.... 57 + 58 + img:_data(element) -- returns/sets raw pixel data 59 + NOTE!! this data is defined by the target and targets with 60 + different color depth, bit packing, etc will not be 61 + compatible with the same image's data on another target 62 + ]] 63 + -------------------------------------------------------------------------------- 64 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 65 + 66 + local _img = {} do 67 + 68 + -- internal constants 69 + local _NIL = nil -- _NIL placeholder 70 + local _math = require("math_ex") -- math functions needed 71 + local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT 72 + 73 + -- returns new image -of- img sized to fit w/h tiling to fit if needed 74 + local function tile(img, w, h) 75 + local hs , ws = img:height(), img:width() 76 + local t_img = rb.new_image(w, h) 77 + 78 + for x = 1, w, ws do t_img:copy(img, x, 1, 1, 1) end 79 + for y = hs, h, hs do t_img:copy(t_img, 1, y, 1, 1, w, hs) end 80 + return t_img 81 + end 82 + 83 + -- resizes src to size of dst 84 + local function resize(dst, src) 85 + -- simple nearest neighbor resize derived from rockbox - pluginlib_bmp.c 86 + -- pretty rough results highly recommend building one more suited.. 87 + local dw, dh = dst:width(), dst:height() 88 + 89 + local xstep = (bit.lshift(src:width(),8) / (dw)) + 1 90 + local ystep = (bit.lshift(src:height(),8) / (dh)) 91 + 92 + local xpos, ypos = 0, 0 93 + local src_x, src_y 94 + 95 + -- walk the dest get src pixel 96 + function rsz_trans(val, x, y) 97 + if x == 1 then 98 + src_y = bit.rshift(ypos,8) + 1 99 + xpos = xstep - bit.rshift(xstep,4) + 1 100 + ypos = ypos + ystep; 101 + end 102 + src_x = bit.rshift(xpos,8) + 1 103 + xpos = xpos + xstep 104 + return (src:get(src_x, src_y, true) or 0) 105 + end 106 + --/* (dst*, [x1, y1, x2, y2, dx, dy, clip, function]) */ 107 + dst:marshal(1, 1, dw, dh, _NIL, _NIL, false, rsz_trans) 108 + end 109 + 110 + -- returns new image -of- img rotated in whole degrees 0 - 360 111 + local function rotate(img, degrees) 112 + -- we do this backwards as if dest was the unrotated object 113 + degrees = 360 - degrees 114 + local c, s = _math.d_cos(degrees), _math.d_sin(degrees) 115 + 116 + -- get the center of the source image 117 + local s_xctr, s_yctr = img:width() / 2, img:height() / 2 118 + 119 + -- get the the new center of the dest image at rotation angle 120 + local d_xctr = ((math.abs(s_xctr * c) + math.abs(s_yctr * s))/ 10000) + 1 121 + local d_yctr = ((math.abs(s_xctr * s) + math.abs(s_yctr * c))/ 10000) + 1 122 + 123 + -- calculate size of rect new image will occupy 124 + local dw, dh = d_xctr * 2 - 1, d_yctr * 2 - 1 125 + 126 + local r_img = rb.new_image(dw, dh) 127 + -- r_img:clear() -- doesn't need cleared as we walk every pixel 128 + 129 + --[[rotation works on origin of 0,0 we need to offset to the center of the 130 + image and then place the upper left back at the origin (0, 0)]] 131 + --[[0,0|-----| ^< |-------| v> 0,0|-------| 132 + | | | 0,0 | | | 133 + |_____| |_______| |_______| ]] 134 + 135 + -- walk the dest get translated src pixel, oversamples src to fill gaps 136 + function rot_trans(val, x, y) 137 + -- move center x/y to the origin 138 + local xtran = x - d_xctr; 139 + local ytran = y - d_yctr; 140 + 141 + -- rotate about the center of the image by x degrees 142 + local yrot = ((xtran * s) + (ytran * c)) / 10000 + s_yctr 143 + local xrot = ((xtran * c) - (ytran * s)) / 10000 + s_xctr 144 + -- upper left of src image back to origin, copy src pixel 145 + return img:get(xrot, yrot, true) or 0 146 + end 147 + r_img:marshal(1, 1, dw, dh, _NIL, _NIL, false, rot_trans) 148 + return r_img 149 + end 150 + 151 + -- saves img to file: name 152 + local function save(img, name) 153 + -- bmp saving derived from rockbox - screendump.c 154 + -- bitdepth is limited by the device 155 + -- eg. device displays greyscale, rgb images are saved greyscale 156 + local file 157 + 158 + local fbuffer = {} -- concat buffer for file writes, reused 159 + 160 + local function dump_fbuffer(thresh) 161 + if #fbuffer >= thresh then 162 + file:write(table.concat(fbuffer)) 163 + for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table 164 + end 165 + end 166 + 167 + local function s_bytesLE(bits, value) 168 + -- bits must be multiples of 8 (sizeof byte) 169 + local byte 170 + local result = "" 171 + for b = 1, bit.rshift(bits, 3) do 172 + if value > 0 then 173 + byte = value % 256 174 + value = (value - byte) / 256 175 + result = result .. string.char(byte) 176 + else 177 + result = result .. string.char(0) 178 + end 179 + end 180 + return result 181 + end 182 + 183 + local function s_bytesBE(bits, value) 184 + -- bits must be multiples of 8 (sizeof byte) 185 + local byte 186 + local result = "" 187 + for b = 1, bit.rshift(bits, 3) do 188 + if value > 0 then 189 + byte = value % 256 190 + value = (value - byte) / 256 191 + result = string.char(byte) .. result 192 + else 193 + result = string.char(0) .. result 194 + end 195 + end 196 + return result 197 + end 198 + 199 + local function c_cmp(color, shift) 200 + -- [RR][GG][BB] 201 + return bit.band(bit.rshift(color, shift), 0xFF) 202 + end 203 + 204 + local cmp = {["r"] = function(c) return c_cmp(c, 16) end, 205 + ["g"] = function(c) return c_cmp(c, 08) end, 206 + ["b"] = function(c) return c_cmp(c, 00) end} 207 + 208 + local function bmp_color(color) 209 + return s_bytesLE(8, cmp.b(color)).. 210 + s_bytesLE(8, cmp.g(color)).. 211 + s_bytesLE(8, cmp.r(color)).. 212 + s_bytesLE(8, 0) .. "" 213 + end -- c_cmp(color, c.r)) 214 + 215 + local function bmp_color_mix(c1, c2, num, den) 216 + -- mixes c1 and c2 as ratio of numerator / denominator 217 + -- used 2x each save results 218 + local bc1, gc1, rc1 = cmp.b(c1), cmp.g(c1), cmp.r(c1) 219 + 220 + return s_bytesLE(8, cmp.b(c2) - bc1 * num / den + bc1).. 221 + s_bytesLE(8, cmp.g(c2) - gc1 * num / den + gc1).. 222 + s_bytesLE(8, cmp.r(c2) - rc1 * num / den + rc1).. 223 + s_bytesLE(8, 0) .. "" 224 + end 225 + 226 + local w, h = img:width(), img:height() 227 + local depth = tonumber(img:__tostring(6)) -- RLI_INFO_DEPTH = 0x6 228 + local format = tonumber(img:__tostring(7)) -- RLI_INFO_FORMAT = 0x7 229 + 230 + local bpp, bypl -- bits per pixel, bytes per line 231 + -- bypl, pad rows to a multiple of 4 bytes 232 + if depth <= 4 then 233 + bpp = 8 -- 256 color image 234 + bypl = (w + 3) 235 + elseif depth <= 16 then 236 + bpp = 16 237 + bypl = (w * 2 + 3) 238 + else 239 + bpp = 24 240 + bypl = (w * 3 + 3) 241 + end 242 + 243 + local linebytes = bit.band(bypl, bit.bnot(3)) 244 + 245 + local bytesperpixel = bit.rshift(bpp, 3) 246 + local headersz = 54 247 + local imgszpad = h * linebytes 248 + 249 + local compression, n_colors = 0, 0 250 + local h_ppm, v_ppm = 0x00000EC4, 0x00000EC4 --Pixels Per Meter ~ 96 dpi 251 + 252 + if depth == 16 then 253 + compression = 3 -- BITFIELDS 254 + n_colors = 3 255 + elseif depth <= 8 then 256 + n_colors = bit.lshift(1, depth) 257 + end 258 + 259 + headersz = headersz + (4 * n_colors) 260 + 261 + file = io.open('/' .. name, "w+") -- overwrite, rb ignores the 'b' flag 262 + 263 + if not file then 264 + rb.splash(rb.HZ, "Error opening /" .. name) 265 + return 266 + end 267 + -- create a bitmap header 'rope' with image details -- concatenated at end 268 + local bmpheader = fbuffer 269 + 270 + bmpheader[01] = "BM" 271 + bmpheader[02] = s_bytesLE(32, headersz + imgszpad) 272 + bmpheader[03] = "\0\0\0\0" -- WORD reserved 1 & 2 273 + bmpheader[04] = s_bytesLE(32, headersz) -- BITMAPCOREHEADER size 274 + bmpheader[05] = s_bytesLE(32, 40) -- BITMAPINFOHEADER size 275 + 276 + bmpheader[06] = s_bytesLE(32, w) 277 + bmpheader[07] = s_bytesLE(32, h) 278 + bmpheader[08] = "\1\0" -- WORD color planes ALWAYS 1 279 + bmpheader[09] = s_bytesLE(16, bpp) -- bits/pixel 280 + bmpheader[10] = s_bytesLE(32, compression) 281 + bmpheader[11] = s_bytesLE(32, imgszpad) 282 + bmpheader[12] = s_bytesLE(32, h_ppm) -- biXPelsPerMeter 283 + bmpheader[13] = s_bytesLE(32, v_ppm) -- biYPelsPerMeter 284 + bmpheader[14] = s_bytesLE(32, n_colors) 285 + bmpheader[15] = s_bytesLE(32, n_colors) 286 + 287 + -- Color Table (#n_colors entries) 288 + if depth == 1 then -- assuming positive display 289 + bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF) 290 + bmpheader[#bmpheader + 1] = bmp_color(0x0) 291 + elseif depth == 2 then 292 + bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF) 293 + bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 1, 3) 294 + bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 2, 3) 295 + bmpheader[#bmpheader + 1] = bmp_color(0x0) 296 + elseif depth == 16 then 297 + -- red bitfield mask 298 + bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000F800) 299 + -- green bitfield mask 300 + bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000007E0) 301 + -- blue bitfield mask 302 + bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F) 303 + end 304 + 305 + dump_fbuffer(0) -- write the header to the file now 306 + 307 + local imgdata = fbuffer 308 + -- pad rows to a multiple of 4 bytes 309 + local bytesleft = linebytes - (bytesperpixel * w) 310 + local t_data = {} 311 + local fs_bytes_E = s_bytesLE -- default save in Little Endian 312 + 313 + if format == 3553 then -- RGB565SWAPPED 314 + fs_bytes_E = s_bytesBE -- Saves in Big Endian 315 + end 316 + 317 + -- Bitmap lines start at bottom unless biHeight is negative 318 + for point in img:points(1, h, w + bytesleft, 1) do 319 + imgdata[#imgdata + 1] = fs_bytes_E(bpp, point or 0) 320 + dump_fbuffer(31) -- buffered write, increase # for performance 321 + end 322 + 323 + dump_fbuffer(0) --write leftovers to file 324 + 325 + file:close() 326 + end -- save(img, name) 327 + 328 + --searches an image for target color 329 + local function search(img, x1, y1, x2, y2, targetclr, variation, stepx, stepy) 330 + 331 + if variation > 128 then variation = 128 end 332 + if variation < -128 then variation = -128 end 333 + 334 + local targeth = targetclr + variation 335 + local targetl = targetclr - variation 336 + 337 + if targeth < targetl then 338 + local swap = targeth 339 + targeth = targetl 340 + targetl = swap 341 + end 342 + 343 + for point, x, y in img:points(x1, y1, x2, y2, stepx, stepy) do 344 + if point >= targetl and point <= targeth then 345 + return point, x, y 346 + end 347 + end 348 + return nil, nil, nil 349 + end 350 + 351 + --[[ we won't be extending these into RLI_IMAGE]] 352 + -- creates a new rbimage size w x h 353 + local function new(w, h) 354 + return rb.new_image(w, h) 355 + end 356 + 357 + -- returns new image -of- file: name (_NIL if error) 358 + local function load(name) 359 + return rb.read_bmp_file("/" .. name) 360 + end 361 + 362 + -- expose tostring constants to outside through _img table 363 + _img.RLI_INFO_ALL = 0x0 364 + _img.RLI_INFO_TYPE = 0x1 365 + _img.RLI_INFO_WIDTH = 0x2 366 + _img.RLI_INFO_HEIGHT = 0x3 367 + _img.RLI_INFO_ELEMS = 0x4 368 + _img.RLI_INFO_BYTES = 0x5 369 + _img.RLI_INFO_DEPTH = 0x6 370 + _img.RLI_INFO_FORMAT = 0x7 371 + _img.RLI_INFO_ADDRESS = 0x8 372 + 373 + -- expose functions to the outside through _img table 374 + _img.save = save 375 + _img.search = search 376 + _img.rotate = rotate 377 + _img.resize = resize 378 + _img.tile = tile 379 + 380 + -- adds the above _img functions into the metatable for RLI_IMAGE 381 + local ex = getmetatable(rb.lcd_framebuffer()) 382 + for k, v in pairs(_img) do 383 + if ex[k] == _NIL then ex[k] = v end 384 + end 385 + -- not exposed through RLI_IMAGE 386 + _img.new = new 387 + _img.load = load 388 + 389 + end -- _img functions 390 + 391 + return _img 392 +
+154
apps/plugins/lua/include_lua/lcd.lua
··· 1 + --[[ Lua LCD Wrapper functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _lcd.clear 27 + _lcd.duplicate 28 + _lcd.image 29 + _lcd.set_viewport 30 + _lcd.splashf 31 + _lcd.text_extent 32 + _lcd.update 33 + _lcd.update_rect 34 + 35 + -- Exposed Constants 36 + _lcd.CX 37 + _lcd.CY 38 + _lcd.DEPTH 39 + _lcd.W 40 + _lcd.H 41 + 42 + _lcd 43 + _LCD 44 + 45 + ]] 46 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 47 + 48 + _LCD = rb.lcd_framebuffer() 49 + 50 + local _lcd = {} do 51 + 52 + --internal constants 53 + local _NIL = nil -- _NIL placeholder 54 + local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT 55 + 56 + -- clamps value to >= min and <= max 57 + local function clamp(val, min, max) 58 + -- Warning doesn't check if min < max 59 + if val < min then 60 + return min 61 + elseif val < max then 62 + return val 63 + end 64 + return max 65 + end 66 + 67 + -- return a copy of lcd screen 68 + local function duplicate(t, screen_img) 69 + screen_img = screen_img or rb.new_image() 70 + screen_img:copy(rb.lcd_framebuffer()) 71 + return screen_img 72 + end 73 + 74 + -- updates screen in specified rectangle 75 + local function update_rect(t, x, y, w, h) 76 + rb.lcd_update_rect(x - 1, y - 1, 77 + clamp(x + w, 1, LCD_W) - 1, 78 + clamp(y + h, 1, LCD_H) - 1) 79 + end 80 + 81 + -- clears lcd, optional.. ([color, x1, y1, x2, y2, clip]) 82 + local function clear(t, clr, ...) 83 + if clr == _NIL and ... == _NIL then 84 + rb.lcd_clear_display() 85 + else 86 + rb.lcd_scroll_stop() --rb really doesn't like bg change while scroll 87 + _LCD:clear(clr, ...) 88 + end 89 + end 90 + 91 + -- loads an image to the screen 92 + local function image(t, src, x, y) 93 + if not src then --make sure an image was passed, otherwise bail 94 + rb.splash(rb.HZ, "No Image!") 95 + return _NIL 96 + end 97 + _LCD:copy(src,x,y,1,1) 98 + end 99 + 100 + -- Formattable version of splash 101 + local function splashf(t, timeout, ...) 102 + rb.splash(timeout, string.format(...)) 103 + end 104 + 105 + -- Gets size of text 106 + local function text_extent(t, msg, font) 107 + font = font or rb.FONT_UI 108 + 109 + return rb.font_getstringsize(msg, font) 110 + end 111 + 112 + -- Sets viewport size 113 + local function set_viewport(t, vp) 114 + if not vp then rb.set_viewport() return end 115 + if rb.LCD_DEPTH == 2 then -- invert 2-bit screens 116 + --vp.drawmode = bit.bxor(vp.drawmode, 4) 117 + vp.fg_pattern = 3 - vp.fg_pattern 118 + vp.bg_pattern = 3 - vp.bg_pattern 119 + end 120 + rb.set_viewport(vp) 121 + end 122 + 123 + -- allows the use of _lcd() as a identifier for the screen 124 + local function index(k, v) 125 + return function(x, ...) 126 + _LCD[v](_LCD, ...) 127 + end 128 + end 129 + 130 + -- allows the use of _lcd() as a identifier for the screen 131 + local function call() 132 + return rb.lcd_framebuffer() 133 + end 134 + 135 + --expose functions to the outside through _lcd table 136 + _lcd.text_extent = text_extent 137 + _lcd.set_viewport = set_viewport 138 + _lcd.duplicate = duplicate 139 + _lcd.update = rb.lcd_update 140 + _lcd.update_rect = update_rect 141 + _lcd.clear = clear 142 + _lcd.splashf = splashf 143 + _lcd.image = image 144 + _lcd.DEPTH = rb.LCD_DEPTH 145 + _lcd.W = rb.LCD_WIDTH 146 + _lcd.H = rb.LCD_HEIGHT 147 + _lcd.CX = (rb.LCD_WIDTH / 2) 148 + _lcd.CY = (rb.LCD_HEIGHT / 2) 149 + _lcd = setmetatable(_lcd,{__index = index, __call = call}) 150 + 151 + end -- _lcd functions 152 + 153 + return _lcd 154 +
+159
apps/plugins/lua/include_lua/math_ex.lua
··· 1 + --[[ Lua Missing Math functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _math.clamp 27 + _math.clamp_roll 28 + _math.d_sin 29 + _math.d_cos 30 + _math.d_tan 31 + _math.i_sqrt 32 + 33 + ]] 34 + 35 + local _math = {} do 36 + 37 + -- internal constants 38 + local _NIL = nil -- _NIL placeholder 39 + 40 + -- clamps value to >= min and <= max 41 + local function clamp(iVal, iMin, iMax) 42 + if iMin > iMax then 43 + local swap = iMin 44 + iMin, iMax = iMax, swap 45 + end 46 + 47 + if iVal < iMin then 48 + return iMin 49 + elseif iVal < iMax then 50 + return iVal 51 + end 52 + 53 + return iMax 54 + end 55 + 56 + -- clamps value to >= min and <= max rolls over to opposite 57 + local function clamp_roll(iVal, iMin, iMax) 58 + if iMin > iMax then 59 + local swap = iMin 60 + iMin, iMax = iMax, swap 61 + end 62 + 63 + if iVal < iMin then 64 + iVal = iMax 65 + elseif iVal > iMax then 66 + iVal = iMin 67 + end 68 + 69 + return iVal 70 + end 71 + 72 + local function i_sqrt(n) 73 + -- Newtons square root approximation 74 + if n < 2 then return n end 75 + local g = n / 2 76 + local l = 1 77 + 78 + for c = 1, 25 do -- if l,g haven't converged after 25 iterations quit 79 + 80 + l = (n / g + g)/ 2 81 + g = (n / l + l)/ 2 82 + 83 + if g == l then return g end 84 + end 85 + 86 + -- check for period-two cycle between g and l 87 + if g - l == 1 then 88 + return l 89 + elseif l - g == 1 then 90 + return g 91 + end 92 + 93 + return _NIL 94 + end 95 + 96 + local function d_sin(iDeg, bExtraPrecision) 97 + --[[ values are returned multiplied by 10000 98 + II | I 180-90 | 90-0 99 + ---(--)--- -------(--)------- 100 + III | IV 180-270 | 270-360 101 + 102 + sine is only positive in quadrants I , II => 0 - 180 degrees 103 + sine 180-360 degrees is a reflection of sine 0-180 104 + Bhaskara I's sine approximation formula isn't overly accurate 105 + but it is close enough for rough image work. 106 + ]] 107 + local sign, x 108 + -- no negative angles -10 degrees = 350 degrees 109 + if iDeg < 0 then 110 + x = 360 + (iDeg % 360) 111 + else --keep rotation in 0-360 range 112 + x = iDeg % 360 113 + end 114 + 115 + -- reflect II & I onto III & IV 116 + if x > 180 then 117 + sign = -1 118 + x = x % 180 119 + else 120 + sign = 1 121 + end 122 + 123 + local x1 = x * (180 - x) 124 + 125 + if bExtraPrecision then -- ~halves the largest errors 126 + if x <= 22 or x >= 158 then 127 + return sign * 39818 * x1 / (40497 - x1) 128 + elseif (x >= 40 and x <= 56) or (x > 124 and x < 140) then 129 + return sign * 40002 * x1 / (40450 - x1) 130 + elseif (x > 31 and x < 71) or (x > 109 and x < 150) then 131 + return sign * 40009 * x1 / (40470 - x1) 132 + end 133 + end 134 + 135 + --multiplied by 10000 so no decimal in results (RB LUA is integer only) 136 + return sign * 40000 * x1 / (40497 - x1) 137 + end 138 + 139 + local function d_cos(iDeg, bExtraPrecision) 140 + --cos is just sine shifed by 90 degrees CCW 141 + return d_sin(90 - iDeg, bExtraPrecision) 142 + end 143 + 144 + local function d_tan(iDeg, bExtraPrecision) 145 + --tan = sin0 / cos0 146 + return (d_sin(iDeg, bExtraPrecision) * 10000 / d_sin(90 - iDeg, bExtraPrecision)) 147 + end 148 + 149 + --expose functions to the outside through _math table 150 + _math.clamp = clamp 151 + _math.clamp_roll = clamp_roll 152 + _math.i_sqrt = i_sqrt 153 + _math.d_sin = d_sin 154 + _math.d_cos = d_cos 155 + _math.d_tan = d_tan 156 + end -- missing math functions 157 + 158 + return _math 159 +
+378
apps/plugins/lua/include_lua/print.lua
··· 1 + --[[ Lua Print functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _print.clear 27 + _print.f 28 + _print.opt 29 + _print.opt.area 30 + _print.opt.autoupdate 31 + _print.opt.color 32 + _print.opt.column 33 + _print.opt.defaults 34 + _print.opt.get 35 + _print.opt.justify 36 + _print.opt.line 37 + _print.opt.overflow 38 + _print.opt.sel_line 39 + _print.opt.set 40 + 41 + ]] 42 + 43 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 44 + 45 + local _print = {} do 46 + 47 + -- internal constants 48 + local _clr = require("color") -- _clr functions required 49 + 50 + local _NIL = nil -- _NIL placeholder 51 + local _LCD = rb.lcd_framebuffer() 52 + local LCD_W, LCD_H = rb.LCD_WIDTH, rb.LCD_HEIGHT 53 + local WHITE = _clr.set(-1, 255, 255, 255) 54 + local BLACK = _clr.set(0, 0, 0, 0) 55 + local DRMODE_SOLID = 3 56 + local col_buf, s_lines = {}, {} 57 + local _p_opts = _NIL 58 + 59 + -- print internal helper functions 60 + -------------------------------------------------------------------------------- 61 + -- clamps value to >= min and <= max 62 + local function clamp(val, min, max) 63 + -- Warning doesn't check if min < max 64 + if val < min then 65 + return min 66 + elseif val < max then 67 + return val 68 + end 69 + return max 70 + end 71 + 72 + -- updates screen in specified rectangle 73 + local function update_rect(x, y, w, h) 74 + rb.lcd_update_rect(x - 1, y - 1, 75 + clamp(x + w, 1, LCD_W) - 1, 76 + clamp(y + h, 1, LCD_H) - 1) 77 + end 78 + 79 + -- Gets size of text 80 + local function text_extent(msg, font) 81 + font = font or rb.FONT_UI 82 + -- res, w, h 83 + return rb.font_getstringsize(msg, font) 84 + end 85 + 86 + -- Sets viewport size 87 + local function set_viewport(vp) 88 + if not vp then rb.set_viewport() return end 89 + 90 + if rb.LCD_DEPTH == 2 then -- invert 2-bit screens 91 + --vp.drawmode = bit.bxor(vp.drawmode, 4) 92 + vp.fg_pattern = 3 - vp.fg_pattern 93 + vp.bg_pattern = 3 - vp.bg_pattern 94 + end 95 + rb.set_viewport(vp) 96 + end 97 + 98 + -- shallow copy of table 99 + function table_clone(t) 100 + local copy = {} 101 + for k, v in pairs(t) do 102 + copy[k] = v 103 + end 104 + return copy 105 + end 106 + 107 + -- Updates a single line on the screen 108 + local function update_line(enabled, opts, line, h) 109 + if enabled ~= true then return end 110 + local o = opts or _p_opts 111 + update_rect(o.x, o.y + line * h + 1, o.width, h) 112 + end 113 + 114 + -- Clears a single line on the screen 115 + local function clear_line(opts, line, h) 116 + local o = opts or _p_opts 117 + _LCD:clear(o.bg_pattern, o.x, o.y + line * h + 1, 118 + o.x + o.width, line * h + h + o.y) 119 + end 120 + 121 + -- Sets the maximum number of lines on the screen 122 + local function max_lines(opts) 123 + local h = opts.height 124 + local _, _, th = text_extent("W", opts.font) 125 + return h / th 126 + end 127 + 128 + --saves the items displayed for side to side scroll 129 + local function col_buf_insert(msg, line, _p_opts) 130 + --if _p_opts.line <= 1 then col_buf = {} end 131 + if not col_buf[line] then 132 + table.insert(col_buf, line, msg) end 133 + end 134 + 135 + --replaces / strips escape characters 136 + local function check_escapes(o, msg) 137 + local tabsz = 2 138 + local tabstr = string.rep(" ", tabsz) 139 + 140 + local function repl(esc) 141 + local ret = "" 142 + if esc:sub(1,1) == "\t" then ret = string.rep(tabstr, esc:len()) end 143 + return ret 144 + end 145 + 146 + msg = msg:gsub("(%c+)", repl) 147 + 148 + local res, w, h = text_extent(msg, o.font) 149 + return w, h, msg 150 + end 151 + -------------------------------------------------------------------------------- 152 + 153 + -- set defaults for print view 154 + local function set_defaults() 155 + _p_opts = { x = 1, 156 + y = 1, 157 + width = LCD_W - 1, 158 + height = LCD_H - 1, 159 + font = rb.FONT_UI, 160 + drawmode = DRMODE_SOLID, 161 + fg_pattern = WHITE, 162 + bg_pattern = BLACK, 163 + sel_pattern = WHITE, 164 + line = 1, 165 + max_line = _NIL, 166 + col = 0, 167 + ovfl = "auto", 168 + justify = "left", 169 + autoupdate = true, 170 + } 171 + _p_opts.max_line = max_lines(_p_opts) 172 + 173 + s_lines, col_buf = {}, {} 174 + return _p_opts 175 + end 176 + 177 + -- returns table with settings for print 178 + -- if bByRef is _NIL or false then a copy is returned 179 + local function get_settings(bByRef) 180 + _p_opts = _p_opts or set_defaults() 181 + if not bByRef then return table_clone(_p_opts) end 182 + return _p_opts 183 + end 184 + 185 + -- sets the settings for print with your passed table 186 + local function set_settings(t_opts) 187 + _p_opts = t_opts or set_defaults() 188 + if t_opts then 189 + _p_opts.max_line = max_lines(_p_opts) 190 + col_buf = {} 191 + end 192 + end 193 + 194 + -- sets colors for print 195 + local function set_color(fgclr, bgclr, selclr) 196 + local o = get_settings(true) 197 + 198 + if fgclr ~= _NIL then 199 + o.fg_pattern, o.sel_pattern = fgclr, fgclr 200 + end 201 + o.sel_pattern = selclr or o.sel_pattern 202 + o.bg_pattern = bgclr or o.bg_pattern 203 + end 204 + 205 + -- helper function sets up colors/marker for selected items 206 + local function show_selected(iLine, msg) 207 + local o = get_settings() -- using a copy of opts so changes revert 208 + 209 + if s_lines[iLine] == true then 210 + if not rb.lcd_set_background then 211 + o.drawmode = bit.bxor(o.drawmode, 4) 212 + else 213 + o.fg_pattern = o.bg_pattern 214 + o.bg_pattern = o.sel_pattern 215 + end 216 + -- alternative selection method 217 + --msg = "> " .. msg 218 + end 219 + set_viewport(o) 220 + o = _NIL 221 + end 222 + 223 + -- sets line explicitly or increments line if line is _NIL 224 + local function set_line(iLine) 225 + local o = get_settings(true) 226 + 227 + o.line = iLine or o.line + 1 228 + 229 + if(o.line < 1 or o.line > o.max_line) then 230 + o.line = 1 231 + end 232 + end 233 + 234 + -- clears the set print area 235 + local function clear() 236 + local o = get_settings(true) 237 + _LCD:clear(o.bg_pattern, o.x, o.y, o.x + o.width, o.y + o.height) 238 + if o.autoupdate == true then rb.lcd_update() end 239 + set_line(1) 240 + for i=1, #col_buf do col_buf[i] = _NIL end 241 + s_lines = {} 242 + collectgarbage("collect") 243 + end 244 + 245 + -- screen update after each call to print.f 246 + local function set_update(bAutoUpdate) 247 + local o = get_settings(true) 248 + o.autoupdate = bAutoUpdate or false 249 + end 250 + 251 + -- sets print area 252 + local function set_area(x, y, w, h) 253 + local o = get_settings(true) 254 + o.x, o.y = clamp(x, 1, LCD_W), clamp(y, 1, LCD_H) 255 + o.width, o.height = clamp(w, 1, LCD_W - o.x), clamp(h, 1, LCD_H - o.y) 256 + o.max_line = max_lines(_p_opts) 257 + 258 + clear() 259 + return o.line, o.max_line 260 + end 261 + 262 + -- when string is longer than print width scroll -- "auto", "manual", "none" 263 + local function set_overflow(str_mode) 264 + -- "auto", "manual", "none" 265 + local str_mode = str_mode or "auto" 266 + local o = get_settings(true) 267 + o.ovfl = str_mode:lower() 268 + col_buf = {} 269 + end 270 + 271 + -- aligns text to: "left", "center", "right" 272 + local function set_justify(str_mode) 273 + -- "left", "center", "right" 274 + local str_mode = str_mode or "left" 275 + local o = get_settings(true) 276 + o.justify = str_mode:lower() 277 + end 278 + 279 + -- selects line 280 + local function select_line(iLine) 281 + s_lines[iLine] = true 282 + end 283 + 284 + -- Internal print function 285 + local function print_internal(t_opts, x, w, h, msg) 286 + 287 + local o = t_opts 288 + if o.justify == "center" then 289 + x = x + (o.width - w) / 2 290 + elseif o.justify == "right" then 291 + x = x + (o.width - w) 292 + end 293 + 294 + local line = o.line - 1 -- rb is 0-based lua is 1-based 295 + if(o.ovfl == "auto" and w >= o.width) then -- -o.x 296 + rb.lcd_puts_scroll(0, line, msg) 297 + else 298 + rb.lcd_putsxy(x, line * h, msg) 299 + if o.ovfl == "manual" then --save msg for later side scroll 300 + col_buf_insert(msg, o.line, o) 301 + end 302 + end 303 + 304 + --only update the line we changed 305 + update_line(o.autoupdate, o, line, h) 306 + 307 + set_line(_NIL) -- increments line counter 308 + end 309 + 310 + -- Helper function that acts mostly like a normal printf() would 311 + local function printf(...) 312 + local o = get_settings(true) 313 + local w, h, msg 314 + local line = o.line - 1 -- rb is 0-based lua is 1-based 315 + if not (...) or (...) == "\n" then -- handles blank line / single '\n' 316 + local res, w, h = text_extent(" ", o.font) 317 + 318 + clear_line(o, line, h) 319 + update_line(o.autoupdate, o, line, h) 320 + 321 + if (...) then set_line(_NIL) end 322 + 323 + return o.line, o.max_line, o.width, h 324 + end 325 + 326 + msg = string.format(...) 327 + 328 + show_selected(o.line, msg) 329 + 330 + w, h, msg = check_escapes(o, msg) 331 + 332 + print_internal(o, o.col, w, h, msg) 333 + 334 + return o.line, o.max_line, w, h 335 + end 336 + 337 + -- x > 0 scrolls right x < 0 scrolls left 338 + local function set_column(x) 339 + local o = get_settings() 340 + if o.ovfl ~= "manual" then return end -- no buffer stored to scroll 341 + local res, w, h, str, line 342 + 343 + for key, value in pairs(col_buf) do 344 + line = key - 1 -- rb is 0-based lua is 1-based 345 + o.line = key 346 + 347 + if value then 348 + show_selected(key, value) 349 + res, w, h = text_extent(value, o.font) 350 + clear_line(o, line, h) 351 + 352 + print_internal(o, x + o.col, w, h, value) 353 + update_line(o.autoupdate, o, line, h) 354 + end 355 + end 356 + o = _NIL 357 + end 358 + 359 + --expose functions to the outside through _print table 360 + _print.opt = {} 361 + _print.opt.column = set_column 362 + _print.opt.color = set_color 363 + _print.opt.area = set_area 364 + _print.opt.set = set_settings 365 + _print.opt.get = get_settings 366 + _print.opt.defaults = set_defaults 367 + _print.opt.overflow = set_overflow 368 + _print.opt.justify = set_justify 369 + _print.opt.sel_line = select_line 370 + _print.opt.line = set_line 371 + _print.opt.autoupdate = set_update 372 + _print.clear = clear 373 + _print.f = printf 374 + 375 + end --_print functions 376 + 377 + return _print 378 +
+115
apps/plugins/lua/include_lua/timer.lua
··· 1 + --[[ Lua Timer functions 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2017 William Wilgus 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + ]] 23 + 24 + --[[ Exposed Functions 25 + 26 + _timer.active 27 + _timer.check 28 + _timer.split 29 + _timer.start 30 + _timer.stop 31 + 32 + ]] 33 + 34 + local _timer = {} do 35 + 36 + --internal constants 37 + local _NIL = nil -- _NIL placeholder 38 + 39 + -- newer versions of lua use table.unpack 40 + local unpack = unpack or table.unpack 41 + 42 + --stores time elapsed at call to split; only vaid for unique timers 43 + local function split(index) 44 + if type(index) ~= "table" then return end 45 + index[#index + 1] = rb.current_tick() - _timer[index] 46 + end 47 + 48 + -- starts a new timer, if index is not specified a unique index is returned 49 + -- numeric or string indices are valid to use directly for permanent timers 50 + -- in this case its up to you to make sure you keep the index unique 51 + local function start(index) 52 + if index == _NIL then 53 + ---if you have _timer.start create timer it returns a unique Id which 54 + -- then has the same methods of _timer :start :stop :check :split 55 + index = setmetatable({}, {__index = _timer}) 56 + end 57 + if _timer[index] == _NIL then 58 + _timer[index] = rb.current_tick() 59 + end 60 + return index 61 + end 62 + 63 + -- returns time elapsed in centiseconds, assigning bCheckonly keeps timer active 64 + local function stop(index, bCheckonly) 65 + 66 + local time_end = rb.current_tick() 67 + index = index or 0 68 + if not _timer[index] then 69 + return 0 70 + else 71 + local time_start = _timer[index] 72 + if not bCheckonly then _timer[index] = _NIL end -- destroy timer 73 + if type(index) ~= "table" then 74 + return time_end - time_start 75 + else 76 + return time_end - time_start, unpack(index) 77 + end 78 + end 79 + end 80 + 81 + -- returns time elapsed in centiseconds, assigning to bUpdate.. updates timer 82 + local function check(index, bUpdate) 83 + local elapsed = stop(index, true) 84 + if bUpdate ~= _NIL and index then 85 + _timer[index] = rb.current_tick() 86 + end 87 + return elapsed 88 + end 89 + 90 + -- returns table of active timers 91 + local function active() 92 + local t_active = {} 93 + local n = 0 94 + for k,v in pairs(_timer) do 95 + if type(_timer[k]) ~= "function" then 96 + n = n + 1 97 + t_active[n]=(k) 98 + end 99 + end 100 + return n, t_active 101 + end 102 + 103 + -- expose functions to the outside through _timer table 104 + _timer.active = active 105 + _timer.check = check 106 + _timer.split = split 107 + _timer.start = start 108 + _timer.stop = stop 109 + 110 + -- allows a call to _timer.start() by just calling _timer() 111 + _timer = setmetatable(_timer,{__call = function(t, i) return start(i) end}) 112 + end -- timer functions 113 + 114 + return _timer 115 +
+8 -1
apps/plugins/lua/lua.make
··· 15 15 16 16 OTHER_SRC += $(LUA_SRC) 17 17 18 + LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua 19 + LUA_INCLUDELIST = blit color draw image lcd math_ex print timer 20 + 18 21 ifndef APP_TYPE 19 22 ifneq (,$(strip $(foreach tgt,RECORDER ONDIO,$(findstring $(tgt),$(TARGET))))) 20 23 ### lowmem targets ··· 30 33 ROCKS += $(LUA_BUILDDIR)/lua.rock 31 34 endif 32 35 33 - $(LUA_BUILDDIR)/lua.rock: $(LUA_OBJ) $(TLSFLIB) $(LUA_BUILDDIR)/actions.lua $(LUA_BUILDDIR)/buttons.lua $(LUA_BUILDDIR)/rocklib_aux.o 36 + $(LUA_BUILDDIR)/lua.rock: $(LUA_OBJ) $(TLSFLIB) $(LUA_BUILDDIR)/actions.lua $(LUA_BUILDDIR)/buttons.lua $(LUA_BUILDDIR)/rocklib_aux.o $(LUA_INCLUDELIST) 34 37 35 38 $(LUA_BUILDDIR)/actions.lua: $(LUA_OBJ) $(LUA_SRCDIR)/action_helper.pl 36 39 $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -E $(APPSDIR)/plugins/lib/pluginlib_actions.h | $(LUA_SRCDIR)/action_helper.pl > $(LUA_BUILDDIR)/actions.lua ··· 46 49 $(LUA_BUILDDIR)/rocklib_aux.o: $(LUA_BUILDDIR)/rocklib_aux.c 47 50 $(call PRINTS,CC $(<F))$(CC) $(INCLUDES) $(PLUGINFLAGS) -I $(LUA_SRCDIR) -c $< -o $@ 48 51 52 + $(LUA_INCLUDELIST): %: $(LUA_INCLUDEDIR)/%.lua 53 + $(call PRINTS,CP $(subst $(LUA_INCLUDEDIR)/,,$<))cp $< $(LUA_BUILDDIR)/$@.lua 54 + 49 55 $(LUA_BUILDDIR)/lua.refmap: $(LUA_OBJ) $(TLSFLIB) 50 56 51 57 $(LUA_OUTLDS): $(PLUGIN_LDS) $(LUA_BUILDDIR)/lua.refmap ··· 58 64 $(filter %.a, $+) \ 59 65 -lgcc $(LUA_OVLFLAGS) 60 66 $(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@) 67 +
+1166 -88
apps/plugins/lua/rocklib.c
··· 9 9 * 10 10 * Copyright (C) 2008 Dan Everton (safetydan) 11 11 * Copyright (C) 2009 Maurus Cuelenaere 12 + * Copyright (C) 2017 William Wilgus 12 13 * 13 14 * This program is free software; you can redistribute it and/or 14 15 * modify it under the terms of the GNU General Public License ··· 47 48 48 49 49 50 /* 50 - * ----------------------------- 51 + * ----------------------------------------------------------------------------- 51 52 * 52 53 * Rockbox Lua image wrapper 53 54 * 54 - * ----------------------------- 55 + * Some devices(1-bit / 2-bit displays) have packed bit formats that 56 + * need to be unpacked in order to work on them at a pixel level. 57 + * 58 + * The internal formats of these devices do not follow the same paradigm 59 + * for image sizes either; We still display the actual width and height to 60 + * the user but store stride based on the native values 61 + * 62 + * Conversion between native addressing and per pixel addressing 63 + * incurs extra overhead but it is much faster to do it 64 + * on the 'C' side rather than in lua. 65 + * 66 + * ----------------------------------------------------------------------------- 55 67 */ 68 + 69 + #ifdef HAVE_LCD_BITMAP 70 + #define RLI_EXTENDED 71 + #endif 56 72 57 73 #define ROCKLUA_IMAGE "rb.image" 74 + #define ERR_IDX_RANGE "index out of range" 75 + #define ERR_DATA_OVF "data overflow" 76 + 77 + /* mark for RLI to LUA Interface functions (luaState *L) is the only argument */ 78 + #define RLI_LUA static int 79 + 80 + #ifndef ABS 81 + #define ABS(a)(((a) < 0) ? - (a) :(a)) 82 + #endif 83 + 84 + #ifndef LCD_BLACK 85 + #define LCD_BLACK 0x0 86 + #endif 87 + 58 88 struct rocklua_image 59 89 { 60 - int width; 61 - int height; 90 + int width; 91 + int height; 92 + int stride; 93 + size_t elems; 62 94 fb_data *data; 63 - fb_data dummy[1][1]; 95 + fb_data dummy[1]; 64 96 }; 65 97 66 - static void rli_wrap(lua_State *L, fb_data *src, int width, int height) 98 + /* holds iterator data for rlimages */ 99 + struct rli_iter_d 67 100 { 68 - struct rocklua_image *a = (struct rocklua_image *)lua_newuserdata(L, sizeof(struct rocklua_image)); 101 + struct rocklua_image *img; 102 + fb_data *elem; 103 + int x , y; 104 + int x1, y1; 105 + int x2, y2; 106 + int dx, dy; 107 + }; 108 + 109 + /* __tostring information enums */ 110 + enum rli_info {RLI_INFO_ALL = 0, RLI_INFO_TYPE, RLI_INFO_WIDTH, 111 + RLI_INFO_HEIGHT, RLI_INFO_ELEMS, RLI_INFO_BYTES, 112 + RLI_INFO_DEPTH, RLI_INFO_FORMAT, RLI_INFO_ADDRESS}; 113 + 114 + #ifdef HAVE_LCD_COLOR 69 115 70 - luaL_getmetatable(L, ROCKLUA_IMAGE); 71 - lua_setmetatable(L, -2); 116 + static inline fb_data invert_color(fb_data rgb) 117 + { 118 + uint8_t r = 0xFFU - FB_UNPACK_RED(rgb); 119 + uint8_t g = 0xFFU - FB_UNPACK_RED(rgb); 120 + uint8_t b = 0xFFU - FB_UNPACK_RED(rgb); 72 121 73 - a->width = width; 74 - a->height = height; 75 - a->data = src; 122 + return FB_RGBPACK(r, g, b); 76 123 } 124 + #else /* !HAVE_LCD_COLOR */ 125 + 126 + #define invert_color(c) (~c) 127 + 128 + #endif /* HAVE_LCD_COLOR */ 129 + 130 + 131 + #if (LCD_DEPTH > 2) /* no native to pixel mapping needed */ 77 132 78 - static fb_data* rli_alloc(lua_State *L, int width, int height) 133 + #define pixel_to_fb(x, y, o, n) {(void) x; (void) y; do { } while (0);} 134 + #define pixel_to_native(x, y, xn, yn) {*xn = x; *yn = y;} 135 + #define init_pixelmask(x, y, m, p) do { } while (0) 136 + 137 + 138 + #else /* some devices need x | y coords shifted to match native format */ 139 + 140 + static fb_data x_shift = FB_SCALARPACK(0); 141 + static fb_data y_shift = FB_SCALARPACK(0); 142 + static fb_data xy_mask = FB_SCALARPACK(0); 143 + static const fb_data *pixelmask = NULL; 144 + 145 + /* conversion between packed native formats and individual pixel addressing */ 146 + static inline void init_pixelmask(fb_data *x_shift, fb_data *y_shift, 147 + fb_data *xy_mask, const fb_data **pixelmask) 79 148 { 80 - size_t nbytes = sizeof(struct rocklua_image) + ((width*height) - 1) * sizeof(fb_data); 81 - struct rocklua_image *a = (struct rocklua_image *)lua_newuserdata(L, nbytes); 149 + 150 + #if(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 1 151 + static const fb_data pixelmask_v1[8] = 152 + {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; 153 + *pixelmask = pixelmask_v1; 154 + 155 + (void) x_shift; 156 + *y_shift = 3U; 157 + *xy_mask = ((1 << (*y_shift)) - 1); 158 + #elif(LCD_PIXELFORMAT == VERTICAL_PACKING) && LCD_DEPTH == 2 159 + static const fb_data pixelmask_v2[4] = {0x03, 0x0C, 0x30, 0xC0}; 160 + *pixelmask = pixelmask_v2; 161 + 162 + (void) x_shift; 163 + *y_shift = 2U; 164 + *xy_mask = ((1 << (*y_shift)) - 1); 165 + #elif(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2 166 + static const fb_data pixelmask_vi2[8] = 167 + {0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080}; 168 + *pixelmask = pixelmask_vi2; 169 + 170 + (void) x_shift; 171 + *y_shift = 3U; 172 + *xy_mask = ((1 << (*y_shift)) - 1); 173 + #elif(LCD_PIXELFORMAT == HORIZONTAL_PACKING) && LCD_DEPTH == 2 174 + /* MSB on left */ 175 + static const fb_data pixelmask_h2[4] = {0x03, 0x0C, 0x30, 0xC0}; 176 + *pixelmask = pixelmask_h2; 177 + 178 + (void) y_shift; 179 + *x_shift = 2U; 180 + *xy_mask = ((1 << (*x_shift)) - 1); 181 + #else 182 + #warning Unknown Pixel Format 183 + #endif /* LCD_PIXELFORMAT */ 184 + 185 + } /* init_pixelmask */ 186 + 187 + static inline void pixel_to_native(int x, int y, int *x_native, int *y_native) 188 + { 189 + *x_native = ((x - 1) >> x_shift) + 1; 190 + *y_native = ((y - 1) >> y_shift) + 1; 191 + } /* pixel_to_native */ 192 + 193 + static inline fb_data set_masked_pixel(fb_data old, 194 + fb_data new, 195 + fb_data mask, 196 + int bit_n) 197 + { 198 + /*equivalent of: (old & (~mask)) | ((new << bit_n) & mask);*/ 199 + return old ^ ((old ^ (new << bit_n)) & mask); 200 + 201 + } /* set_masked_pixel */ 202 + 203 + static inline fb_data get_masked_pixel(fb_data val, fb_data mask, int bit_n) 204 + { 205 + val = val & mask; 206 + return val >> bit_n; 207 + } /* get_masked_pixel */ 208 + 209 + /* conversion between packed native formats and individual pixel addressing */ 210 + static void pixel_to_fb(int x, int y, fb_data *oldv, fb_data *newv) 211 + { 212 + fb_data mask; 213 + int bit_n; 214 + 215 + #if(LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && LCD_DEPTH == 2 216 + (void) x; 217 + const uint16_t greymap_vi2[4] = {0x0000, 0x0001, 0x0100, 0x0101}; 218 + 219 + bit_n = (y - 1) & xy_mask; 220 + mask = pixelmask[bit_n]; 221 + 222 + *newv = greymap_vi2[*newv &(0x3)]; /* [0-3] => greymap */ 223 + *newv = set_masked_pixel(*oldv, *newv, mask, bit_n); 224 + 225 + *oldv = get_masked_pixel(*oldv, mask, bit_n); 226 + 227 + if((*oldv) > 1) /* greymap => [0-3] */ 228 + *oldv = ((*oldv) & 0x1U) + 2U; /* 2, 3 */ 229 + else 230 + *oldv &= 1U; /* 0, 1 */ 231 + 232 + #elif(LCD_DEPTH <= 2) 233 + if(y_shift) 234 + bit_n = (y - 1) & xy_mask; 235 + else if(x_shift) 236 + bit_n = xy_mask - ((x - 1) & xy_mask); /*MSB on left*/ 237 + 238 + if(y_shift || x_shift) 239 + { 240 + mask = pixelmask[bit_n]; 241 + bit_n *= LCD_DEPTH; 242 + 243 + *newv = set_masked_pixel(*oldv, *newv, mask, bit_n); 244 + 245 + *oldv = get_masked_pixel(*oldv, mask, bit_n); 246 + } 247 + #else 248 + #error Unknown Pixel Format 249 + #endif /* LCD_PIXELFORMAT == VERTICAL_INTERLEAVED && LCD_DEPTH == 2 */ 250 + } /* pixel_to_fb */ 251 + 252 + #endif /* (LCD_DEPTH > 2) no native to pixel mapping needed */ 253 + 254 + /* Internal worker functions for image data array *****************************/ 255 + 256 + static inline void swap_int(bool swap, int *v1, int *v2) 257 + { 258 + if(swap) 259 + { 260 + int val = *v1; 261 + *v1 = *v2; 262 + *v2 = val; 263 + } 264 + } /* swap_int */ 265 + 266 + /* Throws error if x or y are out of bounds notifies user which narg indice 267 + the out of bound variable originated */ 268 + static void bounds_check_xy(lua_State *L, struct rocklua_image *img, 269 + int nargx, int x, int nargy, int y) 270 + { 271 + luaL_argcheck(L, x <= img->width && x > 0, nargx, ERR_IDX_RANGE); 272 + luaL_argcheck(L, y <= img->height && y > 0, nargy, ERR_IDX_RANGE); 273 + } /* bounds_check_xy */ 274 + 275 + static struct rocklua_image* rli_checktype(lua_State *L, int arg) 276 + { 277 + void *ud = luaL_checkudata(L, arg, ROCKLUA_IMAGE); 278 + 279 + luaL_argcheck(L, ud != NULL, arg, "'" ROCKLUA_IMAGE "' expected"); 280 + 281 + return (struct rocklua_image*) ud; 282 + } /* rli_checktype */ 283 + 284 + static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data, 285 + int width, int height) 286 + { 287 + /* rliimage is pushed on the stack it is up to you to pop it */ 288 + struct rocklua_image *img; 289 + 290 + const size_t sz_header = sizeof(struct rocklua_image); 291 + size_t sz_data = 0; 292 + size_t n_elems; 293 + 294 + int w_native; 295 + int h_native; 296 + 297 + pixel_to_native(width, height, &w_native, &h_native); 298 + 299 + n_elems = (size_t)(w_native * h_native); 300 + 301 + if(alloc_data) /* if this a new image we need space for image data */ 302 + sz_data = n_elems * sizeof(fb_data); 303 + 304 + /* newuserdata pushes the userdata onto the stack */ 305 + img = (struct rocklua_image *) lua_newuserdata(L, sz_header + sz_data); 82 306 83 307 luaL_getmetatable(L, ROCKLUA_IMAGE); 84 308 lua_setmetatable(L, -2); 85 309 86 - a->width = width; 87 - a->height = height; 88 - a->data = &a->dummy[0][0]; 310 + /* apparent w/h is stored but behind the scenes native w/h is used */ 311 + img->width = width; 312 + img->height = height; 313 + img->stride = w_native; 314 + img->elems = n_elems; 315 + 316 + return img; 317 + } /* alloc_rlimage */ 318 + 319 + static inline void rli_wrap(lua_State *L, fb_data *src, int width, int height) 320 + { 321 + /* rliimage is pushed on the stack it is up to you to pop it */ 322 + struct rocklua_image *a = alloc_rlimage(L, false, width, height); 323 + 324 + a->data = src; 325 + } /* rli_wrap */ 326 + 327 + static inline fb_data* rli_alloc(lua_State *L, int width, int height) 328 + { 329 + /* rliimage is pushed on the stack it is up to you to pop it */ 330 + struct rocklua_image *a = alloc_rlimage(L, true, width, height); 331 + 332 + a->data = &a->dummy[0]; /* ref to beginning of alloc'd img data */ 89 333 90 334 return a->data; 91 - } 335 + } /* rli_alloc */ 336 + 337 + static inline fb_data data_setget(fb_data *elem, int x, int y, fb_data *val) 338 + { 339 + fb_data old_val = FB_SCALARPACK(0); 340 + fb_data new_val; 341 + 342 + if(elem) 343 + { 344 + old_val = *elem; 345 + 346 + if(val) 347 + { 348 + new_val = *val; 349 + pixel_to_fb(x, y, &old_val, &new_val); 350 + *elem = new_val; 351 + } 352 + else 353 + pixel_to_fb(x, y, &old_val, &new_val); 354 + } 355 + 356 + return old_val; 357 + } /* data_setget */ 358 + 359 + static inline fb_data data_set(fb_data *elem, int x, int y, fb_data *new_val) 360 + { 361 + /* get and set share the same underlying function */ 362 + return data_setget(elem, x, y, new_val); 363 + } /* data_set */ 364 + 365 + static inline fb_data data_get(fb_data *elem, int x, int y) 366 + { 367 + /* get and set share the same underlying function */ 368 + return data_setget(elem, x, y, NULL); 369 + } /* data_get */ 370 + 371 + static fb_data* rli_get_element(struct rocklua_image* img, int x, int y) 372 + { 373 + int stride = img->stride; 374 + size_t elements = img->elems; 375 + fb_data *data = img->data; 376 + 377 + pixel_to_native(x, y, &x, &y); 378 + 379 + /* row major address */ 380 + size_t data_address = (stride * (y - 1)) + (x - 1); 381 + 382 + /* x needs bound between 0 and stride otherwise overflow to prev/next y */ 383 + if(x <= 0 || x > stride || data_address >= elements) 384 + return NULL; /* data overflow */ 385 + 386 + return &data[data_address]; /* return element address */ 387 + } /* rli_get_element */ 388 + 389 + /* Lua to C Interface for pixel set and get functions */ 390 + static int rli_setget(lua_State *L, bool is_get) 391 + { 392 + /*(set) (dst*, [x1, y1, clr, clip]) */ 393 + /*(get) (dst*, [x1, y1, clip]) */ 394 + struct rocklua_image *a = rli_checktype(L, 1); 395 + int x = luaL_checkint(L, 2); 396 + int y = luaL_checkint(L, 3); 397 + 398 + fb_data clr = FB_SCALARPACK(0); /* Arg 4 is color if set element */ 399 + fb_data *p_clr = &clr; 400 + 401 + int clip_narg; 402 + 403 + if(is_get) /* get element */ 404 + { 405 + p_clr = NULL; 406 + clip_narg = 4; 407 + } 408 + else /* set element */ 409 + { 410 + clr = FB_SCALARPACK((unsigned) luaL_checknumber(L, 4)); 411 + clip_narg = 5; 412 + } 413 + 414 + fb_data *element = rli_get_element(a, x, y); 415 + 416 + if(!element) 417 + { 418 + if(!luaL_optboolean(L, clip_narg, false)) /* Error if !clip */ 419 + bounds_check_xy(L, a, 2, x, 3, y); 420 + 421 + lua_pushnil(L); 422 + return 1; 423 + } 424 + 425 + lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(data_setget(element, x, y, p_clr))); 426 + 427 + /* returns old value */ 428 + return 1; 429 + } /* rli_setget */ 430 + 431 + #ifdef RLI_EXTENDED 432 + static bool init_rli_iter(struct rli_iter_d *d, 433 + struct rocklua_image *img, 434 + int x1, int y1, 435 + int x2, int y2, 436 + int dx, int dy, 437 + bool swx, bool swy) 438 + { 439 + 440 + swap_int((swx), &x1, &x2); 441 + swap_int((swy), &y1, &y2); 442 + 443 + /* stepping in the correct x direction ? */ 444 + if((dx > 0 && x1 > x2) || (dx < 0 && x1 < x2)) 445 + dx = -dx; 446 + 447 + /* stepping in the correct y direction ? */ 448 + if((dy > 0 && y1 > y2) || (dy < 0 && y1 < y2)) 449 + dy = -dy; 450 + 451 + d->img = img; 452 + d->x = x1; 453 + d->y = y1; 454 + d->x1 = x1; 455 + d->y1 = y1; 456 + d->x2 = x2; 457 + d->y2 = y2; 458 + d->dx = dx; 459 + d->dy = dy; 460 + d->elem = rli_get_element(img, d->x, d->y); 461 + 462 + return(dx != 0 || dy != 0); 463 + } /* init_rli_iter */ 464 + 465 + /* steps to the next point(x, y) by delta x/y, stores pointer to element 466 + returns true if x & y haven't reached x2 & y2 467 + if limit reached - element set to NULL, deltas set to 0 & false returned 468 + */ 469 + static bool next_rli_iter(struct rli_iter_d *d) 470 + { 471 + if((d->dx > 0 && d->x < d->x2) || (d->dx < 0 && d->x > d->x2)) 472 + d->x += d->dx; 473 + else if((d->dy > 0 && d->y < d->y2) || (d->dy < 0 && d->y > d->y2)) 474 + { 475 + d->x = d->x1; /* Reset x*/ 476 + d->y += d->dy; 477 + } 478 + else 479 + { 480 + d->elem = NULL; 481 + d->dx = 0; 482 + d->dy = 0; 483 + return false; 484 + } 485 + 486 + d->elem = rli_get_element(d->img, d->x, d->y); 487 + 488 + return true; 489 + } /* next_rli_iter */ 490 + 491 + static int d_line(struct rocklua_image *img, 492 + int x1, int y1, 493 + int x2, int y2, 494 + fb_data *clr, 495 + bool clip) 496 + { 497 + /* NOTE! clr passed as pointer */ 498 + /* Bresenham midpoint line algorithm */ 499 + fb_data *element; 500 + 501 + int r_a = x2 - x1; /* range of x direction called 'a' for now */ 502 + int r_b = y2 - y1; /* range of y direction called 'b' for now */ 503 + 504 + int s_a = 1; /* step of a direction */ 505 + int s_b = 1; /* step of b direction */ 506 + 507 + int d_err; 508 + int numpixels; 509 + 510 + int *a1 = &x1; /* pointer to the x var 'a' */ 511 + int *b1 = &y1; /* pointer to the y var 'b' */ 512 + 513 + if(r_a < 0) /* instead of negative range we will switch step instead */ 514 + { 515 + r_a = -r_a; 516 + s_a = -s_a; 517 + } 518 + 519 + if(r_b < 0) /* instead of negative range we will switch step instead */ 520 + { 521 + r_b = -r_b; 522 + s_b = -s_b; 523 + } 524 + 525 + x2 += s_a; /* add 1 extra point to make the whole line */ 526 + y2 += s_b; /* add 1 extra point */ 527 + 528 + if(r_b > r_a) /*if rangeY('b') > rangeX('a') swap their identities */ 529 + { 530 + a1 = &y1; 531 + b1 = &x1; 532 + swap_int((true), &r_a, &r_b); 533 + swap_int((true), &s_a, &s_b); 534 + } 535 + 536 + d_err = ((r_b << 1) - r_a) >> 1; /* preload err of 1 step (px centered) */ 537 + 538 + numpixels = r_a + 1; 539 + 540 + r_a -= r_b; /* pre-subtract 'a' - 'b' */ 541 + 542 + for(;numpixels > 0; numpixels--) 543 + { 544 + element = rli_get_element(img, x1, y1); 545 + if(element || clip) 546 + data_set(element, x1, y1, clr); 547 + else 548 + return numpixels + 1; /* Error */ 549 + 550 + if(d_err >= 0) /* 0 is our target midpoint(exact point on the line) */ 551 + { 552 + *b1 += s_b; /* whichever axis is in 'b' stepped(-1 or +1) */ 553 + d_err -= r_a; 554 + } 555 + else 556 + d_err += r_b; /* only add 'b' when d_err < 0 */ 557 + 558 + *a1 += s_a; /* whichever axis is in 'a' stepped(-1 or +1) */ 559 + } 560 + 561 + return 0; 562 + } /* d_line */ 92 563 93 - static int rli_new(lua_State *L) 564 + /* ellipse worker function */ 565 + static int d_ellipse_elements(struct rocklua_image * img, 566 + int x1, int y1, 567 + int x2, int y2, 568 + int sx, int sy, 569 + fb_data *clr, 570 + fb_data *fillclr, 571 + bool clip) 94 572 { 95 - int width = luaL_checkint(L, 1); 96 - int height = luaL_checkint(L, 2); 573 + int ret = 0; 574 + fb_data *element1, *element2, *element3, *element4; 575 + 576 + if(fillclr && x1 - sx != x2 + sx) 577 + { 578 + ret |= d_line(img, x1, y1, x2, y1, fillclr, clip); /* I. II.*/ 579 + ret |= d_line(img, x1, y2, x2, y2, fillclr, clip); /* III.IV.*/ 580 + } 581 + 582 + x1 -= sx; /* shift x & y */ 583 + y1 -= sy; 584 + x2 += sx; 585 + y2 += sy; 586 + 587 + element1 = rli_get_element(img, x2, y1); 588 + element2 = rli_get_element(img, x1, y1); 589 + element3 = rli_get_element(img, x1, y2); 590 + element4 = rli_get_element(img, x2, y2); 591 + 592 + if(clip || (element1 && element2 && element3 && element4)) 593 + { 594 + data_set(element1, x2, y1, clr); /* I. Quadrant +x +y */ 595 + data_set(element2, x1, y1, clr); /* II. Quadrant -x +y */ 596 + data_set(element3, x1, y2, clr); /* III. Quadrant -x -y */ 597 + data_set(element4, x2, y2, clr); /* IV. Quadrant +x -y */ 598 + } 599 + else 600 + ret = 2; /* ERROR */ 601 + 602 + return ret; 603 + } /* d_ellipse_elements */ 604 + 605 + static int d_ellipse(struct rocklua_image *img, 606 + int x1, int y1, 607 + int x2, int y2, 608 + fb_data *clr, 609 + fb_data *fillclr, 610 + bool clip) 611 + { 612 + /* NOTE! clr and fillclr passed as pointers */ 613 + /* Rasterizing algorithm derivative of work by Alois Zing */ 614 + #if LCD_WIDTH > 1024 || LCD_HEIGHT > 1024 615 + /* Prevents overflow on large screens */ 616 + double dx, dy, err, e2; 617 + #else 618 + long dx, dy, err, e2; 619 + #endif 620 + 621 + int ret = 0; 622 + 623 + int a = ABS(x2 - x1); /* diameter */ 624 + int b = ABS(y2 - y1); /* diameter */ 625 + 626 + if(a == 0 || b == 0 || !clr) 627 + return ret; /* not an error but nothing to display */ 628 + 629 + int b1 = (b & 1); 630 + b = b - (1 - b1); 631 + 632 + int a2 = (a * a); 633 + int b2 = (b * b); 634 + 635 + dx = ((1 - a) * b2) >> 1; /* error increment */ 636 + dy = (b1 * a2) >> 1; /* error increment */ 637 + 638 + err = dx + dy + b1 * a2; /* error of 1.step */ 639 + 640 + /* if called with swapped points .. exchange them */ 641 + swap_int((x1 > x2), &x1, &x2); 642 + swap_int((y1 > y2), &y1, &y2); 643 + 644 + y1 += (b + 1) >> 1; 645 + y2 = y1 - b1; 646 + 647 + do 648 + { 649 + ret = d_ellipse_elements(img, x1, y1, x2, y2, 0, 0, clr, fillclr, clip); 650 + 651 + e2 = err; 652 + 653 + /* using division because you can't use bit shift on doubles.. */ 654 + if(e2 <= (dy / 2)) /* target midpoint - y step */ 655 + { 656 + y1++; 657 + y2--; 658 + dy += a2; 659 + err += dy; 660 + } 661 + 662 + if(e2 >= (dx / 2) || err > (dy / 2)) /* target midpoint - x step */ 663 + { 664 + x1++; 665 + x2--; 666 + dx += b2; 667 + err += dx; 668 + } 669 + 670 + } while(ret == 0 && x1 <= x2); 671 + 672 + while (ret == 0 && y1 - y2 < b) /* early stop of flat ellipse a=1 finish tip */ 673 + { 674 + ret = d_ellipse_elements(img, x1, y1, x2, y2, 1, 0, clr, fillclr, clip); 675 + 676 + y1++; 677 + y2--; 678 + } 679 + 680 + return ret; 681 + } /* d_ellipse */ 682 + 683 + /* Lua to C Interface for line and ellipse */ 684 + static int rli_line_ellipse(lua_State *L, bool is_ellipse) 685 + { 686 + struct rocklua_image *a = rli_checktype(L, 1); 687 + 688 + int x1 = luaL_checkint(L, 2); 689 + int y1 = luaL_checkint(L, 3); 690 + int x2 = luaL_optint(L, 4, x1); 691 + int y2 = luaL_optint(L, 5, y1); 692 + 693 + fb_data clr = FB_SCALARPACK((unsigned) luaL_checknumber(L, 6)); 694 + 695 + fb_data fillclr; /* fill color is index 7 if is_ellipse */ 696 + fb_data *p_fillclr = NULL; 697 + 698 + bool clip; 699 + int clip_narg; 700 + 701 + if(is_ellipse) 702 + clip_narg = 8; 703 + else 704 + clip_narg = 7; 705 + 706 + clip = luaL_optboolean(L, clip_narg, false); 707 + 708 + if(!clip) 709 + { 710 + bounds_check_xy(L, a, 2, x1, 3, y1); 711 + bounds_check_xy(L, a, 4, x2, 5, y2); 712 + } 713 + 714 + if(is_ellipse) 715 + { 716 + if(!lua_isnoneornil(L, 7)) 717 + { 718 + fillclr = FB_SCALARPACK((unsigned) luaL_checkint(L, 7)); 719 + p_fillclr = &fillclr; 720 + } 721 + 722 + luaL_argcheck(L, d_ellipse(a, x1, y1, x2, y2, &clr, p_fillclr, clip) == 0, 723 + 1, ERR_DATA_OVF); 724 + } 725 + else 726 + luaL_argcheck(L, d_line(a, x1, y1, x2, y2, &clr, clip) == 0, 727 + 1, ERR_DATA_OVF); 728 + 729 + return 0; 730 + } /* rli_line_ellipse */ 731 + 732 + /* Pushes lua function from Stack at position narg to top of stack 733 + and puts a reference in the global registry for later use */ 734 + static inline int register_luafunc(lua_State *L, int narg_funct) 735 + { 736 + lua_pushvalue(L, narg_funct); /* lua function */ 737 + int lf_ref = luaL_ref(L, LUA_REGISTRYINDEX); 738 + lua_settop(L, 0); /* clear C stack for use by lua function */ 739 + return lf_ref; 740 + } /* register_luafunc */ 741 + 742 + /* User defined pixel manipulations through rli_copy, rli_marshal */ 743 + static int custom_transform(lua_State *L, 744 + struct rli_iter_d *ds, 745 + struct rli_iter_d *ss, 746 + int op, 747 + fb_data *color) 748 + { 749 + (void) color; 750 + 751 + fb_data dst; 752 + fb_data src; 753 + 754 + unsigned int params = 3; 755 + int ret = 0; 756 + 757 + lua_rawgeti(L, LUA_REGISTRYINDEX, op); 758 + 759 + dst = data_get(ds->elem, ds->x, ds->y); 760 + lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(dst)); 761 + lua_pushnumber(L, ds->x); 762 + lua_pushnumber(L, ds->y); 763 + 764 + if(ss) /* Allows src to be omitted */ 765 + { 766 + params += 3; 767 + src = data_get(ss->elem, ss->x, ss->y); 768 + lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(src)); 769 + lua_pushnumber(L, ss->x); 770 + lua_pushnumber(L, ss->y); 771 + } 772 + 773 + lua_call(L, params, 2); /* call custom function w/ n-params & 2 ret */ 774 + 775 + if(!lua_isnoneornil(L, -2)) 776 + { 777 + ret = 1; 778 + dst = FB_SCALARPACK((unsigned) luaL_checknumber(L, -2)); 779 + data_set(ds->elem, ds->x, ds->y, &dst); 780 + } 781 + 782 + if(!lua_isnoneornil(L, -1) && ss) 783 + { 784 + ret |= 2; 785 + src = FB_SCALARPACK((unsigned) luaL_checknumber(L, -1)); 786 + data_set(ss->elem, ss->x, ss->y, &src); 787 + } 788 + 789 + lua_pop(L, 2); 790 + return ret; /* 0 signals iterator to stop */ 791 + } /* custom_transform */ 792 + 793 + /* Pre defined pixel manipulations through rli_copy */ 794 + static int blit_transform(lua_State *L, 795 + struct rli_iter_d *ds, 796 + struct rli_iter_d *ss, 797 + int op, 798 + fb_data *color) 799 + { 800 + (void) L; 801 + unsigned clr = FB_UNPACK_SCALAR_LCD(*color); 802 + unsigned dst = FB_UNPACK_SCALAR_LCD(data_get(ds->elem, ds->x, ds->y)); 803 + unsigned src; 804 + 805 + /* Reuse 0 - 7 for src / clr blits*/ 806 + if(op >= 30 && op <= 37) 807 + { 808 + op -= 30; 809 + src = clr; 810 + } 811 + else 812 + src = FB_UNPACK_SCALAR_LCD(data_get(ss->elem, ss->x, ss->y)); 813 + 814 + switch(op) 815 + { 816 + default: 817 + /* case 30: */ 818 + case 0: { dst = src; break; }/* copyS/C */ 819 + /* case 31: */ 820 + case 1: { dst = src | dst; break; }/* DorS/C */ 821 + /* case 32: */ 822 + case 2: { dst = src ^ dst; break; }/* DxorS/C */ 823 + /* case 33: */ 824 + case 3: { dst = ~(src | dst); break; }/* nDorS/C */ 825 + /* case 34: */ 826 + case 4: { dst = (~src) | dst; break; }/* DornS/C */ 827 + /* case 35: */ 828 + case 5: { dst = src & dst; break; }/* DandS/C */ 829 + /* case 36: */ 830 + case 6: { dst = src & (~dst); break; }/* nDandS/C */ 831 + /* case 37: */ 832 + case 7: { dst = ~src; break; }/* notS/C */ 833 + 834 + /* mask blits */ 835 + case 8: { if(src != 0) { dst = clr; } break; }/* Sand */ 836 + case 9: { if(src == 0) { dst = clr; } break; }/* Snot */ 837 + 838 + case 10: { dst = src | clr; break; }/* SorC */ 839 + case 11: { dst = src ^ clr; break; }/* SxorC */ 840 + case 12: { dst = ~(src | clr); break; }/* nSorC */ 841 + case 13: { dst = src | (~clr); break; }/* SornC */ 842 + case 14: { dst = src & clr; break; }/* SandC */ 843 + case 15: { dst = (~src) & clr; break; }/* nSandC */ 844 + case 16: { dst |= (~src) | clr; break; }/* DornSorC */ 845 + case 17: { dst ^= (src & (dst ^ clr)); break; }/* DxorSandDxorC */ 846 + 847 + case 18: { if(src != clr) { dst = src; } break; } 848 + case 19: { if(src == clr) { dst = src; } break; } 849 + case 20: { if(src > clr) { dst = src; } break; } 850 + case 21: { if(src < clr) { dst = src; } break; } 851 + 852 + case 22: { if(dst != clr) { dst = src; } break; } 853 + case 23: { if(dst == clr) { dst = src; } break; } 854 + case 24: { if(dst > clr) { dst = src; } break; } 855 + case 25: { if(dst < clr) { dst = src; } break; } 856 + 857 + case 26: { if(dst != src) { dst = clr; } break; } 858 + case 27: { if(dst == src) { dst = clr; } break; } 859 + case 28: { if(dst > src) { dst = clr; } break; } 860 + case 29: { if(dst < src) { dst = clr; } break; } 861 + 862 + }/*switch op*/ 863 + fb_data data = FB_SCALARPACK(dst); 864 + data_set(ds->elem, ds->x, ds->y, &data); 865 + return 1; 866 + } /* blit_transform */ 867 + 868 + static int invert_transform(lua_State *L, 869 + struct rli_iter_d *ds, 870 + struct rli_iter_d *ss, 871 + int op, 872 + fb_data *color) 873 + { 874 + (void) L; 875 + (void) color; 876 + (void) op; 877 + (void) ss; 878 + 879 + fb_data val = invert_color(data_get(ds->elem, ds->x, ds->y)); 880 + data_set(ds->elem, ds->x, ds->y, &val); 881 + 882 + return 1; 883 + } /* invert_transform */ 884 + #endif /* RLI_EXTENDED */ 885 + 886 + /* RLI to LUA Interface functions *********************************************/ 887 + RLI_LUA rli_new(lua_State *L) 888 + { /* [width, height] */ 889 + int width = luaL_optint(L, 1, LCD_WIDTH); 890 + int height = luaL_optint(L, 2, LCD_HEIGHT); 891 + 892 + luaL_argcheck(L, width > 0, 1, ERR_IDX_RANGE); 893 + luaL_argcheck(L, height > 0, 2, ERR_IDX_RANGE); 97 894 98 895 rli_alloc(L, width, height); 99 896 100 897 return 1; 101 898 } 102 899 103 - static struct rocklua_image* rli_checktype(lua_State *L, int arg) 900 + RLI_LUA rli_set(lua_State *L) 901 + { 902 + /*(set) (dst*, [x1, y1, clr, clip]) */ 903 + /* get and set share the same underlying function */ 904 + return rli_setget(L, false); 905 + } 906 + 907 + RLI_LUA rli_get(lua_State *L) 908 + { 909 + /*(get) (dst*, [x1, y1, clip]) */ 910 + /* get and set share the same underlying function */ 911 + return rli_setget(L, true); 912 + } 913 + 914 + RLI_LUA rli_height(lua_State *L) 104 915 { 105 - void *ud = luaL_checkudata(L, arg, ROCKLUA_IMAGE); 106 - luaL_argcheck(L, ud != NULL, arg, "'" ROCKLUA_IMAGE "' expected"); 107 - return (struct rocklua_image*) ud; 916 + struct rocklua_image *a = rli_checktype(L, 1); 917 + lua_pushnumber(L, a->height); 918 + return 1; 108 919 } 109 920 110 - static int rli_width(lua_State *L) 921 + RLI_LUA rli_width(lua_State *L) 111 922 { 112 923 struct rocklua_image *a = rli_checktype(L, 1); 113 924 lua_pushnumber(L, a->width); 114 925 return 1; 115 926 } 116 927 117 - static int rli_height(lua_State *L) 928 + RLI_LUA rli_equal(lua_State *L) 118 929 { 119 930 struct rocklua_image *a = rli_checktype(L, 1); 120 - lua_pushnumber(L, a->height); 931 + struct rocklua_image *b = rli_checktype(L, 2); 932 + lua_pushboolean(L, a->data == b->data); 933 + return 1; 934 + } 935 + 936 + RLI_LUA rli_size(lua_State *L) 937 + { 938 + struct rocklua_image *a = rli_checktype(L, 1); 939 + lua_pushnumber(L, a->elems); 940 + return 1; 941 + } 942 + 943 + RLI_LUA rli_raw(lua_State *L) 944 + { 945 + /*val = (img*, index, [new_val]) */ 946 + struct rocklua_image *a = rli_checktype(L, 1); 947 + 948 + size_t i = (unsigned) luaL_checkint(L, 2); 949 + 950 + fb_data val; 951 + 952 + luaL_argcheck(L, i > 0 && i <= (a->elems), 2, ERR_IDX_RANGE); 953 + 954 + lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(a->data[i-1])); 955 + 956 + if(!lua_isnoneornil(L, 3)) 957 + { 958 + val = FB_SCALARPACK((unsigned) luaL_checknumber(L, 3)); 959 + a->data[i-1] = val; 960 + } 961 + 121 962 return 1; 122 963 } 123 964 124 - static fb_data* rli_element(lua_State *L) 965 + RLI_LUA rli_tostring(lua_State *L) 125 966 { 967 + /* (img, [infoitem]) */ 126 968 struct rocklua_image *a = rli_checktype(L, 1); 127 - int x = luaL_checkint(L, 2); 128 - int y = luaL_checkint(L, 3); 969 + 970 + int item = (unsigned) luaL_optint(L, 2, 0); 971 + size_t bytes = a->elems * sizeof(fb_data); 972 + 973 + switch(item) 974 + { 975 + default: 976 + case RLI_INFO_ALL: 977 + { 978 + lua_pushfstring(L, 979 + ROCKLUA_IMAGE ": %dx%d, %d elements, %d bytes, %d-bit depth, %d pack", 980 + a->width, a->height, a->elems, bytes, LCD_DEPTH, LCD_PIXELFORMAT); 981 + break; 982 + } 983 + case RLI_INFO_TYPE: { lua_pushfstring(L, ROCKLUA_IMAGE ); break; } 984 + case RLI_INFO_WIDTH: { lua_pushfstring(L, "%d", a->width ); break; } 985 + case RLI_INFO_HEIGHT: { lua_pushfstring(L, "%d", a->height ); break; } 986 + case RLI_INFO_ELEMS: { lua_pushfstring(L, "%d", a->elems ); break; } 987 + case RLI_INFO_BYTES: { lua_pushfstring(L, "%d", bytes ); break; } 988 + case RLI_INFO_DEPTH: { lua_pushfstring(L, "%d", LCD_DEPTH ); break; } 989 + case RLI_INFO_FORMAT: { lua_pushfstring(L, "%d", LCD_PIXELFORMAT); break; } 990 + case RLI_INFO_ADDRESS: { lua_pushfstring(L, "%p", a->data); break; } 991 + } 992 + 993 + return 1; 994 + } 995 + 996 + #ifdef RLI_EXTENDED 997 + RLI_LUA rli_ellipse(lua_State *L) 998 + { 999 + /* (dst*, x1, y1, x2, y2, [clr, fillclr, clip]) */ 1000 + /* line and ellipse share the same init function */ 1001 + return rli_line_ellipse(L, true); 1002 + } 1003 + 1004 + RLI_LUA rli_line(lua_State *L) 1005 + { 1006 + /* (dst*, x1, y1, [x2, y2, clr, clip]) */ 1007 + /* line and ellipse share the same init function */ 1008 + return rli_line_ellipse(L, false); 1009 + } 129 1010 130 - luaL_argcheck(L, 1 <= x && x <= a->width, 2, 131 - "index out of range"); 132 - luaL_argcheck(L, 1 <= y && y <= a->height, 3, 133 - "index out of range"); 1011 + RLI_LUA rli_iterator(lua_State *L) { 1012 + /* see rli_iterator_factory */ 1013 + struct rli_iter_d *ds; 1014 + ds = (struct rli_iter_d *) lua_touserdata(L, lua_upvalueindex(1)); 1015 + 1016 + if(ds->dx != 0 || ds->dy != 0) 1017 + { 1018 + lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(data_get(ds->elem, ds->x, ds->y))); 1019 + 1020 + lua_pushinteger(L, ds->x); 1021 + lua_pushinteger(L, ds->y); 1022 + 1023 + next_rli_iter(ds); /* load next element */ 134 1024 135 - /* return element address */ 136 - return &a->data[a->width * (y - 1) + (x - 1)]; 1025 + return 3; 1026 + } 1027 + return 0; /* nothing left to do */ 137 1028 } 138 1029 139 - static int rli_set(lua_State *L) 1030 + RLI_LUA rli_iterator_factory(lua_State *L) { 1031 + /* (src*, [x1, y1, x2, y2, dx, dy]) */ 1032 + struct rocklua_image *a = rli_checktype(L, 1); /*image we wish to iterate*/ 1033 + 1034 + struct rli_iter_d *ds; 1035 + 1036 + int x1 = luaL_optint(L, 2, 1); 1037 + int y1 = luaL_optint(L, 3, 1); 1038 + int x2 = luaL_optint(L, 4, a->width - x1 + 1); 1039 + int y2 = luaL_optint(L, 5, a->height - y1 + 1); 1040 + int dx = luaL_optint(L, 6, 1); 1041 + int dy = luaL_optint(L, 7, 1); 1042 + 1043 + /* create new iter + pushed onto stack */ 1044 + ds = (struct rli_iter_d *) lua_newuserdata(L, sizeof(struct rli_iter_d)); 1045 + 1046 + init_rli_iter(ds, a, x1, y1, x2, y2, dx, dy, false, false); 1047 + 1048 + /* returns the iter function with embedded iter data(up values) */ 1049 + lua_pushcclosure(L, &rli_iterator, 1); 1050 + 1051 + return 1; 1052 + } 1053 + 1054 + RLI_LUA rli_marshal(lua_State *L) /* also invert */ 140 1055 { 141 - fb_data newvalue = FB_SCALARPACK((unsigned)luaL_checknumber(L, 4)); 142 - *rli_element(L) = newvalue; 1056 + /* (img*, [x1, y1, x2, y2, dx, dy, clip, function]) */ 1057 + struct rocklua_image *a = rli_checktype(L, 1); 1058 + 1059 + struct rli_iter_d ds; 1060 + 1061 + int x1 = luaL_optint(L, 2, 1); 1062 + int y1 = luaL_optint(L, 3, 1); 1063 + int x2 = luaL_optint(L, 4, a->width); 1064 + int y2 = luaL_optint(L, 5, a->height); 1065 + int dx = luaL_optint(L, 6, 1); 1066 + int dy = luaL_optint(L, 7, 1); 1067 + bool clip = luaL_optboolean(L, 8, false); 1068 + 1069 + int lf_ref = LUA_NOREF; /* de-ref'd without consequence */ 1070 + 1071 + int (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *); 1072 + rli_trans = invert_transform; /* default transformation */ 1073 + 1074 + if(!clip) 1075 + { 1076 + bounds_check_xy(L, a, 2, x1, 3, y1); 1077 + bounds_check_xy(L, a, 4, x2, 5, y2); 1078 + } 1079 + 1080 + init_rli_iter(&ds, a, x1, y1, x2, y2, dx, dy, false, false); 1081 + 1082 + if(lua_isfunction(L, 9)) /* custom function */ 1083 + { 1084 + rli_trans = custom_transform; 1085 + lf_ref = register_luafunc(L, 9); 1086 + } 1087 + 1088 + do 1089 + { 1090 + luaL_argcheck(L, clip || (ds.elem != NULL), 1, ERR_DATA_OVF); 1091 + 1092 + if(!(*rli_trans)(L, &ds, NULL, lf_ref, NULL)) 1093 + break; /* Custom op can quit early */ 1094 + 1095 + } while(next_rli_iter(&ds)); 1096 + 1097 + luaL_unref(L, LUA_REGISTRYINDEX, lf_ref); /* de-reference custom function */ 143 1098 return 0; 144 1099 } 145 1100 146 - static int rli_get(lua_State *L) 1101 + RLI_LUA rli_copy(lua_State *L) 147 1102 { 148 - lua_pushnumber(L, FB_UNPACK_SCALAR_LCD(*rli_element(L))); 149 - return 1; 1103 + /* (dst*, src*, [d_x, d_y, s_x, s_y, x_off, y_off, clip, [op, funct/clr]]) */ 1104 + struct rocklua_image *d = rli_checktype(L, 1); /*dst*/ 1105 + struct rocklua_image *s = rli_checktype(L, 2); /*src*/ 1106 + 1107 + struct rli_iter_d ds; /*dst*/ 1108 + struct rli_iter_d ss; /*src*/ 1109 + 1110 + /* copy whole image if possible */ 1111 + if(s->elems == d->elems && s->width == d->width && lua_gettop(L) < 3) 1112 + { 1113 + memcpy(d->data, s->data, d->elems * sizeof(fb_data)); 1114 + return 0; 1115 + } 1116 + 1117 + int d_x = luaL_optint(L, 3, 1); 1118 + int d_y = luaL_optint(L, 4, 1); 1119 + int s_x = luaL_optint(L, 5, 1); 1120 + int s_y = luaL_optint(L, 6, 1); 1121 + 1122 + int w = MIN(d->width - d_x, s->width - s_x); 1123 + int h = MIN(d->height - d_y, s->height - s_y); 1124 + 1125 + int x_off = luaL_optint(L, 7, w); 1126 + int y_off = luaL_optint(L, 8, h); 1127 + 1128 + bool clip = luaL_optboolean(L, 9, false); 1129 + int op = luaL_optint(L, 10, 0); 1130 + fb_data clr = FB_SCALARPACK(0); /* 11 is custom function | color */ 1131 + 1132 + bool d_swx = (x_off < 0); /* dest swap */ 1133 + bool d_swy = (y_off < 0); 1134 + bool s_swx = false; /* src swap */ 1135 + bool s_swy = false; 1136 + 1137 + int (*rli_trans)(lua_State *, struct rli_iter_d *, struct rli_iter_d *, int, fb_data *); 1138 + rli_trans = blit_transform; /* default transformation */ 1139 + 1140 + int lf_ref = LUA_NOREF; /* de-ref'd without consequence */ 1141 + 1142 + if(!clip) /* Out of bounds is not allowed */ 1143 + { 1144 + bounds_check_xy(L, d, 3, d_x, 4, d_y); 1145 + bounds_check_xy(L, s, 5, s_x, 6, s_y); 1146 + } 1147 + else if (w < 0 || h < 0) /* not an error but nothing to display */ 1148 + return 0; 1149 + 1150 + w = MIN(w, ABS(x_off)); 1151 + h = MIN(h, ABS(y_off)); 1152 + 1153 + /* if(s->data == d->data) need to care about fill direction */ 1154 + if(d_x > s_x) 1155 + { 1156 + d_swx = !d_swx; 1157 + s_swx = !s_swx; 1158 + } 1159 + 1160 + if(d_y > s_y) 1161 + { 1162 + d_swy = !d_swy; 1163 + s_swy = !s_swy; 1164 + } 1165 + 1166 + init_rli_iter(&ds, d, d_x, d_y, d_x + w, d_y + h, 1, 1, d_swx, d_swy); 1167 + 1168 + init_rli_iter(&ss, s, s_x, s_y, s_x + w, s_y + h, 1, 1, s_swx, s_swy); 1169 + 1170 + if (op == 0xFF && lua_isfunction(L, 11)) /* custom function specified.. */ 1171 + { 1172 + rli_trans = custom_transform; 1173 + lf_ref = register_luafunc(L, 11); 1174 + op = lf_ref; 1175 + } 1176 + else 1177 + clr = FB_SCALARPACK((unsigned) luaL_optnumber(L, 11, LCD_BLACK)); 1178 + 1179 + do 1180 + { 1181 + if(!clip) 1182 + { 1183 + luaL_argcheck(L, ss.elem != NULL, 2, ERR_DATA_OVF); 1184 + luaL_argcheck(L, ds.elem != NULL, 1, ERR_DATA_OVF); 1185 + } 1186 + 1187 + if(!(*rli_trans)(L, &ds, &ss, op, &clr)) 1188 + break; /* Custom op can quit early */ 1189 + 1190 + } while(next_rli_iter(&ds) && next_rli_iter(&ss)); 1191 + 1192 + luaL_unref(L, LUA_REGISTRYINDEX, lf_ref); /* de-reference custom function */ 1193 + return 0; 150 1194 } 151 1195 152 - static int rli_tostring(lua_State *L) 1196 + RLI_LUA rli_clear(lua_State *L) 153 1197 { 1198 + /* (dst*, [color, x1, y1, x2, y2, clip]) */ 154 1199 struct rocklua_image *a = rli_checktype(L, 1); 155 - lua_pushfstring(L, ROCKLUA_IMAGE ": %dx%d", a->width, a->height); 156 - return 1; 1200 + 1201 + struct rli_iter_d ds; 1202 + 1203 + fb_data clr = FB_SCALARPACK((unsigned) luaL_optnumber(L, 2, LCD_BLACK)); 1204 + int x1 = luaL_optint(L, 3, 1); 1205 + int y1 = luaL_optint(L, 4, 1); 1206 + int x2 = luaL_optint(L, 5, a->width); 1207 + int y2 = luaL_optint(L, 6, a->height); 1208 + bool clip = luaL_optboolean(L, 7, false); 1209 + 1210 + if(!clip) 1211 + { 1212 + bounds_check_xy(L, a, 3, x1, 4, y1); 1213 + bounds_check_xy(L, a, 5, x2, 6, y2); 1214 + } 1215 + 1216 + init_rli_iter(&ds, a, x1, y1, x2, y2, 1, 1, false, false); 1217 + 1218 + do 1219 + { 1220 + luaL_argcheck(L, clip || (ds.elem != NULL), 1, ERR_DATA_OVF); 1221 + 1222 + data_set(ds.elem, ds.x, ds.y, &clr); 1223 + 1224 + } while(next_rli_iter(&ds)); 1225 + 1226 + return 0; 157 1227 } 1228 + #endif /* RLI_EXTENDED */ 158 1229 1230 + /* Rli Image methods exported to lua */ 159 1231 static const struct luaL_reg rli_lib [] = 160 1232 { 161 1233 {"__tostring", rli_tostring}, 162 - {"set", rli_set}, 163 - {"get", rli_get}, 164 - {"width", rli_width}, 165 - {"height", rli_height}, 1234 + {"_data", rli_raw}, 1235 + {"__len", rli_size}, 1236 + {"__eq", rli_equal}, 1237 + {"width", rli_width}, 1238 + {"height", rli_height}, 1239 + {"set", rli_set}, 1240 + {"get", rli_get}, 1241 + 1242 + #ifdef RLI_EXTENDED 1243 + {"copy", rli_copy}, 1244 + {"clear", rli_clear}, 1245 + {"invert", rli_marshal}, 1246 + {"marshal", rli_marshal}, 1247 + {"points", rli_iterator_factory}, 1248 + {"line", rli_line}, 1249 + {"ellipse", rli_ellipse}, 1250 + #endif /* RLI_EXTENDED */ 166 1251 {NULL, NULL} 167 1252 }; 168 1253 169 1254 static inline void rli_init(lua_State *L) 170 1255 { 1256 + /* some devices need x | y coords shifted to match native format */ 1257 + /* conversion between packed native formats and individual pixel addressing */ 1258 + init_pixelmask(&x_shift, &y_shift, &xy_mask, &pixelmask); 1259 + 171 1260 luaL_newmetatable(L, ROCKLUA_IMAGE); 172 1261 173 1262 lua_pushstring(L, "__index"); ··· 189 1278 #define SIMPLE_VOID_WRAPPER(func) RB_WRAP(func) { (void)L; func(); return 0; } 190 1279 191 1280 /* Helper function for opt_viewport */ 192 - static void check_tablevalue(lua_State *L, const char* key, int tablepos, void* res, bool unsigned_val) 1281 + static void check_tablevalue(lua_State *L, 1282 + const char* key, 1283 + int tablepos, 1284 + void* res, 1285 + bool is_unsigned) 193 1286 { 194 1287 lua_getfield(L, tablepos, key); /* Find table[key] */ 195 1288 196 - if(!lua_isnoneornil(L, -1)) 197 - { 198 - if(unsigned_val) 199 - *(unsigned*)res = luaL_checkint(L, -1); 200 - else 201 - *(int*)res = luaL_checkint(L, -1); 202 - } 1289 + int val = luaL_optint(L, -1, 0); 1290 + 1291 + if(is_unsigned) 1292 + *(unsigned*)res = (unsigned) val; 1293 + else 1294 + *(int*)res = val; 203 1295 204 1296 lua_pop(L, 1); /* Pop the value off the stack */ 205 1297 } 206 1298 207 - static struct viewport* opt_viewport(lua_State *L, int narg, struct viewport* alt) 1299 + static inline struct viewport* opt_viewport(lua_State *L, 1300 + int narg, 1301 + struct viewport* vp, 1302 + struct viewport* alt) 208 1303 { 209 1304 if(lua_isnoneornil(L, narg)) 210 1305 return alt; 211 1306 212 - int tablepos = lua_gettop(L); 213 - struct viewport *vp; 214 - 215 - lua_getfield(L, tablepos, "vp"); /* get table['vp'] */ 216 - if(lua_isnoneornil(L, -1)) 217 - { 218 - lua_pop(L, 1); /* Pop nil off stack */ 219 - 220 - vp = (struct viewport*) lua_newuserdata(L, sizeof(struct viewport)); /* Allocate memory and push it as udata on the stack */ 221 - memset(vp, 0, sizeof(struct viewport)); /* Init viewport values to 0 */ 222 - lua_setfield(L, tablepos, "vp"); /* table['vp'] = vp (pops value off the stack) */ 223 - } 224 - else 225 - { 226 - vp = (struct viewport*) lua_touserdata(L, -1); /* Reuse viewport struct */ 227 - lua_pop(L, 1); /* We don't need the value on stack */ 228 - } 229 - 230 1307 luaL_checktype(L, narg, LUA_TTABLE); 231 1308 232 - check_tablevalue(L, "x", tablepos, &vp->x, false); 233 - check_tablevalue(L, "y", tablepos, &vp->y, false); 234 - check_tablevalue(L, "width", tablepos, &vp->width, false); 235 - check_tablevalue(L, "height", tablepos, &vp->height, false); 1309 + check_tablevalue(L, "x", narg, &vp->x, false); 1310 + check_tablevalue(L, "y", narg, &vp->y, false); 1311 + check_tablevalue(L, "width", narg, &vp->width, false); 1312 + check_tablevalue(L, "height", narg, &vp->height, false); 236 1313 #ifdef HAVE_LCD_BITMAP 237 - check_tablevalue(L, "font", tablepos, &vp->font, false); 238 - check_tablevalue(L, "drawmode", tablepos, &vp->drawmode, false); 1314 + check_tablevalue(L, "font", narg, &vp->font, false); 1315 + check_tablevalue(L, "drawmode", narg, &vp->drawmode, false); 239 1316 #endif 240 1317 #if LCD_DEPTH > 1 241 - check_tablevalue(L, "fg_pattern", tablepos, &vp->fg_pattern, true); 242 - check_tablevalue(L, "bg_pattern", tablepos, &vp->bg_pattern, true); 1318 + check_tablevalue(L, "fg_pattern", narg, &vp->fg_pattern, true); 1319 + check_tablevalue(L, "bg_pattern", narg, &vp->bg_pattern, true); 243 1320 #endif 244 1321 245 1322 return vp; ··· 247 1324 248 1325 RB_WRAP(set_viewport) 249 1326 { 250 - struct viewport *vp = opt_viewport(L, 1, NULL); 1327 + static struct viewport vp; 251 1328 int screen = luaL_optint(L, 2, SCREEN_MAIN); 252 - rb->screens[screen]->set_viewport(vp); 1329 + rb->screens[screen]->set_viewport(opt_viewport(L, 1, &vp, NULL)); 253 1330 return 0; 254 1331 } 255 1332 ··· 801 1878 802 1879 return 1; 803 1880 } 1881 +