public byte[] EncodeRgb4Bpp(Color[] pixels, int width, int height) { if (height != width) { Debug.LogError("Texture isn't square!"); } if (!((height & (height - 1)) == 0)) { Debug.LogError("Texture resolution must be 2^N!"); } int size = width; int blocks = size / 4; int blockMask = blocks - 1; PvrtcPacket[] packets = new PvrtcPacket[blocks * blocks]; for (int i = 0; i < packets.Length; i++) { packets[i] = new PvrtcPacket(); } for (int y = 0; y < blocks; ++y) { for (int x = 0; x < blocks; ++x) { Color32 minColor = Color.white; // white is same as all 255, should be same as Color.max Color32 maxColor = Color.clear; // clear is same as all 0, should be same as Color.min this.GetMinMaxColors(pixels, width, 4 * x, 4 * y, ref minColor, ref maxColor); PvrtcPacket packet = packets[this.GetMortonNumber(x, y)]; packet.SetPunchthroughAlpha(false); packet.SetColorA(minColor.r, minColor.g, minColor.b); packet.SetColorB(maxColor.r, maxColor.g, maxColor.b); } } int currentFactorIndex = 0; for (int y = 0; y < blocks; ++y) { for (int x = 0; x < blocks; ++x) { currentFactorIndex = 0; uint modulationData = 0; for (int py = 0; py < 4; ++py) { int yOffset = (py < 2) ? -1 : 0; int y0 = (y + yOffset) & blockMask; int y1 = (y0 + 1) & blockMask; for (int px = 0; px < 4; ++px) { int xOffset = (px < 2) ? -1 : 0; int x0 = (x + xOffset) & blockMask; int x1 = (x0 + 1) & blockMask; PvrtcPacket p0 = packets[this.GetMortonNumber(x0, y0)]; PvrtcPacket p1 = packets[this.GetMortonNumber(x1, y0)]; PvrtcPacket p2 = packets[this.GetMortonNumber(x0, y1)]; PvrtcPacket p3 = packets[this.GetMortonNumber(x1, y1)]; byte[] currentFactors = PvrtcPacket.BILINEAR_FACTORS[currentFactorIndex]; Vector3Int ca = p0.GetColorRgbA() * currentFactors[0] + p1.GetColorRgbA() * currentFactors[1] + p2.GetColorRgbA() * currentFactors[2] + p3.GetColorRgbA() * currentFactors[3]; Vector3Int cb = p0.GetColorRgbB() * currentFactors[0] + p1.GetColorRgbB() * currentFactors[1] + p2.GetColorRgbB() * currentFactors[2] + p3.GetColorRgbB() * currentFactors[3]; Color32 pixel = pixels[(4 * x + px) * width + 4 * y + py]; //Color32 pixel = bitmap.GetPixel(4*x + px, 4*y + py); Vector3Int d = cb - ca; Vector3Int p = new Vector3Int(pixel.r * 16, pixel.g * 16, pixel.b * 16); Vector3Int v = p - ca; // PVRTC uses weightings of 0, 3/8, 5/8 and 1 // The boundaries for these are 3/16, 1/2 (=8/16), 13/16 int projection = (v % d) * 16; // Mathf.RoundToInt(Vector3.Dot(v, d)) * 16; int lengthSquared = d % d; //Mathf.RoundToInt(Vector3.Dot(d,d)); if (projection > 3 * lengthSquared) { modulationData++; } if (projection > 8 * lengthSquared) { modulationData++; } if (projection > 13 * lengthSquared) { modulationData++; } modulationData = RotateRight(modulationData, 2); currentFactorIndex++; } } PvrtcPacket packet = packets[this.GetMortonNumber(x, y)]; packet.SetModulationData(modulationData); } } byte[] returnValue = new byte[size * size / 2]; // Create final byte array from PVRTC packets for (int i = 0; i < packets.Length; i++) { byte[] tempArray = packets[i].GetAsByteArray(); Buffer.BlockCopy(tempArray, 0, returnValue, 8 * i, 8); } return(returnValue); }
// This function assumes that input texture is square! (width == height) public Texture2D DecodeRgb4Bpp(byte[] data, int width) { int size = width; int blocks = size / 4; int blockMask = blocks - 1; Texture2D returnValue = new Texture2D(size, size, TextureFormat.RGB24, false, true); PvrtcPacket[] packets = new PvrtcPacket[blocks * blocks]; byte[] eightBytes = new byte[8]; for (int i = 0; i < packets.Length; i++) { packets[i] = new PvrtcPacket(); Buffer.BlockCopy(data, i * 8, eightBytes, 0, 8); packets[i].InitFromBytes(eightBytes); } int currentFactorIndex = 0; for (int y = 0; y < blocks; ++y) { for (int x = 0; x < blocks; ++x) { currentFactorIndex = 0; PvrtcPacket packet = packets[this.GetMortonNumber(x, y)]; uint mod = packet.GetModulationData(); for (int py = 0; py < 4; ++py) { int yOffset = (py < 2) ? -1 : 0; int y0 = (y + yOffset) & blockMask; int y1 = (y0 + 1) & blockMask; for (int px = 0; px < 4; ++px) { int xOffset = (px < 2) ? -1 : 0; int x0 = (x + xOffset) & blockMask; int x1 = (x0 + 1) & blockMask; PvrtcPacket p0 = packets[this.GetMortonNumber(x0, y0)]; PvrtcPacket p1 = packets[this.GetMortonNumber(x1, y0)]; PvrtcPacket p2 = packets[this.GetMortonNumber(x0, y1)]; PvrtcPacket p3 = packets[this.GetMortonNumber(x1, y1)]; byte[] currentFactors = PvrtcPacket.BILINEAR_FACTORS[currentFactorIndex]; Vector3Int ca = p0.GetColorRgbA() * currentFactors[0] + p1.GetColorRgbA() * currentFactors[1] + p2.GetColorRgbA() * currentFactors[2] + p3.GetColorRgbA() * currentFactors[3]; Vector3Int cb = p0.GetColorRgbB() * currentFactors[0] + p1.GetColorRgbB() * currentFactors[1] + p2.GetColorRgbB() * currentFactors[2] + p3.GetColorRgbB() * currentFactors[3]; byte[] currentWeights = PvrtcPacket.WEIGHTS[4 * packet.GetPunchthroughAlpha() + mod & 3]; Color32 c = Color.white; c.r = (byte)(Mathf.RoundToInt((ca.x * currentWeights[0] + cb.x * currentWeights[1])) >> 7); c.g = (byte)(Mathf.RoundToInt((ca.y * currentWeights[0] + cb.y * currentWeights[1])) >> 7); c.b = (byte)(Mathf.RoundToInt((ca.z * currentWeights[0] + cb.z * currentWeights[1])) >> 7); returnValue.SetPixel((px + x * 4), (py + y * 4), c); mod >>= 2; currentFactorIndex++; } } } } returnValue.Apply(); return(returnValue); }