Browse Source

Merge pull request #135 from JimBobSquarePants/IFileSystem

Encoder options being passed during path based saves
pull/154/head
Scott Williams 9 years ago
committed by GitHub
parent
commit
66e39f86c4
  1. 27
      src/ImageSharp/Configuration.cs
  2. 31
      src/ImageSharp/IO/IFileSystem.cs
  3. 32
      src/ImageSharp/IO/LocalFileSystem.cs
  4. 30
      src/ImageSharp/Image/Image{TColor}.cs
  5. 12
      tests/ImageSharp.Tests/ConfigurationTests.cs
  6. 7
      tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs
  7. 53
      tests/ImageSharp.Tests/IO/LocalFileSystem.cs
  8. 186
      tests/ImageSharp.Tests/Image/ImageSaveTests.cs

27
src/ImageSharp/Configuration.cs

@ -12,6 +12,7 @@ namespace ImageSharp
using System.Threading.Tasks;
using Formats;
using ImageSharp.IO;
/// <summary>
/// Provides initialization code which allows extending the library.
@ -33,6 +34,25 @@ namespace ImageSharp
/// </summary>
private readonly List<IImageFormat> imageFormatsList = new List<IImageFormat>();
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
public Configuration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="providers">The inital set of image formats.</param>
public Configuration(params IImageFormat[] providers)
{
foreach (IImageFormat p in providers)
{
this.AddImageFormat(p);
}
}
/// <summary>
/// Gets the default <see cref="Configuration"/> instance.
/// </summary>
@ -53,6 +73,13 @@ namespace ImageSharp
/// </summary>
internal int MaxHeaderSize { get; private set; }
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the fielsystem helper for accessing the local file system.
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
#endif
/// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats.
/// </summary>

31
src/ImageSharp/IO/IFileSystem.cs

@ -0,0 +1,31 @@
// <copyright file="IFileSystem.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System.IO;
#if !NETSTANDARD1_1
/// <summary>
/// A simple interface representing the filesystem.
/// </summary>
public interface IFileSystem
{
/// <summary>
/// Returns a readable stream as defined by the path.
/// </summary>
/// <param name="path">Path to the file to open.</param>
/// <returns>A stream representing the file to open.</returns>
Stream OpenRead(string path);
/// <summary>
/// Creates or opens a file and returns it as a writeable stream as defined by the path.
/// </summary>
/// <param name="path">Path to the file to open.</param>
/// <returns>A stream representing the file to open.</returns>
Stream Create(string path);
}
#endif
}

32
src/ImageSharp/IO/LocalFileSystem.cs

@ -0,0 +1,32 @@
// <copyright file="LocalFileSystem.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
#if !NETSTANDARD1_1
/// <summary>
/// A wrapper around the local File apis.
/// </summary>
public class LocalFileSystem : IFileSystem
{
/// <inheritdoc/>
public Stream OpenRead(string path)
{
return File.OpenRead(path);
}
/// <inheritdoc/>
public Stream Create(string path)
{
return File.Create(path);
}
}
#endif
}

30
src/ImageSharp/Image/Image{TColor}.cs

@ -168,7 +168,8 @@ namespace ImageSharp
: base(configuration)
{
Guard.NotNull(filePath, nameof(filePath));
using (FileStream fs = File.OpenRead(filePath))
using (Stream fs = this.Configuration.FileSystem.OpenRead(filePath))
{
this.Load(fs, options);
}
@ -348,9 +349,7 @@ namespace ImageSharp
/// <returns>The <see cref="Image{TColor}"/></returns>
public Image<TColor> Save(Stream stream, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
this.CurrentImageFormat.Encoder.Encode(this, stream, options);
return this;
return this.Save(stream, this.CurrentImageFormat?.Encoder, options);
}
/// <summary>
@ -373,10 +372,9 @@ namespace ImageSharp
/// <returns>The <see cref="Image{TColor}"/></returns>
public Image<TColor> Save(Stream stream, IImageFormat format, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(format, nameof(format));
format.Encoder.Encode(this, stream, options);
return this;
return this.Save(stream, format.Encoder, options);
}
/// <summary>
@ -407,13 +405,8 @@ namespace ImageSharp
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
encoder.Encode(this, stream, options);
// Reset to the start of the stream.
if (stream.CanSeek)
{
stream.Position = 0;
}
encoder.Encode(this, stream, options);
return this;
}
@ -446,7 +439,7 @@ namespace ImageSharp
throw new InvalidOperationException($"No image formats have been registered for the file extension '{ext}'.");
}
return this.Save(filePath, format);
return this.Save(filePath, format, options);
}
/// <summary>
@ -472,10 +465,7 @@ namespace ImageSharp
public Image<TColor> Save(string filePath, IImageFormat format, IEncoderOptions options)
{
Guard.NotNull(format, nameof(format));
using (FileStream fs = File.Create(filePath))
{
return this.Save(fs, format);
}
return this.Save(filePath, format.Encoder, options);
}
/// <summary>
@ -501,9 +491,9 @@ namespace ImageSharp
public Image<TColor> Save(string filePath, IImageEncoder encoder, IEncoderOptions options)
{
Guard.NotNull(encoder, nameof(encoder));
using (FileStream fs = File.Create(filePath))
using (Stream fs = this.Configuration.FileSystem.Create(filePath))
{
return this.Save(fs, encoder);
return this.Save(fs, encoder, options);
}
}
#endif

12
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -10,7 +10,7 @@ namespace ImageSharp.Tests
using System.Linq;
using ImageSharp.Formats;
using ImageSharp.IO;
using Xunit;
/// <summary>
@ -18,6 +18,16 @@ namespace ImageSharp.Tests
/// </summary>
public class ConfigurationTests
{
[Fact]
public void DefaultsToLocalFileSystem()
{
Configuration configuration = Configuration.CreateDefaultInstance();
ImageSharp.IO.IFileSystem fs = configuration.FileSystem;
Assert.IsType<LocalFileSystem>(fs);
}
[Fact]
public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded()
{

7
tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs

@ -6,6 +6,7 @@ namespace ImageSharp.Tests.Drawing.Paths
using ImageSharp;
using Processing;
using System.Collections.Generic;
using ImageSharp.Formats;
/// <summary>
/// Watches but does not actually run the processors against the image.
@ -22,15 +23,11 @@ namespace ImageSharp.Tests.Drawing.Paths
public override void ApplyProcessor(IImageProcessor<Color> processor, Rectangle rectangle)
{
ProcessorApplications.Add(new ProcessorDetails
this.ProcessorApplications.Add(new ProcessorDetails
{
processor = processor,
rectangle = rectangle
});
// doesn't really apply the processor to the fake images as this is supposed
// to be just used to test which processor was finally applied and to interogate
// its settings
}
public struct ProcessorDetails

53
tests/ImageSharp.Tests/IO/LocalFileSystem.cs

@ -0,0 +1,53 @@
// <copyright file="LittleEndianBitConverterTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests.IO
{
using System;
using System.IO;
using ImageSharp.IO;
using Xunit;
public class LocalFileSystemTests
{
[Fact]
public void OpenRead()
{
string path = Path.GetTempFileName();
string testData = Guid.NewGuid().ToString();
File.WriteAllText(path, testData);
LocalFileSystem fs = new LocalFileSystem();
using (StreamReader r = new StreamReader(fs.OpenRead(path)))
{
string data = r.ReadToEnd();
Assert.Equal(testData, data);
}
File.Delete(path);
}
[Fact]
public void Create()
{
string path = Path.GetTempFileName();
string testData = Guid.NewGuid().ToString();
LocalFileSystem fs = new LocalFileSystem();
using (StreamWriter r = new StreamWriter(fs.Create(path)))
{
r.Write(testData);
}
string data = File.ReadAllText(path);
Assert.Equal(testData, data);
File.Delete(path);
}
}
}

186
tests/ImageSharp.Tests/Image/ImageSaveTests.cs

@ -0,0 +1,186 @@
// <copyright file="PixelAccessorTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.IO;
using System.Linq;
using ImageSharp.Formats;
using ImageSharp.IO;
using Moq;
using Xunit;
/// <summary>
/// Tests the <see cref="Image"/> class.
/// </summary>
public class ImageSaveTests : IDisposable
{
private readonly Image Image;
private readonly Mock<IFileSystem> fileSystem;
private readonly Mock<IImageFormat> format;
private readonly Mock<IImageFormat> formatNotRegistered;
private readonly Mock<IImageEncoder> encoder;
private readonly Mock<IImageEncoder> encoderNotInFormat;
private readonly IEncoderOptions encoderOptions;
public ImageSaveTests()
{
this.encoder = new Mock<IImageEncoder>();
this.format = new Mock<IImageFormat>();
this.format.Setup(x => x.Encoder).Returns(this.encoder.Object);
this.format.Setup(x => x.Decoder).Returns(new Mock<IImageDecoder>().Object);
this.format.Setup(x => x.MimeType).Returns("img/test");
this.format.Setup(x => x.Extension).Returns("png");
this.format.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" });
this.encoderNotInFormat = new Mock<IImageEncoder>();
this.formatNotRegistered = new Mock<IImageFormat>();
this.formatNotRegistered.Setup(x => x.Encoder).Returns(this.encoderNotInFormat.Object);
this.formatNotRegistered.Setup(x => x.Decoder).Returns(new Mock<IImageDecoder>().Object);
this.formatNotRegistered.Setup(x => x.MimeType).Returns("img/test");
this.formatNotRegistered.Setup(x => x.Extension).Returns("png");
this.formatNotRegistered.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" });
this.fileSystem = new Mock<IFileSystem>();
this.encoderOptions = new Mock<IEncoderOptions>().Object;
this.Image = new Image(1, 1, new Configuration(this.format.Object) {
FileSystem = this.fileSystem.Object
});
}
[Fact]
public void SavePath()
{
Stream stream = new MemoryStream();
this.fileSystem.Setup(x => x.Create("path.png")).Returns(stream);
this.Image.Save("path.png");
this.encoder.Verify(x => x.Encode<Color>(this.Image, stream, null));
}
[Fact]
public void SavePathWithOptions()
{
Stream stream = new MemoryStream();
this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream);
this.Image.Save("path.jpg", this.encoderOptions);
this.encoder.Verify(x => x.Encode<Color>(this.Image, stream, this.encoderOptions));
}
[Fact]
public void SavePathWithEncoder()
{
Stream stream = new MemoryStream();
this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream);
this.Image.Save("path.jpg", this.encoderNotInFormat.Object);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, null));
}
[Fact]
public void SavePathWithEncoderAndOptions()
{
Stream stream = new MemoryStream();
this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream);
this.Image.Save("path.jpg", this.encoderNotInFormat.Object, this.encoderOptions);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, this.encoderOptions));
}
[Fact]
public void SavePathWithFormat()
{
Stream stream = new MemoryStream();
this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream);
this.Image.Save("path.jpg", this.encoderNotInFormat.Object);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, null));
}
[Fact]
public void SavePathWithFormatAndOptions()
{
Stream stream = new MemoryStream();
this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream);
this.Image.Save("path.jpg", this.encoderNotInFormat.Object, this.encoderOptions);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, this.encoderOptions));
}
[Fact]
public void SaveStream()
{
Stream stream = new MemoryStream();
this.Image.Save(stream);
this.encoder.Verify(x => x.Encode<Color>(this.Image, stream, null));
}
[Fact]
public void SaveStreamWithOptions()
{
Stream stream = new MemoryStream();
this.Image.Save(stream, this.encoderOptions);
this.encoder.Verify(x => x.Encode<Color>(this.Image, stream, this.encoderOptions));
}
[Fact]
public void SaveStreamWithEncoder()
{
Stream stream = new MemoryStream();
this.Image.Save(stream, this.encoderNotInFormat.Object);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, null));
}
[Fact]
public void SaveStreamWithEncoderAndOptions()
{
Stream stream = new MemoryStream();
this.Image.Save(stream, this.encoderNotInFormat.Object, this.encoderOptions);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, this.encoderOptions));
}
[Fact]
public void SaveStreamWithFormat()
{
Stream stream = new MemoryStream();
this.Image.Save(stream, this.formatNotRegistered.Object);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, null));
}
[Fact]
public void SaveStreamWithFormatAndOptions()
{
Stream stream = new MemoryStream();
this.Image.Save(stream, this.formatNotRegistered.Object, this.encoderOptions);
this.encoderNotInFormat.Verify(x => x.Encode<Color>(this.Image, stream, this.encoderOptions));
}
public void Dispose()
{
this.Image.Dispose();
}
}
}
Loading…
Cancel
Save