如何用PowerShell将网站PNG图标转高质量ICO用于桌面快捷方式
提升PowerShell转换PNG到ICO的质量问题
我需要用PowerShell从网站下载favicon.png,再转换成适合桌面快捷方式的favicon.ico文件。已经掌握用Invoke-WebRequest获取网站favicon.png的方法,但之前使用的转换脚本生成的ICO文件质量极低。脚本需通过Intune部署,无法依赖本地资源,否则会直接制作ICO文件托管。
之前使用的转换脚本:
function ConvertTo-Icon { param( [Parameter(Mandatory=$true)] $bitmapPath, $iconPath = "$env:temp\newicon.ico" ) Add-Type -AssemblyName System.Drawing if (Test-Path $bitmapPath) { $b = [System.Drawing.Bitmap]::FromFile($bitmapPath) $icon = [System.Drawing.Icon]::FromHandle($b.GetHicon()) $file = New-Object System.IO.FileStream($iconPath, 'OpenOrCreate') $icon.Save($file) $file.Close() $icon.Dispose() explorer "/SELECT,$iconpath" } else { Write-Warning "$BitmapPath does not exist" } }
当前使用的完整代码:
param ( [string]$name, [string]$url, [string]$exe, [string]$ptemp = "c:\temp\png", [string]$icofold = "c:\Icons\", [string]$s2url = "https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=" ) #Append Icon URl if needed if ($url.SubString(0,8) -like 'https://') {} else { $url = $url.Insert(0,'https://')} $iconurl = -join($s2url, $url, "&size=64") #Verify temp directory and save .png if (Test-Path $ptemp) {} else { New-Item $ptemp -ItemType Directory} $temppng = -join($ptemp, "/", $name, ".png") Invoke-WebRequest -UseBasicParsing -uri $iconurl -outfile $temppng #Verify Icon Folder exists if (Test-Path $icofold) {} else { New-Item $icofold -ItemType Directory} #Convert to .ico and save Add-Type -AssemblyName System.Drawing $iconpath = -join($icofold, $name, ".ico") $img = [System.Drawing.Image]::FromFile($temppng) $img.Save($iconPath, [System.Drawing.Imaging.ImageFormat]::Icon) $img.Dispose() #Create Shortcut on public Desktop $desktopDir = Join-Path -Path $env:PUBLIC -ChildPath "Desktop" $destinationPath = Join-Path -Path $desktopDir -ChildPath "$name.lnk" $WshShell = New-Object -comObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($destinationPath) $Shortcut.TargetPath = $exe $Shortcut.Arguments = $url $Shortcut.IconLocation = $iconpath $Shortcut.Save() #cleanup remove-item $temppng
问题原因与解决方案
原脚本的问题在于:
- 直接通过
[System.Drawing.Icon]::FromHandle转换,仅生成单一尺寸的ICO,桌面快捷方式在不同场景下需要缩放该尺寸,导致模糊 - 使用
Image.Save直接转Icon格式,同样只保留单一尺寸,且未处理透明通道的优化
要生成高质量ICO,需创建包含多尺寸图标的ICO文件,让系统根据场景自动选择最合适的尺寸,避免缩放失真。以下是修改后的完整脚本:
修改后的完整脚本
param ( [string]$name, [string]$url, [string]$exe, [string]$ptemp = "c:\temp\png", [string]$icofold = "c:\Icons\", [string]$s2url = "https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=" ) #Append Icon URl if needed if ($url.SubString(0,8) -like 'https://') {} else { $url = $url.Insert(0,'https://')} $iconurl = -join($s2url, $url, "&size=64") #Verify temp directory and save .png if (Test-Path $ptemp) {} else { New-Item $ptemp -ItemType Directory} $temppng = -join($ptemp, "/", $name, ".png") Invoke-WebRequest -UseBasicParsing -uri $iconurl -outfile $temppng #Verify Icon Folder exists if (Test-Path $icofold) {} else { New-Item $icofold -ItemType Directory} #Convert to .ico and save (优化后的转换逻辑) Add-Type -AssemblyName System.Drawing $iconpath = -join($icofold, $name, ".ico") $originalBitmap = [System.Drawing.Bitmap]::FromFile($temppng) # 定义ICO需要包含的常用尺寸(覆盖桌面快捷方式的不同显示场景) $iconSizes = @(16, 32, 48, 64) # 创建内存流存储多尺寸ICO $memoryStream = New-Object System.IO.MemoryStream $iconWriter = New-Object System.BinaryWriter($memoryStream) # ICO文件头 $iconWriter.Write([byte]0) $iconWriter.Write([byte]0) $iconWriter.Write([int16]1) # 图标类型 $iconWriter.Write([int16]$iconSizes.Count) # 图标数量 $iconDataOffsets = @() $iconDataList = @() # 先写入每个图标的目录项,再收集图标数据 foreach ($size in $iconSizes) { # 缩放位图到目标尺寸,使用高质量插值 $scaledBitmap = New-Object System.Drawing.Bitmap($size, $size) $graphics = [System.Drawing.Graphics]::FromImage($scaledBitmap) $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $graphics.DrawImage($originalBitmap, 0, 0, $size, $size) $graphics.Dispose() # 将缩放后的位图转成Icon并获取其数据 $icon = [System.Drawing.Icon]::FromHandle($scaledBitmap.GetHicon()) $tempStream = New-Object System.IO.MemoryStream $icon.Save($tempStream) $iconData = $tempStream.ToArray() $iconDataList += $iconData # 记录数据偏移(目录项之后的位置) $iconDataOffsets += $memoryStream.Position + (16 * $iconSizes.Count) # 写入目录项 $iconWriter.Write([byte]$size) # 宽度 $iconWriter.Write([byte]$size) # 高度 $iconWriter.Write([byte]0) # 颜色数(0表示真彩色) $iconWriter.Write([byte]0) # 保留位 $iconWriter.Write([int16]1) # 平面数 $iconWriter.Write([int16]32) # 每像素位数(支持透明) $iconWriter.Write([int32]$iconData.Length) # 数据长度 $iconWriter.Write([int32]$iconDataOffsets[-1]) # 数据偏移 # 释放临时资源 $tempStream.Dispose() $icon.Dispose() $scaledBitmap.Dispose() } # 写入所有图标数据 foreach ($data in $iconDataList) { $iconWriter.Write($data) } # 保存到文件 $fileStream = New-Object System.IO.FileStream($iconPath, [System.IO.FileMode]::Create) $memoryStream.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null $memoryStream.CopyTo($fileStream) # 释放所有资源 $fileStream.Dispose() $memoryStream.Dispose() $originalBitmap.Dispose() #Create Shortcut on public Desktop $desktopDir = Join-Path -Path $env:PUBLIC -ChildPath "Desktop" $destinationPath = Join-Path -Path $desktopDir -ChildPath "$name.lnk" $WshShell = New-Object -comObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($destinationPath) $Shortcut.TargetPath = $exe $Shortcut.Arguments = $url $Shortcut.IconLocation = $iconpath $Shortcut.Save() #cleanup remove-item $temppng
优化说明
- 多尺寸支持:生成16x16、32x32、48x48、64x64四种常用尺寸,覆盖桌面快捷方式的小图标、大图标、任务栏显示等场景
- 高质量缩放:使用
HighQualityBicubic插值模式缩放位图,减少缩放失真 - 透明通道保留:指定32位色深,确保PNG的透明效果在ICO中正常显示
- 标准ICO结构:手动构建符合规范的ICO文件头和目录项,确保系统能正确识别多尺寸图标
内容的提问来源于stack exchange,提问作者iamsquash
相关产品推荐
相关产品推荐

