Skip to content

Commit 5909ff3

Browse files
Merge branch 'main' into js/accumulative-memory-limit
2 parents c791923 + 36334cd commit 5909ff3

32 files changed

Lines changed: 12824 additions & 3038 deletions

.github/workflows/build-and-test.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
git config --global core.longpaths true
2828
2929
- name: Git Checkout
30-
uses: actions/checkout@v4
30+
uses: actions/checkout@v6
3131
with:
3232
fetch-depth: 0
3333
submodules: recursive
@@ -137,7 +137,7 @@ jobs:
137137
git config --global core.longpaths true
138138
139139
- name: Git Checkout
140-
uses: actions/checkout@v4
140+
uses: actions/checkout@v6
141141
with:
142142
fetch-depth: 0
143143
submodules: recursive
@@ -209,7 +209,7 @@ jobs:
209209
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
210210

211211
- name: Export Failed Output
212-
uses: actions/upload-artifact@v6
212+
uses: actions/upload-artifact@v7
213213
if: failure()
214214
with:
215215
name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
@@ -227,7 +227,7 @@ jobs:
227227
git config --global core.longpaths true
228228
229229
- name: Git Checkout
230-
uses: actions/checkout@v4
230+
uses: actions/checkout@v6
231231
with:
232232
fetch-depth: 0
233233
submodules: recursive

.github/workflows/code-coverage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
git config --global core.longpaths true
3232
3333
- name: Git Checkout
34-
uses: actions/checkout@v4
34+
uses: actions/checkout@v6
3535
with:
3636
fetch-depth: 0
3737
submodules: recursive
@@ -86,7 +86,7 @@ jobs:
8686
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
8787

8888
- name: Export Failed Output
89-
uses: actions/upload-artifact@v6
89+
uses: actions/upload-artifact@v7
9090
if: failure()
9191
with:
9292
name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,5 @@ artifacts/
227227
#lfs
228228
hooks/**
229229
lfs/**
230+
231+
.dotnet

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,14 @@ SixLabors.ImageSharp
1010
[![Build Status](https://img.shields.io/github/actions/workflow/status/SixLabors/ImageSharp/build-and-test.yml?branch=main)](https://github.com/SixLabors/ImageSharp/actions)
1111
[![codecov](https://codecov.io/gh/SixLabors/ImageSharp/graph/badge.svg?token=g2WJwz770q)](https://codecov.io/gh/SixLabors/ImageSharp)
1212
[![License: Six Labors Split](https://img.shields.io/badge/license-Six%20Labors%20Split-%23e30183)](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE)
13-
[![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors)
1413

1514
</div>
1615

17-
### **ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API.
16+
### **ImageSharp** is a high-performance, fully managed, cross-platform 2D graphics API.
1817

19-
ImageSharp is a new, fully featured, fully managed, cross-platform, 2D graphics library.
20-
Designed to simplify image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API.
18+
ImageSharp is a mature, fully featured, high-performance image processing and graphics library for .NET, built for workloads across device, cloud, and embedded/IoT scenarios.
2119

22-
ImageSharp is designed from the ground up to be flexible and extensible. The library provides API endpoints for common image processing operations and the building blocks to allow for the development of additional operations.
20+
Designed from the ground up to balance performance, portability, and ease of use, ImageSharp provides a powerful yet approachable API for common image processing tasks, along with the low-level building blocks needed to extend the library for specialized workflows.
2321

2422
Built against [.NET 8](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
2523

src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,12 @@ private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out b
15481548
case BmpFileMarkerType.Bitmap:
15491549
if (this.fileHeader.HasValue)
15501550
{
1551+
if (this.fileHeader.Value.Offset > stream.Length)
1552+
{
1553+
BmpThrowHelper.ThrowInvalidImageContentException(
1554+
$"Pixel data offset {this.fileHeader.Value.Offset} exceeds file size {stream.Length}.");
1555+
}
1556+
15511557
colorMapSizeBytes = this.fileHeader.Value.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize;
15521558
}
15531559
else

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,11 @@ internal void ParseStream(BufferedReadStream stream, SpectralConverter spectralC
519519
fileMarker = FindNextFileMarker(stream);
520520
}
521521

522+
if (!metadataOnly && this.Frame is null)
523+
{
524+
JpegThrowHelper.ThrowInvalidImageContentException("No readable SOFn (Start Of Frame) marker found.");
525+
}
526+
522527
this.Metadata.GetJpegMetadata().Interleaved = this.Frame.Interleaved;
523528
}
524529

src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public PngPhysical(uint x, uint y, byte unitSpecifier)
4646
/// <returns>The parsed PhysicalChunkData.</returns>
4747
public static PngPhysical Parse(ReadOnlySpan<byte> data)
4848
{
49+
if (data.Length < 9)
50+
{
51+
PngThrowHelper.ThrowInvalidImageContentException("pHYs chunk is too short");
52+
}
53+
4954
uint hResolution = BinaryPrimitives.ReadUInt32BigEndian(data[..4]);
5055
uint vResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4));
5156
byte unit = data[8];

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,12 @@ private static void AssignColorPalette(ReadOnlySpan<byte> palette, ReadOnlySpan<
12531253
ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(palette);
12541254
Color.FromPixel(rgbTable, colorTable);
12551255

1256+
// The tRNS chunk must not contain more alpha values than there are palette entries.
1257+
if (alpha.Length > colorTable.Length)
1258+
{
1259+
alpha = alpha.Slice(0, colorTable.Length);
1260+
}
1261+
12561262
if (alpha.Length > 0)
12571263
{
12581264
// The alpha chunk may contain as many transparency entries as there are palette entries
@@ -1402,26 +1408,31 @@ private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata met
14021408
return;
14031409
}
14041410

1405-
int zeroIndex = data.IndexOf((byte)0);
1406-
if (zeroIndex is < PngConstants.MinTextKeywordLength or > PngConstants.MaxTextKeywordLength)
1411+
int keywordEnd = data.IndexOf((byte)0);
1412+
if (keywordEnd is < PngConstants.MinTextKeywordLength or > PngConstants.MaxTextKeywordLength)
14071413
{
14081414
return;
14091415
}
14101416

1411-
byte compressionMethod = data[zeroIndex + 1];
1417+
if (keywordEnd < 0 || keywordEnd + 2 > data.Length)
1418+
{
1419+
return; // Not enough data for keyword + null + compression method.
1420+
}
1421+
1422+
byte compressionMethod = data[keywordEnd + 1];
14121423
if (compressionMethod != 0)
14131424
{
14141425
// Only compression method 0 is supported (zlib datastream with deflate compression).
14151426
return;
14161427
}
14171428

1418-
ReadOnlySpan<byte> keywordBytes = data[..zeroIndex];
1429+
ReadOnlySpan<byte> keywordBytes = data[..keywordEnd];
14191430
if (!TryReadTextKeyword(keywordBytes, out string name))
14201431
{
14211432
return;
14221433
}
14231434

1424-
ReadOnlySpan<byte> compressedData = data[(zeroIndex + 2)..];
1435+
ReadOnlySpan<byte> compressedData = data[(keywordEnd + 2)..];
14251436

14261437
if (this.TryDecompressTextData(compressedData, PngConstants.Encoding, out string? uncompressed)
14271438
&& !TryReadTextChunkMetadata(baseMetadata, name, uncompressed))
@@ -1932,6 +1943,11 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan<byt
19321943
return;
19331944
}
19341945

1946+
if (zeroIndexKeyword < 0 || zeroIndexKeyword + 4 > data.Length)
1947+
{
1948+
return; // Not enough data for keyword + null + flag + method + language.
1949+
}
1950+
19351951
byte compressionFlag = data[zeroIndexKeyword + 1];
19361952
if (compressionFlag is not (0 or 1))
19371953
{
@@ -1956,6 +1972,11 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan<byt
19561972

19571973
int translatedKeywordStartIdx = langStartIdx + languageLength + 1;
19581974
int translatedKeywordLength = data[translatedKeywordStartIdx..].IndexOf((byte)0);
1975+
if (translatedKeywordLength < 0)
1976+
{
1977+
return;
1978+
}
1979+
19591980
string translatedKeyword = PngConstants.TranslatedEncoding.GetString(data.Slice(translatedKeywordStartIdx, translatedKeywordLength));
19601981

19611982
ReadOnlySpan<byte> keywordBytes = data[..zeroIndexKeyword];

src/ImageSharp/Formats/Png/PngThrowHelper.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
99
internal static class PngThrowHelper
1010
{
1111
[DoesNotReturn]
12-
public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException)
13-
=> throw new InvalidImageContentException(errorMessage, innerException);
12+
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);
1413

1514
[DoesNotReturn]
1615
public static void ThrowInvalidHeader() => throw new InvalidImageContentException("PNG Image must contain a header chunk and it must be located before any other chunks.");

src/ImageSharp/GraphicsOptions.cs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
namespace SixLabors.ImageSharp;
77

88
/// <summary>
9-
/// Options for influencing the drawing functions.
9+
/// Provides configuration for controlling how graphics operations are rendered,
10+
/// including antialiasing, pixel blending, alpha composition, and coverage thresholding.
1011
/// </summary>
1112
public class GraphicsOptions : IDeepCloneable<GraphicsOptions>
1213
{
13-
private int antialiasSubpixelDepth = 16;
14+
private float antialiasThreshold = .5F;
1415
private float blendPercentage = 1F;
1516

1617
/// <summary>
@@ -24,61 +25,62 @@ private GraphicsOptions(GraphicsOptions source)
2425
{
2526
this.AlphaCompositionMode = source.AlphaCompositionMode;
2627
this.Antialias = source.Antialias;
27-
this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth;
28+
this.AntialiasThreshold = source.AntialiasThreshold;
2829
this.BlendPercentage = source.BlendPercentage;
2930
this.ColorBlendingMode = source.ColorBlendingMode;
3031
}
3132

3233
/// <summary>
3334
/// Gets or sets a value indicating whether antialiasing should be applied.
34-
/// Defaults to true.
35+
/// When <see langword="true"/>, edges are rendered with smooth sub-pixel coverage.
36+
/// When <see langword="false"/>, coverage is snapped to binary (fully opaque or fully transparent)
37+
/// using <see cref="AntialiasThreshold"/> as the cutoff.
38+
/// Defaults to <see langword="true"/>.
3539
/// </summary>
3640
public bool Antialias { get; set; } = true;
3741

3842
/// <summary>
39-
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
40-
/// Defaults to 16.
43+
/// Gets or sets the coverage threshold used when <see cref="Antialias"/> is <see langword="false"/>.
44+
/// Pixels with antialiased coverage above this value are rendered as fully opaque;
45+
/// pixels below are discarded. Valid range is 0 to 1. Lower values preserve more
46+
/// thin features at small sizes. Defaults to <c>0.5F</c>.
4147
/// </summary>
42-
public int AntialiasSubpixelDepth
48+
public float AntialiasThreshold
4349
{
44-
get
45-
{
46-
return this.antialiasSubpixelDepth;
47-
}
50+
get => this.antialiasThreshold;
4851

4952
set
5053
{
51-
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth));
52-
this.antialiasSubpixelDepth = value;
54+
Guard.MustBeBetweenOrEqualTo(value, 0F, 1F, nameof(this.AntialiasThreshold));
55+
this.antialiasThreshold = value;
5356
}
5457
}
5558

5659
/// <summary>
57-
/// Gets or sets a value between indicating the blending percentage to apply to the drawing operation.
58-
/// Range 0..1; Defaults to 1.
60+
/// Gets or sets the blending percentage applied to the drawing operation.
61+
/// A value of <c>1.0</c> applies the operation at full strength; <c>0.0</c> makes it invisible.
62+
/// Valid range is 0 to 1. Defaults to <c>1.0F</c>.
5963
/// </summary>
6064
public float BlendPercentage
6165
{
62-
get
63-
{
64-
return this.blendPercentage;
65-
}
66+
get => this.blendPercentage;
6667

6768
set
6869
{
69-
Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage));
70+
Guard.MustBeBetweenOrEqualTo(value, 0F, 1F, nameof(this.BlendPercentage));
7071
this.blendPercentage = value;
7172
}
7273
}
7374

7475
/// <summary>
75-
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation.
76+
/// Gets or sets the color blending mode used to combine source and destination pixel colors.
7677
/// Defaults to <see cref="PixelColorBlendingMode.Normal"/>.
7778
/// </summary>
7879
public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal;
7980

8081
/// <summary>
81-
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
82+
/// Gets or sets the alpha composition mode that determines how source and destination alpha
83+
/// channels are combined using Porter-Duff operators.
8284
/// Defaults to <see cref="PixelAlphaCompositionMode.SrcOver"/>.
8385
/// </summary>
8486
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver;

0 commit comments

Comments
 (0)