A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 215 lines 8.4 kB view raw
1--[[ Lua Image save 2/*************************************************************************** 3 * __________ __ ___. 4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 * \/ \/ \/ \/ \/ 9 * $Id$ 10 * 11 * Copyright (C) 2017 William Wilgus 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 2 16 * of the License, or (at your option) any later version. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ****************************************************************************/ 22]] 23-- save(img, path/name) 24-- bmp saving derived from rockbox - screendump.c 25-- bitdepth is limited by the device 26-- eg. device displays greyscale, rgb images are saved greyscale 27if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end 28 29do 30 local rocklib_image = getmetatable(rb.lcd_framebuffer()) 31 32 -- internal constants 33 local _NIL = nil -- _NIL placeholder 34 local _points = rocklib_image.points 35 36 -- saves img to file: name 37 return function(img, name) 38 local file 39 local bbuffer = {} -- concat buffer for s_bytes 40 local fbuffer = {} -- concat buffer for file writes, reused 41 42 local function s_bytesLE(bits, value) 43 -- bits must be multiples of 8 (sizeof byte) 44 local byte 45 local nbytes = bit.rshift(bits, 3) 46 for b = 1, nbytes do 47 if value > 0 then 48 byte = value % 256 49 value = (value - byte) / 256 50 else 51 byte = 0 52 end 53 bbuffer[b] = string.char(byte) 54 end 55 return table.concat(bbuffer, _NIL, 1, nbytes) 56 end 57 58 local function s_bytesBE(bits, value) 59 -- bits must be multiples of 8 (sizeof byte) 60 local byte 61 local nbytes = bit.rshift(bits, 3) 62 for b = nbytes, 1, -1 do 63 if value > 0 then 64 byte = value % 256 65 value = (value - byte) / 256 66 else 67 byte = 0 68 end 69 bbuffer[b] = string.char(byte) 70 end 71 return table.concat(bbuffer, _NIL, 1, nbytes) 72 end 73 74 local cmp = {["r"] = function(c) return bit.band(bit.rshift(c, 16), 0xFF) end, 75 ["g"] = function(c) return bit.band(bit.rshift(c, 08), 0xFF) end, 76 ["b"] = function(c) return bit.band(c, 0xFF) end} 77 78 local function bmp_color(color) 79 return s_bytesLE(8, cmp.b(color)).. 80 s_bytesLE(8, cmp.g(color)).. 81 s_bytesLE(8, cmp.r(color)).. 82 s_bytesLE(8, 0) .. "" 83 end -- c_cmp(color, c.r)) 84 85 local function bmp_color_mix(c1, c2, num, den) 86 -- mixes c1 and c2 as ratio of numerator / denominator 87 -- used 2x each save results 88 local bc1, gc1, rc1 = cmp.b(c1), cmp.g(c1), cmp.r(c1) 89 90 return s_bytesLE(8, cmp.b(c2) - bc1 * num / den + bc1).. 91 s_bytesLE(8, cmp.g(c2) - gc1 * num / den + gc1).. 92 s_bytesLE(8, cmp.r(c2) - rc1 * num / den + rc1).. 93 s_bytesLE(8, 0) .. "" 94 end 95 96 local w, h = img:width(), img:height() 97 local depth = tonumber(img:__tostring(6)) -- RLI_INFO_DEPTH = 0x6 98 local format = tonumber(img:__tostring(7)) -- RLI_INFO_FORMAT = 0x7 99 100 local bpp, bypl -- bits per pixel, bytes per line 101 -- bypl, pad rows to a multiple of 4 bytes 102 if depth <= 4 then 103 bpp = 8 -- 256 color image 104 bypl = (w + 3) 105 elseif depth <= 16 then 106 bpp = 16 107 bypl = (w * 2 + 3) 108 elseif depth <= 24 then 109 bpp = 24 110 bypl = (w * 3 + 3) 111 else 112 bpp = 32 113 bypl = (w * 4 + 3) 114 end 115 116 local linebytes = bit.band(bypl, bit.bnot(3)) 117 118 local bytesperpixel = bit.rshift(bpp, 3) 119 local headersz = 54 120 local imgszpad = h * linebytes 121 122 local compression, n_colors = 0, 0 123 local h_ppm, v_ppm = 0x00000EC4, 0x00000EC4 --Pixels Per Meter ~ 96 dpi 124 125 if depth == 16 then 126 compression = 3 -- BITFIELDS 127 n_colors = 3 128 elseif depth <= 8 then 129 n_colors = bit.lshift(1, depth) 130 end 131 132 headersz = headersz + (4 * n_colors) 133 134 file = io.open('/' .. name, "w+") -- overwrite, rb ignores the 'b' flag 135 136 if not file then 137 rb.splash(rb.HZ, "Error opening /" .. name) 138 return 139 end 140 -- create a bitmap header 'rope' with image details -- concatenated at end 141 local bmpheader = fbuffer 142 143 bmpheader[01] = "BM" 144 bmpheader[02] = s_bytesLE(32, headersz + imgszpad) 145 bmpheader[03] = "\0\0\0\0" -- WORD reserved 1 & 2 146 bmpheader[04] = s_bytesLE(32, headersz) -- BITMAPCOREHEADER size 147 bmpheader[05] = s_bytesLE(32, 40) -- BITMAPINFOHEADER size 148 149 bmpheader[06] = s_bytesLE(32, w) 150 bmpheader[07] = s_bytesLE(32, h) 151 bmpheader[08] = "\1\0" -- WORD color planes ALWAYS 1 152 bmpheader[09] = s_bytesLE(16, bpp) -- bits/pixel 153 bmpheader[10] = s_bytesLE(32, compression) 154 bmpheader[11] = s_bytesLE(32, imgszpad) 155 bmpheader[12] = s_bytesLE(32, h_ppm) -- biXPelsPerMeter 156 bmpheader[13] = s_bytesLE(32, v_ppm) -- biYPelsPerMeter 157 bmpheader[14] = s_bytesLE(32, n_colors) 158 bmpheader[15] = s_bytesLE(32, n_colors) 159 160 -- Color Table (#n_colors entries) 161 if depth == 1 then -- assuming positive display 162 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF) 163 bmpheader[#bmpheader + 1] = bmp_color(0x0) 164 elseif depth == 2 then 165 bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF) 166 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 1, 3) 167 bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 2, 3) 168 bmpheader[#bmpheader + 1] = bmp_color(0x0) 169 elseif depth == 16 then 170 if format == 555 then 171 -- red bitfield mask 172 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x00007C00) 173 -- green bitfield mask 174 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000003E0) 175 -- blue bitfield mask 176 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F) 177 else --565 178 -- red bitfield mask 179 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000F800) 180 -- green bitfield mask 181 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000007E0) 182 -- blue bitfield mask 183 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F) 184 end 185 end 186 187 file:write(table.concat(fbuffer))-- write the header to the file now 188 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table 189 190 local imgdata = fbuffer 191 -- pad rows to a multiple of 4 bytes 192 local bytesleft = linebytes - (bytesperpixel * w) 193 local t_data = {} 194 local fs_bytes_E = s_bytesLE -- default save in Little Endian 195 196 if format == 3553 then -- RGB565SWAPPED 197 fs_bytes_E = s_bytesBE -- Saves in Big Endian 198 end 199 200 -- Bitmap lines start at bottom unless biHeight is negative 201 for point in _points(img, 1, h, w + bytesleft, 1, 1, 1, true) do 202 imgdata[#imgdata + 1] = fs_bytes_E(bpp, point or 0) 203 204 if #fbuffer >= 31 then -- buffered write, increase # for performance 205 file:write(table.concat(fbuffer)) 206 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table 207 end 208 209 end 210 file:write(table.concat(fbuffer)) --write leftovers to file 211 fbuffer = _NIL 212 213 file:close() 214 end -- save(img, name) 215end