Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

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

lua add rb_poly.lua vector drawing and memoization demo

just playing around with using lots of ram (and processing power)
in lua threw this together vector draws Rb logo and flips rotates and zoom

Change-Id: Ie1fe16a9a50271657f2ab7b9a39bf71e6db90d2c

+438
+1
apps/plugins/lua/include_lua/menubuttons.lua
··· 53 53 SELR = rb.actions.PLA_SELECT_REPEAT, 54 54 UP = rb.actions.PLA_UP, 55 55 UPR = rb.actions.PLA_UP_REPEAT, 56 + NONE = rb.actions.NONE, 56 57 } 57 58 58 59 rb = oldrb
+437
apps/plugins/lua_scripts/rb_poly.lua
··· 1 + --[[ Rockbox vector logo 2 + /*************************************************************************** 3 + * __________ __ ___. 4 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + * \/ \/ \/ \/ \/ 9 + * $Id$ 10 + * 11 + * Copyright (C) 2026 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 + local _clr = require("color") -- clrset, clrinc provides device independent colors 24 + local _lcd = require("lcd") -- lcd helper functions 25 + local actions = require("menubuttons") 26 + 27 + local WHITE = _clr.set(-1, 255, 255, 255) 28 + local BLACK = _clr.set(0, 0, 0, 0) 29 + local YELLOW = _clr.set(BLACK, 255, 192, 0) 30 + local GREY = _clr.set(WHITE, 180, 195, 211) 31 + if rb.LCD_DEPTH == 2 then --greyscale display 32 + YELLOW = _clr.set(2, 255, 192, 0) 33 + GREY = _clr.set(1, 180, 195, 211) 34 + end 35 + 36 + if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 37 + 38 + --poly draw from draw_poly.lua with negative (inverse) scaling added and optimizations 39 + local _poly = {} do 40 + -- Internal Constants 41 + local rocklib_image = getmetatable(rb.lcd_framebuffer()) 42 + local BSAND = 8 -- blits color to dst if src <> 0 43 + local _NIL = nil -- nil placeholder 44 + 45 + local _abs = math.abs 46 + local _clear = rocklib_image.clear 47 + local _copy = rocklib_image.copy 48 + local _line = rocklib_image.line 49 + local _newimg = rb.new_image 50 + local flood_fill 51 + 52 + local function scale_val_none(val, scale) 53 + return val 54 + end 55 + local function scale_val_up(val, scale) 56 + return val * scale 57 + end 58 + local function scale_val_dn(val, scale) 59 + return val / scale 60 + end 61 + 62 + local function get_scale_fn(scale) 63 + local scale_fn, fn 64 + if (scale < 0) then 65 + scale = -scale 66 + scale_fn = scale_val_dn 67 + elseif scale > 0 then 68 + scale_fn = scale_val_up 69 + else 70 + scale_fn = scale_val_none 71 + end 72 + return scale + 1, scale_fn 73 + end 74 + 75 + local function polyline_size_only(img, x, y, t_pts, scale_x, scale_y) 76 + scale_x = scale_x or 0 77 + scale_y = scale_y or 0 78 + local scale_val_x, scale_val_y 79 + 80 + scale_x, scale_val_x = get_scale_fn(scale_x) 81 + scale_y, scale_val_y = get_scale_fn(scale_y) 82 + 83 + local pt_first_last, pt1, pt2 84 + local max_x, max_y = 0, 0 85 + local len = #t_pts 86 + if len < 4 then error("not enough points", 3) end 87 + 88 + pt_first_last = {scale_val_x(t_pts[1], scale_x), scale_val_y(t_pts[2], scale_y)} 89 + 90 + pt2 = {scale_val_x(t_pts[1], scale_x), scale_val_y(t_pts[2], scale_y)} 91 + for i = 3, len + 2, 2 do 92 + pt1 = pt2 93 + if t_pts[i + 1] == nil then 94 + pt2 = pt_first_last 95 + else 96 + pt2 = {scale_val_x(t_pts[i], scale_x), scale_val_y(t_pts[i + 1], scale_y)} 97 + end-- first and last point 98 + if pt1[1] > max_x then max_x = pt1[1] end 99 + if pt1[2] > max_y then max_y = pt1[2] end 100 + end 101 + if pt2[1] > max_x then max_x = pt2[1] end 102 + if pt2[2] > max_y then max_y = pt2[2] end 103 + return max_x + x, max_y + y 104 + end 105 + 106 + -- draws a non-filled figure based on points in t-points 107 + local function polyline(img, x, y, t_pts, color, bClosed, bClip, scale_x, scale_y) 108 + local draw_fn = _line 109 + scale_x = scale_x or 1 110 + scale_y = scale_y or 1 111 + local scale_val_x, scale_val_y 112 + 113 + scale_x, scale_val_x = get_scale_fn(scale_x) 114 + scale_y, scale_val_y = get_scale_fn(scale_y) 115 + 116 + local pt_first_last, pt1, pt2 117 + 118 + local len = #t_pts 119 + if len < 4 then error("not enough points", 3) end 120 + 121 + if bClosed then 122 + pt_first_last = {scale_val_x(t_pts[1], scale_x), scale_val_y(t_pts[2], scale_y)} 123 + else 124 + pt_first_last = {scale_val_x(t_pts[len - 1], scale_x), scale_val_y(t_pts[len], scale_y)} 125 + end 126 + 127 + pt2 = {scale_val_x(t_pts[1], scale_x), scale_val_y(t_pts[2], scale_y)} 128 + for i = 3, len + 2, 2 do 129 + pt1 = pt2 130 + if t_pts[i + 1] == nil then 131 + pt2 = pt_first_last 132 + else 133 + pt2 = {scale_val_x(t_pts[i], scale_x), scale_val_y(t_pts[i + 1], scale_y)} 134 + end-- first and last point 135 + draw_fn(img, pt1[1] + x, pt1[2] + y, pt2[1] + x, pt2[2] + y, color, bClip) 136 + end 137 + end 138 + 139 + -- draws a closed figure based on points in t_pts 140 + _poly.polygon = function(img, x, y, t_pts, color, fillcolor, bClip, scale_x, scale_y) 141 + scale_x = scale_x or 1 142 + scale_y = scale_y or 1 143 + if #t_pts < 2 then error("not enough points", 3) end 144 + 145 + if fillcolor then 146 + flood_fill = flood_fill or require("draw_floodfill") 147 + local x_min, x_max = _lcd.W, 0 148 + local y_min, y_max = _lcd.H, 0 149 + local w, h = 0, 0 150 + local pt1, pt2 151 + -- find boundries of polygon 152 + for i = 1, #t_pts, 2 do 153 + if t_pts[i] < x_min then x_min = t_pts[i] end 154 + if t_pts[i] > x_max then x_max = t_pts[i] end 155 + 156 + if t_pts[i+1] < y_min then y_min = t_pts[i+1] end 157 + if t_pts[i+1] > y_max then y_max = t_pts[i+1] end 158 + end 159 + 160 + local scale 161 + if (scale_x < 0) then 162 + scale = -scale_x + 1 163 + x_max = x_max / scale 164 + x_min = x_min / scale 165 + else 166 + scale = scale_x + 1 167 + x_max = x_max * scale 168 + x_min = x_min * scale 169 + end 170 + 171 + if (scale_y < 0) then 172 + scale = -scale_y + 1 173 + y_max = y_max / -scale_y 174 + y_min = y_min / -scale_y 175 + else 176 + scale = scale_y + 1 177 + y_max = y_max * scale 178 + y_min = y_min * scale 179 + end 180 + 181 + w = _abs(x_max) + _abs(x_min) 182 + if w > _lcd.W then w = _lcd.W end 183 + 184 + h = _abs(y_max) + _abs(y_min) 185 + if h > _lcd.H then h = _lcd.H end 186 + 187 + if x_min < _lcd.W and y_min < _lcd.H then 188 + -- hack so we don't waste time drawing off screen 189 + x_min = 0 190 + y_min = 0 191 + x_min = -(x_min - 2) -- leave a border to use flood_fill 192 + y_min = -(y_min - 2) 193 + 194 + local fill_img = _newimg(w + 3, h + 3) 195 + _clear(fill_img, 0x1) 196 + 197 + polyline(fill_img, x_min, y_min, t_pts, 0x0, true, bClip, scale_x, scale_y) 198 + -- flood the outside of the figure with 0 the inside will be fillcolor 199 + flood_fill(fill_img, fill_img:width(), fill_img:height() , 0x1, 0x0) 200 + _copy(img, fill_img, x - 1, y - 1, 201 + _NIL, _NIL, _NIL, _NIL, bClip, BSAND, fillcolor) 202 + end 203 + 204 + end 205 + 206 + polyline(img, x, y, t_pts, color, true, bClip, scale_x, scale_y) 207 + end 208 + 209 + -- expose internal functions to the outside through _poly table 210 + _poly.polyline = polyline 211 + _poly.polyline_size_only = polyline_size_only 212 + end 213 + 214 + -- $Rb - Vectors, each is an (x,y) pair lines get drawn between them 215 + local cross_left_pts = {8, 0, 8, 26, 8, 5, 4, 5, 36, 5} 216 + 217 + local R_outline_pts = {12, 10, 43, 10, 47, 11, 50, 13, 54, 16, 56, 20, 218 + 58, 24, 58, 25, 59, 28, 59, 29, 60, 33, 60, 38, 219 + 60, 60, 57, 65, 54, 70, 50, 75, 50, 77, 70, 127, 220 + 50, 127, 34, 84, 27, 84, 26, 127, 12, 127, 12, 10} 221 + 222 + local R_center_pts = {26, 31, 26, 63, 38, 63, 39, 61, 41, 60, 43, 58, 223 + 44, 55, 44, 39, 43, 35, 40, 32, 39, 31, 26, 31} 224 + 225 + local R_shadow_pts = {26, 126, 35, 126, 32, 126, 32, 128, 32, 226 + 59, 37, 57, 40, 56, 41, 40, 38, 36, 27, 35} 227 + 228 + local b_outline_pts = {59, 38, 79, 38, 79, 61, 83, 59, 87, 58, 90, 57, 98, 57, 229 + 101, 57, 105, 58, 110, 60, 114, 63, 117, 66, 121, 70, 124, 230 + 75, 126, 80, 127, 85, 128, 87, 128, 98, 127, 101, 126, 106, 231 + 124, 110, 121, 114, 115, 120, 106, 125, 100, 126, 96, 126, 232 + 88, 126, 82, 125, 78, 122, 76, 125, 59, 125, 59, 38} 233 + 234 + local b_center_pts = {92, 77, 100, 77, 101, 78, 103, 78, 105, 80, 107, 81, 235 + 108, 83, 109, 84, 110, 86, 111, 89, 112, 90, 112, 95, 236 + 111, 97, 110, 100, 108, 103, 105, 105, 102, 106, 98, 237 + 107, 95, 107, 90, 106, 87, 104, 83, 100, 82, 97, 81, 238 + 94, 81, 88, 83, 85, 84, 83, 86, 81, 89, 79, 93, 77} 239 + 240 + local clef_pts = {6, 46, 7, 44, 10, 44, 11, 45, 13, 46, 14, 47, 16, 49, 17, 241 + 50, 18, 52, 18, 54, 17, 59, 17, 60, 16, 62, 12, 75, 12, 85, 242 + 13, 87, 14, 88, 16, 90, 18, 91, 21, 91, 24, 91, 28, 89, 29, 243 + 86, 28, 82, 24, 79, 23, 80, 23, 84, 29, 106, 29, 114, 28, 244 + 116, 25, 117, 24, 116, 23, 114, 23, 112, 25, 111, 26, 113, 245 + 27, 113, 27, 107, 26, 102, 24, 94, 20, 79, 16, 83, 15, 83, 246 + 20, 77, 6, 50, 6, 47, 9, 48, 11, 48, 13, 50, 15, 52, 15, 55, 247 + 14, 57, 9, 76, 9, 86, 10, 90, 13, 94, 19, 95, 23, 94, 29, 90, 248 + 31, 86, 31, 82, 29, 79, 26, 77, 24, 77, 22, 77, 8, 49} 249 + 250 + local clef_void_1_pts = {13, 59, 8, 48, 13, 50, 14, 54, 14, 58} 251 + 252 + local clef_void_2_pts = {15, 69, 13, 76, 12, 79, 12, 86, 13, 88, 17, 91, 21, 253 + 91, 23, 91, 20, 81, 16, 83, 15, 82, 18, 78, 16, 71} 254 + 255 + local clef_void_3_pts = {23, 80, 26, 90, 28, 86, 28, 83, 27, 81, 23, 79} 256 + 257 + local max_x = 1 258 + local max_y = 1 259 + --memorize the rotated points but let them still get collected if ram is needed 260 + local rot_memoized = setmetatable({}, { __mode = 'k' }) --Keys are weak 261 + 262 + local function rb_logo_sz(sx, sy) 263 + local x, y 264 + local t_pts = {cross_left_pts, R_outline_pts, R_shadow_pts, b_outline_pts, clef_pts} 265 + for k, points_t in pairs(t_pts) do 266 + x, y = _poly.polyline_size_only(_LCD, 0, 0, points_t, sx, sy) 267 + if x > max_x then max_x = x end 268 + if y > max_y then max_y = y end 269 + end 270 + end 271 + 272 + local function Rot(t_pts, rot) 273 + local count = #t_pts 274 + local nw = max_x 275 + local nh = max_y 276 + 277 + rot = rot % 7 278 + if rot == 0 then 279 + return t_pts 280 + end 281 + 282 + -- convert to string keys 283 + rot = tostring(rot) 284 + local pts = tostring(t_pts) 285 + 286 + if not rot_memoized[rot] then 287 + rot_memoized[rot] = {} 288 + end 289 + 290 + if not rot_memoized[rot][pts] then 291 + rot_memoized[rot][pts] = {} 292 + else 293 + return rot_memoized[rot][pts] 294 + end 295 + 296 + if rot == "1" then 297 + for i = 1, count, 2 do 298 + rot_memoized[rot][pts][count + 1 - i] = t_pts[i] 299 + rot_memoized[rot][pts][count + 1 - (i + 1)] = nw - t_pts[i+1] 300 + end 301 + elseif rot == "2" then 302 + for i = 1, count, 2 do 303 + rot_memoized[rot][pts][count + 1 - i] = nh - t_pts[i+1] 304 + rot_memoized[rot][pts][count + 1 - (i + 1)] = nw-t_pts[i] 305 + end 306 + elseif rot == "3" then 307 + for i = 1, count, 2 do 308 + rot_memoized[rot][pts][count + 1 - i] = nh - t_pts[i] 309 + rot_memoized[rot][pts][count + 1 - (i + 1)] = t_pts[i+1] 310 + end 311 + elseif rot == "4" then 312 + for i = 1, count, 2 do 313 + rot_memoized[rot][pts][count + 1 - i] = nh - t_pts[i+1] 314 + rot_memoized[rot][pts][count + 1 - (i + 1)] = t_pts[i] 315 + end 316 + elseif rot == "5" then 317 + for i = 1, count, 2 do 318 + rot_memoized[rot][pts][count + 1 - i] = nh - t_pts[i] 319 + rot_memoized[rot][pts][count + 1 - (i + 1)] = nw-t_pts[i+1] 320 + end 321 + elseif rot == "6" then 322 + for i = 1, count, 2 do 323 + rot_memoized[rot][pts][count + 1 - i] = t_pts[i+1] 324 + rot_memoized[rot][pts][count + 1 - (i + 1)] = nw-t_pts[i] 325 + end 326 + end 327 + 328 + return rot_memoized[rot][pts] 329 + end 330 + local count = -1 331 + 332 + local function rb_logo_rot(x, y, sx, sy, rot) 333 + local polygon 334 + 335 + if count >= 0 then 336 + polygon = _poly.polygon 337 + _lcd:clear(BLACK) 338 + _poly.polyline(_LCD, x, y, Rot(cross_left_pts, rot), WHITE, false, true, sx, sy) 339 + _poly.polyline(_LCD, x, y, Rot(R_shadow_pts, rot), WHITE, false, true, sx, sy) 340 + else 341 + polygon = function() end 342 + end 343 + repeat 344 + polygon(_LCD, x, y, Rot(R_outline_pts, rot), BLACK, YELLOW, true, sx, sy) 345 + polygon(_LCD, x, y, Rot(R_center_pts, rot), BLACK, BLACK, true, sx, sy) 346 + polygon(_LCD, x, y, Rot(b_outline_pts, rot), BLACK, GREY, true, sx, sy) 347 + polygon(_LCD, x, y, Rot(b_center_pts, rot), BLACK, BLACK, true, sx, sy) 348 + 349 + polygon(_LCD, x, y, Rot(clef_pts, rot), WHITE, WHITE, true, sx, sy) 350 + polygon(_LCD, x, y, Rot(clef_void_1_pts, rot), WHITE, BLACK, true, sx, sy) 351 + polygon(_LCD, x, y, Rot(clef_void_2_pts, rot), WHITE, YELLOW, true, sx, sy) 352 + polygon(_LCD, x, y, Rot(clef_void_3_pts, rot), WHITE, YELLOW, true, sx, sy) 353 + rot = rot + 1 354 + until count >= 0 or rot >= 6; 355 + end 356 + 357 + local action 358 + local redraw = true; 359 + local rot = 0 360 + local sx = -2 361 + local sy = -2 362 + 363 + -- we want the size of everything @ 1:1 scale so we can use that to flip/rotate the figure 364 + rb_logo_sz(0, 0) 365 + 366 + while true do 367 + 368 + if redraw then 369 + rb_logo_rot(0,0, sx, sy, rot) 370 + _lcd:update() 371 + if count > 0 then 372 + redraw = false 373 + local prot = (rot) % 7 374 + if prot > 0 then 375 + prot = prot - 1 376 + if prot == 0 then prot = 6 end 377 + rot_memoized[tostring(prot)] = nil 378 + end 379 + end 380 + end 381 + 382 + action = rb.get_plugin_action(rb.HZ/2, 1) 383 + if action == actions.CANCEL or action == actions.EXIT then 384 + break 385 + end 386 + 387 + redraw = redraw or (action ~= actions.NONE) 388 + 389 + if action == actions.LEFT then 390 + sx = sx - 1 391 + elseif action == actions.LEFTR then 392 + sx = sx - 1 393 + sy = sy - 1 394 + elseif action == actions.RIGHT then 395 + sx = sx + 1 396 + elseif action == actions.RIGHTR then 397 + sx = sx + 1 398 + sy = sy + 1 399 + elseif action == actions.UP then 400 + sy = sy - 1 401 + elseif action == actions.UPR then 402 + sy = sy - 1 403 + sx = sy 404 + elseif action == actions.DOWN then 405 + sy = sy + 1 406 + elseif action == actions.DOWNR then 407 + sy = sy + 1 408 + sx = sy 409 + elseif action == actions.SELR then 410 + rot = rot + 1 411 + count = 0; 412 + redraw = true 413 + elseif count > 5 then 414 + count = 0; 415 + rot = rot + 1 416 + redraw = true 417 + else 418 + count = count + 1 419 + end 420 + end --wend 421 + 422 + local used, allocd, free = rb.mem_stats() 423 + 424 + collectgarbage("collect") 425 + local lu = collectgarbage("count") 426 + local fmt = function(t, v) return string.format("%s: %d Kb\n", t, v /1024) end 427 + 428 + -- this is how lua recommends to concat strings rather than .. 429 + local s_t = {} 430 + s_t[1] = "rockbox:\n" 431 + s_t[2] = fmt("Used ", used) 432 + s_t[3] = fmt("Allocd ", allocd) 433 + s_t[4] = fmt("Free ", free) 434 + s_t[5] = "\nlua:\n" 435 + s_t[6] = fmt("Used", lu * 1024) 436 + s_t[7] = "\n\nNote that the rockbox used count is a high watermark\n" 437 + rb.splash_scroller(10 * rb.HZ, table.concat(s_t))