diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs index 64d1d68b7c..b7301f3e9c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs @@ -16,28 +16,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// [MethodImpl(InliningOptions.ShortMethod)] public void CopyTo(in BufferArea 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) { - this.Copy1x1Scale(area); + this.Copy1x1Scale(ref areaOrigin, areaStride); return; } if (horizontalScale == 2 && verticalScale == 2) { - this.Copy2x2Scale(area); + this.Copy2x2Scale(ref areaOrigin, areaStride); return; } // 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 destination) + public void Copy1x1Scale(ref float areaOrigin, int areaStride) { ref byte selfBase = ref Unsafe.As(ref this); - ref byte destBase = ref Unsafe.As(ref destination.GetReferenceToOrigin()); - int destStride = destination.Stride * sizeof(float); + ref byte destBase = ref Unsafe.As(ref areaOrigin); + int destStride = areaStride * sizeof(float); CopyRowImpl(ref selfBase, ref destBase, destStride, 0); 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)); } - private void Copy2x2Scale(in BufferArea area) + private void Copy2x2Scale(ref float areaOrigin, int areaStride) { - ref Vector2 destBase = ref Unsafe.As(ref area.GetReferenceToOrigin()); - int destStride = area.Stride / 2; + ref Vector2 destBase = ref Unsafe.As(ref areaOrigin); + int destStride = areaStride / 2; this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); @@ -110,10 +117,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } [MethodImpl(InliningOptions.ColdPath)] - private void CopyArbitraryScale(BufferArea 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++) { int yy = y * verticalScale; @@ -127,12 +132,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components 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++) { // area[xx + j, yy + i] = value; - Unsafe.Add(ref destBase, baseIdx + j) = value; + Unsafe.Add(ref areaOrigin, baseIdx + j) = value; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index 44f9048a59..8c797463ba 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/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 . /// /// The source block. - /// The destination buffer area. + /// Reference to the origin of the destination pixel area. + /// The width of the destination pixel buffer. /// The maximum value derived from the bitdepth. public void ProcessBlockColorsInto( ref Block8x8 sourceBlock, - in BufferArea destArea, + ref float destAreaOrigin, + int destAreaStride, float maximumValue) { 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! 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); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index 22bc8ccaaa..b91287fb3d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/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); float maximumValue = MathF.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1; + int destAreaStride = this.ColorBuffer.Width; + for (int y = 0; y < this.BlockRowsPerStep; y++) { int yBlock = this.currentComponentRowInBlocks + y; @@ -90,22 +92,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int yBuffer = y * this.blockAreaSize.Height; + Span colorBufferRow = this.ColorBuffer.GetRowSpan(yBuffer); Span blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock); - ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow); - 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; + ref float destAreaOrigin = ref colorBufferRow[xBuffer]; - BufferArea destArea = this.ColorBuffer.GetArea( - xBuffer, - yBuffer, - this.blockAreaSize.Width, - this.blockAreaSize.Height); - - blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue); + blockPp.ProcessBlockColorsInto(ref block, ref destAreaOrigin, destAreaStride, maximumValue); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index ea2fc7ab80..e8af799320 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20, AllocationOptions.Clean)) { BufferArea 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[1, 0], buffer[6, 10]);