├── .github
└── workflows
│ └── publish-release.yml
├── .gitignore
├── .idea
└── .idea.SharpIco
│ └── .idea
│ ├── .gitignore
│ ├── encodings.xml
│ ├── indexLayout.xml
│ └── vcs.xml
├── LICENSE.txt
├── README.md
├── SharpIco.sln
├── SharpIco
├── IcoGenerator.cs
├── IcoInspector.cs
├── Program.cs
├── README.md
└── SharpIco.csproj
├── bucket
└── sharp-ico.json
└── docs
└── images
└── logo.png
/.github/workflows/publish-release.yml:
--------------------------------------------------------------------------------
1 | name: 发布SharpIco
2 | run-name: ${{ github.actor }} 正在发布SharpIco 🚀
3 |
4 | on:
5 | push:
6 | tags:
7 | - "v*.*.*" # 更明确的版本格式匹配
8 |
9 | # 为整个工作流设置权限
10 | permissions:
11 | contents: write
12 | id-token: write
13 | issues: write
14 |
15 | jobs:
16 | # 第一步:发布NuGet包
17 | publish-nuget:
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v3
22 | with:
23 | fetch-depth: 0 # 获取所有历史记录用于版本号计算
24 |
25 | - name: Setup .NET
26 | uses: actions/setup-dotnet@v3
27 | with:
28 | dotnet-version: 9.0.x
29 |
30 | - name: 缓存NuGet包
31 | uses: actions/cache@v3
32 | with:
33 | path: ~/.nuget/packages
34 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
35 | restore-keys: |
36 | ${{ runner.os }}-nuget-
37 |
38 | - name: 提取版本号
39 | id: get_version
40 | shell: bash
41 | run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
42 |
43 | - name: 恢复依赖
44 | run: dotnet restore ./SharpIco/SharpIco.csproj
45 |
46 | - name: 运行测试
47 | run: dotnet test --no-restore
48 |
49 | - name: 构建项目
50 | run: dotnet build --no-restore -c Release --nologo ./SharpIco/SharpIco.csproj -p:Version=${{ steps.get_version.outputs.VERSION }}
51 |
52 | - name: 创建NuGet包
53 | run: dotnet pack -c Release ./SharpIco/SharpIco.csproj -p:PackageVersion=${{ steps.get_version.outputs.VERSION }} --no-build --output ./nupkg
54 |
55 | - name: 发布到NuGet Gallery
56 | run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_GALLERY_TOKEN }} --source https://api.nuget.org/v3/index.json --skip-duplicate
57 |
58 | # 第二步:编译各平台可执行文件
59 | build-executables:
60 | needs: publish-nuget # 确保在NuGet包发布后运行
61 | strategy:
62 | fail-fast: false
63 | matrix:
64 | kind: ['windows', 'linux', 'macOS']
65 | include:
66 | - kind: windows
67 | os: windows-latest
68 | target: win-x64
69 | extension: '.zip'
70 | - kind: linux
71 | os: ubuntu-latest
72 | target: linux-x64
73 | extension: '.tar.gz'
74 | - kind: macOS
75 | os: macos-latest
76 | target: osx-x64
77 | extension: '.tar.gz'
78 |
79 | runs-on: ${{ matrix.os }}
80 |
81 | steps:
82 | - uses: actions/checkout@v3
83 | with:
84 | fetch-depth: 0 # 获取所有历史记录用于版本号计算
85 |
86 | - name: 提取版本号
87 | id: get_version
88 | shell: bash
89 | run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
90 |
91 | - name: Setup .NET
92 | uses: actions/setup-dotnet@v3
93 | with:
94 | dotnet-version: 9.0.x
95 |
96 | - name: 缓存NuGet包
97 | uses: actions/cache@v3
98 | with:
99 | path: ~/.nuget/packages
100 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
101 | restore-keys: |
102 | ${{ runner.os }}-nuget-
103 |
104 | - name: 安装Linux依赖
105 | if: matrix.kind == 'linux'
106 | run: |
107 | sudo apt-get update
108 | sudo apt-get install -y clang zlib1g-dev libkrb5-dev
109 |
110 | - name: 设置Windows环境
111 | if: matrix.kind == 'windows'
112 | shell: pwsh
113 | run: |
114 | Write-Host "设置Windows编译环境..."
115 | # 确保有最新的开发者工具
116 | choco install visualstudio2022buildtools -y --no-progress
117 |
118 | - name: 恢复依赖
119 | run: dotnet restore ./SharpIco/SharpIco.csproj
120 |
121 | - name: AOT编译
122 | run: |
123 | echo "正在为 ${{ matrix.kind }} 平台进行AOT编译..."
124 | dotnet publish ./SharpIco/SharpIco.csproj -c Release -r ${{ matrix.target }} --self-contained true -p:PublishAot=true -p:Version=${{ steps.get_version.outputs.VERSION }} -o ./publish/${{ matrix.kind }}
125 |
126 | - name: 打包Windows可执行文件
127 | if: matrix.kind == 'windows'
128 | run: |
129 | cd ./publish/${{ matrix.kind }}
130 | 7z a -tzip ../../SharpIco-${{ matrix.kind }}-${{ steps.get_version.outputs.VERSION }}${{ matrix.extension }} *
131 |
132 | - name: 打包Linux/macOS可执行文件
133 | if: matrix.kind != 'windows'
134 | run: |
135 | cd ./publish/${{ matrix.kind }}
136 | tar -czvf ../../SharpIco-${{ matrix.kind }}-${{ steps.get_version.outputs.VERSION }}${{ matrix.extension }} *
137 |
138 | # 上传构建产物作为工作流构件(artifacts)
139 | - name: 上传构建产物
140 | uses: actions/upload-artifact@v4
141 | with:
142 | name: SharpIco-${{ matrix.kind }}-${{ steps.get_version.outputs.VERSION }}
143 | path: ./SharpIco-${{ matrix.kind }}-${{ steps.get_version.outputs.VERSION }}${{ matrix.extension }}
144 | retention-days: 1
145 |
146 | # 第三步:统一上传所有平台可执行文件到GitHub Release
147 | upload-to-release:
148 | needs: build-executables
149 | runs-on: ubuntu-latest
150 |
151 | steps:
152 | - name: 提取版本号
153 | id: get_version
154 | shell: bash
155 | run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
156 |
157 | # 下载所有平台构建产物
158 | - name: 下载Windows构建产物
159 | uses: actions/download-artifact@v4
160 | with:
161 | name: SharpIco-windows-${{ steps.get_version.outputs.VERSION }}
162 | path: ./artifacts
163 |
164 | - name: 下载Linux构建产物
165 | uses: actions/download-artifact@v4
166 | with:
167 | name: SharpIco-linux-${{ steps.get_version.outputs.VERSION }}
168 | path: ./artifacts
169 |
170 | - name: 下载macOS构建产物
171 | uses: actions/download-artifact@v4
172 | with:
173 | name: SharpIco-macOS-${{ steps.get_version.outputs.VERSION }}
174 | path: ./artifacts
175 |
176 | # 列出下载的文件以确认
177 | - name: 列出下载的文件
178 | run: ls -la ./artifacts
179 |
180 | # 统一上传到GitHub Release
181 | - name: 上传所有文件到GitHub Release
182 | uses: softprops/action-gh-release@v1
183 | env:
184 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
185 | with:
186 | files: ./artifacts/*
187 | tag_name: ${{ github.ref }}
188 | fail_on_unmatched_files: false
189 | draft: false
190 | name: SharpIco 版本 ${{ steps.get_version.outputs.VERSION }}
191 | generate_release_notes: true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | /packages/
4 | riderModule.iml
5 | /_ReSharper.Caches/### VisualStudioCode template
6 | .vscode/*
7 | !.vscode/settings.json
8 | !.vscode/tasks.json
9 | !.vscode/launch.json
10 | !.vscode/extensions.json
11 | !.vscode/*.code-snippets
12 |
13 | # Local History for Visual Studio Code
14 | .history/
15 |
16 | # Built Visual Studio Code Extensions
17 | *.vsix
18 |
19 | ### VisualStudio template
20 | ## Ignore Visual Studio temporary files, build results, and
21 | ## files generated by popular Visual Studio add-ons.
22 | ##
23 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
24 |
25 | # User-specific files
26 | *.rsuser
27 | *.suo
28 | *.user
29 | *.userosscache
30 | *.sln.docstates
31 |
32 | # User-specific files (MonoDevelop/Xamarin Studio)
33 | *.userprefs
34 |
35 | # Mono auto generated files
36 | mono_crash.*
37 |
38 | # Build results
39 | [Dd]ebug/
40 | [Dd]ebugPublic/
41 | [Rr]elease/
42 | [Rr]eleases/
43 | x64/
44 | x86/
45 | [Ww][Ii][Nn]32/
46 | [Aa][Rr][Mm]/
47 | [Aa][Rr][Mm]64/
48 | bld/
49 | [Bb]in/
50 | [Oo]bj/
51 | [Ll]og/
52 | [Ll]ogs/
53 |
54 | # Visual Studio 2015/2017 cache/options directory
55 | .vs/
56 | # Uncomment if you have tasks that create the project's static files in wwwroot
57 | #wwwroot/
58 |
59 | # Visual Studio 2017 auto generated files
60 | Generated\ Files/
61 |
62 | # MSTest test Results
63 | [Tt]est[Rr]esult*/
64 | [Bb]uild[Ll]og.*
65 |
66 | # NUnit
67 | *.VisualState.xml
68 | TestResult.xml
69 | nunit-*.xml
70 |
71 | # Build Results of an ATL Project
72 | [Dd]ebugPS/
73 | [Rr]eleasePS/
74 | dlldata.c
75 |
76 | # Benchmark Results
77 | BenchmarkDotNet.Artifacts/
78 |
79 | # .NET Core
80 | project.lock.json
81 | project.fragment.lock.json
82 | artifacts/
83 |
84 | # ASP.NET Scaffolding
85 | ScaffoldingReadMe.txt
86 |
87 | # StyleCop
88 | StyleCopReport.xml
89 |
90 | # Files built by Visual Studio
91 | *_i.c
92 | *_p.c
93 | *_h.h
94 | *.ilk
95 | *.meta
96 | *.obj
97 | *.iobj
98 | *.pch
99 | *.pdb
100 | *.ipdb
101 | *.pgc
102 | *.pgd
103 | *.rsp
104 | *.sbr
105 | *.tlb
106 | *.tli
107 | *.tlh
108 | *.tmp
109 | *.tmp_proj
110 | *_wpftmp.csproj
111 | *.log
112 | *.tlog
113 | *.vspscc
114 | *.vssscc
115 | .builds
116 | *.pidb
117 | *.svclog
118 | *.scc
119 |
120 | # Chutzpah Test files
121 | _Chutzpah*
122 |
123 | # Visual C++ cache files
124 | ipch/
125 | *.aps
126 | *.ncb
127 | *.opendb
128 | *.opensdf
129 | *.sdf
130 | *.cachefile
131 | *.VC.db
132 | *.VC.VC.opendb
133 |
134 | # Visual Studio profiler
135 | *.psess
136 | *.vsp
137 | *.vspx
138 | *.sap
139 |
140 | # Visual Studio Trace Files
141 | *.e2e
142 |
143 | # TFS 2012 Local Workspace
144 | $tf/
145 |
146 | # Guidance Automation Toolkit
147 | *.gpState
148 |
149 | # ReSharper is a .NET coding add-in
150 | _ReSharper*/
151 | *.[Rr]e[Ss]harper
152 | *.DotSettings.user
153 |
154 | # TeamCity is a build add-in
155 | _TeamCity*
156 |
157 | # DotCover is a Code Coverage Tool
158 | *.dotCover
159 |
160 | # AxoCover is a Code Coverage Tool
161 | .axoCover/*
162 | !.axoCover/settings.json
163 |
164 | # Coverlet is a free, cross platform Code Coverage Tool
165 | coverage*.json
166 | coverage*.xml
167 | coverage*.info
168 |
169 | # Visual Studio code coverage results
170 | *.coverage
171 | *.coveragexml
172 |
173 | # NCrunch
174 | _NCrunch_*
175 | .*crunch*.local.xml
176 | nCrunchTemp_*
177 |
178 | # MightyMoose
179 | *.mm.*
180 | AutoTest.Net/
181 |
182 | # Web workbench (sass)
183 | .sass-cache/
184 |
185 | # Installshield output folder
186 | [Ee]xpress/
187 |
188 | # DocProject is a documentation generator add-in
189 | DocProject/buildhelp/
190 | DocProject/Help/*.HxT
191 | DocProject/Help/*.HxC
192 | DocProject/Help/*.hhc
193 | DocProject/Help/*.hhk
194 | DocProject/Help/*.hhp
195 | DocProject/Help/Html2
196 | DocProject/Help/html
197 |
198 | # Click-Once directory
199 | publish/
200 |
201 | # Publish Web Output
202 | *.[Pp]ublish.xml
203 | *.azurePubxml
204 | # Note: Comment the next line if you want to checkin your web deploy settings,
205 | # but database connection strings (with potential passwords) will be unencrypted
206 | *.pubxml
207 | *.publishproj
208 |
209 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
210 | # checkin your Azure Web App publish settings, but sensitive information contained
211 | # in these scripts will be unencrypted
212 | PublishScripts/
213 |
214 | # NuGet Packages
215 | *.nupkg
216 | # NuGet Symbol Packages
217 | *.snupkg
218 | # The packages folder can be ignored because of Package Restore
219 | **/[Pp]ackages/*
220 | # except build/, which is used as an MSBuild target.
221 | !**/[Pp]ackages/build/
222 | # Uncomment if necessary however generally it will be regenerated when needed
223 | #!**/[Pp]ackages/repositories.config
224 | # NuGet v3's project.json files produces more ignorable files
225 | *.nuget.props
226 | *.nuget.targets
227 |
228 | # Microsoft Azure Build Output
229 | csx/
230 | *.build.csdef
231 |
232 | # Microsoft Azure Emulator
233 | ecf/
234 | rcf/
235 |
236 | # Windows Store app package directories and files
237 | AppPackages/
238 | BundleArtifacts/
239 | Package.StoreAssociation.xml
240 | _pkginfo.txt
241 | *.appx
242 | *.appxbundle
243 | *.appxupload
244 |
245 | # Visual Studio cache files
246 | # files ending in .cache can be ignored
247 | *.[Cc]ache
248 | # but keep track of directories ending in .cache
249 | !?*.[Cc]ache/
250 |
251 | # Others
252 | ClientBin/
253 | ~$*
254 | *~
255 | *.dbmdl
256 | *.dbproj.schemaview
257 | *.jfm
258 | *.pfx
259 | *.publishsettings
260 | orleans.codegen.cs
261 |
262 | # Including strong name files can present a security risk
263 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
264 | #*.snk
265 |
266 | # Since there are multiple workflows, uncomment next line to ignore bower_components
267 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
268 | #bower_components/
269 |
270 | # RIA/Silverlight projects
271 | Generated_Code/
272 |
273 | # Backup & report files from converting an old project file
274 | # to a newer Visual Studio version. Backup files are not needed,
275 | # because we have git ;-)
276 | _UpgradeReport_Files/
277 | Backup*/
278 | UpgradeLog*.XML
279 | UpgradeLog*.htm
280 | ServiceFabricBackup/
281 | *.rptproj.bak
282 |
283 | # SQL Server files
284 | *.mdf
285 | *.ldf
286 | *.ndf
287 |
288 | # Business Intelligence projects
289 | *.rdl.data
290 | *.bim.layout
291 | *.bim_*.settings
292 | *.rptproj.rsuser
293 | *- [Bb]ackup.rdl
294 | *- [Bb]ackup ([0-9]).rdl
295 | *- [Bb]ackup ([0-9][0-9]).rdl
296 |
297 | # Microsoft Fakes
298 | FakesAssemblies/
299 |
300 | # GhostDoc plugin setting file
301 | *.GhostDoc.xml
302 |
303 | # Node.js Tools for Visual Studio
304 | .ntvs_analysis.dat
305 | node_modules/
306 |
307 | # Visual Studio 6 build log
308 | *.plg
309 |
310 | # Visual Studio 6 workspace options file
311 | *.opt
312 |
313 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
314 | *.vbw
315 |
316 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
317 | *.vbp
318 |
319 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
320 | *.dsw
321 | *.dsp
322 |
323 | # Visual Studio 6 technical files
324 |
325 | # Visual Studio LightSwitch build output
326 | **/*.HTMLClient/GeneratedArtifacts
327 | **/*.DesktopClient/GeneratedArtifacts
328 | **/*.DesktopClient/ModelManifest.xml
329 | **/*.Server/GeneratedArtifacts
330 | **/*.Server/ModelManifest.xml
331 | _Pvt_Extensions
332 |
333 | # Paket dependency manager
334 | .paket/paket.exe
335 | paket-files/
336 |
337 | # FAKE - F# Make
338 | .fake/
339 |
340 | # CodeRush personal settings
341 | .cr/personal
342 |
343 | # Python Tools for Visual Studio (PTVS)
344 | __pycache__/
345 | *.pyc
346 |
347 | # Cake - Uncomment if you are using it
348 | # tools/**
349 | # !tools/packages.config
350 |
351 | # Tabs Studio
352 | *.tss
353 |
354 | # Telerik's JustMock configuration file
355 | *.jmconfig
356 |
357 | # BizTalk build output
358 | *.btp.cs
359 | *.btm.cs
360 | *.odx.cs
361 | *.xsd.cs
362 |
363 | # OpenCover UI analysis results
364 | OpenCover/
365 |
366 | # Azure Stream Analytics local run output
367 | ASALocalRun/
368 |
369 | # MSBuild Binary and Structured Log
370 | *.binlog
371 |
372 | # NVidia Nsight GPU debugger configuration file
373 | *.nvuser
374 |
375 | # MFractors (Xamarin productivity tool) working folder
376 | .mfractor/
377 |
378 | # Local History for Visual Studio
379 | .localhistory/
380 |
381 | # Visual Studio History (VSHistory) files
382 | .vshistory/
383 |
384 | # BeatPulse healthcheck temp database
385 | healthchecksdb
386 |
387 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
388 | MigrationBackup/
389 |
390 | # Ionide (cross platform F# VS Code tools) working folder
391 | .ionide/
392 |
393 | # Fody - auto-generated XML schema
394 | FodyWeavers.xsd
395 |
396 | # VS Code files for those working on multiple tools
397 | *.code-workspace
398 |
399 | # Local History for Visual Studio Code
400 |
401 | # Windows Installer files from build outputs
402 | *.cab
403 | *.msi
404 | *.msix
405 | *.msm
406 | *.msp
407 |
408 | # JetBrains Rider
409 | *.sln.iml
410 |
411 | ### Csharp template
412 | ## Ignore Visual Studio temporary files, build results, and
413 | ## files generated by popular Visual Studio add-ons.
414 | ##
415 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
416 |
417 | # User-specific files
418 |
419 | # User-specific files (MonoDevelop/Xamarin Studio)
420 |
421 | # Mono auto generated files
422 |
423 | # Build results
424 |
425 | # Visual Studio 2015/2017 cache/options directory
426 | # Uncomment if you have tasks that create the project's static files in wwwroot
427 | #wwwroot/
428 |
429 | # Visual Studio 2017 auto generated files
430 |
431 | # MSTest test Results
432 |
433 | # NUnit
434 |
435 | # Build Results of an ATL Project
436 |
437 | # Benchmark Results
438 |
439 | # .NET Core
440 |
441 | # ASP.NET Scaffolding
442 |
443 | # StyleCop
444 |
445 | # Files built by Visual Studio
446 |
447 | # Chutzpah Test files
448 |
449 | # Visual C++ cache files
450 |
451 | # Visual Studio profiler
452 |
453 | # Visual Studio Trace Files
454 |
455 | # TFS 2012 Local Workspace
456 |
457 | # Guidance Automation Toolkit
458 |
459 | # ReSharper is a .NET coding add-in
460 |
461 | # TeamCity is a build add-in
462 |
463 | # DotCover is a Code Coverage Tool
464 |
465 | # AxoCover is a Code Coverage Tool
466 |
467 | # Coverlet is a free, cross platform Code Coverage Tool
468 |
469 | # Visual Studio code coverage results
470 |
471 | # NCrunch
472 |
473 | # MightyMoose
474 |
475 | # Web workbench (sass)
476 |
477 | # Installshield output folder
478 |
479 | # DocProject is a documentation generator add-in
480 |
481 | # Click-Once directory
482 |
483 | # Publish Web Output
484 | # Note: Comment the next line if you want to checkin your web deploy settings,
485 | # but database connection strings (with potential passwords) will be unencrypted
486 |
487 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
488 | # checkin your Azure Web App publish settings, but sensitive information contained
489 | # in these scripts will be unencrypted
490 |
491 | # NuGet Packages
492 | # NuGet Symbol Packages
493 | # The packages folder can be ignored because of Package Restore
494 | # except build/, which is used as an MSBuild target.
495 | # Uncomment if necessary however generally it will be regenerated when needed
496 | #!**/[Pp]ackages/repositories.config
497 | # NuGet v3's project.json files produces more ignorable files
498 |
499 | # Microsoft Azure Build Output
500 |
501 | # Microsoft Azure Emulator
502 |
503 | # Windows Store app package directories and files
504 |
505 | # Visual Studio cache files
506 | # files ending in .cache can be ignored
507 | # but keep track of directories ending in .cache
508 |
509 | # Others
510 |
511 | # Including strong name files can present a security risk
512 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
513 | #*.snk
514 |
515 | # Since there are multiple workflows, uncomment next line to ignore bower_components
516 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
517 | #bower_components/
518 |
519 | # RIA/Silverlight projects
520 |
521 | # Backup & report files from converting an old project file
522 | # to a newer Visual Studio version. Backup files are not needed,
523 | # because we have git ;-)
524 |
525 | # SQL Server files
526 |
527 | # Business Intelligence projects
528 |
529 | # Microsoft Fakes
530 |
531 | # GhostDoc plugin setting file
532 |
533 | # Node.js Tools for Visual Studio
534 |
535 | # Visual Studio 6 build log
536 |
537 | # Visual Studio 6 workspace options file
538 |
539 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
540 |
541 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
542 |
543 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
544 |
545 | # Visual Studio 6 technical files
546 |
547 | # Visual Studio LightSwitch build output
548 |
549 | # Paket dependency manager
550 |
551 | # FAKE - F# Make
552 |
553 | # CodeRush personal settings
554 |
555 | # Python Tools for Visual Studio (PTVS)
556 |
557 | # Cake - Uncomment if you are using it
558 | # tools/**
559 | # !tools/packages.config
560 |
561 | # Tabs Studio
562 |
563 | # Telerik's JustMock configuration file
564 |
565 | # BizTalk build output
566 |
567 | # OpenCover UI analysis results
568 |
569 | # Azure Stream Analytics local run output
570 |
571 | # MSBuild Binary and Structured Log
572 |
573 | # NVidia Nsight GPU debugger configuration file
574 |
575 | # MFractors (Xamarin productivity tool) working folder
576 |
577 | # Local History for Visual Studio
578 |
579 | # Visual Studio History (VSHistory) files
580 |
581 | # BeatPulse healthcheck temp database
582 |
583 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
584 |
585 | # Ionide (cross platform F# VS Code tools) working folder
586 |
587 | # Fody - auto-generated XML schema
588 |
589 | # VS Code files for those working on multiple tools
590 |
591 | # Local History for Visual Studio Code
592 |
593 | # Windows Installer files from build outputs
594 |
595 | # JetBrains Rider
596 |
597 | ### Rider template
598 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
599 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
600 |
601 | # User-specific stuff
602 | .idea/**/workspace.xml
603 | .idea/**/tasks.xml
604 | .idea/**/usage.statistics.xml
605 | .idea/**/dictionaries
606 | .idea/**/shelf
607 |
608 | # AWS User-specific
609 | .idea/**/aws.xml
610 |
611 | # Generated files
612 | .idea/**/contentModel.xml
613 |
614 | # Sensitive or high-churn files
615 | .idea/**/dataSources/
616 | .idea/**/dataSources.ids
617 | .idea/**/dataSources.local.xml
618 | .idea/**/sqlDataSources.xml
619 | .idea/**/dynamic.xml
620 | .idea/**/uiDesigner.xml
621 | .idea/**/dbnavigator.xml
622 |
623 | # Gradle
624 | .idea/**/gradle.xml
625 | .idea/**/libraries
626 |
627 | # Gradle and Maven with auto-import
628 | # When using Gradle or Maven with auto-import, you should exclude module files,
629 | # since they will be recreated, and may cause churn. Uncomment if using
630 | # auto-import.
631 | # .idea/artifacts
632 | # .idea/compiler.xml
633 | # .idea/jarRepositories.xml
634 | # .idea/modules.xml
635 | # .idea/*.iml
636 | # .idea/modules
637 | # *.iml
638 | # *.ipr
639 |
640 | # CMake
641 | cmake-build-*/
642 |
643 | # Mongo Explorer plugin
644 | .idea/**/mongoSettings.xml
645 |
646 | # File-based project format
647 | *.iws
648 |
649 | # IntelliJ
650 | out/
651 |
652 | # mpeltonen/sbt-idea plugin
653 | .idea_modules/
654 |
655 | # JIRA plugin
656 | atlassian-ide-plugin.xml
657 |
658 | # Cursive Clojure plugin
659 | .idea/replstate.xml
660 |
661 | # SonarLint plugin
662 | .idea/sonarlint/
663 |
664 | # Crashlytics plugin (for Android Studio and IntelliJ)
665 | com_crashlytics_export_strings.xml
666 | crashlytics.properties
667 | crashlytics-build.properties
668 | fabric.properties
669 |
670 | # Editor-based Rest Client
671 | .idea/httpRequests
672 |
673 | # Android studio 3.1+ serialized cache file
674 | .idea/caches/build_file_checksums.ser
675 |
676 |
--------------------------------------------------------------------------------
/.idea/.idea.SharpIco/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /projectSettingsUpdater.xml
6 | /.idea.SharpIco.iml
7 | /modules.xml
8 | /contentModel.xml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 | # Datasource local storage ignored files
12 | /dataSources/
13 | /dataSources.local.xml
14 |
--------------------------------------------------------------------------------
/.idea/.idea.SharpIco/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.SharpIco/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.SharpIco/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SharpIco
2 |
3 |
4 |
5 | 
6 | 
7 |
8 | SharpIco是一个纯 C# AOT 实现的轻量级图标生成工具,用于生成和检查ICO图标文件。可将一张高分辨率 PNG 图片一键生成标准的 Windows .ico 图标文件,内含多种尺寸(16x16 到 512x512),还可以自定义尺寸。
9 |
10 | 通过SharpIco,您可以轻松将PNG图像转换为包含多种尺寸的ICO图标,完全摆脱命令行依赖,无需 ImageMagick、Node.js 或 Python,适合在 .NET 项目中内嵌、分发或集成自动打包流程中使用。
11 |
12 | 除了图标生成,SharpIco 还内置图标结构分析功能,助你轻松验证 .ico 文件中包含的图层与尺寸。
13 |
14 | 🚀 跨平台、零依赖、极速生成,一切尽在 SharpIco!
15 |
16 |
17 | ## ✨ 功能特点
18 |
19 | - 🖼️ 将PNG图像转换为多尺寸ICO图标
20 | - 🔍 支持生成包含自定义尺寸的ICO图标(最高支持1024×1024)
21 | - 🧐 检查ICO文件的内部结构和信息
22 | - 📏 准确识别并显示超大尺寸图标(如512×512、1024×1024)的实际尺寸
23 |
24 | ## 📦 安装
25 |
26 | ### 使用 Scoop 安装 (Windows)
27 |
28 | ```bash
29 | # 添加自定义 bucket
30 | scoop bucket add sharp-ico https://github.com/star-plan/sharp-ico-bucket
31 |
32 | # 安装 SharpIco
33 | scoop install sharp-ico
34 | ```
35 |
36 | ### 作为 .NET Global Tool 安装
37 |
38 | ```bash
39 | dotnet tool install --global SharpIco
40 | ```
41 |
42 | ### 从源码构建
43 |
44 | ```bash
45 | git clone https://github.com/star-plan/sharp-ico.git
46 | cd SharpIco
47 | dotnet build -c Release
48 | ```
49 |
50 | ## 🚀 使用方法
51 |
52 | ### 生成ICO图标
53 |
54 | ```bash
55 | sharpico generate -i input.png -o output.ico
56 | ```
57 |
58 | 可选参数:
59 | - `-s, --sizes`: 指定图标尺寸,默认为 16,32,48,64,128,256,512,1024
60 |
61 |
62 | 示例:
63 | ```bash
64 | sharpico generate -i input.png -o output.ico -s 16,32,64,128
65 | ```
66 |
67 | ### 检查ICO文件结构
68 |
69 | ```bash
70 | sharpico inspect icon.ico
71 | ```
72 |
73 | ## 📋 参数说明
74 |
75 | ### 生成命令(generate)
76 |
77 | | 参数 | 缩写 | 说明 | 是否必需 |
78 | | --- | --- | --- | --- |
79 | | `--input` | `-i` | 输入PNG图像文件路径 | 是 |
80 | | `--output` | `-o` | 输出ICO文件路径 | 是 |
81 | | `--sizes` | `-s` | 图标尺寸列表,以逗号分隔 | 否,默认为16,32,48,64,128,256,512,1024 |
82 |
83 | ### 检查命令(inspect)
84 |
85 | | 参数 | 说明 | 是否必需 |
86 | | --- | --- | --- |
87 | | `ico-file` | 要检查的ICO文件路径 | 是 |
88 |
89 | ## 📝 示例输出
90 |
91 | ### 生成ICO图标
92 |
93 | ```
94 | 正在将 logo.png 转换为 logo.ico...
95 | 生成尺寸: 16, 32, 48, 64, 128, 256, 512, 1024
96 | ICO 文件已生成: logo.ico
97 | ```
98 |
99 | ### 检查ICO文件
100 |
101 | ```
102 | 正在检查ICO文件: logo.ico
103 | 图标数量: 8
104 | - 第1张图像: 16x16, 32bpp, 大小: 840字节, 偏移: 150
105 | - 第2张图像: 32x32, 32bpp, 大小: 1939字节, 偏移: 990
106 | - 第3张图像: 48x48, 32bpp, 大小: 3375字节, 偏移: 2929
107 | - 第4张图像: 64x64, 32bpp, 大小: 4951字节, 偏移: 6304
108 | - 第5张图像: 128x128, 32bpp, 大小: 13782字节, 偏移: 11255
109 | - 第6张图像: 256x256, 32bpp, 大小: 37823字节, 偏移: 25037
110 | - 第7张图像: 512x512, 32bpp, 大小: 114655字节, 偏移: 62860
111 | 注意: 文件头中指定的尺寸为256x256,但实际图像尺寸为512x512
112 | - 第8张图像: 1024x1024, 32bpp, 大小: 248965字节, 偏移: 177515
113 | 注意: 文件头中指定的尺寸为256x256,但实际图像尺寸为1024x1024
114 | ```
115 |
116 | ## 🔍 关于ICO格式的说明
117 |
118 | ICO文件格式在表示图像尺寸时有一个限制:宽度和高度字段各只有一个字节,值范围是0-255。当这些字段为0时,按照规范表示256像素。对于大于256的尺寸(如512×512或1024×1024),在文件头中仍然会显示为0(即256),但实际图像数据可以包含更大尺寸的图像。
119 |
120 | SharpIco的inspect命令会解析每个图像的实际数据,以获取其真实尺寸。这使得工具能够准确显示ICO文件中图像的实际分辨率,即使它们超过了ICO头部表示的范围限制。当头部信息与实际图像尺寸不一致时,SharpIco会显示相应的提示信息。
121 |
122 | ## 🛠️ 技术实现
123 |
124 | SharpIco使用以下技术:
125 |
126 | - .NET 9.0
127 | - [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) - 用于图像处理
128 | - [System.CommandLine](https://github.com/dotnet/command-line-api) - 用于命令行参数解析
129 |
130 | ## 📄 许可证
131 |
132 | MIT License
133 |
134 | ## 开发者指南
135 |
136 | ### 构建项目
137 |
138 | ```bash
139 | dotnet build
140 | ```
141 |
142 | ### 发布模式
143 |
144 | SharpIco 支持两种发布模式:
145 |
146 | #### AOT 发布 (原生性能,无需 .NET 运行时)
147 |
148 | ```bash
149 | dotnet publish -r win-x64 -c release /p:PublishAot=true /p:TrimMode=full /p:InvariantGlobalization=true /p:IlcGenerateStackTraceData=false /p:IlcOptimizationPreference=Size /p:IlcFoldIdenticalMethodBodies=true /p:JsonSerializerIsReflectionEnabledByDefault=true
150 | ```
151 |
152 | 支持的平台:
153 |
154 | - Windows: win-x64
155 | - macOS: osx-x64, osx-arm64
156 | - Linux: linux-x64, linux-arm64
157 |
158 | #### Framework Dependent 发布 (需要 .NET 运行时)
159 |
160 | ```bash
161 | # 发布为 NuGet 包 (.NET Tool)
162 | dotnet pack
163 |
164 | # 安装本地打包的工具
165 | dotnet tool install --global --add-source ./SharpIco/nupkg SharpIco
166 | ```
167 |
168 | ### 创建 Scoop 安装包
169 |
170 | - 编译 AOT 版本并创建 zip 文件
171 | - 在 GitHub Releases 中发布 AOT 编译的 zip 包
172 | - 计算 zip 文件的 SHA256 哈希值
173 | - 更新 `bucket/sharp-ico.json` 中的版本号和哈希值
174 |
175 | sharp-ico.json 内容示例
176 |
177 | ```json
178 | {
179 | "version": "1.0.0",
180 | "description": "轻量级ICO图标生成和检查工具",
181 | "homepage": "https://github.com/star-plan/sharp-ico",
182 | "license": "MIT",
183 | "architecture": {
184 | "64bit": {
185 | "url": "https://github.com/star-plan/sharp-ico/releases/download/v1.0.0/sharpico-win-x64.zip",
186 | "hash": "SHA256哈希值,发布后填写"
187 | }
188 | },
189 | "bin": "sharpico.exe",
190 | "checkver": "github",
191 | "autoupdate": {
192 | "architecture": {
193 | "64bit": {
194 | "url": "https://github.com/star-plan/sharp-ico/releases/download/v$version/sharpico-win-x64.zip"
195 | }
196 | }
197 | }
198 | }
199 | ```
200 |
201 | 使用此方法,您的用户可以通过以下简单步骤安装您的工具:
202 |
203 | ```bash
204 | scoop bucket add sharp-ico https://github.com/star-plan/sharp-ico
205 | scoop install sharp-ico
206 | ```
207 |
208 | 当您发布新版本时,用户只需运行 `scoop update sharp-ico` 即可获取最新版本。
209 |
210 |
211 | ### 两种发布流程
212 |
213 | #### AOT 发布流程 (Scoop/独立应用)
214 |
215 | 1. 编译 AOT 版本:
216 | ```bash
217 | dotnet publish -r win-x64 -c release /p:PublishAot=true /p:TrimMode=full /p:InvariantGlobalization=true /p:IlcGenerateStackTraceData=false /p:IlcOptimizationPreference=Size /p:IlcFoldIdenticalMethodBodies=true /p:JsonSerializerIsReflectionEnabledByDefault=true
218 | ```
219 |
220 | 2. 打包生成的文件:
221 | ```bash
222 | # 进入发布目录
223 | cd SharpIco/bin/release/net9.0/win-x64/publish/
224 | # 创建 zip 包
225 | powershell Compress-Archive -Path * -DestinationPath sharpico-win-x64.zip
226 | ```
227 |
228 | 3. 将生成的 zip 文件上传到 GitHub Releases
229 |
230 | #### .NET Tool 发布流程
231 |
232 | 1. 打包为 NuGet 包:
233 | ```bash
234 | dotnet pack
235 | ```
236 |
237 | 2. 生成的包将位于 `./SharpIco/nupkg` 目录中
238 |
239 | 3. 发布到 NuGet:
240 | ```bash
241 | dotnet nuget push ./SharpIco/nupkg/SharpIco.1.0.0.nupkg --api-key 您的API密钥 --source https://api.nuget.org/v3/index.json
242 | ```
243 |
244 | ### 本地测试
245 |
246 | #### 测试 .NET Tool
247 |
248 | ```bash
249 | # 安装本地打包的工具
250 | dotnet tool install --global --add-source ./SharpIco/nupkg SharpIco
251 |
252 | # 卸载工具
253 | dotnet tool uninstall --global SharpIco
254 | ```
255 |
256 | #### 测试 AOT 发布版本
257 |
258 | 直接运行生成的可执行文件:
259 | ```bash
260 | ./SharpIco/bin/release/net9.0/win-x64/publish/sharpico.exe
261 | ```
262 |
--------------------------------------------------------------------------------
/SharpIco.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpIco", "SharpIco\SharpIco.csproj", "{CFD818A8-F27F-4A89-BA8B-BE5AF3D150DC}"
4 | EndProject
5 | Global
6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
7 | Debug|Any CPU = Debug|Any CPU
8 | Release|Any CPU = Release|Any CPU
9 | EndGlobalSection
10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
11 | {CFD818A8-F27F-4A89-BA8B-BE5AF3D150DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12 | {CFD818A8-F27F-4A89-BA8B-BE5AF3D150DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
13 | {CFD818A8-F27F-4A89-BA8B-BE5AF3D150DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
14 | {CFD818A8-F27F-4A89-BA8B-BE5AF3D150DC}.Release|Any CPU.Build.0 = Release|Any CPU
15 | EndGlobalSection
16 | EndGlobal
17 |
--------------------------------------------------------------------------------
/SharpIco/IcoGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using SixLabors.ImageSharp;
5 | using SixLabors.ImageSharp.Processing;
6 |
7 | namespace SharpIco;
8 |
9 | public static class IcoGenerator {
10 | // 默认尺寸数组
11 | private static readonly int[] DefaultSizes = new[] { 16, 32, 48, 64, 128, 256, 512 };
12 |
13 | public static void GenerateIcon(string sourcePng, string outputIco) {
14 | GenerateIcon(sourcePng, outputIco, DefaultSizes);
15 | }
16 |
17 | public static void GenerateIcon(string sourcePng, string outputIco, int[] sizes) {
18 | var images = new List();
19 |
20 | using var original = Image.Load(sourcePng);
21 |
22 | foreach (var size in sizes) {
23 | using var clone = original.Clone(ctx => ctx.Resize(size, size));
24 | using var ms = new MemoryStream();
25 | clone.SaveAsPng(ms);
26 | images.Add(ms.ToArray());
27 | }
28 |
29 | using var fs = new FileStream(outputIco, FileMode.Create);
30 | using var writer = new BinaryWriter(fs);
31 |
32 | // ICONDIR
33 | writer.Write((ushort)0); // reserved
34 | writer.Write((ushort)1); // image type: 1 = icon
35 | writer.Write((ushort)images.Count); // number of images
36 |
37 | int offset = 6 + (16 * images.Count);
38 |
39 | foreach (var image in images) {
40 | using var ms = new MemoryStream(image);
41 | using var img = Image.Load(ms);
42 |
43 | writer.Write((byte)(img.Width == 256 ? 0 : img.Width)); // width
44 | writer.Write((byte)(img.Height == 256 ? 0 : img.Height)); // height
45 | writer.Write((byte)0); // colors in palette
46 | writer.Write((byte)0); // reserved
47 | writer.Write((ushort)1); // color planes
48 | writer.Write((ushort)32); // bits per pixel
49 | writer.Write(image.Length); // size of image data
50 | writer.Write(offset); // offset of image data
51 |
52 | offset += image.Length;
53 | }
54 |
55 | // 写入图片数据
56 | foreach (var image in images) {
57 | writer.Write(image);
58 | }
59 |
60 | Console.WriteLine($"ICO 文件已生成: {outputIco}");
61 | }
62 | }
--------------------------------------------------------------------------------
/SharpIco/IcoInspector.cs:
--------------------------------------------------------------------------------
1 | using SixLabors.ImageSharp;
2 | using SixLabors.ImageSharp.Formats.Png;
3 |
4 | namespace SharpIco;
5 |
6 | public static class IcoInspector {
7 | public static void Inspect(string icoPath) {
8 | using var fs = new FileStream(icoPath, FileMode.Open, FileAccess.Read);
9 | using var reader = new BinaryReader(fs);
10 |
11 | ushort reserved = reader.ReadUInt16(); // 0
12 | ushort type = reader.ReadUInt16(); // 1 = icon
13 | ushort count = reader.ReadUInt16(); // image count
14 |
15 | Console.WriteLine($"图标数量: {count}");
16 |
17 | // 存储所有图像条目
18 | var imageEntries = new List();
19 |
20 | // 读取所有图像条目信息
21 | for (int i = 0; i < count; i++) {
22 | byte width = reader.ReadByte();
23 | byte height = reader.ReadByte();
24 | byte colorCount = reader.ReadByte();
25 | byte reservedByte = reader.ReadByte();
26 | ushort planes = reader.ReadUInt16();
27 | ushort bitCount = reader.ReadUInt16();
28 | int sizeInBytes = reader.ReadInt32();
29 | int imageOffset = reader.ReadInt32();
30 |
31 | imageEntries.Add(new ImageEntry {
32 | Index = i,
33 | Width = width,
34 | Height = height,
35 | BitCount = bitCount,
36 | SizeInBytes = sizeInBytes,
37 | ImageOffset = imageOffset
38 | });
39 | }
40 |
41 | // 排序条目,确保按照偏移量顺序处理
42 | imageEntries = imageEntries.OrderBy(e => e.ImageOffset).ToList();
43 |
44 | // 读取并解析每个图像的数据
45 | for (int i = 0; i < imageEntries.Count; i++) {
46 | var entry = imageEntries[i];
47 |
48 | // 保存当前位置
49 | long currentPosition = fs.Position;
50 |
51 | // 跳转到图像数据位置
52 | fs.Seek(entry.ImageOffset, SeekOrigin.Begin);
53 |
54 | // 确定图像数据大小
55 | int dataSize = entry.SizeInBytes;
56 | byte[] imageData = new byte[dataSize];
57 |
58 | // 读取图像数据 - 确保读取全部字节
59 | int bytesRead = 0;
60 | int bytesRemaining = dataSize;
61 | while (bytesRemaining > 0) {
62 | int n = fs.Read(imageData, bytesRead, bytesRemaining);
63 | if (n == 0) break; // 没有更多数据可读
64 | bytesRead += n;
65 | bytesRemaining -= n;
66 | }
67 |
68 | // 使用ImageSharp解析图像数据获取真实尺寸
69 | (int actualWidth, int actualHeight) = GetImageDimensions(imageData);
70 |
71 | // 恢复文件读取位置
72 | fs.Seek(currentPosition, SeekOrigin.Begin);
73 |
74 | // 更新图像条目的实际尺寸
75 | entry.ActualWidth = actualWidth;
76 | entry.ActualHeight = actualHeight;
77 | }
78 |
79 | // 输出图像信息
80 | DisplayImageInfo(imageEntries);
81 | }
82 |
83 | private static (int width, int height) GetImageDimensions(byte[] imageData) {
84 | try {
85 | // PNG图像以字节序列89 50 4E 47 0D 0A 1A 0A开头
86 | bool isPng = imageData.Length > 8 &&
87 | imageData[0] == 0x89 &&
88 | imageData[1] == 0x50 &&
89 | imageData[2] == 0x4E &&
90 | imageData[3] == 0x47;
91 |
92 | if (isPng) {
93 | using var ms = new MemoryStream(imageData);
94 | using var image = Image.Load(ms);
95 | return (image.Width, image.Height);
96 | }
97 |
98 | // 对于不是PNG格式的,尝试通过通用方法读取
99 | using var ms2 = new MemoryStream(imageData);
100 | try {
101 | using var image = Image.Load(ms2);
102 | return (image.Width, image.Height);
103 | }
104 | catch {
105 | // 如果无法解析,返回0表示未知
106 | return (0, 0);
107 | }
108 | }
109 | catch (Exception) {
110 | // 读取失败则返回0表示未知
111 | return (0, 0);
112 | }
113 | }
114 |
115 | private static void DisplayImageInfo(List entries) {
116 | // 按索引排序,保持原始顺序
117 | var sortedEntries = entries.OrderBy(e => e.Index).ToList();
118 |
119 | foreach (var entry in sortedEntries) {
120 | // 文件头中的尺寸信息
121 | int headerWidth = entry.Width == 0 ? 256 : entry.Width;
122 | int headerHeight = entry.Height == 0 ? 256 : entry.Height;
123 |
124 | // 实际图像尺寸
125 | int actualWidth = entry.ActualWidth > 0 ? entry.ActualWidth : headerWidth;
126 | int actualHeight = entry.ActualHeight > 0 ? entry.ActualHeight : headerHeight;
127 |
128 | // 显示信息
129 | Console.WriteLine(
130 | $"- 第{entry.Index + 1}张图像: {actualWidth}x{actualHeight}, {entry.BitCount}bpp, 大小: {entry.SizeInBytes}字节, 偏移: {entry.ImageOffset}");
131 |
132 | // 当实际尺寸与头部信息不一致时,显示提示
133 | if (actualWidth != headerWidth || actualHeight != headerHeight) {
134 | Console.WriteLine($" 注意: 文件头中指定的尺寸为{headerWidth}x{headerHeight},但实际图像尺寸为{actualWidth}x{actualHeight}");
135 | }
136 | }
137 | }
138 |
139 | private class ImageEntry {
140 | public int Index { get; set; }
141 | public byte Width { get; set; }
142 | public byte Height { get; set; }
143 | public ushort BitCount { get; set; }
144 | public int SizeInBytes { get; set; }
145 | public int ImageOffset { get; set; }
146 | public int ActualWidth { get; set; }
147 | public int ActualHeight { get; set; }
148 | }
149 | }
--------------------------------------------------------------------------------
/SharpIco/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 |
3 | using System.CommandLine;
4 | using SharpIco;
5 |
6 | // 定义根命令
7 | var rootCommand = new RootCommand("SharpIco - ICO图标生成和检查工具");
8 |
9 | // 生成命令
10 | var generateCommand = new Command("generate", "将PNG图像转换为ICO图标");
11 | var inputOption = new Option(
12 | new[] { "--input", "-i" },
13 | "输入PNG图像文件路径"
14 | );
15 | inputOption.IsRequired = true;
16 |
17 | var outputOption = new Option(
18 | new[] { "--output", "-o" },
19 | "输出ICO文件路径"
20 | );
21 | outputOption.IsRequired = true;
22 |
23 | var sizesOption = new Option(
24 | new[] { "--sizes", "-s" },
25 | description: "图标尺寸列表,以逗号分隔",
26 | getDefaultValue: () => new[] { 16, 32, 48, 64, 128, 256, 512 }
27 | );
28 | sizesOption.AllowMultipleArgumentsPerToken = true;
29 |
30 | generateCommand.AddOption(inputOption);
31 | generateCommand.AddOption(outputOption);
32 | generateCommand.AddOption(sizesOption);
33 |
34 | generateCommand.SetHandler((input, output, sizes) => {
35 | try {
36 | Console.WriteLine($"正在将 {input.FullName} 转换为 {output.FullName}...");
37 | Console.WriteLine($"生成尺寸: {string.Join(", ", sizes)}");
38 | IcoGenerator.GenerateIcon(input.FullName, output.FullName, sizes);
39 | }
40 | catch (Exception ex) {
41 | Console.WriteLine($"错误: {ex.Message}");
42 | }
43 | }, inputOption, outputOption, sizesOption);
44 |
45 | // 检查命令
46 | var inspectCommand = new Command("inspect", "检查ICO文件结构");
47 | var icoFileArgument = new Argument(
48 | "ico-file",
49 | "要检查的ICO文件路径"
50 | );
51 |
52 | inspectCommand.AddArgument(icoFileArgument);
53 |
54 | inspectCommand.SetHandler((icoFile) => {
55 | try {
56 | Console.WriteLine($"正在检查ICO文件: {icoFile.FullName}");
57 | IcoInspector.Inspect(icoFile.FullName);
58 | }
59 | catch (Exception ex) {
60 | Console.WriteLine($"错误: {ex.Message}");
61 | }
62 | }, icoFileArgument);
63 |
64 | // 将命令添加到根命令
65 | rootCommand.AddCommand(generateCommand);
66 | rootCommand.AddCommand(inspectCommand);
67 |
68 | // 执行命令
69 | return await rootCommand.InvokeAsync(args);
--------------------------------------------------------------------------------
/SharpIco/README.md:
--------------------------------------------------------------------------------
1 | # SharpIco
2 |
3 | SharpIco是一个.NET命令行工具,用于生成和检查ICO图标文件。
4 |
5 | ## 功能
6 |
7 | - 将PNG图像转换为多尺寸ICO图标
8 | - 检查ICO文件的内部结构和信息
9 |
10 | ## 安装
11 |
12 | ```bash
13 | dotnet build
14 | ```
15 |
16 | ## 使用方法
17 |
18 | ### 生成ICO图标
19 |
20 | ```bash
21 | # 使用默认尺寸(16,32,48,64,128,256,512)
22 | dotnet run -- generate -i input.png -o output.ico
23 |
24 | # 指定自定义尺寸
25 | dotnet run -- generate -i input.png -o output.ico -s 16,32,64,128
26 | ```
27 |
28 | ### 检查ICO文件
29 |
30 | ```bash
31 | dotnet run -- inspect icon.ico
32 | ```
33 |
34 | ## 参数说明
35 |
36 | ### 生成命令(generate)
37 |
38 | - `-i, --input`: (必需)输入PNG图像文件路径
39 | - `-o, --output`: (必需)输出ICO文件路径
40 | - `-s, --sizes`: (可选)图标尺寸列表,以逗号分隔,默认为16,32,48,64,128,256,512
41 |
42 | ### 检查命令(inspect)
43 |
44 | - `ico-file`: (必需)要检查的ICO文件路径
45 |
46 | ## 示例
47 |
48 | ```bash
49 | # 生成ICO图标
50 | dotnet run -- generate -i logo.png -o logo.ico
51 |
52 | # 使用自定义尺寸
53 | dotnet run -- generate -i logo.png -o logo.ico -s 16,32,64
54 |
55 | # 检查ICO文件
56 | dotnet run -- inspect logo.ico
57 | ```
58 |
59 | ## 关于ICO格式的说明
60 |
61 | ICO文件格式在表示图像尺寸时有一个限制:宽度和高度字段各只有一个字节,值范围是0-255。当这些字段为0时,按照规范表示256像素。对于大于256的尺寸(如512×512或1024×1024),在文件头中仍然会显示为0(即256),但实际图像数据可以包含更大尺寸的图像。
62 |
63 | SharpIco的inspect命令现在会解析每个图像的实际数据,以获取其真实尺寸。这使得工具能够准确显示ICO文件中图像的实际分辨率,即使它们超过了ICO头部表示的范围限制。当头部信息与实际图像尺寸不一致时,SharpIco会显示相应的提示信息。
--------------------------------------------------------------------------------
/SharpIco/SharpIco.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | enable
8 |
9 | true
10 |
11 |
12 | true
13 | sharpico
14 | ./nupkg
15 |
16 |
17 | SharpIco
18 | 1.0.0
19 | StarPlan
20 | SharpIco是一个纯 C# AOT 实现的轻量级图标生成工具,用于生成和检查ICO图标文件。可将一张高分辨率 PNG 图片一键生成标准的 Windows .ico 图标文件,内含多种尺寸(16x16 到 512x512),还可以自定义尺寸。除了图标生成,SharpIco 还内置图标结构分析功能,助你轻松验证 .ico 文件中包含的图层与尺寸。
21 | icon;ico;png;converter,DealiAxy,cli,tool,dotnet-tool,imagesharp
22 | https://github.com/star-plan/sharp-ico
23 | https://github.com/star-plan/sharp-ico
24 | MIT
25 | README.md
26 |
27 |
28 |
29 |
30 | true
31 | full
32 | true
33 | false
34 | Size
35 | true
36 | true
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/bucket/sharp-ico.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "description": "轻量级ICO图标生成和检查工具",
4 | "homepage": "https://github.com/star-plan/sharp-ico",
5 | "license": "MIT",
6 | "architecture": {
7 | "64bit": {
8 | "url": "https://github.com/star-plan/sharp-ico/releases/download/v1.0.0/sharpico-win-x64.zip",
9 | "hash": "请在发布后替换为实际的SHA256哈希值"
10 | }
11 | },
12 | "bin": "sharpico.exe",
13 | "checkver": "github",
14 | "autoupdate": {
15 | "architecture": {
16 | "64bit": {
17 | "url": "https://github.com/star-plan/sharp-ico/releases/download/v$version/sharpico-win-x64.zip"
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/docs/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/star-plan/sharp-ico/d70588a872c816aaa62355a8a395293d7548cb91/docs/images/logo.png
--------------------------------------------------------------------------------