public MemoryStream EncodeTexture() { // Calculate what the length of the texture will be int textureLength = 16 + (TextureWidth * TextureHeight * DataCodec.Bpp / 8); if (HasGlobalIndex) { textureLength += 16; } if (DataCodec.PaletteEntries != 0 && !DataCodec.NeedsExternalPalette) { textureLength += (DataCodec.PaletteEntries * PixelCodec.Bpp / 8); } // Calculate the mipmap padding (if the texture contains mipmaps) int mipmapPadding = 0; if (DataCodec.HasMipmaps) { if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { // A 1x1 mipmap takes up as much space as a 2x1 mipmap // There are also 4 extra bytes at the end of the file mipmapPadding = (DataCodec.Bpp) >> 3; textureLength += 4; } else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT) { // A 1x1 mipmap takes up as much space as a 2x2 mipmap mipmapPadding = (3 * DataCodec.Bpp) >> 3; } textureLength += mipmapPadding; for (int size = 1; size < TextureWidth; size <<= 1) { textureLength += Math.Max((size * size * DataCodec.Bpp) >> 3, 1); } } MemoryStream destination = new MemoryStream(textureLength); // Write out the GBIX header (if we are including one) if (HasGlobalIndex) { destination.WriteByte((byte)'G'); destination.WriteByte((byte)'B'); destination.WriteByte((byte)'I'); destination.WriteByte((byte)'X'); PTStream.WriteUInt32(destination, 8); PTStream.WriteUInt32(destination, GlobalIndex); PTStream.WriteUInt32(destination, 0); } // Write out the PVRT header destination.WriteByte((byte)'P'); destination.WriteByte((byte)'V'); destination.WriteByte((byte)'R'); destination.WriteByte((byte)'T'); if (HasGlobalIndex) { PTStream.WriteInt32(destination, textureLength - 24); } else { PTStream.WriteInt32(destination, textureLength - 8); } destination.WriteByte((byte)PixelFormat); destination.WriteByte((byte)DataFormat); PTStream.WriteUInt16(destination, 0); PTStream.WriteUInt16(destination, TextureWidth); PTStream.WriteUInt16(destination, TextureHeight); // If we have an internal palette, write it if (DataCodec.PaletteEntries != 0 && !DataCodec.NeedsExternalPalette) { byte[] palette = PixelCodec.EncodePalette(m_texturePalette, DataCodec.PaletteEntries); destination.Write(palette, 0, palette.Length); } // Write out any mipmaps if (DataCodec.HasMipmaps) { // Write out any padding bytes before the 1x1 mipmap for (int i = 0; i < mipmapPadding; i++) { destination.WriteByte(0); } for (int size = 1; size < TextureWidth; size <<= 1) { byte[] mipmapDecodedData = null; if (DataCodec.NeedsExternalPalette) { if (DataCodec.VQ) { mipmapDecodedData = BitmapToRawVQResized(m_decodedBitmap, size, 1, m_codeBook); } else { mipmapDecodedData = BitmapToRawIndexedResized(m_decodedBitmap, size, 1, m_palette); } } else { mipmapDecodedData = BitmapToRawResized(m_decodedBitmap, size, 1); } byte[] mipmapTextureData = DataCodec.Encode(mipmapDecodedData, 0, size, size); destination.Write(mipmapTextureData, 0, mipmapTextureData.Length); } } // Write the texture data byte[] textureData = DataCodec.Encode(m_decodedData, TextureWidth, TextureHeight, null); destination.Write(textureData, 0, textureData.Length); // If the data format is square twiddled with mipmaps, write out the extra bytes. if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { destination.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); } // Compress the texture if (CompressionFormat != PvrCompressionFormat.NONE) { CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat); if (CompressionCodec != null) { // Ok, we need to convert the current stream to an array, compress it, then write it back to a new stream byte[] buffer = destination.ToArray(); buffer = CompressionCodec.Compress(buffer, (HasGlobalIndex ? 0x20 : 0x10), PixelCodec, DataCodec); destination = new MemoryStream(); destination.Write(buffer, 0, buffer.Length); } } return(destination); }
public void Initalize() { // Determine the offsets of the GBIX (if present) and PVRT header chunks. if (PTMethods.Contains(m_encodedData, 0x00, gbixFourCC)) { GbixOffset = 0x00; PvrtOffset = 0x08 + BitConverter.ToInt32(m_encodedData, GbixOffset + 4); } else if (PTMethods.Contains(m_encodedData, 0x04, gbixFourCC)) { GbixOffset = 0x04; PvrtOffset = 0x0C + BitConverter.ToInt32(m_encodedData, GbixOffset + 4); } else if (PTMethods.Contains(m_encodedData, 0x04, pvrtFourCC)) { GbixOffset = -1; PvrtOffset = 0x04; } else { GbixOffset = -1; PvrtOffset = 0x00; } // Read the global index (if it is present). If it is not present, just set it to 0. if (GbixOffset != -1) { GlobalIndex = BitConverter.ToUInt32(m_encodedData, GbixOffset + 0x08); } else { GlobalIndex = 0; } // Read information about the texture TextureWidth = BitConverter.ToUInt16(m_encodedData, PvrtOffset + 0x0C); TextureHeight = BitConverter.ToUInt16(m_encodedData, PvrtOffset + 0x0E); PixelFormat = (PvrPixelFormat)m_encodedData[PvrtOffset + 0x08]; DataFormat = (PvrDataFormat)m_encodedData[PvrtOffset + 0x09]; // Get the codecs and make sure we can decode using them PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat); DataCodec = PvrDataCodec.GetDataCodec(DataFormat); if (DataCodec != null && PixelCodec != null) { DataCodec.PixelCodec = PixelCodec; } // Set the number of palette entries // The number in a Small Vq encoded texture various based on its size m_paletteEntries = DataCodec.PaletteEntries; if (DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL || DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL_MIPMAP) { if (TextureWidth <= 16) { m_paletteEntries = 64; // Actually 16 } else if (TextureWidth <= 32) { m_paletteEntries = 256; // Actually 64 } else if (TextureWidth <= 64) { m_paletteEntries = 512; // Actually 128 } else { m_paletteEntries = 1024; // Actually 256 } } // Set the palette and data offsets if (m_paletteEntries == 0 || DataCodec.NeedsExternalPalette) { m_paletteOffset = -1; m_dataOffset = PvrtOffset + 0x10; } else { m_paletteOffset = PvrtOffset + 0x10; m_dataOffset = m_paletteOffset + (m_paletteEntries * (PixelCodec.Bpp >> 3)); } // Get the compression format and determine if we need to decompress this texture CompressionFormat = GetCompressionFormat(m_encodedData, PvrtOffset, m_dataOffset); CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat); if (CompressionFormat != PvrCompressionFormat.NONE && CompressionCodec != null) { m_encodedData = CompressionCodec.Decompress(m_encodedData, m_dataOffset, PixelCodec, DataCodec); // Now place the offsets in the appropiate area if (CompressionFormat == PvrCompressionFormat.RLE) { if (GbixOffset != -1) { GbixOffset -= 4; } PvrtOffset -= 4; if (m_paletteOffset != -1) { m_paletteOffset -= 4; } m_dataOffset -= 4; } } // If the texture contains mipmaps, gets the offsets of them if (DataCodec.HasMipmaps) { m_mipmapOffsets = new int[(int)Math.Log(TextureWidth, 2) + 1]; int mipmapOffset = 0; // Calculate the padding for the first mipmap offset if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { // A 1x1 mipmap takes up as much space as a 2x1 mipmap mipmapOffset = (DataCodec.Bpp) >> 3; } else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT) { // A 1x1 mipmap takes up as much space as a 2x2 mipmap mipmapOffset = (3 * DataCodec.Bpp) >> 3; } for (int i = m_mipmapOffsets.Length - 1, size = 1; i >= 0; i--, size <<= 1) { m_mipmapOffsets[i] = mipmapOffset; mipmapOffset += Math.Max((size * size * DataCodec.Bpp) >> 3, 1); } } }