/ de-omarchy / bin / omarchy-transcode-ascii
swadhinbiswas Initial de-omarchy: Omarchy UI layer for Arch-based distros · 8/22/2026
9f393b2
239 lines · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/bin/bash

# omarchy:summary=Transcode an image into ASCII/Unicode art text
# omarchy:group=transcode
# omarchy:name=ascii
# omarchy:args=<input-image.svg|png> <output-path> [--width <columns>] [--height <rows>] [--mode <braille|block>] [--threshold <percent>] [--invert]
# omarchy:examples=omarchy transcode ascii ~/logo.svg /tmp/logo.txt | omarchy transcode ascii ~/logo.png ~/.config/omarchy/branding/screensaver.txt --width 80

set -o pipefail

usage() {
  cat <<'EOF'
Usage: omarchy-transcode-ascii <input-image.svg|png> <output-path> [options]

Transcodes an image into ASCII/Unicode art text.

Options:
  -w, --width <columns>       Maximum output width in terminal columns (default: 80)
  -H, --height <rows>         Maximum output height in terminal rows (default: 26)
  -m, --mode <braille|block>  Output style (default: braille)
  -t, --threshold <percent>   Pixel threshold from 0-100 (default: 50)
      --invert                Treat light pixels as the logo instead of dark pixels
      --no-trim               Preserve surrounding whitespace/background
      --help                  Show this help
EOF
}

width=80
height=26
mode=braille
threshold=50
invert=false
trim=true
image_path=""
output_path=""

while (($# > 0)); do
  case "$1" in
  --help)
    usage
    exit 0
    ;;
  -w | --width)
    shift
    [[ -n $1 ]] || { echo "Missing value for --width" >&2; exit 1; }
    width="$1"
    ;;
  -H | --height)
    shift
    [[ -n $1 ]] || { echo "Missing value for --height" >&2; exit 1; }
    height="$1"
    ;;
  -m | --mode)
    shift
    [[ -n $1 ]] || { echo "Missing value for --mode" >&2; exit 1; }
    mode="$1"
    ;;
  -t | --threshold)
    shift
    [[ -n $1 ]] || { echo "Missing value for --threshold" >&2; exit 1; }
    threshold="$1"
    ;;
  --invert)
    invert=true
    ;;
  --no-trim)
    trim=false
    ;;
  -*)
    echo "Unknown option: $1" >&2
    usage >&2
    exit 1
    ;;
  *)
    if [[ -z $image_path ]]; then
      image_path="$1"
    elif [[ -z $output_path ]]; then
      output_path="$1"
    else
      echo "Unexpected argument: $1" >&2
      usage >&2
      exit 1
    fi
    ;;
  esac
  shift
done

if [[ -z $image_path || -z $output_path ]]; then
  usage >&2
  exit 1
fi

if [[ $mode != "braille" && $mode != "block" ]]; then
  echo "Invalid mode: $mode (expected braille or block)" >&2
  exit 1
fi

if [[ ! -f $image_path ]]; then
  echo "Logo file not found: $image_path" >&2
  exit 1
fi

if [[ $mode == "braille" ]]; then
  pixel_width=$((width * 2))
  pixel_height=$((height * 4))
else
  pixel_width=$width
  pixel_height=$((height * 2))
fi

alpha_range=$(magick -background none "$image_path" -alpha extract -format '%[fx:minima] %[fx:maxima]' info: 2>/dev/null) || {
  echo "Unable to read logo image: $image_path" >&2
  exit 1
}
read -r alpha_min alpha_max <<<"$alpha_range"

use_alpha=false
if awk -v min="$alpha_min" -v max="$alpha_max" 'BEGIN { exit !(min < 0.999 && max > min) }'; then
  use_alpha=true
fi

magick_args=(-background none "$image_path" -auto-orient)

if [[ $use_alpha == "true" ]]; then
  magick_args+=(-alpha extract -alpha off)
  if [[ $invert == "true" ]]; then
    magick_args+=(-negate)
  fi
else
  magick_args+=(-alpha remove -alpha off -colorspace Gray)
  if [[ $invert == "false" ]]; then
    magick_args+=(-negate)
  fi
fi

if [[ $trim == "true" ]]; then
  magick_args+=(-bordercolor black -border 1 -trim +repage)
fi

magick_args+=(-resize "${pixel_width}x${pixel_height}" -threshold "${threshold}%" -negate -compress none pbm:-)

tmp_output=$(mktemp)
trap 'rm -f "$tmp_output"' EXIT

if ! magick "${magick_args[@]}" | awk -v mode="$mode" '
  BEGIN {
    braille_base = 10240
    dot[0,0] = 1
    dot[0,1] = 2
    dot[0,2] = 4
    dot[1,0] = 8
    dot[1,1] = 16
    dot[1,2] = 32
    dot[0,3] = 64
    dot[1,3] = 128

    block["11"] = "█"
    block["10"] = "▀"
    block["01"] = "▄"
    block["00"] = " "
  }
  {
    sub(/#.*/, "")
    for (i = 1; i <= NF; i++) {
      token[++token_count] = $i
    }
  }
  END {
    if (token[1] != "P1") {
      exit 1
    }

    width = token[2]
    height = token[3]
    pixel_offset = 4
    row_step = (mode == "braille") ? 4 : 2

    for (y = 0; y < height; y += row_step) {
      line = ""
      if (mode == "braille") {
        for (x = 0; x < width; x += 2) {
          code = 0
          for (dy = 0; dy < 4; dy++) {
            for (dx = 0; dx < 2; dx++) {
              if (y + dy < height && x + dx < width) {
                pixel = token[pixel_offset + ((y + dy) * width) + x + dx]
                if (pixel == 1) {
                  code += dot[dx,dy]
                }
              }
            }
          }

          if (code == 0) {
            line = line " "
          } else {
            line = line sprintf("%c", braille_base + code)
          }
        }
      } else {
        for (x = 0; x < width; x++) {
          top = token[pixel_offset + (y * width) + x]
          bottom = (y + 1 < height) ? token[pixel_offset + ((y + 1) * width) + x] : 0
          line = line block[top bottom]
        }
      }
      sub(/[ ]+$/, "", line)
      lines[++line_count] = line
    }

    first = 1
    while (first <= line_count && lines[first] ~ /^[ ]*$/) {
      first++
    }

    last = line_count
    while (last >= first && lines[last] ~ /^[ ]*$/) {
      last--
    }

    for (i = first; i <= last; i++) {
      print lines[i]
    }
  }
' >"$tmp_output"; then
  echo "Unable to convert logo image: $image_path" >&2
  exit 1
fi

if [[ ! -s $tmp_output ]]; then
  echo "No logo pixels found in: $image_path" >&2
  exit 1
fi

mkdir -p "$(dirname "$output_path")"
cp "$tmp_output" "$output_path"
echo "Wrote ASCII art to $output_path"