Browse Source

Implemented ycbcr/rgb/grayscale color converters

pull/2120/head
Dmitry Pentin 4 years ago
parent
commit
0604a40385
  1. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs
  2. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs
  3. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs
  4. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs
  5. 20
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs
  6. 24
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs
  7. 23
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
  8. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs
  9. 32
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
  10. 42
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
  11. 30
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs
  12. 54
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs
  13. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs
  14. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs
  15. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs
  16. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
  17. 35
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs
  18. 3
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
  19. 4
      src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponent.cs
  20. 17
      src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs
  21. 51
      src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs
  22. 20
      src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs
  23. 24
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs

@ -18,6 +18,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertFromRgbInplace(in ComponentValues values) => throw new System.NotImplementedException();
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs

@ -38,6 +38,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c2[i] = y * k;
}
}
public override void ConvertFromRgbInplace(in ComponentValues values) => throw new NotImplementedException();
}
}
}

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
protected override void ConvertCoreVectorizedInplaceToRgb(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -44,8 +44,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
protected override void ConvertCoreInplaceToRgb(in ComponentValues values) =>
FromCmykScalar.ConvertCoreInplace(values, this.MaximumValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) => throw new System.NotImplementedException();
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) => throw new System.NotImplementedException();
}
}
}

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs

@ -33,6 +33,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c0 = Avx.Multiply(c0, scale);
}
}
public override void ConvertFromRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
// Used for the color conversion
var scale = Vector256.Create(this.MaximumValue);
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
c0 = Avx.Multiply(c0, scale);
}
}
}
}
}

20
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs

@ -16,10 +16,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgbInplace(in ComponentValues values) =>
ConvertCoreInplace(values.Component0, this.MaximumValue);
public override void ConvertToRgbInplace(in ComponentValues values)
=> ConvertCoreInplaceToRgb(values.Component0, this.MaximumValue);
internal static void ConvertCoreInplace(Span<float> values, float maxValue)
public override void ConvertFromRgbInplace(in ComponentValues values)
=> ConvertCoreInplaceFromRgb(values.Component0, this.MaximumValue);
internal static void ConvertCoreInplaceToRgb(Span<float> values, float maxValue)
{
ref float valuesRef = ref MemoryMarshal.GetReference(values);
float scale = 1 / maxValue;
@ -29,6 +32,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
Unsafe.Add(ref valuesRef, i) *= scale;
}
}
internal static void ConvertCoreInplaceFromRgb(Span<float> values, float maxValue)
{
ref float valuesRef = ref MemoryMarshal.GetReference(values);
float scale = maxValue;
for (nint i = 0; i < values.Length; i++)
{
Unsafe.Add(ref valuesRef, i) *= scale;
}
}
}
}
}

24
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
protected override void ConvertCoreVectorizedInplaceToRgb(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -31,8 +31,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, this.MaximumValue);
protected override void ConvertCoreInplaceToRgb(in ComponentValues values)
=> FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, this.MaximumValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
var scale = new Vector<float>(this.MaximumValue);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector<float> c0 = ref Unsafe.Add(ref cBase, i);
c0 *= scale;
}
}
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values)
=> FromGrayscaleScalar.ConvertCoreInplaceFromRgb(values.Component0, this.MaximumValue);
}
}
}

23
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs

@ -40,6 +40,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
b = Avx.Multiply(b, scale);
}
}
public override void ConvertFromRgbInplace(in ComponentValues values)
{
ref Vector256<float> rBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> gBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> bBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
var scale = Vector256.Create(this.MaximumValue);
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector256<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector256<float> b = ref Unsafe.Add(ref bBase, i);
r = Avx.Multiply(r, scale);
g = Avx.Multiply(g, scale);
b = Avx.Multiply(b, scale);
}
}
}
}
}

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs

@ -12,14 +12,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgbInplace(in ComponentValues values) =>
ConvertCoreInplace(values, this.MaximumValue);
public override void ConvertToRgbInplace(in ComponentValues values)
=> ConvertCoreInplaceFromRgb(values, this.MaximumValue);
internal static void ConvertCoreInplace(ComponentValues values, float maxValue)
public override void ConvertFromRgbInplace(in ComponentValues values)
=> ConvertCoreInplaceFromRgb(values, this.MaximumValue);
internal static void ConvertCoreInplaceToRgb(ComponentValues values, float maxValue)
{
FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, maxValue);
FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component1, maxValue);
FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component2, maxValue);
}
internal static void ConvertCoreInplaceFromRgb(ComponentValues values, float maxValue)
{
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, maxValue);
FromGrayscaleScalar.ConvertCoreInplace(values.Component1, maxValue);
FromGrayscaleScalar.ConvertCoreInplace(values.Component2, maxValue);
FromGrayscaleScalar.ConvertCoreInplaceFromRgb(values.Component0, maxValue);
FromGrayscaleScalar.ConvertCoreInplaceFromRgb(values.Component1, maxValue);
FromGrayscaleScalar.ConvertCoreInplaceFromRgb(values.Component2, maxValue);
}
}
}

32
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
protected override void ConvertCoreVectorizedInplaceToRgb(in ComponentValues values)
{
ref Vector<float> rBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -39,8 +39,34 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromRgbScalar.ConvertCoreInplace(values, this.MaximumValue);
protected override void ConvertCoreInplaceToRgb(in ComponentValues values)
=> FromRgbScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values)
{
ref Vector<float> rBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> gBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> bBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
var scale = new Vector<float>(this.MaximumValue);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector<float> b = ref Unsafe.Add(ref bBase, i);
r *= scale;
g *= scale;
b *= scale;
}
}
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values)
=> FromRgbScalar.ConvertCoreInplaceFromRgb(values, this.MaximumValue);
}
}
}

42
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs

@ -71,7 +71,47 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public override void ConvertFromRgbInplace(in ComponentValues values)
{
return;
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
var chromaOffset = Vector256.Create(this.HalfValue);
var scale = Vector256.Create(this.MaximumValue);
var f0299 = Vector256.Create(0.299f);
var f0587 = Vector256.Create(0.587f);
var f0114 = Vector256.Create(0.114f);
var fn0168736 = Vector256.Create(-0.168736f);
var fn0331264 = Vector256.Create(-0.331264f);
var fn0418688 = Vector256.Create(-0.418688f);
var fn0081312F = Vector256.Create(-0.081312F);
var f05 = Vector256.Create(0.5f);
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector256<float> r = Avx.Multiply(c0, scale);
Vector256<float> g = Avx.Multiply(c1, scale);
Vector256<float> b = Avx.Multiply(c2, scale);
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector256<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r);
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r));
Vector256<float> cr = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r));
c0 = y;
c1 = cb;
c2 = cr;
}
}
}
}

30
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs

@ -21,9 +21,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
public override void ConvertToRgbInplace(in ComponentValues values)
=> ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
=> ConvertCoreInplaceToRgb(values, this.MaximumValue, this.HalfValue);
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue)
public override void ConvertFromRgbInplace(in ComponentValues values)
=> ConvertCoreInplaceFromRgb(values, this.MaximumValue, this.HalfValue);
public static void ConvertCoreInplaceToRgb(in ComponentValues values, float maxValue, float halfValue)
{
Span<float> c0 = values.Component0;
Span<float> c1 = values.Component1;
@ -45,6 +48,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scale;
}
}
public static void ConvertCoreInplaceFromRgb(in ComponentValues values, float maxValue, float halfValue)
{
Span<float> c0 = values.Component0;
Span<float> c1 = values.Component1;
Span<float> c2 = values.Component2;
float scale = maxValue;
for (int i = 0; i < c0.Length; i++)
{
float r = c0[i] * scale;
float g = c1[i] * scale;
float b = c2[i] * scale;
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
c0[i] = (0.299f * r) + (0.587f * g) + (0.114f * b);
c1[i] = 128 - (0.168736f * r) - (0.331264f * g) + (0.5f * b);
c2[i] = 128 + (0.5f * r) - (0.418688f * g) - (0.081312f * b);
}
}
}
}
}

54
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
protected override void ConvertCoreVectorizedInplaceToRgb(in ComponentValues values)
{
ref Vector<float> c0Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -67,8 +67,56 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYCbCrScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
protected override void ConvertCoreInplaceToRgb(in ComponentValues values)
=> FromYCbCrScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue, this.HalfValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values)
{
ref Vector<float> c0Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> c1Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> c2Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
var chromaOffset = new Vector<float>(this.HalfValue);
var scale = new Vector<float>(this.MaximumValue);
var rYMult = new Vector<float>(0.299f);
var gYMult = new Vector<float>(0.587f);
var bYMult = new Vector<float>(0.114f);
var rCbMult = new Vector<float>(0.168736f);
var gCbMult = new Vector<float>(0.331264f);
var bCbMult = new Vector<float>(0.5f);
var rCrMult = new Vector<float>(0.5f);
var gCrMult = new Vector<float>(0.418688f);
var bCrMult = new Vector<float>(0.081312f);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector<float> r = c0 * scale;
Vector<float> g = c1 * scale;
Vector<float> b = c2 * scale;
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
c0 = (rYMult * r) + (gYMult * g) + (bYMult * b);
c1 = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b);
c2 = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b);
}
}
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values)
=> FromYCbCrScalar.ConvertCoreInplaceFromRgb(values, this.MaximumValue, this.HalfValue);
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs

@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertFromRgbInplace(in ComponentValues values) => throw new System.NotImplementedException();
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs

@ -38,6 +38,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c2[i] = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * scaledK;
}
}
public override void ConvertFromRgbInplace(in ComponentValues values) => throw new NotImplementedException();
}
}
}

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
protected override void ConvertCoreVectorizedInplaceToRgb(in ComponentValues values)
{
ref Vector<float> c0Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -68,8 +68,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
protected override void ConvertCoreInplaceToRgb(in ComponentValues values) =>
FromYccKScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) => throw new System.NotImplementedException();
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) => throw new System.NotImplementedException();
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs

@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param>
public abstract void ConvertToRgbInplace(in ComponentValues values);
public virtual void ConvertFromRgbInplace(in ComponentValues values) => throw new NotImplementedException("This is a test exception");
public abstract void ConvertFromRgbInplace(in ComponentValues values);
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for all supported colorspaces and precisions.

35
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// Jpeg images are guaranteed to have pixel strides at least 8 pixels wide
// Thus there's no need to check whether simdCount is greater than zero
int simdCount = length - remainder;
this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount));
this.ConvertCoreVectorizedInplaceToRgb(values.Slice(0, simdCount));
// Jpeg images width is always divisible by 8 without a remainder
// so it's safe to say SSE/AVX implementations would never have
@ -47,13 +47,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// remainder pixels
if (remainder > 0)
{
this.ConvertCoreInplace(values.Slice(simdCount, remainder));
this.ConvertCoreInplaceToRgb(values.Slice(simdCount, remainder));
}
}
protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException();
public override void ConvertFromRgbInplace(in ComponentValues values)
{
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware.");
int length = values.Component0.Length;
int remainder = (int)((uint)length % (uint)Vector<float>.Count);
// Jpeg images are guaranteed to have pixel strides at least 8 pixels wide
// Thus there's no need to check whether simdCount is greater than zero
int simdCount = length - remainder;
this.ConvertCoreVectorizedInplaceFromRgb(values.Slice(0, simdCount));
// Jpeg images width is always divisible by 8 without a remainder
// so it's safe to say SSE/AVX implementations would never have
// 'remainder' pixels
// But some exotic simd implementations e.g. AVX-512 can have
// remainder pixels
if (remainder > 0)
{
this.ConvertCoreInplaceFromRgb(values.Slice(simdCount, remainder));
}
}
protected abstract void ConvertCoreVectorizedInplaceToRgb(in ComponentValues values);
protected abstract void ConvertCoreInplaceToRgb(in ComponentValues values);
protected abstract void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values);
protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException();
protected abstract void ConvertCoreInplaceFromRgb(in ComponentValues values);
}
}
}

3
src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs

@ -128,13 +128,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
get => this.emitWriteIndex < (uint)this.emitBuffer.Length / 2;
}
public void Encode<TPixel>(Image<TPixel> image, Block8x8F[] quantTables, Configuration configuration, CancellationToken cancellationToken)
public void EncodeScan<TPixel>(JpegFrame frame, Image<TPixel> image, Block8x8F[] quantTables, Configuration configuration, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
// DEBUG INITIALIZATION SETUP
this.huffmanTables = HuffmanLut.TheHuffmanLut;
var frame = new JpegFrame(configuration.MemoryAllocator, image, componentCount: 3);
frame.Init(1, 1);
frame.AllocateComponents(fullScan: false);

4
src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponent.cs

@ -62,12 +62,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// <summary>
/// Gets or sets the index for the DC Huffman table.
/// </summary>
public int DcTableId { get; set; } = 0; // TODO: DEBUG!!!
public int DcTableId { get; set; }
/// <summary>
/// Gets or sets the index for the AC Huffman table.
/// </summary>
public int AcTableId { get; set; } = 1; // TODO: DEBUG!!!
public int AcTableId { get; set; }
/// <inheritdoc/>
public void Dispose()

17
src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
private readonly JpegComponent component;
private readonly int blockRowsPerStep;
private Block8x8F quantTable;
public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegComponent component, Size postProcessorBufferSize, Block8x8F quantTable)
@ -27,9 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
this.ColorBuffer = memoryAllocator.Allocate2DOveraligned<float>(
postProcessorBufferSize.Width,
postProcessorBufferSize.Height,
this.blockAreaSize.Height);
this.blockRowsPerStep = postProcessorBufferSize.Height / 8 / this.component.SubSamplingDivisors.Height;
this.blockAreaSize.Height,
AllocationOptions.Clean);
}
/// <summary>
@ -42,21 +39,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
Buffer2D<Block8x8> spectralBuffer = this.component.SpectralBlocks;
// should be this.frame.MaxColorChannelValue
// but currently 12-bit jpegs are not supported
float maximumValue = 255f;
// but 12-bit jpegs are not supported currently
float normalizationValue = -128f;
int blocksRowsPerStep = this.component.SamplingFactors.Height;
int destAreaStride = this.ColorBuffer.Width;
int yBlockStart = spectralStep * this.blockRowsPerStep;
int yBlockStart = spectralStep * blocksRowsPerStep;
Size subSamplingDivisors = this.component.SubSamplingDivisors;
Block8x8F workspaceBlock = default;
for (int y = 0; y < this.blockRowsPerStep; y++)
for (int y = 0; y < blocksRowsPerStep; y++)
{
int yBuffer = y * this.blockAreaSize.Height;
@ -73,9 +69,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
subSamplingDivisors.Width,
subSamplingDivisors.Height);
// multiply by maximum (for debug only, should be done in color converter)
workspaceBlock.MultiplyInPlace(maximumValue);
// level shift via -128f
workspaceBlock.AddInPlace(normalizationValue);

51
src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs

@ -11,55 +11,45 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// </summary>
internal sealed class JpegFrame : IDisposable
{
public JpegFrame(MemoryAllocator allocator, Image image, byte componentCount)
public JpegFrame(MemoryAllocator allocator, Image image, Decoder.JpegColorSpace colorSpace)
{
this.ColorSpace = colorSpace;
this.PixelWidth = image.Width;
this.PixelHeight = image.Height;
if (componentCount != 3)
{
throw new ArgumentException("This is YCbCr debug path only.");
}
// int componentCount = 3;
this.Components = new JpegComponent[]
{
new JpegComponent(allocator, 1, 1, 0),
new JpegComponent(allocator, 1, 1, 0),
new JpegComponent(allocator, 1, 1, 0),
// RGB
new JpegComponent(allocator, 1, 1, 0) { DcTableId = 0, AcTableId = 1 },
new JpegComponent(allocator, 1, 1, 0) { DcTableId = 0, AcTableId = 1 },
new JpegComponent(allocator, 1, 1, 0) { DcTableId = 0, AcTableId = 1 },
// YCbCr
//new JpegComponent(allocator, 1, 1, 0) { DcTableId = 0, AcTableId = 1 },
//new JpegComponent(allocator, 1, 1, 1) { DcTableId = 2, AcTableId = 3 },
//new JpegComponent(allocator, 1, 1, 1) { DcTableId = 2, AcTableId = 3 },
// Luminance
//new JpegComponent(allocator, 1, 1, 0) { DcTableId = 0, AcTableId = 1 }
};
}
/// <summary>
/// Gets the number of pixel per row.
/// </summary>
public Decoder.JpegColorSpace ColorSpace { get; }
public int PixelHeight { get; private set; }
/// <summary>
/// Gets the number of pixels per line.
/// </summary>
public int PixelWidth { get; private set; }
/// <summary>
/// Gets the number of components within a frame.
/// </summary>
public int ComponentCount => this.Components.Length;
/// <summary>
/// Gets the frame component collection.
/// </summary>
public JpegComponent[] Components { get; }
/// <summary>
/// Gets or sets the number of MCU's per line.
/// </summary>
public int McusPerLine { get; set; }
/// <summary>
/// Gets or sets the number of MCU's per column.
/// </summary>
public int McusPerColumn { get; set; }
/// <inheritdoc/>
public void Dispose()
{
for (int i = 0; i < this.Components.Length; i++)
@ -68,11 +58,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
}
}
/// <summary>
/// Allocates the frame component blocks.
/// </summary>
/// <param name="maxSubFactorH">Maximal horizontal subsampling factor among all the components.</param>
/// <param name="maxSubFactorV">Maximal vertical subsampling factor among all the components.</param>
public void Init(int maxSubFactorH, int maxSubFactorV)
{
this.McusPerLine = (int)Numerics.DivideCeil((uint)this.PixelWidth, (uint)maxSubFactorH * 8);

20
src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs

@ -25,6 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
private Buffer2D<TPixel> pixelBuffer;
private int alignedPixelWidth;
private Decoder.ColorConverters.JpegColorConverterBase colorConverter;
public SpectralConverter(Configuration configuration) =>
@ -47,7 +49,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// component processors from spectral to Rgba32
const int blockPixelWidth = 8;
var postProcessorBufferSize = new Size(majorBlockWidth * blockPixelWidth, this.pixelRowsPerStep);
this.alignedPixelWidth = majorBlockWidth * blockPixelWidth;
var postProcessorBufferSize = new Size(this.alignedPixelWidth, this.pixelRowsPerStep);
this.componentProcessors = new JpegComponentPostProcessor[frame.Components.Length];
for (int i = 0; i < this.componentProcessors.Length; i++)
{
@ -56,10 +59,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
}
// single 'stride' rgba32 buffer for conversion between spectral and TPixel
this.rgbBuffer = allocator.Allocate<byte>(frame.PixelWidth * 3);
this.rgbBuffer = allocator.Allocate<byte>(this.alignedPixelWidth * 3);
// color converter from Rgb24 to YCbCr
this.colorConverter = Decoder.ColorConverters.JpegColorConverterBase.GetConverter(colorSpace: Decoder.JpegColorSpace.YCbCr, precision: 8);
this.colorConverter = Decoder.ColorConverters.JpegColorConverterBase.GetConverter(colorSpace: frame.ColorSpace, precision: 8);
}
public void ConvertStrideBaseline()
@ -80,18 +83,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
int width = this.pixelBuffer.Width;
// unpack TPixel to r/g/b planes
Span<byte> r = this.rgbBuffer.Slice(0, width);
Span<byte> g = this.rgbBuffer.Slice(width, width);
Span<byte> b = this.rgbBuffer.Slice(width * 2, width);
Span<byte> r = this.rgbBuffer.Slice(0, this.alignedPixelWidth);
Span<byte> g = this.rgbBuffer.Slice(this.alignedPixelWidth, this.alignedPixelWidth);
Span<byte> b = this.rgbBuffer.Slice(this.alignedPixelWidth * 2, this.alignedPixelWidth);
for (int yy = this.pixelRowCounter; yy < maxY; yy++)
{
int y = yy - this.pixelRowCounter;
// PackFromRgbPlanes expects the destination to be padded, so try to get padded span containing extra elements from the next row.
// If we can't get such a padded row because we are on a MemoryGroup boundary or at the last row,
// pack pixels to a temporary, padded proxy buffer, then copy the relevant values to the destination row.
// unpack TPixel to r/g/b planes
Span<TPixel> sourceRow = this.pixelBuffer.DangerousGetRowSpan(yy);
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(this.configuration, r, g, b, sourceRow);

24
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -131,13 +131,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Write the scan header.
this.WriteStartOfScan(componentCount, componentIds);
var frame = new Components.Encoder.JpegFrame(Configuration.Default.MemoryAllocator, image, GetTargetColorSpace(this.colorType.Value));
var quantTables = new Block8x8F[] { luminanceQuantTable, chrominanceQuantTable };
new HuffmanScanEncoder(3, stream).Encode(image, quantTables, Configuration.Default, cancellationToken);
new HuffmanScanEncoder(3, stream).EncodeScan(frame, image, quantTables, Configuration.Default, cancellationToken);
// Write the End Of Image marker.
this.WriteEndOfImageMarker();
stream.Flush();
static JpegColorSpace GetTargetColorSpace(JpegColorType colorType)
{
switch (colorType)
{
case JpegColorType.YCbCrRatio444:
case JpegColorType.YCbCrRatio422:
case JpegColorType.YCbCrRatio420:
case JpegColorType.YCbCrRatio411:
case JpegColorType.YCbCrRatio410:
return JpegColorSpace.YCbCr;
case JpegColorType.Rgb:
return JpegColorSpace.RGB;
case JpegColorType.Cmyk:
return JpegColorSpace.Cmyk;
case JpegColorType.Luminance:
return JpegColorSpace.Grayscale;
default:
throw new NotImplementedException($"Unknown output color space: {colorType}");
}
}
}
/// <summary>

Loading…
Cancel
Save