Browse Source

Fixed tests, fixed compilation, added DHT marker decoding more meaningful exception messages, fixed invalid jpeg encoding

pull/1761/head
Dmitry Pentin 5 years ago
parent
commit
775610d5a0
  1. 22
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
  2. 4
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  3. 20
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

22
src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs

@ -561,13 +561,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// this method endianness dependent. /// this method endianness dependent.
/// </remarks> /// </remarks>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private void FlushToStream() private void FlushToStream(int endIndex)
{ {
Span<byte> emitBytes = MemoryMarshal.AsBytes(this.emitBuffer.AsSpan()); Span<byte> emitBytes = MemoryMarshal.AsBytes(this.emitBuffer.AsSpan());
int writeIdx = 0; int writeIdx = 0;
int startIndex = emitBytes.Length - 1; int startIndex = emitBytes.Length - 1;
int endIndex = this.emitWriteIndex * sizeof(uint);
// Some platforms may fail to eliminate this if-else branching // Some platforms may fail to eliminate this if-else branching
// Even if it happens - buffer is flushed in big packs, // Even if it happens - buffer is flushed in big packs,
@ -621,28 +620,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
} }
this.target.Write(this.streamWriteBuffer, 0, writeIdx); this.target.Write(this.streamWriteBuffer, 0, writeIdx);
}
private void FlushToStream()
{
this.FlushToStream(this.emitWriteIndex * 4);
this.emitWriteIndex = this.emitBuffer.Length; this.emitWriteIndex = this.emitBuffer.Length;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private void FlushRemainingBytes() private void FlushRemainingBytes()
{ {
// Flush full 4-byte blocks
this.FlushToStream();
// Padding all 4 bytes with 1's while not corrupting initial bits stored in accumulatedBits // Padding all 4 bytes with 1's while not corrupting initial bits stored in accumulatedBits
// And writing only valuable count of bytes count we want to write to the output stream // And writing only valuable count of bytes count we want to write to the output stream
int valuableBytesCount = (int)Numerics.DivideCeil((uint)this.bitCount, 8); int valuableBytesCount = (int)Numerics.DivideCeil((uint)this.bitCount, 8);
uint packedBytes = this.accumulatedBits | (uint.MaxValue >> this.bitCount); uint packedBytes = this.accumulatedBits | (uint.MaxValue >> this.bitCount);
this.emitBuffer[--this.emitWriteIndex] = packedBytes;
Span<byte> emitBytes = MemoryMarshal.AsBytes(this.emitBuffer.AsSpan()); // Flush cached bytes to the output stream with padding bits
for (int i = 0; i < valuableBytesCount; i++) this.FlushToStream((this.emitWriteIndex * 4) - 4 + valuableBytesCount);
{
emitBytes[i] = (byte)((packedBytes >> ((3 - i) * 8)) & 0xff);
}
// Flush remaining 'tail' bytes
this.target.Write(emitBytes, 0, valuableBytesCount);
} }
} }
} }

4
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -1071,13 +1071,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Types 0..1 DC..AC // Types 0..1 DC..AC
if (tableType > 1) if (tableType > 1)
{ {
JpegThrowHelper.ThrowInvalidImageContentException("Bad Huffman Table type."); JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}");
} }
// Max tables of each type // Max tables of each type
if (tableIndex > 3) if (tableIndex > 3)
{ {
JpegThrowHelper.ThrowInvalidImageContentException("Bad Huffman Table index."); JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}");
} }
stream.Read(huffmanDataSpan, 0, 16); stream.Read(huffmanDataSpan, 0, 16);

20
tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

@ -2,9 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif #endif
@ -121,24 +118,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void IDCT8x8_Avx(int seed) public void IDCT8x8_Avx(int seed)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
var skip = !Avx.IsSupported; if (!Avx.IsSupported)
#else
var skip = true;
#endif
if (skip)
{ {
this.Output.WriteLine("No AVX present, skipping test!"); this.Output.WriteLine("No AVX present, skipping test!");
return;
} }
Span<float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = Create8x8RoundedRandomFloatData(-200, 200, seed);
var srcBlock = default(Block8x8F); Block8x8F srcBlock = default;
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
var destBlock = default(Block8x8F); Block8x8F destBlock = default;
var expectedDest = new float[64]; float[] expectedDest = new float[64];
// reference, left part // reference, left part
ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src, expectedDest); ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src, expectedDest);
@ -149,10 +140,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// testee, whole 8x8 // testee, whole 8x8
FastFloatingPointDCT.IDCT8x8_Avx(ref srcBlock, ref destBlock); FastFloatingPointDCT.IDCT8x8_Avx(ref srcBlock, ref destBlock);
var actualDest = new float[64]; float[] actualDest = new float[64];
destBlock.ScaledCopyTo(actualDest); destBlock.ScaledCopyTo(actualDest);
Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f));
#endif
} }
[Theory] [Theory]

Loading…
Cancel
Save