mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 1598 additions and 1 deletions
@ -0,0 +1,190 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg; |
||||
|
|
||||
|
internal sealed class JpegBitWriter |
||||
|
{ |
||||
|
private const int ChunkSize = 16 * 1024; |
||||
|
|
||||
|
private readonly Stream stream; |
||||
|
private readonly byte[] buffer; |
||||
|
|
||||
|
private int pos; |
||||
|
private ulong putBuffer; |
||||
|
private int putBits; |
||||
|
|
||||
|
public JpegBitWriter(Stream stream) |
||||
|
{ |
||||
|
this.stream = stream; |
||||
|
this.buffer = new byte[ChunkSize]; |
||||
|
this.putBits = 64; |
||||
|
} |
||||
|
|
||||
|
public bool Healthy { get; private set; } = true; |
||||
|
|
||||
|
private void SwapBuffer() |
||||
|
{ |
||||
|
if (this.pos == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.stream.Write(this.buffer, 0, this.pos); |
||||
|
this.pos = 0; |
||||
|
} |
||||
|
|
||||
|
public void Reserve(int nBytes) |
||||
|
{ |
||||
|
if (this.pos + nBytes > this.buffer.Length) |
||||
|
{ |
||||
|
this.SwapBuffer(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void EmitByte(int value) |
||||
|
{ |
||||
|
this.buffer[this.pos++] = (byte)value; |
||||
|
|
||||
|
if (value == 0xff) |
||||
|
{ |
||||
|
this.buffer[this.pos++] = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void EmitMarker(int marker) |
||||
|
{ |
||||
|
this.Reserve(2); |
||||
|
|
||||
|
this.buffer[this.pos++] = 0xff; |
||||
|
this.buffer[this.pos++] = (byte)marker; |
||||
|
} |
||||
|
|
||||
|
public void StoreBe64(ulong value) |
||||
|
{ |
||||
|
this.buffer[this.pos++] = (byte)(value >> 56); |
||||
|
this.buffer[this.pos++] = (byte)(value >> 48); |
||||
|
this.buffer[this.pos++] = (byte)(value >> 40); |
||||
|
this.buffer[this.pos++] = (byte)(value >> 32); |
||||
|
this.buffer[this.pos++] = (byte)(value >> 24); |
||||
|
this.buffer[this.pos++] = (byte)(value >> 16); |
||||
|
this.buffer[this.pos++] = (byte)(value >> 8); |
||||
|
this.buffer[this.pos++] = (byte)value; |
||||
|
} |
||||
|
|
||||
|
public void DischargeBitBuffer(int nBits, ulong bits) |
||||
|
{ |
||||
|
this.putBuffer |= bits >> -this.putBits; |
||||
|
|
||||
|
if (HasZeroByte(~this.putBuffer)) |
||||
|
{ |
||||
|
this.Reserve(16); |
||||
|
|
||||
|
this.EmitByte((int)(this.putBuffer >> 56)); |
||||
|
this.EmitByte((int)(this.putBuffer >> 48)); |
||||
|
this.EmitByte((int)(this.putBuffer >> 40)); |
||||
|
this.EmitByte((int)(this.putBuffer >> 32)); |
||||
|
this.EmitByte((int)(this.putBuffer >> 24)); |
||||
|
this.EmitByte((int)(this.putBuffer >> 16)); |
||||
|
this.EmitByte((int)(this.putBuffer >> 8)); |
||||
|
this.EmitByte((int)this.putBuffer); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.Reserve(8); |
||||
|
this.StoreBe64(this.putBuffer); |
||||
|
} |
||||
|
|
||||
|
this.putBits += 64; |
||||
|
this.putBuffer = bits << this.putBits; |
||||
|
} |
||||
|
|
||||
|
public void WriteBits(int nBits, ulong bits) |
||||
|
{ |
||||
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(nBits); |
||||
|
|
||||
|
this.putBits -= nBits; |
||||
|
|
||||
|
if (this.putBits < 0) |
||||
|
{ |
||||
|
if (nBits > 64) |
||||
|
{ |
||||
|
this.putBits += nBits; |
||||
|
this.Healthy = false; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.DischargeBitBuffer(nBits, bits); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.putBuffer |= bits << this.putBits; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool JumpToByteBoundary(ref ReadOnlySpan<byte> padBits) |
||||
|
{ |
||||
|
int nBits = this.putBits & 7; |
||||
|
byte padPattern; |
||||
|
|
||||
|
if (padBits.IsEmpty) |
||||
|
{ |
||||
|
padPattern = (byte)((1 << nBits) - 1); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
padPattern = 0; |
||||
|
byte danglingBits = 0; |
||||
|
|
||||
|
for (int i = 0; i < nBits; ++i) |
||||
|
{ |
||||
|
if (padBits.IsEmpty) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
byte bit = padBits[0]; |
||||
|
padBits = padBits[1..]; |
||||
|
|
||||
|
danglingBits |= bit; |
||||
|
|
||||
|
padPattern <<= 1; |
||||
|
padPattern |= bit; |
||||
|
} |
||||
|
|
||||
|
if ((danglingBits & ~1) != 0) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.Reserve(16); |
||||
|
|
||||
|
while (this.putBits <= 56) |
||||
|
{ |
||||
|
int c = (int)(this.putBuffer >> 56); |
||||
|
|
||||
|
this.EmitByte(c); |
||||
|
|
||||
|
this.putBuffer <<= 8; |
||||
|
this.putBits += 8; |
||||
|
} |
||||
|
|
||||
|
if (this.putBits < 64) |
||||
|
{ |
||||
|
int padMask = 0xff >> (64 - this.putBits); |
||||
|
|
||||
|
int c = |
||||
|
((int)(this.putBuffer >> 56) & ~padMask) | |
||||
|
padPattern; |
||||
|
|
||||
|
this.EmitByte(c); |
||||
|
} |
||||
|
|
||||
|
this.putBuffer = 0; |
||||
|
this.putBits = 64; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg; |
||||
|
|
||||
|
internal sealed class JpegDctCodingState |
||||
|
{ |
||||
|
public int EobRun { get; set; } |
||||
|
|
||||
|
public JpegHuffmanCodeTable? CurAcHuff { get; set; } |
||||
|
|
||||
|
public List<ushort> RefinementBits { get; } = new(64); |
||||
|
|
||||
|
public int RefinementBitsCount { get; set; } |
||||
|
|
||||
|
public void Flush(JpegBitWriter writer) => JpegWriter.Flush(this, writer); |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg; |
||||
|
|
||||
|
// InlineArray values cannot be assigned as a property
|
||||
|
#pragma warning disable SA1401 // Fields should be private
|
||||
|
|
||||
|
internal sealed class JpegHuffmanCodeTable |
||||
|
{ |
||||
|
public InlineArray256<byte> Depth; |
||||
|
|
||||
|
public InlineArray256<short> Code; |
||||
|
|
||||
|
public bool Initialized { get; set; } |
||||
|
|
||||
|
public void InitializeDepths(byte value = 0) |
||||
|
{ |
||||
|
Span<byte> depths = this.Depth; |
||||
|
depths.Fill(value); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg; |
||||
|
|
||||
|
internal enum JpegSerializationStage |
||||
|
{ |
||||
|
Initialize, |
||||
|
SerializeSection, |
||||
|
Done, |
||||
|
Error |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg; |
||||
|
|
||||
|
internal sealed class JpegSerializationState |
||||
|
{ |
||||
|
public JpegSerializationStage Stage { get; set; } = JpegSerializationStage.Initialize; |
||||
|
|
||||
|
public int SectionIndex { get; set; } |
||||
|
|
||||
|
public int DhtIndex { get; set; } |
||||
|
|
||||
|
public int DqtIndex { get; set; } |
||||
|
|
||||
|
public int AppIndex { get; set; } |
||||
|
|
||||
|
public int ComIndex { get; set; } |
||||
|
|
||||
|
public int DataIndex { get; set; } |
||||
|
|
||||
|
public int ScanIndex { get; set; } |
||||
|
|
||||
|
public JpegHuffmanCodeTable[] DcHuffTable { get; set; } = []; |
||||
|
|
||||
|
public JpegHuffmanCodeTable[] AcHuffTable { get; set; } = []; |
||||
|
|
||||
|
// Should be field to get ref to it
|
||||
|
public ReadOnlyMemory<byte> PadBits; |
||||
|
|
||||
|
public int PadBitsPos { get; set; } |
||||
|
|
||||
|
public bool SeenDriMarker { get; set; } |
||||
|
|
||||
|
public bool IsProgressive { get; set; } |
||||
|
|
||||
|
public List<List<byte>> OutputQueue { get; set; } = []; |
||||
|
|
||||
|
public EncodeScanState ScanState { get; set; } |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg; |
||||
|
|
||||
|
internal enum JpegSerializationStatus : byte |
||||
|
{ |
||||
|
NeedsMoreInput, |
||||
|
NeedsMoreOutput, |
||||
|
Error, |
||||
|
Done |
||||
|
} |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue