Browse Source

Add Alpha, enhance Contrast

Former-commit-id: a06e8058488c43fa8048b4ffd480d8424d88cc27
Former-commit-id: f0edb842766e8c2d2276625aa23b79ca1739918e
Former-commit-id: c55ff7a504d21ce4941fe51596c1beb621ef2082
pull/17/head
James Jackson-South 10 years ago
parent
commit
aff5df8cca
  1. 56
      src/ImageProcessor/Filters/Alpha.cs
  2. 54
      src/ImageProcessor/Filters/Contrast.cs
  3. 46
      src/ImageProcessor/Filters/ImageFilterExtensions.cs
  4. 2
      src/ImageProcessor/ImageProcessor.csproj
  5. 28
      tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
  6. 3
      tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs

56
src/ImageProcessor/Filters/Alpha.cs

@ -0,0 +1,56 @@
// <copyright file="Alpha.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Filters
{
using System;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>.
/// </summary>
public class Alpha : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="Alpha"/> class.
/// </summary>
/// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="percent"/> is less than 0 or is greater than 100.
/// </exception>
public Alpha(int percent)
{
Guard.MustBeBetweenOrEqualTo(percent, 0, 100, nameof(percent));
this.Value = percent;
}
/// <summary>
/// Gets the alpha value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
double alpha = this.Value / 100.0;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
for (int y = startY; y < endY; y++)
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Bgra color = source[x, y];
double a = color.A * alpha;
target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte());
}
}
}
}
}
}

54
src/ImageProcessor/Filters/Contrast.cs

@ -17,7 +17,7 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param> /// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="contrast"/> is less than -100 is greater than 100. /// <paramref name="contrast"/> is less than -100 or is greater than 100.
/// </exception> /// </exception>
public Contrast(int contrast) public Contrast(int contrast)
{ {
@ -34,35 +34,45 @@ namespace ImageProcessor.Filters
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
double contrast = (100.0 + this.Value) / 100.0; double contrast = (100.0 + this.Value) / 100.0;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
for (int y = startY; y < endY; y++) for (int y = startY; y < endY; y++)
{ {
for (int x = sourceRectangle.X; x < sourceRectangle.Right; x++) if (y >= sourceY && y < sourceBottom)
{ {
Bgra color = source[x, y]; for (int x = startX; x < endX; x++)
{
Bgra sourceColor = source[x, y];
sourceColor = PixelOperations.ToLinear(sourceColor);
double r = color.R / 255.0; double r = sourceColor.R / 255.0;
r -= 0.5; r -= 0.5;
r *= contrast; r *= contrast;
r += 0.5; r += 0.5;
r *= 255; r *= 255;
r = r.ToByte(); r = r.ToByte();
double g = color.G / 255.0; double g = sourceColor.G / 255.0;
g -= 0.5; g -= 0.5;
g *= contrast; g *= contrast;
g += 0.5; g += 0.5;
g *= 255; g *= 255;
g = g.ToByte(); g = g.ToByte();
double b = color.B / 255.0; double b = sourceColor.B / 255.0;
b -= 0.5; b -= 0.5;
b *= contrast; b *= contrast;
b += 0.5; b += 0.5;
b *= 255; b *= 255;
b = b.ToByte(); b = b.ToByte();
target[x, y] = new Bgra((byte)b, (byte)g, (byte)r, color.A); Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
}
} }
} }
} }

46
src/ImageProcessor/Filters/ImageFilterExtensions.cs

@ -6,7 +6,7 @@
namespace ImageProcessor.Filters namespace ImageProcessor.Filters
{ {
/// <summary> /// <summary>
/// Exstensions methods for <see cref="Image"/> to apply filters to the image. /// Extensions methods for <see cref="Image"/> to apply filters to the image.
/// </summary> /// </summary>
public static class ImageFilterExtensions public static class ImageFilterExtensions
{ {
@ -16,6 +16,48 @@ namespace ImageProcessor.Filters
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param> /// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Contrast(this Image source, int amount) => source.Process(new Contrast(amount)); public static Image Contrast(this Image source, int amount)
{
return Contrast(source, amount, source.Bounds);
}
/// <summary>
/// Alters the contrast component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Contrast(this Image source, int amount, Rectangle sourceRectangle)
{
return source.Process(sourceRectangle, new Contrast(amount));
}
/// <summary>
/// Alters the alpha component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Alpha(this Image source, int percent)
{
return Alpha(source, percent, source.Bounds);
}
/// <summary>
/// Alters the alpha component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Alpha(this Image source, int percent, Rectangle sourceRectangle)
{
return source.Process(sourceRectangle, new Alpha(percent));
}
} }
} }

2
src/ImageProcessor/ImageProcessor.csproj

@ -43,6 +43,7 @@
<Compile Include="Colors\Cmyk.cs" /> <Compile Include="Colors\Cmyk.cs" />
<Compile Include="Common\Helpers\ImageMaths.cs" /> <Compile Include="Common\Helpers\ImageMaths.cs" />
<Compile Include="Common\Helpers\PixelOperations.cs" /> <Compile Include="Common\Helpers\PixelOperations.cs" />
<Compile Include="Filters\Alpha.cs" />
<Compile Include="Filters\Contrast.cs" /> <Compile Include="Filters\Contrast.cs" />
<Compile Include="Filters\ImageFilterExtensions.cs" /> <Compile Include="Filters\ImageFilterExtensions.cs" />
<Compile Include="ImageExtensions.cs" /> <Compile Include="ImageExtensions.cs" />
@ -181,6 +182,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Numerics\Rectangle.cs" /> <Compile Include="Numerics\Rectangle.cs" />
<Compile Include="Numerics\Size.cs" /> <Compile Include="Numerics\Size.cs" />
<Compile Include="Samplers\Resamplers\Lanczos5Resampler.cs" />
<Compile Include="Samplers\Resamplers\Lanczos8Resampler.cs" /> <Compile Include="Samplers\Resamplers\Lanczos8Resampler.cs" />
<Compile Include="Samplers\Resamplers\WelchResampler.cs" /> <Compile Include="Samplers\Resamplers\WelchResampler.cs" />
<Compile Include="Samplers\Resamplers\CatmullRomResampler.cs" /> <Compile Include="Samplers\Resamplers\CatmullRomResampler.cs" />

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

@ -1,12 +1,10 @@
 
namespace ImageProcessor.Tests namespace ImageProcessor.Tests
{ {
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using ImageProcessor.Filters; using ImageProcessor.Filters;
using ImageProcessor.Samplers;
using Xunit; using Xunit;
@ -16,6 +14,7 @@ namespace ImageProcessor.Tests
{ {
{ "Contrast-50", new Contrast(50) }, { "Contrast-50", new Contrast(50) },
{ "Contrast--50", new Contrast(-50) }, { "Contrast--50", new Contrast(-50) },
{ "Alpha--50", new Alpha(50) },
}; };
[Theory] [Theory]
@ -43,30 +42,5 @@ namespace ImageProcessor.Tests
} }
} }
} }
[Fact]
public void ResizeImage()
{
if (!Directory.Exists("Resized"))
{
Directory.CreateDirectory("Resized");
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream);
string filename = Path.GetFileName(file);
using (FileStream output = File.OpenWrite($"Resized/{ Path.GetFileName(filename) }"))
{
image.Resize(400, 400).Save(output);
}
Trace.WriteLine($"{ filename }: { watch.ElapsedMilliseconds}ms");
}
}
}
} }
} }

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

@ -46,7 +46,8 @@ namespace ImageProcessor.Tests
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"Resized/{filename}")) using (FileStream output = File.OpenWrite($"Resized/{filename}"))
{ {
image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output); //image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output);
image.Resize(500, 500, sampler, new Rectangle(0, 0, 100, 100), new Rectangle(0, 0, 500, 500)).Save(output);
} }
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");

Loading…
Cancel
Save