protected override void EncodeBlock(ARGBPixel *sPtr, VoidPtr blockAddr, int width)
        {
            CMPRBlock *dPtr = (CMPRBlock *)blockAddr;

            for (int y = 0; y < 2; y++, sPtr += (width * 4))
            {
                for (int x = 0; x < 8; x += 4)
                {
                    *dPtr++ = CMPRBlock.Encode(&sPtr[x], width, false);
                }
            }
        }
        public static CMPRBlock Encode(ARGBPixel* block, int width, bool fast)
        {
            CMPRBlock p = new CMPRBlock();

            uint* pData = stackalloc uint[16];
            ARGBPixel* pColor = (ARGBPixel*)pData;

            bool isSingle = true, hasAlpha = false, allAlpha = true;
            for (int y = 0, i = 0; y < 4; y++, block += width)
            {
                for (int x = 0; x < 4; i++)
                {
                    pColor[i] = block[x++];
                    if (pData[i] != pData[0]) isSingle = false;
                    if (pColor[i].A < 0x80) hasAlpha = true;
                    else allAlpha = false;
                }
            }

            /*
             *  Foreach block:
             *      copy block to buffer
             *      mirror remaning colors?
             *
             *      If block is single color:
             *          run optiml compress?
             *      else
             *          Initialize color set
             *          Compress block using color set
             *
             *
             */

            //BlockDecoder decoder = new BlockDecoder(width, 4, 4, 4);
            //bool isSingle = true, hasAlpha = false, allAlpha = true;
            //for (int i = 0; i < 16; i++)
            //{
            //    int index = decoder[i];
            //    pColor[i] = block[index];
            //    if (pData[i] != pData[0]) isSingle = false;
            //    if (pColor[i].A < 0x80) hasAlpha = true;
            //    else allAlpha = false;
            //}

            //bool isSingle = true, hasAlpha = false, allAlpha = true;
            //ARGBPixel* ptr = block;
            //int index = 0;
            //for (int y = 0; y < 4; y++, ptr += width - 4)
            //{
            //    for (int x = 0; x < 4; x++, ptr++, index++)
            //    {
            //        pColor[index] = block[(y * width) + x];
            //        if (pData[0] != pData[index]) isSingle = false;
            //        if (pColor[index].A < 0x80) hasAlpha = true;
            //        else allAlpha = false;
            //    }
            //}

            //if (allAlpha)
            //{
            //    p._root0._data = 0;
            //    p._root1._data = 0xFFFF;
            //    p._lookup = 0xFFFFFFFF;
            //}
            //else if (isSingle)
            //{
            //    p._root0 = (RGB565Pixel)(*block);
            //    p._root1._data = 0;
            //    p._lookup = 0x00000000;
            //}
            //else
            //{
            uint* palData = stackalloc uint[4];
            ARGBPixel* palCol = (ARGBPixel*)palData;

            int bestDist = -1;
            for (int i = 0; i < 16; i++)
            {
                ARGBPixel p1 = pColor[i];
                for (int x = i + 1; x < 16; x++)
                {
                    ARGBPixel p2 = pColor[x];
                    int d = p1.DistanceTo(p2);
                    if (d > bestDist)
                    {
                        bestDist = d;
                        palCol[2] = p1;
                        palCol[3] = p2;
                    }
                }
            }

            wRGB565Pixel smax = (wRGB565Pixel)palCol[2];
            wRGB565Pixel smin = (wRGB565Pixel)palCol[3];

            if (smax < smin)
            {
                smax = (wRGB565Pixel)palCol[3]; smin = (wRGB565Pixel)palCol[2];
            }

            if (hasAlpha)
            {
                p._root0 = smin;
                p._root1 = smax;
                palCol[0] = (ARGBPixel)smin;
                palCol[1] = (ARGBPixel)smax;
                palCol[2] = new ARGBPixel(255, (byte)((palCol[0].R + palCol[1].R) >> 1), (byte)((palCol[0].G + palCol[1].G) >> 1), (byte)((palCol[0].B + palCol[1].B) >> 1));
                palCol[3] = new ARGBPixel();
            }
            else
            {
                p._root0 = smax;
                p._root1 = smin;
                palCol[0] = (ARGBPixel)smax;
                palCol[1] = (ARGBPixel)smin;
                palCol[2] = new ARGBPixel(255, (byte)(((palCol[0].R << 1) + palCol[1].R) / 3), (byte)(((palCol[0].G << 1) + palCol[1].G) / 3), (byte)(((palCol[0].B << 1) + palCol[1].B) / 3));
                palCol[3] = new ARGBPixel(255, (byte)(((palCol[1].R << 1) + palCol[0].R) / 3), (byte)(((palCol[1].G << 1) + palCol[0].G) / 3), (byte)(((palCol[1].B << 1) + palCol[0].B) / 3));
            }

            uint indicies = 0;
            for (int i = 0, shift = 30; i < 16; i++, shift -= 2)
            {
                uint index = 3;
                if (pColor[i].A >= 0x80)
                {
                    int bd = int.MaxValue;
                    for (int x = 0; x < ((hasAlpha) ? 4 : 3); x++)
                    {
                        int dist = palCol[x].DistanceTo(pColor[i]);
                        if (dist < bd) { bd = dist; index = (uint)x; }
                    }
                }
                indicies |= index << shift;
            }
            p._lookup = indicies;

            //p = DXT1Fast.Compress(pColor);
            //}

            return p;
        }
        public static CMPRBlock Encode(ARGBPixel *block, int width, bool fast)
        {
            CMPRBlock p = new CMPRBlock();

            uint *     pData  = stackalloc uint[16];
            ARGBPixel *pColor = (ARGBPixel *)pData;

            bool isSingle = true, hasAlpha = false, allAlpha = true;

            for (int y = 0, i = 0; y < 4; y++, block += width)
            {
                for (int x = 0; x < 4; i++)
                {
                    pColor[i] = block[x++];
                    if (pData[i] != pData[0])
                    {
                        isSingle = false;
                    }
                    if (pColor[i].A < 0x80)
                    {
                        hasAlpha = true;
                    }
                    else
                    {
                        allAlpha = false;
                    }
                }
            }

            /*
             *  Foreach block:
             *      copy block to buffer
             *      mirror remaning colors?
             *
             *      If block is single color:
             *          run optiml compress?
             *      else
             *          Initialize color set
             *          Compress block using color set
             *
             *
             */

            //BlockDecoder decoder = new BlockDecoder(width, 4, 4, 4);
            //bool isSingle = true, hasAlpha = false, allAlpha = true;
            //for (int i = 0; i < 16; i++)
            //{
            //    int index = decoder[i];
            //    pColor[i] = block[index];
            //    if (pData[i] != pData[0]) isSingle = false;
            //    if (pColor[i].A < 0x80) hasAlpha = true;
            //    else allAlpha = false;
            //}

            //bool isSingle = true, hasAlpha = false, allAlpha = true;
            //ARGBPixel* ptr = block;
            //int index = 0;
            //for (int y = 0; y < 4; y++, ptr += width - 4)
            //{
            //    for (int x = 0; x < 4; x++, ptr++, index++)
            //    {
            //        pColor[index] = block[(y * width) + x];
            //        if (pData[0] != pData[index]) isSingle = false;
            //        if (pColor[index].A < 0x80) hasAlpha = true;
            //        else allAlpha = false;
            //    }
            //}

            //if (allAlpha)
            //{
            //    p._root0._data = 0;
            //    p._root1._data = 0xFFFF;
            //    p._lookup = 0xFFFFFFFF;
            //}
            //else if (isSingle)
            //{
            //    p._root0 = (RGB565Pixel)(*block);
            //    p._root1._data = 0;
            //    p._lookup = 0x00000000;
            //}
            //else
            //{
            uint *     palData = stackalloc uint[4];
            ARGBPixel *palCol  = (ARGBPixel *)palData;

            int bestDist = -1;

            for (int i = 0; i < 16; i++)
            {
                ARGBPixel p1 = pColor[i];
                for (int x = i + 1; x < 16; x++)
                {
                    ARGBPixel p2 = pColor[x];
                    int       d  = p1.DistanceTo(p2);
                    if (d > bestDist)
                    {
                        bestDist  = d;
                        palCol[2] = p1;
                        palCol[3] = p2;
                    }
                }
            }

            wRGB565Pixel smax = (wRGB565Pixel)palCol[2];
            wRGB565Pixel smin = (wRGB565Pixel)palCol[3];

            if (smax < smin)
            {
                smax = (wRGB565Pixel)palCol[3]; smin = (wRGB565Pixel)palCol[2];
            }

            if (hasAlpha)
            {
                p._root0  = smin;
                p._root1  = smax;
                palCol[0] = (ARGBPixel)smin;
                palCol[1] = (ARGBPixel)smax;
                palCol[2] = new ARGBPixel(255, (byte)((palCol[0].R + palCol[1].R) >> 1), (byte)((palCol[0].G + palCol[1].G) >> 1), (byte)((palCol[0].B + palCol[1].B) >> 1));
                palCol[3] = new ARGBPixel();
            }
            else
            {
                p._root0  = smax;
                p._root1  = smin;
                palCol[0] = (ARGBPixel)smax;
                palCol[1] = (ARGBPixel)smin;
                palCol[2] = new ARGBPixel(255, (byte)(((palCol[0].R << 1) + palCol[1].R) / 3), (byte)(((palCol[0].G << 1) + palCol[1].G) / 3), (byte)(((palCol[0].B << 1) + palCol[1].B) / 3));
                palCol[3] = new ARGBPixel(255, (byte)(((palCol[1].R << 1) + palCol[0].R) / 3), (byte)(((palCol[1].G << 1) + palCol[0].G) / 3), (byte)(((palCol[1].B << 1) + palCol[0].B) / 3));
            }

            uint indicies = 0;

            for (int i = 0, shift = 30; i < 16; i++, shift -= 2)
            {
                uint index = 3;
                if (pColor[i].A >= 0x80)
                {
                    int bd = int.MaxValue;
                    for (int x = 0; x < ((hasAlpha) ? 4 : 3); x++)
                    {
                        int dist = palCol[x].DistanceTo(pColor[i]);
                        if (dist < bd)
                        {
                            bd = dist; index = (uint)x;
                        }
                    }
                }
                indicies |= index << shift;
            }
            p._lookup = indicies;

            //p = DXT1Fast.Compress(pColor);
            //}


            return(p);
        }
        private static void optimizeEndPoints4(Vector3* points, CMPRBlock* block)
        {
            float alpha2_sum = 0.0f;
            float beta2_sum = 0.0f;
            float alphabeta_sum = 0.0f;
            Vector3 alphax_sum = new Vector3();
            Vector3 betax_sum = new Vector3();
            uint indices = block->_lookup;

            for (int i = 0, bi = 30; i < 16; ++i, bi -= 2)
            {
                uint bits = indices >> bi;

                float beta = bits & 1;
                if ((bits & 2) != 0) beta = (1 + beta) / 3.0f;
                float alpha = 1.0f - beta;

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

            float denom = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;
            if (Math.Abs(denom) <= 0.0001f)
                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.Clamp(0.0f, 255.0f);
            b.Clamp(0.0f, 255.0f);

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

            if (color0 < color1)
            {
                Vector3 t = a; a = b; b = t;
                VoidPtr.Swap(&color0, &color1);
            }

            //CMPRBlock block = new CMPRBlock();
            block->_root0._data = color0;
            block->_root1._data = color1;
            block->_lookup = computeIndices4(points, &a, &b);
        }
        private static CMPRBlock optimalCompressDXT1(ARGBPixel c)
        {
            CMPRBlock block = new CMPRBlock();

            uint indices = 0xAAAAAAAA;

            block._root0._data = (ushort)((OMatch5[c.R, 0] << 11) | (OMatch6[c.G, 0] << 5) | OMatch5[c.B, 0]);
            block._root1._data = (ushort)((OMatch5[c.R, 1] << 11) | (OMatch6[c.G, 1] << 5) | OMatch5[c.B, 1]);

            if (block._root0 < block._root1)
            {
                VoidPtr.Swap((short*)&block._root0, (short*)&block._root1);
                indices ^= 0x55555555;
            }
            block._lookup = indices;
            return block;
        }
        private static CMPRBlock compressDXT1(ARGBPixel* pBlock)
        {
            float* pointData = stackalloc float[48];
            Vector3* points = (Vector3*)pointData;

            extractColorBlockRGB(pBlock, points);

            // find min and max colors
            Vector3 maxColor, minColor;
            findMinMaxColorsBox(points, 16, &maxColor, &minColor);

            selectDiagonal(points, 16, &maxColor, &minColor);

            insetBBox(&maxColor, &minColor);

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

            if (color0 < color1)
            {
                Vector3 t = maxColor; maxColor = minColor; minColor = t;
                VoidPtr.Swap(&color0, &color1);
            }

            CMPRBlock block = new CMPRBlock();
            block._root0._data = color0;
            block._root1._data = color1;
            block._lookup = computeIndices4(points, &maxColor, &minColor);

            optimizeEndPoints4(points, &block);

            return block;
        }
 public static CMPRBlock optimalCompressDXT1a(ARGBPixel rgba)
 {
     if (rgba.A < 128)
     {
         CMPRBlock block = new CMPRBlock();
         block._root0._data = 0;
         block._root1._data = 0;
         block._lookup = 0xFFFFFFFF;
         return block;
     }
     else
     {
         return optimalCompressDXT1(rgba);
     }
 }
        public static CMPRBlock compressDXT1a(ARGBPixel* img, int imgX, int imgY, int imgW, int imgH)
        {
            uint* pData = stackalloc uint[16];
            ARGBPixel* pBlock = (ARGBPixel*)pData;

            bool hasAlpha = false;
            bool isSingle = true;

            ARGBPixel p;
            ARGBPixel* sPtr = img + (imgX + (imgY * imgW));
            int index = 0;
            for (int y = 0; y < 4; y++)
                for (int x = 0; x < 4; x++)
                {
                    p = sPtr[x + (y * imgW)];
                    pBlock[index++] = p;
                    if (p != pBlock[0]) isSingle = false;
                    if (p.A < 128) hasAlpha = true;
                }

            if (isSingle)
                return optimalCompressDXT1a(sPtr[0]);

            if (!hasAlpha)
                return compressDXT1(pBlock);

            // @@ Handle single RGB, with varying alpha? We need tables for single color compressor in 3 color mode.
            //else if (rgba.isSingleColorNoAlpha()) { ... }

            float* pointData = stackalloc float[48];
            Vector3* points = (Vector3*)pointData;

            // read block
            //Vector3 block[16];
            int num = extractColorBlockRGBA(pBlock, points);

            // find min and max colors
            Vector3 maxColor, minColor;
            findMinMaxColorsBox(points, num, &maxColor, &minColor);

            selectDiagonal(points, num, &maxColor, &minColor);

            insetBBox(&maxColor, &minColor);

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

            if (color0 < color1)
            {
                Vector3 t = maxColor; maxColor = minColor; minColor = t;
                VoidPtr.Swap(&color0, &color1);
            }

            CMPRBlock block = new CMPRBlock();
            block._root0._data = color1;
            block._root1._data = color0;
            block._lookup = computeIndices3(pBlock, &maxColor, &minColor);

            //	optimizeEndPoints(block, dxtBlock);

            return block;
        }