-
-
Notifications
You must be signed in to change notification settings - Fork 851
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2076 from br3aker/dp/jpeg-downscaling-decode
Jpeg downscaling decoding
- Loading branch information
Showing
32 changed files
with
1,273 additions
and
489 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/ComponentProcessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright (c) Six Labors. | ||
// Licensed under the Six Labors Split License. | ||
|
||
using System; | ||
using SixLabors.ImageSharp.Memory; | ||
|
||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
{ | ||
/// <summary> | ||
/// Base class for processing component spectral data and converting it to raw color data. | ||
/// </summary> | ||
internal abstract class ComponentProcessor : IDisposable | ||
{ | ||
public ComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, Size postProcessorBufferSize, IJpegComponent component, int blockSize) | ||
{ | ||
this.Frame = frame; | ||
this.Component = component; | ||
|
||
this.BlockAreaSize = component.SubSamplingDivisors * blockSize; | ||
this.ColorBuffer = memoryAllocator.Allocate2DOveraligned<float>( | ||
postProcessorBufferSize.Width, | ||
postProcessorBufferSize.Height, | ||
this.BlockAreaSize.Height); | ||
} | ||
|
||
protected JpegFrame Frame { get; } | ||
|
||
protected IJpegComponent Component { get; } | ||
|
||
protected Buffer2D<float> ColorBuffer { get; } | ||
|
||
protected Size BlockAreaSize { get; } | ||
|
||
/// <summary> | ||
/// Converts spectral data to color data accessible via <see cref="GetColorBufferRowSpan(int)"/>. | ||
/// </summary> | ||
/// <param name="row">Spectral row index to convert.</param> | ||
public abstract void CopyBlocksToColorBuffer(int row); | ||
|
||
/// <summary> | ||
/// Clears spectral buffers. | ||
/// </summary> | ||
/// <remarks> | ||
/// Should only be called during baseline interleaved decoding. | ||
/// </remarks> | ||
public void ClearSpectralBuffers() | ||
{ | ||
Buffer2D<Block8x8> spectralBlocks = this.Component.SpectralBlocks; | ||
for (int i = 0; i < spectralBlocks.Height; i++) | ||
{ | ||
spectralBlocks.DangerousGetRowSpan(i).Clear(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets converted color buffer row. | ||
/// </summary> | ||
/// <param name="row">Row index.</param> | ||
/// <returns>Color buffer row.</returns> | ||
public Span<float> GetColorBufferRowSpan(int row) => | ||
this.ColorBuffer.DangerousGetRowSpan(row); | ||
|
||
public void Dispose() => this.ColorBuffer.Dispose(); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
...mageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) Six Labors. | ||
// Licensed under the Six Labors Split License. | ||
|
||
using System; | ||
using SixLabors.ImageSharp.Memory; | ||
|
||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
{ | ||
/// <summary> | ||
/// Processes component spectral data and converts it to color data in 1-to-1 scale. | ||
/// </summary> | ||
internal sealed class DirectComponentProcessor : ComponentProcessor | ||
{ | ||
private Block8x8F dequantizationTable; | ||
|
||
public DirectComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) | ||
: base(memoryAllocator, frame, postProcessorBufferSize, component, blockSize: 8) | ||
{ | ||
this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex]; | ||
FloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable); | ||
} | ||
|
||
public override void CopyBlocksToColorBuffer(int spectralStep) | ||
{ | ||
Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks; | ||
|
||
float maximumValue = this.Frame.MaxColorChannelValue; | ||
|
||
int destAreaStride = this.ColorBuffer.Width; | ||
|
||
int blocksRowsPerStep = this.Component.SamplingFactors.Height; | ||
|
||
int yBlockStart = spectralStep * blocksRowsPerStep; | ||
|
||
Size subSamplingDivisors = this.Component.SubSamplingDivisors; | ||
|
||
Block8x8F workspaceBlock = default; | ||
|
||
for (int y = 0; y < blocksRowsPerStep; y++) | ||
{ | ||
int yBuffer = y * this.BlockAreaSize.Height; | ||
|
||
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); | ||
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); | ||
|
||
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) | ||
{ | ||
// Integer to float | ||
workspaceBlock.LoadFrom(ref blockRow[xBlock]); | ||
|
||
// Dequantize | ||
workspaceBlock.MultiplyInPlace(ref this.dequantizationTable); | ||
|
||
// Convert from spectral to color | ||
FloatingPointDCT.TransformIDCT(ref workspaceBlock); | ||
|
||
// To conform better to libjpeg we actually NEED TO loose precision here. | ||
// This is because they store blocks as Int16 between all the operations. | ||
// To be "more accurate", we need to emulate this by rounding! | ||
workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); | ||
|
||
// Write to color buffer acording to sampling factors | ||
int xColorBufferStart = xBlock * this.BlockAreaSize.Width; | ||
workspaceBlock.ScaledCopyTo( | ||
ref colorBufferRow[xColorBufferStart], | ||
destAreaStride, | ||
subSamplingDivisors.Width, | ||
subSamplingDivisors.Height); | ||
} | ||
} | ||
} | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
...arp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright (c) Six Labors. | ||
// Licensed under the Six Labors Split License. | ||
|
||
using System; | ||
using System.Runtime.CompilerServices; | ||
using SixLabors.ImageSharp.Memory; | ||
|
||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
{ | ||
/// <summary> | ||
/// Processes component spectral data and converts it to color data in 2-to-1 scale. | ||
/// </summary> | ||
internal sealed class DownScalingComponentProcessor2 : ComponentProcessor | ||
{ | ||
private Block8x8F dequantizationTable; | ||
|
||
public DownScalingComponentProcessor2(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) | ||
: base(memoryAllocator, frame, postProcessorBufferSize, component, 4) | ||
{ | ||
this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex]; | ||
ScaledFloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable); | ||
} | ||
|
||
public override void CopyBlocksToColorBuffer(int spectralStep) | ||
{ | ||
Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks; | ||
|
||
float maximumValue = this.Frame.MaxColorChannelValue; | ||
float normalizationValue = MathF.Ceiling(maximumValue / 2); | ||
|
||
int destAreaStride = this.ColorBuffer.Width; | ||
|
||
int blocksRowsPerStep = this.Component.SamplingFactors.Height; | ||
Size subSamplingDivisors = this.Component.SubSamplingDivisors; | ||
|
||
Block8x8F workspaceBlock = default; | ||
|
||
int yBlockStart = spectralStep * blocksRowsPerStep; | ||
|
||
for (int y = 0; y < blocksRowsPerStep; y++) | ||
{ | ||
int yBuffer = y * this.BlockAreaSize.Height; | ||
|
||
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); | ||
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); | ||
|
||
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) | ||
{ | ||
// Integer to float | ||
workspaceBlock.LoadFrom(ref blockRow[xBlock]); | ||
|
||
// IDCT/Normalization/Range | ||
ScaledFloatingPointDCT.TransformIDCT_4x4(ref workspaceBlock, ref this.dequantizationTable, normalizationValue, maximumValue); | ||
|
||
// Save to the intermediate buffer | ||
int xColorBufferStart = xBlock * this.BlockAreaSize.Width; | ||
ScaledCopyTo( | ||
ref workspaceBlock, | ||
ref colorBufferRow[xColorBufferStart], | ||
destAreaStride, | ||
subSamplingDivisors.Width, | ||
subSamplingDivisors.Height); | ||
} | ||
} | ||
} | ||
|
||
[MethodImpl(InliningOptions.ShortMethod)] | ||
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale) | ||
{ | ||
// TODO: Optimize: implement all cases with scale-specific, loopless code! | ||
CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale); | ||
|
||
[MethodImpl(InliningOptions.ColdPath)] | ||
static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) | ||
{ | ||
for (int y = 0; y < 4; y++) | ||
{ | ||
int yy = y * verticalScale; | ||
int y8 = y * 8; | ||
|
||
for (int x = 0; x < 4; x++) | ||
{ | ||
int xx = x * horizontalScale; | ||
|
||
float value = block[y8 + x]; | ||
|
||
for (int i = 0; i < verticalScale; i++) | ||
{ | ||
int baseIdx = ((yy + i) * areaStride) + xx; | ||
|
||
for (int j = 0; j < horizontalScale; j++) | ||
{ | ||
// area[xx + j, yy + i] = value; | ||
Unsafe.Add(ref areaOrigin, (nint)(uint)(baseIdx + j)) = value; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.