ターミナルでカラフルなピクセルアート

こんにちは。平井です。

今回は、「デジタルアートの作成、出品、販売を目指してお絵描きをしましょう」という内容です。

2022年2月12日に CryptoPunks の #5822 が驚愕の 8,000 ETH(約27億円)で売却されました。

CryptoPunks がどんな NFT ピクセルアートかというと、こんなやつです。

f:id:xgxgh:20220214002114p:plain
出典: https://www.larvalabs.com/cryptopunks

f:id:xgxgh:20220214000917g:plain
出典: https://medium.com/counterarts/cryptopunks-clone-create-nfts-like-cryptopunks-63d990bf3e8c

ほとんどのクリエイターは、Photoshop や Illustrator などを使ってデジタルアートを作成すると思います。

が、プログラマーにとってのキャンバスといえばターミナルなので、便利サイトでお絵描きをしてターミナルで表示させます。

そのためのお勉強として Bash における色表示の仕組みを解説します。

Bash における色表示の仕組み

まず、以下のシェルスクリプトに実行権限をつけて (chmod +x して)、実行してみましょう。

#!/bin/bash -eu

foreground=38
background=48
for fgbg in $foreground $background ; do
    for color in {0..255} ; do # Colors
        printf "\e[${fgbg};5;%sm  %3s  \e[0m" $color $color # Display the color
        if [ $((($color + 1) % 6)) == 4 ] ; then # Display 6 colors per lines
            echo # New line
        fi
    done
    echo # New line
done

exit 0

上記のシェルスクリプトを実行すると、このような結果が表示されます。

f:id:xgxgh:20220214004042p:plainf:id:xgxgh:20220214004129p:plain

8bit(=256) モードで表示する場合は、\e[ のあとに、 38 を書くと文字色、48 を書くと背景色となり、その後に ;5; (この部分が8bitモードを意味します)、そして色を意味する 0 から 255 (これは上記のように各数字で色が決まっています)、最後に m を書いて、色を定義する部分が完成します。

この後に色付けしたい文字列を記述します。

また、\e[0m と書くと、色の定義をリセットすることができます。

さきほど、8bit モードと書きましたが、実は 8bit モード以外に、3bit モードと 24bit モードによる色の定義方法があります。

各モードでの定義方法をまとめると、このようになります。

# Color

## 3bit

### Foreground

#### Regular
txtblk='\e[0;30m' # Black
txtred='\e[0;31m' # Red
txtgrn='\e[0;32m' # Green
txtylw='\e[0;33m' # Yellow
txtblu='\e[0;34m' # Blue
txtpur='\e[0;35m' # Purple
txtcyn='\e[0;36m' # Cyan
txtwht='\e[0;37m' # White

#### Bright
brtblk='\e[0;90m' # Black
brtred='\e[0;91m' # Red
brtgrn='\e[0;92m' # Green
brtylw='\e[0;93m' # Yellow
brtblu='\e[0;94m' # Blue
brtpur='\e[0;95m' # Purple
brtcyn='\e[0;96m' # Cyan
brtwht='\e[0;97m' # White

#### Bold
bldblk='\e[1;30m' # Black
bldred='\e[1;31m' # Red
bldgrn='\e[1;32m' # Green
bldylw='\e[1;33m' # Yellow
bldblu='\e[1;34m' # Blue
bldpur='\e[1;35m' # Purple
bldcyn='\e[1;36m' # Cyan
bldwht='\e[1;37m' # White

### Faint
fntblk='\e[2;30m' # Black
fntred='\e[2;31m' # Red
fntgrn='\e[2;32m' # Green
fntylw='\e[2;33m' # Yellow
fntblu='\e[2;34m' # Blue
fntpur='\e[2;35m' # Purple
fntcyn='\e[2;36m' # Cyan
fntwht='\e[2;37m' # White

#### Underline
undblk='\e[4;30m' # Black
undred='\e[4;31m' # Red
undgrn='\e[4;32m' # Green
undylw='\e[4;33m' # Yellow
undblu='\e[4;34m' # Blue
undpur='\e[4;35m' # Purple
undcyn='\e[4;36m' # Cyan
undwht='\e[4;37m' # White

#### Blink
blkblk='\e[5;30m' # Black
blkred='\e[5;31m' # Red
blkgrn='\e[5;32m' # Green
blkylw='\e[5;33m' # Yellow
blkblu='\e[5;34m' # Blue
blkpur='\e[5;35m' # Purple
blkcyn='\e[5;36m' # Cyan
blkwht='\e[5;37m' # White

### BackGround

#### Regular
bcrblk='\e[40m' # Black
bcrred='\e[41m' # Red
bcrgrn='\e[42m' # Green
bcrylw='\e[43m' # Yellow
bcrblu='\e[44m' # Blue
bcrpur='\e[45m' # Purple
bcrcyn='\e[46m' # Cyan
bcrwht='\e[47m' # White

#### Bright
bcbblk='\e[100m' # Black
bcbred='\e[101m' # Red
bcbgrn='\e[102m' # Green
bcbylw='\e[103m' # Yellow
bcbblu='\e[104m' # Blue
bcbpur='\e[105m' # Purple
bcbcyn='\e[106m' # Cyan
bcbwht='\e[107m' # White

### Foreground(FG) and Background(BG)
sample='\e[7;32;41m' # \e[7;FG;BGm FG=30~37,90~97 BG=40~47,100~107

## 8bit
### FG Color
sample2='\e[38;5;123m' # \e[38;5;nm  n=0~255(0~7: standard colors as in ESC[30-37m, 8~15: hight intensity colors as in ESC[90-97m, 16~231: 6x6x6 cube(216 colors) 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5))
### BG Color
sample3='\e[48;5;234m' # \e[48;5;nm  n=0~255(0~7: standard colors as in ESC[30-37m, 8~15: hight intensity colors as in ESC[90-97m, 16~231: 6x6x6 cube(216 colors) 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5))

## 24bit
### FG Color
sample4='\e[38;2;100;101;102m' # \e[38;2;r;g;bm r=0~255, g=0~255, b=0~255
### BG Color
sample5='\e[48;2;200;201;202m' # \e[48;2;r;g;bm r=0~255, g=0~255, b=0~255

## Text Reset
txtrst='\e[0m' # Useful for resetting color

たとえば、上記の変数を利用してプロンプトのカスタマイズができる環境変数 PS1 を設定する例はこんな感じです。

export PS1="\[$txtred\][bash:\V]\[$txtylw\][\u]\[$txtpur\][\H]\[$txtgrn\][\D{%Y/%m/%d}-\t]\[$txtcyn\][\w]\n\[$txtwht\]$"

f:id:xgxgh:20220214012626p:plain

赤色で Bash のバージョン、黄色でユーザー名、紫色でホスト名、緑色で日時、シアンでワーキングディレクトリ、改行、白色で $(一般ユーザーの時は $、root の時は #) を表示しています。

ほかにも tput というのがあります。

tput は色だけでなくカーソルの座標も制御できたりします。

気になる方はこちらをご参照ください。

tput(1) - Linux man page

Portable Terminal Control From Scripts - tput: Portable Terminal Control

ピクセルアートの作成および表示

Bash のターミナルで表示するピクセルアートを作成するのに、以下のウェブサイトを利用します。

max.io

f:id:xgxgh:20220214015347p:plain

draw here のとこで、右から色を選択し、ピクセルをクリックしたりドラッグすることで色を塗ります。

f:id:xgxgh:20220214015452p:plain

そうすると、copy this script のとこに、作成したピクセルアートを表示するためのシェルスクリプトが自動生成されます。

「8色すべてを使いたい!」、「カラフルといえば虹!」ということで虹を描いてみます。

f:id:xgxgh:20220214020337p:plain

#!/bin/bash
#Background Colors
E=$(tput sgr0);    R=$(tput setab 1); G=$(tput setab 2); Y=$(tput setab 3);
B=$(tput setab 4); M=$(tput setab 5); C=$(tput setab 6); W=$(tput setab 7);
function e() { echo -e "$E"; }
function x() { echo -n "$E  "; }
function r() { echo -n "$R  "; }
function g() { echo -n "$G  "; }
function y() { echo -n "$Y  "; }
function b() { echo -n "$B  "; }
function m() { echo -n "$M  "; }
function c() { echo -n "$C  "; }
function w() { echo -n "$W  "; }

#putpixels
function u() { 
    h="$*";o=${h:0:1};v=${h:1}; 
    for i in `seq $v` 
    do 
        $o;
    done 
}

img="\
w40 e1 w40 e1 w40 e1 w40 e1 w10 r16 w14 e1 w9 r19 w12 e1 w7 r24 w9 e1 w4 r6 y18 r5 w7 e1 w3 r5 y22 r5 w5 e1 w2 r5 y8 g10 y7 r4 w4 e1 w1 r4 y5 g19 y4 r5 w2 e1 w1 r3 y5 g22 y3 r4 w2 e1 w1 r3 y3 g6 c14 g6 y2 r4 w1 e1 r3 y3 g6 c18 g4 y3 r3 e1 r2 y3 g5 c3 b15 c3 g4 y3 r2 e1 r1 y3 g5 c2 b19 c3 g3 y3 r1 e1 r1 y2 g4 c3 b4 m10 b7 c3 g3 y3 e1 y3 g3 c3 b3 m15 b4 c4 g3 y2 e1 y2 g3 c4 b2 m4 x9 m5 b5 c2 g3 y1 e1 y2 g3 c3 b2 m4 x12 m4 b5 c2 g3 e1 y1 g3 c3 b3 m3 x15 m5 b2 c2 g3 e1 y1 g3 c2 b3 m3 x4 w7 x6 m4 b2 c3 g2 e1 y1 g2 c3 b3 m2 x4 w10 x5 m3 b3 c2 g2"

for n in $img
do
    u $n
done
e;
exit 0;

ターミナルで表示すると、こうなりました。

f:id:xgxgh:20220214023333p:plain

実行してみるとわかるのですが、一瞬では表示してくれません。

なので、.bashrc に追加するのはやめました。。。

まとめ

ターミナルはカラフルなピクセルアートを表示する場所ではないということがわかりました!

最後までお読みいただきありがとうございました!