atanunq / viuer Goto Github PK
View Code? Open in Web Editor NEWRust library for displaying images in the terminal.
License: MIT License
Rust library for displaying images in the terminal.
License: MIT License
This crate depends on ansi_colours
, which is licensed under the LGPL-3.0-or-later.
I think it's confusing that an MIT-licensed crate has copyleft dependency.
$ printf '\033[c'
$ 12;4c
i am using st-flexipatch
which can have sixel support, however viuer and its image viewer app viu
refuses to use sixel. convert file sixel:-
works fine.
Despite the fact that iTerm
's image escape sequences and indeed its imgcat script seems to work inside tmux
, viu
will fall back to half-block rendering.
TERM_PROGRAM
is equal to tmux
inside tmux
but exporting TERM_PROGRAM=iTerm.app
manually doesn't seem to help either; this results in nothing being printed at all.
Is this something that was/can be considered inside viuer
?
Edit:
quoting the imgcat script:
# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux;
# <sequence> ST, and for all ESCs in <sequence> to be replaced with ESC ESC. It
# only accepts ESC backslash for ST. We use TERM instead of TMUX because TERM
# gets passed through ssh.
function print_osc() {
if [[ $TERM == screen* ]]; then
printf "\033Ptmux;\033\033]"
else
printf "\033]"
fi
}
The modern MacOS terminal emulator iTerm2 natively supports high resolution images through a custom protocol. viuer
could check if it is being invoked in iTerm and use this way of printing images instead of half-blocks.
To do that, the print method could be extended to check if the terminal is iTerm. The new printer would live in an iterm.rs
file that has a iTermPrinter
struct, implementing the Printer
trait.
There is an unpublished rust crate that might be an inspiration, called iterm2.
Sixel graphics are a great way to display images in high resolution, given that the terminal supports it. libsixel has Rust bindings that could be used to bring the functionality to viuer
, see on crates.io.
The way to go about it would be to check for sixel compatibility in the main print method. If that is the case, call SixelPrinter
, which lives in sixel.rs
and implements the Printer
trait.
Just like with Kitty, rabite0's hunter has a sample implementation which could be extended.
I posted this issue initially at: atanunq/viu#65
Comparing this source with imgcat
's source makes it clear that the issue is viuer is attempting to manually calculate image size, which does not take into account iTerm2
s specific line height/etc settings.
A simple fix to this is to simply not print width={};height={}
when no width and height are specified, and allow iTerm2 to instead automatically calculate those.
I'm working on a PR right now for this.
Most of the terminals don't support kitty and iTerm protocols. For this reason, many applications(Including ranger) use ueberzug as a way to draw images into the terminal. While this way of displaying images is a bit hacky, it allows us to display full-color full resolution images in almost every common terminal emulator.
It looks like a license was never added when this was extracted from viu
, which is under MIT. This should probably be rectified.
The Terminology terminal has its own custom protocol for displaying images from files. It is very straightforward to implement, the documentation is available in their README and I even wrote a C implementation for it.
(I started the imgt project before I found that viu exists, and now I've basically abandoned it. It would be nice to see a Terminology implementation here instead)
Oh, and Terminology also has the ability to send image files over SSH, but I never tested that. It might require some investigation.
When setting the use_kitty:
config option for viuer to true
and attempting to display an image, the program will hang (seemingly indefinitely) and must be forcibly killed. No image is displayed. Setting this option to false
allows everything to work as expected, albeit with blocks instead of a proper Kitty image. This is almost certainly a bug in my program, although I can't imagine what would cause such behavior and if anyone has any ideas I'd appreciate it :D
I'm using viuer to display album cover. One use met a problem though, as in
tramhao/termusic#92 .
Would you please check if it's I'm using viuer wrong or you need to fix in upstream? Thanks so much.
Currently, viuer
suffers from the following security advisories:
$ cargo audit
Crate: regex
Version: 0.1.80
Title: Regexes with large repetitions on empty sub-expressions take a very long time to parse
Date: 2022-03-08
ID: RUSTSEC-2022-0013
URL: https://rustsec.org/advisories/RUSTSEC-2022-0013
Solution: Upgrade to >=1.5.5
Dependency tree:
regex 0.1.80
└── semver-parser 0.6.2
└── sixel 0.3.2
└── viuer 0.6.1
Crate: thread_local
Version: 0.2.7
Title: Data race in `Iter` and `IterMut`
Date: 2022-01-23
ID: RUSTSEC-2022-0006
URL: https://rustsec.org/advisories/RUSTSEC-2022-0006
Solution: Upgrade to >=1.1.4
Dependency tree:
thread_local 0.2.7
└── regex 0.1.80
└── semver-parser 0.6.2
└── sixel 0.3.2
└── viuer 0.6.1
Both of these crates are reverse dependencies of the sixel crate which is outdated and unmaintained.
I looked at the codebase of sixel
and realized these dependencies are not needed and removing them would fix these security issues. While I'm at it, I decided to maintain a fork of sixel
since the maintainer is not that active on GitHub + they disabled issues on the repository.
That's why I created orhun/sixel-rs and I will be submitting a PR to switch to this crate. See the changelog here.
Please update image dependency to 0.25
There are a number of block characters in Unicode:
Viuer currently only supports half blocks. It would be nice if it could also use quadrants and sextants to show images in higher resolution at the expense of color accuracy.
hi, i simply want to request for support of rendering colored braille characters.
this is primarily useful for users of st
with the boxdraw
patch, as braille is able to be renderered as pixels.
this will be a bit similar to the 'blocks' rendering, but using braille characters.
I'm trying to use the library for a terminal game, but I've come across a few snags against using it, such as #44. But since I'm controlling the screen instead of just outputting things, I also need the ability to delete images displayed using the kitty protocol.
It would be useful a feature (maybe activable with an option) to not stretch image on terminals supporting the image protocol.
For example the following code works for kitty:
diff --git a/Cargo.toml b/Cargo.toml
index 72bfd35..58da970 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,6 +22,7 @@ tempfile = "3.1"
console = { version = "0.15", default-features = false }
lazy_static = "1.4"
sixel-sys = { version = "0.3.1", optional = true }
+nix = {version = "0.25.0", default-feature = false, features = [ "ioctl" ]}
# avoid feature and crate name collision (thanks rabite0/hunter)
[dependencies.sixel-rs]
diff --git a/src/printer/kitty.rs b/src/printer/kitty.rs
index b950254..a2e2cfe 100644
--- a/src/printer/kitty.rs
+++ b/src/printer/kitty.rs
@@ -122,22 +122,48 @@ fn print_local(
img: &image::DynamicImage,
config: &Config,
) -> ViuResult<(u32, u32)> {
- let rgba = img.to_rgba8();
- let raw_img = rgba.as_raw();
- let path = store_in_tmp_file(raw_img)?;
-
adjust_offset(stdout, config)?;
// get the desired width and height
let (w, h) = find_best_fit(img, config.width, config.height);
+ use nix::ioctl_read_bad;
+ use nix::libc::{c_ushort, TIOCGWINSZ};
+
+ #[repr(C)]
+ #[derive(Default, Debug)]
+ pub struct TermSize {
+ rows: c_ushort,
+ columns: c_ushort,
+ x: c_ushort,
+ y: c_ushort,
+ }
+
+ ioctl_read_bad!(
+ /// Get terminal window size
+ tiocgwinsz,
+ TIOCGWINSZ,
+ TermSize
+ );
+
+ let mut tsize = TermSize::default();
+ unsafe { tiocgwinsz(0, &mut tsize as *mut _).expect("Cannot retrive kitty size") };
+
+ let img = img.resize(
+ (w as f64 * tsize.x as f64 / tsize.columns as f64) as u32,
+ (h as f64 * tsize.y as f64 / tsize.rows as f64) as u32,
+ image::imageops::FilterType::Triangle
+ );
+
+ let rgba = img.to_rgba8();
+ let raw_img = rgba.as_raw();
+ let path = store_in_tmp_file(raw_img)?;
+
write!(
stdout,
- "\x1b_Gf=32,s={},v={},c={},r={},a=T,t=t;{}\x1b\\",
+ "\x1b_Gf=32,s={},v={},a=T,t=t;{}\x1b\\",
img.width(),
img.height(),
- w,
- h,
base64::encode(path.to_str().ok_or_else(|| ViuError::Io(Error::new(
ErrorKind::Other,
"Could not convert path to &str"
Instead of telling kitty to stretch the image with the parameters c and r (number of columns and rows)of its graphics protocol, this code resizes the image (retaining aspect ratio) in a way it fits the desired number of rows and columns (bound width and height are computed multiplying the number of columns/rows by the ratio of size in pixel to the number of cells).
I attach an example to understand the importance of this feature: on the left the output of viu
in kitty, on the right the output of the following code with the previous patch:
use viuer::{Config, print_from_file};
fn main() {
let conf = Config {
absolute_offset: false,
..Default::default()
};
print_from_file("sway.png", &conf).expect("Image printing failed.");
}
It can be seen how without this feature the image is unnaturally stretched; this is required for block printer, but easily avoidable for other printers.
Maybe there are also solutions better than the one I suggested.
When placing the terminal into raw mode (through Termion, etc.), each pair of rows will shift over such that the start of every new row is the x offset away from the previous row's end, resembling a stair pattern. From what I can see on the outside, it looks like it's just using a line break and expecting that it will also be properly returned to the beginning of the line after, which would not be the case in raw mode. I'm not experienced in Rust enough to personally confirm this, though. It'd be helpful if this behavior could be altered, perhaps through the Config struct.
This library does not recognize that Konsole has sixel support.
Related Issue: aome510/spotify-player#202
Output of echo $TERM
: xterm-256color
Firstly, thanks for your work and it provides very good display result in kitty.
I'm using it in my repo(https://github.com/tramhao/termusic) to display the album photo.
The display works without problems. However, i need to clear the printed image if the song doesn't have an album photo.
I cannot figure out how to do it.
Thanks so much.
I want to bundle my images with executable.
This is the code I used and it fails compilation with error
expected enum `image::dynimage::DynamicImage`, found array `[u8; 23307]
use viuer::{Config, print_from_file, print};
let conf = Config {
transparent: true,
absolute_offset: false,
..Default::default()
};
let img = include_bytes!("image.png");
print(img, &conf);
How could I convert bytes to DynamicImage
?
In my opinion, your lib would benefit from a method like the following
pub fn image_to_string<R: Read>(from: R) -> Result<String, ViuError>
Disclosure:
I am interested in using this lib for tests of library https://crates.io/crates/bmp-monochrome
The library had a bug RCasatta/bmp-monochrome#2 that wasn't catch because it was on both encode and decode method, so I thought the only way to catch this kind of bug is introducing external image library in tests.
For instance (you need to pip install pillow
to run this):
#! /usr/bin/env python3
from PIL import Image
import subprocess, io, os
solid = Image.new('RGBA', (50,50), (0, 0, 0, 255))
t_red = Image.new('RGBA', (50,50), (255, 0, 0, 0))
combined = Image.new('RGBA', (100, 50))
combined.paste(solid, box=(0,0))
combined.paste(t_red, box=(solid.size[0],0))
with io.BytesIO() as f:
combined.save(f, format='png')
subprocess.run(['viu', '-t', '-'], input=f.getbuffer())
#if the terminal is small enough that the combined image fills the width this will show the border
size = os.get_terminal_size()
print('*'*int(size[0]/2))
I believe this is a of by one bug in printing transparent pixels. Sometimes the border appears 'darker' depending on size, and that's interpolation (probably on a odd total width and a small enough terminal width), but the red must not be interpolation because with a small enough terminal, the midpoint of the terminal width coincides with the last star, and with the end of the first image, and those stars stop before the red line.
FWIW chafa when it needs to 'interpolate' doesn't print a darker red line, but it does print some odd spikes ('-') at the border. Your solution is better +/- this bug.
This is on a terminal without kitty protocol or sixel support of course.
Currently, if you want to pass a Path
to viuer::print_from_file
, you have to convert it to a &str
:
let path = Path::new("some/path/foo.png");
print_from_file(path.to_str().unwrap(), &Default::default());
This isn't very ergonomic, and if your path contains invalid UTF-8, you can't use it all. Since image::io::Reader::open
already takes an AsRef<Path>
, I don't see any technical reason for this limitation. Additionally, str
already implements AsRef<Path>
, so I don't think this would be a breaking change. I'm happy to submit a pull request implementing this change, if it's wanted.
Kitty has a custom way of displaying full resolution images. It would be nice to extend the main print method to check if the terminal is kitty, and if so, use a different printer. The new printer would live in a kitty.rs
file that has a KittyPrinter
struct, implementing the Printer
trait.
rabite0's hunter has a sample implementation which could be extended.
kitty added native animated images feature in 0.20.0: kovidgoyal/kitty#3287
Would love to see support for it landed in viuer as well.
Currently viuer dumbly checks iTerm
in TERM_PROGRAM
. However there are a handful of terminal emulators that support the OSC 1337 escape codes, i.e. iTerm2's image protocols:
There should be a better method of checking support of the protocol.
A workaround is just to fake iTerm to viuer.
I tried using viu
as backend for vifm
to preview images but found out weird behavior (vifm/vifm#814). After some searching in the code i found out that the problem originates from https://github.com/atanunq/viuer/blob/master/src/printer/kitty.rs#L72. Sadly I don't know enough about the Kitty image protocol or similar to fix it properly myself. For my local setup I will probably just add return Ok(());
Yeah, i know it sounds entitled. I suppose this can be done with maturin but i'm not one to do it, i don't know rust well and ffi is supposed to be harder.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.