Browse Source

pixel-agnostic decoder API

af/merge-core
Anton Firszov 7 years ago
parent
commit
ebfecf4fda
  1. 3
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  2. 2
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  3. 2
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  4. 2
      src/ImageSharp/Formats/IImageDecoder.cs
  5. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  6. 2
      src/ImageSharp/Formats/Png/PngDecoder.cs
  7. 12
      src/ImageSharp/Image.Decode.cs
  8. 30
      src/ImageSharp/Image.FromFile.cs
  9. 41
      src/ImageSharp/Image.FromStream.cs
  10. 36
      src/ImageSharp/Image.cs
  11. 9
      src/ImageSharp/ImageExtensions.cs
  12. 14
      src/ImageSharp/Image{TPixel}.cs
  13. 16
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
  14. 2
      tests/ImageSharp.Tests/TestFormat.cs
  15. 2
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
  16. 2
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
  17. 4
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

3
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -21,8 +21,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image.</param>
/// <returns>Returns the configuration.</returns>
public static Configuration GetConfiguration<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
public static Configuration GetConfiguration(this Image source)
=> GetConfiguration((IConfigurable)source);
/// <summary>

2
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -30,6 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{

2
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -44,5 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
var decoder = new GifDecoderCore(configuration, this);
return decoder.Identify(stream);
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}

2
src/ImageSharp/Formats/IImageDecoder.cs

@ -20,5 +20,7 @@ namespace SixLabors.ImageSharp.Formats
/// <returns>The decoded image</returns>
Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>;
Image Decode(Configuration configuration, Stream stream);
}
}

2
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -38,5 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return decoder.Identify(stream);
}
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}

2
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -59,5 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Png
var decoder = new PngDecoderCore(configuration, this);
return decoder.Identify(stream);
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}

12
src/ImageSharp/Image.Decode.cs

@ -102,6 +102,18 @@ namespace SixLabors.ImageSharp
Image<TPixel> img = decoder.Decode<TPixel>(config, stream);
return (img, format);
}
private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config)
{
IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format);
if (decoder is null)
{
return (null, null);
}
Image img = decoder.Decode(config, stream);
return (img, format);
}
/// <summary>
/// Reads the raw image information from the specified stream.

30
src/ImageSharp/Image.FromFile.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(string path) => Load<Rgba32>(path);
public static Image Load(string path) => Load(path);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(string path, out IImageFormat format) => Load<Rgba32>(path, out format);
public static Image Load(string path, out IImageFormat format) => Load(path, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
@ -68,19 +68,7 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, string path) => Load<Rgba32>(config, path);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, string path, out IImageFormat format) => Load<Rgba32>(config, path, out format);
public static Image Load(Configuration config, string path) => Load(config, path);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
@ -92,7 +80,7 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, string path, IImageDecoder decoder) => Load<Rgba32>(config, path, decoder);
public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given file.
@ -103,7 +91,7 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(string path, IImageDecoder decoder) => Load<Rgba32>(path, decoder);
public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.
@ -174,6 +162,14 @@ namespace SixLabors.ImageSharp
return Load<TPixel>(config, stream, out format);
}
}
public static Image Load(Configuration config, string path, out IImageFormat format)
{
using (Stream stream = config.FileSystem.OpenRead(path))
{
return Load(config, stream, out format);
}
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given file.

41
src/ImageSharp/Image.FromStream.cs

@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp
/// <param name="format">the mime type of the decoded image.</param>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>>
public static Image<Rgba32> Load(Stream stream, out IImageFormat format) => Load<Rgba32>(stream, out format);
public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>>
public static Image<Rgba32> Load(Stream stream) => Load<Rgba32>(stream);
public static Image Load(Stream stream) => Load(stream);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>>
public static Image<Rgba32> Load(Stream stream, IImageDecoder decoder) => Load<Rgba32>(stream, decoder);
public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
@ -88,18 +88,7 @@ namespace SixLabors.ImageSharp
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>>
public static Image<Rgba32> Load(Configuration config, Stream stream) => Load<Rgba32>(config, stream);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="format">the mime type of the decoded image.</param>
/// <exception cref="NotSupportedException">Thrown if the stream is not readable.</exception>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>>
public static Image<Rgba32> Load(Configuration config, Stream stream, out IImageFormat format)
=> Load<Rgba32>(config, stream, out format);
public static Image Load(Configuration config, Stream stream) => Load(config, stream);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given stream.
@ -193,6 +182,28 @@ namespace SixLabors.ImageSharp
throw new NotSupportedException(sb.ToString());
}
private static Image Load(Configuration config, Stream stream, out IImageFormat format)
{
config = config ?? Configuration.Default;
(Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config));
format = data.format;
if (data.img != null)
{
return data.img;
}
var sb = new StringBuilder();
sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageFormatsManager.ImageDecoders)
{
sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(sb.ToString());
}
private static T WithSeekableStream<T>(Configuration config, Stream stream, Func<Stream, T> action)
{

36
src/ImageSharp/Image.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata;
@ -45,5 +47,39 @@ namespace SixLabors.ImageSharp
public abstract void Dispose();
internal abstract void AcceptVisitor(IImageVisitor visitor);
/// <summary>
/// Saves the image to the given stream using the given image encoder.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream or encoder is null.</exception>
public void Save(Stream stream, IImageEncoder encoder)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
EncodeVisitor visitor = new EncodeVisitor(encoder, stream);
this.AcceptVisitor(visitor);
}
class EncodeVisitor : IImageVisitor
{
private readonly IImageEncoder encoder;
private readonly Stream stream;
public EncodeVisitor(IImageEncoder encoder, Stream stream)
{
this.encoder = encoder;
this.stream = stream;
}
public void Visit<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
this.encoder.Encode(image, this.stream);
}
}
}
}

9
src/ImageSharp/ImageExtensions.cs

@ -23,8 +23,7 @@ namespace SixLabors.ImageSharp
/// <param name="source">The source image.</param>
/// <param name="filePath">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, string filePath)
where TPixel : struct, IPixel<TPixel>
public static void Save(this Image source, string filePath)
{
Guard.NotNullOrWhiteSpace(filePath, nameof(filePath));
@ -67,8 +66,7 @@ namespace SixLabors.ImageSharp
/// <param name="filePath">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="ArgumentNullException">Thrown if the encoder is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, string filePath, IImageEncoder encoder)
where TPixel : struct, IPixel<TPixel>
public static void Save(this Image source, string filePath, IImageEncoder encoder)
{
Guard.NotNull(encoder, nameof(encoder));
using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath))
@ -85,8 +83,7 @@ namespace SixLabors.ImageSharp
/// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image in.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format)
where TPixel : struct, IPixel<TPixel>
public static void Save(this Image source, Stream stream, IImageFormat format)
{
Guard.NotNull(format, nameof(format));
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);

14
src/ImageSharp/Image{TPixel}.cs

@ -158,20 +158,6 @@ namespace SixLabors.ImageSharp
set => this.PixelSource.PixelBuffer[x, y] = value;
}
/// <summary>
/// Saves the image to the given stream using the given image encoder.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream or encoder is null.</exception>
public void Save(Stream stream, IImageEncoder encoder)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
encoder.Encode(this, stream);
}
/// <summary>
/// Clones the current image
/// </summary>

16
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -42,6 +42,22 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F);
[Fact]
public void Resize_PixelAgnostic()
{
var filePath = TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora);
using (Image image = Image.Load(filePath))
{
image.Mutate(x => x.Resize(image.Size() / 2));
string path = System.IO.Path.Combine(
TestEnvironment.CreateOutputDirectory(nameof(ResizeTests)),
nameof(this.Resize_PixelAgnostic) + ".png");
image.Save(path);
}
}
[Theory(
Skip = "Debug only, enable manually"
)]

2
tests/ImageSharp.Tests/TestFormat.cs

@ -199,6 +199,8 @@ namespace SixLabors.ImageSharp.Tests
}
public bool IsSupportedFileFormat(Span<byte> header) => testFormat.IsSupportedFileFormat(header);
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
public class TestEncoder : ImageSharp.Formats.IImageEncoder

2
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs

@ -57,5 +57,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return result;
}
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}

2
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs

@ -51,5 +51,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata());
}
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}

4
tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

@ -379,6 +379,8 @@ namespace SixLabors.ImageSharp.Tests
this.callerName = name;
invocationCounts[name] = 0;
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
private class TestDecoderWithParameters : IImageDecoder
@ -416,6 +418,8 @@ namespace SixLabors.ImageSharp.Tests
this.callerName = name;
invocationCounts[name] = 0;
}
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}
}
Loading…
Cancel
Save