Files
omarchycn/bin/omarchy-transcode-ascii
9455496990 Fix style inconsistencies in bin/ (#7518)
* Use (( )) for the numeric argument test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Drop the quotes on a variable inside [[ ]]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Use omarchy-pkg-drop instead of raw pacman -Rns

omarchy-pkg-drop already filters to installed packages, so the
2>/dev/null || true suppression is no longer needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Drop defensive checks around default-set commands

ttfx, imagemagick, and networkmanager are all in the default package
set, so their commands are runtime invariants and should be invoked
directly. Removing the nmcli guard also removes the degraded wifi
fallthrough that only ran when nmcli was missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 20:16:47 +02:00

239 lines
5.6 KiB
Bash
Executable File

#!/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"