Browse Source

block scaling bottleneck -> eliminated

pull/1143/head
Anton Firszov 7 years ago
parent
commit
7b7e50244d
  1. 33
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  2. 14
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  3. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  4. 2
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs

33
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -16,28 +16,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale) public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale)
{
ref float areaOrigin = ref area.GetReferenceToOrigin();
this.CopyTo(ref areaOrigin, area.Stride, horizontalScale, verticalScale);
}
[MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{ {
if (horizontalScale == 1 && verticalScale == 1) if (horizontalScale == 1 && verticalScale == 1)
{ {
this.Copy1x1Scale(area); this.Copy1x1Scale(ref areaOrigin, areaStride);
return; return;
} }
if (horizontalScale == 2 && verticalScale == 2) if (horizontalScale == 2 && verticalScale == 2)
{ {
this.Copy2x2Scale(area); this.Copy2x2Scale(ref areaOrigin, areaStride);
return; return;
} }
// TODO: Optimize: implement all cases with scale-specific, loopless code! // TODO: Optimize: implement all cases with scale-specific, loopless code!
this.CopyArbitraryScale(area, horizontalScale, verticalScale); this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale);
} }
public void Copy1x1Scale(in BufferArea<float> destination) public void Copy1x1Scale(ref float areaOrigin, int areaStride)
{ {
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref destination.GetReferenceToOrigin()); ref byte destBase = ref Unsafe.As<float, byte>(ref areaOrigin);
int destStride = destination.Stride * sizeof(float); int destStride = areaStride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0); CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1); CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
@ -57,10 +64,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
} }
private void Copy2x2Scale(in BufferArea<float> area) private void Copy2x2Scale(ref float areaOrigin, int areaStride)
{ {
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref area.GetReferenceToOrigin()); ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin);
int destStride = area.Stride / 2; int destStride = areaStride / 2;
this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
@ -110,10 +117,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
private void CopyArbitraryScale(BufferArea<float> area, int horizontalScale, int verticalScale) private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{ {
ref float destBase = ref area.GetReferenceToOrigin();
for (int y = 0; y < 8; y++) for (int y = 0; y < 8; y++)
{ {
int yy = y * verticalScale; int yy = y * verticalScale;
@ -127,12 +132,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
for (int i = 0; i < verticalScale; i++) for (int i = 0; i < verticalScale; i++)
{ {
int baseIdx = ((yy + i) * area.Stride) + xx; int baseIdx = ((yy + i) * areaStride) + xx;
for (int j = 0; j < horizontalScale; j++) for (int j = 0; j < horizontalScale; j++)
{ {
// area[xx + j, yy + i] = value; // area[xx + j, yy + i] = value;
Unsafe.Add(ref destBase, baseIdx + j) = value; Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
} }
} }
} }

14
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -68,11 +68,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>. /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>.
/// </summary> /// </summary>
/// <param name="sourceBlock">The source block.</param> /// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param> /// <param name="destAreaOrigin">Reference to the origin of the destination pixel area.</param>
/// <param name="destAreaStride">The width of the destination pixel buffer.</param>
/// <param name="maximumValue">The maximum value derived from the bitdepth.</param> /// <param name="maximumValue">The maximum value derived from the bitdepth.</param>
public void ProcessBlockColorsInto( public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock, ref Block8x8 sourceBlock,
in BufferArea<float> destArea, ref float destAreaOrigin,
int destAreaStride,
float maximumValue) float maximumValue)
{ {
ref Block8x8F b = ref this.SourceBlock; ref Block8x8F b = ref this.SourceBlock;
@ -88,7 +90,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// To be "more accurate", we need to emulate this by rounding! // To be "more accurate", we need to emulate this by rounding!
this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue); this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue);
this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height); this.WorkspaceBlock1.CopyTo(
ref destAreaOrigin,
destAreaStride,
this.subSamplingDivisors.Width,
this.subSamplingDivisors.Height);
} }
} }
} }

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

@ -79,6 +79,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component); var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component);
float maximumValue = MathF.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1; float maximumValue = MathF.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1;
int destAreaStride = this.ColorBuffer.Width;
for (int y = 0; y < this.BlockRowsPerStep; y++) for (int y = 0; y < this.BlockRowsPerStep; y++)
{ {
int yBlock = this.currentComponentRowInBlocks + y; int yBlock = this.currentComponentRowInBlocks + y;
@ -90,22 +92,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int yBuffer = y * this.blockAreaSize.Height; int yBuffer = y * this.blockAreaSize.Height;
Span<float> colorBufferRow = this.ColorBuffer.GetRowSpan(yBuffer);
Span<Block8x8> blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock); Span<Block8x8> blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock);
ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow);
for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++) for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++)
{ {
ref Block8x8 block = ref Unsafe.Add(ref blockRowBase, xBlock); ref Block8x8 block = ref blockRow[xBlock];
int xBuffer = xBlock * this.blockAreaSize.Width; int xBuffer = xBlock * this.blockAreaSize.Width;
ref float destAreaOrigin = ref colorBufferRow[xBuffer];
BufferArea<float> destArea = this.ColorBuffer.GetArea( blockPp.ProcessBlockColorsInto(ref block, ref destAreaOrigin, destAreaStride, maximumValue);
xBuffer,
yBuffer,
this.blockAreaSize.Width,
this.blockAreaSize.Height);
blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue);
} }
} }

2
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20, AllocationOptions.Clean)) using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20, AllocationOptions.Clean))
{ {
BufferArea<float> area = buffer.GetArea(5, 10, 8, 8); BufferArea<float> area = buffer.GetArea(5, 10, 8, 8);
block.Copy1x1Scale(area); block.Copy1x1Scale(ref area.GetReferenceToOrigin(), area.Stride);
Assert.Equal(block[0, 0], buffer[5, 10]); Assert.Equal(block[0, 0], buffer[5, 10]);
Assert.Equal(block[1, 0], buffer[6, 10]); Assert.Equal(block[1, 0], buffer[6, 10]);

Loading…
Cancel
Save