param( [Parameter(Mandatory=$true)][string]$InputPath, [Parameter(Mandatory=$true)][string]$OutputPath, [string]$BackupPath = "", [int]$AlphaThreshold = 1, [int]$Padding = 0 ) if (!(Test-Path -LiteralPath $InputPath)) { throw "Input file not found: $InputPath" } if ($BackupPath -ne "") { Copy-Item -LiteralPath $InputPath -Destination $BackupPath -Force } Add-Type -AssemblyName System.Drawing # Load from stream to avoid locking $fs = [System.IO.File]::Open($InputPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) try { $bmp = New-Object System.Drawing.Bitmap($fs) } finally { $fs.Dispose() } $minX = $bmp.Width $minY = $bmp.Height $maxX = -1 $maxY = -1 for ($y = 0; $y -lt $bmp.Height; $y++) { for ($x = 0; $x -lt $bmp.Width; $x++) { $c = $bmp.GetPixel($x, $y) if ($c.A -ge $AlphaThreshold) { if ($x -lt $minX) { $minX = $x } if ($y -lt $minY) { $minY = $y } if ($x -gt $maxX) { $maxX = $x } if ($y -gt $maxY) { $maxY = $y } } } } if ($maxX -lt 0 -or $maxY -lt 0) { # No non-transparent pixels, just copy input $bmp.Dispose() Copy-Item -LiteralPath $InputPath -Destination $OutputPath -Force exit 0 } $minX = [math]::Max(0, $minX - $Padding) $minY = [math]::Max(0, $minY - $Padding) $maxX = [math]::Min($bmp.Width - 1, $maxX + $Padding) $maxY = [math]::Min($bmp.Height - 1, $maxY + $Padding) $newW = $maxX - $minX + 1 $newH = $maxY - $minY + 1 $rect = New-Object System.Drawing.Rectangle($minX, $minY, $newW, $newH) $cropped = $bmp.Clone($rect, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb) $tmp = "$OutputPath.tmp" try { $cropped.Save($tmp, [System.Drawing.Imaging.ImageFormat]::Png) } finally { $cropped.Dispose() $bmp.Dispose() } Move-Item -LiteralPath $tmp -Destination $OutputPath -Force