Zenn
Zennに投稿した記事はこちらです。こちらの記事にはKitty Graphic Protocolなど技術面の話が載っています。
記事の概要
GitHub Copilotとめっちゃ頑張って、めっちゃ高速に表示されるTUIの画像ビューワーを作ったって話。
読者対象
- ターミナルで生活をしている人
- ターミナルで画像を表示したい人
実際の動作
内容物
Wezterm Nightlyしか動作確認していません。
https://github.com/c0b23092db/garou
https://crates.io/crates/garou
cargoでインストールできます。
cargo install garou
なぜ作った?
ターミナルでyaziのように操作できて画像をターミナルいっぱいに埋める画像表示専用のTUIがいくら探しても無い?なら作ってもらうか!
AIの力を使って二時間でターミナル画像ビューワーを作ったで作成したPythonのプロトタイプから見えた課題点や問題点を洗い出して、実践で使える画像ビューワーが欲しいと思ったからです。
そして作り始めたのは3月末です。GitHub CopilotのProに入っているのでトークンをギリギリまで使えます。
どんな問題点?
表示が遅い
致命的。圧倒的に致命的。
画像が点滅する
画像の切り替え時、差分表示の画像だろうがリフレッシュされて表示されるものだからとにかくチカチカして目に悪い。
起動が遅い
一秒以上かかるのは問題があります。
改善するなら?
とにかく早い表示
yaziと同等かそれ以上を求める。全てのファイルを10ms以内に表示することを最終目的にします。
画像が点滅しない
目に優しくしたいです。
完成品の動作
適切な設定で1920x1920を開いたら5msです。早い!
4K画像だと20msかかりますが、それでも従来の画像表示よりは早いんじゃないかな。
それから、課題だった画像の点滅を完全に抑えることができました。
使い方
crate.ioやGitHubに使い方が載っているので、ここでは軽く説明します。
コマンドをcargoでインストールした後、画像のあるカレントディレクトリで以下のコマンドを実行します。
siv
画像がないと「Error: カレントディレクトリ直下に画像ファイルがありません」が出力されます。
引数はPathでディレクトリとファイルを指定することができます。
画像を指定すると指定した画像にカーソルが乗った状態で起動されます。
siv directory\
siv picture.png
そのため、yaziで画像を大きく表示したいときに起動オプションを入れて起動することができます。
play-image = [
{ run = 'siv %*', block = true},
]
操作方法
常時
-
Q / esc: 終了 -
O: デフォルトプログラムで開く -
R: 画像のリフレッシュ -
Shift + R: 画面のリフレッシュ -
Alt + S: サイドバーの切り替え -
Alt + D: ステータスバーの切り替え -
Alt + F: ヘッダーの切り替え
画像表示
-
H / ←: 前の画像へ移動 -
L / →: 次の画像へ移動
サイドバー
-
J / ↓: カーソルを上に移動(即プレビュー) -
K / ↑: カーソルを下に移動(即プレビュー) -
G: 一番上に移動(即プレビュー) -
Shift + G: 一番下に移動(即プレビュー) -
Ctrl + B: 一ページ上に移動(即プレビュー) -
Ctrl + F: 一ページ下に移動(即プレビュー) -
H / ←: フォルダを折りたたむ -
L / →: フォルダを展開 -
Enter: フォルダのトグル -
左クリック: ファイルを選択 -
ホイール: カーソルを移動
設定ファイル
[image]
extensions = ["png", "jpg", "jpeg", "gif", "webp", "bmp"]
diff_mode = "Full"
transport_mode = "auto"
filter_type = "Nearest"
image_width = 5120
image_height = 2880
dirty_ratio = 0.1
tile_grid = 32
skip_step = 1
[display]
sidebar = true
header = true
statusbar = true
sidebar_size = 20
preview_debounce = 100 # プレビュー更新のデバウンス(ミリ秒)
poll_interval = 10 # アイドル状態のポーリング間隔(ミリ秒)
prefetch_interval = 100 # アイドル状態の先読み間隔(ミリ秒)
header_bg_color = "dark_blue" # ヘッダー背景色
header_fg_color = "white" # ヘッダー文字色
statusbar_bg_color = "dark_gray" # ステータスバー背景色
statusbar_fg_color = "white" # ステータスバー文字色
[cache]
lru_size = 10 # LRUキャッシュ最大数
prefetch_size = 1 # 先読みキャッシュ数
max_bytes = 268435456 # キャッシュ総容量上限(バイト)
詳細はリポジトリのREADME.mdを読んでください。
最速な設定
私は画像を高速で表示するために以下の設定を行っています。ローカルであればtransport_modeはfileを指定するのが一番いいかと。
[image]
diff_mode = "All"
transport_mode = "file"
[display]
header = true
sidebar = true
statusbar = false
sidebar_size = 20
preview_debounce = 50
[cache]
lru_size = 5
prefetch_size = 1
裏話
名前の由来
プロジェクトの名前は「画廊」から来ています。
実行名は「Simple Image Protocol Viewer」の略で「siv」になっています。
バージョン1.0.0
元々の目的が「1920x1080の画像(ゲームスチルなど)をWindowsのフォトみたいにターミナルで確認したい」でした。なので前提条件が「一般的な画像の大きさであれば1ms以内で表示できる」です。
それができているのでメジャーバージョンとして扱っているわけです。
バージョン1.1.0
transport_mode = directによる描画が非常に早くなり、10000x10000の画像が開けるようになっています。
メモリ容量を増やしたら20000x20000の画像も開けるはずですが……そこまで大きい画像を開く輩はいなさそうなので確認していません。
次のバージョンはもっと早く開けるように、そしてキャッシュの構造を効率的にしたいです。
最終目標
私は以前『Image Protocolは動画を再生できるのか?』というスクラップを作っています。
私の最終目標はターミナル上で動画もとい映像を流すことです。ターミナルは万物のことができないといけないので。Neovimもあんまり使えないのに思想が強いですね。
mpvの--vo=kittyを使え?Weztermだとenable_kitty_graphicsを切らないと動かないの。ドット絵になるの。それにmpvは毎フレーム画像送信するから重い。
差分表示できるならそれで早くなるんじゃないかと考えています。
ここはまだまだ研究中です。
まとめ
Zennだけだと知名度が上がらなそうでしたのでこちらでも記事を公開することにしました。Zennの内容をそのまま引っ張ってくるのもあれなのでちょっとだけ加筆しています。
ここの具体的な内容は今後のプロジェクト.mdに書かれています。興味があればぜひ。
