From 56584d96562e5a4ff283a5bfec95e2d80b63ff2b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 20 Nov 2015 00:53:07 +1100 Subject: [PATCH] Various fixes - Fix alpha clamping on Resize to prevent bleed. - Finish Color premultiplication updates Former-commit-id: e0cc8e3c08b0626fe1a1a7ce4c2f1246c1b912ca Former-commit-id: 503f9fa8137a2ff561d697bb2a4cdd2071f1728d Former-commit-id: 51ee649dbfec8fc8cc4db412ac6b743ba2ce991c --- README.md | 33 ++++++++++- src/ImageProcessor/Colors/Color.cs | 33 ++++++----- src/ImageProcessor/Colors/Formats/Bgra32.cs | 2 +- src/ImageProcessor/Colors/Formats/Hsv.cs | 2 +- src/ImageProcessor/Colors/Formats/YCbCr.cs | 2 +- src/ImageProcessor/Filters/Alpha.cs | 3 +- src/ImageProcessor/Filters/BackgroundColor.cs | 3 +- src/ImageProcessor/Formats/Bmp/BmpEncoder.cs | 15 ++++- .../Formats/Gif/Quantizer/Quantizer.cs | 7 ++- src/ImageProcessor/Formats/Jpg/JpegEncoder.cs | 3 +- src/ImageProcessor/Formats/Png/PngEncoder.cs | 5 +- src/ImageProcessor/Samplers/Resize.cs | 4 +- src/ImageProcessor/project.json | 4 +- .../Processors/Filters/FilterTests.cs | 55 ++++++++++--------- .../Processors/Formats/EncoderDecoderTests.cs | 6 +- .../Processors/ProcessorTestBase.cs | 10 ++-- .../Processors/Samplers/SamplerTests.cs | 3 +- tests/ImageProcessor.Tests/project.json | 2 +- 18 files changed, 120 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 528015329..73cfdffe7 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,40 @@ This is a complete rewrite from the ground up to allow the processing of images without the use of `System.Drawing` using a cross-platform class library. It's still in early stages but progress has been pretty quick. +[![Build status](https://ci.appveyor.com/api/projects/status/8ypr7527dnao04yr/branch/V3?svg=true)](https://ci.appveyor.com/project/JamesSouth/imageprocessor/branch/V3) +[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/JimBobSquarePants/ImageProcessor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + ###Why am I writing this? -With NETCore there is currently no version of `System.Drawing` to allow continued progress of the existing ImageProcessor library. Progress developing a crossplatform update are restricted to the [CoreFXLab repo](https://github.com/dotnet/corefxlab/tree/master/src/System.Drawing.Graphics) where progress seems to be very slow. +With NETCore there is currently no version of `System.Drawing` to allow continued progress of the existing ImageProcessor library. Progress developing a cross-platform update are restricted to the [CoreFXLab repo](https://github.com/dotnet/corefxlab/tree/master/src/System.Drawing.Graphics) where progress seems to be very slow. ###Is this wise? Honestly... I don't know. I could be writing code that may be suddenly obsolete. There has been little [feedback](https://github.com/dotnet/corefxlab/issues/86#issuecomment-139930600) on questions I've asked but it's a nice learning process if anything and I will definitely be releasing the code for consumption. +### Installation +At present the code is pre-release but when ready it will be available on [Nuget](http://www.nuget.org). + +**Pre-release downloads** + +We already have a [MyGet package repository](https://www.myget.org/F/imageprocessor/api/v3/index.json) - for bleeding-edge / development NuGet releases. + +### Manual build + +If you prefer, you can compile ImageProcessor yourself (please do and help!), you'll need: + +- Visual Studio 2015 (or above) +- The [Windows 10 development tools](https://dev.windows.com/en-us/downloads) - Click `Get Visual Studio Community`. +- Dnvm and Dnx installed + +To install the last two please see the instructions at the [DotNet documentation](http://dotnet.readthedocs.org/en/latest/getting-started/installing-core-windows.html) + +To clone it locally click the "Clone in Windows" button above or run the following git commands. + +```bash +git clone https://github.com/JimBobSquarePants/ImageProcessor +``` + ###What works so far/ What is planned? - Encoding/decoding of image formats (plugable) @@ -19,8 +45,8 @@ Honestly... I don't know. I could be writing code that may be suddenly obsolete. - [x] bmp (More bmp format saving support required, 24bit just now) - [x] png (Need updating for saving indexed support) - [x] gif -- Basic color structs with implicit operators. Vector backed. Need help investigating premultiplication. - - [x] Color - Float based, No limit to r, g, b, a values allowing for a fuller color range. +- Basic color structs with implicit operators. Vector backed. + - [x] Color - Float based, premultiplied alpha, No limit to r, g, b, a values allowing for a fuller color range. - [x] BGRA32 - [ ] CIE Lab - [ ] CIE XYZ @@ -81,6 +107,7 @@ Honestly... I don't know. I could be writing code that may be suddenly obsolete. - [x] Alpha - [x] Contrast - [x] Invert + - [x] BackgroundColor - [x] Brightness - [x] Saturation - [ ] Hue diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs index 4f6ca7eff..0f4d26ce1 100644 --- a/src/ImageProcessor/Colors/Color.cs +++ b/src/ImageProcessor/Colors/Color.cs @@ -81,10 +81,13 @@ namespace ImageProcessor if (hex.Length == 8) { - this.R = Convert.ToByte(hex.Substring(2, 2), 16) / 255f; - this.G = Convert.ToByte(hex.Substring(4, 2), 16) / 255f; - this.B = Convert.ToByte(hex.Substring(6, 2), 16) / 255f; - this.A = Convert.ToByte(hex.Substring(0, 2), 16) / 255f; + float r = Convert.ToByte(hex.Substring(2, 2), 16) / 255f; + float g = Convert.ToByte(hex.Substring(4, 2), 16) / 255f; + float b = Convert.ToByte(hex.Substring(6, 2), 16) / 255f; + float a = Convert.ToByte(hex.Substring(0, 2), 16) / 255f; + + this.backingVector = Color.FromNonPremultiplied(new Color(r, g, b, a)).ToVector4(); + } else if (hex.Length == 6) { @@ -95,13 +98,13 @@ namespace ImageProcessor } else { - string r = char.ToString(hex[0]); - string g = char.ToString(hex[1]); - string b = char.ToString(hex[2]); + string rh = char.ToString(hex[0]); + string gh = char.ToString(hex[1]); + string bh = char.ToString(hex[2]); - this.B = Convert.ToByte(b + b, 16) / 255f; - this.G = Convert.ToByte(g + g, 16) / 255f; - this.R = Convert.ToByte(r + r, 16) / 255f; + this.B = Convert.ToByte(bh + bh, 16) / 255f; + this.G = Convert.ToByte(gh + gh, 16) / 255f; + this.R = Convert.ToByte(rh + rh, 16) / 255f; this.A = 1; } } @@ -235,7 +238,7 @@ namespace ImageProcessor /// public static implicit operator Color(Bgra32 color) { - return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); + return Color.FromNonPremultiplied(new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f)); } /// @@ -471,8 +474,12 @@ namespace ImageProcessor { amount = amount.Clamp(0f, 1f); - //return (from * (1 - amount)) + (to * amount); - return (from * (1 - amount)) + to ; + if (to.A < 1) + { + return (from * (1 - amount)) + to; + } + + return (from * (1 - amount)) + (to * amount); } /// diff --git a/src/ImageProcessor/Colors/Formats/Bgra32.cs b/src/ImageProcessor/Colors/Formats/Bgra32.cs index f76753a79..98b78bc8a 100644 --- a/src/ImageProcessor/Colors/Formats/Bgra32.cs +++ b/src/ImageProcessor/Colors/Formats/Bgra32.cs @@ -94,7 +94,7 @@ namespace ImageProcessor /// public static implicit operator Bgra32(Color color) { - color = color.Limited; + color = Color.ToNonPremultiplied(color.Limited); return new Bgra32((255f * color.B).ToByte(), (255f * color.G).ToByte(), (255f * color.R).ToByte(), (255f * color.A).ToByte()); } diff --git a/src/ImageProcessor/Colors/Formats/Hsv.cs b/src/ImageProcessor/Colors/Formats/Hsv.cs index bc6865cbc..428db9e2c 100644 --- a/src/ImageProcessor/Colors/Formats/Hsv.cs +++ b/src/ImageProcessor/Colors/Formats/Hsv.cs @@ -76,7 +76,7 @@ namespace ImageProcessor /// public static implicit operator Hsv(Color color) { - color = color.Limited; + color = Color.ToNonPremultiplied(color.Limited); float r = color.R; float g = color.G; float b = color.B; diff --git a/src/ImageProcessor/Colors/Formats/YCbCr.cs b/src/ImageProcessor/Colors/Formats/YCbCr.cs index b1550c1ca..ec941321b 100644 --- a/src/ImageProcessor/Colors/Formats/YCbCr.cs +++ b/src/ImageProcessor/Colors/Formats/YCbCr.cs @@ -76,7 +76,7 @@ namespace ImageProcessor /// public static implicit operator YCbCr(Color color) { - color = color.Limited; + color = Color.ToNonPremultiplied(color.Limited); float r = color.R * 255f; float g = color.G * 255f; float b = color.B * 255f; diff --git a/src/ImageProcessor/Filters/Alpha.cs b/src/ImageProcessor/Filters/Alpha.cs index cbb4288c3..e3f051b9a 100644 --- a/src/ImageProcessor/Filters/Alpha.cs +++ b/src/ImageProcessor/Filters/Alpha.cs @@ -49,8 +49,9 @@ namespace ImageProcessor.Filters { for (int x = startX; x < endX; x++) { - Color color = source[x, y]; + Color color = Color.ToNonPremultiplied(source[x, y]); color.A = color.A * alpha; + color = Color.FromNonPremultiplied(color); target[x, y] = color; } } diff --git a/src/ImageProcessor/Filters/BackgroundColor.cs b/src/ImageProcessor/Filters/BackgroundColor.cs index 0f844f7a5..58c33db7e 100644 --- a/src/ImageProcessor/Filters/BackgroundColor.cs +++ b/src/ImageProcessor/Filters/BackgroundColor.cs @@ -46,8 +46,7 @@ namespace ImageProcessor.Filters { Color color = source[x, y]; - // TODO: Fix this nonesense. - if (color.A < .9) + if (color.A < 1) { color = Color.Lerp(color, backgroundColor, .5f); } diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs index ee6555131..2cb80ef7e 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs @@ -110,9 +110,18 @@ namespace ImageProcessor.Formats // Limit the output range and multiply out from our floating point. // Convert back to b-> g-> r-> a order. - writer.Write((byte)(data[offset + 2].Clamp(0, 1) * 255)); - writer.Write((byte)(data[offset + 1].Clamp(0, 1) * 255)); - writer.Write((byte)(data[offset].Clamp(0, 1) * 255)); + // Convert to non-premultiplied color. + float r = data[offset]; + float g = data[offset + 1]; + float b = data[offset + 2]; + float a = data[offset + 3]; + + // Implicit cast to Bgra32 handles premultiplication conversion. + Bgra32 color = new Color(r, g, b, a); + + writer.Write(color.B); + writer.Write(color.G); + writer.Write(color.R); } for (int i = 0; i < amount; i++) diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs index 039f9f82c..136f13037 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs @@ -79,7 +79,8 @@ namespace ImageProcessor.Formats for (int x = 0; x < width; x++) { // Now I have the pixel, call the FirstPassQuantize function... - this.InitialQuantizePixel(Color.ToNonPremultiplied(source[x, y])); + // Implicit cast to Bgra32 handles premultiplication conversion. + this.InitialQuantizePixel(source[x, y]); } } } @@ -106,8 +107,8 @@ namespace ImageProcessor.Formats { for (int x = 0; x < width; x++) { - // Implicit cast here from Color. - Bgra32 sourcePixel = Color.ToNonPremultiplied(source[x, y]); + // Implicit cast to Bgra32 handles premultiplication conversion. + Bgra32 sourcePixel = source[x, y]; // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimization. diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs index ed0a69a64..93c4a9de3 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs @@ -111,7 +111,8 @@ namespace ImageProcessor.Formats float b = sourcePixels[source + 2]; float a = sourcePixels[source + 3]; - Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a)); + // Implicit cast to Bgra32 handles premultiplication conversion. + Bgra32 color = new Color(r, g, b, a); samples[start] = color.R; samples[start + 1] = color.G; diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs index e4501a956..00ba4b0a3 100644 --- a/src/ImageProcessor/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs @@ -326,7 +326,8 @@ namespace ImageProcessor.Formats float b = pixels[pixelOffset + 2]; float a = pixels[pixelOffset + 3]; - Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a)); + // Implicit cast to Bgra32 handles premultiplication conversion. + Bgra32 color = new Color(r, g, b, a); data[dataOffset] = color.R; data[dataOffset + 1] = color.G; @@ -342,7 +343,7 @@ namespace ImageProcessor.Formats b = pixels[lastOffset + 2]; a = pixels[lastOffset + 3]; - color = Color.ToNonPremultiplied(new Color(r, g, b, a)); + color = new Color(r, g, b, a); data[dataOffset] -= color.R; data[dataOffset + 1] -= color.G; diff --git a/src/ImageProcessor/Samplers/Resize.cs b/src/ImageProcessor/Samplers/Resize.cs index 0155aba7f..6ea106724 100644 --- a/src/ImageProcessor/Samplers/Resize.cs +++ b/src/ImageProcessor/Samplers/Resize.cs @@ -17,7 +17,7 @@ namespace ImageProcessor.Samplers /// /// The epsilon for comparing floating point numbers. /// - private const float Epsilon = 0.00001f; + private const float Epsilon = 0.01f; /// /// The horizontal weights. @@ -112,6 +112,8 @@ namespace ImageProcessor.Samplers } } + // Ensure are alpha values only reflect possible values to prevent bleed. + destination.A = (float)Math.Round(destination.A, 2); target[x, y] = Color.Compand(destination); } } diff --git a/src/ImageProcessor/project.json b/src/ImageProcessor/project.json index e1e13a262..dc11613af 100644 --- a/src/ImageProcessor/project.json +++ b/src/ImageProcessor/project.json @@ -2,7 +2,7 @@ "version": "3.0.0-*", "description": "ImageProcessor", "authors": [ - "James Jackson-South" + "James Jackson-South and contributors" ], "tags": [ "Image Resize Crop Quality Gif Jpg Jpeg Bitmap Png Fluent Animated" @@ -19,7 +19,7 @@ "System.Runtime.Extensions": "4.0.10", "System.Reflection": "4.0.10", "System.IO": "4.0.10", - "StyleCop.Analyzers": "1.0.0-beta015", + "StyleCop.Analyzers": "1.0.0-beta016", "Microsoft.NETCore": "5.0.1-beta-23409", "Microsoft.NETCore.Platforms": "1.0.1-beta-23409" }, diff --git a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs index 584b62a5e..496ae75e4 100644 --- a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs @@ -13,33 +13,34 @@ namespace ImageProcessor.Tests { public static readonly TheoryData Filters = new TheoryData { - { "Brightness-50", new Brightness(50) }, - { "Brightness--50", new Brightness(-50) }, - { "Contrast-50", new Contrast(50) }, - { "Contrast--50", new Contrast(-50) }, - { "Blend", new Blend(new Image(File.OpenRead("TestImages/Formats/Bmp/Car.bmp")),15)}, - { "Saturation-50", new Saturation(50) }, - { "Saturation--50", new Saturation(-50) }, - { "Alpha--50", new Alpha(50) }, - { "Invert", new Invert() }, - { "Sepia", new Sepia() }, - { "BlackWhite", new BlackWhite() }, - { "Lomograph", new Lomograph() }, - { "Polaroid", new Polaroid() }, - { "Kodachrome", new Kodachrome() }, - { "GreyscaleBt709", new GreyscaleBt709() }, - { "GreyscaleBt601", new GreyscaleBt601() }, - { "Kayyali", new Kayyali() }, - { "Kirsch", new Kirsch() }, - { "Laplacian3X3", new Laplacian3X3() }, - { "Laplacian5X5", new Laplacian5X5() }, - { "LaplacianOfGaussian", new LaplacianOfGaussian() }, - { "Prewitt", new Prewitt() }, - { "RobertsCross", new RobertsCross() }, - { "Scharr", new Scharr() }, - { "Sobel", new Sobel {Greyscale = true} }, - { "GuassianBlur", new GuassianBlur(10) }, - { "GuassianSharpen", new GuassianSharpen(10) } + //{ "Brightness-50", new Brightness(50) }, + //{ "Brightness--50", new Brightness(-50) }, + //{ "Contrast-50", new Contrast(50) }, + //{ "Contrast--50", new Contrast(-50) }, + //{ "BackgroundColor", new BackgroundColor(new Color(243 / 255f, 87 / 255f, 161 / 255f))}, + { "Blend", new Blend(new Image(File.OpenRead("TestImages/Formats/Bmp/Car.bmp")),50)}, + //{ "Saturation-50", new Saturation(50) }, + //{ "Saturation--50", new Saturation(-50) }, + //{ "Alpha--50", new Alpha(50) }, + //{ "Invert", new Invert() }, + //{ "Sepia", new Sepia() }, + //{ "BlackWhite", new BlackWhite() }, + //{ "Lomograph", new Lomograph() }, + //{ "Polaroid", new Polaroid() }, + //{ "Kodachrome", new Kodachrome() }, + //{ "GreyscaleBt709", new GreyscaleBt709() }, + //{ "GreyscaleBt601", new GreyscaleBt601() }, + //{ "Kayyali", new Kayyali() }, + //{ "Kirsch", new Kirsch() }, + //{ "Laplacian3X3", new Laplacian3X3() }, + //{ "Laplacian5X5", new Laplacian5X5() }, + //{ "LaplacianOfGaussian", new LaplacianOfGaussian() }, + //{ "Prewitt", new Prewitt() }, + //{ "RobertsCross", new RobertsCross() }, + //{ "Scharr", new Scharr() }, + //{ "Sobel", new Sobel {Greyscale = true} }, + //{ "GuassianBlur", new GuassianBlur(10) }, + //{ "GuassianSharpen", new GuassianSharpen(10) } }; [Theory] diff --git a/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs b/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs index 8f3b8c453..38f13fa8e 100644 --- a/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs @@ -44,9 +44,9 @@ [Fact] public void QuantizedImageShouldPreserveMaximumColorPrecision() { - if (!Directory.Exists("Quantized")) + if (!Directory.Exists("TestOutput/Quantized")) { - Directory.CreateDirectory("Quantized"); + Directory.CreateDirectory("TestOutput/Quantized"); } foreach (string file in Files) @@ -57,7 +57,7 @@ IQuantizer quantizer = new OctreeQuantizer(); QuantizedImage quantizedImage = quantizer.Quantize(image); - using (FileStream output = File.OpenWrite($"Quantized/{Path.GetFileName(file)}")) + using (FileStream output = File.OpenWrite($"TestOutput/Quantized/{Path.GetFileName(file)}")) { quantizedImage.ToImage().Save(output); } diff --git a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs index 8d274ae95..28c92d66e 100644 --- a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs +++ b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs @@ -20,20 +20,20 @@ namespace ImageProcessor.Tests public static readonly List Files = new List { //"TestImages/Formats/Jpg/Backdrop.jpg", - //"TestImages/Formats/Jpg/Calliphora.jpg", - "TestImages/Formats/Jpg/china.jpg", + "TestImages/Formats/Jpg/Calliphora.jpg", + //"TestImages/Formats/Jpg/china.jpg", //"TestImages/Formats/Jpg/ant.jpg", //"TestImages/Formats/Jpg/parachute.jpg", //"TestImages/Formats/Jpg/lomo.jpg", //"TestImages/Formats/Jpg/shaftesbury.jpg", //"TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", //"TestImages/Formats/Jpg/greyscale.jpg", - //"TestImages/Formats/Bmp/Car.bmp", + "TestImages/Formats/Bmp/Car.bmp", "TestImages/Formats/Png/cballs.png", - "TestImages/Formats/Png/blur.png", + //"TestImages/Formats/Png/blur.png", //"TestImages/Formats/Png/cmyk.png", //"TestImages/Formats/Png/gamma-1.0-or-2.2.png", - "TestImages/Formats/Png/splash.png", + //"TestImages/Formats/Png/splash.png", "TestImages/Formats/Gif/leaf.gif", //"TestImages/Formats/Gif/ben2.gif", //"TestImages/Formats/Gif/rings.gif", diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs index 88d5b7a38..355230843 100644 --- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs @@ -4,7 +4,6 @@ namespace ImageProcessor.Tests using System.Diagnostics; using System.IO; - using ImageProcessor.Filters; using ImageProcessor.Samplers; using Xunit; @@ -44,7 +43,7 @@ namespace ImageProcessor.Tests { Stopwatch watch = Stopwatch.StartNew(); Image image = new Image(stream); - string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); + string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + ".jpg"; using (FileStream output = File.OpenWrite($"TestOutput/Resized/{filename}")) { image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output); diff --git a/tests/ImageProcessor.Tests/project.json b/tests/ImageProcessor.Tests/project.json index ed1af7522..0771ff1a7 100644 --- a/tests/ImageProcessor.Tests/project.json +++ b/tests/ImageProcessor.Tests/project.json @@ -1,7 +1,7 @@ { "version": "1.0.0-*", "description": "ImageProcessor.Tests Class Library", - "authors": [ "jeavon" ], + "authors": [ "James Jackson-South and contributors" ], "tags": [ "" ], "projectUrl": "", "licenseUrl": "",