From ff9d3a6223c72ca1c5931593e404ce0070db9d9c Mon Sep 17 00:00:00 2001 From: Maxim Shipko Date: Fri, 6 Jun 2025 19:01:01 +0400 Subject: [PATCH] Replace indexed span iteration with foreach (+performance) --- .../Webp/Lossless/BackwardReferenceEncoder.cs | 14 +++++--------- src/ImageSharp/Formats/Webp/Lossless/CostModel.cs | 4 ++-- .../Formats/Webp/Lossless/HistogramEncoder.cs | 9 ++++----- .../Formats/Webp/Lossless/Vp8LBackwardRefs.cs | 11 +++++------ .../Formats/Webp/Lossless/Vp8LEncoder.cs | 3 +-- .../Formats/Webp/Lossless/Vp8LHistogram.cs | 4 ++-- 6 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs index 7a26791730..274d4426f9 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs @@ -149,9 +149,8 @@ internal static class BackwardReferenceEncoder } // Find the cacheBits giving the lowest entropy. - for (int idx = 0; idx < refs.Count; idx++) + foreach (PixOrCopy v in refs) { - PixOrCopy v = refs[idx]; if (v.IsLiteral()) { uint pix = bgra[pos++]; @@ -780,9 +779,8 @@ internal static class BackwardReferenceEncoder { int pixelIndex = 0; ColorCache colorCache = new(cacheBits); - for (int idx = 0; idx < refs.Count; idx++) + foreach (ref PixOrCopy v in refs) { - PixOrCopy v = refs[idx]; if (v.IsLiteral()) { uint bgraLiteral = v.BgraOrDistance; @@ -790,7 +788,7 @@ internal static class BackwardReferenceEncoder if (ix >= 0) { // Color cache contains bgraLiteral - refs[idx] = PixOrCopy.CreateCacheIdx(ix); + v = PixOrCopy.CreateCacheIdx(ix); } else { @@ -812,15 +810,13 @@ internal static class BackwardReferenceEncoder private static void BackwardReferences2DLocality(int xSize, Vp8LBackwardRefs refs) { - for (int idx = 0; idx < refs.Count; idx++) + foreach (ref PixOrCopy v in refs) { - PixOrCopy v = refs[idx]; - if (v.IsCopy()) { int dist = (int)v.BgraOrDistance; int transformedDist = DistanceToPlaneCode(xSize, dist); - refs[idx] = PixOrCopy.CreateCopy((uint)transformedDist, v.Len); + v = PixOrCopy.CreateCopy((uint)transformedDist, v.Len); } } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/CostModel.cs b/src/ImageSharp/Formats/Webp/Lossless/CostModel.cs index 15b0f42229..56a5bb7f59 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/CostModel.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/CostModel.cs @@ -40,9 +40,9 @@ internal class CostModel using OwnedVp8LHistogram histogram = OwnedVp8LHistogram.Create(this.memoryAllocator, cacheBits); // The following code is similar to HistogramCreate but converts the distance to plane code. - for (int i = 0; i < backwardRefs.Count; i++) + foreach (PixOrCopy v in backwardRefs) { - histogram.AddSinglePixOrCopy(backwardRefs[i], true, xSize); + histogram.AddSinglePixOrCopy(v, true, xSize); } ConvertPopulationCountTableToBitEstimates(histogram.NumCodes(), histogram.Literal, this.Literal); diff --git a/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs index c5d1aa1454..b60663fb60 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs @@ -110,9 +110,8 @@ internal static class HistogramEncoder int x = 0, y = 0; int histoXSize = LosslessUtils.SubSampleSize(xSize, histoBits); - for (int i = 0; i < backwardRefs.Count; i++) + foreach (PixOrCopy v in backwardRefs) { - PixOrCopy v = backwardRefs[i]; int ix = ((y >> histoBits) * histoXSize) + (x >> histoBits); histograms[ix].AddSinglePixOrCopy(v, false); x += v.Len; @@ -217,7 +216,7 @@ internal static class HistogramEncoder clusterMappings[idx] = (ushort)idx; } - List indicesToRemove = new(); + List indicesToRemove = []; Vp8LStreaks stats = new(); Vp8LBitEntropy bitsEntropy = new(); for (int idx = 0; idx < histograms.Count; idx++) @@ -345,7 +344,7 @@ internal static class HistogramEncoder // Priority list of histogram pairs. Its size impacts the quality of the compression and the speed: // the smaller the faster but the worse for the compression. - List histoPriorityList = new(); + List histoPriorityList = []; const int maxSize = 9; // Fill the initial mapping. @@ -480,7 +479,7 @@ internal static class HistogramEncoder int histoSize = histograms.Count(h => h != null); // Priority list of histogram pairs. - List histoPriorityList = new(); + List histoPriorityList = []; int maxSize = histoSize * histoSize; Vp8LStreaks stats = new(); Vp8LBitEntropy bitsEntropy = new(); diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LBackwardRefs.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LBackwardRefs.cs index 6a28699955..634fac5e82 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LBackwardRefs.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LBackwardRefs.cs @@ -9,20 +9,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless; internal class Vp8LBackwardRefs : IDisposable { private readonly IMemoryOwner refs; + private int count; public Vp8LBackwardRefs(MemoryAllocator memoryAllocator, int pixels) { this.refs = memoryAllocator.Allocate(pixels); - this.Count = 0; + this.count = 0; } - public int Count { get; private set; } + public void Add(PixOrCopy pixOrCopy) => this.refs.Memory.Span[this.count++] = pixOrCopy; - public ref PixOrCopy this[int index] => ref this.refs.Memory.Span[index]; + public void Clear() => this.count = 0; - public void Add(PixOrCopy pixOrCopy) => this.refs.Memory.Span[this.Count++] = pixOrCopy; - - public void Clear() => this.Count = 0; + public Span.Enumerator GetEnumerator() => this.refs.Slice(0, this.count).GetEnumerator(); /// public void Dispose() => this.refs.Dispose(); diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index e19126fff1..b398554eb1 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -1058,9 +1058,8 @@ internal class Vp8LEncoder : IDisposable int histogramIx = histogramSymbols[0]; Span codes = huffmanCodes.AsSpan(5 * histogramIx); - for (int i = 0; i < backwardRefs.Count; i++) + foreach (PixOrCopy v in backwardRefs) { - PixOrCopy v = backwardRefs[i]; if (tileX != (x & tileMask) || tileY != (y & tileMask)) { tileX = x & tileMask; diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs index ee03499cfc..399af7661d 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs @@ -138,9 +138,9 @@ internal abstract unsafe class Vp8LHistogram /// The backward references. public void StoreRefs(Vp8LBackwardRefs refs) { - for (int i = 0; i < refs.Count; i++) + foreach (PixOrCopy v in refs) { - this.AddSinglePixOrCopy(refs[i], false); + this.AddSinglePixOrCopy(v, false); } }