From b06a96b58bde3e8580f4d03eaee407db47619aa5 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 8 Feb 2023 20:13:27 +0200 Subject: [PATCH 1/3] Add more tests for Graphics --- .../Tests/ImageLoadingServiceTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs b/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs index c1d5682a71cb..aeae2c4ea3c2 100644 --- a/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs +++ b/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs @@ -52,4 +52,44 @@ public async Task CanLoadImage() Assert.Equal(747, image.Width); Assert.Equal(840, image.Height); } + + [Theory] + [InlineData(ImageFormat.Png, 1.0f)] + [InlineData(ImageFormat.Png, 0.8f)] + [InlineData(ImageFormat.Png, 0.4f)] + [InlineData(ImageFormat.Jpeg, 1.0f)] + [InlineData(ImageFormat.Jpeg, 0.8f)] + [InlineData(ImageFormat.Jpeg, 0.4f)] + public async Task CanGetBytesFromImage(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + var bytes = image.AsBytes(format, quality); + + Assert.NotNull(bytes); + Assert.NotEmpty(bytes); + } + + [Theory] + [InlineData(ImageFormat.Png, 1.0f)] + [InlineData(ImageFormat.Png, 0.8f)] + [InlineData(ImageFormat.Png, 0.4f)] + [InlineData(ImageFormat.Jpeg, 1.0f)] + [InlineData(ImageFormat.Jpeg, 0.8f)] + [InlineData(ImageFormat.Jpeg, 0.4f)] + public async Task CanGetStreamFromImage(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + var stream = image.AsStream(format, quality); + + Assert.NotNull(stream); + Assert.True(stream.Length > 0); + } } From 415cf23bbef172f1257333f7cf7cd2bcf7fe84d1 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 8 Feb 2023 20:26:30 +0200 Subject: [PATCH 2/3] newStream --- .../tests/DeviceTests/Tests/ImageLoadingServiceTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs b/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs index aeae2c4ea3c2..74a38a71ef27 100644 --- a/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs +++ b/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs @@ -87,9 +87,9 @@ public async Task CanGetStreamFromImage(ImageFormat format, float quality) using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); using var image = service.FromStream(stream); - var stream = image.AsStream(format, quality); + var newStream = image.AsStream(format, quality); - Assert.NotNull(stream); - Assert.True(stream.Length > 0); + Assert.NotNull(newStream); + Assert.True(newStream.Length > 0); } } From 1d699b537262fc6848fa87b8acaa892fd7a75e4a Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 8 Feb 2023 23:50:33 +0200 Subject: [PATCH 3/3] Add better exceptions --- src/Graphics/src/Graphics.Skia/SkiaImage.cs | 3 + src/Graphics/src/Graphics.Win2D/W2DImage.cs | 6 + src/Graphics/src/Graphics/PlatformImage.cs | 6 + .../Platforms/Android/PlatformImage.cs | 6 + .../Graphics/Platforms/Mac/PlatformImage.cs | 3 + .../Graphics/Platforms/iOS/PlatformImage.cs | 3 + .../Tests/ImageLoadingServiceTests.cs | 40 ------- .../tests/DeviceTests/Tests/ImageTests.cs | 107 ++++++++++++++++++ 8 files changed, 134 insertions(+), 40 deletions(-) create mode 100644 src/Graphics/tests/DeviceTests/Tests/ImageTests.cs diff --git a/src/Graphics/src/Graphics.Skia/SkiaImage.cs b/src/Graphics/src/Graphics.Skia/SkiaImage.cs index f85e5d05c4c9..84b96a4154eb 100644 --- a/src/Graphics/src/Graphics.Skia/SkiaImage.cs +++ b/src/Graphics/src/Graphics.Skia/SkiaImage.cs @@ -119,6 +119,9 @@ public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, private Stream GetSkStream(ImageFormat format, float quality) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + // Skia quality range from 0-100, this is supported by jpeg and webp. Higher values correspond to improved visual quality, but less compression. const int MaxSKQuality = 100; var skQuality = (int)(MaxSKQuality * quality); diff --git a/src/Graphics/src/Graphics.Win2D/W2DImage.cs b/src/Graphics/src/Graphics.Win2D/W2DImage.cs index 16b1e542ff4d..886cfde46e4c 100644 --- a/src/Graphics/src/Graphics.Win2D/W2DImage.cs +++ b/src/Graphics/src/Graphics.Win2D/W2DImage.cs @@ -72,6 +72,9 @@ public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMo public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + switch (format) { case ImageFormat.Jpeg: @@ -85,6 +88,9 @@ public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float qual public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + switch (format) { case ImageFormat.Jpeg: diff --git a/src/Graphics/src/Graphics/PlatformImage.cs b/src/Graphics/src/Graphics/PlatformImage.cs index 738c5e64509f..3449223fb6fc 100644 --- a/src/Graphics/src/Graphics/PlatformImage.cs +++ b/src/Graphics/src/Graphics/PlatformImage.cs @@ -46,6 +46,9 @@ public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMo public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + if (format == _originalFormat) { stream.Write(_bytes, 0, _bytes.Length); @@ -58,6 +61,9 @@ public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float qual public Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + if (format == _originalFormat) { Save(stream, format, quality); diff --git a/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs b/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs index 3126566d5b67..2a36cdb28955 100644 --- a/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs +++ b/src/Graphics/src/Graphics/Platforms/Android/PlatformImage.cs @@ -91,6 +91,9 @@ public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMo public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + switch (format) { case ImageFormat.Jpeg: @@ -104,6 +107,9 @@ public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float qual public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + switch (format) { case ImageFormat.Jpeg: diff --git a/src/Graphics/src/Graphics/Platforms/Mac/PlatformImage.cs b/src/Graphics/src/Graphics/Platforms/Mac/PlatformImage.cs index 15ba4467544a..d48f98cf4157 100644 --- a/src/Graphics/src/Graphics/Platforms/Mac/PlatformImage.cs +++ b/src/Graphics/src/Graphics/Platforms/Mac/PlatformImage.cs @@ -37,6 +37,9 @@ public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, private NSData CreateRepresentation(ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + var previous = NSApplication.CheckForIllegalCrossThreadCalls; NSApplication.CheckForIllegalCrossThreadCalls = false; diff --git a/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs b/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs index 2929249363d3..c1ad1b4c64fa 100644 --- a/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs +++ b/src/Graphics/src/Graphics/Platforms/iOS/PlatformImage.cs @@ -104,6 +104,9 @@ public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, private NSData CreateData(ImageFormat format = ImageFormat.Png, float quality = 1) { + if (quality < 0 || quality > 1) + throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); + NSData data; switch (format) { diff --git a/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs b/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs index 74a38a71ef27..c1d5682a71cb 100644 --- a/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs +++ b/src/Graphics/tests/DeviceTests/Tests/ImageLoadingServiceTests.cs @@ -52,44 +52,4 @@ public async Task CanLoadImage() Assert.Equal(747, image.Width); Assert.Equal(840, image.Height); } - - [Theory] - [InlineData(ImageFormat.Png, 1.0f)] - [InlineData(ImageFormat.Png, 0.8f)] - [InlineData(ImageFormat.Png, 0.4f)] - [InlineData(ImageFormat.Jpeg, 1.0f)] - [InlineData(ImageFormat.Jpeg, 0.8f)] - [InlineData(ImageFormat.Jpeg, 0.4f)] - public async Task CanGetBytesFromImage(ImageFormat format, float quality) - { - var service = new PlatformImageLoadingService(); - - using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); - using var image = service.FromStream(stream); - - var bytes = image.AsBytes(format, quality); - - Assert.NotNull(bytes); - Assert.NotEmpty(bytes); - } - - [Theory] - [InlineData(ImageFormat.Png, 1.0f)] - [InlineData(ImageFormat.Png, 0.8f)] - [InlineData(ImageFormat.Png, 0.4f)] - [InlineData(ImageFormat.Jpeg, 1.0f)] - [InlineData(ImageFormat.Jpeg, 0.8f)] - [InlineData(ImageFormat.Jpeg, 0.4f)] - public async Task CanGetStreamFromImage(ImageFormat format, float quality) - { - var service = new PlatformImageLoadingService(); - - using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); - using var image = service.FromStream(stream); - - var newStream = image.AsStream(format, quality); - - Assert.NotNull(newStream); - Assert.True(newStream.Length > 0); - } } diff --git a/src/Graphics/tests/DeviceTests/Tests/ImageTests.cs b/src/Graphics/tests/DeviceTests/Tests/ImageTests.cs new file mode 100644 index 000000000000..9f5c318de21f --- /dev/null +++ b/src/Graphics/tests/DeviceTests/Tests/ImageTests.cs @@ -0,0 +1,107 @@ +using System.Threading.Tasks; +using Microsoft.Maui.Storage; +using Xunit; +using System; + +#if WINDOWS +using Microsoft.Maui.Graphics.Win2D; +using PlatformImageLoadingService = Microsoft.Maui.Graphics.Win2D.W2DImageLoadingService; +#else +using Microsoft.Maui.Graphics.Platform; +#endif + +namespace Microsoft.Maui.Graphics.DeviceTests; + +public class ImageTests +{ + [Theory] + [InlineData(ImageFormat.Png, 1.0f)] + [InlineData(ImageFormat.Png, 0.8f)] + [InlineData(ImageFormat.Png, 0.4f)] + [InlineData(ImageFormat.Jpeg, 1.0f)] + [InlineData(ImageFormat.Jpeg, 0.8f)] + [InlineData(ImageFormat.Jpeg, 0.4f)] + public async Task CanGetBytesFromImage(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + var bytes = image.AsBytes(format, quality); + + Assert.NotNull(bytes); + Assert.NotEmpty(bytes); + } + + [Theory] + [InlineData(ImageFormat.Png, 1.0f)] + [InlineData(ImageFormat.Png, 0.8f)] + [InlineData(ImageFormat.Png, 0.4f)] + [InlineData(ImageFormat.Jpeg, 1.0f)] + [InlineData(ImageFormat.Jpeg, 0.8f)] + [InlineData(ImageFormat.Jpeg, 0.4f)] + public async Task CanGetStreamFromImage(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + var newStream = image.AsStream(format, quality); + + Assert.NotNull(newStream); + Assert.True(newStream.Length > 0, "Assert.True(newStream.Length > 0)"); + } + + [Theory] + [InlineData(ImageFormat.Png, 2.0f)] + [InlineData(ImageFormat.Png, 80f)] + [InlineData(ImageFormat.Png, -0.8f)] + [InlineData(ImageFormat.Jpeg, 2.0f)] + [InlineData(ImageFormat.Jpeg, 80f)] + [InlineData(ImageFormat.Jpeg, -0.8f)] + public async Task AsBytesWithQualityOutOfRangeThrowsArgumentException(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + Assert.Throws(() => image.AsBytes(format, quality)); + } + + [Theory] + [InlineData(ImageFormat.Png, 2.0f)] + [InlineData(ImageFormat.Png, 80f)] + [InlineData(ImageFormat.Png, -0.8f)] + [InlineData(ImageFormat.Jpeg, 2.0f)] + [InlineData(ImageFormat.Jpeg, 80f)] + [InlineData(ImageFormat.Jpeg, -0.8f)] + public async Task AsBytesAsyncWithQualityOutOfRangeThrowsArgumentException(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + await Assert.ThrowsAsync(() => image.AsBytesAsync(format, quality)); + } + + [Theory] + [InlineData(ImageFormat.Png, 2.0f)] + [InlineData(ImageFormat.Png, 80f)] + [InlineData(ImageFormat.Png, -0.8f)] + [InlineData(ImageFormat.Jpeg, 2.0f)] + [InlineData(ImageFormat.Jpeg, 80f)] + [InlineData(ImageFormat.Jpeg, -0.8f)] + public async Task AsStreamWithQualityOutOfRangeThrowsArgumentException(ImageFormat format, float quality) + { + var service = new PlatformImageLoadingService(); + + using var stream = await FileSystem.OpenAppPackageFileAsync("dotnet_bot.png"); + using var image = service.FromStream(stream); + + Assert.Throws(() => image.AsStream(format, quality)); + } +}