/// <summary> /// Initializes a new instance of the <see cref="ScanDecoder"/> class. /// </summary> /// <param name="stream">The input stream.</param> /// <param name="frame">The image frame.</param> /// <param name="dcHuffmanTables">The DC Huffman tables.</param> /// <param name="acHuffmanTables">The AC Huffman tables.</param> /// <param name="fastACTables">The fast AC decoding tables.</param> /// <param name="componentsLength">The length of the components. Different to the array length.</param> /// <param name="restartInterval">The reset interval.</param> /// <param name="spectralStart">The spectral selection start.</param> /// <param name="spectralEnd">The spectral selection end.</param> /// <param name="successiveHigh">The successive approximation bit high end.</param> /// <param name="successiveLow">The successive approximation bit low end.</param> public ScanDecoder( DoubleBufferedStreamReader stream, JpegFrame frame, HuffmanTables dcHuffmanTables, HuffmanTables acHuffmanTables, FastACTables fastACTables, int componentsLength, int restartInterval, int spectralStart, int spectralEnd, int successiveHigh, int successiveLow) { this.dctZigZag = ZigZag.CreateUnzigTable(); this.stream = stream; this.frame = frame; this.dcHuffmanTables = dcHuffmanTables; this.acHuffmanTables = acHuffmanTables; this.fastACTables = fastACTables; this.components = frame.Components; this.marker = JpegConstants.Markers.XFF; this.markerPosition = 0; this.componentsLength = componentsLength; this.restartInterval = restartInterval; this.spectralStart = spectralStart; this.spectralEnd = spectralEnd; this.successiveHigh = successiveHigh; this.successiveLow = successiveLow; }
public unsafe void Quantize(int seed) { var block = new Block8x8F(); block.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); var qt = new Block8x8F(); qt.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); var unzig = ZigZag.CreateUnzigTable(); int *expectedResults = stackalloc int[Block8x8F.Size]; ReferenceImplementations.QuantizeRational(&block, expectedResults, &qt, unzig.Data); var actualResults = default(Block8x8F); Block8x8F.Quantize(&block, &actualResults, &qt, unzig.Data); for (int i = 0; i < Block8x8F.Size; i++) { int expected = expectedResults[i]; int actual = (int)actualResults[i]; Assert.Equal(expected, actual); } }
public void ZigZagCanHandleAllPossibleCoefficients() { // Mimic the behaviour of the huffman scan decoder using all possible byte values short[] block = new short[64]; var zigzag = ZigZag.CreateUnzigTable(); for (int h = 0; h < 255; h++) { for (int i = 1; i < 64; i++) { int s = h; int r = s >> 4; s &= 15; if (s != 0) { i += r; block[zigzag[i++]] = (short)s; } else { if (r == 0) { break; } i += 16; } } } }
/// <summary> /// Creates and initializes a new <see cref="ComputationData"/> instance /// </summary> /// <returns>The <see cref="ComputationData"/></returns> public static ComputationData Create() { ComputationData data = default; data.Unzig = ZigZag.CreateUnzigTable(); return(data); }
/// <summary> /// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class. /// </summary> /// <param name="stream">The input stream.</param> /// <param name="frame">The image frame.</param> /// <param name="dcHuffmanTables">The DC Huffman tables.</param> /// <param name="acHuffmanTables">The AC Huffman tables.</param> /// <param name="componentsLength">The length of the components. Different to the array length.</param> /// <param name="restartInterval">The reset interval.</param> /// <param name="spectralStart">The spectral selection start.</param> /// <param name="spectralEnd">The spectral selection end.</param> /// <param name="successiveHigh">The successive approximation bit high end.</param> /// <param name="successiveLow">The successive approximation bit low end.</param> public HuffmanScanDecoder( DoubleBufferedStreamReader stream, JpegFrame frame, HuffmanTable[] dcHuffmanTables, HuffmanTable[] acHuffmanTables, int componentsLength, int restartInterval, int spectralStart, int spectralEnd, int successiveHigh, int successiveLow) { this.dctZigZag = ZigZag.CreateUnzigTable(); this.stream = stream; this.scanBuffer = new HuffmanScanBuffer(stream); this.frame = frame; this.dcHuffmanTables = dcHuffmanTables; this.acHuffmanTables = acHuffmanTables; this.components = frame.Components; this.componentsLength = componentsLength; this.restartInterval = restartInterval; this.todo = restartInterval; this.spectralStart = spectralStart; this.spectralEnd = spectralEnd; this.successiveHigh = successiveHigh; this.successiveLow = successiveLow; }
/// <summary> /// Encodes the image with no subsampling. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param> /// <param name="cancellationToken">The token to monitor for cancellation.</param> private void Encode444 <TPixel>(Image <TPixel> pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel <TPixel> { // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // (Partially done with YCbCrForwardConverter<TPixel>) Block8x8F temp1 = default; Block8x8F temp2 = default; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; var unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; var pixelConverter = YCbCrForwardConverter <TPixel> .Create(); ImageFrame <TPixel> frame = pixels.Frames.RootFrame; Buffer2D <TPixel> pixelBuffer = frame.PixelBuffer; for (int y = 0; y < pixels.Height; y += 8) { cancellationToken.ThrowIfCancellationRequested(); var currentRows = new RowOctet <TPixel>(pixelBuffer, y); for (int x = 0; x < pixels.Width; x += 8) { pixelConverter.Convert(frame, x, y, currentRows); prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, ref pixelConverter.Y, ref temp1, ref temp2, ref onStackLuminanceQuantTable, ref unzig); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, ref pixelConverter.Cb, ref temp1, ref temp2, ref onStackChrominanceQuantTable, ref unzig); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, ref pixelConverter.Cr, ref temp1, ref temp2, ref onStackChrominanceQuantTable, ref unzig); } } }
/// <summary> /// Encodes the image with no subsampling. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param> private void Encode444 <TPixel>(Image <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // (Partially done with YCbCrForwardConverter<TPixel>) Block8x8F temp1 = default; Block8x8F temp2 = default; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; var unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; var pixelConverter = YCbCrForwardConverter <TPixel> .Create(); for (int y = 0; y < pixels.Height; y += 8) { for (int x = 0; x < pixels.Width; x += 8) { pixelConverter.Convert(pixels.Frames.RootFrame, x, y); prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, &pixelConverter.Y, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, &pixelConverter.Cb, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, &pixelConverter.Cr, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); } } }
public unsafe void DequantizeBlock(int seed) { Block8x8F original = CreateRandomFloatBlock(-500, 500, seed); Block8x8F qt = CreateRandomFloatBlock(0, 10, seed + 42); var unzig = ZigZag.CreateUnzigTable(); Block8x8F expected = original; Block8x8F actual = original; ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data); Block8x8F.DequantizeBlock(&actual, &qt, unzig.Data); this.CompareBlocks(expected, actual, 0); }
/// <summary> /// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class. /// </summary> /// <param name="stream">The input stream.</param> /// <param name="converter">Spectral to pixel converter.</param> /// <param name="cancellationToken">The token to monitor cancellation.</param> public HuffmanScanDecoder( BufferedReadStream stream, SpectralConverter converter, CancellationToken cancellationToken) { this.dctZigZag = ZigZag.CreateUnzigTable(); this.stream = stream; this.spectralConverter = converter; this.cancellationToken = cancellationToken; // TODO: this is actually a variable value depending on component count const int maxTables = 4; this.dcHuffmanTables = new HuffmanTable[maxTables]; this.acHuffmanTables = new HuffmanTable[maxTables]; }
public unsafe void ZigZag_CreateDequantizationTable_MultiplicationShouldQuantize(int seed) { Block8x8F original = CreateRandomFloatBlock(-500, 500, seed); Block8x8F qt = CreateRandomFloatBlock(0, 10, seed + 42); var unzig = ZigZag.CreateUnzigTable(); Block8x8F zigQt = ZigZag.CreateDequantizationTable(ref qt); Block8x8F expected = original; Block8x8F actual = original; ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data); actual.MultiplyInplace(ref zigQt); this.CompareBlocks(expected, actual, 0); }
/// <summary> /// Encodes the image with subsampling. The Cb and Cr components are each subsampled /// at a factor of 2 both horizontally and vertically. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param> private void Encode420 <TPixel>(Image <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default; BlockQuad cb = default; BlockQuad cr = default; var cbPtr = (Block8x8F *)cb.Data; var crPtr = (Block8x8F *)cr.Data; Block8x8F temp1 = default; Block8x8F temp2 = default; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; var unzig = ZigZag.CreateUnzigTable(); var pixelConverter = YCbCrForwardConverter <TPixel> .Create(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; for (int y = 0; y < pixels.Height; y += 16) { for (int x = 0; x < pixels.Width; x += 16) { for (int i = 0; i < 4; i++) { int xOff = (i & 1) * 8; int yOff = (i & 2) * 4; pixelConverter.Convert(pixels.Frames.RootFrame, x + xOff, y + yOff); cbPtr[i] = pixelConverter.Cb; crPtr[i] = pixelConverter.Cr; prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, &pixelConverter.Y, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); } Block8x8F.Scale16X16To8X8(&b, cbPtr); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, &b, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); Block8x8F.Scale16X16To8X8(&b, crPtr); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, &b, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); } } }
/// <summary> /// Decodes the spectral scan /// </summary> /// <param name="frame">The image frame</param> /// <param name="stream">The input stream</param> /// <param name="dcHuffmanTables">The DC Huffman tables</param> /// <param name="acHuffmanTables">The AC Huffman tables</param> /// <param name="components">The scan components</param> /// <param name="componentIndex">The component index within the array</param> /// <param name="componentsLength">The length of the components. Different to the array length</param> /// <param name="resetInterval">The reset interval</param> /// <param name="spectralStart">The spectral selection start</param> /// <param name="spectralEnd">The spectral selection end</param> /// <param name="successivePrev">The successive approximation bit high end</param> /// <param name="successive">The successive approximation bit low end</param> public void DecodeScan( PdfJsFrame frame, DoubleBufferedStreamReader stream, PdfJsHuffmanTables dcHuffmanTables, PdfJsHuffmanTables acHuffmanTables, PdfJsFrameComponent[] components, int componentIndex, int componentsLength, ushort resetInterval, int spectralStart, int spectralEnd, int successivePrev, int successive) { this.dctZigZag = ZigZag.CreateUnzigTable(); this.markerBuffer = new byte[2]; this.compIndex = componentIndex; this.specStart = spectralStart; this.specEnd = spectralEnd; this.successiveState = successive; this.endOfStreamReached = false; this.unexpectedMarkerReached = false; bool progressive = frame.Progressive; this.mcusPerLine = frame.McusPerLine; this.mcu = 0; int mcuExpected; if (componentsLength == 1) { mcuExpected = components[this.compIndex].WidthInBlocks * components[this.compIndex].HeightInBlocks; } else { mcuExpected = this.mcusPerLine * frame.McusPerColumn; } while (this.mcu < mcuExpected) { // Reset interval stuff this.mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - this.mcu, resetInterval) : mcuExpected; for (int i = 0; i < components.Length; i++) { PdfJsFrameComponent c = components[i]; c.Pred = 0; } this.eobrun = 0; if (!progressive) { this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, stream); } else { bool isAc = this.specStart != 0; bool isFirst = successivePrev == 0; PdfJsHuffmanTables huffmanTables = isAc ? acHuffmanTables : dcHuffmanTables; this.DecodeScanProgressive(huffmanTables, isAc, isFirst, components, componentsLength, stream); } // Reset // TODO: I do not understand why these values are reset? We should surely be tracking the bits across mcu's? this.bitsCount = 0; this.bitsData = 0; this.unexpectedMarkerReached = false; // Some images include more scan blocks than expected, skip past those and // attempt to find the next valid marker PdfJsFileMarker fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); byte marker = fileMarker.Marker; // RSTn - We've already read the bytes and altered the position so no need to skip if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) { continue; } if (!fileMarker.Invalid) { // We've found a valid marker. // Rewind the stream to the position of the marker and break stream.Position = fileMarker.Position; break; } #if DEBUG Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); #endif } }