diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs
index d49653a054..ffd9d6cf36 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
@@ -11,22 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
private const uint NonTrivialSym = 0xffffffff;
- ///
- /// Size of histogram used by CollectHistogram.
- ///
- private const int MaxCoeffThresh = 31;
-
- private int maxValue;
-
- private int lastNonZero;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public Vp8LHistogram()
- {
- }
-
///
/// Initializes a new instance of the class.
///
@@ -317,114 +300,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return true;
}
- public void CollectHistogram(Span reference, Span pred, int startBlock, int endBlock)
- {
- int j;
- var distribution = new int[MaxCoeffThresh + 1];
- for (j = startBlock; j < endBlock; ++j)
- {
- var output = new short[16];
-
- this.Vp8FTransform(reference.Slice(WebpLookupTables.Vp8DspScan[j]), pred.Slice(WebpLookupTables.Vp8DspScan[j]), output);
-
- // Convert coefficients to bin.
- for (int k = 0; k < 16; ++k)
- {
- int v = Math.Abs(output[k]) >> 3;
- int clippedValue = ClipMax(v, MaxCoeffThresh);
- ++distribution[clippedValue];
- }
- }
-
- this.SetHistogramData(distribution);
- }
-
- public int GetAlpha()
- {
- // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer
- // values which happen to be mostly noise. This leaves the maximum precision
- // for handling the useful small values which contribute most.
- int maxValue = this.maxValue;
- int lastNonZero = this.lastNonZero;
- int alpha = (maxValue > 1) ? WebpConstants.AlphaScale * lastNonZero / maxValue : 0;
- return alpha;
- }
-
- public void Merge(Vp8LHistogram other)
- {
- if (this.maxValue > other.maxValue)
- {
- other.maxValue = this.maxValue;
- }
-
- if (this.lastNonZero > other.lastNonZero)
- {
- other.lastNonZero = this.lastNonZero;
- }
- }
-
- private void SetHistogramData(int[] distribution)
- {
- int maxValue = 0;
- int lastNonZero = 1;
- for (int k = 0; k <= MaxCoeffThresh; ++k)
- {
- int value = distribution[k];
- if (value > 0)
- {
- if (value > maxValue)
- {
- maxValue = value;
- }
-
- lastNonZero = k;
- }
- }
-
- this.maxValue = maxValue;
- this.lastNonZero = lastNonZero;
- }
-
- private void Vp8FTransform(Span src, Span reference, Span output)
- {
- int i;
- var tmp = new int[16];
- for (i = 0; i < 4; ++i)
- {
- int d0 = src[0] - reference[0]; // 9bit dynamic range ([-255,255])
- int d1 = src[1] - reference[1];
- int d2 = src[2] - reference[2];
- int d3 = src[3] - reference[3];
- int a0 = d0 + d3; // 10b [-510,510]
- int a1 = d1 + d2;
- int a2 = d1 - d2;
- int a3 = d0 - d3;
- tmp[0 + (i * 4)] = (a0 + a1) * 8; // 14b [-8160,8160]
- tmp[1 + (i * 4)] = ((a2 * 2217) + (a3 * 5352) + 1812) >> 9; // [-7536,7542]
- tmp[2 + (i * 4)] = (a0 - a1) * 8;
- tmp[3 + (i * 4)] = ((a3 * 2217) - (a2 * 5352) + 937) >> 9;
-
- // Do not change the span in the last iteration.
- if (i < 3)
- {
- src = src.Slice(WebpConstants.Bps);
- reference = reference.Slice(WebpConstants.Bps);
- }
- }
-
- for (i = 0; i < 4; ++i)
- {
- int a0 = tmp[0 + i] + tmp[12 + i]; // 15b
- int a1 = tmp[4 + i] + tmp[8 + i];
- int a2 = tmp[4 + i] - tmp[8 + i];
- int a3 = tmp[0 + i] - tmp[12 + i];
- output[0 + i] = (short)((a0 + a1 + 7) >> 4); // 12b
- output[4 + i] = (short)((((a2 * 2217) + (a3 * 5352) + 12000) >> 16) + ((a3 != 0) ? 1 : 0));
- output[8 + i] = (short)((a0 - a1 + 7) >> 4);
- output[12 + i] = (short)(((a3 * 2217) - (a2 * 5352) + 51000) >> 16);
- }
- }
-
private void AddLiteral(Vp8LHistogram b, Vp8LHistogram output, int literalSize)
{
if (this.IsUsed[0])
@@ -636,8 +511,5 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
output[i] = a[i] + b[i];
}
}
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private static int ClipMax(int v, int max) => (v > max) ? max : v;
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
index 340263f7ef..ac582fd424 100644
--- a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
+++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using SixLabors.ImageSharp.Formats.Webp.Lossless;
+using SixLabors.ImageSharp.Formats.WebP.Lossy;
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
@@ -383,7 +383,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.MakeLuma16Preds();
for (mode = 0; mode < maxMode; ++mode)
{
- var histo = new Vp8LHistogram();
+ var histo = new Vp8Histogram();
histo.CollectHistogram(this.YuvIn.AsSpan(YOffEnc), this.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]), 0, 16);
int alpha = histo.GetAlpha();
if (alpha > bestAlpha)
@@ -402,21 +402,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte[] modes = new byte[16];
int maxMode = MaxIntra4Mode;
int i4Alpha;
- var totalHisto = new Vp8LHistogram();
+ var totalHisto = new Vp8Histogram();
int curHisto = 0;
this.StartI4();
do
{
int mode;
int bestModeAlpha = DefaultAlpha;
- var histos = new Vp8LHistogram[2];
+ var histos = new Vp8Histogram[2];
Span src = this.YuvIn.AsSpan(YOffEnc + WebpLookupTables.Vp8Scan[this.I4]);
this.MakeIntra4Preds();
for (mode = 0; mode < maxMode; ++mode)
{
int alpha;
- histos[curHisto] = new Vp8LHistogram();
+ histos[curHisto] = new Vp8Histogram();
histos[curHisto].CollectHistogram(src, this.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]), 0, 1);
alpha = histos[curHisto].GetAlpha();
@@ -456,7 +456,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.MakeChroma8Preds();
for (mode = 0; mode < maxMode; ++mode)
{
- var histo = new Vp8LHistogram();
+ var histo = new Vp8Histogram();
histo.CollectHistogram(this.YuvIn.AsSpan(UOffEnc), this.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]), 16, 16 + 4 + 4);
int alpha = histo.GetAlpha();
if (alpha > bestAlpha)
diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Histogram.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Histogram.cs
new file mode 100644
index 0000000000..e569ab0d9c
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Histogram.cs
@@ -0,0 +1,132 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Formats.Webp;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Lossy
+{
+ internal class Vp8Histogram
+ {
+ ///
+ /// Size of histogram used by CollectHistogram.
+ ///
+ private const int MaxCoeffThresh = 31;
+
+ private int maxValue;
+
+ private int lastNonZero;
+
+ public int GetAlpha()
+ {
+ // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer
+ // values which happen to be mostly noise. This leaves the maximum precision
+ // for handling the useful small values which contribute most.
+ int maxValue = this.maxValue;
+ int lastNonZero = this.lastNonZero;
+ int alpha = (maxValue > 1) ? WebpConstants.AlphaScale * lastNonZero / maxValue : 0;
+ return alpha;
+ }
+
+ public void CollectHistogram(Span reference, Span pred, int startBlock, int endBlock)
+ {
+ int j;
+ int[] distribution = new int[MaxCoeffThresh + 1];
+ for (j = startBlock; j < endBlock; ++j)
+ {
+ short[] output = new short[16];
+
+ this.Vp8FTransform(reference.Slice(WebpLookupTables.Vp8DspScan[j]), pred.Slice(WebpLookupTables.Vp8DspScan[j]), output);
+
+ // Convert coefficients to bin.
+ for (int k = 0; k < 16; ++k)
+ {
+ int v = Math.Abs(output[k]) >> 3;
+ int clippedValue = ClipMax(v, MaxCoeffThresh);
+ ++distribution[clippedValue];
+ }
+ }
+
+ this.SetHistogramData(distribution);
+ }
+
+ public void Merge(Vp8Histogram other)
+ {
+ if (this.maxValue > other.maxValue)
+ {
+ other.maxValue = this.maxValue;
+ }
+
+ if (this.lastNonZero > other.lastNonZero)
+ {
+ other.lastNonZero = this.lastNonZero;
+ }
+ }
+
+ private void SetHistogramData(int[] distribution)
+ {
+ int maxValue = 0;
+ int lastNonZero = 1;
+ for (int k = 0; k <= MaxCoeffThresh; ++k)
+ {
+ int value = distribution[k];
+ if (value > 0)
+ {
+ if (value > maxValue)
+ {
+ maxValue = value;
+ }
+
+ lastNonZero = k;
+ }
+ }
+
+ this.maxValue = maxValue;
+ this.lastNonZero = lastNonZero;
+ }
+
+ private void Vp8FTransform(Span src, Span reference, Span output)
+ {
+ int i;
+ int[] tmp = new int[16];
+ for (i = 0; i < 4; ++i)
+ {
+ int d0 = src[0] - reference[0]; // 9bit dynamic range ([-255,255])
+ int d1 = src[1] - reference[1];
+ int d2 = src[2] - reference[2];
+ int d3 = src[3] - reference[3];
+ int a0 = d0 + d3; // 10b [-510,510]
+ int a1 = d1 + d2;
+ int a2 = d1 - d2;
+ int a3 = d0 - d3;
+ tmp[0 + (i * 4)] = (a0 + a1) * 8; // 14b [-8160,8160]
+ tmp[1 + (i * 4)] = ((a2 * 2217) + (a3 * 5352) + 1812) >> 9; // [-7536,7542]
+ tmp[2 + (i * 4)] = (a0 - a1) * 8;
+ tmp[3 + (i * 4)] = ((a3 * 2217) - (a2 * 5352) + 937) >> 9;
+
+ // Do not change the span in the last iteration.
+ if (i < 3)
+ {
+ src = src.Slice(WebpConstants.Bps);
+ reference = reference.Slice(WebpConstants.Bps);
+ }
+ }
+
+ for (i = 0; i < 4; ++i)
+ {
+ int a0 = tmp[0 + i] + tmp[12 + i]; // 15b
+ int a1 = tmp[4 + i] + tmp[8 + i];
+ int a2 = tmp[4 + i] - tmp[8 + i];
+ int a3 = tmp[0 + i] - tmp[12 + i];
+ output[0 + i] = (short)((a0 + a1 + 7) >> 4); // 12b
+ output[4 + i] = (short)((((a2 * 2217) + (a3 * 5352) + 12000) >> 16) + ((a3 != 0) ? 1 : 0));
+ output[8 + i] = (short)((a0 - a1 + 7) >> 4);
+ output[12 + i] = (short)(((a3 * 2217) - (a2 * 5352) + 51000) >> 16);
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static int ClipMax(int v, int max) => (v > max) ? max : v;
+ }
+}