Пример #1
0
        public static void GetDXT(Texture2D texture, int i, byte[] bytes, TextureFormat format)
        {
            Color32[] colors = texture.GetPixels32(i);
            uint      w      = (uint)texture.width >> i;
            uint      h      = (uint)texture.height >> i;

            ColorBlock rgba   = new ColorBlock();
            BlockDXT1  block1 = new BlockDXT1();
            BlockDXT5  block5 = new BlockDXT5();

            int blocksize = format == TextureFormat.DXT1 ? 8 : 16;
            int index     = 0;

            for (uint y = 0; y < h; y += 4)
            {
                for (uint x = 0; x < w; x += 4)
                {
                    rgba.init(w, h, colors, x, y);

                    if (format == TextureFormat.DXT1)
                    {
                        QuickCompress.compressDXT1(rgba, block1);
                        block1.WriteBytes(bytes, index);
                    }
                    else
                    {
                        QuickCompress.compressDXT5(rgba, block5, 0);
                        block5.WriteBytes(bytes, index);
                    }

                    index += blocksize;
                }
            }
        }
        public static void GetDXT(Texture2D texture, int i, byte[] bytes, TextureFormat format)
        {
            Color32[] colors = texture.GetPixels32(i);
            uint w = (uint) texture.width>>i;
            uint h = (uint) texture.height>>i;

            ColorBlock rgba = new ColorBlock();
            BlockDXT1 block1 = new BlockDXT1();
            BlockDXT5 block5 = new BlockDXT5();

            int blocksize = format == TextureFormat.DXT1 ? 8 : 16;
            int index = 0;
            for (uint y = 0; y < h; y += 4) {
                for (uint x = 0; x < w; x += 4) {
                    rgba.init(w, h, colors, x, y);

                    if (format == TextureFormat.DXT1)
                    {
                        QuickCompress.compressDXT1(rgba, block1);
                        block1.WriteBytes(bytes, index);
                    }
                    else
                    {
                        QuickCompress.compressDXT5(rgba, block5, 0);
                        block5.WriteBytes(bytes, index);
                    }

                    index += blocksize;
                }
            }
        }
        void compressDXT1a(Color32 c, uint alphaMask, BlockDXT1 dxtBlock)
        {
            if (alphaMask == 0)
            {
                compressDXT1(c, dxtBlock);
            }
            else
            {
                dxtBlock.col0.r  = OMatchAlpha5[c.r, 0];
                dxtBlock.col0.g  = OMatchAlpha6[c.g, 0];
                dxtBlock.col0.b  = OMatchAlpha5[c.b, 0];
                dxtBlock.col1.r  = OMatchAlpha5[c.r, 1];
                dxtBlock.col1.g  = OMatchAlpha6[c.g, 1];
                dxtBlock.col1.b  = OMatchAlpha5[c.b, 1];
                dxtBlock.indices = 0xaaaaaaaa; // 0b1010..1010

                if (dxtBlock.col0.u > dxtBlock.col1.u)
                {
                    ushort u0tmp = dxtBlock.col0.u;
                    dxtBlock.col0.u = dxtBlock.col1.u;
                    dxtBlock.col1.u = dxtBlock.col0.u;
                }

                dxtBlock.indices |= alphaMask;
            }
        }
        /*static uint nearestGreen4(uint green, uint maxGreen, uint minGreen)
         * {
         *      uint bias = maxGreen + (maxGreen - minGreen) / 6;
         *
         *      uint index = 0;
         *      if (maxGreen - minGreen != 0) index = clamp(3 * (bias - green) / (maxGreen - minGreen), 0U, 3U);
         *
         *      return (index * minGreen + (3 - index) * maxGreen) / 3;
         * }*/

        static uint computeGreenError(ColorBlock rgba, BlockDXT1 block, uint bestError = uint.MaxValue)
        {
            //      uint g0 = (block.col0.g << 2) | (block.col0.g >> 4);
            //      uint g1 = (block.col1.g << 2) | (block.col1.g >> 4);

            int[] palette = new int[4];
            palette[0] = (block.col0.g << 2) | (block.col0.g >> 4);
            palette[1] = (block.col1.g << 2) | (block.col1.g >> 4);
            palette[2] = (2 * palette[0] + palette[1]) / 3;
            palette[3] = (2 * palette[1] + palette[0]) / 3;

            uint totalError = 0;

            for (uint i = 0; i < 16; i++)
            {
                int green = rgba.color[i].g;

                uint error = greenDistance(green, palette[0]);
                error = Math.Min(error, greenDistance(green, palette[1]));
                error = Math.Min(error, greenDistance(green, palette[2]));
                error = Math.Min(error, greenDistance(green, palette[3]));

                totalError += error;

                //      totalError += nearestGreen4(green, g0, g1);

                if (totalError > bestError)
                {
                    // early out
                    return(totalError);
                }
            }

            return(totalError);
        }
Пример #5
0
        static void optimizeEndPoints3(Vector3[] block, BlockDXT1 dxtBlock)
        {
            float   alpha2_sum    = 0.0f;
            float   beta2_sum     = 0.0f;
            float   alphabeta_sum = 0.0f;
            Vector3 alphax_sum    = Vector3.zero;
            Vector3 betax_sum     = Vector3.zero;

            for (int i = 0; i < 16; ++i)
            {
                uint bits = dxtBlock.indices >> (2 * i);

                float beta = (float)(bits & 1);
                if ((bits & 2) != 0)
                {
                    beta = 0.5f;
                }
                float alpha = 1.0f - beta;

                alpha2_sum    += alpha * alpha;
                beta2_sum     += beta * beta;
                alphabeta_sum += alpha * beta;
                alphax_sum    += alpha * block[i];
                betax_sum     += beta * block[i];
            }

            float denom = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;

            if (Mathf.Approximately(denom, 0.0f))
            {
                return;
            }

            float factor = 1.0f / denom;

            Vector3 a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;
            Vector3 b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;

            a.x = Mathf.Clamp(a.x, 0, 255);
            a.y = Mathf.Clamp(a.y, 0, 255);
            a.z = Mathf.Clamp(a.z, 0, 255);
            b.x = Mathf.Clamp(b.x, 0, 255);
            b.y = Mathf.Clamp(b.y, 0, 255);
            b.z = Mathf.Clamp(b.z, 0, 255);

            ushort color0 = roundAndExpand(ref a);
            ushort color1 = roundAndExpand(ref b);

            if (color0 < color1)
            {
                swap(ref a, ref b);
                swap(ref color0, ref color1);
            }

            dxtBlock.col0    = new Color16(color1);
            dxtBlock.col1    = new Color16(color0);
            dxtBlock.indices = computeIndices3(block, a, b);
        }
Пример #6
0
        void compressDXT1a(ColorBlock rgba, BlockDXT1 dxtBlock)
        {
            bool hasAlpha = false;

            for (uint i = 0; i < 16; i++)
            {
                if (rgba.color[i].a == 0)
                {
                    hasAlpha = true;
                    break;
                }
            }

            if (!hasAlpha)
            {
                compressDXT1(rgba, dxtBlock);
            }
            // @@ Handle single RGB, with varying alpha? We need tables for single color compressor in 3 color mode.
            //else if (rgba.isSingleColorNoAlpha()) { ... }
            else
            {
                // read block
                Vector3[] block = new Vector3[16];
                uint      num   = extractColorBlockRGBA(rgba, block);

                // find min and max colors
                Vector3 maxColor = Vector3.zero, minColor = Vector3.zero;
                findMinMaxColorsBox(block, num, ref maxColor, ref minColor);

                selectDiagonal(block, num, ref maxColor, ref minColor);

                insetBBox(ref maxColor, ref minColor);

                ushort color0 = roundAndExpand(ref maxColor);
                ushort color1 = roundAndExpand(ref minColor);

                if (color0 < color1)
                {
                    swap(ref maxColor, ref minColor);
                    swap(ref color0, ref color1);
                }

                dxtBlock.col0    = new Color16(color1);
                dxtBlock.col1    = new Color16(color0);
                dxtBlock.indices = computeIndices3(block, maxColor, minColor);

                //      optimizeEndPoints(block, dxtBlock);
            }
        }
        void compressDXT1G(byte g, BlockDXT1 dxtBlock)
        {
            dxtBlock.col0.r  = 31;
            dxtBlock.col0.g  = OMatch6[g, 0];
            dxtBlock.col0.b  = 0;
            dxtBlock.col1.r  = 31;
            dxtBlock.col1.g  = OMatch6[g, 1];
            dxtBlock.col1.b  = 0;
            dxtBlock.indices = 0xaaaaaaaa;

            if (dxtBlock.col0.u < dxtBlock.col1.u)
            {
                ushort u0tmp = dxtBlock.col0.u;
                dxtBlock.col0.u   = dxtBlock.col1.u;
                dxtBlock.col1.u   = dxtBlock.col0.u;
                dxtBlock.indices ^= 0x55555555;
            }
        }
        // Single color compressor, based on:
        // https://mollyrocket.com/forums/viewtopic.php?t=392
        public static void compressDXT1(Color32 c, BlockDXT1 dxtBlock)
        {
            initSingleColorLookup();
            dxtBlock.col0.r = OMatch5[c.r, 0];
            dxtBlock.col0.g = OMatch6[c.g, 0];
            dxtBlock.col0.b = OMatch5[c.b, 0];
            dxtBlock.col1.r = OMatch5[c.r, 1];
            dxtBlock.col1.g = OMatch6[c.g, 1];
            dxtBlock.col1.b = OMatch5[c.b, 1];
            dxtBlock.indices = 0xaaaaaaaa;

            if (dxtBlock.col0.u < dxtBlock.col1.u)
            {
                ushort u0tmp = dxtBlock.col0.u;
                dxtBlock.col0.u = dxtBlock.col1.u;
                dxtBlock.col1.u = dxtBlock.col0.u;
                dxtBlock.indices ^= 0x55555555;
            }
        }
        // Single color compressor, based on:
        // https://mollyrocket.com/forums/viewtopic.php?t=392
        public static void compressDXT1(Color32 c, BlockDXT1 dxtBlock)
        {
            initSingleColorLookup();
            dxtBlock.col0.r  = OMatch5[c.r, 0];
            dxtBlock.col0.g  = OMatch6[c.g, 0];
            dxtBlock.col0.b  = OMatch5[c.b, 0];
            dxtBlock.col1.r  = OMatch5[c.r, 1];
            dxtBlock.col1.g  = OMatch6[c.g, 1];
            dxtBlock.col1.b  = OMatch5[c.b, 1];
            dxtBlock.indices = 0xaaaaaaaa;

            if (dxtBlock.col0.u < dxtBlock.col1.u)
            {
                ushort u0tmp = dxtBlock.col0.u;
                dxtBlock.col0.u   = dxtBlock.col1.u;
                dxtBlock.col1.u   = dxtBlock.col0.u;
                dxtBlock.indices ^= 0x55555555;
            }
        }
Пример #10
0
        void outputBlock3(ColorSet set, Vector3 start, Vector3 end, BlockDXT1 block)
        {
            Vector3 minColor = start * 255.0f;
            Vector3 maxColor = end * 255.0f;
            ushort  color0   = roundAndExpand(ref minColor);
            ushort  color1   = roundAndExpand(ref maxColor);

            if (color0 > color1)
            {
                swap(ref maxColor, ref minColor);
                swap(ref color0, ref color1);
            }

            block.col0    = new Color16(color0);
            block.col1    = new Color16(color1);
            block.indices = computeIndices3(set, maxColor / 255.0f, minColor / 255.0f);

            //optimizeEndPoints3(set, block);
        }
        /*void OptimalCompress::initLumaTables() {
         *
         *  // For all possible color pairs:
         *  for (int c0 = 0; c0 < 65536; c0++) {
         *      for (int c1 = 0; c1 < 65536; c1++) {
         *
         *          // Compute
         *
         *      }
         *  }
         *
         *
         *  for (int r = 0; r < 1<<5; r++) {
         *      for (int g = 0; g < 1<<6; g++) {
         *          for (int b = 0; b < 1<<5; b++) {
         *
         *
         *          }
         *      }
         *  }
         * }*/


        // Brute force Luma compressor
        void compressDXT1_Luma(ColorBlock rgba, BlockDXT1 block)
        {
            // F_YR = 19595/65536.0f, F_YG = 38470/65536.0f, F_YB = 7471/65536.0f;
            // 195841
            //if (


            /*
             *  byte ming = 63;
             *  byte maxg = 0;
             *
             *  bool isSingleColor = true;
             *  byte singleColor = rgba.color(0).g;
             *
             *  // Get min/max green.
             *  for (uint i = 0; i < 16; i++)
             *  {
             *          byte green = (rgba.color[i].g + 1) >> 2;
             *          ming = min(ming, green);
             *          maxg = max(maxg, green);
             *
             *          if (rgba.color[i].g != singleColor) isSingleColor = false;
             *  }
             *
             *  if (isSingleColor)
             *  {
             *          compressDXT1G(singleColor, block);
             *          return;
             *  }
             *
             *  block.col0.r = 31;
             *  block.col1.r = 31;
             *  block.col0.g = maxg;
             *  block.col1.g = ming;
             *  block.col0.b = 0;
             *  block.col1.b = 0;
             *
             *  int bestError = computeGreenError(rgba, block);
             *  int bestg0 = maxg;
             *  int bestg1 = ming;
             *
             *  // Expand search space a bit.
             *   int greenExpand = 4;
             *  ming = (ming <= greenExpand) ? 0 : ming - greenExpand;
             *  maxg = (maxg >= 63-greenExpand) ? 63 : maxg + greenExpand;
             *
             *  for (int g0 = ming+1; g0 <= maxg; g0++)
             *  {
             *          for (int g1 = ming; g1 < g0; g1++)
             *          {
             *                  block.col0.g = g0;
             *                  block.col1.g = g1;
             *                  int error = computeGreenError(rgba, block, bestError);
             *
             *                  if (error < bestError)
             *                  {
             *                          bestError = error;
             *                          bestg0 = g0;
             *                          bestg1 = g1;
             *                  }
             *          }
             *  }
             *
             *  block.col0.g = bestg0;
             *  block.col1.g = bestg1;
             *
             *  nvDebugCheck(bestg0 == bestg1 || block.isFourColorMode());
             */

            Color32[] palette = new Color32[4];
            block.evaluatePalette(palette, false); // @@ Use target decoder.
            block.indices = computeGreenIndices(rgba, palette);
        }
        /*void OptimalCompress::initLumaTables() {

            // For all possible color pairs:
            for (int c0 = 0; c0 < 65536; c0++) {
                for (int c1 = 0; c1 < 65536; c1++) {

                    // Compute

                }
            }

            for (int r = 0; r < 1<<5; r++) {
                for (int g = 0; g < 1<<6; g++) {
                    for (int b = 0; b < 1<<5; b++) {

                    }
                }
            }
        }*/
        // Brute force Luma compressor
        void compressDXT1_Luma(ColorBlock rgba, BlockDXT1 block)
        {
            // F_YR = 19595/65536.0f, F_YG = 38470/65536.0f, F_YB = 7471/65536.0f;
            // 195841
            //if (

            /*
                byte ming = 63;
                byte maxg = 0;

                bool isSingleColor = true;
                byte singleColor = rgba.color(0).g;

                // Get min/max green.
                for (uint i = 0; i < 16; i++)
                {
                        byte green = (rgba.color[i].g + 1) >> 2;
                        ming = min(ming, green);
                        maxg = max(maxg, green);

                        if (rgba.color[i].g != singleColor) isSingleColor = false;
                }

                if (isSingleColor)
                {
                        compressDXT1G(singleColor, block);
                        return;
                }

                block.col0.r = 31;
                block.col1.r = 31;
                block.col0.g = maxg;
                block.col1.g = ming;
                block.col0.b = 0;
                block.col1.b = 0;

                int bestError = computeGreenError(rgba, block);
                int bestg0 = maxg;
                int bestg1 = ming;

                // Expand search space a bit.
                 int greenExpand = 4;
                ming = (ming <= greenExpand) ? 0 : ming - greenExpand;
                maxg = (maxg >= 63-greenExpand) ? 63 : maxg + greenExpand;

                for (int g0 = ming+1; g0 <= maxg; g0++)
                {
                        for (int g1 = ming; g1 < g0; g1++)
                        {
                                block.col0.g = g0;
                                block.col1.g = g1;
                                int error = computeGreenError(rgba, block, bestError);

                                if (error < bestError)
                                {
                                        bestError = error;
                                        bestg0 = g0;
                                        bestg1 = g1;
                                }
                        }
                }

                block.col0.g = bestg0;
                block.col1.g = bestg1;

                nvDebugCheck(bestg0 == bestg1 || block.isFourColorMode());
            */

            Color32[] palette = new Color32[4];
            block.evaluatePalette(palette, false); // @@ Use target decoder.
            block.indices = computeGreenIndices(rgba, palette);
        }
        // Brute force green channel compressor
        void compressDXT1G(ColorBlock rgba, BlockDXT1 block)
        {
            byte ming = 63;
            byte maxg = 0;

            bool isSingleColor = true;
            byte singleColor = rgba.color[0].g;

            // Get min/max green.
            for (uint i = 0; i < 16; i++)
            {
                byte green = (byte)((rgba.color[i].g + 1) >> 2);
                ming = Math.Min(ming, green);
                maxg = Math.Max(maxg, green);

                if (rgba.color[i].g != singleColor) isSingleColor = false;
            }

            if (isSingleColor)
            {
                compressDXT1G(singleColor, block);
                return;
            }

            block.col0.r = 31;
            block.col1.r = 31;
            block.col0.g = maxg;
            block.col1.g = ming;
            block.col0.b = 0;
            block.col1.b = 0;

            uint bestError = computeGreenError(rgba, block);
            byte bestg0 = maxg;
            byte bestg1 = ming;

            // Expand search space a bit.
            int greenExpand = 4;
            ming = (byte)((ming <= greenExpand) ? 0 : ming - greenExpand);
            maxg = (byte)((maxg >= 63 - greenExpand) ? 63 : maxg + greenExpand);

            for (byte g0 = (byte)(ming + 1); g0 <= maxg; g0++)
            {
                for (byte g1 = ming; g1 < g0; g1++)
                {
                    block.col0.g = g0;
                    block.col1.g = g1;
                    uint error = computeGreenError(rgba, block, bestError);

                    if (error < bestError)
                    {
                        bestError = error;
                        bestg0 = g0;
                        bestg1 = g1;
                    }
                }
            }

            block.col0.g = bestg0;
            block.col1.g = bestg1;

            Color32[] palette = new Color32[4];
            block.evaluatePalette(palette, false); // @@ Use target decoder.
            block.indices = computeGreenIndices(rgba, palette);
        }
        void compressDXT1G(byte g, BlockDXT1 dxtBlock)
        {
            dxtBlock.col0.r = 31;
            dxtBlock.col0.g = OMatch6[g, 0];
            dxtBlock.col0.b = 0;
            dxtBlock.col1.r = 31;
            dxtBlock.col1.g = OMatch6[g, 1];
            dxtBlock.col1.b = 0;
            dxtBlock.indices = 0xaaaaaaaa;

            if (dxtBlock.col0.u < dxtBlock.col1.u)
            {
                ushort u0tmp = dxtBlock.col0.u;
                dxtBlock.col0.u = dxtBlock.col1.u;
                dxtBlock.col1.u = dxtBlock.col0.u;
                dxtBlock.indices ^= 0x55555555;
            }
        }
        void compressDXT1a(Color32 c, uint alphaMask, BlockDXT1 dxtBlock)
        {
            if (alphaMask == 0)
            {
                compressDXT1(c, dxtBlock);
            }
            else
            {
                dxtBlock.col0.r = OMatchAlpha5[c.r, 0];
                dxtBlock.col0.g = OMatchAlpha6[c.g, 0];
                dxtBlock.col0.b = OMatchAlpha5[c.b, 0];
                dxtBlock.col1.r = OMatchAlpha5[c.r, 1];
                dxtBlock.col1.g = OMatchAlpha6[c.g, 1];
                dxtBlock.col1.b = OMatchAlpha5[c.b, 1];
                dxtBlock.indices = 0xaaaaaaaa; // 0b1010..1010

                if (dxtBlock.col0.u > dxtBlock.col1.u)
                {
                    ushort u0tmp = dxtBlock.col0.u;
                    dxtBlock.col0.u = dxtBlock.col1.u;
                    dxtBlock.col1.u = dxtBlock.col0.u;
                }

                dxtBlock.indices |= alphaMask;
            }
        }
        /*static uint nearestGreen4(uint green, uint maxGreen, uint minGreen)
        {
                uint bias = maxGreen + (maxGreen - minGreen) / 6;

                uint index = 0;
                if (maxGreen - minGreen != 0) index = clamp(3 * (bias - green) / (maxGreen - minGreen), 0U, 3U);

                return (index * minGreen + (3 - index) * maxGreen) / 3;
        }*/
        static uint computeGreenError(ColorBlock rgba, BlockDXT1 block, uint bestError = uint.MaxValue)
        {
            //      uint g0 = (block.col0.g << 2) | (block.col0.g >> 4);
            //      uint g1 = (block.col1.g << 2) | (block.col1.g >> 4);

            int[] palette = new int[4];
            palette[0] = (block.col0.g << 2) | (block.col0.g >> 4);
            palette[1] = (block.col1.g << 2) | (block.col1.g >> 4);
            palette[2] = (2 * palette[0] + palette[1]) / 3;
            palette[3] = (2 * palette[1] + palette[0]) / 3;

            uint totalError = 0;
            for (uint i = 0; i < 16; i++)
            {
                int green = rgba.color[i].g;

                uint error = greenDistance(green, palette[0]);
                error = Math.Min(error, greenDistance(green, palette[1]));
                error = Math.Min(error, greenDistance(green, palette[2]));
                error = Math.Min(error, greenDistance(green, palette[3]));

                totalError += error;

                //      totalError += nearestGreen4(green, g0, g1);

                if (totalError > bestError)
                {
                    // early out
                    return totalError;
                }
            }

            return totalError;
        }
        // Brute force green channel compressor
        void compressDXT1G(ColorBlock rgba, BlockDXT1 block)
        {
            byte ming = 63;
            byte maxg = 0;

            bool isSingleColor = true;
            byte singleColor   = rgba.color[0].g;

            // Get min/max green.
            for (uint i = 0; i < 16; i++)
            {
                byte green = (byte)((rgba.color[i].g + 1) >> 2);
                ming = Math.Min(ming, green);
                maxg = Math.Max(maxg, green);

                if (rgba.color[i].g != singleColor)
                {
                    isSingleColor = false;
                }
            }

            if (isSingleColor)
            {
                compressDXT1G(singleColor, block);
                return;
            }

            block.col0.r = 31;
            block.col1.r = 31;
            block.col0.g = maxg;
            block.col1.g = ming;
            block.col0.b = 0;
            block.col1.b = 0;

            uint bestError = computeGreenError(rgba, block);
            byte bestg0    = maxg;
            byte bestg1    = ming;

            // Expand search space a bit.
            int greenExpand = 4;

            ming = (byte)((ming <= greenExpand) ? 0 : ming - greenExpand);
            maxg = (byte)((maxg >= 63 - greenExpand) ? 63 : maxg + greenExpand);

            for (byte g0 = (byte)(ming + 1); g0 <= maxg; g0++)
            {
                for (byte g1 = ming; g1 < g0; g1++)
                {
                    block.col0.g = g0;
                    block.col1.g = g1;
                    uint error = computeGreenError(rgba, block, bestError);

                    if (error < bestError)
                    {
                        bestError = error;
                        bestg0    = g0;
                        bestg1    = g1;
                    }
                }
            }

            block.col0.g = bestg0;
            block.col1.g = bestg1;

            Color32[] palette = new Color32[4];
            block.evaluatePalette(palette, false); // @@ Use target decoder.
            block.indices = computeGreenIndices(rgba, palette);
        }
Пример #18
0
        public static void compressDXT1(ColorBlock rgba, BlockDXT1 dxtBlock)
        {
            if (rgba.isSingleColor())
            {
                OptimalCompress.compressDXT1(rgba.color[0], dxtBlock);
            }
            else
            {
                // read block
                Vector3[] block = new Vector3[16];
                extractColorBlockRGB(rgba, block);
#if true
                // find min and max colors
                Vector3 maxColor = Vector3.zero, minColor = Vector3.zero;
                findMinMaxColorsBox(block, 16, ref maxColor, ref minColor);

                selectDiagonal(block, 16, ref maxColor, ref minColor);

                insetBBox(ref maxColor, ref minColor);
#else
                float[] weights = new float[16] {
                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
                };
                Vector3[] cluster = new Vector3[4];
                int       count   = Fitting.Compute4Means(16, block, weights, Vector3.one, cluster);

                Vector3 maxColor, minColor;
                float   bestError = FLT_MAX;

                for (int i = 1; i < 4; i++)
                {
                    for (int j = 0; j < i; j++)
                    {
                        uint16 color0 = roundAndExpand(&cluster[i]);
                        uint16 color1 = roundAndExpand(&cluster[j]);

                        float error = evaluatePaletteError4(block, cluster[i], cluster[j]);
                        if (error < bestError)
                        {
                            bestError = error;
                            maxColor  = cluster[i];
                            minColor  = cluster[j];
                        }
                    }
                }
#endif

                ushort color0 = roundAndExpand(ref maxColor);
                ushort color1 = roundAndExpand(ref minColor);

                if (color0 < color1)
                {
                    swap(ref maxColor, ref minColor);
                    swap(ref color0, ref color1);
                }

                dxtBlock.col0    = new Color16(color0);
                dxtBlock.col1    = new Color16(color1);
                dxtBlock.indices = computeIndices4(block, maxColor, minColor);

                optimizeEndPoints4(block, dxtBlock);
            }
        }