Browse Source

Merge branch 'main' into js/encoder-normalization

pull/2269/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
ebdf5c80cc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      src/ImageSharp/IO/BufferedReadStream.cs
  2. 16
      src/ImageSharp/Image.FromStream.cs
  3. 42
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  4. 3
      tests/ImageSharp.Tests/TestImages.cs
  5. 3
      tests/Images/Input/Png/issues/Issue_2259.png

28
src/ImageSharp/IO/BufferedReadStream.cs

@ -60,7 +60,7 @@ internal sealed class BufferedReadStream : Stream
} }
// This triggers a full read on first attempt. // This triggers a full read on first attempt.
this.readBufferIndex = this.BufferSize; this.readBufferIndex = int.MinValue;
} }
/// <summary> /// <summary>
@ -96,8 +96,9 @@ internal sealed class BufferedReadStream : Stream
else else
{ {
// Base stream seek will throw for us if invalid. // Base stream seek will throw for us if invalid.
this.BaseStream.Seek(value, SeekOrigin.Begin);
this.readerPosition = value; this.readerPosition = value;
this.FillReadBuffer(); this.readBufferIndex = int.MinValue;
} }
} }
} }
@ -140,7 +141,7 @@ internal sealed class BufferedReadStream : Stream
// Our buffer has been read. // Our buffer has been read.
// We need to refill and start again. // We need to refill and start again.
if (this.readBufferIndex > this.maxBufferIndex) if ((uint)this.readBufferIndex > (uint)this.maxBufferIndex)
{ {
this.FillReadBuffer(); this.FillReadBuffer();
} }
@ -156,22 +157,7 @@ internal sealed class BufferedReadStream : Stream
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ => this.Read(buffer.AsSpan(offset, count));
// Too big for our buffer. Read directly from the stream.
if (count > this.BufferSize)
{
return this.ReadToBufferDirectSlow(buffer, offset, count);
}
// Too big for remaining buffer but less than entire buffer length
// Copy to buffer then read from there.
if (count + this.readBufferIndex > this.BufferSize)
{
return this.ReadToBufferViaCopySlow(buffer, offset, count);
}
return this.ReadToBufferViaCopyFast(buffer, offset, count);
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -186,7 +172,7 @@ internal sealed class BufferedReadStream : Stream
// Too big for remaining buffer but less than entire buffer length // Too big for remaining buffer but less than entire buffer length
// Copy to buffer then read from there. // Copy to buffer then read from there.
if (count + this.readBufferIndex > this.BufferSize) if ((uint)this.readBufferIndex > (uint)(this.BufferSize - count))
{ {
return this.ReadToBufferViaCopySlow(buffer); return this.ReadToBufferViaCopySlow(buffer);
} }
@ -206,7 +192,7 @@ internal sealed class BufferedReadStream : Stream
} }
// Reset to trigger full read on next attempt. // Reset to trigger full read on next attempt.
this.readBufferIndex = this.BufferSize; this.readBufferIndex = int.MinValue;
} }
/// <inheritdoc/> /// <inheritdoc/>

16
src/ImageSharp/Image.FromStream.cs

@ -517,6 +517,7 @@ public abstract partial class Image
/// <param name="stream">The input stream.</param> /// <param name="stream">The input stream.</param>
/// <param name="action">The action to perform.</param> /// <param name="action">The action to perform.</param>
/// <returns>The <typeparamref name="T"/>.</returns> /// <returns>The <typeparamref name="T"/>.</returns>
/// <exception cref="NotSupportedException">Cannot read from the stream.</exception>
internal static T WithSeekableStream<T>( internal static T WithSeekableStream<T>(
DecoderOptions options, DecoderOptions options,
Stream stream, Stream stream,
@ -587,7 +588,20 @@ public abstract partial class Image
await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0; memoryStream.Position = 0;
return action(memoryStream, cancellationToken); T Action(Stream ms, CancellationToken ct)
{
// Reset the position of the seekable stream if we did not read to the end
// to allow additional reads.
T result = action(ms, ct);
if (stream.CanSeek && ms.Position != ms.Length)
{
stream.Position = ms.Position;
}
return result;
}
return Action(memoryStream, cancellationToken);
} }
[DoesNotReturn] [DoesNotReturn]

42
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -46,6 +46,36 @@ public class GeneralFormatTests
image.DebugSave(provider); image.DebugSave(provider);
} }
[Fact]
public void ReadOriginIsRespectedOnLoad()
{
using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259));
using Image<Rgb24> i = Image.Load<Rgb24>(stream);
long position1 = stream.Position;
Assert.NotEqual(0, position1);
using Image<Rgb24> j = Image.Load<Rgb24>(stream);
long position2 = stream.Position;
Assert.True(position2 > position1);
Assert.NotEqual(i[5, 5], j[5, 5]);
}
[Fact]
public async Task ReadOriginIsRespectedOnLoadAsync()
{
using FileStream stream = File.OpenRead(TestFile.GetInputFileFullPath(TestImages.Png.Issue2259));
using Image<Rgb24> i = await Image.LoadAsync<Rgb24>(stream);
long position1 = stream.Position;
Assert.NotEqual(0, position1);
using Image<Rgb24> j = await Image.LoadAsync<Rgb24>(stream);
long position2 = stream.Position;
Assert.True(position2 > position1);
Assert.NotEqual(i[5, 5], j[5, 5]);
}
[Fact] [Fact]
public void ImageCanEncodeToString() public void ImageCanEncodeToString()
{ {
@ -155,15 +185,15 @@ public class GeneralFormatTests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
byte[] serialized; byte[] serialized;
using (var image = Image.Load(file.Bytes, out IImageFormat mimeType)) using (Image image = Image.Load(file.Bytes, out IImageFormat mimeType))
using (var memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new())
{ {
image.Save(memoryStream, mimeType); image.Save(memoryStream, mimeType);
memoryStream.Flush(); memoryStream.Flush();
serialized = memoryStream.ToArray(); serialized = memoryStream.ToArray();
} }
using var image2 = Image.Load<Rgba32>(serialized); using Image<Rgba32> image2 = Image.Load<Rgba32>(serialized);
image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}"); image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}");
} }
} }
@ -198,8 +228,8 @@ public class GeneralFormatTests
public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension) public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension)
{ {
using var image = Image.LoadPixelData<Rgba32>(new Rgba32[width * height], width, height); using Image<Rgba32> image = Image.LoadPixelData<Rgba32>(new Rgba32[width * height], width, height);
using var memoryStream = new MemoryStream(); using MemoryStream memoryStream = new();
IImageFormat format = GetFormat(extension); IImageFormat format = GetFormat(extension);
image.Save(memoryStream, format); image.Save(memoryStream, format);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -220,7 +250,7 @@ public class GeneralFormatTests
{ {
byte[] invalid = new byte[10]; byte[] invalid = new byte[10];
using var memoryStream = new MemoryStream(invalid); using MemoryStream memoryStream = new(invalid);
IImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format); IImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format);
Assert.Null(imageInfo); Assert.Null(imageInfo);

3
tests/ImageSharp.Tests/TestImages.cs

@ -130,6 +130,9 @@ public static class TestImages
// Issue 2209: https://github.com/SixLabors/ImageSharp/issues/2209 // Issue 2209: https://github.com/SixLabors/ImageSharp/issues/2209
public const string Issue2209IndexedWithTransparency = "Png/issues/Issue_2209.png"; public const string Issue2209IndexedWithTransparency = "Png/issues/Issue_2209.png";
// Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2259
public const string Issue2259 = "Png/issues/Issue_2259.png";
public static class Bad public static class Bad
{ {
public const string MissingDataChunk = "Png/xdtn0g01.png"; public const string MissingDataChunk = "Png/xdtn0g01.png";

3
tests/Images/Input/Png/issues/Issue_2259.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:45b635fe3104e96b7c6d8fa9d28f7fb86fb184c7a407c44a2f90f3f88c140ef0
size 6037
Loading…
Cancel
Save