private void ProcessFrameHeader(ref JpegReader reader, bool metadataOnly, bool overrideAllowed) { // Read length field if (!reader.TryReadLength(out ushort length)) { ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment length."); return; } if (!reader.TryReadBytes(length, out ReadOnlySequence <byte> buffer)) { ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment content."); return; } if (!JpegFrameHeader.TryParse(buffer, metadataOnly, out JpegFrameHeader frameHeader, out int bytesConsumed)) { ThrowInvalidDataException(reader.ConsumedByteCount - length + bytesConsumed, "Failed to parse frame header."); return; } if (!overrideAllowed && _frameHeader.HasValue) { ThrowInvalidDataException(reader.ConsumedByteCount, "Multiple frame is not supported."); return; } _frameHeader = frameHeader; }
public static bool TryParse(ReadOnlySequence <byte> buffer, bool metadataOnly, out JpegFrameHeader frameHeader, out int bytesConsumed) { if (buffer.IsSingleSegment) { return(TryParse(buffer.First.Span, metadataOnly, out frameHeader, out bytesConsumed)); } bytesConsumed = 0; if (buffer.Length < 6) { frameHeader = default; return(false); } Span <byte> local = stackalloc byte[6]; buffer.Slice(0, 6).CopyTo(local); byte numberOfComponenets = local[5]; ushort samplesPerLine = (ushort)(local[4] | (local[3] << 8)); ushort numberOfLines = (ushort)(local[2] | (local[1] << 8)); byte precision = local[0]; buffer = buffer.Slice(6); bytesConsumed += 6; if (buffer.Length < 3 * numberOfComponenets) { frameHeader = default; return(false); } if (metadataOnly) { bytesConsumed += 3 * numberOfComponenets; frameHeader = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, null); return(true); } JpegFrameComponentSpecificationParameters[] components = new JpegFrameComponentSpecificationParameters[numberOfComponenets]; for (int i = 0; i < components.Length; i++) { if (!JpegFrameComponentSpecificationParameters.TryParse(buffer, out components[i])) { frameHeader = default; return(false); } buffer = buffer.Slice(3); bytesConsumed += 3; } frameHeader = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, components); return(true); }
public void Allocate(JpegFrameHeader frameHeader) { if (!(_bufferHandle is null)) { throw new InvalidOperationException(); } // 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); } int horizontalBlockCount = (frameHeader.SamplesPerLine + 7) / 8; int verticalBlockCount = (frameHeader.NumberOfLines + 7) / 8; ComponentAllocation[] componentAllocations = _components = new ComponentAllocation[frameHeader.NumberOfComponents]; int index = 0; foreach (JpegFrameComponentSpecificationParameters component in frameHeader.Components) { int horizontalSubsamplingFactor = maxHorizontalSampling / component.HorizontalSamplingFactor; int verticalSubsamplingFactor = maxVerticalSampling / component.VerticalSamplingFactor; int horizontalComponentBlockCount = (horizontalBlockCount + horizontalSubsamplingFactor - 1) / horizontalSubsamplingFactor; int verticalComponentBlockCount = (verticalBlockCount + verticalSubsamplingFactor - 1) / verticalSubsamplingFactor; componentAllocations[index++] = new ComponentAllocation { HorizontalComponentBlock = horizontalComponentBlockCount, VerticalComponentBlock = verticalComponentBlockCount, HorizontalSubsamplingFactor = horizontalSubsamplingFactor, VerticalSubsamplingFactor = verticalSubsamplingFactor, }; } // allocate an additional block to act as a dummy buffer. index = 1; for (int i = 0; i < componentAllocations.Length; i++) { componentAllocations[i].ComponentBlockOffset = index; index += componentAllocations[i].HorizontalComponentBlock * componentAllocations[i].VerticalComponentBlock; } int length = index * Unsafe.SizeOf <JpegBlock8x8>(); IMemoryOwner <byte> bufferHandle = _bufferHandle = _memoryPool.Rent(length); bufferHandle.Memory.Span.Slice(0, length).Clear(); }
public static bool TryParse(ReadOnlySpan <byte> buffer, bool metadataOnly, out JpegFrameHeader frameHeader, out int bytesConsumed) { bytesConsumed = 0; if (buffer.Length < 6) { frameHeader = default; return(false); } byte numberOfComponenets = buffer[5]; ushort samplesPerLine = (ushort)(buffer[4] | (buffer[3] << 8)); ushort numberOfLines = (ushort)(buffer[2] | (buffer[1] << 8)); byte precision = buffer[0]; buffer = buffer.Slice(6); bytesConsumed += 6; if (buffer.Length < 3 * numberOfComponenets) { frameHeader = default; return(false); } if (metadataOnly) { bytesConsumed += 3 * numberOfComponenets; frameHeader = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, null); return(true); } JpegFrameComponentSpecificationParameters[] components = new JpegFrameComponentSpecificationParameters[numberOfComponenets]; for (int i = 0; i < components.Length; i++) { if (!JpegFrameComponentSpecificationParameters.TryParse(buffer, out components[i])) { frameHeader = default; return(false); } buffer = buffer.Slice(3); bytesConsumed += 3; } frameHeader = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, components); return(true); }
public void Allocate(JpegFrameHeader frameHeader) { // 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); } ComponentAllocation[] componentAllocations = _components = new ComponentAllocation[frameHeader.NumberOfComponents]; int index = 0; foreach (JpegFrameComponentSpecificationParameters component in frameHeader.Components) { int horizontalSubsamplingFactor = maxHorizontalSampling / component.HorizontalSamplingFactor; int verticalSubsamplingFactor = maxVerticalSampling / component.VerticalSamplingFactor; int width = (frameHeader.SamplesPerLine + horizontalSubsamplingFactor - 1) / horizontalSubsamplingFactor; int height = (frameHeader.NumberOfLines + verticalSubsamplingFactor - 1) / verticalSubsamplingFactor; componentAllocations[index++] = new ComponentAllocation { Width = width, Height = height, HorizontalSubsamplingFactor = horizontalSubsamplingFactor, VerticalSubsamplingFactor = verticalSubsamplingFactor, }; } index = 0; for (int i = 0; i < componentAllocations.Length; i++) { componentAllocations[i].ComponentSampleOffset = index; index += componentAllocations[i].Width * 16; } int length = index * Unsafe.SizeOf <short>(); IMemoryOwner <byte> bufferHandle = _bufferHandle = _memoryPool.Rent(length); bufferHandle.Memory.Span.Slice(0, length).Clear(); }
internal bool ShadowEquals(JpegFrameHeader other) { return(SamplePrecision == other.SamplePrecision && NumberOfLines == other.NumberOfLines && SamplesPerLine == other.SamplesPerLine && NumberOfComponents == other.NumberOfComponents); }
private void CopyScanBaseline(ref JpegReader reader, ref JpegWriter writer, JpegScanHeader scanHeader) { JpegFrameHeader frameHeader = _frameHeader.GetValueOrDefault(); if (scanHeader.Components is null) { throw new InvalidOperationException(); } // Compute maximum sampling factor byte maxHorizontalSampling = 1; byte maxVerticalSampling = 1; foreach (JpegFrameComponentSpecificationParameters currentFrameComponent in frameHeader.Components !) { maxHorizontalSampling = Math.Max(maxHorizontalSampling, currentFrameComponent.HorizontalSamplingFactor); maxVerticalSampling = Math.Max(maxVerticalSampling, currentFrameComponent.VerticalSamplingFactor); } // Resolve each component JpegTranscodeComponent[] components = new JpegTranscodeComponent[scanHeader.NumberOfComponents]; for (int i = 0; i < scanHeader.NumberOfComponents; i++) { JpegScanComponentSpecificationParameters scanComponenet = scanHeader.Components[i]; int componentIndex = 0; JpegFrameComponentSpecificationParameters?frameComponent = null; for (int j = 0; j < frameHeader.NumberOfComponents; j++) { JpegFrameComponentSpecificationParameters currentFrameComponent = frameHeader.Components[j]; if (scanComponenet.ScanComponentSelector == currentFrameComponent.Identifier) { componentIndex = j; frameComponent = currentFrameComponent; } } if (frameComponent is null) { throw new InvalidDataException(); } components[i] = new JpegTranscodeComponent { ComponentIndex = componentIndex, HorizontalSamplingFactor = frameComponent.GetValueOrDefault().HorizontalSamplingFactor, VerticalSamplingFactor = frameComponent.GetValueOrDefault().VerticalSamplingFactor, DcTable = GetHuffmanTable(true, scanComponenet.DcEntropyCodingTableSelector), AcTable = GetHuffmanTable(false, scanComponenet.AcEntropyCodingTableSelector), DcEncodingTable = _encodingTables.GetTable(true, scanComponenet.DcEntropyCodingTableSelector), AcEncodingTable = _encodingTables.GetTable(false, scanComponenet.AcEntropyCodingTableSelector) }; } // Prepare int mcusPerLine = (frameHeader.SamplesPerLine + 8 * maxHorizontalSampling - 1) / (8 * maxHorizontalSampling); int mcusPerColumn = (frameHeader.NumberOfLines + 8 * maxVerticalSampling - 1) / (8 * maxVerticalSampling); JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); int mcusBeforeRestart = _restartInterval; bool eoiReached = false; writer.EnterBitMode(); for (int rowMcu = 0; rowMcu < mcusPerColumn && !eoiReached; rowMcu++) { for (int colMcu = 0; colMcu < mcusPerLine && !eoiReached; colMcu++) { foreach (JpegTranscodeComponent component in components) { int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; for (int y = 0; y < v; y++) { for (int x = 0; x < h; x++) { CopyBlockBaseline(ref bitReader, ref writer, component); } } } if (_restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { eoiReached = true; break; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = _restartInterval; writer.ExitBitMode(); writer.WriteMarker(marker); writer.EnterBitMode(); } } } bitReader.AdvanceAlignByte(); writer.ExitBitMode(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (eoiReached) { bytesConsumed -= 2; } else if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }