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/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)); + } +}