Browse Source

Implemented YccK encoding

pull/2120/head
Dmitry Pentin 4 years ago
parent
commit
a7bea83680
  1. 5
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs
  2. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs
  3. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs
  4. 52
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs
  5. 42
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs
  6. 58
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs
  7. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs
  8. 7
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  9. 48
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  10. 5
      src/ImageSharp/Formats/Jpeg/JpegEncodingColor.cs

5
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs

@ -49,6 +49,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
public override void ConvertFromRgbInplace(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
=> ConvertFromRgbInplace(in values, this.MaximumValue, rLane, gLane, bLane);
public static void ConvertFromRgbInplace(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector256<float> destC =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -67,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane));
// Used for the color conversion
var scale = Vector256.Create(this.MaximumValue);
var scale = Vector256.Create(maxValue);
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs

@ -49,6 +49,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
=> CmykScalar.ConvertToRgbInplace(values, this.MaximumValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, r, g, b);
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> ConvertFromRgbInplaceRemainder(values, this.MaximumValue, r, g, b);
public static void ConvertFromRgbInplaceVectorized(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b)
{
ref Vector<float> destC =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -67,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(b));
// Used for the color conversion
var scale = new Vector<float>(this.MaximumValue);
var scale = new Vector<float>(maxValue);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
@ -89,8 +95,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> CmykScalar.ConvertFromRgbInplace(values, this.MaximumValue, r, g, b);
public static void ConvertFromRgbInplaceRemainder(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b)
=> CmykScalar.ConvertFromRgbInplace(values, maxValue, r, g, b);
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal sealed class YCbCrScalar : JpegColorConverterScalar
{
// TODO: comments, derived from ITU-T Rec. T.871
// derived from ITU-T Rec. T.871
internal const float RCrMult = 1.402f;
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587);
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587);

52
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs

@ -78,8 +78,56 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
public override void ConvertFromRgbInplace(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> throw new NotImplementedException();
public override void ConvertFromRgbInplace(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
// rgb -> cmyk
CmykAvx.ConvertFromRgbInplace(in values, this.MaximumValue, rLane, gLane, bLane);
// cmyk -> ycck
ref Vector256<float> destY =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> destCb =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> destCr =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> srcR = ref destY;
ref Vector256<float> srcG = ref destCb;
ref Vector256<float> srcB = ref destCr;
// Used for the color conversion
var maxSampleValue = Vector256.Create(this.MaximumValue);
var chromaOffset = Vector256.Create(this.HalfValue);
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++)
{
Vector256<float> r = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i));
Vector256<float> g = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i));
Vector256<float> b = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcB, i));
// 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));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;
Unsafe.Add(ref destCr, i) = cr;
}
}
}
}
}

42
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs

@ -9,6 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal sealed class YccKScalar : JpegColorConverterScalar
{
// derived from ITU-T Rec. T.871
internal const float RCrMult = 1.402f;
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587);
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587);
internal const float BCbMult = 1.772f;
public YccKScalar(int precision)
: base(JpegColorSpace.Ycck, precision)
{
@ -17,6 +23,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public override void ConvertToRgbInplace(in ComponentValues values)
=> ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue);
public override void ConvertFromRgbInplace(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> ConvertCoreInplaceFromRgb(values, this.HalfValue, this.MaximumValue, r, g, b);
public static void ConvertToRgpInplace(in ComponentValues values, float maxValue, float halfValue)
{
Span<float> c0 = values.Component0;
@ -33,14 +42,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
float cr = c2[i] - halfValue;
float scaledK = c3[i] * scale;
c0[i] = (maxValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * scaledK;
c1[i] = (maxValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * scaledK;
c2[i] = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * scaledK;
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
c0[i] = MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero) * scaledK;
c1[i] = MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero) * scaledK;
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scaledK;
}
}
public override void ConvertFromRgbInplace(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> throw new NotImplementedException();
public static void ConvertCoreInplaceFromRgb(in ComponentValues values, float halfValue, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
// rgb -> cmyk
CmykScalar.ConvertFromRgbInplace(in values, maxValue, rLane, gLane, bLane);
// cmyk -> ycck
Span<float> c = values.Component0;
Span<float> m = values.Component1;
Span<float> y = values.Component2;
for (int i = 0; i < y.Length; i++)
{
float r = maxValue - c[i];
float g = maxValue - m[i];
float b = maxValue - y[i];
// k value is passed untouched from rgb -> cmyk conversion
c[i] = (0.299f * r) + (0.587f * g) + (0.114f * b);
m[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b);
y[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b);
}
}
}
}
}

58
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs

@ -72,11 +72,63 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
protected override void ConvertCoreInplaceToRgb(in ComponentValues values) =>
YccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue);
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> throw new System.NotImplementedException();
protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
// rgb -> cmyk
CmykVector.ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, rLane, gLane, bLane);
// cmyk -> ycck
ref Vector<float> destY =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> destCb =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> destCr =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> srcR = ref destY;
ref Vector<float> srcG = ref destCb;
ref Vector<float> srcB = ref destCr;
var maxSampleValue = new Vector<float>(this.MaximumValue);
var chromaOffset = new Vector<float>(this.HalfValue);
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++)
{
Vector<float> r = maxSampleValue - Unsafe.Add(ref srcR, i);
Vector<float> g = maxSampleValue - Unsafe.Add(ref srcG, i);
Vector<float> b = maxSampleValue - Unsafe.Add(ref srcB, i);
// 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)
Unsafe.Add(ref destY, i) = (rYMult * r) + (gYMult * g) + (bYMult * b);
Unsafe.Add(ref destCb, i) = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b);
Unsafe.Add(ref destCr, i) = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b);
}
}
protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> throw new System.NotImplementedException();
{
// rgb -> cmyk
CmykScalar.ConvertFromRgbInplace(in values, this.MaximumValue, r, g, b);
// cmyk -> ycck
YccKScalar.ConvertCoreInplaceFromRgb(in values, this.HalfValue, this.MaximumValue, r, g, b);
}
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs

@ -38,5 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
public int MaxHorizontalSamplingFactor { get; }
public int MaxVerticalSamplingFactor { get; }
public byte? AdobeColorTransformMarkerFlag { get; set; } = null;
}
}

7
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -596,12 +596,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegColorSpace.Cmyk:
return JpegEncodingColor.Cmyk;
case JpegColorSpace.Ycck:
// TODO: change this after YccK encoding is implemented
// We are deliberately mapping YccK color space to Cmyk color space at metadata
// level so encoder can fallback to cmyk color space from it.
// YccK -> Cmyk is the closest conversion logically wise
return JpegEncodingColor.Cmyk;
return JpegEncodingColor.Ycck;
default:
return JpegEncodingColor.YCbCrRatio420;
}

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

@ -81,16 +81,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Write the Start Of Image marker.
this.WriteStartOfImage();
// Write APP0 marker for any non-RGB colorspace image.
if (frameConfig.EncodingColor != JpegEncodingColor.Rgb)
// Write APP0 marker
if (frameConfig.AdobeColorTransformMarkerFlag is null)
{
this.WriteJfifApplicationHeader(metadata);
}
// Else write App14 marker to indicate RGB color space.
// Write APP14 marker with adobe color extension
else
{
this.WriteApp14Marker();
this.WriteApp14Marker(frameConfig.AdobeColorTransformMarkerFlag.Value);
}
// Write Exif, XMP, ICC and IPTC profiles
@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Writes the APP14 marker to indicate the image is in RGB color space.
/// </summary>
private void WriteApp14Marker()
private void WriteApp14Marker(byte colorTransform)
{
this.WriteMarkerHeader(JpegConstants.Markers.APP14, 2 + Components.Decoder.AdobeMarker.Length);
@ -227,8 +227,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Flags1
BinaryPrimitives.WriteInt16BigEndian(this.buffer.AsSpan(9, 2), 0);
// Transform byte, 0 in combination with three components means the image is in RGB colorspace.
this.buffer[11] = 0;
// Color transform byte
this.buffer[11] = colorTransform;
this.outputStream.Write(this.buffer.AsSpan(0, 12));
}
@ -840,7 +840,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
new JpegQuantizationTableConfig[]
{
defaultLuminanceQuantTable
}),
})
{
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformUnknown
},
// Cmyk
new JpegFrameConfig(
@ -861,7 +864,34 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
new JpegQuantizationTableConfig[]
{
defaultLuminanceQuantTable
}),
})
{
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformUnknown,
},
// YccK
new JpegFrameConfig(
JpegColorSpace.Ycck,
JpegEncodingColor.Ycck,
new JpegComponentConfig[]
{
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0),
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0),
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0),
new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0),
},
new JpegHuffmanTableConfig[]
{
defaultLuminanceHuffmanDC,
defaultLuminanceHuffmanAC
},
new JpegQuantizationTableConfig[]
{
defaultLuminanceQuantTable
})
{
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformYcck,
},
};
}
}

5
src/ImageSharp/Formats/Jpeg/JpegEncodingColor.cs

@ -54,5 +54,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// CMYK colorspace (cyan, magenta, yellow, and key black) intended for printing.
/// </summary>
Cmyk = 7,
/// <summary>
/// YCCK colorspace (Y, Cb, Cr, and key black).
/// </summary>
Ycck = 8,
}
}

Loading…
Cancel
Save