/// <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; } } }