/// <summary> /// Loads a KTX file from a stream. /// </summary> public static KtxFile Load(Stream s) { using (BinaryReader br = new BinaryReader(s, UTF8, true)) { KtxHeader header = br.ReadStruct <KtxHeader>(); if (header.NumberOfArrayElements > 0) { throw new NotSupportedException("KTX files with arrays are not supported."); } KtxFile ktx = new KtxFile(header); int keyValuePairBytesRead = 0; while (keyValuePairBytesRead < header.BytesOfKeyValueData) { KtxKeyValuePair kvp = KtxKeyValuePair.ReadKeyValuePair(br, out int read); keyValuePairBytesRead += read; ktx.KeyValuePairs.Add(kvp); } uint numberOfFaces = Math.Max(1, header.NumberOfFaces); ktx.MipMaps.Capacity = (int)header.NumberOfMipmapLevels; for (uint mipLevel = 0; mipLevel < header.NumberOfMipmapLevels; mipLevel++) { uint imageSize = br.ReadUInt32(); uint mipWidth = header.PixelWidth / (uint)(Math.Pow(2, mipLevel)); uint mipHeight = header.PixelHeight / (uint)(Math.Pow(2, mipLevel)); ktx.MipMaps.Add(new KtxMipmap(imageSize, mipWidth, mipHeight, numberOfFaces)); bool cubemap = header.NumberOfFaces > 1 && header.NumberOfArrayElements == 0; for (uint face = 0; face < numberOfFaces; face++) { byte[] faceData = br.ReadBytes((int)imageSize); ktx.MipMaps[(int)mipLevel].Faces[(int)face] = new KtxMipFace(faceData, mipWidth, mipHeight); if (cubemap) { uint cubePadding = 0u; cubePadding = 3 - ((imageSize + 3) % 4); br.SkipPadding(cubePadding); } } uint mipPaddingBytes = 3 - ((imageSize + 3) % 4); br.SkipPadding(mipPaddingBytes); } return(ktx); } }
/// <summary> /// Writes this ktx file into a stream. /// </summary> public void Write(Stream s) { if (MipMaps.Count < 1 || MipMaps[0].NumberOfFaces < 1) { throw new InvalidOperationException("The KTX structure should have at least 1 mipmap level and 1 Face before writing to file."); } using (BinaryWriter bw = new BinaryWriter(s, UTF8, true)) { uint bytesOfKeyValueData = (uint)KeyValuePairs.Sum(x => x.GetSizeWithPadding()); Header.BytesOfKeyValueData = bytesOfKeyValueData; Header.NumberOfFaces = MipMaps[0].NumberOfFaces; Header.NumberOfMipmapLevels = (uint)MipMaps.Count; Header.NumberOfArrayElements = 0; if (!Header.VerifyHeader()) { throw new InvalidOperationException("Please verify the header validity before writing to file."); } bw.WriteStruct(Header); foreach (KtxKeyValuePair keyValuePair in KeyValuePairs) { KtxKeyValuePair.WriteKeyValuePair(bw, keyValuePair); } for (int mip = 0; mip < Header.NumberOfMipmapLevels; mip++) { uint imageSize = MipMaps[mip].SizeInBytes; bw.Write(imageSize); bool isCubemap = Header.NumberOfFaces == 6 && Header.NumberOfArrayElements == 0; for (int f = 0; f < Header.NumberOfFaces; f++) { bw.Write(MipMaps[mip].Faces[f].Data); uint cubePadding = 0u; if (isCubemap) { cubePadding = 3 - ((imageSize + 3) % 4); } bw.AddPadding(cubePadding); } uint mipPaddingBytes = 3 - ((imageSize + 3) % 4); bw.AddPadding(mipPaddingBytes); } } }
public static uint WriteKeyValuePair(BinaryWriter bw, KtxKeyValuePair pair) { int keySpanLength = UTF8.GetByteCount(pair.Key); Span <byte> keySpan = stackalloc byte[keySpanLength]; Span <byte> valueSpan = pair.Value; uint totalSize = (uint)(keySpan.Length + 1 + valueSpan.Length); int paddingBytes = (int)(3 - ((totalSize + 3) % 4)); bw.Write(totalSize); bw.Write(keySpan); bw.Write((byte)0); bw.Write(valueSpan); return((uint)(totalSize + paddingBytes)); }