Comments (6)
👁️ Preview
Nice... 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!)
👨💻 Code
Here's how i managed to do so:
Click to expand
from 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)
🌐 Research
Sixel and curses related subjects related to:
- General:
- Terminals:
-
- Alacritty:
- NCurses:
-
- Random:
- Python:
-
- Random:
- Projects:
- Other:
from tuifimanager.
Proof of concept
Don't get too excited, I don't have a lot of free time to work on it (money would help)
from tuifimanager.
Actually I should use uberzug++
from tuifimanager.
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.
from tuifimanager.
it works as expected on alacritty-sixel-branch and wezterm but not on xterm for some reason (under ncurses)
from tuifimanager.
Ok it actually works just fine with xterm -ti vt340
. also this video
from tuifimanager.
Related Issues (20)
- Opening wrong file if mouse moved fast enough to another before next click
- Renaming parent ".."-folder results in crush
- CHANGELOG.md is outdated
- Overlapping icons in search mode when mouse previously positioned over a file is moved.
- [Errno 13] Permission denied | on not R_OK folders
- User-Command `open`-file | `NoneType`-error due to unnecessary call to `__set_label_on_file_selection`
- Image view HOT 1
- SyntaxWarning: invalid escape sequence '\.' python 3.12. HOT 6
- Overlaping icons - toggling hidden files when previously selected
- Selected files get opened instead of jumping to a directory when command
- 66 HOT 1
- Exit with `cd` to current directory in Windows HOT 3
- Entering a folder while searching results to opening it in edditor
- Asynchronous handling of file operations, using a stack. HOT 2
- Mouse issues when suspending tuifi
- Deselect error when dragging file immediately after navigating to parrent folder
- Deleting multiple selected files from right to left, results in IndexError HOT 1
- Deselect error when dragging file inside a folder immediately after the creation+naming of the folder
- Error dropping folder into itself when present in multiple section.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tuifimanager.