public ClusterFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // set the iteration count m_iterationCount = ((m_flags & SquishFlags.ColourIterativeClusterFit) != 0) ? kMaxIterations : 1; // initialise the best error m_besterror = new Vec4(float.MaxValue); // initialise the metric bool perceptual = ((m_flags & SquishFlags.ColourMetricPerceptual) != 0); if (perceptual) m_metric = new Vec4(0.2126f, 0.7152f, 0.0722f, 0.0f); else m_metric = new Vec4(1.0f); // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, m_colours.GetWeights()); // compute the principle component m_principle = Sym3x3.ComputePrincipleComponent(covariance); }
public static unsafe void WriteColourBlock4(Vec3 start, Vec3 end, byte* indices, byte* block) { // get the packed values int a = FloatTo565(start); int b = FloatTo565(end); // remap the indices byte[] remapped = new byte[16]; if (a < b) { // swap a and b CMath.Swap<int>(ref a, ref b); for (int i = 0; i < 16; ++i) remapped[i] = (byte)((indices[i] ^ 0x1) & 0x3); } else if (a == b) { // use index 0 for (int i = 0; i < 16; ++i) remapped[i] = 0; } else { // use the indices directly for (int i = 0; i < 16; ++i) remapped[i] = indices[i]; } fixed (byte* fixRemapped = remapped) { // write the block WriteColourBlock(a, b, fixRemapped, block); } }
public static Vec3 Truncate( Vec3 v ) { return new Vec3( v.X() > 0.0f ? CMath.Floor( v.X() ) : CMath.Ceil( v.X() ), v.Y() > 0.0f ? CMath.Floor( v.Y() ) : CMath.Ceil( v.Y() ), v.Z() > 0.0f ? CMath.Floor(v.Z()) : CMath.Ceil(v.Z()) ); }
public static Vec3 Max(Vec3 a, Vec3 b) { return new Vec3( Math.Max(a.X(), b.X()), Math.Max(a.Y(), b.Y()), Math.Max(a.Z(), b.Z()) ); }
public static int FloatTo565(Vec3 colour) { // get the components in the correct range int r = CMath.FloatToInt(31.0f * colour.X(), 31); int g = CMath.FloatToInt(63.0f * colour.Y(), 63); int b = CMath.FloatToInt(31.0f * colour.Z(), 31); // pack into a single value return (r << 11) | (g << 5) | b; }
private bool ConstructOrdering(Vec3 axis, int iteration) { // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); // build the list of dot products float[] dps = new float[16]; int orderIndex = 16 * iteration; for (int i = 0; i < count; ++i) { dps[i] = Vec3.Dot(values[i], axis); m_order[orderIndex + i] = (byte)i; } // stable sort using them for (int i = 0; i < count; ++i) { for (int j = i; j > 0 && dps[j] < dps[j - 1]; --j) { CMath.Swap<float>(ref dps[j], ref dps[j - 1]); CMath.Swap<byte>(ref m_order[orderIndex + j], ref m_order[orderIndex + (j - 1)]); } } // check this ordering is unique for (int it = 0; it < iteration; ++it) { int prevIndex = 16 * it; bool same = true; for (int i = 0; i < count; ++i) { if (m_order[orderIndex + i] != m_order[prevIndex + i]) { same = false; break; } } if (same) return false; } // copy the ordering and weight all the points Vec3[] unweighted = m_colours.GetPoints(); float[] weights = m_colours.GetWeights(); m_xsum_wsum = new Vec4(0.0f); for (int i = 0; i < count; ++i) { int j = m_order[orderIndex + i]; Vec4 p = new Vec4(unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f); Vec4 w = new Vec4(weights[j]); Vec4 x = p * w; m_points_weights[i] = x; m_xsum_wsum += x; } return true; }
protected override unsafe void Compress3(byte* block) { // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); // create a codebook Vec3[] codes = new Vec3[3]; codes[0] = m_start; codes[1] = m_end; codes[2] = 0.5f * m_start + 0.5f * m_end; // match each point to the closest code byte[] closest = new byte[16]; float error = 0.0f; for (int i = 0; i < count; ++i) { // find the closest code float dist = float.MaxValue; int idx = 0; for (int j = 0; j < 3; ++j) { float d = Vec3.LengthSquared(m_metric * (values[i] - codes[j])); if (d < dist) { dist = d; idx = j; } } // save the index closest[i] = (byte)idx; // accumulate the error error += dist; } // save this scheme if it wins if( error < m_besterror ) { // remap the indices byte[] indices = new byte[16]; fixed (byte* fixClosest = closest, fixIndices = indices) { m_colours.RemapIndices(fixClosest, fixIndices); // save the block ColourBlock.WriteColourBlock3(m_start, m_end, fixIndices, block); } // save the error m_besterror = error; } }
public RangeFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // initialise the metric bool perceptual = ((m_flags & SquishFlags.ColourMetricPerceptual) != 0); if (perceptual) m_metric = new Vec3(0.2126f, 0.7152f, 0.0722f); else m_metric = new Vec3(1.0f); // initialise the best error m_besterror = float.MaxValue; // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); float[] weights = m_colours.GetWeights(); // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, weights); // compute the principle component Vec3 principle = Sym3x3.ComputePrincipleComponent(covariance); // get the min and max range as the codebook endpoints Vec3 start = new Vec3(0.0f); Vec3 end = new Vec3(0.0f); if (count > 0) { float min, max; // compute the range start = end = values[0]; min = max = Vec3.Dot(values[0], principle); for (int i = 1; i < count; ++i) { float val = Vec3.Dot(values[i], principle); if (val < min) { start = values[i]; min = val; } else if (val > max) { end = values[i]; max = val; } } } // clamp the output to [0, 1] Vec3 one = new Vec3(1.0f); Vec3 zero = new Vec3(0.0f); start = Vec3.Min(one, Vec3.Max(zero, start)); end = Vec3.Min(one, Vec3.Max(zero, end)); // clamp to the grid and save Vec3 grid = new Vec3(31.0f, 63.0f, 31.0f); Vec3 gridrcp = new Vec3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); Vec3 half = new Vec3(0.5f); m_start = Vec3.Truncate(grid * start + half) * gridrcp; m_end = Vec3.Truncate(grid * end + half) * gridrcp; }
private void ComputeEndPoints(SingleColourLookup[][] lookups) { // check each index combination (endpoint or intermediate) m_error = int.MaxValue; for (int index = 0; index < 2; ++index) { // check the error for this codebook index SourceBlock[] sources = new SourceBlock[3]; int error = 0; for( int channel = 0; channel < 3; ++channel ) { // grab the lookup table and index for this channel SingleColourLookup[] lookup = lookups[channel]; int target = m_colour[channel]; // store a pointer to the source for this channel sources[channel] = lookup[target].sources[index]/* + index*/; // accumulate the error int diff = sources[channel].error; error += diff * diff; } // keep it if the error is lower if (error < m_error) { m_start = new Vec3( (float)sources[0].start / 31.0f, (float)sources[1].start / 63.0f, (float)sources[2].start / 31.0f ); m_end = new Vec3( (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; } } }
public static float LengthSquared(Vec3 v) { return Dot(v, v); }
public static float Dot(Vec3 a, Vec3 b) { return a.X() * b.X() + a.Y()* b.Y() + a.Z() * b.Z(); }
public static Sym3x3 ComputeWeightedCovariance(int n, Vec3[] points, float[] weights) { // compute the centroid float total = 0.0f; Vec3 centroid = new Vec3(0.0f); for (int i = 0; i < n; ++i) { total += weights[i]; centroid += weights[i] * points[i]; } centroid /= total; // accumulate the covariance matrix Sym3x3 covariance = new Sym3x3(0.0f); for (int i = 0; i < n; ++i) { Vec3 a = points[i] - centroid; Vec3 b = weights[i] * a; covariance[0] += a.X() * b.X(); covariance[1] += a.X() * b.Y(); covariance[2] += a.X() * b.Z(); covariance[3] += a.Y() * b.Y(); covariance[4] += a.Y() * b.Z(); covariance[5] += a.Z() * b.Z(); } // return it return covariance; }
public unsafe ColourSet(byte* rgba, int mask, SquishFlags flags) { m_count = 0; m_transparent = false; bool isDxt1 = ((flags & SquishFlags.Dxt1) != 0); bool weightByAlpha = ((flags & SquishFlags.WeightColourByAlpha) != 0); // create the minimal set for (int i = 0; i < 16; ++i) { // check this pixel is enabled int bit = 1 << i; if ((mask & bit) == 0) { m_remap[i] = -1; continue; } // check for transparent pixels when using dxt1 if (isDxt1 && rgba[4 * i + 3] < 128) { m_remap[i] = -1; m_transparent = true; continue; } // loop over previous points for a match for (int j = 0; ; ++j) { // allocate a new point if (j == i) { // normalise coordinates to [0,1] float x = (float)rgba[4 * i] / 255.0f; float y = (float)rgba[4 * i + 1] / 255.0f; float z = (float)rgba[4 * i + 2] / 255.0f; // ensure there is always non-zero weight even for zero alpha float w = (float)(rgba[4 * i + 3] + 1) / 256.0f; // add the point m_points[m_count] = new Vec3(x, y, z); m_weights[m_count] = (weightByAlpha ? w : 1.0f); m_remap[i] = m_count; // advance ++m_count; break; } // check for a match int oldbit = 1 << j; bool match = ((mask & oldbit) != 0) && (rgba[4 * i] == rgba[4 * j]) && (rgba[4 * i + 1] == rgba[4 * j + 1]) && (rgba[4 * i + 2] == rgba[4 * j + 2]) && (rgba[4 * j + 3] >= 128 || !isDxt1); if (match) { // get the index of the match int index = m_remap[j]; // ensure there is always non-zero weight even for zero alpha float w = (float)(rgba[4 * i + 3] + 1) / 256.0f; // map to this point and increase the weight m_weights[index] += (weightByAlpha ? w : 1.0f); m_remap[i] = index; break; } } } // square root the weights for (int i = 0; i < m_count; ++i) m_weights[i] = CMath.Sqrt(m_weights[i]); }