Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbc34345a1 | ||
|
|
53dff6f5b9 | ||
|
|
12089c098a | ||
|
|
1cdfc4d9db | ||
|
|
128e106b82 | ||
|
|
5ef1f70a3a | ||
|
|
f055e5b675 | ||
|
|
d749d51042 | ||
|
|
cd2805d33d | ||
|
|
7e5b9d94ee | ||
|
|
0973fbd76c | ||
|
|
419056506f | ||
|
|
f97c6e9eb0 | ||
|
|
06467c38d8 | ||
|
|
6b80bc0717 | ||
|
|
69f57a4730 | ||
|
|
b58820e647 | ||
|
|
3e57ebb80c | ||
|
|
28e9f5e40e | ||
|
|
9717adc840 | ||
|
|
543cebef37 | ||
|
|
aef2fbccdc | ||
|
|
9734ff585d | ||
|
|
9943deed2c | ||
|
|
287fde3341 | ||
|
|
949f72ffa9 | ||
|
|
f80071243f | ||
|
|
34dd22e1e8 | ||
|
|
b27e359e4f | ||
|
|
cca06fdaca | ||
|
|
deab25bf68 | ||
|
|
37ceba3e0b | ||
|
|
ed158d6ba9 | ||
|
|
dd92c03299 | ||
|
|
073b76d9b4 | ||
|
|
141205e83e | ||
|
|
4a1a82a5c0 | ||
|
|
b66e502782 | ||
|
|
40cd46ea4e | ||
|
|
945502ff76 | ||
|
|
4b116332f7 | ||
|
|
c7c05bba79 |
15
.gitignore
vendored
15
.gitignore
vendored
@@ -91,4 +91,17 @@ Icon
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
.apdisk
|
||||
|
||||
# Intellij
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
|
||||
###Snap###
|
||||
|
||||
parts/
|
||||
prime/
|
||||
snap/
|
||||
stage/
|
||||
*.snap
|
||||
|
||||
16
README.md
16
README.md
@@ -13,14 +13,27 @@ For each 4x8 pixel cell of the (potentially downscaled) image:
|
||||
|
||||
See the difference by disabling this optimization using the `-0` option. Or just take a look at the comparison image at the end of this text.
|
||||
|
||||
## News
|
||||
|
||||
- 2019-03-26: Exciting week: @Cableo has fixed output redirection, @boretom has added cross-compilation support to the build file and @AlanDeSmet has fixed tall thumbnails and greyscale images.
|
||||
- 2019-01-14: Install via snap: `sudo snap install --edge tiv`
|
||||
|
||||
## Installation
|
||||
|
||||
### Snap
|
||||
|
||||
sudo snap install --edge tiv
|
||||
|
||||
### Build from source
|
||||
|
||||
sudo apt install imagemagick || yum install ImageMagick
|
||||
git clone https://github.com/stefanhaustein/TerminalImageViewer.git
|
||||
cd TerminalImageViewer/src/main/cpp
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Note: On MacOS, you'll need to install GCC because of this issue: https://stackoverflow.com/q/42633477. Find some more details here: https://github.com/stefanhaustein/TerminalImageViewer/issues/36
|
||||
|
||||
## Usage
|
||||
|
||||
tiv [options] <filename(s)>
|
||||
@@ -33,11 +46,14 @@ The shell will expand wildcards. By default, thumbnails and file names will be d
|
||||
https://build.opensuse.org/package/show/home:megamaced/terminalimageviewer
|
||||
- bperel has created a Docker image:
|
||||
https://hub.docker.com/r/bperel/terminalimageviewer
|
||||
- teresaejunior has created a snapcraft.yaml file, which can build a Snap package with `sudo docker run -it --rm -v "$PWD:$PWD" -w "$PWD" snapcore/snapcraft sh -c 'apt-get update && snapcraft'`, and then installed with `sudo snap install --dangerous ./*.snap`.
|
||||
|
||||
## Common problems
|
||||
|
||||
- If you see strange horizontal lines, the characters don't fully fill the character cell. Remove additional line spacing in your terminal app
|
||||
- Wrong colors? Try -256 to use a 256 color palette instead of 24 bit colors
|
||||
- Strange characters? Try -0 or install an use full unicode font (e.g. inconsolata or firacode)
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
73
snapcraft.yaml
Normal file
73
snapcraft.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
name: tiv
|
||||
version: "v1.0.0"
|
||||
summary: Terminal Image Viewer
|
||||
description: |
|
||||
tiv is a small C++ program to display images in a (modern) terminal using
|
||||
RGB ANSI codes and unicode block graphic characters.
|
||||
|
||||
apps:
|
||||
tiv:
|
||||
command: usr/bin/tiv
|
||||
plugs: [home]
|
||||
|
||||
parts:
|
||||
tiv:
|
||||
plugin: make
|
||||
source-type: tar
|
||||
source: https://github.com/stefanhaustein/TerminalImageViewer/archive/v1.0.0.tar.gz
|
||||
source-subdir: src/main/cpp
|
||||
build-packages:
|
||||
- g++
|
||||
- make
|
||||
- imagemagick
|
||||
override-build: |
|
||||
snapcraftctl build
|
||||
mkdir -p $SNAPCRAFT_PART_INSTALL/usr/bin/
|
||||
install $SNAPCRAFT_PART_BUILD/tiv $SNAPCRAFT_PART_INSTALL/usr/bin/
|
||||
imagemagick:
|
||||
plugin: autotools
|
||||
source: https://www.imagemagick.org/download/releases/ImageMagick-7.0.8-23.tar.xz
|
||||
source-type: tar
|
||||
configflags:
|
||||
- --enable-hdri=yes
|
||||
- --enable-shared=yes
|
||||
- --enable-static=yes
|
||||
- --with-autotrace=yes
|
||||
- --with-fpx=no
|
||||
- --with-gnu-ld=yes
|
||||
- --with-gslib=yes
|
||||
- --with-modules=no
|
||||
- --with-quantum-depth=32
|
||||
- --with-rsvg=yes
|
||||
build-packages:
|
||||
- autoconf
|
||||
- build-essential
|
||||
- fftw-dev
|
||||
- libautotrace-dev
|
||||
- libbz2-dev
|
||||
- libdjvulibre-dev
|
||||
- libfftw3-dev
|
||||
- libfontconfig1-dev
|
||||
- libfreetype6-dev
|
||||
- libgs-dev
|
||||
- libgvc6
|
||||
- libjbig-dev
|
||||
- libjpeg-dev
|
||||
- liblcms2-dev
|
||||
- liblqr-1-0-dev
|
||||
- libltdl-dev
|
||||
- libmagick++-dev
|
||||
- libopenexr-dev
|
||||
- libopenjp2-7-dev
|
||||
- libpango1.0-dev
|
||||
- libperl-dev
|
||||
- libpng12-dev
|
||||
- librsvg2-dev
|
||||
- libtiff5-dev
|
||||
- libwebp-dev
|
||||
- libwmf-dev
|
||||
- libx11-dev
|
||||
- lzma-dev
|
||||
- ocl-icd-opencl-dev
|
||||
- perlmagick
|
||||
- zlib1g-dev
|
||||
@@ -1,15 +1,22 @@
|
||||
CXX=g++
|
||||
# set CXX to g++ if not set
|
||||
CXX ?= g++
|
||||
|
||||
default: tiv
|
||||
# append necessary arguments
|
||||
override CPPFLAGS += -std=c++17 -Wall -fpermissive -fexceptions -O2
|
||||
override LDFLAGS += -lstdc++fs -pthread -s
|
||||
|
||||
all: tiv
|
||||
|
||||
tiv.o: tiv.cpp CImg.h
|
||||
$(CXX) -std=c++17 -Wall -fpermissive -fexceptions -O2 -c tiv.cpp -o tiv.o
|
||||
$(CXX) $(CPPFLAGS) -c tiv.cpp -o $@
|
||||
|
||||
tiv : tiv.o
|
||||
$(CXX) tiv.o -o tiv -lstdc++fs -pthread -s
|
||||
$(CXX) $^ -o $@ $(LDFLAGS)
|
||||
|
||||
install: tiv
|
||||
cp tiv /usr/local/bin/tiv
|
||||
.PHONY: all install clean
|
||||
install: all
|
||||
test -d $(DESTDIR)/usr/local/bin || mkdir -p $(DESTDIR)/usr/local/bin
|
||||
cp tiv $(DESTDIR)/usr/local/bin/tiv
|
||||
|
||||
clean:
|
||||
rm -f tiv tiv.o
|
||||
rm -f tiv tiv.o
|
||||
|
||||
@@ -195,8 +195,7 @@ CharData getCharData(const cimg_library::CImg<unsigned char> & image, int x0, in
|
||||
int count2 = iter->first;
|
||||
long max_count_color_1 = iter->second;
|
||||
long max_count_color_2 = max_count_color_1;
|
||||
if (iter != color_per_count.rend()) {
|
||||
++iter;
|
||||
if ((++iter) != color_per_count.rend()) {
|
||||
count2 += iter->first;
|
||||
max_count_color_2 = iter->second;
|
||||
}
|
||||
@@ -330,7 +329,7 @@ void emit_color(int flags, int r, int g, int b) {
|
||||
int gq = COLOR_STEPS[gi];
|
||||
int bq = COLOR_STEPS[bi];
|
||||
|
||||
int gray = std::round(r * 0.2989f + g * 0.5870f + b * 0.1140f);
|
||||
int gray = static_cast<int>(std::round(r * 0.2989f + g * 0.5870f + b * 0.1140f));
|
||||
|
||||
int gri = best_index(gray, GRAYSCALE_STEPS, GRAYSCALE_STEP_COUNT);
|
||||
int grq = GRAYSCALE_STEPS[gri];
|
||||
@@ -382,6 +381,31 @@ void emit_image(const cimg_library::CImg<unsigned char> & image, int flags) {
|
||||
}
|
||||
|
||||
|
||||
struct size {
|
||||
size(unsigned int in_width, unsigned int in_height) :
|
||||
width(in_width), height(in_height) {
|
||||
}
|
||||
size(cimg_library::CImg<unsigned int> img) :
|
||||
width(img.width()), height(img.height()) {
|
||||
}
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
size scaled(double scale) {
|
||||
return size(width*scale, height*scale);
|
||||
}
|
||||
size fitted_within(size container) {
|
||||
double scale = std::min(container.width / (double) width, container.height / (double) height);
|
||||
return scaled(scale);
|
||||
}
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& stream, size sz) {
|
||||
stream << sz.width << "x" << sz.height;
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void emit_usage() {
|
||||
std::cerr << "Terminal Image Viewer" << std::endl << std::endl;
|
||||
std::cerr << "usage: tiv [options] <image> [<image>...]" << std::endl << std::endl;
|
||||
@@ -397,12 +421,42 @@ void emit_usage() {
|
||||
|
||||
enum Mode {AUTO, THUMBNAILS, FULL_SIZE};
|
||||
|
||||
|
||||
/* Wrapper around CImg<T>(const char*) to ensure the result has 3 channels as RGB
|
||||
*/
|
||||
cimg_library::CImg<unsigned char> load_rgb_CImg(const char * const filename) {
|
||||
cimg_library::CImg<unsigned char> image(filename);
|
||||
if(image.spectrum() == 1) {
|
||||
// Greyscale. Just copy greyscale data to all channels
|
||||
cimg_library::CImg<unsigned char> rgb_image(image.width(), image.height(), image.depth(), 3);
|
||||
for(unsigned int chn = 0; chn < 3; chn++) {
|
||||
rgb_image.draw_image(0, 0, 0,chn, image);
|
||||
}
|
||||
return rgb_image;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int maxWidth = 80;
|
||||
int maxHeight = 24;
|
||||
bool sizeDetectionSuccessful = true;
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
int maxWidth = w.ws_col * 4;
|
||||
int maxHeight = w.ws_row * 8;
|
||||
int ioStatus = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
// If redirecting STDOUT to one file ( col or row == 0, or the previous ioctl call's failed )
|
||||
if (ioStatus != 0 || (w.ws_col | w.ws_row) == 0) {
|
||||
ioStatus = ioctl(STDIN_FILENO, TIOCGWINSZ, &w);
|
||||
if (ioStatus != 0 || (w.ws_col | w.ws_row) == 0) {
|
||||
std::cerr << "Warning: failed to determine most reasonable size, defaulting to 80x24" << std::endl;
|
||||
sizeDetectionSuccessful = false;
|
||||
}
|
||||
}
|
||||
if (sizeDetectionSuccessful)
|
||||
{
|
||||
maxWidth = w.ws_col * 4;
|
||||
maxHeight = w.ws_row * 8;
|
||||
}
|
||||
int flags = 0;
|
||||
Mode mode = AUTO;
|
||||
int columns = 3;
|
||||
@@ -452,14 +506,14 @@ int main(int argc, char* argv[]) {
|
||||
if (mode == FULL_SIZE || (mode == AUTO && file_names.size() == 1)) {
|
||||
for (unsigned int i = 0; i < file_names.size(); i++) {
|
||||
try {
|
||||
cimg_library::CImg<unsigned char> image(file_names[i].c_str());
|
||||
cimg_library::CImg<unsigned char> image = load_rgb_CImg(file_names[i].c_str());
|
||||
|
||||
if (image.width() > maxWidth || image.height() > maxHeight) {
|
||||
double scale = std::min(maxWidth / (double) image.width(), maxHeight / (double) image.height());
|
||||
image.resize((int) (image.width() * scale), (int) (image.height() * scale), -100, -100, 5);
|
||||
size new_size = size(image).fitted_within(size(maxWidth,maxHeight));
|
||||
image.resize(new_size.width, new_size.height, -100, -100, 5);
|
||||
}
|
||||
emit_image(image, flags);
|
||||
} catch(cimg_library::CImgIOException e) {
|
||||
} catch(cimg_library::CImgIOException & e) {
|
||||
error = 1;
|
||||
std::cerr << "File format is not recognized for '" << file_names[i] << "'" << std::endl;
|
||||
}
|
||||
@@ -471,6 +525,7 @@ int main(int argc, char* argv[]) {
|
||||
int cw = (((maxWidth / 4) - 2 * (columns - 1)) / columns);
|
||||
int tw = cw * 4;
|
||||
cimg_library::CImg<unsigned char> image(tw * columns + 2 * 4 * (columns - 1), tw, 1, 3);
|
||||
size maxThumbSize(tw, tw);
|
||||
|
||||
while (index < file_names.size()) {
|
||||
image.fill(0);
|
||||
@@ -479,21 +534,21 @@ int main(int argc, char* argv[]) {
|
||||
while (index < file_names.size() && count < columns) {
|
||||
std::string name = file_names[index++];
|
||||
try {
|
||||
cimg_library::CImg<unsigned char> original(name.c_str());
|
||||
unsigned int cut = name.find_last_of("/");
|
||||
cimg_library::CImg<unsigned char> original = load_rgb_CImg(name.c_str());
|
||||
auto cut = name.find_last_of("/");
|
||||
sb += cut == std::string::npos ? name : name.substr(cut + 1);
|
||||
int th = original.height() * tw / original.width();
|
||||
original.resize(tw, th, 1, -100, 5);
|
||||
image.draw_image(count * (tw + 8), (tw - th) / 2, 0, 0, original);
|
||||
size newSize = size(original).fitted_within(maxThumbSize);
|
||||
original.resize(newSize.width, newSize.height, 1, -100, 5);
|
||||
image.draw_image(count * (tw + 8) + (tw - newSize.width) / 2, (tw - newSize.height) / 2, 0, 0, original);
|
||||
count++;
|
||||
unsigned int sl = count * (cw + 2);
|
||||
sb.resize(sl - 2, ' ');
|
||||
sb += " ";
|
||||
} catch (std::exception e) {
|
||||
} catch (std::exception & e) {
|
||||
// Probably no image; ignore.
|
||||
}
|
||||
}
|
||||
emit_image(image, flags);
|
||||
if (count) emit_image(image, flags);
|
||||
std::cout << sb << std::endl << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user