Created
April 4, 2019 17:15
-
-
Save max1220/0346199b0386b2b87f9f00688412fb72 to your computer and use it in GitHub Desktop.
Unicode Braile character graphics for Lua!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
This library converts pixels into a sequence of utf8 braile characters, | |
for displaying higher-resolution graphics on terminal emulators. | |
It features 3 functions to turn pixel data into braile characters. | |
All of them return a list of lines, and use the draw_pixel_callback | |
function internally. | |
Braile.draw_pixel_callback | |
--------------------------- | |
Braile.draw_pixel_callback(width, height, pixel_callback, color_callback) | |
pixel_callback(x,y) --> 1 if pixel is set, 0 otherwise | |
color_callback(x,y) --> ANSI color code(string) | |
Calls the pixel_callback for each pixel in the specified dimensions to | |
set the braile character bits for each character. pixel_callback is | |
called with the pixel coordinates requested, and should return 1 if the | |
pixel is set, 0 otherwise. | |
If color_callback if set, also calls color_callback for each character | |
generated with the coordinates of the top-left pixel of that character. | |
color_callback should return a ANSI escape sequence that sets the | |
foreground/background color of the terminal to the color of the pixel. | |
The color codes can be generated using the helper functions | |
Braile.get_color_code_fg(r,g,b) | |
Braile.get_color_code_bg(r,g,b) | |
These functions take a r,g,b value in the range [0, 255] and generate an | |
ANSI escape sequence that changes the foreground/background color of | |
the current terminal. Currently, it uses a 216-color escape sequence for | |
compabillity, but changing to 24-bit color is possible for some terminal | |
emulators by swapping out the get_color_code_fg and get_color_code_bg | |
functions with ones that support 24-bit color. | |
Braile.draw_db and Braile.draw_table use these functions internally. | |
Braile.draw_db | |
--------------- | |
Braile.draw_db(db, threshold, color) | |
Converts the lua-db drawbuffer to a braile character sequence. | |
A pixel is considered set if the average of the r,g,b values is above | |
the specified threshold. If colors is true, the output will also be | |
colored as described above. | |
Braile.draw_table | |
----------------------- | |
Braile.draw_table(tbl) | |
Iterates over the table tbl to draw an image in braile characters. | |
The width drawn is either the lenght of the first line or | |
tbl.width(if present), and the height the ammount of lines or | |
tbl.height. The table can be arbitrarily sparse, pixels not present in | |
the table are considered not set. | |
tbl should contain a list of lines so that each line contains a list of | |
pixel/color values, so that you could get the pixel information by: | |
local px, r, g, b = unpack(tbl[y][x]) | |
If the r,g,b values are present, the character at that position will be | |
colored. | |
]] | |
local utf8 = require("utf8") | |
local Braile = {} | |
-- get an ANSI escape sequence for setting the foreground color(r,g,b: 0-255) | |
function Braile.get_color_code_fg(r, g, b) | |
local _r = math.floor((r/255)*5) | |
local _g = math.floor((g/255)*5) | |
local _b = math.floor((b/255)*5) | |
-- 216-color index | |
local color_code = 16 + 36*_r + 6*_g + _b | |
-- set foreground color ANSI escape sequence | |
local fg = "\027[38;5;"..color_code.."m" | |
return fg | |
end | |
-- get an ANSI escape sequence for setting the background color(r,g,b: 0-255) | |
function Braile.get_color_code_bg(r, g, b) | |
-- 26-greyscale index(used as bg color) | |
local grey_level = 231 + math.floor(((r+g+b)/768)*26) | |
if grey_level == 0 then | |
-- black background | |
return "\027[48;5;0m" | |
elseif grey_level == 26 then | |
-- white background | |
return "\027[48;5;15m" | |
else | |
-- greyscale background | |
return "\027[48;5;" .. grey_level .. "m" | |
end | |
end | |
-- convert the set bits to a utf8 character sequence | |
function Braile.get_chars(bits) | |
-- braile characters start at unicode 0x2800 | |
return utf8.char(bits + 0x2800) | |
end | |
-- draw using a pixel callback (and optional color_callback) | |
-- the pixel callback is called for every pixel(8x per character), takes an x,y coordinate, and should return 1 if the pixel is set, 0 otherwise | |
-- the color callback is called for every character, takes an x,y coordinate, and should return an ANSI escape sequence to set the foreground/background color | |
function Braile.draw_pixel_callback(width, height, pixel_callback, color_callback) | |
local chars_x = math.ceil(width/2)-1 | |
local chars_y = math.ceil(height/4)-1 | |
-- iterate over every character that should be generated | |
local lines = {} | |
for y=0, chars_y do | |
local cline = {} | |
for x=0, chars_x do | |
local rx = x*2 | |
local ry = y*4 | |
local char_num = 0 | |
-- left 3 | |
char_num = char_num + pixel_callback(rx+0, ry+0) | |
char_num = char_num + pixel_callback(rx+0, ry+1)*2 | |
char_num = char_num + pixel_callback(rx+0, ry+2)*4 | |
--right 3 | |
char_num = char_num + pixel_callback(rx+1, ry+0)*8 | |
char_num = char_num + pixel_callback(rx+1, ry+1)*16 | |
char_num = char_num + pixel_callback(rx+1, ry+2)*32 | |
--bottom 2 | |
char_num = char_num + pixel_callback(rx+0, ry+3)*64 | |
char_num = char_num + pixel_callback(rx+1, ry+3)*128 | |
if color_callback then | |
local color_code = color_callback(rx, ry) | |
table.insert(cline, color_code) | |
end | |
if char_num == 0 then | |
--empty char, use space | |
table.insert(cline, " ") | |
else | |
-- generate a utf8 character sequence for the braile code | |
local chars = Braile.get_chars(char_num) | |
table.insert(cline, chars) | |
end | |
end | |
table.insert(lines, table.concat(cline)) | |
end | |
return lines | |
end | |
-- draw using a lfb/lua-db drawbuffer(uses draw_pixel_callback) | |
function Braile.draw_db(db, threshold, color) | |
-- get a boolean pixel value from the drawbuffer for the braile chars | |
local function pixel_callback(x, y) | |
local r,g,b,a = db:get_pixel(x,y) | |
if a > 0 then | |
local avg = (r+g+b)/3 | |
if avg > threshold then | |
return 1 | |
end | |
end | |
return 0 | |
end | |
-- get foreground/background color codes from the drawbuffer | |
local function color_callback(x, y) | |
local r,g,b = db:get_pixel(x,y) | |
local fg_code = Braile.get_color_code_fg(r,g,b) | |
-- the background color is offset to encode more colors from the source drawbuffer | |
r,g,b = db:get_pixel(x+1,y+2) | |
local bg_code = Braile.get_color_code_bg(r,g,b) | |
return fg_code .. bg_code | |
end | |
local width = db:width() | |
local height = db:height() | |
if color then | |
return Braile.draw_pixel_callback(width, height, pixel_callback, color_callback) | |
else | |
return Braile.draw_pixel_callback(width, height, pixel_callback) | |
end | |
end | |
-- draw from a table containing pixel(and color) information | |
function Braile.draw_table(tbl) | |
-- get a boolean pixel value from the table for the braile chars | |
local function pixel_callback(x, y) | |
if tbl[y+1] and tbl[y+1][x+1] then | |
local px = unpack(tbl[y+1][x+1]) | |
return px | |
end | |
return 0 | |
end | |
-- get foreground/background color codes from the drawbuffer | |
local function color_callback(x, y) | |
local px,r,g,b = unpack(tbl[y+1][x+1]) | |
if r and g and b then | |
local fg_code = Braile.get_color_code_fg(r,g,b) | |
local bg_code = Braile.get_color_code_bg(r,g,b) | |
return fg_code .. bg_code | |
end | |
return "" | |
end | |
local width = tbl.width or #tbl[1] | |
local height = tbl.height or #tbl | |
return Braile.draw_pixel_callback(width, height, color_callback) | |
end | |
return Braile |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment