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); }
protected static void DequantizeBlockAndUnZigZag(JpegQuantizationTable quantizationTable, ref JpegBlock8x8 input, ref JpegBlock8x8F output) { Debug.Assert(!quantizationTable.IsEmpty); ref ushort elementRef = ref MemoryMarshal.GetReference(quantizationTable.Elements);