Skip to content

Commit 9f0fba4

Browse files
Merge branch 'main' into js/jpeg_JFXX
2 parents 492507f + c68be04 commit 9f0fba4

File tree

17 files changed

+233
-84
lines changed

17 files changed

+233
-84
lines changed

src/ImageSharp/Formats/Pbm/BinaryDecoder.cs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ private static void ProcessBlackAndWhite<TPixel>(Configuration configuration, Bu
152152
{
153153
int width = pixels.Width;
154154
int height = pixels.Height;
155-
int startBit = 0;
156155
MemoryAllocator allocator = configuration.MemoryAllocator;
157156
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
158157
Span<L8> rowSpan = row.GetSpan();
@@ -162,23 +161,12 @@ private static void ProcessBlackAndWhite<TPixel>(Configuration configuration, Bu
162161
for (int x = 0; x < width;)
163162
{
164163
int raw = stream.ReadByte();
165-
int bit = startBit;
166-
startBit = 0;
167-
for (; bit < 8; bit++)
164+
int stopBit = Math.Min(8, width - x);
165+
for (int bit = 0; bit < stopBit; bit++)
168166
{
169167
bool bitValue = (raw & (0x80 >> bit)) != 0;
170168
rowSpan[x] = bitValue ? black : white;
171169
x++;
172-
if (x == width)
173-
{
174-
startBit = (bit + 1) & 7; // Round off to below 8.
175-
if (startBit != 0)
176-
{
177-
stream.Seek(-1, System.IO.SeekOrigin.Current);
178-
}
179-
180-
break;
181-
}
182170
}
183171
}
184172

src/ImageSharp/Formats/Pbm/BinaryEncoder.cs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,40 @@ public static void WritePixels<TPixel>(Configuration configuration, Stream strea
3333
{
3434
WriteGrayscale(configuration, stream, image);
3535
}
36-
else
36+
else if (componentType == PbmComponentType.Short)
3737
{
3838
WriteWideGrayscale(configuration, stream, image);
3939
}
40+
else
41+
{
42+
throw new ImageFormatException("Component type not supported for Grayscale PBM.");
43+
}
4044
}
4145
else if (colorType == PbmColorType.Rgb)
4246
{
4347
if (componentType == PbmComponentType.Byte)
4448
{
4549
WriteRgb(configuration, stream, image);
4650
}
47-
else
51+
else if (componentType == PbmComponentType.Short)
4852
{
4953
WriteWideRgb(configuration, stream, image);
5054
}
55+
else
56+
{
57+
throw new ImageFormatException("Component type not supported for Color PBM.");
58+
}
5159
}
5260
else
5361
{
54-
WriteBlackAndWhite(configuration, stream, image);
62+
if (componentType == PbmComponentType.Bit)
63+
{
64+
WriteBlackAndWhite(configuration, stream, image);
65+
}
66+
else
67+
{
68+
throw new ImageFormatException("Component type not supported for Black & White PBM.");
69+
}
5570
}
5671
}
5772

@@ -164,8 +179,6 @@ private static void WriteBlackAndWhite<TPixel>(Configuration configuration, Stre
164179
using IMemoryOwner<L8> row = allocator.Allocate<L8>(width);
165180
Span<L8> rowSpan = row.GetSpan();
166181

167-
int previousValue = 0;
168-
int startBit = 0;
169182
for (int y = 0; y < height; y++)
170183
{
171184
Span<TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y);
@@ -177,28 +190,19 @@ private static void WriteBlackAndWhite<TPixel>(Configuration configuration, Stre
177190

178191
for (int x = 0; x < width;)
179192
{
180-
int value = previousValue;
181-
for (int i = startBit; i < 8; i++)
193+
int value = 0;
194+
int stopBit = Math.Min(8, width - x);
195+
for (int i = 0; i < stopBit; i++)
182196
{
183197
if (rowSpan[x].PackedValue < 128)
184198
{
185199
value |= 0x80 >> i;
186200
}
187201

188202
x++;
189-
if (x == width)
190-
{
191-
previousValue = value;
192-
startBit = (i + 1) & 7; // Round off to below 8.
193-
break;
194-
}
195203
}
196204

197-
if (startBit == 0)
198-
{
199-
stream.WriteByte((byte)value);
200-
previousValue = 0;
201-
}
205+
stream.WriteByte((byte)value);
202206
}
203207
}
204208
}

src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,29 @@ public bool ReadNextCodeWord()
125125
if (value == Len7Code0000000.Code)
126126
{
127127
this.Code = Len7Code0000000;
128-
return false;
128+
129+
// We do not support Extensions1D codes, but some encoders (scanner from epson) write a premature EOL code,
130+
// which at this point cannot be distinguished from the marker, because we read the data bit by bit.
131+
// Read the next 5 bit, if its a EOL code return true, indicating its the end of the image.
132+
if (this.ReadValue(5) == 1)
133+
{
134+
return true;
135+
}
136+
137+
throw new NotSupportedException("ccitt extensions 1D codes are not supported.");
129138
}
130139

131140
if (value == Len7Code0000001.Code)
132141
{
133142
this.Code = Len7Code0000001;
134-
return false;
143+
144+
// Same as above, we do not support Extensions2D codes, but it could be a EOL instead.
145+
if (this.ReadValue(5) == 1)
146+
{
147+
return true;
148+
}
149+
150+
throw new NotSupportedException("ccitt extensions 2D codes are not supported.");
135151
}
136152

137153
if (value == Len7Code0000011.Code)

src/ImageSharp/IO/IFileSystem.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

44
namespace SixLabors.ImageSharp.IO;
@@ -9,16 +9,32 @@ namespace SixLabors.ImageSharp.IO;
99
internal interface IFileSystem
1010
{
1111
/// <summary>
12-
/// Returns a readable stream as defined by the path.
12+
/// Opens a file as defined by the path and returns it as a readable stream.
1313
/// </summary>
1414
/// <param name="path">Path to the file to open.</param>
15-
/// <returns>A stream representing the file to open.</returns>
15+
/// <returns>A stream representing the opened file.</returns>
1616
Stream OpenRead(string path);
1717

1818
/// <summary>
19-
/// Creates or opens a file and returns it as a writable stream as defined by the path.
19+
/// Opens a file as defined by the path and returns it as a readable stream
20+
/// that can be used for asynchronous reading.
2021
/// </summary>
2122
/// <param name="path">Path to the file to open.</param>
22-
/// <returns>A stream representing the file to open.</returns>
23+
/// <returns>A stream representing the opened file.</returns>
24+
Stream OpenReadAsynchronous(string path);
25+
26+
/// <summary>
27+
/// Creates or opens a file as defined by the path and returns it as a writable stream.
28+
/// </summary>
29+
/// <param name="path">Path to the file to open.</param>
30+
/// <returns>A stream representing the opened file.</returns>
2331
Stream Create(string path);
32+
33+
/// <summary>
34+
/// Creates or opens a file as defined by the path and returns it as a writable stream
35+
/// that can be used for asynchronous reading and writing.
36+
/// </summary>
37+
/// <param name="path">Path to the file to open.</param>
38+
/// <returns>A stream representing the opened file.</returns>
39+
Stream CreateAsynchronous(string path);
2440
}

src/ImageSharp/IO/LocalFileSystem.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

44
namespace SixLabors.ImageSharp.IO;
@@ -11,6 +11,24 @@ internal sealed class LocalFileSystem : IFileSystem
1111
/// <inheritdoc/>
1212
public Stream OpenRead(string path) => File.OpenRead(path);
1313

14+
/// <inheritdoc/>
15+
public Stream OpenReadAsynchronous(string path) => File.Open(path, new FileStreamOptions
16+
{
17+
Mode = FileMode.Open,
18+
Access = FileAccess.Read,
19+
Share = FileShare.Read,
20+
Options = FileOptions.Asynchronous,
21+
});
22+
1423
/// <inheritdoc/>
1524
public Stream Create(string path) => File.Create(path);
25+
26+
/// <inheritdoc/>
27+
public Stream CreateAsynchronous(string path) => File.Open(path, new FileStreamOptions
28+
{
29+
Mode = FileMode.Create,
30+
Access = FileAccess.ReadWrite,
31+
Share = FileShare.None,
32+
Options = FileOptions.Asynchronous,
33+
});
1634
}

src/ImageSharp/Image.FromFile.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public static async Task<IImageFormat> DetectFormatAsync(
7272
{
7373
Guard.NotNull(options, nameof(options));
7474

75-
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
75+
await using Stream stream = options.Configuration.FileSystem.OpenReadAsynchronous(path);
7676
return await DetectFormatAsync(options, stream, cancellationToken).ConfigureAwait(false);
7777
}
7878

@@ -144,7 +144,7 @@ public static async Task<ImageInfo> IdentifyAsync(
144144
CancellationToken cancellationToken = default)
145145
{
146146
Guard.NotNull(options, nameof(options));
147-
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
147+
await using Stream stream = options.Configuration.FileSystem.OpenReadAsynchronous(path);
148148
return await IdentifyAsync(options, stream, cancellationToken).ConfigureAwait(false);
149149
}
150150

@@ -214,7 +214,7 @@ public static async Task<Image> LoadAsync(
214214
string path,
215215
CancellationToken cancellationToken = default)
216216
{
217-
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
217+
await using Stream stream = options.Configuration.FileSystem.OpenReadAsynchronous(path);
218218
return await LoadAsync(options, stream, cancellationToken).ConfigureAwait(false);
219219
}
220220

@@ -291,7 +291,7 @@ public static async Task<Image<TPixel>> LoadAsync<TPixel>(
291291
Guard.NotNull(options, nameof(options));
292292
Guard.NotNull(path, nameof(path));
293293

294-
using Stream stream = options.Configuration.FileSystem.OpenRead(path);
294+
await using Stream stream = options.Configuration.FileSystem.OpenReadAsynchronous(path);
295295
return await LoadAsync<TPixel>(options, stream, cancellationToken).ConfigureAwait(false);
296296
}
297297
}

src/ImageSharp/ImageExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public static async Task SaveAsync(
7070
Guard.NotNull(path, nameof(path));
7171
Guard.NotNull(encoder, nameof(encoder));
7272

73-
using Stream fs = source.GetConfiguration().FileSystem.Create(path);
73+
await using Stream fs = source.GetConfiguration().FileSystem.CreateAsynchronous(path);
7474
await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
7575
}
7676

tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public void ImageLoadRgb24CanDecode(string imagePath)
8181
[Theory]
8282
[WithFile(BlackAndWhitePlain, PixelTypes.L8, "pbm")]
8383
[WithFile(BlackAndWhiteBinary, PixelTypes.L8, "pbm")]
84+
[WithFile(Issue2477, PixelTypes.L8, "pbm")]
8485
[WithFile(GrayscalePlain, PixelTypes.L8, "pgm")]
8586
[WithFile(GrayscalePlainNormalized, PixelTypes.L8, "pgm")]
8687
[WithFile(GrayscaleBinary, PixelTypes.L8, "pgm")]

tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class PbmEncoderTests
2626
{
2727
{ BlackAndWhiteBinary, PbmColorType.BlackAndWhite },
2828
{ BlackAndWhitePlain, PbmColorType.BlackAndWhite },
29+
{ Issue2477, PbmColorType.BlackAndWhite },
2930
{ GrayscaleBinary, PbmColorType.Grayscale },
3031
{ GrayscaleBinaryWide, PbmColorType.Grayscale },
3132
{ GrayscalePlain, PbmColorType.Grayscale },
@@ -96,6 +97,11 @@ public void PbmEncoder_P1_Works<TPixel>(TestImageProvider<TPixel> provider)
9697
public void PbmEncoder_P4_Works<TPixel>(TestImageProvider<TPixel> provider)
9798
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.BlackAndWhite, PbmEncoding.Binary);
9899

100+
[Theory]
101+
[WithFile(Issue2477, PixelTypes.Rgb24)]
102+
public void PbmEncoder_P4_Irregular_Works<TPixel>(TestImageProvider<TPixel> provider)
103+
where TPixel : unmanaged, IPixel<TPixel> => TestPbmEncoderCore(provider, PbmColorType.BlackAndWhite, PbmEncoding.Binary);
104+
99105
[Theory]
100106
[WithFile(GrayscalePlainMagick, PixelTypes.Rgb24)]
101107
public void PbmEncoder_P2_Works<TPixel>(TestImageProvider<TPixel> provider)

0 commit comments

Comments
 (0)