├── .dockerignore
├── .github
└── workflows
│ ├── build and deploy to dev.yml
│ └── build and deploy to prod.yml
├── .gitignore
├── .vscode
├── launch.json
└── tasks.json
├── AppImageGenerator.csproj
├── AppImageGenerator.sln
├── App_Data
├── androidImages.json
├── chromeImages.json
├── firefoxImages.json
├── iosImages.json
├── msteamsImages.json
├── pwabuilderImages.json
├── windows10Images.json
├── windows11Images.json
└── windowsImages.json
├── Controllers
└── ImageController.cs
├── Dockerfile
├── Models
├── ImageGenerationModel.cs
├── Profile.cs
└── WebManifestIcon.cs
├── Program.cs
├── Properties
└── launchSettings.json
├── README.md
├── appsettings.Development.json
├── appsettings.json
├── docker-compose.debug.yml
└── docker-compose.yml
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/bin
15 | **/charts
16 | **/docker-compose*
17 | **/compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
26 |
--------------------------------------------------------------------------------
/.github/workflows/build and deploy to dev.yml:
--------------------------------------------------------------------------------
1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
2 | # More GitHub Actions for Azure: https://github.com/Azure/actions
3 |
4 | name: appimagegenerator-prod to dev slot
5 |
6 | on:
7 | push:
8 | branches:
9 | - dev
10 | pull_request:
11 | branches:
12 | - dev
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 |
21 | - name: Set up .NET Core
22 | uses: actions/setup-dotnet@v2
23 | with:
24 | dotnet-version: '7.x'
25 | include-prerelease: true
26 |
27 | - name: Build with dotnet
28 | run: dotnet build --configuration Release
29 |
30 | - name: dotnet publish
31 | run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
32 |
33 | - name: Upload artifact for deployment job
34 | uses: actions/upload-artifact@v3
35 | with:
36 | name: .net-app
37 | path: ${{env.DOTNET_ROOT}}/myapp
38 |
39 | deploy:
40 | runs-on: ubuntu-latest
41 | needs: build
42 | environment:
43 | name: 'dev'
44 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
45 |
46 | steps:
47 | - name: 'Login via Azure CLI'
48 | uses: azure/login@v1
49 | with:
50 | creds: ${{ secrets.AZURE_CREDENTIALS_PRINCIPAL }}
51 |
52 | - name: Download artifact from build job
53 | uses: actions/download-artifact@v3
54 | with:
55 | name: .net-app
56 |
57 | - name: Deploy to Azure Web App
58 | id: deploy-to-webapp
59 | uses: azure/webapps-deploy@v2
60 | with:
61 | app-name: 'appimagegenerator-prod'
62 | slot-name: 'dev'
63 | package: .
64 |
--------------------------------------------------------------------------------
/.github/workflows/build and deploy to prod.yml:
--------------------------------------------------------------------------------
1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
2 | # More GitHub Actions for Azure: https://github.com/Azure/actions
3 |
4 | name: appimagegenerator-prod to prod slot
5 |
6 | on:
7 | push:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 |
18 | - name: Set up .NET Core
19 | uses: actions/setup-dotnet@v2
20 | with:
21 | dotnet-version: '7.x'
22 | include-prerelease: true
23 |
24 | - name: Build with dotnet
25 | run: dotnet build --configuration Release
26 |
27 | - name: dotnet publish
28 | run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
29 |
30 | - name: Upload artifact for deployment job
31 | uses: actions/upload-artifact@v3
32 | with:
33 | name: .net-app
34 | path: ${{env.DOTNET_ROOT}}/myapp
35 |
36 | deploy:
37 | runs-on: ubuntu-latest
38 | needs: build
39 | environment:
40 | name: 'dev'
41 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
42 |
43 | steps:
44 | - name: 'Login via Azure CLI'
45 | uses: azure/login@v1
46 | with:
47 | creds: ${{ secrets.AZURE_CREDENTIALS_PRINCIPAL }}
48 |
49 | - name: Download artifact from build job
50 | uses: actions/download-artifact@v3
51 | with:
52 | name: .net-app
53 |
54 | - name: Deploy to Azure Web App
55 | id: deploy-to-webapp
56 | uses: azure/webapps-deploy@v2
57 | with:
58 | app-name: 'appimagegenerator-prod'
59 | package: .
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # git-ls-files --others --exclude-from=.git/info/exclude
2 | # Lines that start with '#' are comments.
3 | # For a project mostly in C, the following would be a good set of
4 | # exclude patterns (uncomment them if you want to use them):
5 | # *.[oa]
6 | # *~
7 | *.o
8 | *.sdf
9 | *.lo
10 | *.la
11 | *.al
12 | .libs
13 | *.so
14 | *.a
15 | *.pyc
16 | *.pyo
17 | *.rej
18 | *.suo
19 | *.user
20 | *.cache
21 | *.xap
22 | *.log
23 | *.vspcc
24 | *.vspscc
25 | *.vscc
26 | *.vssscc
27 | *~
28 | *.*~
29 | .*.swp
30 | .DS_Store
31 | bin
32 | Bin
33 | obj
34 | Obj
35 | Debug
36 | Release
37 | *.suo
38 | *.user
39 | TestResults
40 | *.Cache
41 | _ReSharper.*
42 | .vs
43 | ClientBin
44 | stylecop.*
45 | pkgobj
46 | pkg
47 | .svn
48 | .builds
49 | ~$*
50 | *.dbmdl
51 | coverage
52 | packages
53 | /App_Data/*.zip
54 | /App_Data/*.tmp
55 | /App_Data/BodyPart_*
56 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": ".NET Core Launch (web)",
6 | "type": "coreclr",
7 | "request": "launch",
8 | "preLaunchTask": "build",
9 | "program": "${workspaceFolder}/bin/Debug/net7.0/AppImageGenerator.dll",
10 | "args": [],
11 | "cwd": "${workspaceFolder}",
12 | "stopAtEntry": false,
13 | "serverReadyAction": {
14 | "action": "openExternally",
15 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
16 | },
17 | "env": {
18 | "ASPNETCORE_ENVIRONMENT": "Development"
19 | },
20 | "sourceFileMap": {
21 | "/Views": "${workspaceFolder}/Views"
22 | }
23 | },
24 | {
25 | "name": ".NET Core Attach",
26 | "type": "coreclr",
27 | "request": "attach"
28 | },
29 | {
30 | "name": "Docker .NET Core Launch",
31 | "type": "docker",
32 | "request": "launch",
33 | "preLaunchTask": "docker-run: debug",
34 | "netCore": {
35 | "appProject": "${workspaceFolder}/AppImageGenerator.csproj"
36 | }
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/AppImageGenerator.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/AppImageGenerator.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "--project",
36 | "${workspaceFolder}/AppImageGenerator.csproj"
37 | ],
38 | "problemMatcher": "$msCompile"
39 | },
40 | {
41 | "type": "docker-build",
42 | "label": "docker-build: debug",
43 | "dependsOn": [
44 | "build"
45 | ],
46 | "dockerBuild": {
47 | "tag": "pwabuilderimagegenerator:dev",
48 | "target": "base",
49 | "dockerfile": "${workspaceFolder}/Dockerfile",
50 | "context": "${workspaceFolder}",
51 | "pull": true
52 | },
53 | "netCore": {
54 | "appProject": "${workspaceFolder}/AppImageGenerator.csproj"
55 | }
56 | },
57 | {
58 | "type": "docker-build",
59 | "label": "docker-build: release",
60 | "dependsOn": [
61 | "build"
62 | ],
63 | "dockerBuild": {
64 | "tag": "pwabuilderimagegenerator:latest",
65 | "dockerfile": "${workspaceFolder}/Dockerfile",
66 | "context": "${workspaceFolder}",
67 | "pull": true
68 | },
69 | "netCore": {
70 | "appProject": "${workspaceFolder}/AppImageGenerator.csproj"
71 | }
72 | },
73 | {
74 | "type": "docker-run",
75 | "label": "docker-run: debug",
76 | "dependsOn": [
77 | "docker-build: debug"
78 | ],
79 | "dockerRun": {},
80 | "netCore": {
81 | "appProject": "${workspaceFolder}/AppImageGenerator.csproj",
82 | "enableDebugging": true
83 | }
84 | },
85 | {
86 | "type": "docker-run",
87 | "label": "docker-run: release",
88 | "dependsOn": [
89 | "docker-build: release"
90 | ],
91 | "dockerRun": {},
92 | "netCore": {
93 | "appProject": "${workspaceFolder}/AppImageGenerator.csproj"
94 | }
95 | }
96 | ]
97 | }
--------------------------------------------------------------------------------
/AppImageGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | enable
7 | 9423d0e8-1eb9-474b-9ce8-a383ce07fab7
8 | Linux
9 | .
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/AppImageGenerator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33205.214
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppImageGenerator", "AppImageGenerator.csproj", "{ABE74024-2AEC-4CA9-8D34-0E4608FF3C23}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {ABE74024-2AEC-4CA9-8D34-0E4608FF3C23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {ABE74024-2AEC-4CA9-8D34-0E4608FF3C23}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {ABE74024-2AEC-4CA9-8D34-0E4608FF3C23}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {ABE74024-2AEC-4CA9-8D34-0E4608FF3C23}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {D0EF26B5-F3FD-4067-B530-29C7D4EDA8A5}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/App_Data/androidImages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 512,
4 | "height": 512,
5 | "name": "android-launchericon-512-512",
6 | "desc": "android-launchericon-512-512",
7 | "folder": "android/"
8 | },
9 | {
10 | "width": 192,
11 | "height": 192,
12 | "name": "android-launchericon-192-192",
13 | "desc": "android-launchericon-192-192",
14 | "folder": "android/"
15 | },
16 | {
17 | "width": 144,
18 | "height": 144,
19 | "name": "android-launchericon-144-144",
20 | "desc": "android-launchericon-144-144",
21 | "folder": "android/"
22 | },
23 | {
24 | "width": 96,
25 | "height": 96,
26 | "name": "android-launchericon-96-96",
27 | "desc": "android-launchericon-96-96",
28 | "folder": "android/"
29 | },
30 | {
31 | "width": 72,
32 | "height": 72,
33 | "name": "android-launchericon-72-72",
34 | "desc": "android-launchericon-72-72",
35 | "folder": "android/"
36 | },
37 | {
38 | "width": 48,
39 | "height": 48,
40 | "name": "android-launchericon-48-48",
41 | "desc": "android-launchericon-48-48",
42 | "folder": "android/"
43 | }
44 | ]
45 |
--------------------------------------------------------------------------------
/App_Data/chromeImages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 48,
4 | "height": 48,
5 | "name": "chrome-extensionmanagementpage-48-48",
6 | "desc": "chrome-extensionmanagementpage-48-48",
7 | "folder": "chrome/"
8 | },
9 | {
10 | "width": 16,
11 | "height": 16,
12 | "name": "chrome-favicon-16-16",
13 | "desc": "chrome-favicon-16-16",
14 | "folder": "chrome/"
15 | },
16 | {
17 | "width": 128,
18 | "height": 128,
19 | "name": "chrome-installprocess-128-128",
20 | "desc": "chrome-installprocess-128-128",
21 | "folder": "chrome/"
22 | }
23 | ]
24 |
--------------------------------------------------------------------------------
/App_Data/firefoxImages.json:
--------------------------------------------------------------------------------
1 | [
2 | { "width": 512, "height": 512, "name": "firefox-marketplace-512-512", "desc": "firefox-marketplace-512-512", "folder": "firefox/" },
3 | { "width": 128, "height": 128, "name": "firefox-marketplace-128-128", "desc": "firefox-marketplace-128-128", "folder": "firefox/" },
4 |
5 | { "width": 256, "height": 256, "name": "firefox-general-256-256", "desc": "firefox-general-256-256", "folder": "firefox/" },
6 | { "width": 128, "height": 128, "name": "firefox-general-128-128", "desc": "firefox-general-128-128", "folder": "firefox/" },
7 | { "width": 90, "height": 90, "name": "firefox-general-90-90", "desc": "firefox-general-90-90", "folder": "firefox/" },
8 | { "width": 64, "height": 64, "name": "firefox-general-64-64", "desc": "firefox-general-64-64", "folder": "firefox/" },
9 | { "width": 48, "height": 48, "name": "firefox-general-48-48", "desc": "firefox-general-48-48", "folder": "firefox/" },
10 | { "width": 32, "height": 32, "name": "firefox-general-32-32", "desc": "firefox-general-32-32", "folder": "firefox/" },
11 | { "width": 16, "height": 16, "name": "firefox-general-16-16", "desc": "firefox-general-16-16", "folder": "firefox/" }
12 | ]
13 |
--------------------------------------------------------------------------------
/App_Data/iosImages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 16,
4 | "height": 16,
5 | "name": "16",
6 | "desc": "16",
7 | "folder": "ios/"
8 | },
9 | {
10 | "width": 20,
11 | "height": 20,
12 | "name": "20",
13 | "desc": "20",
14 | "folder": "ios/"
15 | },
16 | {
17 | "width": 29,
18 | "height": 29,
19 | "name": "29",
20 | "desc": "29",
21 | "folder": "ios/"
22 | },
23 | {
24 | "width": 32,
25 | "height": 32,
26 | "name": "32",
27 | "desc": "32",
28 | "folder": "ios/"
29 | },
30 | {
31 | "width": 40,
32 | "height": 40,
33 | "name": "40",
34 | "desc": "40",
35 | "folder": "ios/"
36 | },
37 | {
38 | "width": 50,
39 | "height": 50,
40 | "name": "50",
41 | "desc": "50",
42 | "folder": "ios/"
43 | },
44 | {
45 | "width": 57,
46 | "height": 57,
47 | "name": "57",
48 | "desc": "57",
49 | "folder": "ios/"
50 | },
51 | {
52 | "width": 58,
53 | "height": 58,
54 | "name": "58",
55 | "desc": "58",
56 | "folder": "ios/"
57 | },
58 | {
59 | "width": 60,
60 | "height": 60,
61 | "name": "60",
62 | "desc": "60",
63 | "folder": "ios/"
64 | },
65 | {
66 | "width": 64,
67 | "height": 64,
68 | "name": "64",
69 | "desc": "64",
70 | "folder": "ios/"
71 | },
72 | {
73 | "width": 72,
74 | "height": 72,
75 | "name": "72",
76 | "desc": "72",
77 | "folder": "ios/"
78 |
79 | },
80 | {
81 | "width": 76,
82 | "height": 76,
83 | "name": "76",
84 | "desc": "76",
85 | "folder": "ios/"
86 | },
87 | {
88 | "width": 80,
89 | "height": 80,
90 | "name": "80",
91 | "desc": "80",
92 | "folder": "ios/"
93 | },
94 | {
95 | "width": 87,
96 | "height": 87,
97 | "name": "87",
98 | "desc": "87",
99 | "folder": "ios/"
100 | },
101 | {
102 | "width": 100,
103 | "height": 100,
104 | "name": "100",
105 | "desc": "100",
106 | "folder": "ios/"
107 | },
108 | {
109 | "width": 114,
110 | "height": 114,
111 | "name": "114",
112 | "desc": "114",
113 | "folder": "ios/"
114 | },
115 | {
116 | "width": 120,
117 | "height": 120,
118 | "name": "120",
119 | "desc": "120",
120 | "folder": "ios/"
121 | },
122 | {
123 | "width": 128,
124 | "height": 128,
125 | "name": "128",
126 | "desc": "128",
127 | "folder": "ios/"
128 | },
129 | {
130 | "width": 144,
131 | "height": 144,
132 | "name": "144",
133 | "desc": "144",
134 | "folder": "ios/"
135 | },
136 | {
137 | "width": 152,
138 | "height": 152,
139 | "name": "152",
140 | "desc": "152",
141 | "folder": "ios/"
142 | },
143 | {
144 | "width": 167,
145 | "height": 167,
146 | "name": "167",
147 | "desc": "167",
148 | "folder": "ios/"
149 | },
150 | {
151 | "width": 180,
152 | "height": 180,
153 | "name": "180",
154 | "desc": "180",
155 | "folder": "ios/"
156 | },
157 | {
158 | "width": 192,
159 | "height": 192,
160 | "name": "192",
161 | "desc": "192",
162 | "folder": "ios/"
163 | },
164 | {
165 | "width": 256,
166 | "height": 256,
167 | "name": "256",
168 | "desc": "256",
169 | "folder": "ios/"
170 | },
171 | {
172 | "width": 512,
173 | "height": 512,
174 | "name": "512",
175 | "desc": "512",
176 | "folder": "ios/"
177 | },
178 | {
179 | "width": 1024,
180 | "height": 1024,
181 | "name": "1024",
182 | "desc": "1024",
183 | "folder": "ios/"
184 | }
185 |
186 | ]
187 |
--------------------------------------------------------------------------------
/App_Data/msteamsImages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 192,
4 | "height": 192,
5 | "name": "msteams-192-192",
6 | "desc": "msteams-192-192",
7 | "folder": "msteams/"
8 | },
9 | {
10 | "width": 32,
11 | "height": 32,
12 | "name": "msteams-silhouette-32-32",
13 | "desc": "msteams-silhouette-32-32",
14 | "folder": "msteams/"
15 | }
16 | ]
17 |
--------------------------------------------------------------------------------
/App_Data/pwabuilderImages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 44,
4 | "height": 44,
5 | "name": "Square44x44Logo.scale-100.png",
6 | "desc": "Square44x44Logo.scale-100.png",
7 | "folder": "windows10/"
8 | },
9 | {
10 | "width": 48,
11 | "height": 48,
12 | "name": "LockScreenLogo.scale-200.png",
13 | "desc": "LockScreenLogo.scale-200.png",
14 | "folder": "windows10/"
15 | },
16 | {
17 | "width": 1240,
18 | "height": 600,
19 | "name": "SplashScreen.scale-200.png",
20 | "desc": "SplashScreen.scale-200.png",
21 | "folder": "windows10/"
22 | },
23 | {
24 | "width": 300,
25 | "height": 300,
26 | "name": "Square150x150Logo.scale-200.png",
27 | "desc": "Square150x150Logo.scale-200.png",
28 | "folder": "windows10/"
29 | },
30 | {
31 | "width": 150,
32 | "height": 150,
33 | "name": "Square150x150Logo.scale-100.png",
34 | "desc": "Square150x150Logo.scale-100.png",
35 | "folder": "windows10/"
36 | },
37 | {
38 | "width": 88,
39 | "height": 88,
40 | "name": "Square44x44Logo.scale-200.png",
41 | "desc": "Square44x44Logo.scale-200.png",
42 | "folder": "windows10/"
43 | },
44 | {
45 | "width": 24,
46 | "height": 24,
47 | "name": "Square44x44Logo.targetsize-24_altform-unplated.png",
48 | "desc": "Square44x44Logo.targetsize-24_altform-unplated.png",
49 | "folder": "windows10/"
50 | },
51 | {
52 | "width": 50,
53 | "height": 50,
54 | "name": "StoreLogo.png",
55 | "desc": "StoreLogo.png",
56 | "folder": "windows10/"
57 | },
58 | {
59 | "width": 620,
60 | "height": 300,
61 | "name": "Wide310x150Logo.scale-200.png",
62 | "desc": "Wide310x150Logo.scale-200.png",
63 | "folder": "windows10/"
64 | },
65 | {
66 | "width": 192,
67 | "height": 192,
68 | "name": "android-launchericon-192-192",
69 | "desc": "android-launchericon-192-192",
70 | "folder": "android/"
71 | },
72 | {
73 | "width": 144,
74 | "height": 144,
75 | "name": "android-launchericon-144-144",
76 | "desc": "android-launchericon-144-144",
77 | "folder": "android/"
78 | },
79 | {
80 | "width": 96,
81 | "height": 96,
82 | "name": "android-launchericon-96-96",
83 | "desc": "android-launchericon-96-96",
84 | "folder": "android/"
85 | },
86 | {
87 | "width": 72,
88 | "height": 72,
89 | "name": "android-launchericon-72-72",
90 | "desc": "android-launchericon-72-72",
91 | "folder": "android/"
92 | },
93 | {
94 | "width": 48,
95 | "height": 48,
96 | "name": "android-launchericon-48-48",
97 | "desc": "android-launchericon-48-48",
98 | "folder": "android/"
99 | },
100 | {
101 | "width": 36,
102 | "height": 36,
103 | "name": "android-launchericon-36-36",
104 | "desc": "android-launchericon-36-36",
105 | "folder": "android/"
106 | },
107 | {
108 | "width": 1024,
109 | "height": 1024,
110 | "name": "ios-appicon-1024-1024",
111 | "desc": "ios-appicon-1024-1024",
112 | "folder": "ios/"
113 | },
114 | {
115 | "width": 180,
116 | "height": 180,
117 | "name": "ios-appicon-180-180",
118 | "desc": "ios-appicon-180-180",
119 | "folder": "ios/"
120 | },
121 | {
122 | "width": 152,
123 | "height": 152,
124 | "name": "ios-appicon-152-152",
125 | "desc": "ios-appicon-152-152",
126 | "folder": "ios/"
127 | },
128 | {
129 | "width": 120,
130 | "height": 120,
131 | "name": "ios-appicon-120-120",
132 | "desc": "ios-appicon-120-120",
133 | "folder": "ios/"
134 | },
135 | {
136 | "width": 76,
137 | "height": 76,
138 | "name": "ios-appicon-76-76",
139 | "desc": "ios-appicon-76-76",
140 | "folder": "ios/"
141 | },
142 | {
143 | "width": 192,
144 | "height": 192,
145 | "name": "msteams-192-192",
146 | "desc": "msteams-192-192",
147 | "folder": "msteams/"
148 | },
149 | {
150 | "width": 32,
151 | "height": 32,
152 | "name": "msteams-silhouette-32-32",
153 | "desc": "msteams-silhouette-32-32",
154 | "folder": "msteams/"
155 | }
156 | ]
157 |
--------------------------------------------------------------------------------
/App_Data/windows10Images.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 71,
4 | "height": 71,
5 | "name": "SmallTile.scale-100",
6 | "desc": "SmallTile.scale-100",
7 | "folder": "windows10/"
8 | },
9 | {
10 | "width": 89,
11 | "height": 89,
12 | "name": "SmallTile.scale-125",
13 | "desc": "SmallTile.scale-125",
14 | "folder": "windows10/"
15 | },
16 | {
17 | "width": 107,
18 | "height": 107,
19 | "name": "SmallTile.scale-150",
20 | "desc": "SmallTile.scale-150",
21 | "folder": "windows10/"
22 | },
23 | {
24 | "width": 142,
25 | "height": 142,
26 | "name": "SmallTile.scale-200",
27 | "desc": "SmallTile.scale-200",
28 | "folder": "windows10/"
29 | },
30 | {
31 | "width": 284,
32 | "height": 284,
33 | "name": "SmallTile.scale-400",
34 | "desc": "SmallTile.scale-400",
35 | "folder": "windows10/"
36 | },
37 |
38 | {
39 | "width": 150,
40 | "height": 150,
41 | "name": "Square150x150Logo.scale-100",
42 | "desc": "Square150x150Logo.scale-100",
43 | "folder": "windows10/"
44 | },
45 | {
46 | "width": 188,
47 | "height": 188,
48 | "name": "Square150x150Logo.scale-125",
49 | "desc": "Square150x150Logo.scale-125",
50 | "folder": "windows10/"
51 | },
52 | {
53 | "width": 225,
54 | "height": 225,
55 | "name": "Square150x150Logo.scale-150",
56 | "desc": "Square150x150Logo.scale-150",
57 | "folder": "windows10/"
58 | },
59 | {
60 | "width": 300,
61 | "height": 300,
62 | "name": "Square150x150Logo.scale-200",
63 | "desc": "Square150x150Logo.scale-200",
64 | "folder": "windows10/"
65 | },
66 | {
67 | "width": 600,
68 | "height": 600,
69 | "name": "Square150x150Logo.scale-400",
70 | "desc": "Square150x150Logo.scale-400",
71 | "folder": "windows10/"
72 | },
73 |
74 | {
75 | "width": 310,
76 | "height": 150,
77 | "name": "Wide310x150Logo.scale-100",
78 | "desc": "Wide310x150Logo.scale-100",
79 | "folder": "windows10/"
80 | },
81 | {
82 | "width": 388,
83 | "height": 188,
84 | "name": "Wide310x150Logo.scale-125",
85 | "desc": "Wide310x150Logo.scale-125",
86 | "folder": "windows10/"
87 | },
88 | {
89 | "width": 465,
90 | "height": 225,
91 | "name": "Wide310x150Logo.scale-150",
92 | "desc": "Wide310x150Logo.scale-150",
93 | "folder": "windows10/"
94 | },
95 | {
96 | "width": 620,
97 | "height": 300,
98 | "name": "Wide310x150Logo.scale-200",
99 | "desc": "Wide310x150Logo.scale-200",
100 | "folder": "windows10/"
101 | },
102 | {
103 | "width": 1240,
104 | "height": 600,
105 | "name": "Wide310x150Logo.scale-400",
106 | "desc": "Wide310x150Logo.scale-400",
107 | "folder": "windows10/"
108 | },
109 |
110 | {
111 | "width": 310,
112 | "height": 310,
113 | "name": "LargeTile.scale-100",
114 | "desc": "LargeTile.scale-100",
115 | "folder": "windows10/"
116 | },
117 | {
118 | "width": 388,
119 | "height": 388,
120 | "name": "LargeTile.scale-125",
121 | "desc": "LargeTile.scale-125",
122 | "folder": "windows10/"
123 | },
124 | {
125 | "width": 465,
126 | "height": 465,
127 | "name": "LargeTile.scale-150",
128 | "desc": "LargeTile.scale-150",
129 | "folder": "windows10/"
130 | },
131 | {
132 | "width": 620,
133 | "height": 620,
134 | "name": "LargeTile.scale-200",
135 | "desc": "LargeTile.scale-200",
136 | "folder": "windows10/"
137 | },
138 | {
139 | "width": 1240,
140 | "height": 1240,
141 | "name": "LargeTile.scale-400",
142 | "desc": "LargeTile.scale-400",
143 | "folder": "windows10/"
144 | },
145 |
146 | {
147 | "width": 44,
148 | "height": 44,
149 | "name": "Square44x44Logo.scale-100",
150 | "desc": "Square44x44Logo.scale-100",
151 | "folder": "windows10/"
152 | },
153 | {
154 | "width": 55,
155 | "height": 55,
156 | "name": "Square44x44Logo.scale-125",
157 | "desc": "Square44x44Logo.scale-125",
158 | "folder": "windows10/"
159 | },
160 | {
161 | "width": 66,
162 | "height": 66,
163 | "name": "Square44x44Logo.scale-150",
164 | "desc": "Square44x44Logo.scale-150",
165 | "folder": "windows10/"
166 | },
167 | {
168 | "width": 88,
169 | "height": 88,
170 | "name": "Square44x44Logo.scale-200",
171 | "desc": "Square44x44Logo.scale-200",
172 | "folder": "windows10/"
173 | },
174 | {
175 | "width": 176,
176 | "height": 176,
177 | "name": "Square44x44Logo.scale-400",
178 | "desc": "Square44x44Logo.scale-400",
179 | "folder": "windows10/"
180 | },
181 |
182 | {
183 | "width": 50,
184 | "height": 50,
185 | "name": "StoreLogo.scale-100",
186 | "desc": "StoreLogo.scale-100",
187 | "folder": "windows10/"
188 | },
189 | {
190 | "width": 63,
191 | "height": 63,
192 | "name": "StoreLogo.scale-125",
193 | "desc": "StoreLogo.scale-125",
194 | "folder": "windows10/"
195 | },
196 | {
197 | "width": 75,
198 | "height": 75,
199 | "name": "StoreLogo.scale-150",
200 | "desc": "StoreLogo.scale-150",
201 | "folder": "windows10/"
202 | },
203 | {
204 | "width": 100,
205 | "height": 100,
206 | "name": "StoreLogo.scale-200",
207 | "desc": "StoreLogo.scale-200",
208 | "folder": "windows10/"
209 | },
210 | {
211 | "width": 200,
212 | "height": 200,
213 | "name": "StoreLogo.scale-400",
214 | "desc": "StoreLogo.scale-400",
215 | "folder": "windows10/"
216 | },
217 |
218 | {
219 | "width": 620,
220 | "height": 300,
221 | "name": "SplashScreen.scale-100",
222 | "desc": "SplashScreen.scale-100",
223 | "folder": "windows10/"
224 | },
225 | {
226 | "width": 775,
227 | "height": 375,
228 | "name": "SplashScreen.scale-125",
229 | "desc": "SplashScreen.scale-125",
230 | "folder": "windows10/"
231 | },
232 | {
233 | "width": 930,
234 | "height": 450,
235 | "name": "SplashScreen.scale-150",
236 | "desc": "SplashScreen.scale-150",
237 | "folder": "windows10/"
238 | },
239 | {
240 | "width": 1240,
241 | "height": 600,
242 | "name": "SplashScreen.scale-200",
243 | "desc": "SplashScreen.scale-200",
244 | "folder": "windows10/"
245 | },
246 | {
247 | "width": 2480,
248 | "height": 1200,
249 | "name": "SplashScreen.scale-400",
250 | "desc": "SplashScreen.scale-400",
251 | "folder": "windows10/"
252 | },
253 |
254 | {
255 | "width": 16,
256 | "height": 16,
257 | "name": "Square44x44Logo.targetsize-16",
258 | "desc": "Square44x44Logo.targetsize-16",
259 | "folder": "windows10/"
260 | },
261 | {
262 | "width": 20,
263 | "height": 20,
264 | "name": "Square44x44Logo.targetsize-20",
265 | "desc": "Square44x44Logo.targetsize-20",
266 | "folder": "windows10/"
267 | },
268 | {
269 | "width": 24,
270 | "height": 24,
271 | "name": "Square44x44Logo.targetsize-24",
272 | "desc": "Square44x44Logo.targetsize-24",
273 | "folder": "windows10/"
274 | },
275 | {
276 | "width": 30,
277 | "height": 30,
278 | "name": "Square44x44Logo.targetsize-30",
279 | "desc": "Square44x44Logo.targetsize-30",
280 | "folder": "windows10/"
281 | },
282 | {
283 | "width": 32,
284 | "height": 32,
285 | "name": "Square44x44Logo.targetsize-32",
286 | "desc": "Square44x44Logo.targetsize-32",
287 | "folder": "windows10/"
288 | },
289 | {
290 | "width": 36,
291 | "height": 36,
292 | "name": "Square44x44Logo.targetsize-36",
293 | "desc": "Square44x44Logo.targetsize-36",
294 | "folder": "windows10/"
295 | },
296 | {
297 | "width": 40,
298 | "height": 40,
299 | "name": "Square44x44Logo.targetsize-40",
300 | "desc": "Square44x44Logo.targetsize-40",
301 | "folder": "windows10/"
302 | },
303 | {
304 | "width": 44,
305 | "height": 44,
306 | "name": "Square44x44Logo.targetsize-44",
307 | "desc": "Square44x44Logo.targetsize-44",
308 | "folder": "windows10/"
309 | },
310 | {
311 | "width": 48,
312 | "height": 48,
313 | "name": "Square44x44Logo.targetsize-48",
314 | "desc": "Square44x44Logo.targetsize-48",
315 | "folder": "windows10/"
316 | },
317 | {
318 | "width": 60,
319 | "height": 60,
320 | "name": "Square44x44Logo.targetsize-60",
321 | "desc": "Square44x44Logo.targetsize-60",
322 | "folder": "windows10/"
323 | },
324 | {
325 | "width": 64,
326 | "height": 64,
327 | "name": "Square44x44Logo.targetsize-64",
328 | "desc": "Square44x44Logo.targetsize-64",
329 | "folder": "windows10/"
330 | },
331 | {
332 | "width": 72,
333 | "height": 72,
334 | "name": "Square44x44Logo.targetsize-72",
335 | "desc": "Square44x44Logo.targetsize-72",
336 | "folder": "windows10/"
337 | },
338 | {
339 | "width": 80,
340 | "height": 80,
341 | "name": "Square44x44Logo.targetsize-80",
342 | "desc": "Square44x44Logo.targetsize-80",
343 | "folder": "windows10/"
344 | },
345 | {
346 | "width": 96,
347 | "height": 96,
348 | "name": "Square44x44Logo.targetsize-96",
349 | "desc": "Square44x44Logo.targetsize-96",
350 | "folder": "windows10/"
351 | },
352 | {
353 | "width": 256,
354 | "height": 256,
355 | "name": "Square44x44Logo.targetsize-256",
356 | "desc": "Square44x44Logo.targetsize-256",
357 | "folder": "windows10/"
358 | },
359 |
360 | {
361 | "width": 16,
362 | "height": 16,
363 | "name": "Square44x44Logo.altform-unplated_targetsize-16",
364 | "desc": "Square44x44Logo.altform-unplated_targetsize-16",
365 | "folder": "windows10/"
366 | },
367 | {
368 | "width": 20,
369 | "height": 20,
370 | "name": "Square44x44Logo.altform-unplated_targetsize-20",
371 | "desc": "Square44x44Logo.altform-unplated_targetsize-20",
372 | "folder": "windows10/"
373 | },
374 | {
375 | "width": 24,
376 | "height": 24,
377 | "name": "Square44x44Logo.altform-unplated_targetsize-24",
378 | "desc": "Square44x44Logo.altform-unplated_targetsize-24",
379 | "folder": "windows10/"
380 | },
381 | {
382 | "width": 30,
383 | "height": 30,
384 | "name": "Square44x44Logo.altform-unplated_targetsize-30",
385 | "desc": "Square44x44Logo.altform-unplated_targetsize-30",
386 | "folder": "windows10/"
387 | },
388 | {
389 | "width": 32,
390 | "height": 32,
391 | "name": "Square44x44Logo.altform-unplated_targetsize-32",
392 | "desc": "Square44x44Logo.altform-unplated_targetsize-32",
393 | "folder": "windows10/"
394 | },
395 | {
396 | "width": 36,
397 | "height": 36,
398 | "name": "Square44x44Logo.altform-unplated_targetsize-36",
399 | "desc": "Square44x44Logo.altform-unplated_targetsize-36",
400 | "folder": "windows10/"
401 | },
402 | {
403 | "width": 40,
404 | "height": 40,
405 | "name": "Square44x44Logo.altform-unplated_targetsize-40",
406 | "desc": "Square44x44Logo.altform-unplated_targetsize-40",
407 | "folder": "windows10/"
408 | },
409 | {
410 | "width": 44,
411 | "height": 44,
412 | "name": "Square44x44Logo.altform-unplated_targetsize-44",
413 | "desc": "Square44x44Logo.altform-unplated_targetsize-44",
414 | "folder": "windows10/"
415 | },
416 | {
417 | "width": 48,
418 | "height": 48,
419 | "name": "Square44x44Logo.altform-unplated_targetsize-48",
420 | "desc": "Square44x44Logo.altform-unplated_targetsize-48",
421 | "folder": "windows10/"
422 | },
423 | {
424 | "width": 60,
425 | "height": 60,
426 | "name": "Square44x44Logo.altform-unplated_targetsize-60",
427 | "desc": "Square44x44Logo.altform-unplated_targetsize-60",
428 | "folder": "windows10/"
429 | },
430 | {
431 | "width": 64,
432 | "height": 64,
433 | "name": "Square44x44Logo.altform-unplated_targetsize-64",
434 | "desc": "Square44x44Logo.altform-unplated_targetsize-64",
435 | "folder": "windows10/"
436 | },
437 | {
438 | "width": 72,
439 | "height": 72,
440 | "name": "Square44x44Logo.altform-unplated_targetsize-72",
441 | "desc": "Square44x44Logo.altform-unplated_targetsize-72",
442 | "folder": "windows10/"
443 | },
444 | {
445 | "width": 80,
446 | "height": 80,
447 | "name": "Square44x44Logo.altform-unplated_targetsize-80",
448 | "desc": "Square44x44Logo.altform-unplated_targetsize-80",
449 | "folder": "windows10/"
450 | },
451 | {
452 | "width": 96,
453 | "height": 96,
454 | "name": "Square44x44Logo.altform-unplated_targetsize-96",
455 | "desc": "Square44x44Logo.altform-unplated_targetsize-96",
456 | "folder": "windows10/"
457 | },
458 | {
459 | "width": 256,
460 | "height": 256,
461 | "name": "Square44x44Logo.altform-unplated_targetsize-256",
462 | "desc": "Square44x44Logo.altform-unplated_targetsize-256",
463 | "folder": "windows10/"
464 | },
465 |
466 | {
467 | "width": 16,
468 | "height": 16,
469 | "name": "Square44x44Logo.altform-lightunplated_targetsize-16",
470 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-16",
471 | "folder": "windows10/"
472 | },
473 | {
474 | "width": 20,
475 | "height": 20,
476 | "name": "Square44x44Logo.altform-lightunplated_targetsize-20",
477 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-20",
478 | "folder": "windows10/"
479 | },
480 | {
481 | "width": 24,
482 | "height": 24,
483 | "name": "Square44x44Logo.altform-lightunplated_targetsize-24",
484 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-24",
485 | "folder": "windows10/"
486 | },
487 | {
488 | "width": 30,
489 | "height": 30,
490 | "name": "Square44x44Logo.altform-lightunplated_targetsize-30",
491 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-30",
492 | "folder": "windows10/"
493 | },
494 | {
495 | "width": 32,
496 | "height": 32,
497 | "name": "Square44x44Logo.altform-lightunplated_targetsize-32",
498 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-32",
499 | "folder": "windows10/"
500 | },
501 | {
502 | "width": 36,
503 | "height": 36,
504 | "name": "Square44x44Logo.altform-lightunplated_targetsize-36",
505 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-36",
506 | "folder": "windows10/"
507 | },
508 | {
509 | "width": 40,
510 | "height": 40,
511 | "name": "Square44x44Logo.altform-lightunplated_targetsize-40",
512 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-40",
513 | "folder": "windows10/"
514 | },
515 | {
516 | "width": 44,
517 | "height": 44,
518 | "name": "Square44x44Logo.altform-lightunplated_targetsize-44",
519 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-44",
520 | "folder": "windows10/"
521 | },
522 | {
523 | "width": 48,
524 | "height": 48,
525 | "name": "Square44x44Logo.altform-lightunplated_targetsize-48",
526 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-48",
527 | "folder": "windows10/"
528 | },
529 | {
530 | "width": 60,
531 | "height": 60,
532 | "name": "Square44x44Logo.altform-lightunplated_targetsize-60",
533 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-60",
534 | "folder": "windows10/"
535 | },
536 | {
537 | "width": 64,
538 | "height": 64,
539 | "name": "Square44x44Logo.altform-lightunplated_targetsize-64",
540 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-64",
541 | "folder": "windows10/"
542 | },
543 | {
544 | "width": 72,
545 | "height": 72,
546 | "name": "Square44x44Logo.altform-lightunplated_targetsize-72",
547 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-72",
548 | "folder": "windows10/"
549 | },
550 | {
551 | "width": 80,
552 | "height": 80,
553 | "name": "Square44x44Logo.altform-lightunplated_targetsize-80",
554 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-80",
555 | "folder": "windows10/"
556 | },
557 | {
558 | "width": 96,
559 | "height": 96,
560 | "name": "Square44x44Logo.altform-lightunplated_targetsize-96",
561 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-96",
562 | "folder": "windows10/"
563 | },
564 | {
565 | "width": 256,
566 | "height": 256,
567 | "name": "Square44x44Logo.altform-lightunplated_targetsize-256",
568 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-256",
569 | "folder": "windows10/"
570 | }
571 | ]
572 |
--------------------------------------------------------------------------------
/App_Data/windows11Images.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "width": 71,
4 | "height": 71,
5 | "name": "SmallTile.scale-100",
6 | "desc": "SmallTile.scale-100",
7 | "folder": "windows11/",
8 | "padding": 0.3
9 | },
10 | {
11 | "width": 89,
12 | "height": 89,
13 | "name": "SmallTile.scale-125",
14 | "desc": "SmallTile.scale-125",
15 | "folder": "windows11/",
16 | "padding": 0.3
17 | },
18 | {
19 | "width": 107,
20 | "height": 107,
21 | "name": "SmallTile.scale-150",
22 | "desc": "SmallTile.scale-150",
23 | "folder": "windows11/",
24 | "padding": 0.3
25 | },
26 | {
27 | "width": 142,
28 | "height": 142,
29 | "name": "SmallTile.scale-200",
30 | "desc": "SmallTile.scale-200",
31 | "folder": "windows11/",
32 | "padding": 0.3
33 | },
34 | {
35 | "width": 284,
36 | "height": 284,
37 | "name": "SmallTile.scale-400",
38 | "desc": "SmallTile.scale-400",
39 | "folder": "windows11/",
40 | "padding": 0.3
41 | },
42 |
43 | {
44 | "width": 150,
45 | "height": 150,
46 | "name": "Square150x150Logo.scale-100",
47 | "desc": "Square150x150Logo.scale-100",
48 | "folder": "windows11/",
49 | "padding": 0.3
50 | },
51 | {
52 | "width": 188,
53 | "height": 188,
54 | "name": "Square150x150Logo.scale-125",
55 | "desc": "Square150x150Logo.scale-125",
56 | "folder": "windows11/",
57 | "padding": 0.3
58 | },
59 | {
60 | "width": 225,
61 | "height": 225,
62 | "name": "Square150x150Logo.scale-150",
63 | "desc": "Square150x150Logo.scale-150",
64 | "folder": "windows11/",
65 | "padding": 0.3
66 | },
67 | {
68 | "width": 300,
69 | "height": 300,
70 | "name": "Square150x150Logo.scale-200",
71 | "desc": "Square150x150Logo.scale-200",
72 | "folder": "windows11/",
73 | "padding": 0.3
74 | },
75 | {
76 | "width": 600,
77 | "height": 600,
78 | "name": "Square150x150Logo.scale-400",
79 | "desc": "Square150x150Logo.scale-400",
80 | "folder": "windows11/",
81 | "padding": 0.3
82 | },
83 |
84 | {
85 | "width": 310,
86 | "height": 150,
87 | "name": "Wide310x150Logo.scale-100",
88 | "desc": "Wide310x150Logo.scale-100",
89 | "folder": "windows11/",
90 | "padding": 0.3
91 | },
92 | {
93 | "width": 388,
94 | "height": 188,
95 | "name": "Wide310x150Logo.scale-125",
96 | "desc": "Wide310x150Logo.scale-125",
97 | "folder": "windows11/",
98 | "padding": 0.3
99 | },
100 | {
101 | "width": 465,
102 | "height": 225,
103 | "name": "Wide310x150Logo.scale-150",
104 | "desc": "Wide310x150Logo.scale-150",
105 | "folder": "windows11/",
106 | "padding": 0.3
107 | },
108 | {
109 | "width": 620,
110 | "height": 300,
111 | "name": "Wide310x150Logo.scale-200",
112 | "desc": "Wide310x150Logo.scale-200",
113 | "folder": "windows11/",
114 | "padding": 0.3
115 | },
116 | {
117 | "width": 1240,
118 | "height": 600,
119 | "name": "Wide310x150Logo.scale-400",
120 | "desc": "Wide310x150Logo.scale-400",
121 | "folder": "windows11/",
122 | "padding": 0.3
123 | },
124 |
125 | {
126 | "width": 310,
127 | "height": 310,
128 | "name": "LargeTile.scale-100",
129 | "desc": "LargeTile.scale-100",
130 | "folder": "windows11/",
131 | "padding": 0.3
132 | },
133 | {
134 | "width": 388,
135 | "height": 388,
136 | "name": "LargeTile.scale-125",
137 | "desc": "LargeTile.scale-125",
138 | "folder": "windows11/",
139 | "padding": 0.3
140 | },
141 | {
142 | "width": 465,
143 | "height": 465,
144 | "name": "LargeTile.scale-150",
145 | "desc": "LargeTile.scale-150",
146 | "folder": "windows11/",
147 | "padding": 0.3
148 | },
149 | {
150 | "width": 620,
151 | "height": 620,
152 | "name": "LargeTile.scale-200",
153 | "desc": "LargeTile.scale-200",
154 | "folder": "windows11/",
155 | "padding": 0.3
156 | },
157 | {
158 | "width": 1240,
159 | "height": 1240,
160 | "name": "LargeTile.scale-400",
161 | "desc": "LargeTile.scale-400",
162 | "folder": "windows11/",
163 | "padding": 0.3
164 | },
165 |
166 | {
167 | "width": 44,
168 | "height": 44,
169 | "name": "Square44x44Logo.scale-100",
170 | "desc": "Square44x44Logo.scale-100",
171 | "folder": "windows11/",
172 | "padding": 0.0
173 | },
174 | {
175 | "width": 55,
176 | "height": 55,
177 | "name": "Square44x44Logo.scale-125",
178 | "desc": "Square44x44Logo.scale-125",
179 | "folder": "windows11/",
180 | "padding": 0.0
181 | },
182 | {
183 | "width": 66,
184 | "height": 66,
185 | "name": "Square44x44Logo.scale-150",
186 | "desc": "Square44x44Logo.scale-150",
187 | "folder": "windows11/",
188 | "padding": 0.0
189 | },
190 | {
191 | "width": 88,
192 | "height": 88,
193 | "name": "Square44x44Logo.scale-200",
194 | "desc": "Square44x44Logo.scale-200",
195 | "folder": "windows11/",
196 | "padding": 0.0
197 | },
198 | {
199 | "width": 176,
200 | "height": 176,
201 | "name": "Square44x44Logo.scale-400",
202 | "desc": "Square44x44Logo.scale-400",
203 | "folder": "windows11/",
204 | "padding": 0.0
205 | },
206 |
207 | {
208 | "width": 50,
209 | "height": 50,
210 | "name": "StoreLogo.scale-100",
211 | "desc": "StoreLogo.scale-100",
212 | "folder": "windows11/",
213 | "padding": 0.3
214 | },
215 | {
216 | "width": 63,
217 | "height": 63,
218 | "name": "StoreLogo.scale-125",
219 | "desc": "StoreLogo.scale-125",
220 | "folder": "windows11/",
221 | "padding": 0.3
222 | },
223 | {
224 | "width": 75,
225 | "height": 75,
226 | "name": "StoreLogo.scale-150",
227 | "desc": "StoreLogo.scale-150",
228 | "folder": "windows11/",
229 | "padding": 0.3
230 | },
231 | {
232 | "width": 100,
233 | "height": 100,
234 | "name": "StoreLogo.scale-200",
235 | "desc": "StoreLogo.scale-200",
236 | "folder": "windows11/",
237 | "padding": 0.3
238 | },
239 | {
240 | "width": 200,
241 | "height": 200,
242 | "name": "StoreLogo.scale-400",
243 | "desc": "StoreLogo.scale-400",
244 | "folder": "windows11/",
245 | "padding": 0.3
246 | },
247 |
248 | {
249 | "width": 620,
250 | "height": 300,
251 | "name": "SplashScreen.scale-100",
252 | "desc": "SplashScreen.scale-100",
253 | "folder": "windows11/",
254 | "padding": 0.3
255 | },
256 | {
257 | "width": 775,
258 | "height": 375,
259 | "name": "SplashScreen.scale-125",
260 | "desc": "SplashScreen.scale-125",
261 | "folder": "windows11/",
262 | "padding": 0.3
263 | },
264 | {
265 | "width": 930,
266 | "height": 450,
267 | "name": "SplashScreen.scale-150",
268 | "desc": "SplashScreen.scale-150",
269 | "folder": "windows11/",
270 | "padding": 0.3
271 | },
272 | {
273 | "width": 1240,
274 | "height": 600,
275 | "name": "SplashScreen.scale-200",
276 | "desc": "SplashScreen.scale-200",
277 | "folder": "windows11/",
278 | "padding": 0.3
279 | },
280 | {
281 | "width": 2480,
282 | "height": 1200,
283 | "name": "SplashScreen.scale-400",
284 | "desc": "SplashScreen.scale-400",
285 | "folder": "windows11/",
286 | "padding": 0.3
287 | },
288 |
289 | {
290 | "width": 16,
291 | "height": 16,
292 | "name": "Square44x44Logo.targetsize-16",
293 | "desc": "Square44x44Logo.targetsize-16",
294 | "folder": "windows11/",
295 | "padding": 0.0
296 | },
297 | {
298 | "width": 20,
299 | "height": 20,
300 | "name": "Square44x44Logo.targetsize-20",
301 | "desc": "Square44x44Logo.targetsize-20",
302 | "folder": "windows11/",
303 | "padding": 0.0
304 | },
305 | {
306 | "width": 24,
307 | "height": 24,
308 | "name": "Square44x44Logo.targetsize-24",
309 | "desc": "Square44x44Logo.targetsize-24",
310 | "folder": "windows11/",
311 | "padding": 0.0
312 | },
313 | {
314 | "width": 30,
315 | "height": 30,
316 | "name": "Square44x44Logo.targetsize-30",
317 | "desc": "Square44x44Logo.targetsize-30",
318 | "folder": "windows11/",
319 | "padding": 0.0
320 | },
321 | {
322 | "width": 32,
323 | "height": 32,
324 | "name": "Square44x44Logo.targetsize-32",
325 | "desc": "Square44x44Logo.targetsize-32",
326 | "folder": "windows11/",
327 | "padding": 0.0
328 | },
329 | {
330 | "width": 36,
331 | "height": 36,
332 | "name": "Square44x44Logo.targetsize-36",
333 | "desc": "Square44x44Logo.targetsize-36",
334 | "folder": "windows11/",
335 | "padding": 0.0
336 | },
337 | {
338 | "width": 40,
339 | "height": 40,
340 | "name": "Square44x44Logo.targetsize-40",
341 | "desc": "Square44x44Logo.targetsize-40",
342 | "folder": "windows11/",
343 | "padding": 0.0
344 | },
345 | {
346 | "width": 44,
347 | "height": 44,
348 | "name": "Square44x44Logo.targetsize-44",
349 | "desc": "Square44x44Logo.targetsize-44",
350 | "folder": "windows11/",
351 | "padding": 0.0
352 | },
353 | {
354 | "width": 48,
355 | "height": 48,
356 | "name": "Square44x44Logo.targetsize-48",
357 | "desc": "Square44x44Logo.targetsize-48",
358 | "folder": "windows11/",
359 | "padding": 0.0
360 | },
361 | {
362 | "width": 60,
363 | "height": 60,
364 | "name": "Square44x44Logo.targetsize-60",
365 | "desc": "Square44x44Logo.targetsize-60",
366 | "folder": "windows11/",
367 | "padding": 0.0
368 | },
369 | {
370 | "width": 64,
371 | "height": 64,
372 | "name": "Square44x44Logo.targetsize-64",
373 | "desc": "Square44x44Logo.targetsize-64",
374 | "folder": "windows11/",
375 | "padding": 0.0
376 | },
377 | {
378 | "width": 72,
379 | "height": 72,
380 | "name": "Square44x44Logo.targetsize-72",
381 | "desc": "Square44x44Logo.targetsize-72",
382 | "folder": "windows11/",
383 | "padding": 0.0
384 | },
385 | {
386 | "width": 80,
387 | "height": 80,
388 | "name": "Square44x44Logo.targetsize-80",
389 | "desc": "Square44x44Logo.targetsize-80",
390 | "folder": "windows11/",
391 | "padding": 0.0
392 | },
393 | {
394 | "width": 96,
395 | "height": 96,
396 | "name": "Square44x44Logo.targetsize-96",
397 | "desc": "Square44x44Logo.targetsize-96",
398 | "folder": "windows11/",
399 | "padding": 0.0
400 | },
401 | {
402 | "width": 256,
403 | "height": 256,
404 | "name": "Square44x44Logo.targetsize-256",
405 | "desc": "Square44x44Logo.targetsize-256",
406 | "folder": "windows11/",
407 | "padding": 0.0
408 | },
409 |
410 | {
411 | "width": 16,
412 | "height": 16,
413 | "name": "Square44x44Logo.altform-unplated_targetsize-16",
414 | "desc": "Square44x44Logo.altform-unplated_targetsize-16",
415 | "folder": "windows11/",
416 | "padding": 0.0
417 | },
418 | {
419 | "width": 20,
420 | "height": 20,
421 | "name": "Square44x44Logo.altform-unplated_targetsize-20",
422 | "desc": "Square44x44Logo.altform-unplated_targetsize-20",
423 | "folder": "windows11/",
424 | "padding": 0.0
425 | },
426 | {
427 | "width": 24,
428 | "height": 24,
429 | "name": "Square44x44Logo.altform-unplated_targetsize-24",
430 | "desc": "Square44x44Logo.altform-unplated_targetsize-24",
431 | "folder": "windows11/",
432 | "padding": 0.0
433 | },
434 | {
435 | "width": 30,
436 | "height": 30,
437 | "name": "Square44x44Logo.altform-unplated_targetsize-30",
438 | "desc": "Square44x44Logo.altform-unplated_targetsize-30",
439 | "folder": "windows11/",
440 | "padding": 0.0
441 | },
442 | {
443 | "width": 32,
444 | "height": 32,
445 | "name": "Square44x44Logo.altform-unplated_targetsize-32",
446 | "desc": "Square44x44Logo.altform-unplated_targetsize-32",
447 | "folder": "windows11/",
448 | "padding": 0.0
449 | },
450 | {
451 | "width": 36,
452 | "height": 36,
453 | "name": "Square44x44Logo.altform-unplated_targetsize-36",
454 | "desc": "Square44x44Logo.altform-unplated_targetsize-36",
455 | "folder": "windows11/",
456 | "padding": 0.0
457 | },
458 | {
459 | "width": 40,
460 | "height": 40,
461 | "name": "Square44x44Logo.altform-unplated_targetsize-40",
462 | "desc": "Square44x44Logo.altform-unplated_targetsize-40",
463 | "folder": "windows11/",
464 | "padding": 0.0
465 | },
466 | {
467 | "width": 44,
468 | "height": 44,
469 | "name": "Square44x44Logo.altform-unplated_targetsize-44",
470 | "desc": "Square44x44Logo.altform-unplated_targetsize-44",
471 | "folder": "windows11/",
472 | "padding": 0.0
473 | },
474 | {
475 | "width": 48,
476 | "height": 48,
477 | "name": "Square44x44Logo.altform-unplated_targetsize-48",
478 | "desc": "Square44x44Logo.altform-unplated_targetsize-48",
479 | "folder": "windows11/",
480 | "padding": 0.0
481 | },
482 | {
483 | "width": 60,
484 | "height": 60,
485 | "name": "Square44x44Logo.altform-unplated_targetsize-60",
486 | "desc": "Square44x44Logo.altform-unplated_targetsize-60",
487 | "folder": "windows11/",
488 | "padding": 0.0
489 | },
490 | {
491 | "width": 64,
492 | "height": 64,
493 | "name": "Square44x44Logo.altform-unplated_targetsize-64",
494 | "desc": "Square44x44Logo.altform-unplated_targetsize-64",
495 | "folder": "windows11/",
496 | "padding": 0.0
497 | },
498 | {
499 | "width": 72,
500 | "height": 72,
501 | "name": "Square44x44Logo.altform-unplated_targetsize-72",
502 | "desc": "Square44x44Logo.altform-unplated_targetsize-72",
503 | "folder": "windows11/",
504 | "padding": 0.0
505 | },
506 | {
507 | "width": 80,
508 | "height": 80,
509 | "name": "Square44x44Logo.altform-unplated_targetsize-80",
510 | "desc": "Square44x44Logo.altform-unplated_targetsize-80",
511 | "folder": "windows11/",
512 | "padding": 0.0
513 | },
514 | {
515 | "width": 96,
516 | "height": 96,
517 | "name": "Square44x44Logo.altform-unplated_targetsize-96",
518 | "desc": "Square44x44Logo.altform-unplated_targetsize-96",
519 | "folder": "windows11/",
520 | "padding": 0.0
521 | },
522 | {
523 | "width": 256,
524 | "height": 256,
525 | "name": "Square44x44Logo.altform-unplated_targetsize-256",
526 | "desc": "Square44x44Logo.altform-unplated_targetsize-256",
527 | "folder": "windows11/",
528 | "padding": 0.0
529 | },
530 |
531 | {
532 | "width": 16,
533 | "height": 16,
534 | "name": "Square44x44Logo.altform-lightunplated_targetsize-16",
535 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-16",
536 | "folder": "windows11/",
537 | "padding": 0.0
538 | },
539 | {
540 | "width": 20,
541 | "height": 20,
542 | "name": "Square44x44Logo.altform-lightunplated_targetsize-20",
543 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-20",
544 | "folder": "windows11/",
545 | "padding": 0.0
546 | },
547 | {
548 | "width": 24,
549 | "height": 24,
550 | "name": "Square44x44Logo.altform-lightunplated_targetsize-24",
551 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-24",
552 | "folder": "windows11/",
553 | "padding": 0.0
554 | },
555 | {
556 | "width": 30,
557 | "height": 30,
558 | "name": "Square44x44Logo.altform-lightunplated_targetsize-30",
559 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-30",
560 | "folder": "windows11/",
561 | "padding": 0.0
562 | },
563 | {
564 | "width": 32,
565 | "height": 32,
566 | "name": "Square44x44Logo.altform-lightunplated_targetsize-32",
567 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-32",
568 | "folder": "windows11/",
569 | "padding": 0.0
570 | },
571 | {
572 | "width": 36,
573 | "height": 36,
574 | "name": "Square44x44Logo.altform-lightunplated_targetsize-36",
575 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-36",
576 | "folder": "windows11/",
577 | "padding": 0.0
578 | },
579 | {
580 | "width": 40,
581 | "height": 40,
582 | "name": "Square44x44Logo.altform-lightunplated_targetsize-40",
583 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-40",
584 | "folder": "windows11/",
585 | "padding": 0.0
586 | },
587 | {
588 | "width": 44,
589 | "height": 44,
590 | "name": "Square44x44Logo.altform-lightunplated_targetsize-44",
591 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-44",
592 | "folder": "windows11/",
593 | "padding": 0.0
594 | },
595 | {
596 | "width": 48,
597 | "height": 48,
598 | "name": "Square44x44Logo.altform-lightunplated_targetsize-48",
599 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-48",
600 | "folder": "windows11/",
601 | "padding": 0.0
602 | },
603 | {
604 | "width": 60,
605 | "height": 60,
606 | "name": "Square44x44Logo.altform-lightunplated_targetsize-60",
607 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-60",
608 | "folder": "windows11/",
609 | "padding": 0.0
610 | },
611 | {
612 | "width": 64,
613 | "height": 64,
614 | "name": "Square44x44Logo.altform-lightunplated_targetsize-64",
615 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-64",
616 | "folder": "windows11/",
617 | "padding": 0.0
618 | },
619 | {
620 | "width": 72,
621 | "height": 72,
622 | "name": "Square44x44Logo.altform-lightunplated_targetsize-72",
623 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-72",
624 | "folder": "windows11/",
625 | "padding": 0.0
626 | },
627 | {
628 | "width": 80,
629 | "height": 80,
630 | "name": "Square44x44Logo.altform-lightunplated_targetsize-80",
631 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-80",
632 | "folder": "windows11/",
633 | "padding": 0.0
634 | },
635 | {
636 | "width": 96,
637 | "height": 96,
638 | "name": "Square44x44Logo.altform-lightunplated_targetsize-96",
639 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-96",
640 | "folder": "windows11/",
641 | "padding": 0.0
642 | },
643 | {
644 | "width": 256,
645 | "height": 256,
646 | "name": "Square44x44Logo.altform-lightunplated_targetsize-256",
647 | "desc": "Square44x44Logo.altform-lightunplated_targetsize-256",
648 | "folder": "windows11/",
649 | "padding": 0.0
650 | }
651 | ]
652 |
--------------------------------------------------------------------------------
/App_Data/windowsImages.json:
--------------------------------------------------------------------------------
1 | [
2 | { "width": 24, "height": 24, "name": "windows-smallsquare-24-24", "desc": "windows-smallsquare-24-24", "folder": "windows/" },
3 | { "width": 30, "height": 30, "name": "windows-smallsquare-30-30", "desc": "windows-smallsquare-30-30", "folder": "windows/" },
4 | { "width": 42, "height": 42, "name": "windows-smallsquare-42-42", "desc": "windows-smallsquare-42-42", "folder": "windows/" },
5 | { "width": 54, "height": 54, "name": "windows-smallsquare-54-54", "desc": "windows-smallsquare-54-54", "folder": "windows/" },
6 |
7 | { "width": 1116, "height": 540, "name": "windows-splashscreen-1116-540", "desc": "windows-splashscreen-1116-540", "folder": "windows/" },
8 | { "width": 868, "height": 420, "name": "windows-splashscreen-868-420", "desc": "windows-splashscreen-868-420", "folder": "windows/" },
9 | { "width": 620, "height": 300, "name": "windows-splashscreen-620-300", "desc": "windows-splashscreen-620-300", "folder": "windows/" },
10 |
11 | { "width": 270, "height": 270, "name": "windows-squarelogo-270-270", "desc": "windows-squarelogo-270-270", "folder": "windows/" },
12 | { "width": 210, "height": 210, "name": "windows-squarelogo-210-210", "desc": "windows-squarelogo-210-210", "folder": "windows/" },
13 | { "width": 150, "height": 150, "name": "windows-squarelogo-150-150", "desc": "windows-squarelogo-150-150", "folder": "windows/" },
14 | { "width": 120, "height": 120, "name": "windows-squarelogo-120-120", "desc": "windows-squarelogo-120-120", "folder": "windows/" },
15 |
16 | { "width": 90, "height": 90, "name": "windows-storelogo-90-90", "desc": "windows-storelogo-90-90", "folder": "windows/" },
17 | { "width": 70, "height": 70, "name": "windows-storelogo-70-70", "desc": "windows-storelogo-70-70", "folder": "windows/" },
18 | { "width": 50, "height": 50, "name": "windows-storelogo-50-50", "desc": "windows-storelogo-50-50", "folder": "windows/" },
19 |
20 | { "width": 106, "height": 106, "name": "windowsphone-appicon-106-106", "desc": "windowsphone-appicon-106-106", "folder": "windows/" },
21 | { "width": 62, "height": 62, "name": "windowsphone-appicon-62-62", "desc": "windowsphone-appicon-62-62", "folder": "windows/" },
22 | { "width": 44, "height": 44, "name": "windowsphone-appicon-44-44", "desc": "windowsphone-appicon-44-44", "folder": "windows/" },
23 |
24 | { "width": 360, "height": 360, "name": "windowsphone-mediumtile-360-360", "desc": "windowsphone-mediumtile-360-360", "folder": "windows/" },
25 | { "width": 210, "height": 210, "name": "windowsphone-mediumtile-210-210", "desc": "windowsphone-mediumtile-210-210", "folder": "windows/" },
26 | { "width": 150, "height": 150, "name": "windowsphone-mediumtile-150-150", "desc": "windowsphone-mediumtile-150-150", "folder": "windows/" },
27 |
28 | { "width": 170, "height": 170, "name": "windowsphone-smalltile-170-170", "desc": "windowsphone-mediumtile-170-170", "folder": "windows/" },
29 | { "width": 99, "height": 99, "name": "windowsphone-smalltile-99-99", "desc": "windowsphone-mediumtile-99-99", "folder": "windows/" },
30 | { "width": 71, "height": 71, "name": "windowsphone-smalltile-71-71", "desc": "windowsphone-mediumtile-71-71", "folder": "windows/" },
31 |
32 | { "width": 120, "height": 120, "name": "windowsphone-storelogo-120-120", "desc": "windowsphone-storelogo-120-120", "folder": "windows/" },
33 | { "width": 70, "height": 70, "name": "windowsphone-storelogo-70-70", "desc": "windowsphone-storelogo-70-70", "folder": "windows/" },
34 | { "width": 50, "height": 50, "name": "windowsphone-storelogo-50-50", "desc": "windowsphone-storelogo-50-50", "folder": "windows/" },
35 | ]
36 |
--------------------------------------------------------------------------------
/Controllers/ImageController.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Compression;
2 | using System.Net;
3 | using System.Text.Json;
4 |
5 | using AppImageGenerator.Models;
6 |
7 | using SixLabors.ImageSharp.Formats;
8 | using SixLabors.ImageSharp.Formats.Jpeg;
9 | using SixLabors.ImageSharp.Formats.Png;
10 | using SixLabors.ImageSharp.Formats.Webp;
11 | using SixLabors.ImageSharp.Formats.Bmp;
12 | using SixLabors.ImageSharp.Formats.Tiff;
13 |
14 | using Microsoft.AspNetCore.Mvc;
15 |
16 | using System.ComponentModel;
17 | using System.ComponentModel.DataAnnotations;
18 | using System.Text.Json.Serialization;
19 | using Microsoft.AspNetCore.Mvc.ApplicationModels;
20 | using Microsoft.AspNetCore.Mvc.Infrastructure;
21 |
22 | namespace AppImageGenerator.Controllers;
23 |
24 | [ApiController]
25 | [Route("api/")]
26 | public class ImageController : ControllerBase
27 | {
28 | private readonly IWebHostEnvironment _webHostEnvironment;
29 | private readonly ILogger _logger;
30 |
31 | private const string FileDownloadName = "AppImages.zip";
32 | private const string FileDownloadType = "application/octet-stream";
33 | private const string FileIconsJsonName = "icons.json";
34 | private const string PlatformNameCommonPart = "Images.json";
35 | private const string AppDataFolderName = "App_Data";
36 | private const string GetZipById = "getIconsZipById";
37 | private const string GenerateIconsZip = "generateIconsZip";
38 |
39 | public ImageController (IWebHostEnvironment webHostEnvironment, ILogger logger)
40 | {
41 | _logger = logger;
42 | _webHostEnvironment = webHostEnvironment;
43 | }
44 |
45 | [HttpGet(GetZipById, Name = GetZipById)]
46 | public async Task Get(string id)
47 | {
48 | try
49 | {
50 | // Create path from the id and return the file...
51 | var zipFilePath = CreateFilePathFromId(new Guid(id));
52 | if (string.IsNullOrEmpty(zipFilePath))
53 | {
54 | return new NotFoundResult();
55 | }
56 |
57 | var archive = File(await System.IO.File.ReadAllBytesAsync(zipFilePath), FileDownloadType, fileDownloadName: FileDownloadName);
58 |
59 | System.IO.File.Delete(zipFilePath);
60 |
61 | return archive;
62 | }
63 | catch (Exception ex)
64 | {
65 | if (ex.Message.StartsWith("Could not find file"))
66 | {
67 | _logger.LogError(ex, string.Format("{{GetZipById}}: Couldn't find file", GetZipById));
68 | return StatusCode((int)HttpStatusCode.NotFound, ex.ToString());
69 | }
70 | else {
71 | _logger.LogError(ex, string.Format("{{GetZipById}}: Couldn't get generated zip due to exception", GetZipById));
72 | return StatusCode((int)HttpStatusCode.InternalServerError, ex.ToString());
73 | }
74 |
75 | }
76 | }
77 |
78 | ///
79 | /// Generates a list of images from the specified base image.
80 | /// Expected arguments:
81 | /// - baseImage: the image as a multipart form POST. This is the image from which all other images will be generated. Should generally be 512x512 or larger, ideally in PNG format.
82 | /// - platform: a list of values specifying the platform(s) for which the images are being generated, e.g. "windows10"
83 | /// - padding: a value between 0 and 1 specifying the padding for the generated images.
84 | /// - color: a hex color value to use as the background color of the generated images. If null, a best guess -- the color of pixel (0,0) -- will be used.
85 | ///
86 | ///
87 | [HttpPost(GenerateIconsZip)]
88 | public async Task Post([FromForm] ImageFormData Form)
89 | {
90 | var zipId = Guid.NewGuid();
91 |
92 | try
93 | {
94 | using var args = ImageGenerationModel.FromFormData(HttpContext.Request.Form, HttpContext.Request.Form.Files);
95 |
96 | // Punt if we have invalid arguments.
97 | if (!string.IsNullOrEmpty(args.ErrorMessage))
98 | {
99 | return new ObjectResult(args.ErrorMessage) { StatusCode = (int?)HttpStatusCode.BadRequest };
100 | }
101 |
102 | var profiles = GetProfilesFromPlatforms(args.Platforms);
103 | if (profiles == null)
104 | throw new Exception(string.Format("No platforms found in config: {{PLATFORMS}}", args.Platforms != null? args.Platforms : "no param"));
105 |
106 | var imageStreams = new List(profiles.Count);
107 |
108 | using (var zip = ZipFile.Open(CreateFilePathFromId(zipId), ZipArchiveMode.Create))
109 | {
110 | var iconObject = new IconRootObject();
111 | foreach (var profile in profiles)
112 | {
113 | var stream = CreateImageStream(args, profile);
114 | if (stream != null)
115 | {
116 | imageStreams.Add(stream);
117 | var fmt = string.IsNullOrEmpty(profile.Format) ? "png" : profile.Format;
118 | var iconEntry = zip.CreateEntry(profile.Folder + profile.Name + "." + fmt, CompressionLevel.Fastest);
119 | var iconStream = iconEntry.Open();
120 | await stream.CopyToAsync(iconStream);
121 | iconStream.Close();
122 | stream.Close();
123 |
124 | iconObject.Icons.Add(new IconObject(profile.Folder + profile.Name + "." + fmt, profile.Width + "x" + profile.Height));
125 | }
126 | }
127 |
128 | var options = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
129 | var iconStr = JsonSerializer.Serialize(iconObject, options);
130 |
131 | using (StreamWriter writer = new(zip.CreateEntry(FileIconsJsonName, CompressionLevel.Optimal).Open()))
132 | {
133 | writer.Write(iconStr);
134 | }
135 |
136 | var zipFilePath = CreateFilePathFromId(zipId);
137 | imageStreams.ForEach(s => s.Dispose());
138 | }
139 |
140 | // Send back a route to download the zip file.
141 | var url = Url.RouteUrl(GetZipById, new { id = zipId.ToString() });
142 |
143 | if (url == null)
144 | throw new Exception(string.Format("Couldn't generate RouteUrl for ID: {{ID}}", zipId.ToString()));
145 |
146 | _logger.LogInformation(string.Format("{{GenerateIconsZip}}: icons generated for platforms: {{PLATFORMS}}", GenerateIconsZip,
147 | args.Platforms != null ? args.Platforms : "no param"));
148 |
149 | return new RedirectResult(url);
150 |
151 | }
152 | //catch (OutOfMemoryException ex)
153 | //{
154 | // _logger.LogError(ex, string.Format("{{GenerateIconsZip}}: Couldn't generate images due to exception" , GenerateIconsZip));
155 | // return StatusCode((int)HttpStatusCode.UnsupportedMediaType, ex.ToString());
156 | //}
157 | catch (Exception ex)
158 | {
159 | _logger.LogError(ex, string.Format("{{GenerateIconsZip}}: Couldn't generate images due to exception", GenerateIconsZip));
160 | return StatusCode((int)HttpStatusCode.UnsupportedMediaType, ex.ToString());
161 | }
162 | }
163 |
164 | // Legacy wrapper for new generateIconsZip method
165 | [Obsolete]
166 | [HttpPost("image")]
167 | public async Task LegacyGetGeneratedImagesZip([FromForm] MultipartFormDataContent Form)
168 | {
169 | var formData = new ImageFormData();
170 |
171 | // Convert from MultipartFormDataContent to ImageFormData
172 | if (Request.Form.Files.Count > 0)
173 | {
174 | formData.FileName = Request.Form.Files[0];
175 | }
176 | if (Request.Form.TryGetValue("Platform", out var platformValue))
177 | {
178 | if (Enum.TryParse(platformValue, out var platform))
179 | {
180 | formData.Platform = platform;
181 | }
182 | }
183 | if (Request.Form.TryGetValue("Padding", out var padding))
184 | {
185 | formData.Padding = padding;
186 | }
187 | if (Request.Form.TryGetValue("Color", out var color))
188 | {
189 | formData.Color = color;
190 | }
191 |
192 | if (formData.FileName == null)
193 | {
194 | _logger.LogError(null, "legacyGenerateIconsZip: Couldn't generate images due to bad FormData");
195 | return StatusCode((int)HttpStatusCode.BadRequest);
196 | }
197 |
198 | var postResponse = await Post(formData);
199 | if (postResponse is RedirectResult redirectResult)
200 | {
201 | var responseMessage = new HttpResponseMessage(HttpStatusCode.Redirect);
202 | responseMessage.Content = new StringContent(JsonSerializer.Serialize(new ImageResponse { Uri = redirectResult.Url }));
203 |
204 | HttpContext.Request.Form.TryGetValue("platform", out var platforms);
205 | _logger.LogInformation(string.Format("legacyGenerateIconsZip: icons generated for platforms: {{PLATFORMS}}", platforms.ToString()));
206 |
207 | return new ObjectResult(new ImageResponse { Uri = redirectResult.Url });
208 | }
209 | else
210 | {
211 | if (postResponse is ObjectResult redirectFail)
212 | {
213 | if (postResponse.GetType().GetProperty("Value") != null && postResponse.GetType().GetProperty("StatusCode") != null)
214 | {
215 | _logger.LogError(redirectFail.Value?.ToString(), "legacyGenerateIconsZip: Couldn't generate images due to exception");
216 | return StatusCode((int)redirectFail.StatusCode!, redirectFail.Value!.ToString());
217 | }
218 |
219 | }
220 | }
221 | _logger.LogError(null, "legacyGenerateIconsZip: Couldn't generate images due to exception");
222 | return StatusCode((int)HttpStatusCode.BadRequest);
223 | }
224 |
225 | [HttpPost("generateBase64Icons")]
226 | public ActionResult Base64([FromForm] ImageFormData Form)
227 | {
228 | try
229 | {
230 | using var args = ImageGenerationModel.FromFormData(HttpContext.Request.Form, HttpContext.Request.Form.Files);
231 | if (!string.IsNullOrEmpty(args.ErrorMessage))
232 | {
233 | return new ObjectResult(args.ErrorMessage) { StatusCode = (int?)HttpStatusCode.BadRequest };
234 | }
235 |
236 | var profiles = GetProfilesFromPlatforms(args.Platforms);
237 | if (profiles == null)
238 | throw new Exception(string.Format("No platforms found in config: {{PLATFORMS}}", args.Platforms != null ? args.Platforms : "no param"));
239 |
240 | var imgs = profiles
241 | .Select(profile => new WebManifestIcon
242 | {
243 | Purpose = "any",
244 | Sizes = $"{profile.Width}x{profile.Height}",
245 | Src = CreateBase64Image(args, profile),
246 | Type = string.IsNullOrEmpty(profile.Format) ? "image/png" : profile.Format
247 | });
248 |
249 | var options = new JsonSerializerOptions { WriteIndented = true };
250 | var response = new ObjectResult(JsonSerializer.Serialize(imgs, options));
251 |
252 | _logger.LogInformation(string.Format("generateBase64Icons: icons generated for platforms: {{PLATFORMS}}",
253 | args.Platforms != null ? args.Platforms : "no param"));
254 |
255 | return response;
256 | }
257 | catch (Exception ex)
258 | {
259 | _logger.LogError(ex, "generateBase64Icons: Couldn't generate images due to exception");
260 | return StatusCode((int)HttpStatusCode.InternalServerError, ex.ToString());
261 | }
262 | }
263 |
264 | private static string ReadStringFromConfigFile(string filePath)
265 | {
266 | using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
267 | using (var sr = new StreamReader(fs))
268 | {
269 | return sr.ReadToEnd();
270 | }
271 | }
272 |
273 | private IEnumerable GetConfig(string platformId)
274 | {
275 | var config = new List();
276 | var webRootPath = _webHostEnvironment.WebRootPath ?? _webHostEnvironment.ContentRootPath;
277 | var root = Path.Combine(webRootPath, AppDataFolderName);
278 |
279 | var filePath = Path.Combine(root, platformId + PlatformNameCommonPart);
280 | config.Add(ReadStringFromConfigFile(filePath));
281 | return config;
282 | }
283 |
284 | private IReadOnlyList? GetProfilesFromPlatforms(IEnumerable? platforms)
285 | {
286 | List? profiles = null;
287 | if (platforms != null)
288 | {
289 | foreach (var platform in platforms)
290 | {
291 | // Get the platform and profiles
292 | var config = GetConfig(platform);
293 | if (config.Count() < 1)
294 | {
295 | throw new HttpRequestException(HttpStatusCode.BadRequest.ToString());
296 | }
297 |
298 | foreach (var cfg in config)
299 | {
300 | if (cfg != null)
301 | {
302 | var profile = JsonSerializer.Deserialize>(cfg);
303 | if (profile != null)
304 | {
305 | //profiles == null ? profiles = profile : profiles.AddRange(profile);
306 | if (profiles == null)
307 | {
308 | profiles = profile;
309 | }
310 | else
311 | {
312 | profiles.AddRange(profile);
313 | }
314 | }
315 | }
316 | }
317 | }
318 | }
319 |
320 | return profiles;
321 | }
322 |
323 | private string CreateFilePathFromId(Guid id)
324 | {
325 | var webRootPath = _webHostEnvironment.WebRootPath ?? _webHostEnvironment.ContentRootPath;
326 | var root = Path.Combine(webRootPath, AppDataFolderName);
327 | var zipFilePath = Path.Combine(root, id + ".zip");
328 | return zipFilePath;
329 | }
330 |
331 | private static IImageEncoder GetEncoderFromType(string? type)
332 | {
333 | if (!string.IsNullOrEmpty(type))
334 | {
335 | string ImageType = type.ToLower();
336 |
337 | if (ImageType.EndsWith("png"))
338 | return new PngEncoder();
339 | if (ImageType.EndsWith("jpeg")|| ImageType.EndsWith("jpg"))
340 | return new JpegEncoder();
341 | if (ImageType.EndsWith("webp"))
342 | return new WebpEncoder();
343 | if (ImageType.EndsWith("bmp"))
344 | return new BmpEncoder();
345 | if (ImageType.EndsWith("tiff"))
346 | return new TiffEncoder();
347 | }
348 |
349 | return new PngEncoder();
350 | }
351 |
352 | private static MemoryStream? CreateImageStream(ImageGenerationModel model, Profile profile)
353 | {
354 | // We the individual image has padding specified, used that.
355 | // Otherwise, use the general padding passed into the model.
356 | var padding = profile.Padding ?? model.Padding;
357 |
358 | var imageEncoder = GetEncoderFromType(profile.Format);
359 |
360 | if (model.SvgFormData != null)
361 | {
362 | return ImageGenerationModel.ProcessSvgToStream(model.SvgFormData, profile.Width, profile.Height, imageEncoder, padding, model.BackgroundColor);
363 | }
364 | else if (model.BaseImage != null)
365 | {
366 | return ImageGenerationModel.ProcessImageToStream(model.BaseImage, profile.Width, profile.Height, imageEncoder, padding, model.BackgroundColor);
367 | }
368 |
369 | return null;
370 | }
371 |
372 | private static string? CreateBase64Image(ImageGenerationModel model, Profile profile)
373 | {
374 | var formatOrPng = string.IsNullOrEmpty(profile.Format) ? "image/png" : profile.Format;
375 | using (var imgStream = CreateImageStream(model, profile))
376 | { if (imgStream != null)
377 | {
378 | var base64 = Convert.ToBase64String(imgStream.ToArray());
379 | return $"data:{formatOrPng};base64,{base64}";
380 | }
381 | }
382 | return null;
383 | }
384 | }
385 |
386 | public class IconObject
387 | {
388 | public IconObject(string src, string size)
389 | {
390 | this.Src = src;
391 | this.Sizes = size;
392 | }
393 |
394 | public string Src { get; set; }
395 |
396 | public string Sizes { get; set; }
397 | }
398 |
399 | public class IconRootObject
400 | {
401 | public List Icons { get; set; } = new List();
402 | }
403 |
404 | public class ImageFormData
405 | {
406 | [Required]
407 | public IFormFile? FileName { get; set; }
408 |
409 | [Required]
410 | [DefaultValue(Platform.android)]
411 | public Platform Platform { get; set; } /* "android" | "chrome "| "firefox" | "ios" | "msteams" | "windows10" | "windows11" */
412 | public string? Padding { get; set; }
413 |
414 | public string? Color { get; set; }
415 | }
416 |
417 | public enum Platform {
418 | android,
419 | chrome, firefox, ios, msteams, windows10, windows11 }
420 |
421 | // Legacy
422 | public class ImageResponse
423 | {
424 | [JsonPropertyName("Uri")]
425 | public string? Uri { get; set; }
426 | }
427 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # docker build -t image-generator .
2 | # docker run -p 7120:80 image-generator
3 |
4 | # apt-get update && apt-get install -y libfontconfig1
5 |
6 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
7 | WORKDIR /app
8 | EXPOSE 7120
9 |
10 | ENV ASPNETCORE_URLS=http://+:7120
11 |
12 | # Creates a non-root user with an explicit UID and adds permission to access the /app folder
13 | # For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers
14 | RUN adduser -u 5678 --disabled-password --gecos "" appuser && mkdir /app/App_Data && chown -R appuser /app
15 | USER appuser
16 |
17 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
18 | WORKDIR /src
19 | COPY ["AppImageGenerator.csproj", "./"]
20 | RUN dotnet restore "AppImageGenerator.csproj"
21 | COPY . .
22 | WORKDIR "/src/."
23 | RUN dotnet build "AppImageGenerator.csproj" -c Release -o /app/build
24 |
25 | FROM build AS publish
26 | RUN dotnet publish "AppImageGenerator.csproj" -c Release -o /app/publish /p:UseAppHost=false
27 |
28 | FROM base AS final
29 | WORKDIR /app
30 | COPY --from=publish /app/publish .
31 | ENTRYPOINT ["dotnet", "AppImageGenerator.dll"]
32 |
33 | USER root
34 | RUN chown -R appuser /app/App_Data
35 | USER appuser
36 |
--------------------------------------------------------------------------------
/Models/ImageGenerationModel.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | using SixLabors.ImageSharp.Formats;
4 | using SkiaSharp;
5 | using SKSvg = Svg.Skia.SKSvg;
6 |
7 | namespace AppImageGenerator.Models
8 | {
9 | public class ImageGenerationModel : IDisposable
10 | {
11 | private bool disposed = false;
12 |
13 | public double Padding { get; set; }
14 | public Color? BackgroundColor { get; set; }
15 | public string[]? Platforms { get; set; }
16 | public IFormFile? BaseImageData { get; set; }
17 | public Image? BaseImage { get; set; }
18 | public string? ErrorMessage { get; set; }
19 | public IFormFile? SvgFormData { get; set; }
20 |
21 | public static ImageGenerationModel FromFormData(IFormCollection form, IFormFileCollection files)
22 | {
23 | // Validate base image data.
24 | var baseImageData = files.FirstOrDefault();
25 | if (baseImageData == null)
26 | {
27 | return new ImageGenerationModel
28 | {
29 | ErrorMessage = "No base image specified. Request must contain image."
30 | };
31 | }
32 |
33 | // Get the image, or SVG file name if it's an SVG image.
34 | var svgFormData = default(IFormFile);
35 | var baseImage = default(Image);
36 | if (baseImageData.Headers.ContentType.ToString().Contains("svg") == true)
37 | {
38 | svgFormData = baseImageData;
39 | }
40 | else
41 | {
42 | using var rs = baseImageData.OpenReadStream();
43 | baseImage = Image.Load(rs);
44 | }
45 |
46 | // Validate platforms.
47 | form.TryGetValue("platform", out var platforms);
48 |
49 | if (platforms.Count < 1)
50 | {
51 | return new ImageGenerationModel
52 | {
53 | ErrorMessage = "No platform has been specified."
54 | };
55 | }
56 |
57 | // Validate the padding.
58 | form.TryGetValue("padding", out var paddings);
59 | var hasPadding = paddings.Count > 0 ? true : false;
60 | double padding = 0;
61 |
62 | if (hasPadding)
63 | {
64 | double.TryParse(paddings.First()?.Replace(',', '.'), NumberStyles.Number, CultureInfo.InvariantCulture, out padding);
65 | }
66 |
67 | if (!hasPadding || padding < 0 || padding > 1.0)
68 | {
69 | padding = 0;
70 | }
71 |
72 | // Validate the color.
73 | form.TryGetValue("color", out var colorStrings);
74 |
75 | var colorStr = colorStrings.First();
76 | var color = Color.FromRgba(0, 0, 0, 0);
77 |
78 | if (!string.IsNullOrEmpty(colorStr))
79 | {
80 | try
81 | {
82 | if (!Color.TryParse(colorStr, out color) && !Color.TryParseHex(colorStr, out color))
83 | {
84 | throw new ArgumentException("Parsing color unsucessful");
85 | }
86 | }
87 | catch
88 | {
89 | return new ImageGenerationModel
90 | {
91 | ErrorMessage = "Background Color value invalid. Please input a valid hex color."
92 | };
93 | }
94 | }
95 |
96 | return new ImageGenerationModel
97 | {
98 | BaseImageData = baseImageData,
99 | BaseImage = baseImage,
100 | SvgFormData = svgFormData,
101 | BackgroundColor = color,
102 | Padding = padding,
103 | Platforms = platforms
104 | };
105 | }
106 |
107 | public static MemoryStream? ProcessSvgToStream(IFormFile inputSvg, int newWidth, int newHeight, IImageEncoder imageEncoder, double? paddingProp, Color? backgroundColor = null)
108 | {
109 | using (var svg = new SKSvg())
110 | {
111 | using var svgStream = inputSvg.OpenReadStream();
112 | if (svg.Load(svgStream) != null && svg.Picture != null)
113 | {
114 | int adjustWidth;
115 | int adjustedHeight;
116 | int paddingW;
117 | int paddingH;
118 |
119 | if (paddingProp > 0)
120 | {
121 | paddingW = (int)(paddingProp * newWidth * 0.5);
122 | adjustWidth = newWidth - paddingW;
123 | paddingH = (int)(paddingProp * newHeight * 0.5);
124 | adjustedHeight = newHeight - paddingH;
125 | }
126 | else
127 | {
128 | paddingW = paddingH = 0;
129 | adjustWidth = newWidth;
130 | adjustedHeight = newHeight;
131 | }
132 |
133 | // Translate, scale and convert SVG to Image
134 | var svgWidth = svg.Picture.CullRect.Width;
135 | var svgHeight = svg.Picture.CullRect.Height;
136 | var svgMax = Math.Max(svgWidth, svgHeight);
137 | var imageMin = Math.Min(adjustWidth, adjustedHeight);
138 | var scale = imageMin / svgMax;
139 | var scaleMatrix = SKMatrix.CreateIdentity();
140 | SKMatrix.Concat(ref scaleMatrix,
141 | SKMatrix.CreateTranslation((adjustWidth - svgWidth * scale) / 2 , (adjustedHeight - svgHeight * scale) / 2),
142 |
143 | SKMatrix.CreateScale(scale, scale));
144 |
145 | var SkiaImage = SKImage.FromPicture(svg.Picture, new SKSizeI(adjustWidth, adjustedHeight), scaleMatrix);
146 |
147 | // Save the image to the stream in the specified format
148 | var outputImage = new MemoryStream();
149 | using (SKData data = SkiaImage.Encode(SKEncodedImageFormat.Png, 100))
150 | {
151 | data.SaveTo(outputImage);
152 | }
153 | outputImage.Position = 0;
154 |
155 | // Conver to ImageSharp and Resize with padding
156 | Image processedImage = Image.Load(outputImage);
157 | outputImage.Position = 0;
158 |
159 | if (backgroundColor != null)
160 | {
161 | processedImage.Mutate(x => x.BackgroundColor((Color)backgroundColor));
162 | }
163 | if (paddingProp > 0)
164 | {
165 | processedImage.Mutate(x => x.Resize(
166 | new ResizeOptions
167 | {
168 | Size = new Size(newWidth, newHeight),
169 | Mode = ResizeMode.BoxPad,
170 | PadColor = backgroundColor ?? Color.Transparent
171 | }));
172 | }
173 |
174 |
175 | processedImage.Save(outputImage, imageEncoder);
176 | outputImage.Position = 0;
177 |
178 | return outputImage;
179 |
180 | }
181 | else
182 | {
183 | return null;
184 | }
185 |
186 | }
187 | }
188 |
189 | public static MemoryStream ProcessImageToStream(Image inputImage, int newWidth, int newHeight, IImageEncoder imageEncoder, double paddingProp = 0, Color? backgroundColor = null)
190 | {
191 | int adjustWidth;
192 | int adjustedHeight;
193 | var processedImage = inputImage.Clone(x => { });
194 |
195 | if (paddingProp > 0)
196 | {
197 | adjustWidth = newWidth - (int)(paddingProp * newWidth * 0.5);
198 | adjustedHeight = newHeight - (int)(paddingProp * newHeight * 0.5);
199 | }
200 | else
201 | {
202 | adjustWidth = newWidth;
203 | adjustedHeight = newHeight;
204 | }
205 |
206 | processedImage.Mutate(x => x.Resize(new ResizeOptions
207 | {
208 | Size = new Size(adjustWidth, adjustedHeight),
209 | Mode = ResizeMode.Pad,
210 | Sampler = KnownResamplers.Lanczos3
211 | }));
212 |
213 | if (backgroundColor != null)
214 | {
215 | processedImage.Mutate(x => x.BackgroundColor((Color)backgroundColor));
216 | }
217 |
218 | if (paddingProp > 0)
219 | {
220 | processedImage.Mutate(x => x.Resize(
221 | new ResizeOptions
222 | {
223 | Size = new Size(newWidth, newHeight),
224 | Mode = ResizeMode.BoxPad,
225 | PadColor = backgroundColor ?? Color.Transparent
226 | })
227 | );
228 | }
229 |
230 | var outputImage = new MemoryStream();
231 | processedImage.Save(outputImage, imageEncoder);
232 | outputImage.Position = 0;
233 |
234 |
235 | return outputImage;
236 | }
237 |
238 | public void Dispose()
239 | {
240 | Dispose(true);
241 | GC.SuppressFinalize(this);
242 | }
243 |
244 | protected virtual void Dispose(bool disposing)
245 | {
246 | if (disposed)
247 | {
248 | return;
249 | }
250 |
251 | if (disposing)
252 | {
253 | if (BaseImage != null)
254 | {
255 | BaseImage.Dispose();
256 | }
257 |
258 | if (!string.IsNullOrEmpty(BaseImageData?.FileName))
259 | {
260 | File.Delete(BaseImageData.FileName);
261 | }
262 | }
263 |
264 | disposed = true;
265 | }
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/Models/Profile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.Serialization;
5 | using System.Text.Json.Serialization;
6 |
7 | namespace AppImageGenerator.Models
8 | {
9 | public class Profile
10 | {
11 | [JsonPropertyName("width")]
12 | public int Width { get; set; }
13 |
14 | [JsonPropertyName("height")]
15 | public int Height { get; set; }
16 |
17 | [JsonPropertyName("name")]
18 | public string? Name { get; set; }
19 |
20 | [JsonPropertyName("desc")]
21 | public string? Desc { get; set; }
22 |
23 | [JsonPropertyName("folder")]
24 | public string? Folder { get; set; }
25 |
26 | [JsonPropertyName("format")]
27 | public string? Format { get; set; }
28 |
29 | [JsonPropertyName("padding")]
30 | public double? Padding { get; set; }
31 | }
32 | }
--------------------------------------------------------------------------------
/Models/WebManifestIcon.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace AppImageGenerator.Models
4 | {
5 | ///
6 | /// A web manifest icon.
7 | ///
8 | public class WebManifestIcon
9 | {
10 | [JsonPropertyName("src")]
11 | public string? Src { get; set; }
12 | [JsonPropertyName("type")]
13 | public string? Type { get; set; }
14 | [JsonPropertyName("sizes")]
15 | public string? Sizes { get; set; }
16 | [JsonPropertyName("purpose")]
17 | public string? Purpose { get; set; } // "any" | "maskable" | "monochrome";
18 |
19 | public bool IsSquare()
20 | {
21 | if (Sizes == null)
22 | {
23 | return false;
24 | }
25 |
26 | return GetAllDimensions()
27 | .Any(d => d.width == d.height);
28 | }
29 |
30 | ///
31 | /// Gets the largest dimension for the image.
32 | ///
33 | ///
34 | public (int width, int height)? GetLargestDimension()
35 | {
36 | var largest = GetAllDimensions()
37 | .OrderByDescending(i => i.width + i.height)
38 | .FirstOrDefault();
39 | if (largest.height == 0 && largest.width == 0)
40 | {
41 | return null;
42 | }
43 |
44 | return largest;
45 | }
46 |
47 | ///
48 | /// Finds the largest dimension from the property
49 | ///
50 | /// The largest dimension from the string. If no valid size could be found, null.
51 | public List<(int width, int height)> GetAllDimensions()
52 | {
53 | if (Sizes == null)
54 | {
55 | return new List<(int width, int height)>(0);
56 | }
57 |
58 | return Sizes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
59 | .Select(size => size.Split(new[] { 'x' }, StringSplitOptions.RemoveEmptyEntries))
60 | .Select(widthAndHeight =>
61 | {
62 | if (int.TryParse(widthAndHeight.ElementAtOrDefault(0), out var width) &&
63 | int.TryParse(widthAndHeight.ElementAtOrDefault(1), out var height))
64 | {
65 | return (width, height);
66 | }
67 | return (width: 0, height: 0);
68 | })
69 | .Where(d => d.width != 0 && d.height != 0)
70 | .ToList();
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | var builder = WebApplication.CreateBuilder(args);
4 |
5 | builder.Logging.ClearProviders();
6 | builder.Logging.AddConsole();
7 |
8 | // Add services to the container.
9 |
10 | builder.Services.AddControllers()
11 | .AddJsonOptions(options =>
12 | {
13 | options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
14 | });
15 | builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase);
16 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
17 | builder.Services.AddEndpointsApiExplorer();
18 |
19 | // Configure swagger to use strings for enums
20 | builder.Services.AddSwaggerGen(config => { config.UseInlineDefinitionsForEnums(); });
21 | builder.Services.Configure(options =>
22 | {
23 | options.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
24 | options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
25 | });
26 | builder.Services.Configure(options =>
27 | {
28 | options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
29 | options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
30 | });
31 |
32 | var app = builder.Build();
33 |
34 | // Configure the HTTP request pipeline.
35 | // if (app.Environment.IsDevelopment())
36 | // {
37 | app.UseSwagger();
38 | app.UseSwaggerUI();
39 | // }
40 |
41 |
42 | // app.UseHttpsRedirection();
43 |
44 | app.UseAuthorization();
45 |
46 | app.MapControllers();
47 |
48 | app.Run();
49 |
--------------------------------------------------------------------------------
/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "http": {
4 | "commandName": "Project",
5 | "launchBrowser": true,
6 | "launchUrl": "swagger",
7 | "environmentVariables": {
8 | "ASPNETCORE_ENVIRONMENT": "Development"
9 | },
10 | "dotnetRunMessages": true,
11 | "applicationUrl": "http://localhost:5248"
12 | },
13 | "https": {
14 | "commandName": "Project",
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "environmentVariables": {
18 | "ASPNETCORE_ENVIRONMENT": "Development"
19 | },
20 | "dotnetRunMessages": true,
21 | "applicationUrl": "https://localhost:7120;http://localhost:5248"
22 | },
23 | "IIS Express": {
24 | "commandName": "IISExpress",
25 | "launchBrowser": true,
26 | "launchUrl": "swagger",
27 | "environmentVariables": {
28 | "ASPNETCORE_ENVIRONMENT": "Development"
29 | }
30 | },
31 | "Docker": {
32 | "commandName": "Docker",
33 | "launchBrowser": true,
34 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
35 | "publishAllPorts": true,
36 | "useSSL": true
37 | }
38 | },
39 | "$schema": "https://json.schemastore.org/launchsettings.json",
40 | "iisSettings": {
41 | "windowsAuthentication": false,
42 | "anonymousAuthentication": true,
43 | "iisExpress": {
44 | "applicationUrl": "http://localhost:38097",
45 | "sslPort": 44348
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Please use our [main repository for any issues/bugs/features suggestion](https://github.com/pwa-builder/PWABuilder/issues/new/choose).
2 |
3 | # App Image Generator
4 | A web tool that generates images for various app platforms. Used by PWABuilder.com.
5 |
6 | See a version of the website at [http://appimagegenerator-prod.azurewebsites.net](http://appimagegenerator-prod.azurewebsites.net/swagger/index.html).
7 |
8 | Written in C# and .NET 7.0.0
9 |
10 | Origionally written by [Peter Daukintis](https://github.com/peted70)
11 |
12 | ## Usage
13 |
14 | POST to **/api/generateImagesZip** or **/api/generateBase64Images** with the following Form Data:
15 |
16 | | Option | Type | Description |
17 | |--------------|-----------|------------|
18 | | fileName | bytes (Blob or File in JS) | The bytes of the file from which to generate all the image of the target platform(s) |
19 | | platform | "android" \| "chrome "\| "firefox" \| "ios" \| "msteams" \| "windows10" \| "windows11" | The platforms to generate the images for. To use multiple platforms, add this field multiple times to the form data |
20 | | padding | number | Optional. How much padding to add to generated images. Must be between 0 and 1, where 0 is no padding and 1 is maximum. 0.3 is a reasonable value. Some platforms, such as Windows 11, control padding per-image. In such cases, this value will be ignored. |
21 | | color | string | Optional. The background color to fill in the images' padding and transparent areas. The string can be a well-known color ("blue"), a hex color ("#ffeedd"), a hex color with alpha (#2828ab73). If omitted or null, the background color will be transparent. |
22 |
23 |
24 | ## Development
25 |
26 | dotnet build
27 | dotnet watch
28 |
29 | ## Docker
30 |
31 | docker build -t image-generator .
32 | docker run -p 7120:80 image-generator
33 | http://localhost/swagger/index.html
34 |
--------------------------------------------------------------------------------
/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/docker-compose.debug.yml:
--------------------------------------------------------------------------------
1 | # Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
2 |
3 | version: '3.4'
4 |
5 | services:
6 | appimagegenerator:
7 | image: appimagegenerator
8 | build:
9 | context: .
10 | dockerfile: ./Dockerfile
11 | ports:
12 | - 80:7120
13 | environment:
14 | - ASPNETCORE_ENVIRONMENT=Development
15 | volumes:
16 | - ~/.vsdbg:/remote_debugger:rw
17 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
2 |
3 | version: '3.4'
4 |
5 | services:
6 | appimagegenerator:
7 | image: appimagegenerator
8 | build:
9 | context: .
10 | dockerfile: ./Dockerfile
11 | ports:
12 | - 80:7120
13 |
--------------------------------------------------------------------------------