From 1eaf1d3015709559452e2d2c1c4c2929df0de97f Mon Sep 17 00:00:00 2001 From: Julius Friedman Date: Thu, 24 Oct 2024 17:10:21 -0400 Subject: [PATCH] Working on JpegImage. --- Codecs/Image/Jpeg/JpegCodec.cs | 88 +++++++++++++++++++++++++++------- Codecs/Image/Jpeg/JpegImage.cs | 16 ++++--- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/Codecs/Image/Jpeg/JpegCodec.cs b/Codecs/Image/Jpeg/JpegCodec.cs index 13a12785..22fde886 100644 --- a/Codecs/Image/Jpeg/JpegCodec.cs +++ b/Codecs/Image/Jpeg/JpegCodec.cs @@ -105,7 +105,7 @@ public static IEnumerable ReadMarkers(Stream jpegStream) internal const int BlockSize = 8; internal static readonly double SqrtHalf = 1.0 / System.Math.Sqrt(2.0); - private static void InverseQuantize(short[] block, short[] quantizationTable) + private static void InverseQuantize(Span block, Span quantizationTable) { for (int i = 0; i < block.Length; i++) { @@ -355,35 +355,87 @@ private static int DecodeHuffman(BitReader stream, HuffmanTable table) } } - internal static void Decompress(ImageFormat imageFormat, JpegState jpegImage, BitReader stream) + private static short[] ReadBlock(BitReader stream, HuffmanTable dcTable, HuffmanTable acTable, ref int previousDC) { - //Span output = stackalloc double[BlockSize]; + var block = new short[BlockSize * BlockSize]; // Assuming 8x8 block + + // Decode DC coefficient + int dcDifference = DecodeHuffman(stream, dcTable); + previousDC += dcDifference; + block[0] = (short)previousDC; + + // Decode AC coefficients + int i = 1; + while (i < 64) + { + int acValue = DecodeHuffman(stream, acTable); + + if (acValue == 0) // End of Block (EOB) + break; + + int runLength = (acValue >> 4) & 0xF; // Upper 4 bits + int coefficient = acValue & 0xF; // Lower 4 bits + + i += runLength; // Skip zeros + if (i < 64) + { + block[i] = (short)DecodeCoefficient(stream, coefficient); + i++; + } + } + + return block; + } + + private static int DecodeCoefficient(BitReader stream, int size) + { + if (size == 0) + return 0; + + // Read 'size' number of bits + int value = (int)stream.ReadBits(size); + + // Convert to a signed integer + int signBit = 1 << (size - 1); + if ((value & signBit) == 0) + { + // If the sign bit is not set, the number is negative + value -= (1 << size) - 1; + } + + return value; + } + + internal static void Decompress(JpegImage jpegImage) + { + using var bitStream = new BitReader(jpegImage.Data.Array, Binary.BitOrder.MostSignificant, 0, 0, true, Environment.ProcessorCount * Environment.ProcessorCount); + + //int previousDc = 0; //// Step 2: Decode Huffman encoded data - //foreach (var component in imageFormat.Components) + //foreach (var component in jpegImage.ImageFormat.Components) //{ // for (int i = 0; i < component.Blocks.Length; i++) // { - // var block = new short[BlockSize]; - // // Decode DC coefficient - // int dcCoefficient = DecodeHuffman(stream, jpegState.HuffmanTables[component.Id]); - // block[0] = dcCoefficient; - - // // Decode AC coefficients - // for (int j = 1; j < block.Length; j++) - // { - // int acCoefficient = DecodeHuffman(stream, jegState.HuffmanTables[component.Id]); - // block[j] = acCoefficient; - // } + // var block = ReadBlock(stream, jpegImage.JpegState.HuffmanTables[0], jpegImage.JpegState.HuffmanTables[1], ref previousDc); + + // using var Qk = jpegImage.JpegState.QuantizationTables[component.Id].Qk; + + // var span = Qk.ToSpan(); + + // var reinterpret = MemoryMarshal.Cast(span); // // Step 3: Inverse Quantize - // InverseQuantize(block, jpegState.QuantizationTables[component.Id].Qk); + // InverseQuantize(block, reinterpret); // // Step 4: Apply IDCT - // IDCT(block, output); + // if (Vector.IsHardwareAccelerated) + // VIDCT(block, output); + // else + // IDCT(block, output); // // Step 5: Store block data for the specific component - // component.Blocks[i] = block; + // ?? // } //} } diff --git a/Codecs/Image/Jpeg/JpegImage.cs b/Codecs/Image/Jpeg/JpegImage.cs index 9972e5a4..55ab5efa 100644 --- a/Codecs/Image/Jpeg/JpegImage.cs +++ b/Codecs/Image/Jpeg/JpegImage.cs @@ -157,10 +157,7 @@ public static JpegImage FromStream(Stream stream) var read = stream.Read(dataSegment.Array, dataSegment.Offset, dataSegment.Count); if (read < dataSegment.Count) - dataSegment = dataSegment.Slice(0, read); - - using var bitStream = new BitReader(dataSegment.Array, Binary.BitOrder.MostSignificant, 0, 0, true, Environment.ProcessorCount * Environment.ProcessorCount); - JpegCodec.Decompress(imageFormat, jpegState, bitStream); + dataSegment = dataSegment.Slice(0, read); break; } @@ -192,11 +189,18 @@ public static JpegImage FromStream(Stream stream) } } + // If the required tags were not present throw an exception. if (imageFormat == null || dataSegment == null && thumbnailData == null) throw new InvalidDataException("The provided stream does not contain valid JPEG image data."); - // Create and return the JpegImage (Could use a Default Jpeg State?) - return new JpegImage(imageFormat, width, height, dataSegment ?? thumbnailData, jpegState, markers); + // Create the JpegImage which still contains compressed image data. + var result = new JpegImage(imageFormat, width, height, dataSegment ?? thumbnailData, jpegState, markers); + + //Decompress the JpegImage using the JpegCodec. + JpegCodec.Decompress(result); + + //Return the result. + return result; } public void Save(Stream stream, int quality = 99)