Browse Source

Upstream V3 changes.

pull/2894/head
James Jackson-South 1 year ago
parent
commit
86b9917049
  1. 17
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 78
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 11
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  4. 18
      src/ImageSharp/Processing/Processors/Quantization/IColorIndexCache.cs
  5. 1
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  6. 1
      tests/ImageSharp.Tests/TestImages.cs
  7. 3
      tests/Images/Input/Png/issues/Issue_2882.png

17
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -138,10 +138,11 @@ internal static class AotCompilerTools
AotCompileResamplers<TPixel>();
AotCompileQuantizers<TPixel>();
AotCompilePixelSamplingStrategys<TPixel>();
AotCompilePixelMaps<TPixel>();
AotCompileDithers<TPixel>();
AotCompileMemoryManagers<TPixel>();
Unsafe.SizeOf<TPixel>();
_ = Unsafe.SizeOf<TPixel>();
// TODO: Do the discovery work to figure out what works and what doesn't.
}
@ -514,6 +515,20 @@ internal static class AotCompilerTools
default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default(ImageFrame<TPixel>));
}
/// <summary>
/// This method pre-seeds the all <see cref="IColorIndexCache{T}" /> in the AoT compiler.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve]
private static void AotCompilePixelMaps<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
default(EuclideanPixelMap<TPixel, HybridCache>).GetClosestColor(default, out _);
default(EuclideanPixelMap<TPixel, AccurateCache>).GetClosestColor(default, out _);
default(EuclideanPixelMap<TPixel, CoarseCache>).GetClosestColor(default, out _);
default(EuclideanPixelMap<TPixel, NullCache>).GetClosestColor(default, out _);
}
/// <summary>
/// This method pre-seeds the all <see cref="IDither" /> in the AoT compiler.
/// </summary>

78
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -1086,7 +1086,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
{
PixelBlender<TPixel> blender =
PixelOperations<TPixel>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver);
blender.Blend<TPixel>(this.configuration, destination, destination, rowSpan, 1f);
blender.Blend(this.configuration, destination, destination, rowSpan, 1f);
}
}
finally
@ -1208,7 +1208,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
{
PixelBlender<TPixel> blender =
PixelOperations<TPixel>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver);
blender.Blend<TPixel>(this.configuration, destination, destination, rowSpan, 1f);
blender.Blend(this.configuration, destination, destination, rowSpan, 1F);
}
}
finally
@ -1866,6 +1866,9 @@ internal sealed class PngDecoderCore : ImageDecoderCore
return false;
}
// Capture the current position so we can revert back to it if we fail to read a valid chunk.
long position = this.currentStream.Position;
if (!this.TryReadChunkLength(buffer, out int length))
{
// IEND
@ -1884,7 +1887,48 @@ internal sealed class PngDecoderCore : ImageDecoderCore
}
}
PngChunkType type = this.ReadChunkType(buffer);
PngChunkType type;
// Loop until we get a chunk type that is valid.
while (true)
{
type = this.ReadChunkType(buffer);
if (!IsValidChunkType(type))
{
// The chunk type is invalid.
// Revert back to the next byte past the previous position and try again.
this.currentStream.Position = ++position;
// If we are now at the end of the stream, we're done.
if (this.currentStream.Position >= this.currentStream.Length)
{
chunk = default;
return false;
}
// Read the next chunk’s length.
if (!this.TryReadChunkLength(buffer, out length))
{
chunk = default;
return false;
}
while (length < 0)
{
if (!this.TryReadChunkLength(buffer, out length))
{
chunk = default;
return false;
}
}
// Continue to try reading the next chunk.
continue;
}
// We have a valid chunk type.
break;
}
// If we're reading color metadata only we're only interested in the IHDR and tRNS chunks.
// We can skip most other chunk data in the stream for better performance.
@ -1901,7 +1945,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
// A chunk might report a length that exceeds the length of the stream.
// Take the minimum of the two values to ensure we don't read past the end of the stream.
long position = this.currentStream.Position;
position = this.currentStream.Position;
chunk = new PngChunk(
length: (int)Math.Min(length, this.currentStream.Length - position),
type: type,
@ -1919,6 +1963,32 @@ internal sealed class PngDecoderCore : ImageDecoderCore
return true;
}
/// <summary>
/// Determines whether the 4-byte chunk type is valid (all ASCII letters).
/// </summary>
/// <param name="type">The chunk type.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static bool IsValidChunkType(PngChunkType type)
{
uint value = (uint)type;
byte b0 = (byte)(value >> 24);
byte b1 = (byte)(value >> 16);
byte b2 = (byte)(value >> 8);
byte b3 = (byte)value;
return IsAsciiLetter(b0) && IsAsciiLetter(b1) && IsAsciiLetter(b2) && IsAsciiLetter(b3);
}
/// <summary>
/// Returns a value indicating whether the given byte is an ASCII letter.
/// </summary>
/// <param name="b">The byte to check.</param>
/// <returns>
/// <see langword="true"/> if the byte is an ASCII letter; otherwise, <see langword="false"/>.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static bool IsAsciiLetter(byte b)
=> (b >= (byte)'A' && b <= (byte)'Z') || (b >= (byte)'a' && b <= (byte)'z');
/// <summary>
/// Validates the png chunk.
/// </summary>

11
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -290,7 +290,16 @@ internal sealed class PngEncoderCore : IDisposable
frameMetadata = currentFrame.Metadata.GetPngMetadata();
bool blend = frameMetadata.BlendMode == FrameBlendMode.Over;
// Determine whether to blend the current frame over the existing canvas.
// Blending is applied only when the blend method is 'Over' (source-over blending)
// and when the frame's disposal method is not 'RestoreToPrevious', which indicates that
// the frame should not permanently alter the canvas.
bool blend = frameMetadata.BlendMode == FrameBlendMode.Over
&& frameMetadata.DisposalMode != FrameDisposalMode.RestoreToPrevious;
// Establish the background color for the current frame.
// If the disposal method is 'RestoreToBackground', use the predefined background color;
// otherwise, use transparent, as no explicit background restoration is needed.
Color background = frameMetadata.DisposalMode == FrameDisposalMode.RestoreToBackground
? this.backgroundColor.Value
: Color.Transparent;

18
src/ImageSharp/Processing/Processors/Quantization/IColorIndexCache.cs

@ -69,11 +69,11 @@ internal interface IColorIndexCache<T> : IColorIndexCache
internal unsafe struct HybridCache : IColorIndexCache<HybridCache>
{
private CoarseCache coarseCache;
private ExactCache exactCache;
private AccurateCache accurateCache;
public HybridCache(MemoryAllocator allocator)
{
this.exactCache = ExactCache.Create(allocator);
this.accurateCache = AccurateCache.Create(allocator);
this.coarseCache = CoarseCache.Create(allocator);
}
@ -84,7 +84,7 @@ internal unsafe struct HybridCache : IColorIndexCache<HybridCache>
[MethodImpl(InliningOptions.ShortMethod)]
public bool TryAdd(Rgba32 color, short index)
{
if (this.exactCache.TryAdd(color, index))
if (this.accurateCache.TryAdd(color, index))
{
return true;
}
@ -96,7 +96,7 @@ internal unsafe struct HybridCache : IColorIndexCache<HybridCache>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly bool TryGetValue(Rgba32 color, out short value)
{
if (this.exactCache.TryGetValue(color, out value))
if (this.accurateCache.TryGetValue(color, out value))
{
return true;
}
@ -107,14 +107,14 @@ internal unsafe struct HybridCache : IColorIndexCache<HybridCache>
/// <inheritdoc/>
public readonly void Clear()
{
this.exactCache.Clear();
this.accurateCache.Clear();
this.coarseCache.Clear();
}
/// <inheritdoc/>
public void Dispose()
{
this.exactCache.Dispose();
this.accurateCache.Dispose();
this.coarseCache.Dispose();
}
}
@ -311,7 +311,7 @@ internal unsafe struct CoarseCache : IColorIndexCache<CoarseCache>
/// typically very short; in the worst-case, the number of iterations is bounded by 256.
/// This guarantees highly efficient and predictable performance for small, fixed-size color palettes.
/// </remarks>
internal unsafe struct ExactCache : IColorIndexCache<ExactCache>
internal unsafe struct AccurateCache : IColorIndexCache<AccurateCache>
{
// Buckets array: each bucket holds the index (0-based) into the entries array
// of the first entry in the chain, or -1 if empty.
@ -326,7 +326,7 @@ internal unsafe struct ExactCache : IColorIndexCache<ExactCache>
public const int Capacity = 512;
private ExactCache(MemoryAllocator allocator)
private AccurateCache(MemoryAllocator allocator)
{
this.Count = 0;
@ -346,7 +346,7 @@ internal unsafe struct ExactCache : IColorIndexCache<ExactCache>
public int Count { get; private set; }
/// <inheritdoc/>
public static ExactCache Create(MemoryAllocator allocator) => new(allocator);
public static AccurateCache Create(MemoryAllocator allocator) => new(allocator);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]

1
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -407,6 +407,7 @@ public partial class PngEncoderTests
[WithFile(TestImages.Png.APng, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.FrameOffset, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Issue2882, PixelTypes.Rgba32)]
public void Encode_APng<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{

1
tests/ImageSharp.Tests/TestImages.cs

@ -76,6 +76,7 @@ public static class TestImages
public const string FrameOffset = "Png/animated/frame-offset.png";
public const string DefaultNotAnimated = "Png/animated/default-not-animated.png";
public const string Issue2666 = "Png/issues/Issue_2666.png";
public const string Issue2882 = "Png/issues/Issue_2882.png";
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png";

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

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