Add-Type -AssemblyName System.Drawing function New-AnimeEyeBitmap { param([int]$Size) $bmp = New-Object System.Drawing.Bitmap($Size, $Size) $g = [System.Drawing.Graphics]::FromImage($bmp) $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality $g.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality $g.Clear([System.Drawing.Color]::Transparent) $s = $Size $rect = New-Object System.Drawing.Rectangle(0, 0, $s, $s) $radius = [Math]::Max(2, [int]($s * 0.22)) # --- rounded-square background gradient (indigo -> violet -> magenta, anime-ish) --- $path = New-Object System.Drawing.Drawing2D.GraphicsPath $d = $radius * 2 $path.AddArc(0, 0, $d, $d, 180, 90) $path.AddArc($s - $d, 0, $d, $d, 270, 90) $path.AddArc($s - $d, $s - $d, $d, $d, 0, 90) $path.AddArc(0, $s - $d, $d, $d, 90, 90) $path.CloseFigure() $bgBrush = New-Object System.Drawing.Drawing2D.LinearGradientBrush( $rect, [System.Drawing.Color]::FromArgb(255, 40, 20, 90), [System.Drawing.Color]::FromArgb(255, 150, 40, 130), [System.Drawing.Drawing2D.LinearGradientMode]::ForwardDiagonal) $g.FillPath($bgBrush, $path) # subtle top highlight $topRect = New-Object System.Drawing.Rectangle(0, 0, $s, [int]($s * 0.55)) $topBrush = New-Object System.Drawing.Drawing2D.LinearGradientBrush( $topRect, [System.Drawing.Color]::FromArgb(60, 255, 255, 255), [System.Drawing.Color]::FromArgb(0, 255, 255, 255), [System.Drawing.Drawing2D.LinearGradientMode]::Vertical) $oldClip = $g.Clip $g.SetClip($path) $g.FillRectangle($topBrush, $topRect) $g.Clip = $oldClip # --- anime eye --- $cx = $s * 0.5 $cy = $s * 0.46 $eyeW = $s * 0.72 $eyeH = $s * 0.46 # white of the eye $eyePath = New-Object System.Drawing.Drawing2D.GraphicsPath $eyeRect = New-Object System.Drawing.RectangleF(($cx - $eyeW/2), ($cy - $eyeH/2), $eyeW, $eyeH) $eyePath.AddEllipse($eyeRect) $whiteBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(250, 255, 255, 255)) $g.FillPath($whiteBrush, $eyePath) # upper eyelid line (thick, sweeping) + eyelash accent $lidPen = New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(255, 25, 15, 40), [Math]::Max(1.0, $s * 0.045)) $lidPen.StartCap = [System.Drawing.Drawing2D.LineCap]::Round $lidPen.EndCap = [System.Drawing.Drawing2D.LineCap]::Round $lidRect = New-Object System.Drawing.RectangleF(($cx - $eyeW/2), ($cy - $eyeH/2 - $s*0.02), $eyeW, $eyeH*1.1) $g.DrawArc($lidPen, $lidRect, 195, 150) $lashX = $cx + $eyeW/2 * 0.92 $lashY = $cy - $eyeH/2 * 0.55 $g.DrawLine($lidPen, $lashX, $lashY, $lashX + $s*0.06, $lashY - $s*0.10) # iris (gradient blue -> violet with highlight) $irisSize = $eyeH * 0.95 $irisRect = New-Object System.Drawing.RectangleF(($cx - $irisSize/2), ($cy - $irisSize/2), $irisSize, $irisSize) $irisBrush = New-Object System.Drawing.Drawing2D.LinearGradientBrush( $irisRect, [System.Drawing.Color]::FromArgb(255, 60, 190, 255), [System.Drawing.Color]::FromArgb(255, 130, 60, 220), [System.Drawing.Drawing2D.LinearGradientMode]::Vertical) $g.FillEllipse($irisBrush, $irisRect) # pupil $pupilSize = $irisSize * 0.42 $pupilRect = New-Object System.Drawing.RectangleF(($cx - $pupilSize/2), ($cy - $pupilSize/2), $pupilSize, $pupilSize) $g.FillEllipse([System.Drawing.Brushes]::Black, $pupilRect) # ring accent inside iris $ringPen = New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(150, 20, 20, 40), [Math]::Max(1.0, $s*0.012)) $ringSize = $irisSize * 0.72 $ringRect = New-Object System.Drawing.RectangleF(($cx - $ringSize/2), ($cy - $ringSize/2), $ringSize, $ringSize) $g.DrawEllipse($ringPen, $ringRect) # big specular highlight $hlSize = $irisSize * 0.32 $hlRect = New-Object System.Drawing.RectangleF(($cx - $irisSize*0.28), ($cy - $irisSize*0.32), $hlSize, $hlSize) $g.FillEllipse([System.Drawing.Brushes]::White, $hlRect) # small secondary sparkle $sp2Size = $irisSize * 0.14 $sp2Rect = New-Object System.Drawing.RectangleF(($cx + $irisSize*0.10), ($cy + $irisSize*0.18), $sp2Size, $sp2Size) $g.FillEllipse([System.Drawing.Brushes]::White, $sp2Rect) # --- "subtitle bar" accent motif near the bottom --- $barY = $s * 0.80 $barH = [Math]::Max(1.0, $s * 0.07) $barW1 = $s * 0.5 $bar1 = New-Object System.Drawing.RectangleF(($cx - $barW1/2), $barY, $barW1, $barH) $barBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(235, 255, 255, 255)) $g.FillRectangle($barBrush, $bar1) $g.Dispose() return $bmp } function Get-PngBytes($bmp) { $ms = New-Object System.IO.MemoryStream $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) return [byte[]]$ms.ToArray() } $sizes = @(16, 32, 48, 256) $pngList = @() foreach ($sz in $sizes) { $bmp = New-AnimeEyeBitmap -Size $sz $bytes = Get-PngBytes $bmp $pngList += ,@{ Size = $sz; Bytes = $bytes } $bmp.Dispose() } # Save the 256px version separately as a standalone PNG for use as ui:ImageIcon source. $png256 = ($pngList | Where-Object { $_.Size -eq 256 }).Bytes [System.IO.File]::WriteAllBytes("Assets/app.png", [byte[]]$png256) # --- Manually pack a multi-size .ico (ICONDIR + ICONDIRENTRY headers wrapping raw PNG bytes) --- $icoPath = "Assets/app.ico" $fs = New-Object System.IO.FileStream($icoPath, [System.IO.FileMode]::Create) $bw = New-Object System.IO.BinaryWriter($fs) # ICONDIR: reserved(2)=0, type(2)=1 (icon), count(2) $bw.Write([UInt16]0) $bw.Write([UInt16]1) $bw.Write([UInt16]$pngList.Count) $headerSize = 6 $entrySize = 16 $offset = $headerSize + ($entrySize * $pngList.Count) foreach ($entry in $pngList) { $sz = $entry.Size $byteLen = $entry.Bytes.Length $widthByte = if ($sz -ge 256) { 0 } else { $sz } $heightByte = if ($sz -ge 256) { 0 } else { $sz } $bw.Write([byte]$widthByte) # width (0 = 256) $bw.Write([byte]$heightByte) # height (0 = 256) $bw.Write([byte]0) # color palette $bw.Write([byte]0) # reserved $bw.Write([UInt16]1) # color planes $bw.Write([UInt16]32) # bits per pixel $bw.Write([UInt32]$byteLen) # size of image data $bw.Write([UInt32]$offset) # offset of image data $offset += $byteLen } # NOTE: the explicit [byte[]] cast below is required - without it, PowerShell's # method-overload binding can silently resolve BinaryWriter.Write() to the wrong # overload and write garbage instead of the actual PNG bytes. foreach ($entry in $pngList) { $bw.Write([byte[]]$entry.Bytes) } $bw.Flush() $bw.Close() $fs.Close() Write-Output "Icon generated: $icoPath ($((Get-Item $icoPath).Length) bytes)" Write-Output "PNG generated: Assets/app.png"