-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SIXEL support #86
Comments
Proof of conceptDon't get too excited, I don't have a lot of free time to work on it (money would help) |
Actually I should use uberzug++ |
You know what... I want to stay original, so... I replaced alacritty with alacritty-sixel-branch and I'll start expirimenting with real sixels instead of trying the work-around uberzug method. |
👁️ PreviewNice... we have some progress. After some intense pitfalls, research, trial and error, I finally figured out how to print actual sixels inside ncurses, without uberzug. (Preview Might seem as a joke, but (just so you know) it is a huge progress!) 👨💻 CodeHere's how i managed to do so: Click to expandfrom io import StringIO
import unicurses as uc
from PIL import Image
class SixelConverter: # https://github.com/lubosz/python-sixel
def __init__(self, f8bit=False, ncolor=256):
self._slots = [0] * 257
self._ncolor = ncolor
if f8bit: # 8bit mode
self.DCS = '\x90'
self.ST = '\x9c'
else:
self.DCS = '\x1bP'
self.ST = '\x1b\\'
def __write_header(self):
# start Device Control String (DCS)
uc.putp(self.DCS)
# write header
aspect_ratio = 7 # means 1:1
background_option = 0
# background_option = 1
dpi = 75 # dummy value
template = '%d;%d;%dq"1;1;%d;%d'
args = (aspect_ratio, background_option, dpi, self.width, self.height)
uc.putp(template % args)
def __write_palette_section(self):
palette = self.palette
# write palette section
for i in range(0, self._ncolor * 3, 3):
no = i / 3
r = palette[i + 0] * 100 / 256
g = palette[i + 1] * 100 / 256
b = palette[i + 2] * 100 / 256
uc.putp('#%d;2;%d;%d;%d' % (no, r, g, b))
def __write_body_without_alpha_threshold_fast(self, data ):
height = self.height
width = self.width
n = 1
for y in range(0, height):
p = y * width
cached_no = data[p]
count = 1
c = -1
for x in range(0, width):
color_no = data[p + x]
if color_no == cached_no: # and count < 255:
count += 1
else:
if cached_no == -1 :
c = 0x3f
else:
c = 0x3f + n
if self._slots[cached_no] == 0:
palette = self.palette
r = palette[cached_no * 3 + 0] * 100 / 256
g = palette[cached_no * 3 + 1] * 100 / 256
b = palette[cached_no * 3 + 2] * 100 / 256
self._slots[cached_no] = 1
uc.putp('#%d;2;%d;%d;%d' % (cached_no, r, g, b))
uc.putp('#%d' % cached_no)
if count < 3:
uc.putp(chr(c) * count)
else:
uc.putp('!%d%c' % (count, c))
count = 1
cached_no = color_no
if c != -1 and count > 1:
if cached_no == -1 :
c = 0x3f
else:
if self._slots[cached_no] == 0:
palette = self.palette
r = palette[cached_no * 3 + 0] * 100 / 256
g = palette[cached_no * 3 + 1] * 100 / 256
b = palette[cached_no * 3 + 2] * 100 / 256
self._slots[cached_no] = 1
uc.putp('#%d;2;%d;%d;%d' % (cached_no, r, g, b))
uc.putp('#%d' % cached_no)
if count < 3:
uc.putp(chr(c) * count)
else:
uc.putp('!%d%c' % (count, c))
if n == 32:
n = 1
uc.putp('-') # write sixel line separator
else:
n <<= 1
uc.putp('$') # write line terminator
def __write_body_section(self):
data = self.data
self.__write_body_without_alpha_threshold_fast(data)
def __write_terminator(self):
# write ST
uc.putp(self.ST) # terminate Device Control String
def getvalue(self):
output = StringIO()
try:
self.write(output)
value = output.getvalue()
finally:
output.close()
return value
def write(self, file, w=None, h=None,):
self._slots = [0] * 257
image = Image.open(file)
image = image.convert("RGBA" if image.format == "PNG" else "RGB").convert("P",
palette=Image.Palette.ADAPTIVE,
colors=self._ncolor)
if w or h:
width, height = image.size
if not w:
w = width
if not h:
h = height
image = image.resize((w, h))
self.palette = image.getpalette()
self.data = image.getdata()
self.width, self.height = image.size
self.__write_header()
self.__write_body_section()
self.__write_terminator()
uc.putp('\n')
stdscr = uc.initscr() # Global UniCurses Variable
event = -1
uc.start_color ( )
uc.cbreak ( )
uc.noecho ( )
uc.curs_set (0)
uc.mouseinterval(0) # Initializing Mouse and then Update/refresh() stdscr
uc.mousemask (uc.ALL_MOUSE_EVENTS | uc.REPORT_MOUSE_POSITION) # print("\033[?1003h\n")
uc.keypad (stdscr, True )
uc.nodelay (stdscr, False)
uc.raw()
uc.move(3,7)
uc.refresh ( )
HEIGHT,WIDTH = uc.getmaxyx(stdscr)
c = SixelConverter()
i = 1
while event != 27 : # Main loop
event = uc.get_wch()
if event == uc.KEY_MOUSE:
uc.clear()
continue
uc.lib1.mvcur(HEIGHT-1, WIDTH-1, 5,i)
c.write("/home/xou/Downloads/me.png", h=96,w=96)
i+=1
if event == uc.CCHAR('a'):
uc.mvaddstr(i,10, 'adfabfea')
# uc.lib2.puts(uc.CSTR("\x1bPq#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0#1~~@@vv@@~~@@~~$#2??}}GG}}??}}??-#1!14@\x1b\\" ))
uc.refresh()
if event == uc.KEY_RESIZE:
uc.resize_term(0,0)
uc.endwin()
# # print("\x1bPq#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0#1~~@@vv@@~~@@~~$#2??}}GG}}??}}??-#1!14@\x1b\\")
# c = SixelConverter()
# c.write("/home/xou/Downloads/me.png", h=160,w=160)
# c.write("/home/xou/Downloads/image.jpg", h=160,w=560)
# c.write("/home/xou/Downloads/Afisa.jpg", h=60,w=150) 🌐 ResearchSixel and curses related subjects related to:
|
it works as expected on alacritty-sixel-branch and wezterm but not on xterm for some reason (under ncurses) |
Ok it actually works just fine with |
So I think I found out how to display sixels properly with ueberzug
Preview
Flexing a picture I took of a wasp, found on top of a harvesting net for olives with the 2017
Xiaomi Redmi 5A
Minimal example
The text was updated successfully, but these errors were encountered: