From a3c1d1302f9fa9ca86f4b4d2dbecc02b25d9f260 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 28 Jul 2016 13:29:01 +1000 Subject: [PATCH] Hue, Brightness Former-commit-id: 390a36f397e55d0caeaca4d5353be432da1c96b9 Former-commit-id: a802e31bbbfe18164aa7f17b057bcf69d3f495b3 Former-commit-id: 6275da035cee61d2d7abf1da32acc7db5f6aae2d --- src/ImageProcessorCore/Filters/Brightness.cs | 60 +++++++++++++++ src/ImageProcessorCore/Filters/Hue.cs | 60 +++++++++++++++ .../Filters/Processors/BrightnessProcessor.cs | 77 +++++++++++++++++++ src/ImageProcessorCore/Filters/Saturation.cs | 2 +- .../Processors/Filters/BrightnessTest.cs | 47 +++++++++++ .../Processors/Filters/HueTest.cs | 47 +++++++++++ 6 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 src/ImageProcessorCore/Filters/Brightness.cs create mode 100644 src/ImageProcessorCore/Filters/Hue.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs create mode 100644 tests/ImageProcessorCore.Tests/Processors/Filters/BrightnessTest.cs create mode 100644 tests/ImageProcessorCore.Tests/Processors/Filters/HueTest.cs diff --git a/src/ImageProcessorCore/Filters/Brightness.cs b/src/ImageProcessorCore/Filters/Brightness.cs new file mode 100644 index 000000000..fb29ff0d9 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Brightness.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the brightness component of the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The new brightness of the image. Must be between -100 and 100. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Brightness(this Image source, int amount, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return Brightness(source, amount, source.Bounds, progressHandler); + } + + /// + /// Alters the brightness component of the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The new brightness of the image. Must be between -100 and 100. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Brightness(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + BrightnessProcessor processor = new BrightnessProcessor(amount); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Hue.cs b/src/ImageProcessorCore/Filters/Hue.cs new file mode 100644 index 000000000..a4de7c610 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Hue.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the hue component of the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The angle in degrees to adjust the image. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return Hue(source, degrees, source.Bounds, progressHandler); + } + + /// + /// Alters the hue component of the image. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image this method extends. + /// The angle in degrees to adjust the image. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Hue(this Image source, float degrees, Rectangle rectangle, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + HueProcessor processor = new HueProcessor(degrees); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs new file mode 100644 index 000000000..d253c4ebf --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs @@ -0,0 +1,77 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System.Numerics; + using System.Threading.Tasks; + + /// + /// An to change the brightness of an . + /// + /// The pixel format. + /// The packed format. long, float. + public class BrightnessProcessor : ImageProcessor + where T : IPackedVector + where TP : struct + { + /// + /// Initializes a new instance of the class. + /// + /// The new brightness of the image. Must be between -100 and 100. + /// + /// is less than -100 or is greater than 100. + /// + public BrightnessProcessor(int brightness) + { + Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness)); + this.Value = brightness; + } + + /// + /// Gets the brightness value. + /// + public int Value { get; } + + /// + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + { + float brightness = this.Value / 100f; + int sourceY = sourceRectangle.Y; + int sourceBottom = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + using (IPixelAccessor sourcePixels = source.Lock()) + using (IPixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + // TODO: Check this with other formats. + Vector4 vector = sourcePixels[x, y].ToVector4().Expand(); + Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z); + transformed += new Vector3(brightness); + vector = new Vector4(transformed.X, transformed.Y, transformed.Z, vector.W); + + T packed = default(T); + packed.PackVector(vector.Compress()); + + targetPixels[x, y] = packed; + } + + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Saturation.cs b/src/ImageProcessorCore/Filters/Saturation.cs index 3c39d4e5c..cdd2a1bc0 100644 --- a/src/ImageProcessorCore/Filters/Saturation.cs +++ b/src/ImageProcessorCore/Filters/Saturation.cs @@ -1,7 +1,7 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// ------------------------------------------------------------------------------------------------------------------- +// namespace ImageProcessorCore { diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/BrightnessTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/BrightnessTest.cs new file mode 100644 index 000000000..9c0d840df --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/BrightnessTest.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Tests +{ + using System.IO; + + using Xunit; + + public class BrightnessTest : FileTestBase + { + public static readonly TheoryData BrightnessValues + = new TheoryData + { + 50 , + -50 , + }; + + [Theory] + [MemberData("BrightnessValues")] + public void ImageShouldApplyBrightnessFilter(int value) + { + const string path = "TestOutput/Brightness"; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + foreach (string file in Files) + { + using (FileStream stream = File.OpenRead(file)) + { + string filename = Path.GetFileNameWithoutExtension(file) + "-" + value + Path.GetExtension(file); + + Image image = new Image(stream); + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Brightness(value) + .Save(output); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/HueTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/HueTest.cs new file mode 100644 index 000000000..7ece88159 --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/HueTest.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Tests +{ + using System.IO; + + using Xunit; + + public class HueTest : FileTestBase + { + public static readonly TheoryData HueValues + = new TheoryData + { + 180 , + -180 , + }; + + [Theory] + [MemberData("HueValues")] + public void ImageShouldApplyHueFilter(int value) + { + const string path = "TestOutput/Hue"; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + foreach (string file in Files) + { + using (FileStream stream = File.OpenRead(file)) + { + string filename = Path.GetFileNameWithoutExtension(file) + "-" + value + Path.GetExtension(file); + + Image image = new Image(stream); + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Hue(value) + .Save(output); + } + } + } + } + } +} \ No newline at end of file