public static RgbValue YuvToRgb(YuvValue yuv) { RgbValue rgb = new RgbValue(); rgb.r = (byte)Clip(yuv.y + (91881 * yuv.v >> 16) - 179); rgb.g = (byte)Clip(yuv.y - ((22544 * yuv.u + 46793 * yuv.v) >> 16) + 135); rgb.b = (byte)Clip(yuv.y + (116129 * yuv.u >> 16) - 226); return(rgb); }
// https://stackoverflow.com/questions/1737726/how-to-perform-rgb-yuv-conversion-in-c-c public static YuvValue RgbToYuv(RgbValue rgb) { YuvValue yuv = new YuvValue(); yuv.y = Clip((19595 * rgb.r + 38470 * rgb.g + 7471 * rgb.b) >> 16); yuv.u = Clip((36962 * (rgb.b - Clip((19595 * rgb.r + 38470 * rgb.g + 7471 * rgb.b) >> 16)) >> 16) + 128); yuv.v = Clip((46727 * (rgb.r - Clip((19595 * rgb.r + 38470 * rgb.g + 7471 * rgb.b) >> 16)) >> 16) + 128); return(yuv); }
/// Borrowed from Microprose: Magic The Gathering (1997) /// (can't identify if its borrowed from somewhere else :-)) /// Buggy. public static YuvValue RgbToYuv2(RgbValue rgb) { YuvValue yuv = new YuvValue(); yuv.y = (64 * rgb.g + 8 * rgb.r + 5 * rgb.b) / 28; yuv.u = (4 * rgb.b - yuv.y + 0x400) / 2; yuv.v = (32 * rgb.r - 8 * yuv.y + 0x1998) / 13; return(yuv); }
public static RgbValue YuvToRgb2(YuvValue yuv) { RgbValue rgb = new RgbValue(); int r = yuv.y + yuv.v + yuv.v / 2 + yuv.v / 8 - 0x333; int b = yuv.y + yuv.u * 2 - 0x400; int g = yuv.y * 2 - yuv.y / 4 - r / 2 - b / 4 - b / 16; rgb.r = (byte)Clip(r / 4); rgb.g = (byte)Clip(g / 4); rgb.b = (byte)Clip(b / 4); return(rgb); }
public byte [] Compress(Image image, int iterations = 3) { int width = image.Width; int height = image.Height; YuvValue [] yuvSurface = new YuvValue[width * height]; /// RGB -> Yuv Bitmap bitmap = new Bitmap(image); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { Color color = bitmap.GetPixel(x, y); RgbValue rgb = new RgbValue(); rgb.r = color.R; rgb.g = color.G; rgb.b = color.B; yuvSurface[y * width + x] = Yuv.RgbToYuv(rgb); } } /// Lerp U/V /// Seems doesnt't affect compression ratio much... //LerpUV(width, height, yuvSurface); /// Extract components and align on power of 2 int alignedSize = UpperPowerOfTwo(Math.Max(width, height)); Int32[] yvalues = new Int32[alignedSize * alignedSize]; Int32[] uvalues = new Int32[alignedSize * alignedSize]; Int32[] vvalues = new Int32[alignedSize * alignedSize]; for (int y = 0; y < alignedSize; y++) { for (int x = 0; x < alignedSize; x++) { if (y >= height || x >= width) { yvalues[y * alignedSize + x] = -1; uvalues[y * alignedSize + x] = -1; vvalues[y * alignedSize + x] = -1; } else { yvalues[y * alignedSize + x] = yuvSurface[y * width + x].y; uvalues[y * alignedSize + x] = yuvSurface[y * width + x].u; vvalues[y * alignedSize + x] = yuvSurface[y * width + x].v; } } } /// Adjust border pixels by last color, to avoid edge glitching if ((width & 1) != 0) { for (int y = 0; y < height; y++) { yvalues[y * alignedSize + width] = yvalues[y * alignedSize + width - 1]; uvalues[y * alignedSize + width] = uvalues[y * alignedSize + width - 1]; vvalues[y * alignedSize + width] = vvalues[y * alignedSize + width - 1]; } } if ((height & 1) != 0) { for (int x = 0; x < width; x++) { yvalues[height * alignedSize + x] = yvalues[(height - 1) * alignedSize + x]; uvalues[height * alignedSize + x] = uvalues[(height - 1) * alignedSize + x]; vvalues[height * alignedSize + x] = vvalues[(height - 1) * alignedSize + x]; } } /// Wavelet Wavelet.FWT(yvalues, alignedSize, alignedSize, iterations); Wavelet.FWT(uvalues, alignedSize, alignedSize, iterations); Wavelet.FWT(vvalues, alignedSize, alignedSize, iterations); /// Gzip byte[] ycomp = Gzip.Compress(ToByteArray(yvalues)); byte[] ucomp = Gzip.Compress(ToByteArray(uvalues)); byte[] vcomp = Gzip.Compress(ToByteArray(vvalues)); /// Header Int32[] header = new Int32[8]; header[0] = alignedSize; header[1] = width; header[2] = height; header[3] = iterations; header[4] = ycomp.Length; header[5] = ucomp.Length; header[6] = vcomp.Length; header[7] = WayaSignature; /// Waya Image byte[] headerByte = ToByteArray(header); byte[] waveImage = new byte[headerByte.Length + ycomp.Length + ucomp.Length + vcomp.Length]; headerByte.CopyTo(waveImage, 0); ycomp.CopyTo(waveImage, headerByte.Length); ucomp.CopyTo(waveImage, headerByte.Length + ycomp.Length); vcomp.CopyTo(waveImage, headerByte.Length + ycomp.Length + ucomp.Length); return(waveImage); }
public Image Decompress(byte [] data) { byte[] headerByte = new byte[8 * 4]; Array.Copy(data, 0, headerByte, 0, headerByte.Length); Int32[] header = ToIntArray(headerByte); int alignedSize = header[0]; int width = header[1]; int height = header[2]; int iterations = header[3]; int ycompSize = header[4]; int ucompSize = header[5]; int vcompSize = header[6]; int signature = header[7]; if (signature != WayaSignature) { return(null); } Bitmap bitmap = new Bitmap(width, height); YuvValue[] yuvSurface = new YuvValue[width * height]; byte[] ycomp = new byte[ycompSize]; byte[] ucomp = new byte[ucompSize]; byte[] vcomp = new byte[vcompSize]; Array.Copy(data, headerByte.Length, ycomp, 0, ycompSize); Array.Copy(data, headerByte.Length + ycompSize, ucomp, 0, ucompSize); Array.Copy(data, headerByte.Length + ycompSize + ucompSize, vcomp, 0, vcompSize); /// Gzip Int32[] yvalues = ToIntArray(Gzip.Decompress(ycomp)); Int32[] uvalues = ToIntArray(Gzip.Decompress(ucomp)); Int32[] vvalues = ToIntArray(Gzip.Decompress(vcomp)); /// Wavelet Wavelet.IWT(yvalues, alignedSize, alignedSize, iterations); Wavelet.IWT(uvalues, alignedSize, alignedSize, iterations); Wavelet.IWT(vvalues, alignedSize, alignedSize, iterations); /// Extract components for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { yuvSurface[y * width + x] = new YuvValue(); yuvSurface[y * width + x].y = yvalues[y * alignedSize + x]; yuvSurface[y * width + x].u = uvalues[y * alignedSize + x]; yuvSurface[y * width + x].v = vvalues[y * alignedSize + x]; } } /// Yuv -> RGB for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { RgbValue rgb = Yuv.YuvToRgb(yuvSurface[y * width + x]); Color color = Color.FromArgb(rgb.r, rgb.g, rgb.b); bitmap.SetPixel(x, y, color); } } return(bitmap); }