diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 3a29e21d9c..ddbac2d072 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -18,6 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Represents a Jpeg block with coefficients.
///
+ [StructLayout(LayoutKind.Sequential)]
internal partial struct Block8x8F : IEquatable
{
///
@@ -51,9 +52,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public Vector4 V7R;
#pragma warning restore SA1600 // ElementsMustBeDocumented
- private static readonly Vector4 NegativeOne = new Vector4(-1);
- private static readonly Vector4 Offset = new Vector4(.5F);
-
///
/// Get/Set scalar elements at a given index
///
@@ -155,10 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
[MethodImpl(InliningOptions.ShortMethod)]
public void Clear()
- {
- // The cheapest way to do this in C#:
- this = default;
- }
+ => this = default; // The cheapest way to do this in C#:
///
/// Load raw 32bit floating point data from source.
@@ -180,9 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Source
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source)
- {
- blockPtr->LoadFrom(source);
- }
+ => blockPtr->LoadFrom(source);
///
/// Load raw 32bit floating point data from source
@@ -236,9 +229,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// The destination.
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void ScaledCopyTo(Block8x8F* blockPtr, Span dest)
- {
- blockPtr->ScaledCopyTo(dest);
- }
+ => blockPtr->ScaledCopyTo(dest);
///
/// Copy raw 32bit floating point data to dest
@@ -439,7 +430,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// The block pointer.
/// The qt pointer.
/// Unzig pointer
- // [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, byte* unzigPtr)
{
float* b = (float*)blockPtr;
@@ -556,22 +546,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{
- a.V0L = DivideRound(a.V0L, b.V0L);
- a.V0R = DivideRound(a.V0R, b.V0R);
- a.V1L = DivideRound(a.V1L, b.V1L);
- a.V1R = DivideRound(a.V1R, b.V1R);
- a.V2L = DivideRound(a.V2L, b.V2L);
- a.V2R = DivideRound(a.V2R, b.V2R);
- a.V3L = DivideRound(a.V3L, b.V3L);
- a.V3R = DivideRound(a.V3R, b.V3R);
- a.V4L = DivideRound(a.V4L, b.V4L);
- a.V4R = DivideRound(a.V4R, b.V4R);
- a.V5L = DivideRound(a.V5L, b.V5L);
- a.V5R = DivideRound(a.V5R, b.V5R);
- a.V6L = DivideRound(a.V6L, b.V6L);
- a.V6R = DivideRound(a.V6R, b.V6R);
- a.V7L = DivideRound(a.V7L, b.V7L);
- a.V7R = DivideRound(a.V7R, b.V7R);
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx.IsSupported)
+ {
+ var vnegOne = Vector256.Create(-1f);
+ var vadd = Vector256.Create(.5F);
+ var vone = Vector256.Create(1f);
+
+ ref Vector256 aBase = ref Unsafe.AsRef(Unsafe.As>(ref a.V0L));
+ ref Vector256 bBase = ref Unsafe.AsRef(Unsafe.As>(ref b.V0L));
+ ref Vector256 aEnd = ref Unsafe.Add(ref aBase, 8);
+
+ do
+ {
+ Vector256 voff = Avx.Multiply(Avx.Min(Avx.Max(vnegOne, aBase), vone), vadd);
+ Unsafe.Add(ref aBase, 0) = Avx.Add(Avx.Divide(aBase, bBase), voff);
+
+ aBase = ref Unsafe.Add(ref aBase, 1);
+ bBase = ref Unsafe.Add(ref bBase, 1);
+ }
+ while (Unsafe.IsAddressLessThan(ref aBase, ref aEnd));
+ }
+ else
+#endif
+ {
+ a.V0L = DivideRound(a.V0L, b.V0L);
+ a.V0R = DivideRound(a.V0R, b.V0R);
+ a.V1L = DivideRound(a.V1L, b.V1L);
+ a.V1R = DivideRound(a.V1R, b.V1R);
+ a.V2L = DivideRound(a.V2L, b.V2L);
+ a.V2R = DivideRound(a.V2R, b.V2R);
+ a.V3L = DivideRound(a.V3L, b.V3L);
+ a.V3R = DivideRound(a.V3R, b.V3R);
+ a.V4L = DivideRound(a.V4L, b.V4L);
+ a.V4R = DivideRound(a.V4R, b.V4R);
+ a.V5L = DivideRound(a.V5L, b.V5L);
+ a.V5R = DivideRound(a.V5R, b.V5R);
+ a.V6L = DivideRound(a.V6L, b.V6L);
+ a.V6R = DivideRound(a.V6R, b.V6R);
+ a.V7L = DivideRound(a.V7L, b.V7L);
+ a.V7R = DivideRound(a.V7R, b.V7R);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
+ {
+ var neg = new Vector4(-1);
+ var add = new Vector4(.5F);
+
+ // sign(dividend) = max(min(dividend, 1), -1)
+ Vector4 sign = Numerics.Clamp(dividend, neg, Vector4.One);
+
+ // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
+ return (dividend / divisor) + (sign * add);
}
public void RoundInto(ref Block8x8 dest)
@@ -673,8 +701,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
public bool Equals(Block8x8F other)
- {
- return this.V0L == other.V0L
+ => this.V0L == other.V0L
&& this.V0R == other.V0R
&& this.V1L == other.V1L
&& this.V1R == other.V1R
@@ -690,7 +717,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
&& this.V6R == other.V6R
&& this.V7L == other.V7L
&& this.V7R == other.V7R;
- }
///
public override string ToString()
@@ -718,16 +744,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound();
}
- [MethodImpl(InliningOptions.ShortMethod)]
- private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
- {
- // sign(dividend) = max(min(dividend, 1), -1)
- Vector4 sign = Numerics.Clamp(dividend, NegativeOne, Vector4.One);
-
- // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
- return (dividend / divisor) + (sign * Offset);
- }
-
[Conditional("DEBUG")]
private static void GuardBlockIndex(int idx)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
index 8fcc63c6aa..81e64b277b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
@@ -59,9 +59,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
///
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , )
///
- public void Convert(ImageFrame frame, int x, int y, in RowOctet currentRows)
+ public void Convert(ImageFrame frame, int x, int y, ref RowOctet currentRows)
{
- this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, currentRows);
+ this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows);
Span rgbSpan = this.rgbBlock.AsSpanUnsafe();
PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), rgbSpan);
@@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
}
else
{
- this.colorTables.Convert(rgbSpan, ref yBlock, ref cbBlock, ref crBlock);
+ this.colorTables.Convert(rgbSpan, ref yBlock, ref cbBlock, ref crBlock);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
index 92ba1afd35..42c01d770e 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Load a 8x8 region of an image into the block.
/// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image.
///
- public void LoadAndStretchEdges(Buffer2D source, int sourceX, int sourceY, in RowOctet currentRows)
+ public void LoadAndStretchEdges(Buffer2D source, int sourceX, int sourceY, ref RowOctet currentRows)
{
int width = Math.Min(8, source.Width - sourceX);
int height = Math.Min(8, source.Height - sourceY);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs b/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
index ae10bfba83..f35bb44682 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -12,39 +12,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Cache 8 pixel rows on the stack, which may originate from different buffers of a .
///
[StructLayout(LayoutKind.Sequential)]
- internal readonly ref struct RowOctet
+ internal ref struct RowOctet
where T : struct
{
- private readonly Span row0;
- private readonly Span row1;
- private readonly Span row2;
- private readonly Span row3;
- private readonly Span row4;
- private readonly Span row5;
- private readonly Span row6;
- private readonly Span row7;
-
- public RowOctet(Buffer2D buffer, int startY)
- {
- int y = startY;
- int height = buffer.Height;
- this.row0 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row1 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row2 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row3 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row4 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row5 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row6 = y < height ? buffer.GetRowSpan(y++) : default;
- this.row7 = y < height ? buffer.GetRowSpan(y) : default;
- }
+ private Span row0;
+ private Span row1;
+ private Span row2;
+ private Span row3;
+ private Span row4;
+ private Span row5;
+ private Span row6;
+ private Span row7;
+ // No unsafe tricks, since Span can't be used as a generic argument
public Span this[int y]
{
- [MethodImpl(InliningOptions.ShortMethod)]
- get
- {
- // No unsafe tricks, since Span can't be used as a generic argument
- return y switch
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get =>
+ y switch
{
0 => this.row0,
1 => this.row1,
@@ -56,13 +41,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
7 => this.row7,
_ => ThrowIndexOutOfRangeException()
};
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private set
+ {
+ switch (y)
+ {
+ case 0:
+ this.row0 = value;
+ break;
+ case 1:
+ this.row1 = value;
+ break;
+ case 2:
+ this.row2 = value;
+ break;
+ case 3:
+ this.row3 = value;
+ break;
+ case 4:
+ this.row4 = value;
+ break;
+ case 5:
+ this.row5 = value;
+ break;
+ case 6:
+ this.row6 = value;
+ break;
+ default:
+ this.row7 = value;
+ break;
+ }
}
}
- [MethodImpl(InliningOptions.ColdPath)]
- private static Span ThrowIndexOutOfRangeException()
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Update(Buffer2D buffer, int startY)
{
- throw new IndexOutOfRangeException();
+ // We don't actually have to assign values outside of the
+ // frame pixel buffer since they are never requested.
+ int y = startY;
+ int yEnd = Math.Min(y + 8, buffer.Height);
+
+ int i = 0;
+ while (y < yEnd)
+ {
+ this[i++] = buffer.GetRowSpan(y++);
+ }
}
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Span ThrowIndexOutOfRangeException()
+ => throw new IndexOutOfRangeException();
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 36766d05f0..422c7bd7ec 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -6,6 +6,7 @@ using System.Buffers.Binary;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Threading;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
@@ -313,7 +314,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The packed bits.
/// The number of bits
- private void Emit(uint bits, uint count)
+ /// The reference to the emitBuffer.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void Emit(uint bits, uint count, ref byte emitBufferBase)
{
count += this.bitCount;
bits <<= (int)(32 - count);
@@ -327,10 +330,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
while (count >= 8)
{
byte b = (byte)(bits >> 24);
- this.emitBuffer[len++] = b;
- if (b == 0xff)
+ Unsafe.Add(ref emitBufferBase, len++) = b;
+ if (b == byte.MaxValue)
{
- this.emitBuffer[len++] = 0x00;
+ Unsafe.Add(ref emitBufferBase, len++) = byte.MinValue;
}
bits <<= 8;
@@ -352,11 +355,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The index of the Huffman encoder
/// The value to encode.
+ /// The reference to the emit buffer.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void EmitHuff(HuffIndex index, int value)
+ private void EmitHuff(HuffIndex index, int value, ref byte emitBufferBase)
{
uint x = HuffmanLut.TheHuffmanLut[(int)index].Values[value];
- this.Emit(x & ((1 << 24) - 1), x >> 24);
+ this.Emit(x & ((1 << 24) - 1), x >> 24, ref emitBufferBase);
}
///
@@ -365,8 +369,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The index of the Huffman encoder
/// The number of copies to encode.
/// The value to encode.
+ /// The reference to the emit buffer.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void EmitHuffRLE(HuffIndex index, int runLength, int value)
+ private void EmitHuffRLE(HuffIndex index, int runLength, int value, ref byte emitBufferBase)
{
int a = value;
int b = value;
@@ -386,10 +391,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
bt = 8 + (uint)BitCountLut[a >> 8];
}
- this.EmitHuff(index, (int)((uint)(runLength << 4) | bt));
+ this.EmitHuff(index, (int)((uint)(runLength << 4) | bt), ref emitBufferBase);
if (bt > 0)
{
- this.Emit((uint)b & (uint)((1 << ((int)bt)) - 1), bt);
+ this.Emit((uint)b & (uint)((1 << ((int)bt)) - 1), bt, ref emitBufferBase);
}
}
@@ -399,7 +404,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The pixel format.
/// The pixel accessor providing access to the image pixels.
/// The token to monitor for cancellation.
- private void Encode444(Image pixels, CancellationToken cancellationToken)
+ /// The reference to the emit buffer.
+ private void Encode444(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
where TPixel : unmanaged, IPixel
{
// TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
@@ -418,15 +424,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
var pixelConverter = YCbCrForwardConverter.Create();
ImageFrame frame = pixels.Frames.RootFrame;
Buffer2D pixelBuffer = frame.PixelBuffer;
+ RowOctet currentRows = default;
for (int y = 0; y < pixels.Height; y += 8)
{
cancellationToken.ThrowIfCancellationRequested();
- var currentRows = new RowOctet(pixelBuffer, y);
+ currentRows.Update(pixelBuffer, y);
for (int x = 0; x < pixels.Width; x += 8)
{
- pixelConverter.Convert(frame, x, y, currentRows);
+ pixelConverter.Convert(frame, x, y, ref currentRows);
prevDCY = this.WriteBlock(
QuantIndex.Luminance,
@@ -435,7 +442,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1,
ref temp2,
ref onStackLuminanceQuantTable,
- ref unzig);
+ ref unzig,
+ ref emitBufferBase);
+
prevDCCb = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCb,
@@ -443,7 +452,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1,
ref temp2,
ref onStackChrominanceQuantTable,
- ref unzig);
+ ref unzig,
+ ref emitBufferBase);
+
prevDCCr = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCr,
@@ -451,7 +462,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1,
ref temp2,
ref onStackChrominanceQuantTable,
- ref unzig);
+ ref unzig,
+ ref emitBufferBase);
}
}
}
@@ -517,9 +529,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Temporal block 2
/// Quantization table
/// The 8x8 Unzig block.
- ///
- /// The
- ///
+ /// The reference to the emit buffer.
+ /// The .
private int WriteBlock(
QuantIndex index,
int prevDC,
@@ -527,7 +538,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref Block8x8F tempDest1,
ref Block8x8F tempDest2,
ref Block8x8F quant,
- ref ZigZag unZig)
+ ref ZigZag unZig,
+ ref byte emitBufferBase)
{
FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2);
@@ -536,7 +548,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int dc = (int)tempDest2[0];
// Emit the DC delta.
- this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC);
+ this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC, ref emitBufferBase);
// Emit the AC components.
var h = (HuffIndex)((2 * (int)index) + 1);
@@ -554,18 +566,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
while (runLength > 15)
{
- this.EmitHuff(h, 0xf0);
+ this.EmitHuff(h, 0xf0, ref emitBufferBase);
runLength -= 16;
}
- this.EmitHuffRLE(h, runLength, ac);
+ this.EmitHuffRLE(h, runLength, ac, ref emitBufferBase);
runLength = 0;
}
}
if (runLength > 0)
{
- this.EmitHuff(h, 0x00);
+ this.EmitHuff(h, 0x00, ref emitBufferBase);
}
return dc;
@@ -747,9 +759,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The length of the data the app1 marker contains.
private void WriteApp1Header(int app1Length)
- {
- this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1);
- }
+ => this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1);
///
/// Writes a AppX header.
@@ -953,19 +963,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
// TODO: We should allow grayscale writing.
this.outputStream.Write(SosHeaderYCbCr);
-
+ ref byte emitBufferBase = ref MemoryMarshal.GetReference(this.emitBuffer);
switch (this.subsample)
{
case JpegSubsample.Ratio444:
- this.Encode444(image, cancellationToken);
+ this.Encode444(image, cancellationToken, ref emitBufferBase);
break;
case JpegSubsample.Ratio420:
- this.Encode420(image, cancellationToken);
+ this.Encode420(image, cancellationToken, ref emitBufferBase);
break;
}
// Pad the last byte with 1's.
- this.Emit(0x7f, 7);
+ this.Emit(0x7f, 7, ref emitBufferBase);
}
///
@@ -975,7 +985,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The pixel format.
/// The pixel accessor providing access to the image pixels.
/// The token to monitor for cancellation.
- private void Encode420(Image pixels, CancellationToken cancellationToken)
+ /// The reference to the emit buffer.
+ private void Encode420(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
where TPixel : unmanaged, IPixel
{
// TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
@@ -997,6 +1008,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
ImageFrame frame = pixels.Frames.RootFrame;
Buffer2D pixelBuffer = frame.PixelBuffer;
+ RowOctet currentRows = default;
for (int y = 0; y < pixels.Height; y += 16)
{
@@ -1008,10 +1020,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4;
- // TODO: Try pushing this to the outer loop!
- var currentRows = new RowOctet(pixelBuffer, y + yOff);
-
- pixelConverter.Convert(frame, x + xOff, y + yOff, currentRows);
+ currentRows.Update(pixelBuffer, y + yOff);
+ pixelConverter.Convert(frame, x + xOff, y + yOff, ref currentRows);
cb[i] = pixelConverter.Cb;
cr[i] = pixelConverter.Cr;
@@ -1023,7 +1033,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1,
ref temp2,
ref onStackLuminanceQuantTable,
- ref unzig);
+ ref unzig,
+ ref emitBufferBase);
}
Block8x8F.Scale16X16To8X8(ref b, cb);
@@ -1034,7 +1045,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1,
ref temp2,
ref onStackChrominanceQuantTable,
- ref unzig);
+ ref unzig,
+ ref emitBufferBase);
Block8x8F.Scale16X16To8X8(ref b, cr);
prevDCCr = this.WriteBlock(
@@ -1044,7 +1056,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ref temp1,
ref temp2,
ref onStackChrominanceQuantTable,
- ref unzig);
+ ref unzig,
+ ref emitBufferBase);
}
}
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Scale16X16To8X8.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Scale16X16To8X8.cs
index 8188297608..ebd3e40130 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Scale16X16To8X8.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Scale16X16To8X8.cs
@@ -1,3 +1,6 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
using System;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
@@ -15,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Format.Jpeg.Components
{
var random = new Random();
- float[] f = new float[8*8];
+ float[] f = new float[8 * 8];
for (int i = 0; i < f.Length; i++)
{
f[i] = (float)random.NextDouble();
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
index bfbd150fea..81a5604f1e 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
if (this.bmpStream == null)
{
- const string TestImage = TestImages.Bmp.Car;
+ const string TestImage = TestImages.Bmp.NegHeight;
this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
this.bmpCore = Image.Load(this.bmpStream);
this.bmpStream.Position = 0;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs
index c366e4f56e..c0f3b6a6a4 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs
@@ -42,8 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (Image s = provider.GetImage())
{
var d = default(GenericBlock8x8);
- var rowOctet = new RowOctet(s.GetRootFramePixelBuffer(), 0);
- d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 0, 0, rowOctet);
+ RowOctet rowOctet = default;
+ rowOctet.Update(s.GetRootFramePixelBuffer(), 0);
+ d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 0, 0, ref rowOctet);
TPixel a = s.Frames.RootFrame[0, 0];
TPixel b = d[0, 0];
@@ -67,8 +68,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (Image s = provider.GetImage())
{
var d = default(GenericBlock8x8);
- var rowOctet = new RowOctet(s.GetRootFramePixelBuffer(), 7);
- d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 6, 7, rowOctet);
+ RowOctet rowOctet = default;
+ rowOctet.Update(s.GetRootFramePixelBuffer(), 7);
+
+ d.LoadAndStretchEdges(s.Frames.RootFrame.PixelBuffer, 6, 7, ref rowOctet);
Assert.Equal(s[6, 7], d[0, 0]);
Assert.Equal(s[6, 8], d[0, 1]);
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
index 16d0baff39..17c73cc834 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
@@ -375,7 +375,7 @@ namespace SixLabors.ImageSharp.Tests
var array = new Rgba32[size];
var memory = new Memory(array);
- Image.WrapMemory(memory, height, width);
+ Image.WrapMemory(memory, height, width);
}
private class TestMemoryOwner : IMemoryOwner
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
index b80a29646c..8d1b0f7938 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
@@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp.Tests
private static string GetNetCoreVersion()
{
Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
- string[] assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
+ string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
{