From b19f65560210bee73db3007019ad971e10941db6 Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Thu, 5 Oct 2017 14:00:19 +1100 Subject: [PATCH 01/10] Update EXIF on rotate. Fix #268 Since we don't know the updated rotation value and EXIF tags only cover certain angles the best fix is to remove the rotate tag. We also update height width if the canvas has been expanded. --- .../Processors/Transforms/RotateProcessor.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index a7fb400ac..f057c8254 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -24,6 +25,11 @@ namespace SixLabors.ImageSharp.Processing.Processors /// private Matrix3x2 processMatrix; + /// + /// The final rotated angle. + /// + private int optimizedRotatedAngle; + /// /// Gets or sets the angle of processMatrix in degrees. /// @@ -87,6 +93,30 @@ namespace SixLabors.ImageSharp.Processing.Processors } } + /// + protected override void AfterImageApply(Image source, Rectangle sourceRectangle) + { + ExifProfile profile = source.MetaData.ExifProfile; + if (profile == null) + { + return; + } + + if (MathF.Abs(this.Angle) < Constants.Epsilon) + { + // No need to do anything so return. + return; + } + + profile.RemoveValue(ExifTag.Orientation); + + if (this.Expand && profile.GetValue(ExifTag.PixelXDimension) != null) + { + profile.SetValue(ExifTag.PixelXDimension, source.Width); + profile.SetValue(ExifTag.PixelYDimension, source.Height); + } + } + /// /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// From 14756d7a603857ec8ccaa78037023810667fded9 Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Thu, 5 Oct 2017 14:03:27 +1100 Subject: [PATCH 02/10] Remove unused field --- .../Processing/Processors/Transforms/RotateProcessor.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index f057c8254..86a0c7360 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -25,11 +25,6 @@ namespace SixLabors.ImageSharp.Processing.Processors /// private Matrix3x2 processMatrix; - /// - /// The final rotated angle. - /// - private int optimizedRotatedAngle; - /// /// Gets or sets the angle of processMatrix in degrees. /// From bfc90878badbf9fa469a8b328bf1b0d29d8b5f95 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 5 Oct 2017 23:56:43 +1100 Subject: [PATCH 03/10] Handle corrupted data portions. Fix #358 --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 13 ++++++++++++- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Png/big-corrupted-chunk.png | Bin 0 -> 9195 bytes 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Png/big-corrupted-chunk.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 3bca4b261..7149b74d8 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1124,12 +1124,23 @@ namespace SixLabors.ImageSharp.Formats.Png { var chunk = new PngChunk(); this.ReadChunkLength(chunk); - if (chunk.Length < 0) + + if (chunk.Length == -1) { + // IEND return null; } + if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position) + { + // Not a valid chunk so we skip back all but one of the four bytes we have just read. + // That lets us read one byte at a time until we reach a known chunk. + this.currentStream.Position -= 3; + return chunk; + } + this.ReadChunkType(chunk); + if (chunk.Type == PngChunkTypes.Data) { return chunk; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index dbcacb4f3..8af9d170b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -20,6 +20,7 @@ namespace SixLabors.ImageSharp.Tests public const string Blur = "Png/blur.png"; public const string Indexed = "Png/indexed.png"; public const string Splash = "Png/splash.png"; + public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; public const string Cross = "Png/cross.png"; public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; diff --git a/tests/Images/Input/Png/big-corrupted-chunk.png b/tests/Images/Input/Png/big-corrupted-chunk.png new file mode 100644 index 0000000000000000000000000000000000000000..83026897717a00976c0851e2a7b693e4f7fdaec5 GIT binary patch literal 9195 zcmbVybx>T*wl6xk2Mad11cn)$!8Jf|cXwxShu{H1f&>U2Jh%l9!QCymyAxb5Ip=)$ zy+3ZgSs6)HI5>Fh7aE9)@ba}sfPeYI zk-AA~yQw)@x_O$wEa1eX z`ail}T0%6|Zf?#{c6LusPmm`k$O&fkg5~FD2XnA}KQS2>dJ2#MH^%O^D{j(to?);H;qV-+~=o{}ZT}kgs}=EIWqAMi`;{&z)x8DFe{D#2`CCdI^F(#g!- z!NSo^R#J%OWdvkyV-Dpowcs{4H-oTon7_DeX~AW|X2QkE^Fr~PTXOMnLO6N&|B>@Q z;rYeIdBG4c7r%tK7z854BgQ8#CdMZw&BrG##t9LZ`UflP=;~(TXlC(`Zkrd~|G{$p zuUM!A%)-RY38wDkWdBbVC|f(ZIk{RpIRhmm{?0l#h#SbHW#ee>2u~8s_8x{8yx*HvfYa|8EiaH{SgJH-hXhMzQ}*m;Wn${^@$j$-m@(7sE^Azst$O z@uh}fFU1nOcHjL{v}m%DV(MP=hx#aP>KgSwJ$!^LqhB$R=7m;cR3vegbAcXO89z8_R6lrwslf*8`qC+y?p zkk@R{(%lbRlFlWCcF8ChD431{@C%fn=sxyK5%3rkp+ckQI(^ST+s`;;l)f9M)y zP<#oM)JUG2o3X_HeEK2!>m3f}tYoOI9n|H0q>?fh@9T~!YM)kxSrge~Dr}+J+7#(H zSuT=8o{~2#y06psjPH$jE-EyCLFdb;N;w~W#&jxN2~e@M)UJW35g$=ea4`LSgzvkx z0s^zT4<`@|#bdU?BOcY9nsHMMWbMqZCnVzf8guowpnyV1KmBWY z>XmiRPa)pd2Ur`7SU20ze@36p>{~Sh*$R1B^#$FuS=|U(w*K6dzT0_YbkpmP2@v9& zYrmorBRrg{`m&Sh@`FT&h9M&?T;v8Wu#1UHT~3?rwXNko=528%Gsa!IKFH}^BaMaxSP3j0`&eL2c+QJCpvElbC4-i`Vk%v_&y?0Hf@ z85k(`VauZ8{A^xI`60dI=7VA-?LH|;&dy?wv`o00S6bVB&>kEFA>nEs#%e^dLF{ce z-p7#rV=K_FzYfu0sT$t?mcvDYnKf!_58f4UfViB8Lxqkz30d$e>sHNC7TWkVBFVg` zB_gU5O$)@=m?}OBd!_Ju%Xy;P)W!74{6gkvh>qTu##yuG`tboOrW=2!i`Q|dwudl4 zM;D3{Q#9%#N|@FeYZ5B@Yx_()`%Mc8b{{Rii?0#DrpIzZ=t*s?#ZVL|NwapfH*D#e zHjJI~iI&JUM2*b;c}@MyOE(-FrlENjkAFl+IH6M`@#7GPL%dvSBse=aGqMUZeQx|L z;LSqr*3#Ov)rx}@a+iiaK&w@O+fu)Fjl<$Q#;d{0;nAv@a=mTknK#g#QZ0CM9`Ysf!eUN)lne)B~v&> zvc>G}hd;1`8w8@?qVTT;m$!O?;Dv>pkieqfd6gv_`MIS-mJYgQ4F|KMB^p9%z9Dou z{V9l6h-FBg6nM&kzm7dvIZ9vOcH+&v_G;N+%on`#A|k&%Ykn8&E}avJMIMi&jL;3H zit(RGkmz`LL(`2Mi*miZdOnBgbG=Wmzj=0NuG+5l=k4k3()7Nj%4FkYy*mX_97+f& zpy$o2>I6XOaJo=l997fM#|q&=z(R`r9f4~&4i)L_&ik*g4X*k*YWm?rq#lDD>l%Aw z_GV`s%S*VO`8-{`SW@&Aaf*he*gp#Smu~JSy;i9h2wUUZ?≥RPx%zrhhte9xv-) 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 literal 0 HcmV?d00001 From 25647a5afc02c3d2a85eb3551fd27bd63cd6f821 Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Fri, 6 Oct 2017 09:00:25 +1100 Subject: [PATCH 04/10] Add missing test for #359 --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index fc759fb56..d39d0651d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Tests TestImages.Png.Splash, TestImages.Png.Indexed, TestImages.Png.FilterVar, TestImages.Png.Bad.ChunkLength1, + TestImages.Png.Bad.CorruptedChunk, TestImages.Png.VimImage1, TestImages.Png.VersioningImage1, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8af9d170b..c2a6ed1ad 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -20,7 +20,6 @@ namespace SixLabors.ImageSharp.Tests public const string Blur = "Png/blur.png"; public const string Indexed = "Png/indexed.png"; public const string Splash = "Png/splash.png"; - public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; public const string Cross = "Png/cross.png"; public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; @@ -57,6 +56,7 @@ namespace SixLabors.ImageSharp.Tests // Odd chunk lengths public const string ChunkLength1 = "Png/chunklength1.png"; public const string ChunkLength2 = "Png/chunklength2.png"; + public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; } public static readonly string[] All = From f741b3a2a0803cb34b560169b2b719e624ca82da Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Fri, 6 Oct 2017 11:34:16 +1100 Subject: [PATCH 05/10] Ensure colormap cache is cleared --- src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs | 1 + src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs | 1 + src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index 49adce23b..8766f1042 100644 --- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -61,6 +61,7 @@ namespace SixLabors.ImageSharp.Quantizers this.colors = (byte)maxColors.Clamp(1, 255); this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors)); this.palette = null; + this.colorMap.Clear(); return base.Quantize(image, this.colors); } diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index ca11a042f..0b95c09a6 100644 --- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -58,6 +58,7 @@ namespace SixLabors.ImageSharp.Quantizers public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); + this.colorMap.Clear(); return base.Quantize(image, maxColors); } diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index 77c421468..8ab390f4e 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -139,6 +139,7 @@ namespace SixLabors.ImageSharp.Quantizers this.colors = maxColors.Clamp(1, 255); this.palette = null; + this.colorMap.Clear(); try { From 5237688ce4ab4e74a2f4f18cfb05469e0316cdb0 Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Fri, 6 Oct 2017 11:34:41 +1100 Subject: [PATCH 06/10] FS makes much smaller files --- src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs index 7f58ff1bf..20ba2e637 100644 --- a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base public bool Dither { get; set; } = true; /// - public IErrorDiffuser DitherType { get; set; } = new SierraLiteDiffuser(); + public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser(); /// public virtual QuantizedImage Quantize(ImageFrame image, int maxColors) From eeab5854d0c2857c485916ba934061a5ab0ab910 Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Fri, 6 Oct 2017 15:49:43 +1100 Subject: [PATCH 07/10] Quality 0 and 1 should be equal --- .../Jpeg/GolangPort/JpegEncoderCore.cs | 49 +++++++---------- src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 2 - .../Formats/Jpg/JpegEncoderTests.cs | 54 +++++++++++++++++-- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index 2deb3f62d..2912a8719 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -125,6 +125,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// private readonly byte[] huffmanBuffer = new byte[179]; + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + private readonly bool ignoreMetadata; + + /// + /// The quality, that will be used to encode the image. + /// + private readonly int quality; + + /// + /// Gets or sets the subsampling method to use. + /// + private readonly JpegSubsample? subsample; + /// /// The accumulated bits to write to the stream. /// @@ -150,37 +165,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// private Stream outputStream; - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - private bool ignoreMetadata = false; - - /// - /// Gets or sets the quality, that will be used to encode the image. Quality - /// index must be between 0 and 100 (compression from max to min). - /// - /// The quality of the jpg image from 0 to 100. - private int quality = 0; - - /// - /// Gets or sets the subsampling method to use. - /// - private JpegSubsample? subsample; - /// /// Initializes a new instance of the class. /// /// The options public JpegEncoderCore(IJpegEncoderOptions options) { - int quality = options.Quality; - if (quality == 0) - { - quality = 75; - } - - this.quality = quality; - this.subsample = options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); + // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1. + this.quality = options.Quality.Clamp(1, 100); + this.subsample = options.Subsample ?? (this.quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); this.ignoreMetadata = options.IgnoreMetadata; } @@ -205,17 +198,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.outputStream = stream; - int quality = this.quality.Clamp(1, 100); - // Convert from a quality rating to a scaling factor. int scale; if (this.quality < 50) { - scale = 5000 / quality; + scale = 5000 / this.quality; } else { - scale = 200 - (quality * 2); + scale = 200 - (this.quality * 2); } // Initialize the quantization tables. diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 3d79faabc..a14fa16a5 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -21,13 +21,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Gets or sets the quality, that will be used to encode the image. Quality /// index must be between 0 and 100 (compression from max to min). /// - /// The quality of the jpg image from 0 to 100. public int Quality { get; set; } /// /// Gets or sets the subsample ration, that will be used to encode the image. /// - /// The subsample ratio of the jpg image. public JpegSubsample? Subsample { get; set; } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 3bd1ed265..c8d416bea 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(x=>x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) + using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) { image.MetaData.ExifProfile = null; // Reduce the size of the file @@ -62,8 +62,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { image.Save(outputStream, new JpegEncoder() { - Subsample = subSample, - Quality = quality + Subsample = subSample, + Quality = quality }); } } @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { using (MemoryStream memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, options); memStream.Position = 0; using (Image output = Image.Load(memStream)) @@ -118,5 +118,51 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } } + + [Fact] + public void Encode_Quality_0_And_1_Are_Identical() + { + var options = new JpegEncoder + { + Quality = 0 + }; + + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); + + using (Image input = testFile.CreateImage()) + using (var memStream0 = new MemoryStream()) + using (var memStream1 = new MemoryStream()) + { + input.SaveAsJpeg(memStream0, options); + + options.Quality = 1; + input.SaveAsJpeg(memStream1, options); + + Assert.Equal(memStream0.ToArray(), memStream1.ToArray()); + } + } + + [Fact] + public void Encode_Quality_0_And_100_Are_Not_Identical() + { + var options = new JpegEncoder + { + Quality = 0 + }; + + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); + + using (Image input = testFile.CreateImage()) + using (var memStream0 = new MemoryStream()) + using (var memStream1 = new MemoryStream()) + { + input.SaveAsJpeg(memStream0, options); + + options.Quality = 100; + input.SaveAsJpeg(memStream1, options); + + Assert.NotEqual(memStream0.ToArray(), memStream1.ToArray()); + } + } } } \ No newline at end of file From 3a8f4fd39411a4bdf70dfad22588262f5815326f Mon Sep 17 00:00:00 2001 From: JimBobSquarePants Date: Fri, 6 Oct 2017 16:53:01 +1100 Subject: [PATCH 08/10] Whoops! no default value --- src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index a14fa16a5..8850f581c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Gets or sets the quality, that will be used to encode the image. Quality /// index must be between 0 and 100 (compression from max to min). /// - public int Quality { get; set; } + public int Quality { get; set; } = 75; /// /// Gets or sets the subsample ration, that will be used to encode the image. From b0f140af87e4a3e67985aeefd196030aae976c15 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Oct 2017 12:04:54 +1100 Subject: [PATCH 09/10] Update Xunit and Moq --- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index e8a6e8c59..b3c241c22 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -17,10 +17,10 @@ - - + + - + From 051088160a7704e05259371c5e4f17cfb947b7f2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Oct 2017 12:11:35 +1100 Subject: [PATCH 10/10] Remove SDK --- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index b3c241c22..2f45e4c83 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -19,7 +19,6 @@ -