Browse Source

RGB debug pass encoding done

pull/2120/head
Dmitry Pentin 4 years ago
parent
commit
45dfed1411
  1. 32
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs
  2. 13
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  3. 5
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
  4. 21
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
  5. 6
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
  6. 4
      src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponent.cs
  7. 80
      src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs
  8. 4
      src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegFrame.cs
  9. 83
      src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs
  10. 40
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  11. 37
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

32
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
@ -21,6 +22,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
this.ScaledCopyTo(ref areaOrigin, region.Stride, horizontalScale, verticalScale);
}
[MethodImpl(InliningOptions.ShortMethod)]
public void ScaledCopyFrom(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref areaOrigin);
int destStride = areaStride * sizeof(float);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 0);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 1);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 2);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 3);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 4);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 5);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 6);
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 7);
return;
}
throw new NotImplementedException("This is a test setup!");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void CopyRowImplFromStrides(ref byte src, ref byte dst, int srcStride, int row)
{
ref byte s = ref Unsafe.Add(ref dst, row * srcStride);
ref byte d = ref Unsafe.Add(ref src, row * 8 * sizeof(float));
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
}
[MethodImpl(InliningOptions.ShortMethod)]
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{

13
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -352,6 +352,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
public void DE_NormalizeColors(float maximum)
{
if (SimdUtils.HasVector8)
{
this.NormalizeColorsAndRoundInPlaceVector8(maximum);
}
else
{
this.NormalizeColorsInPlace(maximum);
this.RoundInPlace();
}
}
/// <summary>
/// Rounds all values in the block.
/// </summary>

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

@ -68,6 +68,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c2 = b;
}
}
public override void ConvertFromRgbInplace(in ComponentValues values)
{
return;
}
}
}
}

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

@ -79,6 +79,8 @@ 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");
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for all supported colorspaces and precisions.
/// </summary>
@ -233,6 +235,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : Span<float>.Empty;
}
/// <summary>
/// Initializes a new instance of the <see cref="ComponentValues"/> struct.
/// </summary>
/// <param name="processors">List of component color processors.</param>
/// <param name="row">Row to convert</param>
public ComponentValues(IReadOnlyList<Encoder.JpegComponentPostProcessor> processors, int row)
{
DebugGuard.MustBeGreaterThan(processors.Count, 0, nameof(processors));
this.ComponentCount = processors.Count;
this.Component0 = processors[0].GetColorBufferRowSpan(row);
// In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? processors[2].GetColorBufferRowSpan(row) : this.Component0;
this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : Span<float>.Empty;
}
internal ComponentValues(
int componentCount,
Span<float> c0,

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

@ -132,6 +132,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
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);
@ -188,6 +190,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// After all interleaved components, that's an interleaved MCU
mcu++;
if (this.IsStreamFlushNeeded)
{
this.FlushToStream();
}
}
}

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

@ -67,12 +67,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; }
public int DcTableId { get; set; } = 0; // TODO: DEBUG!!!
/// <summary>
/// Gets or sets the index for the AC Huffman table.
/// </summary>
public int AcTableId { get; set; }
public int AcTableId { get; set; } = 1; // TODO: DEBUG!!!
/// <inheritdoc/>
public void Dispose()

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

@ -8,18 +8,90 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
internal class JpegComponentPostProcessor : IDisposable
{
private readonly Size blockAreaSize;
private readonly JpegComponent component;
private readonly Block8x8F dequantTable;
private readonly int blockRowsPerStep;
private Block8x8F quantTable;
public JpegComponentPostProcessor(JpegComponent component, Block8x8F dequantTable)
public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegComponent component, Size postProcessorBufferSize, Block8x8F quantTable)
{
this.component = component;
this.dequantTable = dequantTable;
this.quantTable = quantTable;
FastFloatingPointDCT.AdjustToFDCT(ref this.quantTable);
this.component = component;
this.blockAreaSize = this.component.SubSamplingDivisors * 8;
this.ColorBuffer = memoryAllocator.Allocate2DOveraligned<float>(
postProcessorBufferSize.Width,
postProcessorBufferSize.Height,
this.blockAreaSize.Height);
this.blockRowsPerStep = postProcessorBufferSize.Height / 8 / this.component.SubSamplingDivisors.Height;
}
public void Dispose()
/// <summary>
/// Gets the temporary working buffer of color values.
/// </summary>
public Buffer2D<float> ColorBuffer { get; }
public void CopyColorBufferToBlocks(int spectralStep)
{
Buffer2D<Block8x8> spectralBuffer = this.component.SpectralBlocks;
// should be this.frame.MaxColorChannelValue
// but currently 12-bit jpegs are not supported
float maximumValue = 255f;
float normalizationValue = -128f;
int blocksRowsPerStep = this.component.SamplingFactors.Height;
int destAreaStride = this.ColorBuffer.Width;
int yBlockStart = spectralStep * this.blockRowsPerStep;
Size subSamplingDivisors = this.component.SubSamplingDivisors;
Block8x8F workspaceBlock = default;
for (int y = 0; y < this.blockRowsPerStep; y++)
{
int yBuffer = y * this.blockAreaSize.Height;
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
{
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
// load 8x8 block from 8 pixel strides
int xColorBufferStart = xBlock * this.blockAreaSize.Width;
workspaceBlock.ScaledCopyFrom(
ref colorBufferRow[xColorBufferStart],
destAreaStride,
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);
// FDCT
FastFloatingPointDCT.TransformFDCT(ref workspaceBlock);
// Quantize and save to spectral blocks
Block8x8F.Quantize(ref workspaceBlock, ref blockRow[xBlock], ref this.quantTable);
}
}
}
public Span<float> GetColorBufferRowSpan(int row)
=> this.ColorBuffer.DangerousGetRowSpan(row);
public void Dispose()
=> this.ColorBuffer.Dispose();
}
}

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

@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
this.Components = new JpegComponent[]
{
new JpegComponent(allocator, 1, 1, 0),
new JpegComponent(allocator, 1, 1, 1),
new JpegComponent(allocator, 1, 1, 1),
new JpegComponent(allocator, 1, 1, 0),
new JpegComponent(allocator, 1, 1, 0),
};
}

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

@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
private IMemoryOwner<byte> rgbBuffer;
private IMemoryOwner<TPixel> paddedProxyPixelRow;
private Buffer2D<TPixel> pixelBuffer;
private Decoder.ColorConverters.JpegColorConverterBase colorConverter;
public SpectralConverter(Configuration configuration) =>
this.configuration = configuration;
@ -46,9 +46,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// currently codec only supports encoding single frame jpegs
this.pixelBuffer = image.GetRootFramePixelBuffer();
// ???
//this.paddedProxyPixelRow = allocator.Allocate<TPixel>(frame.PixelWidth + 3);
// component processors from spectral to Rgba32
const int blockPixelWidth = 8;
var postProcessorBufferSize = new Size(majorBlockWidth * blockPixelWidth, this.pixelRowsPerStep);
@ -56,14 +53,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
for (int i = 0; i < this.componentProcessors.Length; i++)
{
JpegComponent component = frame.Components[i];
this.componentProcessors[i] = new JpegComponentPostProcessor(component, dequantTables[component.QuantizationTableIndex]);
this.componentProcessors[i] = new JpegComponentPostProcessor(allocator, component, postProcessorBufferSize, dequantTables[component.QuantizationTableIndex]);
}
// single 'stride' rgba32 buffer for conversion between spectral and TPixel
//this.rgbBuffer = allocator.Allocate<byte>(frame.PixelWidth * 3);
this.rgbBuffer = allocator.Allocate<byte>(frame.PixelWidth * 3);
// color converter from Rgba32 to TPixel
//this.colorConverter = this.GetColorConverter(frame, jpegData);
// color converter from Rgb24 to YCbCr
this.colorConverter = Decoder.ColorConverters.JpegColorConverterBase.GetConverter(colorSpace: Decoder.JpegColorSpace.YCbCr, precision: 8);
}
public void ConvertStrideBaseline()
@ -83,44 +80,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep);
//for (int i = 0; i < this.componentProcessors.Length; i++)
//{
// this.componentProcessors[i].CopyBlocksToColorBuffer(spectralStep);
//}
//int width = this.pixelBuffer.Width;
//for (int yy = this.pixelRowCounter; yy < maxY; yy++)
//{
// int y = yy - this.pixelRowCounter;
// var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y);
// this.colorConverter.ConvertToRgbInplace(values);
// values = values.Slice(0, width); // slice away Jpeg padding
// 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);
// SimdUtils.NormalizedFloatToByteSaturate(values.Component0, r);
// SimdUtils.NormalizedFloatToByteSaturate(values.Component1, g);
// SimdUtils.NormalizedFloatToByteSaturate(values.Component2, b);
// // 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.
// if (this.pixelBuffer.DangerousTryGetPaddedRowSpan(yy, 3, out Span<TPixel> destRow))
// {
// PixelOperations<TPixel>.Instance.PackFromRgbPlanes(this.configuration, r, g, b, destRow);
// }
// else
// {
// Span<TPixel> proxyRow = this.paddedProxyPixelRow.GetSpan();
// PixelOperations<TPixel>.Instance.PackFromRgbPlanes(this.configuration, r, g, b, proxyRow);
// proxyRow.Slice(0, width).CopyTo(this.pixelBuffer.DangerousGetRowSpan(yy));
// }
//}
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);
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.
Span<TPixel> sourceRow = this.pixelBuffer.DangerousGetRowSpan(yy);
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(this.configuration, r, g, b, sourceRow);
var values = new Decoder.ColorConverters.JpegColorConverterBase.ComponentValues(this.componentProcessors, y);
SimdUtils.ByteToNormalizedFloat(r, values.Component0);
SimdUtils.ByteToNormalizedFloat(g, values.Component1);
SimdUtils.ByteToNormalizedFloat(b, values.Component2);
this.colorConverter.ConvertFromRgbInplace(values);
}
for (int i = 0; i < this.componentProcessors.Length; i++)
{
this.componentProcessors[i].CopyColorBufferToBlocks(spectralStep);
}
this.pixelRowCounter += this.pixelRowsPerStep;
}

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

@ -131,28 +131,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Write the scan header.
this.WriteStartOfScan(componentCount, componentIds);
var quantTables = new Block8x8F[] { luminanceQuantTable, chrominanceQuantTable };
new HuffmanScanEncoder(3, stream).Encode(image, quantTables, Configuration.Default, cancellationToken);
//var quantTables = new Block8x8F[] { luminanceQuantTable, chrominanceQuantTable };
//new HuffmanScanEncoder(3, stream).Encode(image, quantTables, Configuration.Default, cancellationToken);
// Write the scan compressed data.
//switch (this.colorType)
//{
// case JpegColorType.YCbCrRatio444:
// new HuffmanScanEncoder(3, stream).Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
// break;
// case JpegColorType.YCbCrRatio420:
// new HuffmanScanEncoder(6, stream).Encode420(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
// break;
// case JpegColorType.Luminance:
// new HuffmanScanEncoder(1, stream).EncodeGrayscale(image, ref luminanceQuantTable, cancellationToken);
// break;
// case JpegColorType.Rgb:
// new HuffmanScanEncoder(3, stream).EncodeRgb(image, ref luminanceQuantTable, cancellationToken);
// break;
// default:
// // all other non-supported color types are checked at the start of this method
// break;
//}
switch (this.colorType)
{
case JpegColorType.YCbCrRatio444:
new HuffmanScanEncoder(3, stream).Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
break;
case JpegColorType.YCbCrRatio420:
new HuffmanScanEncoder(6, stream).Encode420(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
break;
case JpegColorType.Luminance:
new HuffmanScanEncoder(1, stream).EncodeGrayscale(image, ref luminanceQuantTable, cancellationToken);
break;
case JpegColorType.Rgb:
new HuffmanScanEncoder(3, stream).EncodeRgb(image, ref luminanceQuantTable, cancellationToken);
break;
default:
// all other non-supported color types are checked at the start of this method
break;
}
// Write the End Of Image marker.
this.WriteEndOfImageMarker();

37
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -198,6 +198,43 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <summary>
/// Bulk operation that unpacks pixels from <paramref name="source"/>
/// into 3 seperate RGB channels. The destination must have a padding of 3.
/// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="redChannel">A <see cref="ReadOnlySpan{T}"/> to the red values.</param>
/// <param name="greenChannel">A <see cref="ReadOnlySpan{T}"/> to the green values.</param>
/// <param name="blueChannel">A <see cref="ReadOnlySpan{T}"/> to the blue values.</param>
/// <param name="source">A <see cref="Span{T}"/> to the destination pixels.</param>
internal virtual void UnpackIntoRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
Span<TPixel> source)
{
Guard.NotNull(configuration, nameof(configuration));
int count = redChannel.Length;
Rgba32 rgba32 = default;
ref byte r = ref MemoryMarshal.GetReference(redChannel);
ref byte g = ref MemoryMarshal.GetReference(greenChannel);
ref byte b = ref MemoryMarshal.GetReference(blueChannel);
ref TPixel d = ref MemoryMarshal.GetReference(source);
for (int i = 0; i < count; i++)
{
// TODO: Create ToRgb24 method in IPixel
// TODO: create a fast intrinsic accelerated Rgb24 -> r/g/b planes overload
Unsafe.Add(ref d, i).ToRgba32(ref rgba32);
Unsafe.Add(ref r, i) = rgba32.R;
Unsafe.Add(ref g, i) = rgba32.G;
Unsafe.Add(ref b, i) = rgba32.B;
}
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void GuardPackFromRgbPlanes(ReadOnlySpan<byte> greenChannel, ReadOnlySpan<byte> blueChannel, Span<TPixel> destination, int count)
{

Loading…
Cancel
Save