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