From 31dbd1750f29852a3a8a5c227ba2dafa06bb731b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 24 Dec 2016 04:09:04 +0100 Subject: [PATCH] CopyRGBBytesStretchedTo --- src/ImageSharp/Formats/Jpg/Components/DCT.cs | 52 +++++++--- src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs | 17 ++-- src/ImageSharp/Formats/Jpg/JpegUtils.cs | 25 ++++- .../ImageSharp.Tests/Formats/Jpg/JpegTests.cs | 98 +++++++++++++++++++ 4 files changed, 162 insertions(+), 30 deletions(-) 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