Files
YMhut-box-C-/scripts/generate-app-icons.ps1

188 lines
6.3 KiB
PowerShell

[CmdletBinding()]
param(
[string] $Root = (Split-Path -Parent $PSScriptRoot)
)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Drawing
$sourcePath = Join-Path $Root 'assets\icons\app_icon.png'
if (-not (Test-Path -LiteralPath $sourcePath -PathType Leaf)) {
throw "App icon source was not found: $sourcePath"
}
function New-ResizedBitmap {
param(
[Drawing.Image] $Source,
[int] $Width,
[int] $Height,
[int] $Padding = 0,
[Drawing.Color] $Background = [Drawing.Color]::Transparent,
[switch] $SharpenSmallAlpha
)
$bitmap = [Drawing.Bitmap]::new($Width, $Height, [Drawing.Imaging.PixelFormat]::Format32bppArgb)
$graphics = [Drawing.Graphics]::FromImage($bitmap)
try {
$graphics.CompositingMode = [Drawing.Drawing2D.CompositingMode]::SourceCopy
$graphics.CompositingQuality = [Drawing.Drawing2D.CompositingQuality]::HighQuality
$graphics.InterpolationMode = [Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graphics.PixelOffsetMode = [Drawing.Drawing2D.PixelOffsetMode]::HighQuality
$graphics.SmoothingMode = [Drawing.Drawing2D.SmoothingMode]::HighQuality
$graphics.Clear($Background)
$availableWidth = $Width - (2 * $Padding)
$availableHeight = $Height - (2 * $Padding)
$scale = [Math]::Min($availableWidth / $Source.Width, $availableHeight / $Source.Height)
$drawWidth = [Math]::Max(1, [int][Math]::Round($Source.Width * $scale))
$drawHeight = [Math]::Max(1, [int][Math]::Round($Source.Height * $scale))
$drawX = [int][Math]::Floor(($Width - $drawWidth) / 2)
$drawY = [int][Math]::Floor(($Height - $drawHeight) / 2)
$attributes = [Drawing.Imaging.ImageAttributes]::new()
try {
$attributes.SetWrapMode([Drawing.Drawing2D.WrapMode]::TileFlipXY)
$graphics.DrawImage(
$Source,
[Drawing.Rectangle]::new($drawX, $drawY, $drawWidth, $drawHeight),
0,
0,
$Source.Width,
$Source.Height,
[Drawing.GraphicsUnit]::Pixel,
$attributes)
} finally {
$attributes.Dispose()
}
} finally {
$graphics.Dispose()
}
if ($SharpenSmallAlpha) {
for ($y = 0; $y -lt $Height; $y++) {
for ($x = 0; $x -lt $Width; $x++) {
$color = $bitmap.GetPixel($x, $y)
if ($color.A -le 10) {
$bitmap.SetPixel($x, $y, [Drawing.Color]::Transparent)
} else {
$alpha = if ($color.A -ge 244) { 255 } else { [Math]::Min(255, [int](($color.A - 10) * 255 / 234)) }
$bitmap.SetPixel($x, $y, [Drawing.Color]::FromArgb($alpha, 0, 114, 255))
}
}
}
}
return $bitmap
}
function Get-PngBytes([Drawing.Bitmap] $Bitmap) {
$stream = [IO.MemoryStream]::new()
try {
$Bitmap.Save($stream, [Drawing.Imaging.ImageFormat]::Png)
return $stream.ToArray()
} finally {
$stream.Dispose()
}
}
function Write-PngIconFile([string] $Path, [hashtable] $Frames) {
$orderedSizes = @($Frames.Keys | ForEach-Object { [int] $_ } | Sort-Object)
$headerSize = 6 + (16 * $orderedSizes.Count)
$offset = $headerSize
$stream = [IO.MemoryStream]::new()
$writer = [IO.BinaryWriter]::new($stream)
try {
$writer.Write([uint16] 0)
$writer.Write([uint16] 1)
$writer.Write([uint16] $orderedSizes.Count)
foreach ($size in $orderedSizes) {
$bytes = [byte[]] $Frames[$size]
$writer.Write([byte] $(if ($size -eq 256) { 0 } else { $size }))
$writer.Write([byte] $(if ($size -eq 256) { 0 } else { $size }))
$writer.Write([byte] 0)
$writer.Write([byte] 0)
$writer.Write([uint16] 1)
$writer.Write([uint16] 32)
$writer.Write([uint32] $bytes.Length)
$writer.Write([uint32] $offset)
$offset += $bytes.Length
}
foreach ($size in $orderedSizes) {
$writer.Write([byte[]] $Frames[$size])
}
$writer.Flush()
$directory = Split-Path -Parent $Path
if ($directory) { [IO.Directory]::CreateDirectory($directory) | Out-Null }
[IO.File]::WriteAllBytes($Path, $stream.ToArray())
} finally {
$writer.Dispose()
$stream.Dispose()
}
}
function Save-Logo {
param(
[Drawing.Image] $Source,
[string] $Path,
[int] $Width,
[int] $Height,
[int] $Padding,
[Drawing.Color] $Background = [Drawing.Color]::Transparent
)
$bitmap = New-ResizedBitmap $Source $Width $Height $Padding $Background
try {
[IO.Directory]::CreateDirectory((Split-Path -Parent $Path)) | Out-Null
$bitmap.Save($Path, [Drawing.Imaging.ImageFormat]::Png)
} finally {
$bitmap.Dispose()
}
}
$loaded = [Drawing.Bitmap]::FromFile($sourcePath)
try {
$master = [Drawing.Bitmap]::new($loaded)
} finally {
$loaded.Dispose()
}
try {
$frames = @{}
foreach ($size in @(16, 20, 24, 32, 40, 48, 64, 96, 128, 256)) {
$frame = New-ResizedBitmap $master $size $size 0 ([Drawing.Color]::Transparent) -SharpenSmallAlpha:($size -le 24)
try {
$frames[$size] = Get-PngBytes $frame
} finally {
$frame.Dispose()
}
}
$iconTargets = @(
(Join-Path $Root 'assets\icon.ico'),
(Join-Path $Root 'assets\icons\app_icon.ico'),
(Join-Path $Root 'src\box-winUI\Assets\app_icon.ico'),
(Join-Path $Root 'src\YMhut.Box.PluginTauriHost\src-tauri\icons\icon.ico')
)
foreach ($target in $iconTargets) {
Write-PngIconFile $target $frames
}
$normalizedSource = Join-Path ([IO.Path]::GetTempPath()) ("ymhut-app-icon-{0}.png" -f [Guid]::NewGuid().ToString('N'))
try {
$master.Save($normalizedSource, [Drawing.Imaging.ImageFormat]::Png)
Copy-Item -LiteralPath $normalizedSource -Destination (Join-Path $Root 'assets\icons\app_icon.png') -Force
Copy-Item -LiteralPath $normalizedSource -Destination (Join-Path $Root 'assets\icons\icon.png') -Force
} finally {
Remove-Item -LiteralPath $normalizedSource -Force -ErrorAction SilentlyContinue
}
$projectAssets = Join-Path $Root 'src\box-winUI\Assets'
Save-Logo $master (Join-Path $projectAssets 'StoreLogo.png') 50 50 5
Save-Logo $master (Join-Path $projectAssets 'Square44x44Logo.png') 44 44 4
Save-Logo $master (Join-Path $projectAssets 'Square150x150Logo.png') 150 150 16
Save-Logo $master (Join-Path $projectAssets 'LockScreenLogo.png') 70 70 8
Save-Logo $master (Join-Path $projectAssets 'Wide310x150Logo.png') 310 150 24 ([Drawing.Color]::FromArgb(255, 245, 246, 247))
} finally {
$master.Dispose()
}
Write-Host 'Generated DPI-aware YMhut Box application icons.'