mirror of https://github.com/SixLabors/ImageSharp
6 changed files with 240 additions and 20 deletions
@ -0,0 +1,47 @@ |
|||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using SixLabors.ImageSharp.ColorSpaces; |
||||
|
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverter |
||||
|
{ |
||||
|
private class FromYCbCr : JpegColorConverter |
||||
|
{ |
||||
|
private static readonly YCbCrAndRgbConverter Converter = new YCbCrAndRgbConverter(); |
||||
|
|
||||
|
public FromYCbCr() |
||||
|
: base(JpegColorSpace.YCbCr) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) |
||||
|
{ |
||||
|
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
|
||||
|
ReadOnlySpan<float> yVals = values.Component0; |
||||
|
ReadOnlySpan<float> cbVals = values.Component1; |
||||
|
ReadOnlySpan<float> crVals = values.Component2; |
||||
|
|
||||
|
Vector4 rgbaVector = new Vector4(0, 0, 0, 1); |
||||
|
|
||||
|
for (int i = 0; i < result.Length; i++) |
||||
|
{ |
||||
|
float colY = yVals[i]; |
||||
|
float colCb = cbVals[i]; |
||||
|
float colCr = crVals[i]; |
||||
|
|
||||
|
YCbCr yCbCr = new YCbCr(colY, colCb, colCr); |
||||
|
|
||||
|
// Slow conversion for now:
|
||||
|
Rgb rgb = Converter.Convert(yCbCr); |
||||
|
|
||||
|
Unsafe.As<Vector4, Vector3>(ref rgbaVector) = rgb.Vector; |
||||
|
result[i] = rgbaVector; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverter |
||||
|
{ |
||||
|
private static readonly JpegColorConverter[] Converters = { new FromYCbCr(), }; |
||||
|
|
||||
|
protected JpegColorConverter(JpegColorSpace colorSpace) |
||||
|
{ |
||||
|
this.ColorSpace = colorSpace; |
||||
|
} |
||||
|
|
||||
|
public JpegColorSpace ColorSpace { get; } |
||||
|
|
||||
|
public static JpegColorConverter GetConverter(JpegColorSpace colorSpace) |
||||
|
{ |
||||
|
JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace); |
||||
|
if (converter == null) |
||||
|
{ |
||||
|
throw new Exception($"Could not find any converter for JpegColorSpace {colorSpace}!"); |
||||
|
} |
||||
|
|
||||
|
return converter; |
||||
|
} |
||||
|
|
||||
|
public abstract void ConvertToRGBA(ComponentValues values, Span<Vector4> result); |
||||
|
|
||||
|
public struct ComponentValues |
||||
|
{ |
||||
|
public readonly int ComponentCount; |
||||
|
|
||||
|
public readonly ReadOnlySpan<float> Component0; |
||||
|
|
||||
|
public readonly ReadOnlySpan<float> Component1; |
||||
|
|
||||
|
public readonly ReadOnlySpan<float> Component2; |
||||
|
|
||||
|
public readonly ReadOnlySpan<float> Component3; |
||||
|
|
||||
|
public ComponentValues(IReadOnlyList<IBuffer2D<float>> componentBuffers, int row) |
||||
|
{ |
||||
|
this.ComponentCount = componentBuffers.Count; |
||||
|
|
||||
|
this.Component0 = componentBuffers[0].GetRowSpan(row); |
||||
|
this.Component1 = Span<float>.Empty; |
||||
|
this.Component2 = Span<float>.Empty; |
||||
|
this.Component3 = Span<float>.Empty; |
||||
|
|
||||
|
if (this.ComponentCount > 1) |
||||
|
{ |
||||
|
this.Component1 = componentBuffers[1].GetRowSpan(row); |
||||
|
if (this.ComponentCount > 2) |
||||
|
{ |
||||
|
this.Component2 = componentBuffers[2].GetRowSpan(row); |
||||
|
if (this.ComponentCount > 3) |
||||
|
{ |
||||
|
this.Component3 = componentBuffers[3].GetRowSpan(row); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Memory |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
/// <summary>
|
||||
|
/// Represents a contigous memory buffer of value-type items "promising" a <see cref="T:System.Span`1" />
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The value type</typeparam>
|
||||
|
internal interface IBuffer<T> : IDisposable |
||||
|
where T : struct |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the span to the memory "promised" by this buffer
|
||||
|
/// </summary>
|
||||
|
Span<T> Span { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,81 @@ |
|||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using SixLabors.ImageSharp.ColorSpaces; |
||||
|
using SixLabors.ImageSharp.ColorSpaces.Conversion; |
||||
|
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce; |
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.Tests.Colorspaces; |
||||
|
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; |
||||
|
|
||||
|
using Xunit; |
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Formats.Jpg |
||||
|
{ |
||||
|
public class JpegColorConverterTests |
||||
|
{ |
||||
|
private const float Precision = 0.1f; |
||||
|
|
||||
|
private const int InputBufferLength = 42; |
||||
|
|
||||
|
// The result buffer could be shorter
|
||||
|
private const int ResultBufferLength = 40; |
||||
|
|
||||
|
private readonly Vector4[] Result = new Vector4[ResultBufferLength]; |
||||
|
|
||||
|
private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); |
||||
|
|
||||
|
public JpegColorConverterTests(ITestOutputHelper output) |
||||
|
{ |
||||
|
this.Output = output; |
||||
|
} |
||||
|
|
||||
|
private ITestOutputHelper Output { get; } |
||||
|
|
||||
|
private static JpegColorConverter.ComponentValues CreateRandomValues(int componentCount, float maxVal = 255f) |
||||
|
{ |
||||
|
var rnd = new Random(42); |
||||
|
Buffer2D<float>[] buffers = new Buffer2D<float>[componentCount]; |
||||
|
for (int i = 0; i < componentCount; i++) |
||||
|
{ |
||||
|
float[] values = new float[InputBufferLength]; |
||||
|
|
||||
|
for (int j = 0; j < InputBufferLength; j++) |
||||
|
{ |
||||
|
values[j] = (float)rnd.NextDouble() * maxVal; |
||||
|
} |
||||
|
|
||||
|
// no need to dispose when buffer is not array owner
|
||||
|
buffers[i] = new Buffer2D<float>(values, values.Length, 1); |
||||
|
} |
||||
|
return new JpegColorConverter.ComponentValues(buffers, 0); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void ConvertFromYCbCr() |
||||
|
{ |
||||
|
var converter = JpegColorConverter.GetConverter(JpegColorSpace.YCbCr); |
||||
|
|
||||
|
JpegColorConverter.ComponentValues values = CreateRandomValues(3); |
||||
|
|
||||
|
converter.ConvertToRGBA(values, this.Result); |
||||
|
|
||||
|
for (int i = 0; i < ResultBufferLength; i++) |
||||
|
{ |
||||
|
float y = values.Component0[i]; |
||||
|
float cb = values.Component1[i]; |
||||
|
float cr = values.Component2[i]; |
||||
|
YCbCr ycbcr = new YCbCr(y, cb, cr); |
||||
|
|
||||
|
Vector4 rgba = this.Result[i]; |
||||
|
Rgb actual = new Rgb(rgba.X, rgba.Y, rgba.Z); |
||||
|
Rgb expected = ColorSpaceConverter.ToRgb(ycbcr); |
||||
|
|
||||
|
Assert.True(actual.AlmostEquals(expected, Precision)); |
||||
|
Assert.Equal(1, rgba.W); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue