mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
7.0 KiB
164 lines
7.0 KiB
// Copyright (c) Six Labors and contributors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using SixLabors.ImageSharp.Advanced;
|
|
using SixLabors.ImageSharp.PixelFormats;
|
|
using SixLabors.Memory;
|
|
using SixLabors.Primitives;
|
|
using JpegColorConverter = SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters.JpegColorConverter;
|
|
|
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
|
|
{
|
|
/// <summary>
|
|
/// Encapsulates the execution od post-processing algorithms to be applied on a <see cref="IRawJpegData"/> to produce a valid <see cref="Image{TPixel}"/>: <br/>
|
|
/// (1) Dequantization <br/>
|
|
/// (2) IDCT <br/>
|
|
/// (3) Color conversion form one of the <see cref="JpegColorSpace"/>-s into a <see cref="Vector4"/> buffer of RGBA values <br/>
|
|
/// (4) Packing <see cref="Image{TPixel}"/> pixels from the <see cref="Vector4"/> buffer. <br/>
|
|
/// These operations are executed in <see cref="NumberOfPostProcessorSteps"/> steps.
|
|
/// <see cref="PixelRowsPerStep"/> image rows are converted in one step,
|
|
/// which means that size of the allocated memory is limited (does not depend on <see cref="ImageFrame{TPixel}.Height"/>).
|
|
/// </summary>
|
|
internal class JpegImagePostProcessor : IDisposable
|
|
{
|
|
/// <summary>
|
|
/// The number of block rows to be processed in one Step.
|
|
/// </summary>
|
|
public const int BlockRowsPerStep = 4;
|
|
|
|
/// <summary>
|
|
/// The number of image pixel rows to be processed in one step.
|
|
/// </summary>
|
|
public const int PixelRowsPerStep = 4 * 8;
|
|
|
|
/// <summary>
|
|
/// Temporal buffer to store a row of colors.
|
|
/// </summary>
|
|
private readonly IBuffer<Vector4> rgbaBuffer;
|
|
|
|
/// <summary>
|
|
/// The <see cref="JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.
|
|
/// </summary>
|
|
private readonly JpegColorConverter colorConverter;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
|
|
/// </summary>
|
|
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
|
|
/// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param>
|
|
public JpegImagePostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg)
|
|
{
|
|
this.RawJpeg = rawJpeg;
|
|
IJpegComponent c0 = rawJpeg.Components.First();
|
|
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
|
|
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
|
|
|
|
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
|
|
this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
|
|
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="JpegComponentPostProcessor"/> instances.
|
|
/// </summary>
|
|
public JpegComponentPostProcessor[] ComponentProcessors { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="IRawJpegData"/> to be processed.
|
|
/// </summary>
|
|
public IRawJpegData RawJpeg { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the total number of post processor steps deduced from the height of the image and <see cref="PixelRowsPerStep"/>.
|
|
/// </summary>
|
|
public int NumberOfPostProcessorSteps { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the size of the temporary buffers we need to allocate into <see cref="JpegComponentPostProcessor.ColorBuffer"/>.
|
|
/// </summary>
|
|
public Size PostProcessorBufferSize { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the value of the counter that grows by each step by <see cref="PixelRowsPerStep"/>.
|
|
/// </summary>
|
|
public int PixelRowCounter { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
|
|
{
|
|
cpp.Dispose();
|
|
}
|
|
|
|
this.rgbaBuffer.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process all pixels into 'destination'. The image dimensions should match <see cref="RawJpeg"/>.
|
|
/// </summary>
|
|
/// <typeparam name="TPixel">The pixel type</typeparam>
|
|
/// <param name="destination">The destination image</param>
|
|
public void PostProcess<TPixel>(ImageFrame<TPixel> destination)
|
|
where TPixel : struct, IPixel<TPixel>
|
|
{
|
|
this.PixelRowCounter = 0;
|
|
|
|
if (this.RawJpeg.ImageSizeInPixels != destination.Size())
|
|
{
|
|
throw new ArgumentException("Input image is not of the size of the processed one!");
|
|
}
|
|
|
|
while (this.PixelRowCounter < this.RawJpeg.ImageSizeInPixels.Height)
|
|
{
|
|
this.DoPostProcessorStep(destination);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute one step processing <see cref="PixelRowsPerStep"/> pixel rows into 'destination'.
|
|
/// </summary>
|
|
/// <typeparam name="TPixel">The pixel type</typeparam>
|
|
/// <param name="destination">The destination image.</param>
|
|
public void DoPostProcessorStep<TPixel>(ImageFrame<TPixel> destination)
|
|
where TPixel : struct, IPixel<TPixel>
|
|
{
|
|
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
|
|
{
|
|
cpp.CopyBlocksToColorBuffer();
|
|
}
|
|
|
|
this.ConvertColorsInto(destination);
|
|
|
|
this.PixelRowCounter += PixelRowsPerStep;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert and copy <see cref="PixelRowsPerStep"/> row of colors into 'destination' starting at row <see cref="PixelRowCounter"/>.
|
|
/// </summary>
|
|
/// <typeparam name="TPixel">The pixel type</typeparam>
|
|
/// <param name="destination">The destination image</param>
|
|
private void ConvertColorsInto<TPixel>(ImageFrame<TPixel> destination)
|
|
where TPixel : struct, IPixel<TPixel>
|
|
{
|
|
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
|
|
|
|
Buffer2D<float>[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray();
|
|
|
|
for (int yy = this.PixelRowCounter; yy < maxY; yy++)
|
|
{
|
|
int y = yy - this.PixelRowCounter;
|
|
|
|
var values = new JpegColorConverter.ComponentValues(buffers, y);
|
|
this.colorConverter.ConvertToRgba(values, this.rgbaBuffer.GetSpan());
|
|
|
|
Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
|
|
|
|
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer.GetSpan(), destRow, destination.Width);
|
|
}
|
|
}
|
|
}
|
|
}
|