public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { if (scanHeader.Components is null) { throw new InvalidOperationException(); } if (Decoder.GetOutputWriter() is null) { throw new InvalidOperationException(); } // Resolve each component Span <JpegHuffmanDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(_frameHeader, scanHeader, _components)); _restartInterval = Decoder.GetRestartInterval(); _mcusBeforeRestart = _restartInterval; _eobrun = 0; if (components.Length == 1) { DecodeProgressiveDataNonInterleaved(ref reader, scanHeader, components[0]); } else { DecodeProgressiveDataInterleaved(ref reader, scanHeader, components); } }
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { if (scanHeader.Components is null) { throw new InvalidOperationException(); } if (Decoder.GetOutputWriter() is null) { throw new InvalidOperationException(); } // Resolve each component Span <JpegHuffmanDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(_frameHeader, scanHeader, _components)); foreach (JpegHuffmanDecodingComponent component in components) { if (component.QuantizationTable.IsEmpty) { ThrowInvalidDataException($"Quantization table of component {component.ComponentIndex} is not defined."); } } _restartInterval = Decoder.GetRestartInterval(); _mcusBeforeRestart = _restartInterval; _eobrun = 0; if (components.Length == 1) { DecodeProgressiveDataNonInterleaved(ref reader, scanHeader, components[0]); } else { DecodeProgressiveDataInterleaved(ref reader, scanHeader, components); } }
public void Initialize(ushort restartInterval, ushort[] subsamplingFactors) { _restartInterval = restartInterval; // Make sure everything is initialized if (_components.Length == 0) { throw new InvalidDataException(); } foreach (ComponentInfo component in _components) { if (!component.IsInitialized) { throw new InvalidDataException(); } } var frameComponents = new JpegFrameComponentSpecificationParameters[_components.Length]; var scanComponents = new JpegScanComponentSpecificationParameters[_components.Length]; // Special case for YCbCr. if (subsamplingFactors.Length == 2) { if (frameComponents.Length != 3) { // YCbCr image must have 3 components. throw new InvalidDataException(); } if (subsamplingFactors[0] != 1 && subsamplingFactors[0] != 2 && subsamplingFactors[0] != 4) { throw new InvalidDataException("Subsampling factor other than 1,2,4 is not supported."); } if (subsamplingFactors[1] != 1 && subsamplingFactors[1] != 2 && subsamplingFactors[1] != 4) { throw new InvalidDataException("Subsampling factor other than 1,2,4 is not supported."); } ushort maxFactor = Math.Max(subsamplingFactors[0], subsamplingFactors[1]); frameComponents[0] = new JpegFrameComponentSpecificationParameters(0, (byte)maxFactor, (byte)maxFactor, 0); frameComponents[1] = new JpegFrameComponentSpecificationParameters(1, (byte)(maxFactor / subsamplingFactors[0]), (byte)(maxFactor / subsamplingFactors[1]), 1); frameComponents[2] = new JpegFrameComponentSpecificationParameters(2, (byte)(maxFactor / subsamplingFactors[0]), (byte)(maxFactor / subsamplingFactors[1]), 2); } else { for (int i = 0; i < frameComponents.Length; i++) { frameComponents[i] = new JpegFrameComponentSpecificationParameters((byte)i, 1, 1, (byte)i); } } for (int i = 0; i < scanComponents.Length; i++) { scanComponents[i] = new JpegScanComponentSpecificationParameters((byte)i, (byte)i, (byte)i); } _frameHeader = new JpegFrameHeader(8, 0, 0, (byte)frameComponents.Length, frameComponents); _scanHeader = new JpegScanHeader((byte)scanComponents.Length, scanComponents, 0, 0, 0, 0); }
private void DecodeProgressiveDataInterleaved(ref JpegReader reader, JpegScanHeader scanHeader, Span <JpegArithmeticDecodingComponent> components) { foreach (JpegArithmeticDecodingComponent component in components) { if (component.DcTable is null || component.DcStatistics is null) { ThrowInvalidDataException(); } } JpegBlockAllocator allocator = _allocator; JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); int mcusPerColumn = _mcusPerColumn; int mcusPerLine = _mcusPerLine; for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { foreach (JpegArithmeticDecodingComponent component in components) { int index = component.ComponentIndex; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int offsetX = colMcu * h; int offsetY = rowMcu * v; for (int y = 0; y < v; y++) { int blockOffsetY = offsetY + y; for (int x = 0; x < h; x++) { ref JpegBlock8x8 blockRef = ref allocator.GetBlockReference(index, offsetX + x, blockOffsetY); ReadBlockProgressiveDC(ref bitReader, component, scanHeader, ref blockRef); } } } if (!HandleRestart(ref bitReader, ref reader, ref scanHeader, ref MemoryMarshal.GetReference(components), components.Length)) { return; } } }
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { if (scanHeader.Components is null) { throw new InvalidOperationException(); } if (Decoder.GetOutputWriter() is null) { throw new InvalidOperationException(); } // Resolve each component Span <JpegArithmeticDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(_frameHeader, scanHeader, _components)); foreach (JpegArithmeticDecodingComponent component in _components) { if (scanHeader.StartOfSpectralSelection == 0 && scanHeader.SuccessiveApproximationBitPositionHigh == 0) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); } if (scanHeader.StartOfSpectralSelection != 0) { component.AcStatistics?.Reset(); } } _restartInterval = Decoder.GetRestartInterval(); _mcusBeforeRestart = _restartInterval; Reset(); if (components.Length == 1) { DecodeProgressiveDataNonInterleaved(ref reader, scanHeader, components[0]); } else { DecodeProgressiveDataInterleaved(ref reader, scanHeader, components); } }
private void DecodeProgressiveDataInterleaved(ref JpegReader reader, JpegScanHeader scanHeader, Span <JpegHuffmanDecodingComponent> components) { JpegBlockAllocator allocator = _allocator; JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); int mcusPerColumn = _mcusPerColumn; int mcusPerLine = _mcusPerLine; for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { foreach (JpegHuffmanDecodingComponent component in components) { int index = component.ComponentIndex; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int offsetX = colMcu * h; int offsetY = rowMcu * v; for (int y = 0; y < v; y++) { int blockOffsetY = offsetY + y; for (int x = 0; x < h; x++) { ref JpegBlock8x8 blockRef = ref allocator.GetBlockReference(index, offsetX + x, blockOffsetY); ReadBlockProgressiveDC(ref bitReader, component, scanHeader, ref blockRef); } } } if (!HandleRestart(ref bitReader, ref reader)) { return; } } }
protected int InitDecodeComponents(JpegFrameHeader frameHeader, JpegScanHeader scanHeader, Span <JpegArithmeticDecodingComponent> components) { Debug.Assert(!(frameHeader.Components is null)); Debug.Assert(!(scanHeader.Components is null)); // Compute maximum sampling factor int maxHorizontalSampling = 1; int maxVerticalSampling = 1; foreach (JpegFrameComponentSpecificationParameters currentFrameComponent in frameHeader.Components !) { maxHorizontalSampling = Math.Max(maxHorizontalSampling, currentFrameComponent.HorizontalSamplingFactor); maxVerticalSampling = Math.Max(maxVerticalSampling, currentFrameComponent.VerticalSamplingFactor); } // Resolve each component if (components.Length < scanHeader.NumberOfComponents) { throw new InvalidOperationException(); } for (int i = 0; i < scanHeader.NumberOfComponents; i++) { JpegScanComponentSpecificationParameters scanComponenet = scanHeader.Components ![i];
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { JpegFrameHeader frameHeader = _frameHeader; JpegBlockOutputWriter?outputWriter = Decoder.GetOutputWriter(); if (frameHeader.Components is null) { ThrowInvalidDataException(); } if (scanHeader.Components is null) { ThrowInvalidDataException(); } if (outputWriter is null) { ThrowInvalidDataException(); } // Resolve each component Span <JpegArithmeticDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(frameHeader, scanHeader, _components)); foreach (JpegArithmeticDecodingComponent component in _components) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); component.AcStatistics?.Reset(); } Reset(); // Prepare int maxHorizontalSampling = _maxHorizontalSampling; int maxVerticalSampling = _maxVerticalSampling; int mcusBeforeRestart = _restartInterval; int mcusPerLine = _mcusPerLine; int mcusPerColumn = _mcusPerColumn; int levelShift = _levelShift; JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); // DCT Block JpegBlock8x8F blockFBuffer = default; JpegBlock8x8F outputFBuffer = default; JpegBlock8x8F tempFBuffer = default; JpegBlock8x8 outputBuffer; for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { int offsetY = rowMcu * maxVerticalSampling; for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { int offsetX = colMcu * maxHorizontalSampling; // Scan an interleaved mcu... process components in order foreach (JpegArithmeticDecodingComponent component in components) { int index = component.ComponentIndex; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int hs = component.HorizontalSubsamplingFactor; int vs = component.VerticalSubsamplingFactor; for (int y = 0; y < v; y++) { int blockOffsetY = (offsetY + y) * 8; for (int x = 0; x < h; x++) { // Read MCU outputBuffer = default; ReadBlock(ref bitReader, component, ref outputBuffer); // Dequantization DequantizeBlockAndUnZigZag(component.QuantizationTable, ref outputBuffer, ref blockFBuffer); // IDCT FastFloatingPointDCT.TransformIDCT(ref blockFBuffer, ref outputFBuffer, ref tempFBuffer); // Level shift ShiftDataLevel(ref outputFBuffer, ref outputBuffer, levelShift); // CopyToOutput WriteBlock(outputWriter, ref Unsafe.As <JpegBlock8x8, short>(ref outputBuffer), index, (offsetX + x) * 8, blockOffsetY, hs, vs); } } } // Handle restart if (_restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { int bytesConsumedEoi = reader.RemainingByteCount - bitReader.RemainingBits / 8; reader.TryAdvance(bytesConsumedEoi - 2); return; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = _restartInterval; foreach (JpegArithmeticDecodingComponent component in components) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); component.AcStatistics?.Reset(); } Reset(); } } } bitReader.AdvanceAlignByte(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { if (scanHeader.Components is null) { throw new InvalidOperationException(); } if (Decoder.GetOutputWriter() is null) { throw new InvalidOperationException(); } // Resolve each component Span <JpegHuffmanDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(_frameHeader, scanHeader, _components)); foreach (JpegHuffmanDecodingComponent component in components) { if (component.DcTable is null) { ThrowInvalidDataException($"Huffman table of component {component.ComponentIndex} is not defined."); } } JpegPartialScanlineAllocator allocator = _allocator; int mcusPerLine = _mcusPerLine; int mcusPerColumn = _mcusPerColumn; // Prepare JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); int restartInterval = _restartInterval; int mcusBeforeRestart = restartInterval; int predictor = scanHeader.StartOfSpectralSelection; int initialPrediction = 1 << (_frameHeader.SamplePrecision - scanHeader.SuccessiveApproximationBitPositionLow - 1); for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { // Scan an interleaved mcu... process components in order foreach (JpegHuffmanDecodingComponent component in components) { int index = component.ComponentIndex; JpegHuffmanDecodingTable losslessTable = component.DcTable !; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int offsetX = colMcu * h; int offsetY = rowMcu * v; for (int y = 0; y < v; y++) { Span <short> scanline = allocator.GetScanlineSpan(index, offsetY + y); Span <short> lastScanline = (y == 0 && rowMcu == 0) ? default : allocator.GetScanlineSpan(index, offsetY + y - 1); for (int x = 0; x < h; x++) { int diffValue = ReadSampleLossless(ref bitReader, losslessTable); // The one-dimensional horizontal predictor (prediction sample Ra) is used // for the first line of samples at the start of the scan and at the beginning of each restart interval if (rowMcu == 0 || (restartInterval > 0 && mcusBeforeRestart == restartInterval)) { // At the beginning of the first line and at the beginning of each restart interval the prediction value of 2^(P – 1) is used, where P is the input precision. // If the point transformation parameter (see A.4) is non-zero, the prediction value at the beginning of the first lines and the beginning of each restart interval is 2^(P – Pt – 1), where Pt is the value of the point transformation parameter. if (colMcu == 0 && x == 0) { diffValue += initialPrediction; } else { int ra = scanline[offsetX + x - 1]; int rb = y == 0 ? initialPrediction : lastScanline[offsetX + x]; int rc = y == 0 ? initialPrediction : lastScanline[offsetX + x - 1]; diffValue += predictor switch { 1 => ra, // Px = Ra 2 => rb, // Px = Rb 3 => rc, // Px = Rc 4 => ra + rb - rc, // Px = Ra + Rb – Rc 5 => ra + ((rb - rc) >> 1), // Px = Ra + (Rb – Rc)/2 6 => rb + ((ra - rc) >> 1), // Px = Rb + (Ra – Rc)/2 7 => (ra + rb) >> 1, // Px = (Ra + Rb)/2 _ => 0, // No prediction (See Annex J) }; } } // The sample from the line above(prediction sample Rb) is used at the start of each line, except for the first line else if (colMcu == 0) { diffValue += lastScanline[offsetX + x]; } else { diffValue += predictor switch { 1 => scanline[offsetX + x - 1], // Px = Ra 2 => lastScanline[offsetX + x], // Px = Rb 3 => lastScanline[offsetX + x - 1], // Px = Rc 4 => scanline[offsetX + x - 1] + lastScanline[offsetX + x] - lastScanline[offsetX + x - 1], // Px = Ra + Rb – Rc 5 => scanline[offsetX + x - 1] + ((lastScanline[offsetX + x] - lastScanline[offsetX + x - 1]) >> 1), // Px = Ra + (Rb – Rc)/2 6 => lastScanline[offsetX + x] + ((scanline[offsetX + x - 1] - lastScanline[offsetX + x - 1]) >> 1), // Px = Rb + (Ra – Rc)/2 7 => (scanline[offsetX + x - 1] + lastScanline[offsetX + x]) >> 1, // Px = (Ra + Rb)/2 _ => 0, // No prediction (See Annex J) }; } scanline[offsetX + x] = (short)diffValue; } } } // Handle restart if (restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { int bytesConsumedEoi = reader.RemainingByteCount - bitReader.RemainingBits / 8; reader.TryAdvance(bytesConsumedEoi - 2); return; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = restartInterval; } } // Flush allocator if (rowMcu == mcusPerColumn - 1) { foreach (JpegHuffmanDecodingComponent component in components) { allocator.FlushLastMcu(component.ComponentIndex, (rowMcu + 1) * component.VerticalSamplingFactor); } } else { foreach (JpegHuffmanDecodingComponent component in components) { allocator.FlushMcu(component.ComponentIndex, (rowMcu + 1) * component.VerticalSamplingFactor); } } } bitReader.AdvanceAlignByte(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }
public abstract void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader);