Last active
October 23, 2024 02:45
-
-
Save romero126/9e07a397b0fad5d6f33d58838b3c8017 to your computer and use it in GitHub Desktop.
Sixel Demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class FrameBuffer : IDisposable { | |
[System.Int32] $Width | |
[System.Int32] $Height | |
[System.Boolean] $Disposed | |
hidden [System.Int32[]] $buffer | |
hidden [System.Runtime.InteropServices.GCHandle] $bufferHandle | |
hidden [System.Drawing.Image] $bitmap | |
FrameBuffer([System.Int32]$w, [System.Int32]$h) { | |
$this.Width = $w | |
$this.Height = $h | |
# Bind the image to the buffer | |
$this.buffer = [System.Int32[]]::new($this.Width * $this.Height) | |
$this.bufferHandle = [System.Runtime.InteropServices.GCHandle]::Alloc($this.buffer, [System.Runtime.InteropServices.GCHandleType]::Pinned) | |
$this.bitmap = [System.Drawing.Bitmap]::new($this.Width, $this.Height, $this.Width * 4, [System.Drawing.Imaging.PixelFormat]::Format32bppPArgb, $this.bufferHandle.AddrOfPinnedObject()) | |
} | |
FrameBuffer([System.Drawing.Image]$image) { | |
$this.Width = $image.Width | |
$this.Height = $image.Height | |
# Bind the image to the buffer | |
$this.buffer = [System.Int32[]]::new($this.Width * $this.Height) | |
$this.bufferHandle = [System.Runtime.InteropServices.GCHandle]::Alloc($this.buffer, [System.Runtime.InteropServices.GCHandleType]::Pinned) | |
$this.bitmap = [System.Drawing.Bitmap]::new($this.Width, $this.Height, $this.Width * 4, [System.Drawing.Imaging.PixelFormat]::Format32bppPArgb, $this.bufferHandle.AddrOfPinnedObject()) | |
$gfx = [System.Drawing.Graphics]::FromImage($this.bitmap) | |
$gfx.DrawImage($image, 0, 0, $image.Width, $image.Height) | |
$gfx.Dispose() | |
} | |
FrameBuffer([System.String]$path) { | |
$image = [System.Drawing.Image]::FromFile($path) | |
$this.Width = $image.Width | |
$this.Height = $image.Height | |
# Bind the image to the buffer | |
$this.buffer = [System.Int32[]]::new($this.Width * $this.Height) | |
$this.bufferHandle = [System.Runtime.InteropServices.GCHandle]::Alloc($this.buffer, [System.Runtime.InteropServices.GCHandleType]::Pinned) | |
$this.bitmap = [System.Drawing.Bitmap]::new($this.Width, $this.Height, $this.Width * 4, [System.Drawing.Imaging.PixelFormat]::Format32bppPArgb, $this.bufferHandle.AddrOfPinnedObject()) | |
$this.Clear() | |
$this.DrawImage($image, 0, 0, $image.Width, $image.Height) | |
} | |
static [FrameBuffer] Create($path) { | |
$_fromBitmap = [System.Drawing.Image]::FromFile($path) | |
$result = [FrameBuffer]::new($_fromBitmap) | |
return $result | |
} | |
[System.Drawing.Color] GetPixel($i) { | |
if ($i -gt $this.buffer.Length) { | |
throw "Index out of bounds" | |
} | |
$col = $this.buffer[$i] | |
$result = [System.Drawing.Color]::FromArgb($col) | |
return $result | |
} | |
[System.Drawing.Color] GetPixel($x, $y) { | |
if ($x -gt $this.Width -or $y -gt $this.Height) { | |
throw "Index out of bounds" | |
} | |
$i = $x + ($y * $this.Width) | |
$col = $this.buffer[$i] | |
$result = [System.Drawing.Color]::FromArgb($col) | |
return $result | |
} | |
[System.Void] Clear() { | |
$defaultColorName = [System.Console]::BackgroundColor.ToString() | |
$defaultColor = [System.Drawing.Color]::FromName($defaultColorName) | |
$this.Clear($defaultColor) | |
} | |
[System.Void] Clear([System.Drawing.Color] $color) { | |
$gfx = [System.Drawing.Graphics]::FromImage($this.bitmap) | |
$gfx.CompositingMode = ([System.Drawing.Drawing2D.CompositingMode]::SourceOver) | |
$gfx.CompositingQuality = ([System.Drawing.Drawing2D.CompositingQuality]::Default) | |
$gfx.Clear($color) | |
$gfx.Flush() | |
$gfx.Dispose() | |
} | |
SaveImage($path) { | |
$this.bitmap.Save($path) | |
} | |
[System.Void] DrawImage([System.Drawing.Image]$image, [System.Int32]$x, [System.Int32]$y) { | |
$this.DrawImage($image, $x, $y, $image.Width, $image.Height) | |
} | |
[System.Void] DrawImage([System.Drawing.Image]$image, [System.Int32]$x, [System.Int32]$y, [System.Int32]$w, [System.Int32]$h) { | |
$gfx = [System.Drawing.Graphics]::FromImage($this.bitmap) | |
$gfx.CompositingMode = ([System.Drawing.Drawing2D.CompositingMode]::SourceOver) | |
$gfx.CompositingQuality = ([System.Drawing.Drawing2D.CompositingQuality]::HighQuality) | |
$image.MakeTransparent() | |
$gfx.DrawImage($image, $x, $y, $w, $h) | |
$gfx.Flush() | |
$gfx.Dispose() | |
} | |
[System.String] ToString([System.String]$format) { | |
if ($format -ne "Sixel") { | |
throw "Invalid Format" | |
} | |
$stringBuilder = [System.Text.StringBuilder]::new() | |
$sixelColorMap = @{} | |
$stringBuilder.Append([char]0x1b + "P0;1q") | |
# Force 1-1 Scale | |
$stringBuilder.AppendFormat("`"1;1;{0};{1};", $this.Width, $this.Height) | |
$stringBuilder.Append("#0;2;0;0;0") # This is a weird color | |
for ($i = 0; $i -le $this.buffer.Length-1; $i++) { | |
# Lets always look at the colors | |
$argb = $this.buffer[$i] | |
$A = $argb -shr 24 -band 0xFF | |
$R = $argb -shr 16 -band 0xFF | |
$G = $argb -shr 8 -band 0xFF | |
$B = $argb -band 0xFF | |
# Normalize the colors to match 255 Total colors | |
# Shrink the color indexes by a factor of 3 | |
if ($R % 41 -ne 0) { $R = $R - ($R % 41) } | |
if ($G % 41 -ne 0) { $G = $G - ($G % 41) } | |
if ($B % 41 -ne 0) { $B = $B - ($B % 41) } | |
# Calculate new ARGB value | |
$newARGB = ($A -shl 24) -bor ($R -shl 16) -bor ($G -shl 8) -bor $B | |
if (-not $sixelColorMap.ContainsKey($newARGB)) { | |
$R = [int]([Math]::Round($R / 255 * 100)) | |
$G = [int]([Math]::Round($G / 255 * 100)) | |
$B = [int]([Math]::Round($B / 255 * 100)) | |
$count = $SixelColorMap.Count+1 | |
$SixelColorMap.Add($newARGB, $count) | |
$stringBuilder.AppendFormat("#{0};2;{1};{2};{3}", $count, $R, $G, $B) | |
} | |
# It might be faster to set x and y as incremental variables instead of using math | |
$x = ($i) % ($this.Width) | |
$y = [math]::Floor(($i) / ($this.Width)) | |
$sixelPos = $y % 6 | |
if ($newARGB -eq -16777216) { | |
$stringBuilder.Append("#0?" ) | |
} else { | |
$stringBuilder.AppendFormat("#{0}{1}", $SixelColorMap[$newARGB], [char][int](63+[math]::pow(2, $sixelPos)) ) | |
} | |
if ($sixelPos -eq 5 -and $x -eq $this.Width-1) { | |
$stringBuilder.Append('-') | |
} elseif ($x -eq $this.Width-1) { | |
$stringBuilder.Append('$') | |
} | |
} | |
$stringBuilder.Append([char]0x1b + "\") | |
return $stringBuilder.ToString() | |
} | |
Dispose() { | |
if (-not $this.Disposed) { | |
$this.Disposed = $true | |
$this.bufferHandle.Free() | |
$this.bitmap.Dispose() | |
} | |
} | |
} | |
#[FrameBuffer]::new(100, 100) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[CmdletBinding()] | |
param( | |
[parameter(Mandatory = $true)] | |
[ArgumentCompleter({ | |
$path = Get-ChildItem -Path $PWD -Filter "*.png" -Recurse | |
$path | ForEach-Object { $_.Name } | |
})] | |
[System.IO.FileInfo]$path | |
) | |
# Load dependencies | |
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | |
# Import the Class Object | |
. .\ConsolePresentationFramework.ps1 | |
$path = (Get-Item -Path $path).FullName | |
$frameBuffer = [FrameBuffer]::new($path) | |
$frameBuffer.SaveImage("$PSScriptRoot\buffer.png") | |
Measure-Command -Expression { | |
$sixel = $frameBuffer.ToString("Sixel") | |
} | |
Write-Host "Sixel Width: $($frameBuffer.Width) Height: $($frameBuffer.Height)" | |
$sixel |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment