diff --git a/Filters/ConvolveFilter.cs b/Filters/ConvolveFilter.cs index 13cab78..62a5fb0 100644 --- a/Filters/ConvolveFilter.cs +++ b/Filters/ConvolveFilter.cs @@ -396,4 +396,129 @@ public static bool NearColors(int rgb1, int rgb2, int tolerance) { return Math.Abs(r1 - r2) <= tolerance && Math.Abs(g1 - g2) <= tolerance && Math.Abs(b1 - b2) <= tolerance; } } + + public class ImageMath { + /** + * Clamp a value to an interval. + * @param a the lower clamp threshold + * @param b the upper clamp threshold + * @param x the input parameter + * @return the clamped value + */ + + public static float Clamp(float x, float a, float b) { + return (x < a) ? a : (x > b) ? b : x; + } + + /** + * Clamp a value to an interval. + * @param a the lower clamp threshold + * @param b the upper clamp threshold + * @param x the input parameter + * @return the clamped value + */ + + public static int Clamp(int x, int a, int b) { + return (x < a) ? a : (x > b) ? b : x; + } + + + + // Catmull-Rom splines + private static float m00 = -0.5f; + private static float m01 = 1.5f; + private static float m02 = -1.5f; + private static float m03 = 0.5f; + private static float m10 = 1.0f; + private static float m11 = -2.5f; + private static float m12 = 2.0f; + private static float m13 = -0.5f; + private static float m20 = -0.5f; + private static float m21 = 0.0f; + private static float m22 = 0.5f; + private static float m23 = 0.0f; + private static float m30 = 0.0f; + private static float m31 = 1.0f; + private static float m32 = 0.0f; + private static float m33 = 0.0f; + + /** + * Compute a Catmull-Rom spline. + * @param x the input parameter + * @param numKnots the number of knots in the spline + * @param knots the array of knots + * @return the spline value + */ + + public static float spline(float x, int numKnots, float[] knots) { + int span; + int numSpans = numKnots - 3; + float k0, k1, k2, k3; + float c0, c1, c2, c3; + + if (numSpans < 1) + throw new ArgumentException("Too few knots in spline"); + + x = Clamp(x, 0, 1)*numSpans; + span = (int)x; + if (span > numKnots - 4) + span = numKnots - 4; + x -= span; + + k0 = knots[span]; + k1 = knots[span + 1]; + k2 = knots[span + 2]; + k3 = knots[span + 3]; + + c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3; + c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3; + c1 = m20*k0 + m21*k1 + m22*k2 + m23*k3; + c0 = m30*k0 + m31*k1 + m32*k2 + m33*k3; + + return ((c3*x + c2)*x + c1)*x + c0; + } + + /** + * Compute a Catmull-Rom spline, but with variable knot spacing. + * @param x the input parameter + * @param numKnots the number of knots in the spline + * @param xknots the array of knot x values + * @param yknots the array of knot y values + * @return the spline value + */ + + public static float spline(float x, int numKnots, int[] xknots, int[] yknots) { + int span; + int numSpans = numKnots - 3; + float k0, k1, k2, k3; + float c0, c1, c2, c3; + + if (numSpans < 1) + throw new ArgumentException("Too few knots in spline"); + + for (span = 0; span < numSpans; span++) + if (xknots[span + 1] > x) + break; + if (span > numKnots - 3) + span = numKnots - 3; + float t = (float)(x - xknots[span])/(xknots[span + 1] - xknots[span]); + span--; + if (span < 0) { + span = 0; + t = 0; + } + + k0 = yknots[span]; + k1 = yknots[span + 1]; + k2 = yknots[span + 2]; + k3 = yknots[span + 3]; + + c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3; + c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3; + c1 = m20*k0 + m21*k1 + m22*k2 + m23*k3; + c0 = m30*k0 + m31*k1 + m32*k2 + m33*k3; + + return ((c3*t + c2)*t + c1)*t + c0; + } + } } diff --git a/Filters/Curve.cs b/Filters/Curve.cs new file mode 100644 index 0000000..e46d24a --- /dev/null +++ b/Filters/Curve.cs @@ -0,0 +1,127 @@ +#region License and copyright notice +/* + * Ported to .NET for use in Kaliko.ImageLibrary by Fredrik Schultz 2015 + * + * Original License: + * Copyright 2006 Jerry Huxtable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#endregion + +namespace Kaliko.ImageLibrary.Filters { + using System; + + public class Curve { + public float[] X; + public float[] Y; + + public Curve() { + X = new float[] { 0, 1 }; + Y = new float[] { 0, 1 }; + } + + public Curve(Curve curve) { + X = (float[])curve.X.Clone(); + Y = (float[])curve.Y.Clone(); + } + + public int AddKnot(float kx, float ky) { + var pos = -1; + var numKnots = X.Length; + var nx = new float[numKnots + 1]; + var ny = new float[numKnots + 1]; + var j = 0; + for (var i = 0; i < numKnots; i++) { + if (pos == -1 && X[i] > kx) { + pos = j; + nx[j] = kx; + ny[j] = ky; + j++; + } + nx[j] = X[i]; + ny[j] = Y[i]; + j++; + } + if (pos == -1) { + pos = j; + nx[j] = kx; + ny[j] = ky; + } + X = nx; + Y = ny; + return pos; + } + + public void RemoveKnot(int n) { + var numKnots = X.Length; + if (numKnots <= 2) { + return; + } + var nx = new float[numKnots - 1]; + var ny = new float[numKnots - 1]; + var j = 0; + for (var i = 0; i < numKnots - 1; i++) { + if (i == n) { + j++; + } + nx[i] = X[j]; + ny[i] = Y[j]; + j++; + } + X = nx; + Y = ny; + } + + private void SortKnots() { + var numKnots = X.Length; + for (var i = 1; i < numKnots - 1; i++) { + for (var j = 1; j < i; j++) { + if (!(X[i] < X[j])) { + continue; + } + + var t = X[i]; + X[i] = X[j]; + X[j] = t; + t = Y[i]; + Y[i] = Y[j]; + Y[j] = t; + } + } + } + + public int[] MakeTable() { + var numKnots = X.Length; + var nx = new float[numKnots + 2]; + var ny = new float[numKnots + 2]; + Array.Copy(X, 0, nx, 1, numKnots); + Array.Copy(Y, 0, ny, 1, numKnots); + nx[0] = nx[1]; + ny[0] = ny[1]; + nx[numKnots + 1] = nx[numKnots]; + ny[numKnots + 1] = ny[numKnots]; + + var table = new int[256]; + for (var i = 0; i < 1024; i++) { + var f = i / 1024.0f; + var x = (int)(255 * ImageMath.spline(f, nx.Length, nx) + 0.5f); + var y = (int)(255 * ImageMath.spline(f, nx.Length, ny) + 0.5f); + x = ImageMath.Clamp(x, 0, 255); + y = ImageMath.Clamp(y, 0, 255); + table[x] = y; + } + return table; + } + } +} \ No newline at end of file diff --git a/Filters/CurvesFilter.cs b/Filters/CurvesFilter.cs new file mode 100644 index 0000000..df94abb --- /dev/null +++ b/Filters/CurvesFilter.cs @@ -0,0 +1,69 @@ +#region License and copyright notice +/* + * Ported to .NET for use in Kaliko.ImageLibrary by Fredrik Schultz 2015 + * + * Original License: + * Copyright 2006 Jerry Huxtable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#endregion + +namespace Kaliko.ImageLibrary.Filters { + using System; + + public class CurvesFilter : TransferFilter, IFilter { + + private Curve[] _curves; + + public CurvesFilter() { + _curves = new Curve[3]; + _curves[0] = new Curve(); + _curves[1] = new Curve(); + _curves[2] = new Curve(); + } + + protected override void Initialize() { + Initialized = true; + if (_curves.Length == 1) { + rTable = gTable = bTable = _curves[0].MakeTable(); + } + else { + rTable = _curves[0].MakeTable(); + gTable = _curves[1].MakeTable(); + bTable = _curves[2].MakeTable(); + } + } + + public void SetCurve(Curve curve) { + _curves = new[] { curve }; + Initialized = false; + } + + public void SetCurves(Curve[] curves) { + if (curves == null || (curves.Length != 1 && curves.Length != 3)) { + throw new ArgumentException("Curves must be length 1 or 3"); + } + _curves = curves; + Initialized = false; + } + + public Curve[] GetCurves() { + return _curves; + } + + public void Run(KalikoImage image) { + + } + } +} diff --git a/Filters/PointFilter.cs b/Filters/PointFilter.cs new file mode 100644 index 0000000..fa9634f --- /dev/null +++ b/Filters/PointFilter.cs @@ -0,0 +1,37 @@ +namespace Kaliko.ImageLibrary.Filters { + public abstract class PointFilter { + + protected bool CanFilterIndexColorModel = false; + + public void ApplyFilter(KalikoImage image) { + var pixels = image.IntArray; + + var width = image.Width; + var height = image.Height; + + for (var y = 0; y < height; y++) { + // We try to avoid calling getRGB on images as it causes them to become unmanaged, causing horrible performance problems. + //if (type == BufferedImage.TYPE_INT_ARGB) { + // srcRaster.getDataElements(0, y, width, 1, inPixels); + // for (int x = 0; x < width; x++) + // inPixels[x] = filterRGB(x, y, inPixels[x]); + // dstRaster.setDataElements(0, y, width, 1, inPixels); + //} + //else { + // src.getRGB(0, y, width, 1, inPixels, 0, width); + for (var x = 0; x < width; x++) { + pixels[x] = (int)FilterRgb(x, y, (uint)pixels[x]); + } + // dst.setRGB(0, y, width, 1, inPixels, 0, width); + //} + } + + image.IntArray = pixels; + } + + public void SetDimensions(int width, int height) { + } + + public abstract uint FilterRgb(int x, int y, uint rgb); + } +} \ No newline at end of file diff --git a/Filters/TransferFilter.cs b/Filters/TransferFilter.cs new file mode 100644 index 0000000..e521365 --- /dev/null +++ b/Filters/TransferFilter.cs @@ -0,0 +1,58 @@ +namespace Kaliko.ImageLibrary.Filters { + public abstract class TransferFilter : PointFilter { + + protected int[] rTable, gTable, bTable; + protected bool Initialized = false; + + protected TransferFilter() { + CanFilterIndexColorModel = true; + } + + public override uint FilterRgb(int x, int y, uint rgb) { + uint a = rgb & 0xff000000; + uint r = (rgb >> 16) & 0xff; + uint g = (rgb >> 8) & 0xff; + uint b = rgb & 0xff; + r = (uint)rTable[r]; + g = (uint)gTable[g]; + b = (uint)bTable[b]; + return a | (r << 16) | (g << 8) | b; + } + + //public BufferedImage filter(BufferedImage src, BufferedImage dst) { + // if (!initialized) + // initialize(); + // return super.filter(src, dst); + //} + + protected virtual void Initialize() { + Initialized = true; + rTable = gTable = bTable = MakeTable(); + } + + protected int[] MakeTable() { + var table = new int[256]; + for (var i = 0; i < 256; i++) { + table[i] = PixelUtils.Clamp((int)(255*TransferFunction(i/255.0f))); + } + return table; + } + + protected float TransferFunction(float v) { + return 0; + } + + public uint[] GetLUT() { + if (!Initialized) { + Initialize(); + } + + var lut = new uint[256]; + for (uint i = 0; i < 256; i++) { + lut[i] = FilterRgb(0, 0, (i << 24) | (i << 16) | (i << 8) | i); + } + return lut; + } + + } +} \ No newline at end of file diff --git a/ImageLibrary.csproj b/ImageLibrary.csproj index 5cc0346..d340c45 100644 --- a/ImageLibrary.csproj +++ b/ImageLibrary.csproj @@ -40,6 +40,61 @@ false bin\Release\Kaliko.ImageLibrary.XML + + pdbonly + true + TRACE + prompt + 4 + false + ..\.build\net20\Kaliko.ImageLibrary.XML + ..\.build\net20\ + v2.0 + + + pdbonly + true + TRACE + prompt + 4 + false + ..\.build\net30\Kaliko.ImageLibrary.XML + ..\.build\net30\ + v3.0 + + + pdbonly + true + TRACE + prompt + 4 + false + ..\.build\net35\Kaliko.ImageLibrary.XML + ..\.build\net35\ + v3.5 + + + pdbonly + true + TRACE + prompt + 4 + false + ..\.build\net40\Kaliko.ImageLibrary.XML + ..\.build\net40\ + v4.0 + + + pdbonly + true + TRACE + prompt + 4 + false + ..\.build\net45\Kaliko.ImageLibrary.XML + ..\.build\net45\ + v4.5 + @@ -59,15 +114,20 @@ + + + + + diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 4f25029..93477b6 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -21,15 +20,3 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("58b4262b-c04f-4c08-a1c9-948ddd0cbf84")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("2.0.4.0")] -[assembly: AssemblyFileVersion("2.0.4.0")] diff --git a/Properties/SolutionInfo.cs b/Properties/SolutionInfo.cs new file mode 100644 index 0000000..be03d13 --- /dev/null +++ b/Properties/SolutionInfo.cs @@ -0,0 +1,10 @@ +// +using System.Reflection; + +[assembly: AssemblyVersionAttribute("2.0.5")] +[assembly: AssemblyFileVersionAttribute("2.0.5")] +namespace System { + internal static class AssemblyVersionInformation { + internal const string Version = "2.0.5"; + } +} diff --git a/ReleaseNotes.md b/ReleaseNotes.md new file mode 100644 index 0000000..88916cb --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,29 @@ +### New in 2.0.5 +* Added curves filter + +### New in 2.0.4-beta +* Added new TextField class for better text support +* Fixed scaling bug and updated test program + +### New in 2.0.0 +* Replaced Gaussian blur filter with better implementation (affects unsharpen masks) +* Added chroma key filter +* Rewritten API for Scaling +* Added color space handling + +### New in 1.2.4 +* Updated to Visual Studio 2010. +* Code clean-up. +* Unwanted-border-artifact-problem fixed (thanks Richard!) +* IDisponable has been implemented. + +### New in 1.2.3 +* Minor changes. +* First API documentation uploaded. Still missing a whole lot, but it's a start :) + +### New in 1.2.2 +* Minor changes + +### New in 1.2.1 +* Bug in thumbnail function fixed. +* Code cleaned up.