|
1 |
| -using SixLabors.ImageSharp; |
2 |
| -using SixLabors.ImageSharp.PixelFormats; |
| 1 | +using SkiaSharp; |
| 2 | +using SS.Utilities; |
3 | 3 | using System;
|
4 | 4 | using System.Collections.Generic;
|
5 | 5 | using System.Collections.ObjectModel;
|
| 6 | +using System.IO; |
6 | 7 | using System.IO.MemoryMappedFiles;
|
7 | 8 | using System.Runtime.InteropServices;
|
8 | 9 |
|
@@ -180,38 +181,114 @@ protected void ReadPlainTileData(MemoryMappedViewAccessor accessor, long positio
|
180 | 181 | IsTileDataLoaded = true;
|
181 | 182 | }
|
182 | 183 |
|
183 |
| - /// <summary> |
184 |
| - /// Creates an image of the map, saving it to a specified <paramref name="path"/>. |
185 |
| - /// The image format is automatically determined based on the filename extension. |
186 |
| - /// </summary> |
187 |
| - /// <param name="path"></param> |
188 |
| - /// <exception cref="ArgumentException">The <paramref name="path"/> is null or white-space.</exception> |
189 |
| - /// <exception cref="NotSupportedException">No encoder available for the provided <paramref name="path"/>.</exception> |
190 |
| - public void SaveImage(string path) |
| 184 | + // Note: It seems SkiaSharp only supports encoding to jpg, png, and webp even though it has many other image formats defined. |
| 185 | + private static readonly Trie<SKEncodedImageFormat> _extensionToImageFormatTrie = new(false) |
191 | 186 | {
|
192 |
| - if (string.IsNullOrWhiteSpace(path)) |
193 |
| - throw new ArgumentException("A path is required.", nameof(path)); |
| 187 | + //{ ".bmp", SKEncodedImageFormat.Bmp }, |
| 188 | + //{ "bmp", SKEncodedImageFormat.Bmp }, |
| 189 | + //{ ".gif", SKEncodedImageFormat.Gif }, |
| 190 | + //{ "gif", SKEncodedImageFormat.Gif }, |
| 191 | + //{ ".ico", SKEncodedImageFormat.Ico}, |
| 192 | + //{ "ico", SKEncodedImageFormat.Ico}, |
| 193 | + { ".jpg", SKEncodedImageFormat.Jpeg }, |
| 194 | + { "jpg", SKEncodedImageFormat.Jpeg }, |
| 195 | + { ".jpeg", SKEncodedImageFormat.Jpeg }, |
| 196 | + { "jpeg", SKEncodedImageFormat.Jpeg }, |
| 197 | + { ".png", SKEncodedImageFormat.Png }, |
| 198 | + { "png", SKEncodedImageFormat.Png }, |
| 199 | + { ".webp", SKEncodedImageFormat.Webp }, |
| 200 | + { "webp", SKEncodedImageFormat.Webp }, |
| 201 | + //{ ".heif", SKEncodedImageFormat.Heif }, |
| 202 | + //{ "heif", SKEncodedImageFormat.Heif }, |
| 203 | + }; |
194 | 204 |
|
195 |
| - using Image<Rgb24> image = new(1024, 1024, Color.Black); |
| 205 | + /// <summary> |
| 206 | + /// Creates an image of the map, saving it to a specified <paramref name="path"/>. |
| 207 | + /// </summary> |
| 208 | + /// <param name="path">The path to save the file to. The image format is automatically determined based on the filename extension.</param> |
| 209 | + /// <exception cref="ArgumentException">The <paramref name="path"/> is null or white-space.</exception> |
| 210 | + /// <exception cref="ArgumentException">The <paramref name="path"/> file extension specifies an unsupported image format.</exception> |
| 211 | + /// <exception cref="Exception">Error encoding image.</exception> |
| 212 | + public void SaveImage(string path) |
| 213 | + { |
| 214 | + ArgumentException.ThrowIfNullOrWhiteSpace(path); |
196 | 215 |
|
197 |
| - foreach (KeyValuePair<MapCoordinate, MapTile> kvp in _tileLookup) |
198 |
| - { |
199 |
| - Color color = kvp.Value switch |
200 |
| - { |
201 |
| - { IsDoor: true } => Color.Blue, |
202 |
| - { IsSafe: true } => Color.LightGreen, |
203 |
| - { IsTurfFlag: true } => Color.Yellow, |
204 |
| - { IsGoal: true } => Color.Red, |
205 |
| - { IsWormhole: true } => Color.Purple, |
206 |
| - { IsFlyOver: true } => Color.DarkGray, |
207 |
| - { IsFlyUnder: true } => Color.DarkGray, |
208 |
| - _ => Color.White |
209 |
| - }; |
210 |
| - |
211 |
| - image[kvp.Key.X, kvp.Key.Y] = color; |
212 |
| - } |
| 216 | + string extension = Path.GetExtension(path); |
| 217 | + if (!_extensionToImageFormatTrie.TryGetValue(extension, out SKEncodedImageFormat format)) |
| 218 | + throw new ArgumentException("Unsupported image format.", nameof(path)); |
213 | 219 |
|
214 |
| - image.Save(path); |
215 |
| - } |
216 |
| - } |
| 220 | + using SKBitmap bitmap = CreateBitmap(); |
| 221 | + |
| 222 | + bool success = false; |
| 223 | + |
| 224 | + using (FileStream fs = new(path, FileMode.CreateNew)) |
| 225 | + { |
| 226 | + success = bitmap.Encode(fs, format, 100); |
| 227 | + } |
| 228 | + |
| 229 | + if (!success) |
| 230 | + { |
| 231 | + try |
| 232 | + { |
| 233 | + File.Delete(path); |
| 234 | + } |
| 235 | + catch |
| 236 | + { |
| 237 | + } |
| 238 | + |
| 239 | + throw new Exception($"Error encoding as {format}."); |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + /// <summary> |
| 244 | + /// Creates an image of the map, saving it to a specified <paramref name="path"/>. |
| 245 | + /// </summary> |
| 246 | + /// <param name="imageFormat">The format to save the image as.</param> |
| 247 | + /// <exception cref="ArgumentException">The <paramref name="imageFormat"/> is white-space.</exception> |
| 248 | + /// <exception cref="ArgumentException">Unsupported image format for the provided <paramref name="imageFormat"/>.</exception> |
| 249 | + /// <exception cref="Exception">Error encoding image.</exception> |
| 250 | + public void SaveImage(Stream stream, ReadOnlySpan<char> imageFormat) |
| 251 | + { |
| 252 | + ArgumentNullException.ThrowIfNull(stream); |
| 253 | + |
| 254 | + if (imageFormat.IsWhiteSpace()) |
| 255 | + throw new ArgumentException("Cannot be whitespace.", nameof(imageFormat)); |
| 256 | + |
| 257 | + if (!_extensionToImageFormatTrie.TryGetValue(imageFormat, out SKEncodedImageFormat format)) |
| 258 | + throw new ArgumentException("Unsupported image format.", nameof(imageFormat)); |
| 259 | + |
| 260 | + using SKBitmap bitmap = CreateBitmap(); |
| 261 | + |
| 262 | + if (!bitmap.Encode(stream, format, 100)) |
| 263 | + throw new Exception($"Error encoding as {format}."); |
| 264 | + } |
| 265 | + |
| 266 | + private SKBitmap CreateBitmap() |
| 267 | + { |
| 268 | + SKImageInfo info = new(1024, 1024); |
| 269 | + SKBitmap bitmap = new(info); |
| 270 | + |
| 271 | + using SKCanvas canvas = new(bitmap); |
| 272 | + canvas.Clear(SKColors.Black); |
| 273 | + |
| 274 | + foreach (KeyValuePair<MapCoordinate, MapTile> kvp in _tileLookup) |
| 275 | + { |
| 276 | + SKColor color = kvp.Value switch |
| 277 | + { |
| 278 | + { IsDoor: true } => SKColors.Blue, |
| 279 | + { IsSafe: true } => SKColors.LightGreen, |
| 280 | + { IsTurfFlag: true } => SKColors.Yellow, |
| 281 | + { IsGoal: true } => SKColors.Red, |
| 282 | + { IsWormhole: true } => SKColors.Purple, |
| 283 | + { IsFlyOver: true } => SKColors.DarkGray, |
| 284 | + { IsFlyUnder: true } => SKColors.DarkGray, |
| 285 | + _ => SKColors.White |
| 286 | + }; |
| 287 | + |
| 288 | + canvas.DrawPoint(kvp.Key.X, kvp.Key.Y, color); |
| 289 | + } |
| 290 | + |
| 291 | + return bitmap; |
| 292 | + } |
| 293 | + } |
217 | 294 | }
|
0 commit comments