Skip to content

Instantly share code, notes, and snippets.

@max1220
Created April 4, 2019 17:15
Show Gist options
  • Save max1220/0346199b0386b2b87f9f00688412fb72 to your computer and use it in GitHub Desktop.
Save max1220/0346199b0386b2b87f9f00688412fb72 to your computer and use it in GitHub Desktop.
Unicode Braile character graphics for Lua!
--[[
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