diff --git a/TagTool/Bitmaps/Utils/BitmapDownsampler.cs b/TagTool/Bitmaps/Utils/BitmapDownsampler.cs index fc88f737a..63393c94b 100644 --- a/TagTool/Bitmaps/Utils/BitmapDownsampler.cs +++ b/TagTool/Bitmaps/Utils/BitmapDownsampler.cs @@ -54,6 +54,49 @@ public static byte[] Downsample4x4BlockRgba(BitmapSampler sampler) return result; } + public static byte[] Downsample4x4BlockRgba(BitmapSampler sampler, int width, int height) + { + sampler.SetFilteringMode(BitmapSampler.FilteringMode.Bilinear); // must use bilinear + + byte[] result = new byte[width * height * 4]; + + float pixelWidth = 1.0f / width; + float pixelHeight = 1.0f / height; + float halfPxWidth = pixelWidth * 0.5f; + float halfPxHeight = pixelHeight * 0.5f; + + int rowBytes = width * 4; + int rowOffset = 0; + + for (int i = 0; i < height; i++) + { + float yCoord = (pixelHeight * i) + halfPxHeight; + + for (int j = 0; j < width; j++) + { + float xCoord = (pixelWidth * j) + halfPxWidth; + + // 4x4 block + var sample0 = sampler.Sample2dOffsetF(xCoord, yCoord, -1, -1); + var sample1 = sampler.Sample2dOffsetF(xCoord, yCoord, 1, -1); + var sample2 = sampler.Sample2dOffsetF(xCoord, yCoord, -1, 1); + var sample3 = sampler.Sample2dOffsetF(xCoord, yCoord, 1, 1); + RealVector4d sample = (sample0 + sample1 + sample2 + sample3) / 4.0f; + sample *= 255; // to byte + + int streamOffset = rowOffset + j * 4; + result[streamOffset] = (byte)sample.I; + result[streamOffset + 1] = (byte)sample.J; + result[streamOffset + 2] = (byte)sample.K; + result[streamOffset + 3] = (byte)sample.W; + } + + rowOffset += rowBytes; + } + + return result; + } + // try not to use this public static byte[] DownsampleBilinearRgba(BitmapSampler sampler) { diff --git a/TagTool/Bitmaps/Utils/BitmapSampler.cs b/TagTool/Bitmaps/Utils/BitmapSampler.cs index 7377cc524..7524f9c70 100644 --- a/TagTool/Bitmaps/Utils/BitmapSampler.cs +++ b/TagTool/Bitmaps/Utils/BitmapSampler.cs @@ -6,6 +6,7 @@ namespace TagTool.Bitmaps.Utils public class BitmapSampler { private byte[] Rgba; + private uint StreamOffset; private int Width; private int Height; private FilteringMode FilterMode; @@ -20,6 +21,18 @@ public BitmapSampler(byte[] rgba, int width, int height, FilteringMode filter = FilterMode = filter; MipmapCount = mipCount; CurrentLevel = 0; + StreamOffset = 0; + } + + public BitmapSampler(byte[] rgba, uint streamOffset, int width, int height, FilteringMode filter = FilteringMode.Point, int mipCount = 0) + { + Rgba = rgba; + Width = width; + Height = height; + FilterMode = filter; + MipmapCount = mipCount; + CurrentLevel = 0; + StreamOffset = streamOffset; } public void SetData(byte[] d) => Rgba = d; @@ -76,7 +89,7 @@ public RealVector4d Sample2dOffsetF(float x, float y, int offsetX, int offsetY) private float PixelWidth() => 1.0f / Width; private float PixelHeight() => 1.0f / Height; private int UvToIndex(float x, float y) => 4 * ((int)(y * Height) * Width + (int)(x * Width)); - private RGBAColor GetColour(int index) => new RGBAColor(Rgba[index], Rgba[index + 1], Rgba[index + 2], Rgba[index + 3]); + private RGBAColor GetColour(int index) => new RGBAColor(Rgba[StreamOffset + index], Rgba[StreamOffset + index + 1], Rgba[StreamOffset + index + 2], Rgba[StreamOffset + index + 3]); private RGBAColor Sample2dPoint(int index) => GetColour(index); private RGBAColor Sample2dPoint(float x, float y) => Sample2dPoint(UvToIndex(x, y)); private RGBAColor Sample2dPointBounded(float x, float y, bool hasX, bool hasY) => GetColourBounded(UvToIndex(x, y), x, y, hasX, hasY); diff --git a/TagTool/Bitmaps/Utils/SquishLib.cs b/TagTool/Bitmaps/Utils/SquishLib.cs index 519423b22..e3e351556 100644 --- a/TagTool/Bitmaps/Utils/SquishLib.cs +++ b/TagTool/Bitmaps/Utils/SquishLib.cs @@ -37,7 +37,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Threading.Tasks; using TagTool.Common; using static TagTool.Bitmaps.Utils.SquishLib; -using static TagTool.Tags.Definitions.Scenario.AiObjective; +using System.Numerics; +using TagTool.Extensions; namespace TagTool.Bitmaps.Utils { @@ -105,12 +106,13 @@ public static int FloatToInt(float a, int limit) return i; } - public static int FloatTo565(RealVector3d colour) + // 3d vector + public static int FloatTo565(Vector colour) { // get the components in the correct range - int r = FloatToInt(31.0f * colour.I, 31); - int g = FloatToInt(63.0f * colour.J, 63); - int b = FloatToInt(31.0f * colour.K, 31); + int r = FloatToInt(31.0f * colour[0], 31); + int g = FloatToInt(63.0f * colour[1], 63); + int b = FloatToInt(31.0f * colour[2], 31); // pack into a single value return (r << 11) | (g << 5) | b; @@ -135,7 +137,8 @@ public static void WriteColourBlock(int a, int b, byte[] indices, byte[] block) } } - public static void WriteColourBlock3(RealVector3d start, RealVector3d end, byte[] indices, byte[] block) + //3d vectors + public static void WriteColourBlock3(Vector start, Vector end, byte[] indices, byte[] block) { // get the packed values int a = FloatTo565(start); @@ -171,7 +174,8 @@ public static void WriteColourBlock3(RealVector3d start, RealVector3d end, byte[ WriteColourBlock(a, b, remapped, block); } - public static void WriteColourBlock4(RealVector3d start, RealVector3d end, byte[] indices, byte[] block) + // 3d vectors + public static void WriteColourBlock4(Vector start, Vector end, byte[] indices, byte[] block) { // get the packed values int a = FloatTo565(start); @@ -209,7 +213,8 @@ public static void WriteColourBlock4(RealVector3d start, RealVector3d end, byte[ public static class SquishMath { - public static RealVector3d GetMultiplicity1Evector(Sym3x3 matrix, float evalue) + // 3d + public static Vector GetMultiplicity1Evector(Sym3x3 matrix, float evalue) { // compute M Sym3x3 m = new Sym3x3(); @@ -246,16 +251,17 @@ public static RealVector3d GetMultiplicity1Evector(Sym3x3 matrix, float evalue) switch (mi) { case 0: - return new RealVector3d(u[0], u[1], u[2]); + return VectorExtensions.InitializeVector(new float[] { u[0], u[1], u[2] }); case 1: case 3: - return new RealVector3d(u[1], u[3], u[4]); + return VectorExtensions.InitializeVector(new float[] { u[1], u[3], u[4] }); default: - return new RealVector3d(u[2], u[4], u[5]); + return VectorExtensions.InitializeVector(new float[] { u[2], u[4], u[5] }); } } - public static RealVector3d GetMultiplicity2Evector(Sym3x3 matrix, float evalue) + // 3d + public static Vector GetMultiplicity2Evector(Sym3x3 matrix, float evalue) { // compute M Sym3x3 m = new Sym3x3(); @@ -284,18 +290,19 @@ public static RealVector3d GetMultiplicity2Evector(Sym3x3 matrix, float evalue) { case 0: case 1: - return new RealVector3d(-m[1], m[0], 0.0f); + return VectorExtensions.InitializeVector(new float[] { -m[1], m[0], 0.0f }); case 2: - return new RealVector3d(m[2], 0.0f, -m[0]); + return VectorExtensions.InitializeVector(new float[] { m[2], 0.0f, -m[0] }); case 3: case 4: - return new RealVector3d(0.0f, -m[4], m[3]); + return VectorExtensions.InitializeVector(new float[] { 0.0f, -m[4], m[3] }); default: - return new RealVector3d(0.0f, -m[5], m[4]); + return VectorExtensions.InitializeVector(new float[] { 0.0f, -m[5], m[4] }); } } public static float LengthSquared(RealVector3d v) => RealVector3d.DotProduct(v, v); + public static float LengthSquared3d(Vector v) => VectorExtensions.Dot3d(v, v); public static RealVector3d Min(RealVector3d a, RealVector3d b) => @@ -308,6 +315,45 @@ public static RealVector3d Max(RealVector3d a, RealVector3d b) => public static RealVector4d Max(RealVector4d a, RealVector4d b) => new RealVector4d(Math.Max(a.I, b.I), Math.Max(a.J, b.J), Math.Max(a.K, b.K), Math.Max(a.W, b.W)); + public static Vector Max4d(Vector a, Vector b) + { + return VectorExtensions.InitializeVector(new float[] + { + Math.Max(a[0], b[0]), + Math.Max(a[1], b[1]), + Math.Max(a[2], b[2]), + Math.Max(a[3], b[3]) + }); + } + public static Vector Min4d(Vector a, Vector b) + { + return VectorExtensions.InitializeVector(new float[] + { + Math.Min(a[0], b[0]), + Math.Min(a[1], b[1]), + Math.Min(a[2], b[2]), + Math.Min(a[3], b[3]) + }); + } + public static Vector Max3d(Vector a, Vector b) + { + return VectorExtensions.InitializeVector(new float[] + { + Math.Max(a[0], b[0]), + Math.Max(a[1], b[1]), + Math.Max(a[2], b[2]) + }); + } + public static Vector Min3d(Vector a, Vector b) + { + return VectorExtensions.InitializeVector(new float[] + { + Math.Min(a[0], b[0]), + Math.Min(a[1], b[1]), + Math.Min(a[2], b[2]) + }); + } + public static RealVector3d Truncate(RealVector3d input) => new RealVector3d( input.I > 0.0f ? (float)Math.Floor(input.I) : (float)Math.Ceiling(input.I), input.J > 0.0f ? (float)Math.Floor(input.J) : (float)Math.Ceiling(input.J), @@ -319,6 +365,19 @@ public static RealVector4d Max(RealVector4d a, RealVector4d b) => input.K > 0.0f ? (float)Math.Floor(input.K) : (float)Math.Ceiling(input.K), input.W > 0.0f ? (float)Math.Floor(input.W) : (float)Math.Ceiling(input.W) ); + public static Vector Truncate4d(Vector input) => VectorExtensions.InitializeVector(new float[] + { + input[0] > 0.0f ? (float)Math.Floor(input[0]) : (float)Math.Ceiling(input[0]), + input[1] > 0.0f ? (float)Math.Floor(input[1]) : (float)Math.Ceiling(input[1]), + input[2] > 0.0f ? (float)Math.Floor(input[2]) : (float)Math.Ceiling(input[2]), + input[3] > 0.0f ? (float)Math.Floor(input[3]) : (float)Math.Ceiling(input[3]) + }); + public static Vector Truncate3d(Vector input) => VectorExtensions.InitializeVector(new float[] + { + input[0] > 0.0f ? (float)Math.Floor(input[0]) : (float)Math.Ceiling(input[0]), + input[1] > 0.0f ? (float)Math.Floor(input[1]) : (float)Math.Ceiling(input[1]), + input[2] > 0.0f ? (float)Math.Floor(input[2]) : (float)Math.Ceiling(input[2]) + }); public static bool CompareAnyLessThan(RealVector4d a, RealVector4d b) => @@ -544,7 +603,8 @@ public float this[int index] float[] m_x; //[6] - public RealVector3d ComputePrincipleComponent() + // 3d + public Vector ComputePrincipleComponent() { float FLT_EPSILON = 1.192092896e-07F; var matrix = this; @@ -570,7 +630,7 @@ public RealVector3d ComputePrincipleComponent() if (FLT_EPSILON < Q) { // only one root, which implies we have a multiple of the identity - return new RealVector3d(1.0f, 1.0f, 1.0f); + return VectorExtensions.InitializeVector(new float[] { 1.0f, 1.0f, 1.0f }); } else if (Q < -FLT_EPSILON) { @@ -615,31 +675,32 @@ public RealVector3d ComputePrincipleComponent() } } - public static Sym3x3 ComputeWeightedCovariance(int n, RealVector3d[] points, float[] weights) + // 3d + public static Sym3x3 ComputeWeightedCovariance(int n, Vector[] points, float[] weights) { // compute the centroid float total = 0.0f; - RealVector3d centroid = new RealVector3d( 0.0f, 0.0f, 0.0f ); + Vector centroid = VectorExtensions.InitializeVector(); for( int i = 0; i < n; ++i ) { total += weights[i]; centroid += weights[i]*points[i]; } - centroid /= total; + centroid *= (1.0f / total); // accumulate the covariance matrix Sym3x3 covariance = new Sym3x3(0.0f); for( int i = 0; i < n; ++i ) { - RealVector3d a = points[i] - centroid; - RealVector3d b = weights[i]*a; + Vector a = points[i] - centroid; + Vector b = weights[i]*a; - covariance[0] += a.I * b.I; - covariance[1] += a.I * b.J; - covariance[2] += a.I * b.K; - covariance[3] += a.J * b.J; - covariance[4] += a.J * b.K; - covariance[5] += a.K * b.K; + covariance[0] += a[0] * b[0]; + covariance[1] += a[0] * b[1]; + covariance[2] += a[0] * b[2]; + covariance[3] += a[1] * b[1]; + covariance[4] += a[1] * b[2]; + covariance[5] += a[2] * b[2]; } // return it @@ -670,7 +731,7 @@ public ColourSet(byte[] rgba, uint mask, uint flags) Transparent = false; // init arrays - Points = new RealVector3d[16]; + Points = new Vector[16]; Weights = new float[16]; Remap = new int[16]; @@ -712,7 +773,7 @@ public ColourSet(byte[] rgba, uint mask, uint flags) float w = (float)(rgba[4 * i + 3] + 1) / 256.0f; // add the point - Points[Count] = new RealVector3d(x, y, z); + Points[Count] = VectorExtensions.InitializeVector(new float[] { x, y, z }); Weights[Count] = (weightByAlpha ? w : 1.0f); Remap[i] = Count; @@ -762,12 +823,12 @@ public void RemapIndices(byte[] source, byte[] target) } public int GetCount() => Count; - public RealVector3d[] GetPoints() => Points; + public Vector[] GetPoints() => Points; //3d public float[] GetWeights() => Weights; public bool IsTransparent() => Transparent; private int Count; - private RealVector3d[] Points; // [16] + private Vector[] Points; // 3d [16] private float[] Weights; // [16] private int[] Remap; // [16] private bool Transparent; @@ -792,13 +853,13 @@ public static int FloatToInt(float a, int limit) public SingleColourFit(ColourSet colours, uint flags) : base(colours, flags) { - // grab the single colour - RealVector3d[] values = m_colours.GetPoints(); + // grab the single colour + Vector[] values = m_colours.GetPoints(); List colour_list = new List { - (byte)FloatToInt(255.0f * values[0].I, 255), - (byte)FloatToInt(255.0f * values[0].J, 255), - (byte)FloatToInt(255.0f * values[0].K, 255) + (byte)FloatToInt(255.0f * values[0][0], 255), + (byte)FloatToInt(255.0f * values[0][1], 255), + (byte)FloatToInt(255.0f * values[0][2], 255) }; m_colour = colour_list.ToArray(); @@ -888,16 +949,16 @@ public void ComputeEndPoints(SingleColourLookup[][] lookups) // keep it if the error is lower if (error < m_error) { - m_start = new RealVector3d( + m_start = VectorExtensions.InitializeVector( new float[] { (float)sources[0].start / 31.0f, (float)sources[1].start / 63.0f, (float)sources[2].start / 31.0f - ); - m_end = new RealVector3d( + }); + m_end = VectorExtensions.InitializeVector( new float[] { (float)sources[0].end / 31.0f, (float)sources[1].end / 63.0f, (float)sources[2].end / 31.0f - ); + }); m_index = (byte)(2 * index); m_error = error; } @@ -905,8 +966,8 @@ public void ComputeEndPoints(SingleColourLookup[][] lookups) } private byte[] m_colour; // [3] - private RealVector3d m_start; - private RealVector3d m_end; + private Vector m_start; // 3d + private Vector m_end; //3d private byte m_index; private int m_error; private int m_besterror; @@ -919,37 +980,37 @@ public RangeFit(ColourSet colours, uint flags) : base(colours, flags) // initialise the metric bool perceptual = ((m_flags & (uint)SquishFlags.kColourMetricPerceptual) != 0); if (perceptual) - m_metric = new RealVector3d(0.2126f, 0.7152f, 0.0722f); + m_metric = VectorExtensions.InitializeVector(new float[] { 0.2126f, 0.7152f, 0.0722f }); else - m_metric = new RealVector3d(1.0f, 1.0f, 1.0f); + m_metric = VectorExtensions.InitializeVector(new float[] { 1.0f, 1.0f, 1.0f }); // initialise the best error m_besterror = float.MaxValue; // cache some values int count = m_colours.GetCount(); - RealVector3d[] values = m_colours.GetPoints(); + Vector[] values = m_colours.GetPoints(); float[] weights = m_colours.GetWeights(); // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, weights); // compute the principle component - RealVector3d principle = covariance.ComputePrincipleComponent(); + Vector principle = covariance.ComputePrincipleComponent(); // get the min and max range as the codebook endpoints - RealVector3d start = new RealVector3d(0.0f, 0.0f, 0.0f); - RealVector3d end = new RealVector3d(0.0f, 0.0f, 0.0f); + Vector start = VectorExtensions.InitializeVector(); + Vector end = VectorExtensions.InitializeVector(); if (count > 0) { float min, max; // compute the range start = end = values[0]; - min = max = RealVector3d.DotProduct(values[0], principle); + min = max = VectorExtensions.Dot3d(values[0], principle); for (int i = 1; i < count; ++i) { - float val = RealVector3d.DotProduct(values[i], principle); + float val = VectorExtensions.Dot3d(values[i], principle); if (val < min) { start = values[i]; @@ -964,27 +1025,27 @@ public RangeFit(ColourSet colours, uint flags) : base(colours, flags) } // clamp the output to [0, 1] - RealVector3d one = new RealVector3d(1.0f, 1.0f, 1.0f); - RealVector3d zero = new RealVector3d(0.0f, 0.0f, 0.0f); - start = SquishMath.Min(one, SquishMath.Max(zero, start)); - end = SquishMath.Min(one, SquishMath.Max(zero, end)); + Vector one = VectorExtensions.InitializeVector(new float[] { 1.0f, 1.0f, 1.0f }); + Vector zero = VectorExtensions.InitializeVector(); + start = SquishMath.Min3d(one, SquishMath.Max3d(zero, start)); + end = SquishMath.Min3d(one, SquishMath.Max3d(zero, end)); // clamp to the grid and save - RealVector3d grid = new RealVector3d(31.0f, 63.0f, 31.0f); - RealVector3d gridrcp = new RealVector3d(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); - RealVector3d half = new RealVector3d(0.5f, 0.5f, 0.5f); - m_start = SquishMath.Truncate(grid * start + half) * gridrcp; - m_end = SquishMath.Truncate(grid * end + half) * gridrcp; + Vector grid = VectorExtensions.InitializeVector(new float[] { 31.0f, 63.0f, 31.0f }); + Vector gridrcp = VectorExtensions.InitializeVector(new float[] { 1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f }); + Vector half = VectorExtensions.InitializeVector(new float[] { 0.5f, 0.5f, 0.5f }); + m_start = SquishMath.Truncate3d(grid * start + half) * gridrcp; + m_end = SquishMath.Truncate3d(grid * end + half) * gridrcp; } protected override void Compress3(byte[] block) { // cache some values int count = m_colours.GetCount(); - RealVector3d[] values = m_colours.GetPoints(); + Vector[] values = m_colours.GetPoints(); // create a codebook - RealVector3d[] codes = new RealVector3d[] + Vector[] codes = new Vector[] { m_start, m_end, @@ -1001,7 +1062,7 @@ protected override void Compress3(byte[] block) int idx = 0; for (int j = 0; j < 3; ++j) { - float d = SquishMath.LengthSquared(m_metric * (values[i] - codes[j])); + float d = SquishMath.LengthSquared3d(m_metric * (values[i] - codes[j])); if (d < dist) { dist = d; @@ -1035,10 +1096,10 @@ protected override void Compress4(byte[] block) { // cache some values int count = m_colours.GetCount(); - RealVector3d[] values = m_colours.GetPoints(); + Vector[] values = m_colours.GetPoints(); - // create a codebook - RealVector3d[] codes = new RealVector3d[] { + // create a codebook + Vector[] codes = new Vector[] { m_start, m_end, (2.0f / 3.0f) * m_start + (1.0f / 3.0f) * m_end, @@ -1055,7 +1116,7 @@ protected override void Compress4(byte[] block) int idx = 0; for (int j = 0; j < 4; ++j) { - float d = SquishMath.LengthSquared(m_metric * (values[i] - codes[j])); + float d = SquishMath.LengthSquared3d(m_metric * (values[i] - codes[j])); if (d < dist) { dist = d; @@ -1085,9 +1146,10 @@ protected override void Compress4(byte[] block) } } - private RealVector3d m_metric; - private RealVector3d m_start; - private RealVector3d m_end; + // all 3d + private Vector m_metric; + private Vector m_start; + private Vector m_end; private float m_besterror; } @@ -1101,27 +1163,27 @@ public ClusterFit(ColourSet colours, uint flags) : base(colours, flags) orderList.Add(new byte[16]); m_order = orderList.ToArray(); - List pointWeightsList = new List(); + List> pointWeightsList = new List>(); for (int i = 0; i < 16; i++) - pointWeightsList.Add(new RealVector4d()); + pointWeightsList.Add(VectorExtensions.InitializeVector()); m_points_weights = pointWeightsList.ToArray(); // set the iteration count m_iterationCount = (m_flags & (uint)SquishFlags.kColourIterativeClusterFit) != 0 ? kMaxIterations : 1; // initialise the best error - m_besterror = new RealVector4d(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue); // .. + m_besterror = float.MaxValue; // .. // initialise the metric bool perceptual = ((m_flags & (uint)SquishFlags.kColourMetricPerceptual) != 0); if (perceptual) - m_metric = new RealVector4d(0.2126f, 0.7152f, 0.0722f, 0.0f); + m_metric = VectorExtensions.InitializeVector(new float[] { 0.2126f, 0.7152f, 0.0722f, 0.0f }); else - m_metric = new RealVector4d(1.0f, 1.0f, 1.0f, 1.0f); // .. + m_metric = VectorExtensions.InitializeVector(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }); // .. // cache some values int count = m_colours.GetCount(); - RealVector3d[] values = m_colours.GetPoints(); + Vector[] values = m_colours.GetPoints(); // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, m_colours.GetWeights()); @@ -1130,18 +1192,19 @@ public ClusterFit(ColourSet colours, uint flags) : base(colours, flags) m_principle = covariance.ComputePrincipleComponent(); } - private bool ConstructOrdering(ref RealVector3d axis, int iteration) + // axis 3d + private bool ConstructOrdering(ref Vector axis, int iteration) { // cache some values int count = m_colours.GetCount(); - RealVector3d[] values = m_colours.GetPoints(); + Vector[] values = m_colours.GetPoints(); // build the list of dot products float[] dps = new float[16]; byte[] order = m_order[iteration]; for( int i = 0; i < count; ++i ) { - dps[i] = RealVector3d.DotProduct( values[i], axis ); + dps[i] = VectorExtensions.Dot3d( values[i], axis ); order[i] = (byte)i; } @@ -1176,17 +1239,17 @@ private bool ConstructOrdering(ref RealVector3d axis, int iteration) if( same ) return false; } - - // copy the ordering and weight all the points - RealVector3d[] unweighted = m_colours.GetPoints(); + + // copy the ordering and weight all the points + Vector[] unweighted = m_colours.GetPoints(); float[] weights = m_colours.GetWeights(); - m_xsum_wsum = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); + m_xsum_wsum = VectorExtensions.InitializeVector(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }); for( int i = 0; i < count; ++i ) { int j = order[i]; - RealVector4d p = new RealVector4d(unweighted[j].I, unweighted[j].J, unweighted[j].K, 1.0f); - RealVector4d w = new RealVector4d(weights[j], weights[j], weights[j], weights[j]); - RealVector4d x = p*w; + Vector p = VectorExtensions.InitializeVector(new float[] { unweighted[j][0], unweighted[j][1], unweighted[j][2], 1.0f }); + Vector w = VectorExtensions.InitializeVector(new float[] { weights[j], weights[j], weights[j], weights[j] }); + Vector x = p*w; m_points_weights[i] = x; m_xsum_wsum += x; } @@ -1197,21 +1260,21 @@ protected override void Compress3(byte[] block) { // declare variables int count = m_colours.GetCount(); - RealVector4d two = new RealVector4d(2.0f, 2.0f, 2.0f, 2.0f); - RealVector4d one = new RealVector4d(1.0f, 1.0f, 1.0f, 1.0f); - RealVector4d half_half2 = new RealVector4d( 0.5f, 0.5f, 0.5f, 0.25f ); - RealVector4d zero = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); - RealVector4d half = new RealVector4d(0.5f, 0.5f, 0.5f, 0.5f); - RealVector4d grid = new RealVector4d( 31.0f, 63.0f, 31.0f, 0.0f ); - RealVector4d gridrcp = new RealVector4d( 1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f, 0.0f ); + Vector two = VectorExtensions.InitializeVector(new float[] { 2.0f, 2.0f, 2.0f, 2.0f }); + Vector one = VectorExtensions.InitializeVector(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }); + Vector half_half2 = VectorExtensions.InitializeVector(new float[] { 0.5f, 0.5f, 0.5f, 0.25f }); + Vector zero = VectorExtensions.InitializeVector(); + Vector half = VectorExtensions.InitializeVector(new float[] { 0.5f, 0.5f, 0.5f, 0.5f }); + Vector grid = VectorExtensions.InitializeVector(new float[] { 31.0f, 63.0f, 31.0f, 0.0f }); + Vector gridrcp = VectorExtensions.InitializeVector(new float[] { 1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f, 0.0f }); // prepare an ordering using the principle axis ConstructOrdering(ref m_principle, 0); // check all possible clusters and iterate on the total order - RealVector4d beststart = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); - RealVector4d bestend = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); - RealVector4d besterror = m_besterror; + Vector beststart = VectorExtensions.InitializeVector(); + Vector bestend = VectorExtensions.InitializeVector(); + float besterror = m_besterror; byte[] bestindices = new byte[16]; int bestiteration = 0; int besti = 0, bestj = 0; @@ -1220,49 +1283,49 @@ protected override void Compress3(byte[] block) for (int iterationIndex = 0; ;) { // first cluster [0,i) is at the start - RealVector4d part0 = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); + Vector part0 = VectorExtensions.InitializeVector(); for (int i = 0; i < count; ++i) { // second cluster [i,j) is half along - RealVector4d part1 = (i == 0) ? m_points_weights[0] : new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); + Vector part1 = (i == 0) ? m_points_weights[0] : VectorExtensions.InitializeVector(); int jmin = (i == 0) ? 1 : i; for (int j = jmin; ;) { // last cluster [j,count) is at the end - RealVector4d part2 = m_xsum_wsum - part1 - part0; + Vector part2 = m_xsum_wsum - part1 - part0; // compute least squares terms directly - RealVector4d alphax_sum = part1 * half_half2 + part0; - RealVector4d alpha2_sum = alphax_sum.WWWW; + Vector alphax_sum = part1 * half_half2 + part0; + float alpha2_sum = alphax_sum[3]; - RealVector4d betax_sum = part1 * half_half2 + part2; - RealVector4d beta2_sum = betax_sum.WWWW; + Vector betax_sum = part1 * half_half2 + part2; + float beta2_sum = betax_sum[3]; - RealVector4d alphabeta_sum = (part1 * half_half2).WWWW; + float alphabeta_sum = (part1[3] * half_half2[3]); // compute the least-squares optimal points - RealVector4d factor = 1.0f / (-1.0f * (alphabeta_sum * alphabeta_sum - (alpha2_sum * beta2_sum))); - RealVector4d a = -1.0f * (betax_sum * alphabeta_sum - (alphax_sum * beta2_sum)) * factor; - RealVector4d b = -1.0f * (alphax_sum * alphabeta_sum - (betax_sum * alpha2_sum)) * factor; + float factor = 1.0f / (-1.0f * (alphabeta_sum * alphabeta_sum - (alpha2_sum * beta2_sum))); + Vector a = -1.0f * (betax_sum * alphabeta_sum - (alphax_sum * beta2_sum)) * factor; + Vector b = -1.0f * (alphax_sum * alphabeta_sum - (betax_sum * alpha2_sum)) * factor; // clamp to the grid - a = SquishMath.Min(one, SquishMath.Max(zero, a)); - b = SquishMath.Min(one, SquishMath.Max(zero, b)); - a = SquishMath.Truncate(grid * a + half) * gridrcp; - b = SquishMath.Truncate(grid * b + half) * gridrcp; + a = SquishMath.Min4d(one, SquishMath.Max4d(zero, a)); + b = SquishMath.Min4d(one, SquishMath.Max4d(zero, b)); + a = SquishMath.Truncate4d(grid * a + half) * gridrcp; + b = SquishMath.Truncate4d(grid * b + half) * gridrcp; // compute the error (we skip the constant xxsum) - RealVector4d e1 = (a * a) * alpha2_sum + (b * b * beta2_sum); - RealVector4d e2 = -1.0f * (a * alphax_sum - (a * b * alphabeta_sum)); - RealVector4d e3 = -1.0f * (b * betax_sum - e2); - RealVector4d e4 = two * e3 + e1; + Vector e1 = (a * a) * alpha2_sum + (b * b * beta2_sum); + Vector e2 = -1.0f * (a * alphax_sum - (a * b * alphabeta_sum)); + Vector e3 = -1.0f * (b * betax_sum - e2); + Vector e4 = two * e3 + e1; // apply the metric to the error term - RealVector4d e5 = e4 * m_metric; - RealVector4d error = e5.XXXX + e5.YYYY + e5.ZZZZ; + Vector e5 = e4 * m_metric; + float error = e5[0] + e5[1] + e5[2]; // keep the solution if it wins - if (SquishMath.CompareAnyLessThan(error, besterror)) + if (error < besterror) { beststart = a; bestend = b; @@ -1293,13 +1356,13 @@ protected override void Compress3(byte[] block) break; // stop if a new iteration is an ordering that has already been tried - RealVector3d axis = (bestend - beststart).XYZ; + Vector axis = VectorExtensions.Convert4dTo3d(bestend - beststart); if (!ConstructOrdering(ref axis, iterationIndex)) break; } // save the block if necessary - if (SquishMath.CompareAnyLessThan(besterror, m_besterror)) + if (besterror < m_besterror) { // remap the indices byte[] order = m_order[bestiteration]; @@ -1315,7 +1378,7 @@ protected override void Compress3(byte[] block) m_colours.RemapIndices(unordered, bestindices); // save the block - ColourBlock.WriteColourBlock3(beststart.XYZ, bestend.XYZ, bestindices, block); + ColourBlock.WriteColourBlock3(VectorExtensions.Convert4dTo3d(beststart), VectorExtensions.Convert4dTo3d(bestend), bestindices, block); // save the error m_besterror = besterror; @@ -1326,23 +1389,23 @@ protected override void Compress4(byte[] block) { // declare variables int count = m_colours.GetCount(); - RealVector4d two = new RealVector4d(2.0f, 2.0f, 2.0f, 2.0f); - RealVector4d one = new RealVector4d(1.0f, 1.0f, 1.0f, 1.0f); - RealVector4d onethird_onethird2 = new RealVector4d( 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 9.0f ); - RealVector4d twothirds_twothirds2 = new RealVector4d( 2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f, 4.0f / 9.0f ); - RealVector4d twonineths = new RealVector4d(2.0f / 9.0f, 2.0f / 9.0f, 2.0f / 9.0f, 2.0f / 9.0f); - RealVector4d zero = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); - RealVector4d half = new RealVector4d(0.5f, 0.5f, 0.5f, 0.5f); - RealVector4d grid = new RealVector4d( 31.0f, 63.0f, 31.0f, 0.0f ); - RealVector4d gridrcp = new RealVector4d( 1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f, 0.0f ); + Vector two = VectorExtensions.InitializeVector(new float[] { 2.0f, 2.0f, 2.0f, 2.0f }); + Vector one = VectorExtensions.InitializeVector(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }); + Vector onethird_onethird2 = VectorExtensions.InitializeVector(new float[] { 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 9.0f }); + Vector twothirds_twothirds2 = VectorExtensions.InitializeVector(new float[] { 2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f, 4.0f / 9.0f }); + Vector twonineths = VectorExtensions.InitializeVector(new float[] { 2.0f / 9.0f, 2.0f / 9.0f, 2.0f / 9.0f, 2.0f / 9.0f }); + Vector zero = VectorExtensions.InitializeVector(); + Vector half = VectorExtensions.InitializeVector(new float[] { 0.5f, 0.5f, 0.5f, 0.5f }); + Vector grid = VectorExtensions.InitializeVector(new float[] { 31.0f, 63.0f, 31.0f, 0.0f }); + Vector gridrcp = VectorExtensions.InitializeVector(new float[] { 1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f, 0.0f }); // prepare an ordering using the principle axis ConstructOrdering(ref m_principle, 0); // check all possible clusters and iterate on the total order - RealVector4d beststart = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); - RealVector4d bestend = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); - RealVector4d besterror = m_besterror; + Vector beststart = VectorExtensions.InitializeVector(); + Vector bestend = VectorExtensions.InitializeVector(); + float besterror = m_besterror; byte[] bestindices = new byte[16]; int bestiteration = 0; int besti = 0, bestj = 0, bestk = 0; @@ -1351,53 +1414,55 @@ protected override void Compress4(byte[] block) for (int iterationIndex = 0; ;) { // first cluster [0,i) is at the start - RealVector4d part0 = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); + Vector part0 = VectorExtensions.InitializeVector(); for (int i = 0; i < count; ++i) { // second cluster [i,j) is one third along - RealVector4d part1 = new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); + Vector part1 = VectorExtensions.InitializeVector(); for (int j = i; ;) { // third cluster [j,k) is two thirds along - RealVector4d part2 = (j == 0) ? m_points_weights[0] : new RealVector4d(0.0f, 0.0f, 0.0f, 0.0f); + Vector part2 = (j == 0) ? m_points_weights[0] : VectorExtensions.InitializeVector(); int kmin = (j == 0) ? 1 : j; for (int k = kmin; ;) { + // TODO: swap to 128bit vectors where possible + // last cluster [k,count) is at the end - RealVector4d part3 = m_xsum_wsum - part2 - part1 - part0; + Vector part3 = m_xsum_wsum - part2 - part1 - part0; // compute least squares terms directly - RealVector4d alphax_sum = (part2 * onethird_onethird2 + (part1 * twothirds_twothirds2 + part0)); - RealVector4d alpha2_sum = alphax_sum.WWWW; + Vector alphax_sum = (part2 * onethird_onethird2 + (part1 * twothirds_twothirds2 + part0)); + float alpha2_sum = alphax_sum[3]; - RealVector4d betax_sum = (part1 * onethird_onethird2 + (part2 * twothirds_twothirds2 + part3)); - RealVector4d beta2_sum = betax_sum.WWWW; + Vector betax_sum = (part1 * onethird_onethird2 + (part2 * twothirds_twothirds2 + part3)); + float beta2_sum = betax_sum[3]; - RealVector4d alphabeta_sum = twonineths * (part1 + part2).WWWW; + Vector alphabeta_sum = twonineths * (part1[3] + part2[3]); // compute the least-squares optimal points - RealVector4d factor = 1.0f / (-1.0f * (alphabeta_sum * alphabeta_sum - (alpha2_sum * beta2_sum))); - RealVector4d a = -1.0f * (betax_sum * alphabeta_sum - (alphax_sum * beta2_sum)) * factor; - RealVector4d b = -1.0f * (alphax_sum * alphabeta_sum - (betax_sum * alpha2_sum)) * factor; + Vector factor = -VectorExtensions.Subtract4d(alphabeta_sum * alphabeta_sum, alpha2_sum * beta2_sum); + Vector a = -(betax_sum * alphabeta_sum - (alphax_sum * beta2_sum)) / factor; + Vector b = -(alphax_sum * alphabeta_sum - (betax_sum * alpha2_sum)) / factor; // clamp to the grid - a = SquishMath.Min(one, SquishMath.Max(zero, a)); - b = SquishMath.Min(one, SquishMath.Max(zero, b)); - a = SquishMath.Truncate(grid * a + half) * gridrcp; - b = SquishMath.Truncate(grid * b + half) * gridrcp; + a = SquishMath.Min4d(one, SquishMath.Max4d(zero, a)); + b = SquishMath.Min4d(one, SquishMath.Max4d(zero, b)); + a = SquishMath.Truncate4d(grid * a + half) * gridrcp; + b = SquishMath.Truncate4d(grid * b + half) * gridrcp; // compute the error (we skip the constant xxsum) - RealVector4d e1 = a * a * alpha2_sum + (b * b * beta2_sum); - RealVector4d e2 = -1.0f * (a * alphax_sum - (a * b * alphabeta_sum)); - RealVector4d e3 = -1.0f * (b * betax_sum - e2); - RealVector4d e4 = two * e3 + e1; + Vector e1 = a * a * alpha2_sum + (b * b * beta2_sum); + Vector e2 = -(a * alphax_sum - (a * b * alphabeta_sum)); + Vector e3 = -(b * betax_sum - e2); + Vector e4 = two * e3 + e1; // apply the metric to the error term - RealVector4d e5 = e4 * m_metric; - RealVector4d error = e5.XXXX + e5.YYYY + e5.ZZZZ; + Vector e5 = e4 * m_metric; + float error = e5[0] + e5[1] + e5[2]; // keep the solution if it wins - if (SquishMath.CompareAnyLessThan(error, besterror)) + if (error < besterror) { beststart = a; bestend = b; @@ -1436,13 +1501,13 @@ protected override void Compress4(byte[] block) break; // stop if a new iteration is an ordering that has already been tried - RealVector3d axis = (bestend - beststart).XYZ; + Vector axis = VectorExtensions.Convert4dTo3d(bestend - beststart); if (!ConstructOrdering(ref axis, iterationIndex)) break; } // save the block if necessary - if (SquishMath.CompareAnyLessThan(besterror, m_besterror)) + if (besterror < m_besterror) { // remap the indices byte[] order = m_order[bestiteration]; @@ -1460,7 +1525,7 @@ protected override void Compress4(byte[] block) m_colours.RemapIndices(unordered, bestindices); // save the block - ColourBlock.WriteColourBlock4(beststart.XYZ, bestend.XYZ, bestindices, block); + ColourBlock.WriteColourBlock4(VectorExtensions.Convert4dTo3d(beststart), VectorExtensions.Convert4dTo3d(bestend), bestindices, block); // save the error m_besterror = besterror; @@ -1470,12 +1535,12 @@ protected override void Compress4(byte[] block) private static readonly int kMaxIterations = 8; private int m_iterationCount; - private RealVector3d m_principle; + private Vector m_principle; //3d private byte[][] m_order; // [16][kMaxIterations] - private RealVector4d[] m_points_weights; //[16] - private RealVector4d m_xsum_wsum; - private RealVector4d m_metric; - private RealVector4d m_besterror; + private Vector[] m_points_weights; //4d [16] + private Vector m_xsum_wsum;//4d + private Vector m_metric; //4d + private float m_besterror;//4d } // NOTE that the return value is RGBA not BGRA diff --git a/TagTool/Extensions/VectorExtensions.cs b/TagTool/Extensions/VectorExtensions.cs new file mode 100644 index 000000000..b4ebd0360 --- /dev/null +++ b/TagTool/Extensions/VectorExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Numerics; + +namespace TagTool.Extensions +{ + public static class VectorExtensions + { + // initializes a vector, padding according to SIMD width (4 x86, 8 x64) + public static Vector InitializeVector(float[] a) + { + float[] vector = new float[Vector.Count]; + vector[0] = a[0]; + vector[1] = a[1]; + vector[2] = a[2]; + if (a.Length > 3) + vector[3] = a[3]; + return new Vector(vector); + } + public static Vector InitializeVector(float a) + { + float[] vector = new float[Vector.Count]; + for (int i = 0; i < vector.Length; i++) + vector[i] = a; + return new Vector(vector); + } + + public static Vector InitializeVector() => new Vector(new float[Vector.Count]); + + public static Vector Subtract4d(Vector a, float b) => a - InitializeVector(b); + + public static float Dot3d(Vector a, Vector b) + { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + public static Vector Convert4dTo3d(Vector a) => a; + //{ + // return new Vector(new float[] { a[0], a[1], a[2] }); + //} + } +} diff --git a/TagTool/TagTool.csproj b/TagTool/TagTool.csproj index 5e8cd816f..1e6109953 100644 --- a/TagTool/TagTool.csproj +++ b/TagTool/TagTool.csproj @@ -469,6 +469,7 @@ +