Browse Source

Merge branch 'jpeg-lab' of https://github.com/SixLabors/ImageSharp into jpeg-lab

af/merge-core
James Jackson-South 9 years ago
parent
commit
68cc3ede9e
  1. 2
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  2. 4
      src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
  3. 99
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
  4. 13
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs

2
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -424,7 +424,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="qtPtr">Qt pointer</param> /// <param name="qtPtr">Qt pointer</param>
/// <param name="unzigPtr">Unzig pointer</param> /// <param name="unzigPtr">Unzig pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void QuantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr)
{ {
float* b = (float*)blockPtr; float* b = (float*)blockPtr;
float* qtp = (float*)qtPtr; float* qtp = (float*)qtPtr;

4
src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs

@ -38,8 +38,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
int QuantizationTableIndex { get; } int QuantizationTableIndex { get; }
/// <summary> /// <summary>
/// Gets the <see cref="Buffer2D{Block8x8}"/> storing the "raw" frequency-domain decoded blocks. /// Gets the <see cref="Buffer2D{Block8x8}"/> storing the "raw" frequency-domain decoded + unzigged blocks.
/// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. /// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
/// </summary> /// </summary>
Buffer2D<Block8x8> SpectralBlocks { get; } Buffer2D<Block8x8> SpectralBlocks { get; }
} }

99
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs

@ -1,26 +1,48 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{ {
/// <summary>
/// Encapsulates the execution 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="ImageBase{TPixel}.Height"/>).
/// </summary>
internal class JpegImagePostProcessor : IDisposable internal class JpegImagePostProcessor : IDisposable
{ {
/// <summary>
/// The number of block rows to be processed in one Step.
/// </summary>
public const int BlockRowsPerStep = 4; 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; public const int PixelRowsPerStep = 4 * 8;
/// <summary>
/// Temporal buffer to store a row of colors.
/// </summary>
private readonly Buffer<Vector4> rgbaBuffer; private readonly Buffer<Vector4> rgbaBuffer;
/// <summary>
/// The <see cref="JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.
/// </summary>
private JpegColorConverter colorConverter; private JpegColorConverter colorConverter;
/// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
/// </summary>
/// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param>
public JpegImagePostProcessor(IRawJpegData rawJpeg) public JpegImagePostProcessor(IRawJpegData rawJpeg)
{ {
this.RawJpeg = rawJpeg; this.RawJpeg = rawJpeg;
@ -33,16 +55,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
} }
/// <summary>
/// Gets the <see cref="JpegComponentPostProcessor"/> instances.
/// </summary>
public JpegComponentPostProcessor[] ComponentProcessors { get; } public JpegComponentPostProcessor[] ComponentProcessors { get; }
/// <summary>
/// Gets the <see cref="IRawJpegData"/> to be processed.
/// </summary>
public IRawJpegData RawJpeg { get; } 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; } public int NumberOfPostProcessorSteps { get; }
/// <summary>
/// Gets the size of the temporal buffers we need to allocate into <see cref="JpegComponentPostProcessor.ColorBuffer"/>.
/// </summary>
public Size PostProcessorBufferSize { get; } public Size PostProcessorBufferSize { get; }
public int CurrentImageRowInPixels { get; private set; } /// <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() public void Dispose()
{ {
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
@ -53,43 +91,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.rgbaBuffer.Dispose(); this.rgbaBuffer.Dispose();
} }
public void DoPostProcessorStep<TPixel>(Image<TPixel> destination) /// <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>(Image<TPixel> destination)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) this.PixelRowCounter = 0;
if (this.RawJpeg.ImageSizeInPixels != destination.Size())
{ {
cpp.CopyBlocksToColorBuffer(); throw new ArgumentException("Input image is not of the size of the processed one!");
} }
this.ConvertColors(destination); while (this.PixelRowCounter < this.RawJpeg.ImageSizeInPixels.Height)
{
this.CurrentImageRowInPixels += PixelRowsPerStep; this.DoPostProcessorStep(destination);
}
} }
public void PostProcess<TPixel>(Image<TPixel> destination) /// <summary>
/// Execute one step rocessing <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>(Image<TPixel> destination)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.RawJpeg.ImageSizeInPixels != destination.Size()) foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
{ {
throw new ArgumentException("Input image is not of the size of the processed one!"); cpp.CopyBlocksToColorBuffer();
} }
while (this.CurrentImageRowInPixels < this.RawJpeg.ImageSizeInPixels.Height) this.ConvertColorsInto(destination);
{
this.DoPostProcessorStep(destination); this.PixelRowCounter += PixelRowsPerStep;
}
} }
private void ConvertColors<TPixel>(Image<TPixel> destination) /// <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>(Image<TPixel> destination)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
int maxY = Math.Min(destination.Height, this.CurrentImageRowInPixels + PixelRowsPerStep); int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
Buffer2D<float>[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray(); Buffer2D<float>[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray();
for (int yy = this.CurrentImageRowInPixels; yy < maxY; yy++) for (int yy = this.PixelRowCounter; yy < maxY; yy++)
{ {
int y = yy - this.CurrentImageRowInPixels; int y = yy - this.PixelRowCounter;
var values = new JpegColorConverter.ComponentValues(buffers, y); var values = new JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer); this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer);

13
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs

@ -1,18 +1,15 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Encapsulates the implementation of processing "raw" <see cref="Buffer{T}"/>-s into Jpeg image channels. /// Encapsulates the implementation of processing "raw" <see cref="Buffer{T}"/>-s into Jpeg image channels.
/// </summary> /// </summary>
@ -63,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
Block8x8F* b = this.pointers.SourceBlock; Block8x8F* b = this.pointers.SourceBlock;
Block8x8F.QuantizeBlock(b, this.pointers.QuantiazationTable, this.pointers.Unzig); Block8x8F.DequantizeBlock(b, this.pointers.QuantiazationTable, this.pointers.Unzig);
FastFloatingPointDCT.TransformIDCT(ref *b, ref this.data.WorkspaceBlock1, ref this.data.WorkspaceBlock2); FastFloatingPointDCT.TransformIDCT(ref *b, ref this.data.WorkspaceBlock1, ref this.data.WorkspaceBlock2);
} }
@ -77,13 +74,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.QuantizeAndTransform(decoder, component, ref sourceBlock); this.QuantizeAndTransform(decoder, component, ref sourceBlock);
this.data.WorkspaceBlock1.NormalizeColorsInplace(); this.data.WorkspaceBlock1.NormalizeColorsInplace();
Size divs = component.SubSamplingDivisors;
// To conform better to libjpeg we actually NEED TO loose precision here. // To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations. // This is because they store blocks as Int16 between all the operations.
// Unfortunately, we need to emulate this to be "more accurate" :( // Unfortunately, we need to emulate this to be "more accurate" :(
this.data.WorkspaceBlock1.RoundInplace(); this.data.WorkspaceBlock1.RoundInplace();
Size divs = component.SubSamplingDivisors;
this.data.WorkspaceBlock1.CopyTo(destArea, divs.Width, divs.Height); this.data.WorkspaceBlock1.CopyTo(destArea, divs.Width, divs.Height);
} }

Loading…
Cancel
Save