/// <summary>
        /// Dither an image using Floyd-Steinberg's dither.
        /// </summary>
        /// <param name="src">The image buffer (RGBA pixels).</param>
        /// <param name="srcPos">Initial position on the source buffer.</param>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="stride">The distance, in bytes, between rows of the image buffer.</param>
        private void DitherImage(byte[] src, int srcPos, int width, int height, int stride)
        {
            int srcCurrentPos = srcPos;

            for (int y = 0; y < height; y++, srcCurrentPos += stride - width * 4)
            {
                for (int x = 0; x < width; x++, srcCurrentPos += 4)
                {
                    ColorRGBA pixel    = ColorRGBA.Read(src, srcCurrentPos);
                    ColorRGBA newPixel = TrimColor(pixel);
                    newPixel.Write(src, srcCurrentPos);

                    int errorR = pixel.r - newPixel.r;
                    int errorG = pixel.g - newPixel.g;
                    int errorB = pixel.b - newPixel.b;
                    int errorA = pixel.a - newPixel.a;

                    // Distributes error to pixel at (deltaX, deltaY) of current pixel, using the specified weight.

                    /* Floyd-Steinberg dither.
                     * Distributes the error at pixel X to neighboring pixels using those weights:
                     *       -1  0  1
                     *      /--------
                     *   -1 | 0  0  0
                     *    0 | 0  X  7
                     *    1 | 3  5  1
                     */
                    DistributeDitheringError(src, srcCurrentPos, width, height, stride, x, y, 1, 0, 7.0f / 16.0f, errorR, errorG, errorB, errorA);
                    DistributeDitheringError(src, srcCurrentPos, width, height, stride, x, y, -1, 1, 3.0f / 16.0f, errorR, errorG, errorB, errorA);
                    DistributeDitheringError(src, srcCurrentPos, width, height, stride, x, y, 0, 1, 5.0f / 16.0f, errorR, errorG, errorB, errorA);
                    DistributeDitheringError(src, srcCurrentPos, width, height, stride, x, y, 1, 1, 1.0f / 16.0f, errorR, errorG, errorB, errorA);
                }
            }
        }
 static void DistributeDitheringError(byte[] src, int srcPos, int width, int height, int stride, int x, int y,
                                      int deltaX, int deltaY, float weight, int errorR, int errorG, int errorB, int errorA)
 {
     if ((x + deltaX) < width && (y + deltaY) < height)
     {
         ColorRGBA origPx = ColorRGBA.Read(src, srcPos + 4 * deltaX + stride * deltaY);
         ColorRGBA newPx  = new ColorRGBA(Utils.Trim8((int)(origPx.r + errorR * weight)),
                                          Utils.Trim8((int)(origPx.g + errorG * weight)),
                                          Utils.Trim8((int)(origPx.b + errorB * weight)),
                                          Utils.Trim8((int)(origPx.a + errorA * weight)));
         newPx.Write(src, srcPos + 4 * deltaX + stride * deltaY);
     }
 }
        /// <summary>
        /// Convert an image to grayscale.
        /// </summary>
        /// <param name="src">The image buffer (RGBA pixels).</param>
        /// <param name="srcPos">Initial position on the source buffer.</param>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="stride">The distance, in bytes, between rows of the image buffer.</param>
        static void GrayscaleImage(byte[] src, int srcPos, int width, int height, int stride)
        {
            int pos = srcPos;

            for (int y = 0; y < height; y++, pos += stride - width * 4)
            {
                for (int x = 0; x < width; x++, pos += 4)
                {
                    ColorRGBA srcColor       = ColorRGBA.Read(src, pos);
                    ColorRGBA grayscaleColor = ColorRGBA.FromIntensityAlpha(srcColor.Intensity(), srcColor.a);
                    grayscaleColor.Write(src, pos);
                }
            }
        }
        protected override void EncodeTile(byte[] src, int srcPos, int stride, byte[] dst, int dstPos)
        {
            int srcCurrentPos = srcPos;

            for (int ty = 0; ty < TileHeight; ty++, srcCurrentPos += stride - TileWidth * 4)
            {
                for (int tx = 0; tx < TileWidth; tx++, srcCurrentPos += 4)
                {
                    ColorRGBA color = ColorRGBA.Read(src, srcCurrentPos);
                    int       dstMv = dstPos + ty * 8 + tx * 2;

                    dst[dstMv + 0]  = color.a;
                    dst[dstMv + 1]  = color.r;
                    dst[dstMv + 32] = color.g;
                    dst[dstMv + 33] = color.b;
                }
            }
        }