diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs
index 71458333f0..6457b6f500 100644
--- a/src/ImageSharp/Formats/ImageExtensions.Save.cs
+++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs
@@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Qoi;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Tiff;
@@ -836,4 +837,105 @@ public static partial class ImageExtensions
encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(TiffFormat.Instance),
cancellationToken);
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The file path to save the image to.
+ /// Thrown if the path is null.
+ public static void SaveAsQoi(this Image source, string path) => SaveAsQoi(source, path, default);
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The file path to save the image to.
+ /// Thrown if the path is null.
+ /// A representing the asynchronous operation.
+ public static Task SaveAsQoiAsync(this Image source, string path) => SaveAsQoiAsync(source, path, default);
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The file path to save the image to.
+ /// The token to monitor for cancellation requests.
+ /// Thrown if the path is null.
+ /// A representing the asynchronous operation.
+ public static Task SaveAsQoiAsync(this Image source, string path, CancellationToken cancellationToken)
+ => SaveAsQoiAsync(source, path, default, cancellationToken);
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The file path to save the image to.
+ /// The encoder to save the image with.
+ /// Thrown if the path is null.
+ public static void SaveAsQoi(this Image source, string path, QoiEncoder encoder) =>
+ source.Save(
+ path,
+ encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(QoiFormat.Instance));
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The file path to save the image to.
+ /// The encoder to save the image with.
+ /// The token to monitor for cancellation requests.
+ /// Thrown if the path is null.
+ /// A representing the asynchronous operation.
+ public static Task SaveAsQoiAsync(this Image source, string path, QoiEncoder encoder, CancellationToken cancellationToken = default)
+ => source.SaveAsync(
+ path,
+ encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(QoiFormat.Instance),
+ cancellationToken);
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// Thrown if the stream is null.
+ public static void SaveAsQoi(this Image source, Stream stream)
+ => SaveAsQoi(source, stream, default);
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// The token to monitor for cancellation requests.
+ /// Thrown if the stream is null.
+ /// A representing the asynchronous operation.
+ public static Task SaveAsQoiAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
+ => SaveAsQoiAsync(source, stream, default, cancellationToken);
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// The encoder to save the image with.
+ /// Thrown if the stream is null.
+ public static void SaveAsQoi(this Image source, Stream stream, QoiEncoder encoder)
+ => source.Save(
+ stream,
+ encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(QoiFormat.Instance));
+
+ ///
+ /// Saves the image to the given stream with the Qoi format.
+ ///
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// The encoder to save the image with.
+ /// The token to monitor for cancellation requests.
+ /// Thrown if the stream is null.
+ /// A representing the asynchronous operation.
+ public static Task SaveAsQoiAsync(this Image source, Stream stream, QoiEncoder encoder, CancellationToken cancellationToken = default)
+ => source.SaveAsync(
+ stream,
+ encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(QoiFormat.Instance),
+ cancellationToken);
}
diff --git a/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs
new file mode 100644
index 0000000000..31ec27da0c
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs
@@ -0,0 +1,135 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Qoi;
+using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Qoi;
+
+public class ImageExtensionsTest
+{
+ [Fact]
+ public void SaveAsQoi_Path()
+ {
+ string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
+ string file = Path.Combine(dir, "SaveAsQoi_Path.qoi");
+
+ using (Image image = new(10, 10))
+ {
+ image.SaveAsQoi(file);
+ }
+
+ IImageFormat format = Image.DetectFormat(file);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public async Task SaveAsQoiAsync_Path()
+ {
+ string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
+ string file = Path.Combine(dir, "SaveAsQoiAsync_Path.qoi");
+
+ using (Image image = new(10, 10))
+ {
+ await image.SaveAsQoiAsync(file);
+ }
+
+ IImageFormat format = Image.DetectFormat(file);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public void SaveAsQoi_Path_Encoder()
+ {
+ string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
+ string file = Path.Combine(dir, "SaveAsQoi_Path_Encoder.qoi");
+
+ using (Image image = new(10, 10))
+ {
+ image.SaveAsQoi(file, new QoiEncoder());
+ }
+
+ IImageFormat format = Image.DetectFormat(file);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public async Task SaveAsQoiAsync_Path_Encoder()
+ {
+ string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
+ string file = Path.Combine(dir, "SaveAsQoiAsync_Path_Encoder.qoi");
+
+ using (Image image = new(10, 10))
+ {
+ await image.SaveAsQoiAsync(file, new QoiEncoder());
+ }
+
+ IImageFormat format = Image.DetectFormat(file);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public void SaveAsQoi_Stream()
+ {
+ using MemoryStream memoryStream = new();
+
+ using (Image image = new(10, 10))
+ {
+ image.SaveAsQoi(memoryStream);
+ }
+
+ memoryStream.Position = 0;
+
+ IImageFormat format = Image.DetectFormat(memoryStream);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public async Task SaveAsQoiAsync_StreamAsync()
+ {
+ using MemoryStream memoryStream = new();
+
+ using (Image image = new(10, 10))
+ {
+ await image.SaveAsQoiAsync(memoryStream);
+ }
+
+ memoryStream.Position = 0;
+
+ IImageFormat format = Image.DetectFormat(memoryStream);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public void SaveAsQoi_Stream_Encoder()
+ {
+ using MemoryStream memoryStream = new();
+
+ using (Image image = new(10, 10))
+ {
+ image.SaveAsQoi(memoryStream, new QoiEncoder());
+ }
+
+ memoryStream.Position = 0;
+
+ IImageFormat format = Image.DetectFormat(memoryStream);
+ Assert.True(format is QoiFormat);
+ }
+
+ [Fact]
+ public async Task SaveAsQoiAsync_Stream_Encoder()
+ {
+ using MemoryStream memoryStream = new();
+
+ using (Image image = new(10, 10))
+ {
+ await image.SaveAsQoiAsync(memoryStream, new QoiEncoder());
+ }
+
+ memoryStream.Position = 0;
+
+ IImageFormat format = Image.DetectFormat(memoryStream);
+ Assert.True(format is QoiFormat);
+ }
+}