public byte[] EncodeRgba4Bpp(Texture2D bitmap) { if (bitmap.height != bitmap.width) { Debug.LogError("Texture isn't square!"); } if (!((bitmap.height & (bitmap.height - 1)) == 0)) { Debug.LogError("Texture resolution must be 2^N!"); } int size = bitmap.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.GetMinMaxColorsWithAlpha(bitmap, 4 * x, 4 * y, ref minColor, ref maxColor); PvrtcPacket packet = packets[this.GetMortonNumber(x, y)]; packet.SetPunchthroughAlpha(false); packet.SetColorA(minColor); packet.SetColorB(maxColor); } } 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]; Vector4Int ca = p0.GetColorRgbaA() * currentFactors[0] + p1.GetColorRgbaA() * currentFactors[1] + p2.GetColorRgbaA() * currentFactors[2] + p3.GetColorRgbaA() * currentFactors[3]; Vector4Int cb = p0.GetColorRgbaB() * currentFactors[0] + p1.GetColorRgbaB() * currentFactors[1] + p2.GetColorRgbaB() * currentFactors[2] + p3.GetColorRgbaB() * currentFactors[3]; Color32 pixel = (Color32)bitmap.GetPixel(4 * x + px, 4 * y + py); Vector4Int d = cb - ca; Vector4Int p = new Vector4Int(pixel.r * 16, pixel.g * 16, pixel.b * 16, pixel.a * 16); Vector4Int 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(Vector4.Dot(v, d)) * 16; int lengthSquared = d % d; //Mathf.RoundToInt(Vector4.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 DecodeRgba4Bpp(byte[] data, int width) { int size = width; int blocks = size / 4; int blockMask = blocks - 1; Texture2D returnValue = new Texture2D(size, size, TextureFormat.RGBA32, 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]; Vector4Int ca = p0.GetColorRgbaA() * currentFactors[0] + p1.GetColorRgbaA() * currentFactors[1] + p2.GetColorRgbaA() * currentFactors[2] + p3.GetColorRgbaA() * currentFactors[3]; Vector4Int cb = p0.GetColorRgbaB() * currentFactors[0] + p1.GetColorRgbaB() * currentFactors[1] + p2.GetColorRgbaB() * currentFactors[2] + p3.GetColorRgbaB() * 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)); c.a = (byte)(Mathf.RoundToInt((ca.w * currentWeights[2] + cb.w * currentWeights[3]) >> 7)); returnValue.SetPixel((px + x * 4), (py + y * 4), c); mod >>= 2; currentFactorIndex++; } } } } returnValue.Apply(); return(returnValue); }