Browse Source

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
pull/17/head
James Jackson-South 10 years ago
parent
commit
56584d9656
  1. 33
      README.md
  2. 33
      src/ImageProcessor/Colors/Color.cs
  3. 2
      src/ImageProcessor/Colors/Formats/Bgra32.cs
  4. 2
      src/ImageProcessor/Colors/Formats/Hsv.cs
  5. 2
      src/ImageProcessor/Colors/Formats/YCbCr.cs
  6. 3
      src/ImageProcessor/Filters/Alpha.cs
  7. 3
      src/ImageProcessor/Filters/BackgroundColor.cs
  8. 15
      src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
  9. 7
      src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
  10. 3
      src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
  11. 5
      src/ImageProcessor/Formats/Png/PngEncoder.cs
  12. 4
      src/ImageProcessor/Samplers/Resize.cs
  13. 4
      src/ImageProcessor/project.json
  14. 55
      tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
  15. 6
      tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs
  16. 10
      tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs
  17. 3
      tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
  18. 2
      tests/ImageProcessor.Tests/project.json

33
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. 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? ###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? ###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. 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? ###What works so far/ What is planned?
- Encoding/decoding of image formats (plugable) - 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] bmp (More bmp format saving support required, 24bit just now)
- [x] png (Need updating for saving indexed support) - [x] png (Need updating for saving indexed support)
- [x] gif - [x] gif
- Basic color structs with implicit operators. Vector backed. Need help investigating premultiplication. - Basic color structs with implicit operators. Vector backed.
- [x] Color - Float based, No limit to r, g, b, a values allowing for a fuller color range. - [x] Color - Float based, premultiplied alpha, No limit to r, g, b, a values allowing for a fuller color range.
- [x] BGRA32 - [x] BGRA32
- [ ] CIE Lab - [ ] CIE Lab
- [ ] CIE XYZ - [ ] CIE XYZ
@ -81,6 +107,7 @@ Honestly... I don't know. I could be writing code that may be suddenly obsolete.
- [x] Alpha - [x] Alpha
- [x] Contrast - [x] Contrast
- [x] Invert - [x] Invert
- [x] BackgroundColor
- [x] Brightness - [x] Brightness
- [x] Saturation - [x] Saturation
- [ ] Hue - [ ] Hue

33
src/ImageProcessor/Colors/Color.cs

@ -81,10 +81,13 @@ namespace ImageProcessor
if (hex.Length == 8) if (hex.Length == 8)
{ {
this.R = Convert.ToByte(hex.Substring(2, 2), 16) / 255f; float r = Convert.ToByte(hex.Substring(2, 2), 16) / 255f;
this.G = Convert.ToByte(hex.Substring(4, 2), 16) / 255f; float g = Convert.ToByte(hex.Substring(4, 2), 16) / 255f;
this.B = Convert.ToByte(hex.Substring(6, 2), 16) / 255f; float b = Convert.ToByte(hex.Substring(6, 2), 16) / 255f;
this.A = Convert.ToByte(hex.Substring(0, 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) else if (hex.Length == 6)
{ {
@ -95,13 +98,13 @@ namespace ImageProcessor
} }
else else
{ {
string r = char.ToString(hex[0]); string rh = char.ToString(hex[0]);
string g = char.ToString(hex[1]); string gh = char.ToString(hex[1]);
string b = char.ToString(hex[2]); string bh = char.ToString(hex[2]);
this.B = Convert.ToByte(b + b, 16) / 255f; this.B = Convert.ToByte(bh + bh, 16) / 255f;
this.G = Convert.ToByte(g + g, 16) / 255f; this.G = Convert.ToByte(gh + gh, 16) / 255f;
this.R = Convert.ToByte(r + r, 16) / 255f; this.R = Convert.ToByte(rh + rh, 16) / 255f;
this.A = 1; this.A = 1;
} }
} }
@ -235,7 +238,7 @@ namespace ImageProcessor
/// </returns> /// </returns>
public static implicit operator Color(Bgra32 color) 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));
} }
/// <summary> /// <summary>
@ -471,8 +474,12 @@ namespace ImageProcessor
{ {
amount = amount.Clamp(0f, 1f); amount = amount.Clamp(0f, 1f);
//return (from * (1 - amount)) + (to * amount); if (to.A < 1)
return (from * (1 - amount)) + to ; {
return (from * (1 - amount)) + to;
}
return (from * (1 - amount)) + (to * amount);
} }
/// <summary> /// <summary>

2
src/ImageProcessor/Colors/Formats/Bgra32.cs

@ -94,7 +94,7 @@ namespace ImageProcessor
/// </returns> /// </returns>
public static implicit operator Bgra32(Color color) 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()); return new Bgra32((255f * color.B).ToByte(), (255f * color.G).ToByte(), (255f * color.R).ToByte(), (255f * color.A).ToByte());
} }

2
src/ImageProcessor/Colors/Formats/Hsv.cs

@ -76,7 +76,7 @@ namespace ImageProcessor
/// </returns> /// </returns>
public static implicit operator Hsv(Color color) public static implicit operator Hsv(Color color)
{ {
color = color.Limited; color = Color.ToNonPremultiplied(color.Limited);
float r = color.R; float r = color.R;
float g = color.G; float g = color.G;
float b = color.B; float b = color.B;

2
src/ImageProcessor/Colors/Formats/YCbCr.cs

@ -76,7 +76,7 @@ namespace ImageProcessor
/// </returns> /// </returns>
public static implicit operator YCbCr(Color color) public static implicit operator YCbCr(Color color)
{ {
color = color.Limited; color = Color.ToNonPremultiplied(color.Limited);
float r = color.R * 255f; float r = color.R * 255f;
float g = color.G * 255f; float g = color.G * 255f;
float b = color.B * 255f; float b = color.B * 255f;

3
src/ImageProcessor/Filters/Alpha.cs

@ -49,8 +49,9 @@ namespace ImageProcessor.Filters
{ {
for (int x = startX; x < endX; x++) 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.A = color.A * alpha;
color = Color.FromNonPremultiplied(color);
target[x, y] = color; target[x, y] = color;
} }
} }

3
src/ImageProcessor/Filters/BackgroundColor.cs

@ -46,8 +46,7 @@ namespace ImageProcessor.Filters
{ {
Color color = source[x, y]; Color color = source[x, y];
// TODO: Fix this nonesense. if (color.A < 1)
if (color.A < .9)
{ {
color = Color.Lerp(color, backgroundColor, .5f); color = Color.Lerp(color, backgroundColor, .5f);
} }

15
src/ImageProcessor/Formats/Bmp/BmpEncoder.cs

@ -110,9 +110,18 @@ namespace ImageProcessor.Formats
// Limit the output range and multiply out from our floating point. // Limit the output range and multiply out from our floating point.
// Convert back to b-> g-> r-> a order. // Convert back to b-> g-> r-> a order.
writer.Write((byte)(data[offset + 2].Clamp(0, 1) * 255)); // Convert to non-premultiplied color.
writer.Write((byte)(data[offset + 1].Clamp(0, 1) * 255)); float r = data[offset];
writer.Write((byte)(data[offset].Clamp(0, 1) * 255)); 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++) for (int i = 0; i < amount; i++)

7
src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs

@ -79,7 +79,8 @@ namespace ImageProcessor.Formats
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
// Now I have the pixel, call the FirstPassQuantize function... // 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++) for (int x = 0; x < width; x++)
{ {
// Implicit cast here from Color. // Implicit cast to Bgra32 handles premultiplication conversion.
Bgra32 sourcePixel = Color.ToNonPremultiplied(source[x, y]); Bgra32 sourcePixel = source[x, y];
// Check if this is the same as the last pixel. If so use that value // 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. // rather than calculating it again. This is an inexpensive optimization.

3
src/ImageProcessor/Formats/Jpg/JpegEncoder.cs

@ -111,7 +111,8 @@ namespace ImageProcessor.Formats
float b = sourcePixels[source + 2]; float b = sourcePixels[source + 2];
float a = sourcePixels[source + 3]; 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] = color.R;
samples[start + 1] = color.G; samples[start + 1] = color.G;

5
src/ImageProcessor/Formats/Png/PngEncoder.cs

@ -326,7 +326,8 @@ namespace ImageProcessor.Formats
float b = pixels[pixelOffset + 2]; float b = pixels[pixelOffset + 2];
float a = pixels[pixelOffset + 3]; 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] = color.R;
data[dataOffset + 1] = color.G; data[dataOffset + 1] = color.G;
@ -342,7 +343,7 @@ namespace ImageProcessor.Formats
b = pixels[lastOffset + 2]; b = pixels[lastOffset + 2];
a = pixels[lastOffset + 3]; 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] -= color.R;
data[dataOffset + 1] -= color.G; data[dataOffset + 1] -= color.G;

4
src/ImageProcessor/Samplers/Resize.cs

@ -17,7 +17,7 @@ namespace ImageProcessor.Samplers
/// <summary> /// <summary>
/// The epsilon for comparing floating point numbers. /// The epsilon for comparing floating point numbers.
/// </summary> /// </summary>
private const float Epsilon = 0.00001f; private const float Epsilon = 0.01f;
/// <summary> /// <summary>
/// The horizontal weights. /// 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); target[x, y] = Color.Compand(destination);
} }
} }

4
src/ImageProcessor/project.json

@ -2,7 +2,7 @@
"version": "3.0.0-*", "version": "3.0.0-*",
"description": "ImageProcessor", "description": "ImageProcessor",
"authors": [ "authors": [
"James Jackson-South" "James Jackson-South and contributors"
], ],
"tags": [ "tags": [
"Image Resize Crop Quality Gif Jpg Jpeg Bitmap Png Fluent Animated" "Image Resize Crop Quality Gif Jpg Jpeg Bitmap Png Fluent Animated"
@ -19,7 +19,7 @@
"System.Runtime.Extensions": "4.0.10", "System.Runtime.Extensions": "4.0.10",
"System.Reflection": "4.0.10", "System.Reflection": "4.0.10",
"System.IO": "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": "5.0.1-beta-23409",
"Microsoft.NETCore.Platforms": "1.0.1-beta-23409" "Microsoft.NETCore.Platforms": "1.0.1-beta-23409"
}, },

55
tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs

@ -13,33 +13,34 @@ namespace ImageProcessor.Tests
{ {
public static readonly TheoryData<string, IImageProcessor> Filters = new TheoryData<string, IImageProcessor> public static readonly TheoryData<string, IImageProcessor> Filters = new TheoryData<string, IImageProcessor>
{ {
{ "Brightness-50", new Brightness(50) }, //{ "Brightness-50", new Brightness(50) },
{ "Brightness--50", new Brightness(-50) }, //{ "Brightness--50", new Brightness(-50) },
{ "Contrast-50", new Contrast(50) }, //{ "Contrast-50", new Contrast(50) },
{ "Contrast--50", new Contrast(-50) }, //{ "Contrast--50", new Contrast(-50) },
{ "Blend", new Blend(new Image(File.OpenRead("TestImages/Formats/Bmp/Car.bmp")),15)}, //{ "BackgroundColor", new BackgroundColor(new Color(243 / 255f, 87 / 255f, 161 / 255f))},
{ "Saturation-50", new Saturation(50) }, { "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) }, //{ "Saturation--50", new Saturation(-50) },
{ "Invert", new Invert() }, //{ "Alpha--50", new Alpha(50) },
{ "Sepia", new Sepia() }, //{ "Invert", new Invert() },
{ "BlackWhite", new BlackWhite() }, //{ "Sepia", new Sepia() },
{ "Lomograph", new Lomograph() }, //{ "BlackWhite", new BlackWhite() },
{ "Polaroid", new Polaroid() }, //{ "Lomograph", new Lomograph() },
{ "Kodachrome", new Kodachrome() }, //{ "Polaroid", new Polaroid() },
{ "GreyscaleBt709", new GreyscaleBt709() }, //{ "Kodachrome", new Kodachrome() },
{ "GreyscaleBt601", new GreyscaleBt601() }, //{ "GreyscaleBt709", new GreyscaleBt709() },
{ "Kayyali", new Kayyali() }, //{ "GreyscaleBt601", new GreyscaleBt601() },
{ "Kirsch", new Kirsch() }, //{ "Kayyali", new Kayyali() },
{ "Laplacian3X3", new Laplacian3X3() }, //{ "Kirsch", new Kirsch() },
{ "Laplacian5X5", new Laplacian5X5() }, //{ "Laplacian3X3", new Laplacian3X3() },
{ "LaplacianOfGaussian", new LaplacianOfGaussian() }, //{ "Laplacian5X5", new Laplacian5X5() },
{ "Prewitt", new Prewitt() }, //{ "LaplacianOfGaussian", new LaplacianOfGaussian() },
{ "RobertsCross", new RobertsCross() }, //{ "Prewitt", new Prewitt() },
{ "Scharr", new Scharr() }, //{ "RobertsCross", new RobertsCross() },
{ "Sobel", new Sobel {Greyscale = true} }, //{ "Scharr", new Scharr() },
{ "GuassianBlur", new GuassianBlur(10) }, //{ "Sobel", new Sobel {Greyscale = true} },
{ "GuassianSharpen", new GuassianSharpen(10) } //{ "GuassianBlur", new GuassianBlur(10) },
//{ "GuassianSharpen", new GuassianSharpen(10) }
}; };
[Theory] [Theory]

6
tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs

@ -44,9 +44,9 @@
[Fact] [Fact]
public void QuantizedImageShouldPreserveMaximumColorPrecision() public void QuantizedImageShouldPreserveMaximumColorPrecision()
{ {
if (!Directory.Exists("Quantized")) if (!Directory.Exists("TestOutput/Quantized"))
{ {
Directory.CreateDirectory("Quantized"); Directory.CreateDirectory("TestOutput/Quantized");
} }
foreach (string file in Files) foreach (string file in Files)
@ -57,7 +57,7 @@
IQuantizer quantizer = new OctreeQuantizer(); IQuantizer quantizer = new OctreeQuantizer();
QuantizedImage quantizedImage = quantizer.Quantize(image); 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); quantizedImage.ToImage().Save(output);
} }

10
tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs

@ -20,20 +20,20 @@ namespace ImageProcessor.Tests
public static readonly List<string> Files = new List<string> public static readonly List<string> Files = new List<string>
{ {
//"TestImages/Formats/Jpg/Backdrop.jpg", //"TestImages/Formats/Jpg/Backdrop.jpg",
//"TestImages/Formats/Jpg/Calliphora.jpg", "TestImages/Formats/Jpg/Calliphora.jpg",
"TestImages/Formats/Jpg/china.jpg", //"TestImages/Formats/Jpg/china.jpg",
//"TestImages/Formats/Jpg/ant.jpg", //"TestImages/Formats/Jpg/ant.jpg",
//"TestImages/Formats/Jpg/parachute.jpg", //"TestImages/Formats/Jpg/parachute.jpg",
//"TestImages/Formats/Jpg/lomo.jpg", //"TestImages/Formats/Jpg/lomo.jpg",
//"TestImages/Formats/Jpg/shaftesbury.jpg", //"TestImages/Formats/Jpg/shaftesbury.jpg",
//"TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", //"TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg",
//"TestImages/Formats/Jpg/greyscale.jpg", //"TestImages/Formats/Jpg/greyscale.jpg",
//"TestImages/Formats/Bmp/Car.bmp", "TestImages/Formats/Bmp/Car.bmp",
"TestImages/Formats/Png/cballs.png", "TestImages/Formats/Png/cballs.png",
"TestImages/Formats/Png/blur.png", //"TestImages/Formats/Png/blur.png",
//"TestImages/Formats/Png/cmyk.png", //"TestImages/Formats/Png/cmyk.png",
//"TestImages/Formats/Png/gamma-1.0-or-2.2.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/leaf.gif",
//"TestImages/Formats/Gif/ben2.gif", //"TestImages/Formats/Gif/ben2.gif",
//"TestImages/Formats/Gif/rings.gif", //"TestImages/Formats/Gif/rings.gif",

3
tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs

@ -4,7 +4,6 @@ namespace ImageProcessor.Tests
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using ImageProcessor.Filters;
using ImageProcessor.Samplers; using ImageProcessor.Samplers;
using Xunit; using Xunit;
@ -44,7 +43,7 @@ namespace ImageProcessor.Tests
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); 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}")) using (FileStream output = File.OpenWrite($"TestOutput/Resized/{filename}"))
{ {
image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output); image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output);

2
tests/ImageProcessor.Tests/project.json

@ -1,7 +1,7 @@
{ {
"version": "1.0.0-*", "version": "1.0.0-*",
"description": "ImageProcessor.Tests Class Library", "description": "ImageProcessor.Tests Class Library",
"authors": [ "jeavon" ], "authors": [ "James Jackson-South and contributors" ],
"tags": [ "" ], "tags": [ "" ],
"projectUrl": "", "projectUrl": "",
"licenseUrl": "", "licenseUrl": "",

Loading…
Cancel
Save