From d06bb12469857b6ca46b53411ebe51e37b98dd04 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 1 Nov 2015 00:42:36 +1100 Subject: [PATCH] Filters now use correct color struct. Former-commit-id: 532b3bb4f629e1f61451c1549f855617245c6b34 Former-commit-id: 70f1bfd3db4addc970722242bf3f153958cc91c4 Former-commit-id: e3087704f67c536e89c807e5e1f89f81b67cbb6f --- src/ImageProcessor/Colors/Color.cs | 43 ++++++++++++++- src/ImageProcessor/Filters/Alpha.cs | 8 +-- .../Filters/ColorMatrix/ColorMatrixFilter.cs | 48 ++++++---------- .../Filters/ColorMatrix/Invert.cs | 35 ------------ src/ImageProcessor/Filters/Contrast.cs | 55 ++++++++++--------- src/ImageProcessor/Filters/Invert.cs | 8 ++- src/ImageProcessor/ImageProcessor.csproj | 1 + .../Processors/Filters/FilterTests.cs | 12 ++-- .../Processors/ProcessorTestBase.cs | 4 +- 9 files changed, 104 insertions(+), 110 deletions(-) delete mode 100644 src/ImageProcessor/Filters/ColorMatrix/Invert.cs diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs index 1ffda3c46..d85464471 100644 --- a/src/ImageProcessor/Colors/Color.cs +++ b/src/ImageProcessor/Colors/Color.cs @@ -111,11 +111,34 @@ namespace ImageProcessor /// /// The vector. /// - private Color(Vector4 vector) + public Color(Vector4 vector) { this.backingVector = vector; } + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector representing the red, green, and blue componenets. + /// + public Color(Vector3 vector) + { + this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector representing the red, green, and blue componenets. + /// + /// The alpha component. + public Color(Vector3 vector, float alpha) + { + this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, alpha); + } + /// /// Gets or sets the red component of the color. /// @@ -450,6 +473,24 @@ namespace ImageProcessor return (from * (1 - amount)) + (to * amount); } + /// + /// Gets a representation for this . + /// + /// A representation for this object. + public Vector4 ToVector4() + { + return new Vector4(this.R, this.G, this.B, this.A); + } + + /// + /// Gets a representation for this . + /// + /// A representation for this object. + public Vector3 ToVector3() + { + return new Vector3(this.R, this.G, this.B); + } + /// public override bool Equals(object obj) { diff --git a/src/ImageProcessor/Filters/Alpha.cs b/src/ImageProcessor/Filters/Alpha.cs index 02c7ff364..4f2e01f70 100644 --- a/src/ImageProcessor/Filters/Alpha.cs +++ b/src/ImageProcessor/Filters/Alpha.cs @@ -34,7 +34,7 @@ namespace ImageProcessor.Filters /// 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; } } }); diff --git a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs index 68f929be6..17a49b8fb 100644 --- a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs +++ b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs @@ -36,13 +36,12 @@ namespace ImageProcessor.Filters /// 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 /// /// Applies the color matrix against the given color. /// - /// The source color. + /// The source color. /// The matrix. + /// Whether to perform gamma adjustments. /// - /// The . + /// The . /// - 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; } } } diff --git a/src/ImageProcessor/Filters/ColorMatrix/Invert.cs b/src/ImageProcessor/Filters/ColorMatrix/Invert.cs deleted file mode 100644 index 24c8f52a0..000000000 --- a/src/ImageProcessor/Filters/ColorMatrix/Invert.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Filters -{ - /// - /// Inverts the colors of the image. - /// - public class Invert : ColorMatrixFilter - { - /// - /// The inversion matrix. - /// TODO: With gamma adjustment enabled this leaves the image too bright. - /// - 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 } - }); - - /// - /// Initializes a new instance of the class. - /// - public Invert() - : base(Matrix, false) - { - } - } -} diff --git a/src/ImageProcessor/Filters/Contrast.cs b/src/ImageProcessor/Filters/Contrast.cs index 2644b8f72..0986dc3d0 100644 --- a/src/ImageProcessor/Filters/Contrast.cs +++ b/src/ImageProcessor/Filters/Contrast.cs @@ -34,7 +34,7 @@ namespace ImageProcessor.Filters /// 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(); + /// + /// Returns a with the contrast adjusted. + /// + /// The source color. + /// The contrast adjustment factor. + /// + /// The . + /// + 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); } } } diff --git a/src/ImageProcessor/Filters/Invert.cs b/src/ImageProcessor/Filters/Invert.cs index ade3c6829..d2fa9445a 100644 --- a/src/ImageProcessor/Filters/Invert.cs +++ b/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; } } }); diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 3e506f65e..48294d14f 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -40,6 +40,7 @@ prompt 4 bin\Release\ImageProcessor.XML + true diff --git a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs index f364dd3f0..e1fe80d01 100644 --- a/tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs +++ b/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] diff --git a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs index 7f9e8ccc6..9ece3b02b 100644 --- a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs +++ b/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" }; } }