Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Created September 1, 2024 04:55
Show Gist options
  • Save Jaykul/55b681ef358acbe5534039b6279ae608 to your computer and use it in GitHub Desktop.
Save Jaykul/55b681ef358acbe5534039b6279ae608 to your computer and use it in GitHub Desktop.
ImageMagick and Sixels -- this is not fast, but it works even across ssh
<#
.SYNOPSIS
gcixels is like gci, but with sixels...
.DESCRIPTION
Shows thumbnails of images with titles, directly in terminal.
However, it's done with ImageMagick montage, so it's awfully slow.
Just sharing it for your inspiration.
.NOTES
Requires ImageMagick and a Sixel terminal (like Windows Terminal 1.22+ or WezTerm)
#>
[CmdletBinding()]
param(
# The path to the images to list
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
[Alias("PSPath")]
$Path = $pwd,
# Background color.
$background = "none",
# Text color.
$foreground = "white",
# The number of colors to use (defaults to 256)
$NumColors = 256,
# Screen width (in pixels).
$width = 1800,
# Set both the Width and Height of thumbnails (in pixels)
$tilesize = 120,
# Manually set the width of each thumbnail
$tilewidth = $tilesize,
# Manually set the height of each thumbnail
$tileheight = $tilesize,
# Manually set the horizontal space between thumbnails
$tilexspace = 10,
# Manually set the vertical space between thumbnails
$tileyspace = 5,
# Override the number of tiles
$numtiles = ([Math]::Floor($width / ($tilewidth + 2 * $tilexspace + 1))),
# The font family. If you want to specify the font, use one from: magick -list font
$fontfamily,
# The default font size is based on the width of each image
$fontsize= ([Math]::Floor($tilewidth / 10)),
# How long to wait for the terminal to respond
$timeout = 0.25
)
begin {
if (-not (Get-Command magick -ErrorAction SilentlyContinue)) {
Write-Error "ImageMagick is not installed. Try installing it from https://imagemagick.org or with winget or choco:`n`twinget install ImageMagick.ImageMagick`n`tchoco install imagemagick"
return
}
# Function to clean up and exit
function Cleanup {
"`e\\"
exit 0
}
# Set up cleanup on exit
trap { Cleanup }
# TODO: Detect if the terminal is not sixel capable
# Function to process image labels
function ProcessLabel {
param($label)
# Try fixing up labels for image magick
# 1. Remove [0] suffix and : prefix.
# 2. Escape the percent, backslash, and @ sign.
# 3. Replace control characters with question marks.
# 4. If a filename is too long, remove extension (.jpg).
# 5. Split long filenames with newlines (recursively)
$label = $label -replace '^\:', '' -replace '\[0]$', '' -replace '([%\\])', '$1$1' -replace '@', '\@'
$label = $label -replace '[\x00-\x1F]', '?'
if ($label.Length -gt 15) {
$label = $label -replace '\.[^.]{1,4}$', ''
}
if ($label.Length -gt 15) {
$label = $label -replace "(.{1,15})", '$1\n'
}
$label
}
$Images = @()
}
process {
$Images += Get-ChildItem $Path -File | Where-Object {
$_.Extension -match '\.(jpg|jpeg|png|gif|webp|tiff|tif|p[bgp]m|x[pb]m|bmp|ico|svg|eps)$'
} | Sort-Object Name
}
end {
$imoptions = @(
"-tile", "${numtiles}x1"
"-geometry", "${tilewidth}x${tileheight}>+${tilexspace}+${tileyspace}"
"-background", "$background"
"-fill", "$foreground"
"-auto-orient" # if it comes from a camera, rotate it right
"-colors", "$NumColors"
if ($numcolors -gt 16) {
"-shadow"
}
if ($fontfamily) {
"-font", "$fontfamily"
}
if ($fontsize) {
"-pointsize", "$fontsize"
}
)
# Process images in batches
for ($i = 0; $i -lt $Images.Count; $i += $numtiles) {
Write-Verbose "Take the next $numtiles from $($Images.Count) ($($i..($i + $numtiles - 1) -join ','))"
$batch = $Images[$i..($i + $numtiles - 1)]
$magickArgs = @()
foreach ($file in $batch) {
$label = ProcessLabel $file.Name
$magickArgs += "-label", $label, "$file"
}
# Use ImageMagick to create montage and convert to Sixel
$magickArgs += $imoptions
Write-Debug "magick montage $($magickArgs -join ' ') sixel:-"
magick montage @magickArgs sixel:-
}
}
# Send an escape sequence to finish
# Write-Host -NoNewline "`e[c"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment