forked from len0rd/rockbox
		
	
		
			
				
	
	
		
			215 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| --[[ Lua Image save
 | |
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 | |
|  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 | |
|  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 | |
|  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 | |
|  *                     \/            \/     \/    \/            \/
 | |
|  * $Id$
 | |
|  *
 | |
|  * Copyright (C) 2017 William Wilgus
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License
 | |
|  * as published by the Free Software Foundation; either version 2
 | |
|  * of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | |
|  * KIND, either express or implied.
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| ]]
 | |
| -- save(img, path/name)
 | |
| -- bmp saving derived from rockbox - screendump.c
 | |
| -- bitdepth is limited by the device
 | |
| -- eg. device displays greyscale, rgb images are saved greyscale
 | |
| if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
 | |
| 
 | |
| do
 | |
|     local rocklib_image = getmetatable(rb.lcd_framebuffer())
 | |
| 
 | |
|     -- internal constants
 | |
|     local _NIL = nil -- _NIL placeholder
 | |
|     local _points  = rocklib_image.points
 | |
| 
 | |
|     -- saves img to file: name
 | |
|     return function(img, name)
 | |
|         local file
 | |
|         local bbuffer = {} -- concat buffer for s_bytes
 | |
|         local fbuffer = {} -- concat buffer for file writes, reused
 | |
| 
 | |
|         local function s_bytesLE(bits, value)
 | |
|             -- bits must be multiples of 8 (sizeof byte)
 | |
|             local byte
 | |
|             local nbytes = bit.rshift(bits, 3)
 | |
|             for b = 1, nbytes do
 | |
|                 if value > 0 then
 | |
|                     byte  = value % 256
 | |
|                     value = (value - byte) / 256
 | |
|                 else
 | |
|                     byte = 0
 | |
|                 end
 | |
|                 bbuffer[b] = string.char(byte)
 | |
|             end
 | |
|             return table.concat(bbuffer, _NIL, 1, nbytes)
 | |
|         end
 | |
| 
 | |
|         local function s_bytesBE(bits, value)
 | |
|             -- bits must be multiples of 8 (sizeof byte)
 | |
|             local byte
 | |
|             local nbytes = bit.rshift(bits, 3)
 | |
|             for b = nbytes, 1, -1 do
 | |
|                 if value > 0 then
 | |
|                     byte  = value % 256
 | |
|                     value = (value - byte) / 256
 | |
|                 else
 | |
|                     byte = 0
 | |
|                 end
 | |
|                 bbuffer[b] = string.char(byte)
 | |
|             end
 | |
|             return table.concat(bbuffer, _NIL, 1, nbytes)
 | |
|         end
 | |
| 
 | |
|         local cmp = {["r"] =  function(c) return bit.band(bit.rshift(c, 16), 0xFF) end,
 | |
|                      ["g"] =  function(c) return bit.band(bit.rshift(c, 08), 0xFF) end,
 | |
|                      ["b"] =  function(c) return bit.band(c, 0xFF) end}
 | |
| 
 | |
|         local function bmp_color(color)
 | |
|             return s_bytesLE(8, cmp.b(color))..
 | |
|                    s_bytesLE(8, cmp.g(color))..
 | |
|                    s_bytesLE(8, cmp.r(color))..
 | |
|                    s_bytesLE(8, 0) .. ""
 | |
|         end -- c_cmp(color, c.r))
 | |
| 
 | |
|         local function bmp_color_mix(c1, c2, num, den)
 | |
|             -- mixes c1 and c2 as ratio of numerator / denominator
 | |
|             -- used 2x each save results
 | |
|             local bc1, gc1, rc1 = cmp.b(c1), cmp.g(c1), cmp.r(c1)
 | |
| 
 | |
|             return s_bytesLE(8, cmp.b(c2) - bc1 * num / den + bc1)..
 | |
|                    s_bytesLE(8, cmp.g(c2) - gc1 * num / den + gc1)..
 | |
|                    s_bytesLE(8, cmp.r(c2) - rc1 * num / den + rc1)..
 | |
|                    s_bytesLE(8, 0) .. ""
 | |
|         end
 | |
| 
 | |
|         local w, h = img:width(), img:height()
 | |
|         local depth  = tonumber(img:__tostring(6)) -- RLI_INFO_DEPTH   = 0x6
 | |
|         local format = tonumber(img:__tostring(7)) -- RLI_INFO_FORMAT  = 0x7
 | |
| 
 | |
|         local bpp, bypl -- bits per pixel, bytes per line
 | |
|         -- bypl, pad rows to a multiple of 4 bytes
 | |
|         if depth <= 4 then
 | |
|             bpp = 8 -- 256 color image
 | |
|             bypl = (w + 3)
 | |
|         elseif depth <= 16 then
 | |
|             bpp = 16
 | |
|             bypl = (w * 2 + 3)
 | |
|         elseif depth <= 24 then
 | |
|             bpp = 24
 | |
|             bypl = (w * 3 + 3)
 | |
|         else
 | |
|             bpp = 32
 | |
|             bypl = (w * 4 + 3)
 | |
|         end
 | |
| 
 | |
|         local linebytes = bit.band(bypl, bit.bnot(3))
 | |
| 
 | |
|         local bytesperpixel = bit.rshift(bpp, 3)
 | |
|         local headersz = 54
 | |
|         local imgszpad = h * linebytes
 | |
| 
 | |
|         local compression, n_colors = 0, 0
 | |
|         local h_ppm, v_ppm = 0x00000EC4, 0x00000EC4 --Pixels Per Meter ~ 96 dpi
 | |
| 
 | |
|         if depth == 16 then
 | |
|             compression = 3 -- BITFIELDS
 | |
|             n_colors    = 3
 | |
|         elseif depth <= 8 then
 | |
|             n_colors = bit.lshift(1, depth)
 | |
|         end
 | |
| 
 | |
|         headersz = headersz + (4 * n_colors)
 | |
| 
 | |
|         file = io.open('/' .. name, "w+") -- overwrite, rb ignores the 'b' flag
 | |
| 
 | |
|         if not file then
 | |
|             rb.splash(rb.HZ, "Error opening /" .. name)
 | |
|             return
 | |
|         end
 | |
|         -- create a bitmap header 'rope' with image details -- concatenated at end
 | |
|         local bmpheader = fbuffer
 | |
| 
 | |
|               bmpheader[01] = "BM"
 | |
|               bmpheader[02] = s_bytesLE(32, headersz + imgszpad)
 | |
|               bmpheader[03] = "\0\0\0\0" -- WORD reserved 1 & 2
 | |
|               bmpheader[04] = s_bytesLE(32, headersz) -- BITMAPCOREHEADER size
 | |
|               bmpheader[05] = s_bytesLE(32, 40) -- BITMAPINFOHEADER size
 | |
| 
 | |
|               bmpheader[06] = s_bytesLE(32, w)
 | |
|               bmpheader[07] = s_bytesLE(32, h)
 | |
|               bmpheader[08] = "\1\0" -- WORD color planes ALWAYS 1
 | |
|               bmpheader[09] = s_bytesLE(16, bpp) -- bits/pixel
 | |
|               bmpheader[10] = s_bytesLE(32, compression)
 | |
|               bmpheader[11] = s_bytesLE(32, imgszpad)
 | |
|               bmpheader[12] = s_bytesLE(32, h_ppm) -- biXPelsPerMeter
 | |
|               bmpheader[13] = s_bytesLE(32, v_ppm) -- biYPelsPerMeter
 | |
|               bmpheader[14] = s_bytesLE(32, n_colors)
 | |
|               bmpheader[15] = s_bytesLE(32, n_colors)
 | |
| 
 | |
|         -- Color Table (#n_colors entries)
 | |
|         if depth == 1 then -- assuming positive display
 | |
|             bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
 | |
|             bmpheader[#bmpheader + 1] = bmp_color(0x0)
 | |
|         elseif depth == 2 then
 | |
|             bmpheader[#bmpheader + 1] = bmp_color(0xFFFFFF)
 | |
|             bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 1, 3)
 | |
|             bmpheader[#bmpheader + 1] = bmp_color_mix(0xFFFFFF, 0, 2, 3)
 | |
|             bmpheader[#bmpheader + 1] = bmp_color(0x0)
 | |
|         elseif depth == 16 then
 | |
|             if format == 555 then
 | |
|                 -- red bitfield mask
 | |
|                 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x00007C00)
 | |
|                 -- green bitfield mask
 | |
|                 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000003E0)
 | |
|                 -- blue bitfield mask
 | |
|                 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
 | |
| 			else --565
 | |
|                 -- red bitfield mask
 | |
|                 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000F800)
 | |
|                 -- green bitfield mask
 | |
|                 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x000007E0)
 | |
|                 -- blue bitfield mask
 | |
|                 bmpheader[#bmpheader + 1] = s_bytesLE(32, 0x0000001F)
 | |
|             end
 | |
|         end
 | |
| 
 | |
|         file:write(table.concat(fbuffer))-- write the header to the file now
 | |
|         for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
 | |
| 
 | |
|         local imgdata = fbuffer
 | |
|         -- pad rows to a multiple of 4 bytes
 | |
|         local bytesleft = linebytes - (bytesperpixel * w)
 | |
|         local t_data = {}
 | |
|         local fs_bytes_E = s_bytesLE -- default save in Little Endian
 | |
| 
 | |
|         if format == 3553 then -- RGB565SWAPPED
 | |
|             fs_bytes_E = s_bytesBE -- Saves in Big Endian
 | |
|         end
 | |
| 
 | |
|         -- Bitmap lines start at bottom unless biHeight is negative
 | |
|         for point in _points(img, 1, h, w + bytesleft, 1) do
 | |
|             imgdata[#imgdata + 1] = fs_bytes_E(bpp, point or 0)
 | |
| 
 | |
|             if #fbuffer >= 31 then -- buffered write, increase # for performance
 | |
|                 file:write(table.concat(fbuffer))
 | |
|                 for i=1, #fbuffer do fbuffer[i] = _NIL end -- reuse table
 | |
|             end
 | |
| 
 | |
|         end
 | |
|         file:write(table.concat(fbuffer)) --write leftovers to file
 | |
|         fbuffer = _NIL
 | |
| 
 | |
|         file:close()
 | |
|     end -- save(img, name)
 | |
| end
 |