Browse Source

Filters now use correct color struct.

Former-commit-id: 532b3bb4f629e1f61451c1549f855617245c6b34
Former-commit-id: 70f1bfd3db4addc970722242bf3f153958cc91c4
Former-commit-id: e3087704f67c536e89c807e5e1f89f81b67cbb6f
af/merge-core
James Jackson-South 10 years ago
parent
commit
d06bb12469
  1. 43
      src/ImageProcessor/Colors/Color.cs
  2. 8
      src/ImageProcessor/Filters/Alpha.cs
  3. 48
      src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
  4. 35
      src/ImageProcessor/Filters/ColorMatrix/Invert.cs
  5. 55
      src/ImageProcessor/Filters/Contrast.cs
  6. 8
      src/ImageProcessor/Filters/Invert.cs
  7. 1
      src/ImageProcessor/ImageProcessor.csproj
  8. 12
      tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
  9. 4
      tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs

43
src/ImageProcessor/Colors/Color.cs

@ -111,11 +111,34 @@ namespace ImageProcessor
/// <param name="vector">
/// The vector.
/// </param>
private Color(Vector4 vector)
public Color(Vector4 vector)
{
this.backingVector = vector;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector representing the red, green, and blue componenets.
/// </param>
public Color(Vector3 vector)
{
this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, 1);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector representing the red, green, and blue componenets.
/// </param>
/// <param name="alpha">The alpha component.</param>
public Color(Vector3 vector, float alpha)
{
this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, alpha);
}
/// <summary>
/// Gets or sets the red component of the color.
/// </summary>
@ -450,6 +473,24 @@ namespace ImageProcessor
return (from * (1 - amount)) + (to * amount);
}
/// <summary>
/// Gets a <see cref="Vector4"/> representation for this <see cref="Color"/>.
/// </summary>
/// <returns>A <see cref="Vector4"/> representation for this object.</returns>
public Vector4 ToVector4()
{
return new Vector4(this.R, this.G, this.B, this.A);
}
/// <summary>
/// Gets a <see cref="Vector3"/> representation for this <see cref="Color"/>.
/// </summary>
/// <returns>A <see cref="Vector3"/> representation for this object.</returns>
public Vector3 ToVector3()
{
return new Vector3(this.R, this.G, this.B);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{

8
src/ImageProcessor/Filters/Alpha.cs

@ -34,7 +34,7 @@ namespace ImageProcessor.Filters
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
double alpha = this.Value / 100.0;
float alpha = this.Value / 100f;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
@ -49,9 +49,9 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
Bgra32 color = source[x, y];
double a = color.A * alpha;
target[x, y] = new Bgra32(color.B, color.G, color.R, a.ToByte());
Color color = source[x, y];
color.A = color.A * alpha;
target[x, y] = color;
}
}
});

48
src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs

@ -36,13 +36,12 @@ namespace ImageProcessor.Filters
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
bool gamma = this.GammaAdjust;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
ColorMatrix matrix = this.Value;
Bgra32 previousColor = source[0, 0];
Bgra32 pixelValue = this.ApplyMatrix(previousColor, matrix);
Parallel.For(
startY,
@ -53,20 +52,7 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
Bgra32 sourceColor = 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.
if (sourceColor != previousColor)
{
// Perform the operation on the pixel.
pixelValue = this.ApplyMatrix(sourceColor, matrix);
// And setup the previous pointer
previousColor = sourceColor;
}
target[x, y] = pixelValue;
target[x, y] = ApplyMatrix(source[x, y], matrix, gamma);
}
}
});
@ -75,33 +61,31 @@ namespace ImageProcessor.Filters
/// <summary>
/// Applies the color matrix against the given color.
/// </summary>
/// <param name="sourceColor">The source color.</param>
/// <param name="color">The source color.</param>
/// <param name="matrix">The matrix.</param>
/// <param name="gamma">Whether to perform gamma adjustments.</param>
/// <returns>
/// The <see cref="Bgra32"/>.
/// The <see cref="Color"/>.
/// </returns>
private Bgra32 ApplyMatrix(Bgra32 sourceColor, ColorMatrix matrix)
private static Color ApplyMatrix(Color color, ColorMatrix matrix, bool gamma)
{
bool gamma = this.GammaAdjust;
if (gamma)
{
sourceColor = PixelOperations.ToLinear(sourceColor);
color = PixelOperations.ToLinear(color);
}
int sr = sourceColor.R;
int sg = sourceColor.G;
int sb = sourceColor.B;
int sa = sourceColor.A;
float sr = color.R;
float sg = color.G;
float sb = color.B;
float sa = color.A;
// TODO: Investigate RGBAW
byte r = ((sr * matrix.Matrix00) + (sg * matrix.Matrix10) + (sb * matrix.Matrix20) + (sa * matrix.Matrix30) + (255f * matrix.Matrix40)).ToByte();
byte g = ((sr * matrix.Matrix01) + (sg * matrix.Matrix11) + (sb * matrix.Matrix21) + (sa * matrix.Matrix31) + (255f * matrix.Matrix41)).ToByte();
byte b = ((sr * matrix.Matrix02) + (sg * matrix.Matrix12) + (sb * matrix.Matrix22) + (sa * matrix.Matrix32) + (255f * matrix.Matrix42)).ToByte();
byte a = ((sr * matrix.Matrix03) + (sg * matrix.Matrix13) + (sb * matrix.Matrix23) + (sa * matrix.Matrix33) + (255f * matrix.Matrix43)).ToByte();
color.R = (sr * matrix.Matrix00) + (sg * matrix.Matrix10) + (sb * matrix.Matrix20) + (sa * matrix.Matrix30) + matrix.Matrix40;
color.G = (sr * matrix.Matrix01) + (sg * matrix.Matrix11) + (sb * matrix.Matrix21) + (sa * matrix.Matrix31) + matrix.Matrix41;
color.B = (sr * matrix.Matrix02) + (sg * matrix.Matrix12) + (sb * matrix.Matrix22) + (sa * matrix.Matrix32) + matrix.Matrix42;
color.A = (sr * matrix.Matrix03) + (sg * matrix.Matrix13) + (sb * matrix.Matrix23) + (sa * matrix.Matrix33) + matrix.Matrix43;
// TODO: Fix this.
return gamma ? (Bgra32)PixelOperations.ToSrgb(new Bgra32(b, g, r, a)) : new Bgra32(b, g, r, a);
return gamma ? PixelOperations.ToSrgb(color) : color;
}
}
}

35
src/ImageProcessor/Filters/ColorMatrix/Invert.cs

@ -1,35 +0,0 @@
// <copyright file="Invert.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Filters
{
/// <summary>
/// Inverts the colors of the image.
/// </summary>
public class Invert : ColorMatrixFilter
{
/// <summary>
/// The inversion matrix.
/// TODO: With gamma adjustment enabled this leaves the image too bright.
/// </summary>
private static readonly ColorMatrix Matrix = new ColorMatrix(
new[]
{
new float[] { -1, 0, 0, 0, 0 },
new float[] { 0, -1, 0, 0, 0 },
new float[] { 0, 0, -1, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 1, 1, 1, 0, 1 }
});
/// <summary>
/// Initializes a new instance of the <see cref="Invert"/> class.
/// </summary>
public Invert()
: base(Matrix, false)
{
}
}
}

55
src/ImageProcessor/Filters/Contrast.cs

@ -34,7 +34,7 @@ namespace ImageProcessor.Filters
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
double contrast = (100.0 + this.Value) / 100.0;
float contrast = (100f + this.Value) / 100f;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
@ -49,36 +49,37 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
Bgra32 sourceColor = source[x, y];
sourceColor = PixelOperations.ToLinear(sourceColor);
target[x, y] = AdjustContrast(source[x, y], contrast);
}
}
});
}
double r = sourceColor.R / 255.0;
r -= 0.5;
r *= contrast;
r += 0.5;
r *= 255;
r = r.ToByte();
/// <summary>
/// Returns a <see cref="Color"/> with the contrast adjusted.
/// </summary>
/// <param name="color">The source color.</param>
/// <param name="contrast">The contrast adjustment factor.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
private static Color AdjustContrast(Color color, float contrast)
{
color = PixelOperations.ToLinear(color);
double g = sourceColor.G / 255.0;
g -= 0.5;
g *= contrast;
g += 0.5;
g *= 255;
g = g.ToByte();
color.R -= 0.5f;
color.R *= contrast;
color.R += 0.5f;
double b = sourceColor.B / 255.0;
b -= 0.5;
b *= contrast;
b += 0.5;
b *= 255;
b = b.ToByte();
color.G -= 0.5f;
color.G *= contrast;
color.G += 0.5f;
Bgra32 destinationColor = new Bgra32(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
}
}
});
color.B -= 0.5f;
color.B *= contrast;
color.B += 0.5f;
return PixelOperations.ToSrgb(color);
}
}
}

8
src/ImageProcessor/Filters/Invert.cs

@ -30,9 +30,11 @@ namespace ImageProcessor.Filters
for (int x = startX; x < endX; x++)
{
// TODO: This doesn't work for gamma test images.
Bgra32 color = source[x, y];
Bgra32 targetColor = new Bgra32((255 - color.B).ToByte(), (255 - color.G).ToByte(), (255 - color.R).ToByte(), color.A);
target[x, y] = targetColor;
Color color = source[x, y];
color.R = 1 - color.R;
color.G = 1 - color.G;
color.B = 1 - color.B;
target[x, y] = color;
}
}
});

1
src/ImageProcessor/ImageProcessor.csproj

@ -40,6 +40,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\ImageProcessor.XML</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="Colors\Formats\Cmyk.cs" />

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

@ -15,13 +15,13 @@ namespace ImageProcessor.Tests
//{ "Contrast-50", new Contrast(50) },
//{ "Contrast--50", new Contrast(-50) },
//{ "Alpha--50", new Alpha(50) },
//{ "Invert", new Invert() },
//{ "Sepia", new Sepia() },
//{ "BlackWhite", new BlackWhite() },
//{ "Lomograph", new Lomograph() },
//{ "Polaroid", new Polaroid() },
{ "Invert", new Invert() },
{ "Sepia", new Sepia() },
{ "BlackWhite", new BlackWhite() },
{ "Lomograph", new Lomograph() },
{ "Polaroid", new Polaroid() },
{ "GreyscaleBt709", new GreyscaleBt709() },
//{ "GreyscaleBt601", new GreyscaleBt601() },
{ "GreyscaleBt601", new GreyscaleBt601() },
};
[Theory]

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

@ -29,8 +29,8 @@ namespace ImageProcessor.Tests
"../../TestImages/Formats/Png/splash.png",
"../../TestImages/Formats/Gif/leaf.gif",
"../../TestImages/Formats/Gif/rings.gif",
"../../TestImages/Formats/Gif/ani2.gif",
"../../TestImages/Formats/Gif/giphy.gif"
//"../../TestImages/Formats/Gif/ani2.gif",
//"../../TestImages/Formats/Gif/giphy.gif"
};
}
}

Loading…
Cancel
Save