diff --git a/src/BlurHashSharp.SkiaSharp/BlurHashEncoder.cs b/src/BlurHashSharp.SkiaSharp/BlurHashEncoder.cs
index e762fb6..acd79de 100644
--- a/src/BlurHashSharp.SkiaSharp/BlurHashEncoder.cs
+++ b/src/BlurHashSharp.SkiaSharp/BlurHashEncoder.cs
@@ -15,9 +15,10 @@ public static class BlurHashEncoder
/// The number y components.
/// The IO stream of an encoded image.
/// BlurHash representation of the image.
+ /// The image is in an unsupported format or could not be decoded.
public static string Encode(int xComponent, int yComponent, Stream stream)
{
- using (SKCodec codec = SKCodec.Create(stream))
+ using (SKCodec codec = CreateCodec(stream))
{
return Encode(xComponent, yComponent, codec);
}
@@ -30,9 +31,11 @@ public static string Encode(int xComponent, int yComponent, Stream stream)
/// The number y components.
/// The path to an encoded image on the file system.
/// BlurHash representation of the image.
+ /// The file does not exist.
+ /// The image is in an unsupported format or could not be decoded.
public static string Encode(int xComponent, int yComponent, string filename)
{
- using (SKCodec codec = SKCodec.Create(filename))
+ using (SKCodec codec = CreateCodec(filename))
{
return Encode(xComponent, yComponent, codec);
}
@@ -49,7 +52,7 @@ internal static string Encode(int xComponent, int yComponent, SKCodec codec)
ColorSpace = SKColorSpace.CreateSrgb()
};
- using (SKBitmap bitmap = SKBitmap.Decode(codec, newInfo))
+ using (SKBitmap bitmap = DecodeBitmap(codec, newInfo))
{
return EncodeInternal(xComponent, yComponent, bitmap);
}
@@ -64,9 +67,10 @@ internal static string Encode(int xComponent, int yComponent, SKCodec codec)
/// The maximum width to resize the image to.
/// The maximum height to resize the image to.
/// BlurHash representation of the image.
+ /// The image is in an unsupported format or could not be decoded.
public static string Encode(int xComponent, int yComponent, Stream stream, int maxWidth, int maxHeight)
{
- using (SKCodec codec = SKCodec.Create(stream))
+ using (SKCodec codec = CreateCodec(stream))
{
return Encode(xComponent, yComponent, codec, maxWidth, maxHeight);
}
@@ -81,9 +85,11 @@ public static string Encode(int xComponent, int yComponent, Stream stream, int m
/// The maximum width to resize the image to.
/// The maximum height to resize the image to.
/// BlurHash representation of the image.
+ /// The file does not exist.
+ /// The image is in an unsupported format or could not be decoded.
public static string Encode(int xComponent, int yComponent, string filename, int maxWidth, int maxHeight)
{
- using (SKCodec codec = SKCodec.Create(filename))
+ using (SKCodec codec = CreateCodec(filename))
{
return Encode(xComponent, yComponent, codec, maxWidth, maxHeight);
}
@@ -111,7 +117,7 @@ internal static string Encode(int xComponent, int yComponent, SKCodec codec, int
ColorSpace = SKColorSpace.CreateSrgb()
};
- using (SKBitmap bitmap = SKBitmap.Decode(codec, newInfo))
+ using (SKBitmap bitmap = DecodeBitmap(codec, newInfo))
{
if (scaleFactor == 0f)
{
@@ -129,6 +135,25 @@ internal static string Encode(int xComponent, int yComponent, SKCodec codec, int
}
}
+ private static SKCodec CreateCodec(Stream stream)
+ => SKCodec.Create(stream)
+ ?? throw new InvalidDataException("The stream does not contain an image in a format supported by SkiaSharp.");
+
+ private static SKCodec CreateCodec(string filename)
+ {
+ if (!File.Exists(filename))
+ {
+ throw new FileNotFoundException("The image file could not be found.", filename);
+ }
+
+ return SKCodec.Create(filename)
+ ?? throw new InvalidDataException($"The file '{filename}' does not contain an image in a format supported by SkiaSharp.");
+ }
+
+ private static SKBitmap DecodeBitmap(SKCodec codec, SKImageInfo info)
+ => SKBitmap.Decode(codec, info)
+ ?? throw new InvalidDataException("The image could not be decoded; the data is corrupt or truncated.");
+
internal static string EncodeInternal(int xComponent, int yComponent, SKBitmap bitmap)
=> CoreBlurHashEncoder.Encode(xComponent, yComponent, bitmap.Width, bitmap.Height, bitmap.GetPixelSpan(), bitmap.RowBytes, PixelFormat.RGB888x);
}
diff --git a/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashEncoderInvalidInputTests.cs b/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashEncoderInvalidInputTests.cs
new file mode 100644
index 0000000..b4ecbaf
--- /dev/null
+++ b/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashEncoderInvalidInputTests.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+using Xunit;
+
+namespace BlurHashSharp.SkiaSharp.Tests
+{
+ public class BlurHashEncoderInvalidInputTests
+ {
+ // A 64x96 progressive JPEG cut off half way through its first scan: Skia reads the header
+ // and creates a codec, but decoding the pixels fails with InvalidInput.
+ private static readonly string TruncatedImagePath = Path.Join(AppContext.BaseDirectory, "TestData", "truncated-progressive.jpg");
+
+ private static byte[] UnrecognizedData()
+ {
+ var data = new byte[2048];
+ for (int i = 0; i < data.Length; i++)
+ {
+ data[i] = (byte)i;
+ }
+
+ return data;
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Encode_UnrecognizedStream_ThrowsInvalidDataException(bool resize)
+ {
+ using var stream = new MemoryStream(UnrecognizedData());
+
+ Assert.Throws(() => resize
+ ? BlurHashEncoder.Encode(4, 3, stream, 128, 128)
+ : BlurHashEncoder.Encode(4, 3, stream));
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Encode_UnrecognizedFile_ThrowsInvalidDataException(bool resize)
+ {
+ var path = Path.Join(Path.GetTempPath(), Path.GetRandomFileName() + ".jpg");
+ File.WriteAllBytes(path, UnrecognizedData());
+ try
+ {
+ Assert.Throws(() => resize
+ ? BlurHashEncoder.Encode(4, 3, path, 128, 128)
+ : BlurHashEncoder.Encode(4, 3, path));
+ }
+ finally
+ {
+ File.Delete(path);
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Encode_TruncatedStream_ThrowsInvalidDataException(bool resize)
+ {
+ using var stream = File.OpenRead(TruncatedImagePath);
+
+ Assert.Throws(() => resize
+ ? BlurHashEncoder.Encode(4, 3, stream, 32, 32)
+ : BlurHashEncoder.Encode(4, 3, stream));
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Encode_TruncatedFile_ThrowsInvalidDataException(bool resize)
+ {
+ Assert.Throws(() => resize
+ ? BlurHashEncoder.Encode(4, 3, TruncatedImagePath, 32, 32)
+ : BlurHashEncoder.Encode(4, 3, TruncatedImagePath));
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Encode_MissingFile_ThrowsFileNotFoundException(bool resize)
+ {
+ var path = Path.Join(Path.GetTempPath(), Path.GetRandomFileName() + ".png");
+
+ Assert.Throws(() => resize
+ ? BlurHashEncoder.Encode(4, 3, path, 128, 128)
+ : BlurHashEncoder.Encode(4, 3, path));
+ }
+ }
+}
diff --git a/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashSharp.SkiaSharp.Tests.csproj b/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashSharp.SkiaSharp.Tests.csproj
index 169b878..3d18c02 100644
--- a/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashSharp.SkiaSharp.Tests.csproj
+++ b/tests/BlurHashSharp.SkiaSharp.Tests/BlurHashSharp.SkiaSharp.Tests.csproj
@@ -18,6 +18,10 @@
+
+
+
+
diff --git a/tests/BlurHashSharp.SkiaSharp.Tests/TestData/truncated-progressive.jpg b/tests/BlurHashSharp.SkiaSharp.Tests/TestData/truncated-progressive.jpg
new file mode 100644
index 0000000..0402dd3
Binary files /dev/null and b/tests/BlurHashSharp.SkiaSharp.Tests/TestData/truncated-progressive.jpg differ