mirror of https://github.com/SixLabors/ImageSharp
7 changed files with 474 additions and 45 deletions
@ -0,0 +1,157 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
internal sealed class DownScalingComponentProcessor2 : ComponentProcessor |
|||
{ |
|||
private readonly IRawJpegData rawJpeg; |
|||
|
|||
public DownScalingComponentProcessor2(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) |
|||
: base(memoryAllocator, frame, postProcessorBufferSize, component, 4) |
|||
=> this.rawJpeg = rawJpeg; |
|||
|
|||
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 dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex]; |
|||
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
|
|||
TransformIDCT_4x4(ref workspaceBlock, ref dequantTable, normalizationValue, maximumValue); |
|||
|
|||
// Save to the intermediate buffer
|
|||
int xColorBufferStart = xBlock * this.BlockAreaSize.Width; |
|||
ScaledCopyTo( |
|||
ref workspaceBlock, |
|||
ref colorBufferRow[xColorBufferStart], |
|||
destAreaStride, |
|||
subSamplingDivisors.Width, |
|||
subSamplingDivisors.Height); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void TransformIDCT_4x4(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue) |
|||
{ |
|||
const int DCTSIZE = 8; |
|||
const float FIX_0_541196100 = 0.541196100f; |
|||
const float FIX_0_765366865 = 0.765366865f; |
|||
const float FIX_1_847759065 = 1.847759065f; |
|||
|
|||
// input block is transposed so term indices must be tranposed too
|
|||
float tmp0, tmp2, tmp10, tmp12; |
|||
float z1, z2, z3; |
|||
|
|||
for (int ctr = 0; ctr < 4; ctr++) |
|||
{ |
|||
// Even part
|
|||
tmp0 = block[ctr * DCTSIZE] * dequantTable[ctr * DCTSIZE]; |
|||
tmp2 = block[(ctr * DCTSIZE) + 2] * dequantTable[(ctr * DCTSIZE) + 2]; |
|||
|
|||
tmp10 = tmp0 + tmp2; |
|||
tmp12 = tmp0 - tmp2; |
|||
|
|||
// Odd part
|
|||
z2 = block[(ctr * DCTSIZE) + 1] * dequantTable[(ctr * DCTSIZE) + 1]; |
|||
z3 = block[(ctr * DCTSIZE) + 3] * dequantTable[(ctr * DCTSIZE) + 3]; |
|||
|
|||
z1 = (z2 + z3) * FIX_0_541196100; |
|||
tmp0 = z1 + (z2 * FIX_0_765366865); |
|||
tmp2 = z1 - (z3 * FIX_1_847759065); |
|||
|
|||
/* Final output stage */ |
|||
block[ctr + 4] = tmp10 + tmp0; |
|||
block[ctr + 28] = tmp10 - tmp0; |
|||
block[ctr + 12] = tmp12 + tmp2; |
|||
block[ctr + 20] = tmp12 - tmp2; |
|||
} |
|||
|
|||
for (int ctr = 0; ctr < 4; ctr++) |
|||
{ |
|||
// Even part
|
|||
tmp0 = block[(ctr * 8) + 0 + 4]; |
|||
tmp2 = block[(ctr * 8) + 2 + 4]; |
|||
|
|||
tmp10 = tmp0 + tmp2; |
|||
tmp12 = tmp0 - tmp2; |
|||
|
|||
// Odd part
|
|||
z2 = block[(ctr * 8) + 1 + 4]; |
|||
z3 = block[(ctr * 8) + 3 + 4]; |
|||
|
|||
z1 = (z2 + z3) * FIX_0_541196100; |
|||
tmp0 = z1 + (z2 * FIX_0_765366865); |
|||
tmp2 = z1 - (z3 * FIX_1_847759065); |
|||
|
|||
/* Final output stage */ |
|||
block[(ctr * 8) + 0] = (float)Math.Round(Numerics.Clamp(tmp10 + tmp0 + normalizationValue, 0, maxValue)); |
|||
block[(ctr * 8) + 3] = (float)Math.Round(Numerics.Clamp(tmp10 - tmp0 + normalizationValue, 0, maxValue)); |
|||
block[(ctr * 8) + 1] = (float)Math.Round(Numerics.Clamp(tmp12 + tmp2 + normalizationValue, 0, maxValue)); |
|||
block[(ctr * 8) + 2] = (float)Math.Round(Numerics.Clamp(tmp12 - tmp2 + normalizationValue, 0, maxValue)); |
|||
} |
|||
} |
|||
|
|||
[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, baseIdx + j) = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,127 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
internal sealed class DownScalingComponentProcessor4 : ComponentProcessor |
|||
{ |
|||
private readonly IRawJpegData rawJpeg; |
|||
|
|||
public DownScalingComponentProcessor4(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) |
|||
: base(memoryAllocator, frame, postProcessorBufferSize, component, 2) |
|||
=> this.rawJpeg = rawJpeg; |
|||
|
|||
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 dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex]; |
|||
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
|
|||
TransformIDCT_2x2(ref workspaceBlock, ref dequantTable, normalizationValue, maximumValue); |
|||
|
|||
// Save to the intermediate buffer
|
|||
int xColorBufferStart = xBlock * this.BlockAreaSize.Width; |
|||
ScaledCopyTo( |
|||
ref workspaceBlock, |
|||
ref colorBufferRow[xColorBufferStart], |
|||
destAreaStride, |
|||
subSamplingDivisors.Width, |
|||
subSamplingDivisors.Height); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void TransformIDCT_2x2(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue) |
|||
{ |
|||
// input block is transposed so term indices must be tranposed too
|
|||
float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; |
|||
|
|||
// 0
|
|||
// => 0 1
|
|||
// 8
|
|||
tmp4 = block[0] * dequantTable[0]; |
|||
tmp5 = block[1] * dequantTable[1]; |
|||
tmp0 = tmp4 + tmp5; |
|||
tmp2 = tmp4 - tmp5; |
|||
|
|||
// 1
|
|||
// => 8 9
|
|||
// 9
|
|||
tmp4 = block[8] * dequantTable[8]; |
|||
tmp5 = block[9] * dequantTable[9]; |
|||
tmp1 = tmp4 + tmp5; |
|||
tmp3 = tmp4 - tmp5; |
|||
|
|||
// Row 0
|
|||
block[0] = (float)Math.Round(Numerics.Clamp(tmp0 + tmp1 + normalizationValue, 0, maxValue)); |
|||
block[1] = (float)Math.Round(Numerics.Clamp(tmp0 - tmp1 + normalizationValue, 0, maxValue)); |
|||
|
|||
// Row 1
|
|||
block[8] = (float)Math.Round(Numerics.Clamp(tmp2 + tmp3 + normalizationValue, 0, maxValue)); |
|||
block[9] = (float)Math.Round(Numerics.Clamp(tmp2 - tmp3 + normalizationValue, 0, maxValue)); |
|||
} |
|||
|
|||
[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 < 2; y++) |
|||
{ |
|||
int yy = y * verticalScale; |
|||
int y8 = y * 8; |
|||
|
|||
for (int x = 0; x < 2; 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, baseIdx + j) = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue