diff --git a/src/ImageSharp/Formats/Jpg/Components/DCT.cs b/src/ImageSharp/Formats/Jpg/Components/DCT.cs
index a4763a8f5..6b2344b39 100644
--- a/src/ImageSharp/Formats/Jpg/Components/DCT.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/DCT.cs
@@ -3,13 +3,14 @@
// Licensed under the Apache License, Version 2.0.
//
// ReSharper disable InconsistentNaming
+
namespace ImageSharp.Formats
{
using System.Numerics;
using System.Runtime.CompilerServices;
///
- /// Contains forward & inverse DCT implementations
+ /// Contains forward and inverse DCT implementations
///
internal static class DCT
{
@@ -35,16 +36,27 @@ namespace ImageSharp.Formats
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
private static readonly Vector4 C_1_175876 = new Vector4(1.175876f);
+
private static readonly Vector4 C_1_961571 = new Vector4(-1.961571f);
+
private static readonly Vector4 C_0_390181 = new Vector4(-0.390181f);
+
private static readonly Vector4 C_0_899976 = new Vector4(-0.899976f);
+
private static readonly Vector4 C_2_562915 = new Vector4(-2.562915f);
+
private static readonly Vector4 C_0_298631 = new Vector4(0.298631f);
+
private static readonly Vector4 C_2_053120 = new Vector4(2.053120f);
+
private static readonly Vector4 C_3_072711 = new Vector4(3.072711f);
+
private static readonly Vector4 C_1_501321 = new Vector4(1.501321f);
+
private static readonly Vector4 C_0_541196 = new Vector4(0.541196f);
+
private static readonly Vector4 C_1_847759 = new Vector4(-1.847759f);
+
private static readonly Vector4 C_0_765367 = new Vector4(0.765367f);
private static readonly Vector4 C_0_125 = new Vector4(0.1250f);
@@ -52,7 +64,7 @@ namespace ImageSharp.Formats
private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f);
///
- /// Do IDCT internal operations on the left part of the block. Original source:
+ /// Do IDCT internal operations on the left part of the block. Original src:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
///
/// Destination block
@@ -109,7 +121,7 @@ namespace ImageSharp.Formats
///
/// Do IDCT internal operations on the right part of the block.
- /// Original source:
+ /// Original src:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -163,7 +175,6 @@ namespace ImageSharp.Formats
d.V4R = my3 - mb3;
}
-
///
/// Original:
///
@@ -220,7 +231,6 @@ namespace ImageSharp.Formats
Vector4 w0 = new Vector4(0.541196f);
Vector4 w1 = new Vector4(1.306563f);
-
d.V2L = (w0 * c2) + (w1 * c3);
d.V6L = (w0 * c3) - (w1 * c2);
@@ -325,7 +335,6 @@ namespace ImageSharp.Formats
Vector4 w0 = new Vector4(0.541196f);
Vector4 w1 = new Vector4(1.306563f);
-
d.V2R = (w0 * c2) + (w1 * c3);
d.V6R = (w0 * c3) - (w1 * c2);
@@ -373,23 +382,34 @@ namespace ImageSharp.Formats
}*/
}
- public static void TransformFDCT(ref Block8x8F s, ref Block8x8F d, ref Block8x8F temp, bool offsetSourceByNeg128 = true)
+ ///
+ /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
+ ///
+ /// Source
+ /// Destination
+ /// Temporary block provided by the caller
+ /// If true, a constant -128.0 offset is applied for all values before FDCT
+ public static void TransformFDCT(
+ ref Block8x8F src,
+ ref Block8x8F dest,
+ ref Block8x8F temp,
+ bool offsetSourceByNeg128 = true)
{
- s.TransposeInto(ref temp);
+ src.TransposeInto(ref temp);
if (offsetSourceByNeg128)
{
temp.AddToAllInplace(new Vector4(-128));
}
- FDCT8x4_LeftPart(ref temp, ref d);
- FDCT8x4_RightPart(ref temp, ref d);
+ FDCT8x4_LeftPart(ref temp, ref dest);
+ FDCT8x4_RightPart(ref temp, ref dest);
+
+ dest.TransposeInto(ref temp);
+
+ FDCT8x4_LeftPart(ref temp, ref dest);
+ FDCT8x4_RightPart(ref temp, ref dest);
- d.TransposeInto(ref temp);
-
- FDCT8x4_LeftPart(ref temp, ref d);
- FDCT8x4_RightPart(ref temp, ref d);
-
- d.MultiplyAllInplace(C_0_125);
+ dest.MultiplyAllInplace(C_0_125);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index 548f237b7..0010ff98f 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -34,7 +34,10 @@ namespace ImageSharp.Formats
{
0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
},
- new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }),
+ new byte[]
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ }),
new HuffmanSpec(
new byte[]
{
@@ -562,8 +565,7 @@ namespace ImageSharp.Formats
ColorRGBToYCbCr(asStandardColorAccessor, x, y, yBlockRaw, cbBlockRaw, crBlockRaw);
return;
}
-
-
+
Vector4 maxBytes = new Vector4(255f);
Vector4 half = new Vector4(0.5f);
int xmax = pixels.Width - 1;
@@ -590,9 +592,8 @@ namespace ImageSharp.Formats
}
}
}
-
-
+ // ReSharper disable once InconsistentNaming
private static void ColorRGBToYCbCr(
PixelAccessor pixels,
int x,
@@ -632,10 +633,6 @@ namespace ImageSharp.Formats
crBlockRaw[index] = cr;
}
}
-
- //(((y * pixels.Width) + x) * colorSize);
-
-
}
@@ -934,13 +931,11 @@ namespace ImageSharp.Formats
{
Block8x8F b = new Block8x8F();
-
BlockQuad cb = new BlockQuad();
BlockQuad cr = new BlockQuad();
Block8x8F* cbPtr = (Block8x8F*)cb.Data;
Block8x8F* crPtr = (Block8x8F*)cr.Data;
-
Block8x8F temp1 = new Block8x8F();
Block8x8F temp2 = new Block8x8F();
diff --git a/src/ImageSharp/Formats/Jpg/JpegUtils.cs b/src/ImageSharp/Formats/Jpg/JpegUtils.cs
index 564ab8655..f881a4088 100644
--- a/src/ImageSharp/Formats/Jpg/JpegUtils.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegUtils.cs
@@ -13,12 +13,12 @@
*dest = *source; // B
}
- internal static unsafe void RepeatPixelsBottomRight(PixelArea area, int fromX, int fromY)
+ private static unsafe void StretchPixels(PixelArea area, int fromX, int fromY)
where TColor : struct, IPackedPixel, IEquatable
{
- if (fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height)
+ if (IsInvalidStretchArea(area, fromX, fromY))
{
- throw new InvalidOperationException();
+ return;
}
for (int y = 0; y < fromY; y++)
@@ -49,5 +49,24 @@
}
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsInvalidStretchArea(PixelArea area, int fromX, int fromY) where TColor : struct, IPackedPixel, IEquatable
+ {
+ return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height;
+ }
+
+ public static void CopyRGBBytesStretchedTo(
+ this PixelAccessor pixels,
+ PixelArea dest,
+ int sourceY,
+ int sourceX)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ pixels.CopyTo(dest, sourceY, sourceX);
+ int stretchFromX = pixels.Width - sourceX;
+ int stretchFromY = pixels.Height - sourceY;
+ StretchPixels(dest, stretchFromX, stretchFromY);
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
index 6a9267fec..fb1ca9371 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
@@ -5,9 +5,12 @@ using System.Linq;
using ImageSharp.Formats;
using Xunit;
using Xunit.Abstractions;
+// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests
{
+ using System.Numerics;
+
public class JpegTests
{
private ITestOutputHelper Output { get; }
@@ -53,5 +56,100 @@ namespace ImageSharp.Tests
image.Save(outputStream, encoder);
}
}
+
+ public static Image CreateTestImage(GenericFactory factory)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ Image image = factory.CreateImage(10, 10);
+
+ using (var pixels = image.Lock())
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ for (int j = 0; j < 10; j++)
+ {
+ Vector4 v = new Vector4(i/10f, j/10f, 0, 1);
+
+ TColor color = default(TColor);
+ color.PackFromVector4(v);
+
+ pixels[i, j] = color;
+ }
+ }
+ }
+ return image;
+ }
+
+ [Theory]
+ [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)]
+ public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ var src = provider.GetImage();
+
+ PixelArea area = new PixelArea(8, 8, ComponentOrder.XYZ);
+ var dest = provider.Factory.CreateImage(8, 8);
+
+ using (var s = src.Lock())
+ {
+ using (var d = dest.Lock())
+ {
+ s.CopyRGBBytesStretchedTo(area, 0, 0);
+ d.CopyFrom(area, 0, 0);
+
+ Assert.Equal(s[0, 0], d[0, 0]);
+ Assert.Equal(s[7, 0], d[7, 0]);
+ Assert.Equal(s[0, 7], d[0, 7]);
+ Assert.Equal(s[7, 7], d[7, 7]);
+ }
+ }
+ }
+
+ [Theory]
+ [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)]
+ public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ var src = provider.GetImage();
+
+ PixelArea area = new PixelArea(8, 8, ComponentOrder.XYZ);
+ var dest = provider.Factory.CreateImage(8, 8);
+
+ using (var s = src.Lock())
+ {
+ using (var d = dest.Lock())
+ {
+ s.CopyRGBBytesStretchedTo(area, 7, 6);
+ d.CopyFrom(area, 0, 0);
+
+ Assert.Equal(s[6, 7], d[0, 0]);
+ Assert.Equal(s[6, 8], d[0, 1]);
+ Assert.Equal(s[7, 8], d[1, 1]);
+
+ Assert.Equal(s[6, 9], d[0, 2]);
+ Assert.Equal(s[6, 9], d[0, 3]);
+ Assert.Equal(s[6, 9], d[0, 7]);
+
+ Assert.Equal(s[7, 9], d[1, 2]);
+ Assert.Equal(s[7, 9], d[1, 3]);
+ Assert.Equal(s[7, 9], d[1, 7]);
+
+ Assert.Equal(s[9, 9], d[3, 2]);
+ Assert.Equal(s[9, 9], d[3, 3]);
+ Assert.Equal(s[9, 9], d[3, 7]);
+
+ Assert.Equal(s[9, 7], d[3, 0]);
+ Assert.Equal(s[9, 7], d[4, 0]);
+ Assert.Equal(s[9, 7], d[7, 0]);
+
+ Assert.Equal(s[9, 9], d[3, 2]);
+ Assert.Equal(s[9, 9], d[4, 2]);
+ Assert.Equal(s[9, 9], d[7, 2]);
+
+ Assert.Equal(s[9, 9], d[4, 3]);
+ Assert.Equal(s[9, 9], d[7, 7]);
+ }
+ }
+ }
}
}
\ No newline at end of file