using System.Diagnostics;
using ImageMagick;
using ImageMagick.Formats;
using SkiaSharp;
namespace PicView.Core.ImageDecoding
{
///
/// Provides methods for decoding various image formats.
///
public static class ImageDecoder
{
///
/// Asynchronously reads and returns a MagickImage from the specified FileInfo.
///
/// The FileInfo representing the image file.
/// A Task containing the MagickImage if successful, otherwise null.
public static async Task 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;
}
}
///
/// Asynchronously reads and returns a MagickImage from the specified FileInfo for SVG format.
///
/// The FileInfo representing the SVG file.
/// The MagickFormat for the SVG image (SVG or SVGZ).
/// A Task containing the MagickImage for SVG if successful, otherwise null.
public static async Task 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;
}
}
///
/// Asynchronously reads and returns an SKBitmap from the specified FileInfo.
///
/// The FileInfo representing the image file.
/// A Task containing the SKBitmap if successful, otherwise null.
// ReSharper disable once InconsistentNaming
public static async Task 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;
}
}
}
}