-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Czi Color Zone Index Map reader and writer
Can read, unpack , pack and write Czi files
- Loading branch information
Showing
6 changed files
with
245 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using Bitter; | ||
using FauFau.Util; | ||
using SharpCompress; | ||
using SharpCompress.Compressors; | ||
using SharpCompress.Compressors.Deflate; | ||
|
||
namespace FauFau.Formats | ||
{ | ||
public class Czi : BinaryWrapper | ||
{ | ||
public Header Head; | ||
public List<MipInfo> MipInfos = new (); | ||
public List<CompressedBlock> CompressedBlocks = new (); | ||
|
||
public Czi() { } | ||
public Czi(string filePath) | ||
{ | ||
Load(filePath); | ||
} | ||
|
||
public static Czi CreateMaskCzi(int width, int height) | ||
{ | ||
var czi = new Czi(); | ||
czi.Head = new Header | ||
{ | ||
Magic = "CZIM", | ||
Version = 3, | ||
Width = width, | ||
Height = height, | ||
Unk = 0, | ||
PatternFlags = 0, | ||
NumMipLevels = 0 | ||
}; | ||
|
||
return czi; | ||
} | ||
|
||
public void Load(string filePath) | ||
{ | ||
var data = File.ReadAllBytes(filePath); | ||
using var bs = new BinaryStream(new MemoryStream(data)); | ||
|
||
Read(bs); | ||
} | ||
|
||
public void Save(string path) | ||
{ | ||
using var fs = new FileStream(path, FileMode.Create); | ||
Write(fs); | ||
} | ||
|
||
public override void Read(BinaryStream bs) | ||
{ | ||
Head = bs.Read.Type<Header>(); | ||
MipInfos = bs.Read.TypeList<MipInfo>(Head.NumMipLevels); | ||
CompressedBlocks = bs.Read.TypeList<CompressedBlock>(Head.NumMipLevels); | ||
} | ||
|
||
public override void Write(BinaryStream bs) | ||
{ | ||
bs.Write.Type(Head); | ||
bs.Write.TypeList(MipInfos); | ||
bs.Write.TypeList(CompressedBlocks); | ||
} | ||
|
||
public byte[] GetMipDecompressed(int idx) | ||
{ | ||
var mipInfo = MipInfos[idx]; | ||
var compressedBlock = CompressedBlocks[idx]; | ||
|
||
byte[] decompressedData = new byte[mipInfo.Size]; | ||
var msIn = new MemoryStream(compressedBlock.Data); | ||
using var zlib = new ZlibStream(msIn, CompressionMode.Decompress); | ||
zlib.ReadFully(decompressedData); | ||
|
||
return decompressedData; | ||
} | ||
|
||
public void AddMipLevel(ReadOnlySpan<byte> data) | ||
{ | ||
var mipInfo = new MipInfo | ||
{ | ||
Offset = Head.NumMipLevels > 0 ? MipInfos[Head.NumMipLevels - 1].Offset + MipInfos[Head.NumMipLevels - 1].Size : 0, | ||
Size = data.Length | ||
}; | ||
|
||
using var inMs = new BinaryStream(new MemoryStream(data.ToArray())); | ||
using var outMs = new MemoryStream(); | ||
using var outBs = new BinaryStream(outMs); | ||
outBs.Write.Byte(0x78); | ||
outBs.Write.Byte(0x9c); | ||
Common.Deflate(inMs, outBs, CompressionLevel.Default); | ||
outBs.ByteOrder = BinaryStream.Endianness.BigEndian; | ||
outBs.Write.UInt(Checksum.Adler32(data)); | ||
outBs.ByteOrder = BinaryStream.Endianness.LittleEndian; | ||
outBs.Flush(); | ||
|
||
var outData = outMs.ToArray(); | ||
|
||
var block = new CompressedBlock() | ||
{ | ||
Length = outData.Length, | ||
Data = outData | ||
}; | ||
|
||
MipInfos.Add(mipInfo); | ||
CompressedBlocks.Add(block); | ||
Head.NumMipLevels++; | ||
} | ||
|
||
public class Header : ReadWrite | ||
{ | ||
public string Magic; | ||
public int Version; | ||
public int Width; | ||
public int Height; | ||
public byte PatternFlags; | ||
public byte Unk; | ||
public int NumMipLevels; | ||
|
||
public void Read(BinaryStream bs) | ||
{ | ||
Magic = bs.Read.String(4); | ||
Version = bs.Read.Int(); | ||
Width = bs.Read.Int(); | ||
Height = bs.Read.Int(); | ||
PatternFlags = bs.Read.Byte(); | ||
Unk = bs.Read.Byte(); | ||
NumMipLevels = bs.Read.Int(); | ||
} | ||
|
||
public void Write(BinaryStream bs) | ||
{ | ||
bs.Write.String(Magic); | ||
bs.Write.Int(Version); | ||
bs.Write.Int(Width); | ||
bs.Write.Int(Height); | ||
bs.Write.Byte(PatternFlags); | ||
bs.Write.Byte(Unk); | ||
bs.Write.Int(NumMipLevels); | ||
} | ||
} | ||
|
||
public class MipInfo : ReadWrite | ||
{ | ||
public int Offset; // The combined size of all the previous mip infos added | ||
public int Size; | ||
|
||
public void Read(BinaryStream bs) | ||
{ | ||
Offset = bs.Read.Int(); | ||
Size = bs.Read.Int(); | ||
} | ||
|
||
public void Write(BinaryStream bs) | ||
{ | ||
bs.Write.Int(Offset); | ||
bs.Write.Int(Size); | ||
} | ||
} | ||
|
||
public class CompressedBlock : ReadWrite | ||
{ | ||
public int Length; | ||
public byte[] Data; | ||
|
||
public void Read(BinaryStream bs) | ||
{ | ||
Length = bs.Read.Int(); | ||
Data = bs.Read.ByteArray(Length); | ||
} | ||
|
||
public void Write(BinaryStream bs) | ||
{ | ||
bs.Write.Int(Length); | ||
bs.Write.ByteArray(Data); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System.IO; | ||
using System.Linq; | ||
using FauFau.Formats; | ||
|
||
namespace Tests | ||
{ | ||
public class CziTests | ||
{ | ||
|
||
public static void ReadTest() | ||
{ | ||
var czi = new Czi("C:\\temp\\FauFau\\00107072_org.czi"); | ||
DumpToFolder(czi, "C:\\temp\\FauFau\\00107072_og"); | ||
|
||
string packDir = "C:\\temp\\FauFau\\00107072_pack"; | ||
if (Directory.Exists(packDir)) { | ||
var newCzi = PackFromFolder(2048, 2048, packDir); | ||
newCzi.Save("C:\\temp\\FauFau\\00107072.czi"); | ||
} | ||
} | ||
|
||
private static void DumpToFolder(Czi czi, string folder) | ||
{ | ||
Directory.CreateDirectory(folder); | ||
|
||
for (int i = 0; i < czi.Head.NumMipLevels; i++) { | ||
var mipData = czi.GetMipDecompressed(i); | ||
File.WriteAllBytes($"{folder}\\{i}.raw", mipData); | ||
} | ||
} | ||
|
||
private static Czi PackFromFolder(int width, int height, string folder) | ||
{ | ||
var czi = Czi.CreateMaskCzi(width, height); | ||
|
||
var files = Directory.GetFiles(folder).Order(); | ||
foreach (var file in files) { | ||
var data = File.ReadAllBytes(file); | ||
czi.AddMipLevel(data); | ||
} | ||
|
||
return czi; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters