# gen-icons.ps1 — Generate build/icon.ico from an existing logo PNG. # Produces a multi-size PNG-compressed ICO (16/24/32/48/64/128/256), usable by # electron-builder (win.icon / installerHeaderIcon) and NSIS on Win10+. param( [string]$Source = "images/bailongma-logo.png", [string]$OutDir = "build" ) $ErrorActionPreference = "Stop" Add-Type -AssemblyName System.Drawing $srcPath = Join-Path (Split-Path $PSScriptRoot) $Source $outPath = Join-Path (Split-Path $PSScriptRoot) "$OutDir\icon.ico" if (-not (Test-Path $srcPath)) { Write-Error "Source image not found: $srcPath" } $src = [System.Drawing.Image]::FromFile((Resolve-Path $srcPath)) try { # Square crop to the shorter side so the icon is not distorted. $side = [Math]::Min($src.Width, $src.Height) $cropX = [int](($src.Width - $side) / 2) $cropY = [int](($src.Height - $side) / 2) $square = New-Object System.Drawing.Bitmap($side, $side) $g0 = [System.Drawing.Graphics]::FromImage($square) try { $g0.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $g0.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality $g0.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality $g0.DrawImage($src, (New-Object System.Drawing.Rectangle(0, 0, $side, $side)), (New-Object System.Drawing.Rectangle($cropX, $cropY, $side, $side)), [System.Drawing.GraphicsUnit]::Pixel) } finally { $g0.Dispose() } $sizes = @(16, 24, 32, 48, 64, 128, 256) $pngBlobs = @() foreach ($s in $sizes) { $bmp = New-Object System.Drawing.Bitmap($s, $s, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb) $g = [System.Drawing.Graphics]::FromImage($bmp) try { $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality $g.Clear([System.Drawing.Color]::Transparent) $g.DrawImage($square, 0, 0, $s, $s) } finally { $g.Dispose() } $ms = New-Object System.IO.MemoryStream $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) $pngBlobs += , $ms.ToArray() $ms.Dispose() $bmp.Dispose() } $square.Dispose() # Assemble ICO: ICONDIR + ICONDIRENTRY per image + PNG blobs. $fs = [System.IO.File]::Create($outPath) $w = New-Object System.IO.BinaryWriter($fs) try { $w.Write([UInt16]0) # reserved $w.Write([UInt16]1) # type: icon $w.Write([UInt16]$pngBlobs.Count) # image count $offset = 6 + 16 * $pngBlobs.Count foreach ($i in 0..($pngBlobs.Count - 1)) { $blob = $pngBlobs[$i] $size = $sizes[$i] $w.Write([Byte]($(if ($size -ge 256) { 0 } else { $size }))) # width (0 = 256) $w.Write([Byte]($(if ($size -ge 256) { 0 } else { $size }))) # height (0 = 256) $w.Write([Byte]0) # color count $w.Write([Byte]0) # reserved $w.Write([UInt16]1) # planes $w.Write([UInt16]32) # bit count $w.Write([UInt32]$blob.Length) # bytes in resource $w.Write([UInt32]$offset) # image offset $offset += $blob.Length } foreach ($blob in $pngBlobs) { $w.Write($blob) } $w.Flush() } finally { $w.Dispose() $fs.Dispose() } Write-Host "[gen-icons] wrote $outPath ($((Get-Item $outPath).Length) bytes, $($pngBlobs.Count) sizes)" } finally { $src.Dispose() }