| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- using System.Diagnostics;
- using ImageMagick;
- using ImageMagick.Formats;
- using SkiaSharp;
- namespace PicView.Core.ImageDecoding
- {
- /// <summary>
- /// Provides methods for decoding various image formats.
- /// </summary>
- public static class ImageDecoder
- {
- /// <summary>
- /// Asynchronously reads and returns a MagickImage from the specified FileInfo.
- /// </summary>
- /// <param name="fileInfo">The FileInfo representing the image file.</param>
- /// <returns>A Task containing the MagickImage if successful, otherwise null.</returns>
- public static async Task<MagickImage?> GetMagickImageAsync(FileInfo fileInfo)
- {
- try
- {
- var magickImage = new MagickImage();
- MagickFormat format;
- var extension = fileInfo.Extension.ToLower();
- switch (extension)
- {
- case ".heic":
- case ".heif":
- magickImage.Settings.SetDefines(new HeicReadDefines
- {
- PreserveOrientation = true,
- DepthImage = true,
- });
- format = extension is ".heic" ? MagickFormat.Heic : MagickFormat.Heif;
- break;
- case ".jp2":
- magickImage.Settings.SetDefines(new Jp2ReadDefines
- {
- QualityLayers = 100,
- });
- format = MagickFormat.Jp2;
- break;
- case ".tif":
- case ".tiff":
- magickImage.Settings.SetDefines(new TiffReadDefines
- {
- IgnoreTags = new[]
- {
- "34022", // ColorTable
- "34025", // ImageColorValue
- "34026", // BackgroundColorValue
- "32928",
- },
- });
- format = MagickFormat.Tif;
- break;
- case ".psd":
- format = MagickFormat.Psd;
- break;
- default:
- format = MagickFormat.Unknown;
- break;
- }
- if (fileInfo.Length >= 2147483648)
- {
- await using var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read,
- FileShare.ReadWrite, bufferSize: 4096, useAsync: fileInfo.Length > 1e+8);
- // Fixes "The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size."
- // ReSharper disable once MethodHasAsyncOverload
- magickImage.Read(fileStream, format);
- }
- else
- {
- await magickImage.ReadAsync(fileInfo, format).ConfigureAwait(false);
- }
- magickImage?.AutoOrient();
- return magickImage;
- }
- catch (Exception e)
- {
- #if DEBUG
- Trace.WriteLine($"{nameof(GetMagickImageAsync)} {fileInfo.Name} exception, \n {e.Message}");
- #endif
- return null;
- }
- }
- /// <summary>
- /// Asynchronously reads and returns a MagickImage from the specified FileInfo for SVG format.
- /// </summary>
- /// <param name="fileInfo">The FileInfo representing the SVG file.</param>
- /// <param name="magickFormat">The MagickFormat for the SVG image (SVG or SVGZ).</param>
- /// <returns>A Task containing the MagickImage for SVG if successful, otherwise null.</returns>
- public static async Task<MagickImage?> GetMagickSvgAsync(FileInfo fileInfo, MagickFormat magickFormat)
- {
- try
- {
- var magickImage = new MagickImage
- {
- Quality = 100,
- ColorSpace = ColorSpace.Transparent,
- BackgroundColor = MagickColors.Transparent,
- Format = magickFormat,
- };
- // Streams with a length larger than 2GB are not supported, read from file instead
- if (fileInfo.Length >= 2147483648)
- {
- await Task.Run(() =>
- {
- magickImage = new MagickImage();
- magickImage.Read(fileInfo);
- }).ConfigureAwait(false);
- }
- else
- {
- await using var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read,
- FileShare.ReadWrite, bufferSize: 4096, useAsync: fileInfo.Length > 1e+8);
- var data = new byte[fileStream.Length];
- // ReSharper disable once MustUseReturnValue
- await fileStream.ReadAsync(data.AsMemory(0, (int)fileStream.Length)).ConfigureAwait(false);
- magickImage.Read(data);
- }
- magickImage.Settings.BackgroundColor = MagickColors.Transparent;
- magickImage.Settings.FillColor = MagickColors.Transparent;
- magickImage.Settings.SetDefine("svg:xml-parse-huge", "true");
- return magickImage;
- }
- catch (Exception e)
- {
- #if DEBUG
- Trace.WriteLine($"{nameof(GetMagickSvgAsync)} {fileInfo.Name} exception, \n {e.Message}");
- #endif
- return null;
- }
- }
- /// <summary>
- /// Asynchronously reads and returns an SKBitmap from the specified FileInfo.
- /// </summary>
- /// <param name="fileInfo">The FileInfo representing the image file.</param>
- /// <returns>A Task containing the SKBitmap if successful, otherwise null.</returns>
- // ReSharper disable once InconsistentNaming
- public static async Task<SKBitmap?> GetSKBitmapAsync(this FileInfo fileInfo)
- {
- try
- {
- await using var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read,
- FileShare.ReadWrite, bufferSize: 4096, useAsync: fileInfo.Length > 1e+8);
- return SKBitmap.Decode(fileStream);
- }
- catch (Exception e)
- {
- #if DEBUG
- Trace.WriteLine($"{nameof(GetSKBitmapAsync)} {fileInfo.Name} exception:\n{e.Message}");
- #endif
- return null;
- }
- }
- }
- }
|