diff --git a/benchmark/CharLS.Managed.Benchmark.csproj b/benchmark/CharLS.Managed.Benchmark.csproj
index 84b1fd6..135f6d0 100644
--- a/benchmark/CharLS.Managed.Benchmark.csproj
+++ b/benchmark/CharLS.Managed.Benchmark.csproj
@@ -14,8 +14,9 @@
True
- $(NoWarn),1591
+ $(NoWarn),1591,CA1515
diff --git a/benchmark/PortableAnymapFile.cs b/benchmark/PortableAnymapFile.cs
index 5bfc825..73d9886 100644
--- a/benchmark/PortableAnymapFile.cs
+++ b/benchmark/PortableAnymapFile.cs
@@ -113,7 +113,7 @@ public override string ReadLine()
byte b = (byte)current;
bytes.Add(b);
}
- return Encoding.ASCII.GetString(bytes.ToArray());
+ return Encoding.ASCII.GetString([.. bytes]);
}
// Read works differently than the `Read()` method of a
diff --git a/exclusion.dic b/exclusion.dic
index af7fb3d..7d41bc6 100644
--- a/exclusion.dic
+++ b/exclusion.dic
@@ -10,3 +10,4 @@ opto
palletised
glimit
Golomb
+Errval
diff --git a/global.json b/global.json
index 55ccd56..41c9ad2 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.400",
+ "version": "9.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
}
diff --git a/src/JpegLSDecoder.cs b/src/JpegLSDecoder.cs
index aed06df..c6d74c4 100644
--- a/src/JpegLSDecoder.cs
+++ b/src/JpegLSDecoder.cs
@@ -375,7 +375,7 @@ public void Decode(Span destination, int stride = AutoCalculateStride)
{
int scanStride = CheckStrideAndDestinationLength(destination.Length, stride);
_scanDecoder = new ScanDecoder(_reader.ScanFrameInfo, _reader.GetValidatedPresetCodingParameters(), _reader.GetCodingParameters());
- int bytesRead = _scanDecoder.DecodeScan(_reader.RemainingSource(), destination, scanStride);
+ int bytesRead = _scanDecoder.DecodeScan(_reader.RemainingSource(), destination, scanStride, _reader.Widths, _reader.Heights);
_reader.AdvancePosition(bytesRead);
component += _reader.ScanComponentCount;
diff --git a/src/JpegLSEncoder.cs b/src/JpegLSEncoder.cs
index e3504c2..6e99994 100644
--- a/src/JpegLSEncoder.cs
+++ b/src/JpegLSEncoder.cs
@@ -20,7 +20,6 @@ public sealed class JpegLSEncoder
private JpegStreamWriter _writer;
private ScanEncoder _scanEncoder;
- private FrameInfo _frameInfo;
private int _nearLossless;
private InterleaveMode _interleaveMode;
private ColorTransformation _colorTransformation;
@@ -93,18 +92,7 @@ private enum State
///
/// The frame information of the image.
///
- /// Thrown when the passed FrameInfo is invalid.
- /// Thrown when the passed FrameInfo instance is null.
- public FrameInfo FrameInfo
- {
- get => _frameInfo;
-
- set
- {
- ArgumentNullException.ThrowIfNull(value);
- _frameInfo = value;
- }
- }
+ public FrameInfo FrameInfo { get; set; }
///
/// Gets or sets the near lossless parameter to be used to encode the JPEG-LS stream.
diff --git a/src/JpegStreamReader.cs b/src/JpegStreamReader.cs
index 29d2839..41000a4 100644
--- a/src/JpegStreamReader.cs
+++ b/src/JpegStreamReader.cs
@@ -30,6 +30,8 @@ internal struct JpegStreamReader
private int _verticalSamplingMax = 1;
private bool _dnlMarkerExpected;
private bool _componentWithMappingTableExists;
+ private int[]? _widths;
+ private int[]? _heights;
public JpegStreamReader()
: this(null!)
@@ -99,6 +101,10 @@ internal readonly uint MaximumSampleValue
}
}
+ internal readonly int[] Widths => _widths!;
+
+ internal readonly int[] Heights => _heights!;
+
private readonly int SegmentBytesToRead => _segmentStartPosition + _segmentDataSize - Position;
internal readonly int GetMappingTableId(int componentIndex)
@@ -494,9 +500,29 @@ private void ReadStartOfFrameSegment()
SkipByte(); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0)
}
+ ComputeWidths();
+ ComputeHeights();
_state = State.ScanSection;
}
+ private void ComputeWidths()
+ {
+ _widths = new int[_componentCount];
+ for (int i = 0; i < _componentCount; i++)
+ {
+ _widths[i] = GetScanWidth(i);
+ }
+ }
+
+ private void ComputeHeights()
+ {
+ _heights = new int[_componentCount];
+ for (int i = 0; i < _componentCount; i++)
+ {
+ _heights[i] = GetScanHeight(i);
+ }
+ }
+
private void ReadApplicationDataSegment(JpegMarkerCode markerCode)
{
RaiseApplicationDataEvent(markerCode);
diff --git a/src/ScanDecoder.cs b/src/ScanDecoder.cs
index 93f8b99..aeaee81 100644
--- a/src/ScanDecoder.cs
+++ b/src/ScanDecoder.cs
@@ -52,7 +52,7 @@ internal ScanDecoder(FrameInfo frameInfo, JpegLSPresetCodingParameters presetCod
private readonly int PixelStride => FrameInfo.Width + 2;
- internal int DecodeScan(ReadOnlyMemory source, Span destination, int stride)
+ internal int DecodeScan(ReadOnlyMemory source, Span destination, int stride, Span widths, Span heights)
{
Initialize(source);
@@ -61,11 +61,11 @@ internal int DecodeScan(ReadOnlyMemory source, Span destination, int
if (FrameInfo.BitsPerSample <= 8)
{
- DecodeLines8Bit(destination, stride);
+ DecodeLines8Bit(destination, stride, widths, heights);
}
else
{
- DecodeLines16Bit(destination, stride);
+ DecodeLines16Bit(destination, stride, widths);
}
EndScan();
@@ -352,7 +352,7 @@ private bool FillReadCacheOptimistic()
return true;
}
- private void DecodeLines8Bit(Span destination, int stride)
+ private void DecodeLines8Bit(Span destination, int stride, Span widths, Span heights)
{
switch (CodingParameters.InterleaveMode)
{
@@ -361,7 +361,7 @@ private void DecodeLines8Bit(Span destination, int stride)
break;
case InterleaveMode.Line:
- DecodeLines8BitInterleaveModeLine(destination, stride);
+ DecodeLines8BitInterleaveModeLine(destination, stride, widths, heights);
break;
case InterleaveMode.Sample:
@@ -382,7 +382,7 @@ private void DecodeLines8Bit(Span destination, int stride)
}
}
- private void DecodeLines16Bit(Span destination, int stride)
+ private void DecodeLines16Bit(Span destination, int stride, Span widths)
{
switch (CodingParameters.InterleaveMode)
{
@@ -391,7 +391,7 @@ private void DecodeLines16Bit(Span destination, int stride)
break;
case InterleaveMode.Line:
- DecodeLines16BitInterleaveModeLine(destination, stride);
+ DecodeLines16BitInterleaveModeLine(destination, stride, widths);
break;
case InterleaveMode.Sample:
@@ -438,7 +438,7 @@ private void DecodeLines8BitInterleaveModeNone(Span destination, int strid
previousLine[FrameInfo.Width + 1] = previousLine[FrameInfo.Width];
currentLine[0] = previousLine[1];
- DecodeSampleLine(previousLine, currentLine);
+ DecodeSampleLine(previousLine, currentLine, FrameInfo.Width);
CopyLineBufferToDestinationInterleaveNone(currentLine[1..], destination, FrameInfo.Width);
@@ -489,7 +489,7 @@ private void DecodeLines16BitInterleaveModeNone(Span destination, int stri
previousLine[FrameInfo.Width + 1] = previousLine[FrameInfo.Width];
currentLine[0] = previousLine[1];
- DecodeSampleLine(previousLine, currentLine);
+ DecodeSampleLine(previousLine, currentLine, FrameInfo.Width);
CopyLineBufferToDestinationInterleaveNone(currentLine[1..], destination, FrameInfo.Width);
@@ -515,7 +515,7 @@ private void DecodeLines16BitInterleaveModeNone(Span destination, int stri
}
}
- private void DecodeLines8BitInterleaveModeLine(Span destination, int stride)
+ private void DecodeLines8BitInterleaveModeLine(Span destination, int stride, Span widths, Span heights)
{
int pixelStride = FrameInfo.Width + 2;
int componentCount = FrameInfo.ComponentCount;
@@ -544,10 +544,13 @@ private void DecodeLines8BitInterleaveModeLine(Span destination, int strid
_scanCodec.RunIndex = runIndex[component];
// Initialize edge pixels used for prediction
- previousLine[FrameInfo.Width + 1] = previousLine[FrameInfo.Width];
+ previousLine[widths[component] + 1] = previousLine[widths[component]];
currentLine[0] = previousLine[1];
- DecodeSampleLine(previousLine, currentLine);
+ if (line + mcu < heights[component])
+ {
+ DecodeSampleLine(previousLine, currentLine, widths[component]);
+ }
runIndex[component] = _scanCodec.RunIndex;
currentLine = currentLine[pixelStride..];
@@ -580,7 +583,7 @@ private void DecodeLines8BitInterleaveModeLine(Span destination, int strid
}
}
- private void DecodeLines16BitInterleaveModeLine(Span destination, int stride)
+ private void DecodeLines16BitInterleaveModeLine(Span destination, int stride, Span widths)
{
int pixelStride = FrameInfo.Width + 2;
int componentCount = FrameInfo.ComponentCount;
@@ -612,7 +615,7 @@ private void DecodeLines16BitInterleaveModeLine(Span destination, int stri
previousLine[FrameInfo.Width + 1] = previousLine[FrameInfo.Width];
currentLine[0] = previousLine[1];
- DecodeSampleLine(previousLine, currentLine);
+ DecodeSampleLine(previousLine, currentLine, widths[component]);
runIndex[component] = _scanCodec.RunIndex;
currentLine = currentLine[pixelStride..];
@@ -957,13 +960,13 @@ private readonly int QuantizeGradient(int di)
return _scanCodec.QuantizationLut[(_scanCodec.QuantizationLut.Length / 2) + di];
}
- private void DecodeSampleLine(Span previousLine, Span currentLine)
+ private void DecodeSampleLine(Span previousLine, Span currentLine, int width)
{
int index = 1;
int rb = previousLine[0];
int rd = previousLine[index];
- while (index <= FrameInfo.Width)
+ while (index <= width)
{
int ra = currentLine[index - 1];
int rc = rb;
@@ -974,7 +977,7 @@ private void DecodeSampleLine(Span previousLine, Span currentLine)
QuantizeGradient(rd - rb), QuantizeGradient(rb - rc), QuantizeGradient(rc - ra));
if (qs == 0)
{
- index += DecodeRunMode(index, previousLine, currentLine);
+ index += DecodeRunMode(index, previousLine, currentLine, width);
rb = previousLine[index - 1];
rd = previousLine[index];
}
@@ -986,13 +989,13 @@ private void DecodeSampleLine(Span previousLine, Span currentLine)
}
}
- private void DecodeSampleLine(Span previousLine, Span currentLine)
+ private void DecodeSampleLine(Span previousLine, Span currentLine, int width)
{
int index = 1;
int rb = previousLine[0];
int rd = previousLine[index];
- while (index <= FrameInfo.Width)
+ while (index <= width)
{
int ra = currentLine[index - 1];
int rc = rb;
@@ -1236,14 +1239,14 @@ private int DecodeRegular(int qs, int predicted)
return Traits.ComputeReconstructedSample(correctedPrediction, errorValue);
}
- private int DecodeRunMode(int startIndex, Span previousLine, Span currentLine)
+ private int DecodeRunMode(int startIndex, Span previousLine, Span currentLine, int width)
{
var ra = currentLine[startIndex - 1];
- int runLength = DecodeRunPixels(ra, currentLine[startIndex..], FrameInfo.Width - (startIndex - 1));
+ int runLength = DecodeRunPixels(ra, currentLine[startIndex..], width - (startIndex - 1));
int endIndex = startIndex + runLength;
- if (endIndex - 1 == FrameInfo.Width)
+ if (endIndex - 1 == width)
return endIndex - startIndex;
// Run interruption
diff --git a/test/CharLS.Managed.Test.csproj b/test/CharLS.Managed.Test.csproj
index 07db514..e205f37 100644
--- a/test/CharLS.Managed.Test.csproj
+++ b/test/CharLS.Managed.Test.csproj
@@ -62,6 +62,12 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
diff --git a/test/ComplianceTest.cs b/test/ComplianceTest.cs
index 10096f2..4a18ae9 100644
--- a/test/ComplianceTest.cs
+++ b/test/ComplianceTest.cs
@@ -47,6 +47,26 @@ public void DecodeEncodeColor8BitInterleaveSampleNearLossless3()
DecodeEncodeFile("conformance/t8c2e3.jls", "conformance/test8.ppm");
}
+ ////[Fact]
+ ////public void DecodeEncodeColor8BitInterleaveLineLosslessSubSampled()
+ ////{
+ //// // ISO 14495-1: official test image 7
+ //// //DecodeEncodeFile("conformance/t8sse0.jls", "conformance/test8.ppm");
+
+ //// var encodedSource = Util.ReadFile("conformance/t8sse0.jls");
+
+ //// JpegLSDecoder decoder = new(encodedSource);
+
+ //// var destination = new byte[decoder.GetDestinationSize()];
+ //// decoder.Decode(destination);
+
+ //// //JpegLSDecoder decoder = new(encodedSource);
+
+ //// //var referenceFile = Util.ReadAnymapReferenceFile(rawFilename, decoder.GetInterleaveMode(), decoder.FrameInfo);
+
+ //// //Util.TestCompliance(encodedSource, referenceFile.ImageData, checkEncode);
+ ////}
+
[Fact]
public void DecodeEncodeColor8BitInterleaveNoneLosslessNonDefault()
{
diff --git a/test/EncodeTest.cs b/test/EncodeTest.cs
index f835b98..86edc7a 100644
--- a/test/EncodeTest.cs
+++ b/test/EncodeTest.cs
@@ -439,31 +439,31 @@ public void EncodeSubSamplingInterleaveNone()
CheckOutput(component2, destination[(2 * 2 * 2)..], decoder, 1, 2 * 1);
}
- [Fact]
- public void EncodeSubSamplingInterleaveLine()
- {
- JpegLSEncoder encoder = new() { FrameInfo = new FrameInfo(2, 2, 8, 3), InterleaveMode = InterleaveMode.None };
+ ////[Fact]
+ ////public void EncodeSubSamplingInterleaveLine()
+ ////{
+ //// JpegLSEncoder encoder = new() { FrameInfo = new FrameInfo(2, 2, 8, 3), InterleaveMode = InterleaveMode.None };
- Memory encodedData = new byte[encoder.EstimatedDestinationSize];
- encoder.Destination = encodedData;
+ //// Memory encodedData = new byte[encoder.EstimatedDestinationSize];
+ //// encoder.Destination = encodedData;
- byte[] components = [24, 25, 26, 23, 0, 0, 22, 0, 0, 21, 0, 0];
+ //// byte[] components = [24, 25, 26, 23, 0, 0, 22, 0, 0, 21, 0, 0];
- encoder.SetSamplingFactor(0, 2, 2);
- encoder.SetSamplingFactor(1, 1, 1);
- encoder.SetSamplingFactor(2, 1, 1);
- encoder.InterleaveMode = InterleaveMode.Line;
- encoder.Encode(components);
+ //// encoder.SetSamplingFactor(0, 2, 2);
+ //// encoder.SetSamplingFactor(1, 1, 1);
+ //// encoder.SetSamplingFactor(2, 1, 1);
+ //// encoder.InterleaveMode = InterleaveMode.Line;
+ //// encoder.Encode(components);
- JpegLSDecoder decoder = new() { Source = encoder.EncodedData };
- decoder.ReadHeader();
+ //// JpegLSDecoder decoder = new() { Source = encoder.EncodedData };
+ //// decoder.ReadHeader();
- Span destination = new byte[decoder.GetDestinationSize()];
- decoder.Decode(destination);
+ //// Span destination = new byte[decoder.GetDestinationSize()];
+ //// decoder.Decode(destination);
- //CheckOutput(component0, destination, decoder, 1, 2 * 2);
- //CheckOutput(component1And2, destination[(2 * 2)..], decoder, 1, 1 * 2);
- }
+ //// //CheckOutput(component0, destination, decoder, 1, 2 * 2);
+ //// //CheckOutput(component1And2, destination[(2 * 2)..], decoder, 1, 1 * 2);
+ ////}
[Fact]
public void EncodeSubSamplingInterleaveSample()
diff --git a/test/PortableAnymapFile.cs b/test/PortableAnymapFile.cs
index 717bb16..a2ff2e8 100644
--- a/test/PortableAnymapFile.cs
+++ b/test/PortableAnymapFile.cs
@@ -104,7 +104,7 @@ public override string ReadLine()
byte b = (byte)current;
bytes.Add(b);
}
- return Encoding.ASCII.GetString(bytes.ToArray());
+ return Encoding.ASCII.GetString([.. bytes]);
}
// Read works differently than the `Read()` method of a