monochromatic

monochromatic blog: http://blog.z3bra.org
git clone git://z3bra.org/monochromatic
Log | Files | Refs

images-in-terminal.txt (7235B)


      1 # [Images in terminal](#)
      2 ## — 28 January, 2014
      3 
      4 I am a huge fan of the terminal. Really. 90% of the magic I realize on my
      5 computer is through a terminal: IRC, text editing, ,e-mails, file managing,
      6 package managing, developpement, even web browsing sometimes !
      7 
      8 But the terminal lack one thing: **image rendering**.
      9 
     10 I have search a way to display images in the terminal for a looooong time now,
     11 and after digging through fbi, fbterm, and obscure graphical drivers, I finally
     12 found my goldmine.. I stumbled upon
     13 [this picture](http://www.nongnu.org/ranger/screenshots/w3mimgpreview.png)
     14 taken from [this website](http://www.nongnu.org/ranger/).  Ranger. It's a
     15 text-based file manager (that's cool bro'), but the interesting point sits in
     16 the "dependencies" section:
     17 
     18 > • w3m for previewing images in "true color".
     19 
     20 [w3m](http://w3m.sourceforge.net). That was my answer.
     21 
     22 ### the package
     23 
     24 w3m is a text-based web browser. It means that you can use it to browse the web
     25 from within your terminal (good stuff!). There are many like it (lynx, links,
     26 elinks, edbrowse,..), but this one is different, as it acts more like a
     27 point'n'click software than a CLI app.
     28 
     29 w3m uses gpm, a tool that let you use your terminal cursor like a mouse, moving
     30 it character by character.  Anyway, that's not the point here. Let's go back to
     31 image viewing!  w3m has the particularity to render images in your terminal,
     32 and it is pretty good at it! The problem was to find out **HOW**. I browsed the
     33 manpage many, many times, searching for keywords like <q>image</q>,
     34 <q>preview</q>, <q>gimme my f\*\*cking image rendering, damn software!</q>. Every
     35 usefull keyword I could find. **Nothing**.
     36 
     37 ### the pursuit
     38 
     39 A few minutes (when all the buckets were fullfilled with my tears), I finally
     40 tough: <q>Use the source, z3bra</q>. That's how I installed ranger.
     41 
     42 Ranger is written in python. And if it uses w3m to render images, I would find
     43 the tool it uses to do so. Here is how I managed to find it:
     44 
     45     $ pacman -Ql ranger | grep -E 'image|img|w3m|picture|preview'
     46     ranger /usr/lib/python3.3/site-packages/ranger/ext/__pycache__/img_display.cpython-33.pyc
     47     ranger /usr/lib/python3.3/site-packages/ranger/ext/__pycache__/img_display.cpython-33.pyo
     48     ranger /usr/lib/python3.3/site-packages/ranger/ext/img_display.py
     49 
     50     $ grep 'w3m' /usr/lib/python3.3/site-packages/ranger/ext/img_display.py
     51     ...
     52     W3MIMGDISPLAY_PATH = '/usr/lib/w3m/w3mimgdisplay'
     53     ...
     54 
     55 **HOORAY!** A binary ! Next step will be to understand how to make it render
     56 images in the terminal..
     57 
     58 ### the trials
     59 
     60 Obviously, running `w3mimgdisplay --help` would've been too easy.. But I
     61 finally managed to understand a few things using the ranger source I just
     62 found, and
     63 [this thread](https://www.mail-archive.com/mutt-users@mutt.org/msg34447.html).
     64 Here is the idea: w3mimgdisplay reads commands from stdin, and draws something
     65 on your terminal, pixel by pixel.
     66 
     67 w3mimgdisplay commands are numbers from 0 to 6, and some commands take
     68 additionnal parameters.  
     69 In the w3m tarball, you can find this:
     70 
     71     w3mimgdisplay.c
     72 
     73     /*
     74     * w3mimg protocol
     75     *  0  1  2 ....
     76     * +--+--+--+--+ ...... +--+--+
     77     * |op|; |args             |\n|
     78     * +--+--+--+--+ .......+--+--+
     79     *
     80     * args is separeted by ';'
     81     * op   args
     82     *  0;  params          draw image
     83     *  1;  params          redraw image
     84     *  2;  -none-          terminate drawing
     85     *  3;  -none-          sync drawing
     86     *  4;  -none-          nop, sync communication
     87     *                      response '\n'
     88     *  5;  path            get size of image,
     89     *                      response "<width> <height>\n"
     90     *  6;  params(6)       clear image
     91     *
     92     * params
     93     *      <n>;<x>;<y>;<w>;<h>;<sx>;<sy>;<sw>;<sh>;<path>
     94     * params(6)
     95     *      <x>;<y>;<w>;<h>
     96     *
     97     */
     98 
     99 Here is the _params_ interpreted on the mutt mail list:
    100 
    101     >  n  - This is used when displaying multiple images
    102     >  x  - x coordinate to draw the image at (top left corner)
    103     >  y  - y coordinate to draw the image at (top left corner)
    104     >  w  - width to draw the image
    105     >  h  - height to draw the image
    106     >  sx - x offset to draw the image
    107     >  xy - y offset to draw the image
    108     >  sw - width of the original (source) image
    109     >  sh - height of the original (source) image
    110 
    111 I now have a better idea on how the protocol works.  Now, by crossing it with
    112 the ranger source, I ended up with this line:
    113 
    114     echo -e '0;1;0;0;200;160;;;;;ant.jpg\n4;\n3;' | /usr/lib/w3m/w3mimgdisplay 
    115 
    116 **BOOM !** [It works!](http://chezmoicamarche.com)  
    117 [![Fucked up w3mimgdisplay trial](http://pub.z3bra.org/monochromatic/img/thumb/w3mimgdisplay-crap.jpg)](http://pub.z3bra.org/monochromatic/img/w3mimgdisplay-crap.jpg)
    118 <span class='caption'>The result of the previous command. Our picture drawn in
    119 200x100px, at offset +0+0 in the terminal.  I'm sure you're already trying it
    120 ;)</span>
    121 
    122 ### the wrapping
    123 
    124 Okay, we can now display an image in the terminal, at the offset and size we
    125 want. Let's wrap it up in a script, to be more adaptive!  We will need some
    126 tools to help us here. Feel free to search by yourself, as an exercise. Here is
    127 the script I came with:
    128 
    129     #!/bin/bash
    130     #
    131     # z3bra -- 2014-01-21
    132 
    133     test -z "$1" && exit
    134 
    135     W3MIMGDISPLAY="/usr/lib/w3m/w3mimgdisplay"
    136     FILENAME=$1
    137     FONTH=14 # Size of one terminal row
    138     FONTW=8  # Size of one terminal column
    139     COLUMNS=`tput cols`
    140     LINES=`tput lines`
    141 
    142     read width height <<< `echo -e "5;$FILENAME" | $W3MIMGDISPLAY`
    143 
    144     max_width=$(($FONTW * $COLUMNS))
    145     max_height=$(($FONTH * $(($LINES - 2)))) # substract one line for prompt
    146 
    147     if test $width -gt $max_width; then
    148     height=$(($height * $max_width / $width))
    149     width=$max_width
    150     fi
    151     if test $height -gt $max_height; then
    152     width=$(($width * $max_height / $height))
    153     height=$max_height
    154     fi
    155 
    156     w3m_command="0;1;0;0;$width;$height;;;;;$FILENAME\n4;\n3;"
    157 
    158     tput cup $(($height/$FONTH)) 0
    159     echo -e $w3m_command|$W3MIMGDISPLAY
    160 
    161 Let's see the rendering...  
    162 [![Fucked up w3mimgdisplay trial](http://pub.z3bra.org/monochromatic/img/thumb/w3mimgdisplay-good.jpg)](http://pub.z3bra.org/monochromatic/img/w3mimgdisplay-good.jpg)
    163 
    164 The script draws the image depending on the terminal size (width AND height),
    165 and put the cursor after the image (exactly 2 lines after).  
    166 You might want to adapt it to your own case, as the character height and width
    167 is hardcoded.
    168 
    169 Aaaaaaaaand it's cool !
    170 
    171 ### the end
    172 
    173 There you are. You have a tool to preview images in your terminal, in an easy
    174 way. The dependency is not huge, and you can script it the way you want.  
    175 
    176 I hope you learnt a few things here, like tips to grok softwares, understand
    177 libs/protocols, or at least, the w3mimg protocol.  My script is not perfect,
    178 because I have no idea how one can get the current cursor line and such. so if
    179 you have any improvement or idea, I'll be glad to modify my script and add your
    180 name :)
    181 
    182 _Side note:_ w3m can't render images in urxvt, if the depth is 32. That means
    183 that you can't render images on a transparent background. Be sure that you
    184 comment the line `URxvt*depth: 32` in your `~/.Xresources`.
    185 
    186 ### That's all, folks!
    187 
    188 <!-- vim: set ft=markdown ts=4 et: -->