Browse Source

Chroma subsampling for downscaling decoder

pull/2076/head
Dmitry Pentin 4 years ago
parent
commit
f4d5f1a760
  1. 46
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
  2. 8
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

46
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -19,8 +20,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks; Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks;
float maximumValue = this.Frame.MaxColorChannelValue; float maximumValue = this.Frame.MaxColorChannelValue;
float normalizationValue = MathF.Ceiling(maximumValue / 2);
int destAreaStride = this.ColorBuffer.Width;
int blocksRowsPerStep = this.Component.SamplingFactors.Height; int blocksRowsPerStep = this.Component.SamplingFactors.Height;
Size subSamplingDivisors = this.Component.SubSamplingDivisors;
int yBlockStart = spectralStep * blocksRowsPerStep; int yBlockStart = spectralStep * blocksRowsPerStep;
@ -33,20 +38,55 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
{ {
// get Direct current term - averaged 8x8 pixel value // get direct current term - averaged 8x8 pixel value
float dc = blockRow[xBlock][0]; float dc = blockRow[xBlock][0];
// dequantization // dequantization
dc *= this.dcDequantizer; dc *= this.dcDequantizer;
// Normalize & round // Normalize & round
dc = (float)Math.Round(Numerics.Clamp(dc + MathF.Ceiling(maximumValue / 2), 0, maximumValue)); dc = (float)Math.Round(Numerics.Clamp(dc + normalizationValue, 0, maximumValue));
// Save to the intermediate buffer // Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width; int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
colorBufferRow[xColorBufferStart] = dc; ScaledCopyTo(
dc,
ref colorBufferRow[xColorBufferStart],
destAreaStride,
subSamplingDivisors.Width,
subSamplingDivisors.Height);
} }
} }
} }
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(float value, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
Unsafe.Add(ref destRef, 0) = value;
return;
}
if (horizontalScale == 2 && verticalScale == 2)
{
Unsafe.Add(ref destRef, 0) = value;
Unsafe.Add(ref destRef, 1) = value;
Unsafe.Add(ref destRef, 0 + destStrideWidth) = value;
Unsafe.Add(ref destRef, 1 + destStrideWidth) = value;
return;
}
// TODO: Optimize: implement all cases with scale-specific, loopless code!
for (int y = 0; y < verticalScale; y++)
{
for (int x = 0; x < horizontalScale; x++)
{
Unsafe.Add(ref destRef, x) = value;
}
destRef = ref Unsafe.Add(ref destRef, destStrideWidth);
}
}
} }
} }

8
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -241,9 +241,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.paddedProxyPixelRow = allocator.Allocate<TPixel>(pixelSize.Width + 3); this.paddedProxyPixelRow = allocator.Allocate<TPixel>(pixelSize.Width + 3);
// component processors from spectral to RGB // component processors from spectral to RGB
// TODO: refactor this mess
int bufferWidth = majorBlockWidth * blockPixelSize; int bufferWidth = majorBlockWidth * blockPixelSize;
int batchSize = converter.ElementsPerBatch; int batchSize = converter.ElementsPerBatch;
int converterAlignedBufferWidth = bufferWidth + (batchSize - (bufferWidth % batchSize)); int batchRemainder = bufferWidth % batchSize;
int converterAlignedBufferWidth = bufferWidth + (batchRemainder != 0 ? batchSize - batchRemainder : 0);
var postProcessorBufferSize = new Size(converterAlignedBufferWidth, this.pixelRowsPerStep); var postProcessorBufferSize = new Size(converterAlignedBufferWidth, this.pixelRowsPerStep);
this.componentProcessors = new ComponentProcessor[frame.Components.Length]; this.componentProcessors = new ComponentProcessor[frame.Components.Length];
switch (blockPixelSize) switch (blockPixelSize)
@ -262,8 +264,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
break; break;
default:
// TODO: default? throw new Exception("This is a debug message which should NEVER be seen in release build");
} }
// single 'stride' rgba32 buffer for conversion between spectral and TPixel // single 'stride' rgba32 buffer for conversion between spectral and TPixel

Loading…
Cancel
Save