public IMGC(Stream input) { using (var br = new BinaryReaderX(input, true)) { //Header header = br.ReadType <IMGCHeader>(); if (header.imageFormat == 28 && header.bitDepth == 8) { editMode = true; header.imageFormat = 29; } //get tile table br.BaseStream.Position = header.tableDataOffset; var tableC = br.ReadBytes(header.tableSize1); tableComp = (CompressionMethod)(tableC[0] & 0x7); byte[] table = Compressor.Decompress(new MemoryStream(tableC)); //get image data br.BaseStream.Position = header.tableDataOffset + header.tableSize2; var texC = br.ReadBytes(header.imgDataSize); picComp = (CompressionMethod)(texC[0] & 0x7); byte[] tex = Compressor.Decompress(new MemoryStream(texC)); //order pic blocks by table byte[] pic = Order(new MemoryStream(table), new MemoryStream(tex)); //return finished image settings = new ImageSettings(Support.Format[header.imageFormat], header.width, header.height) { Swizzle = new ImgcSwizzle(header.width, header.height) }; Image = Kolors.Load(pic, settings); } }
/// <inheritdoc cref="IIndexEncoding.Quantize(IEnumerable{Color},QuantizationSettings)"/> public (IEnumerable <IndexData> indices, IList <Color> palette) Quantize(IEnumerable <Color> colors, QuantizationSettings settings) { if (settings.ColorModel == ColorModel.RGBA) { settings.ColorModel = ColorModel.RGB; } var colorList = colors.ToList(); var(indices, palette) = Kolors.Quantize(colorList, settings); var alphaIndexData = indices. Zip(colorList, (x, y) => new { index = x, alpha = y.A }). Select(x => new AlphaIndexData(Convert.ChangeBitDepth(x.alpha, 8, _alphaDepth), x.index)); return(alphaIndexData, palette); }
public void Save(Stream file) { int width = (Image.Width + 0x7) & ~0x7; int height = (Image.Height + 0x7) & ~0x7; var settings = new ImageSettings(Support.Format[header.imageFormat], width, height) { Swizzle = new ImgcSwizzle(width, height) }; byte[] pic = Kolors.Save(Image, settings); using (var bw = new BinaryWriterX(file, true)) { //Header header.width = (short)Image.Width; header.height = (short)Image.Height; //tile table var table = new MemoryStream(); byte[] importPic = Deflate(pic, Support.Format[header.imageFormat].BitDepth, out table); //Table bw.BaseStream.Position = 0x48; var comp = Compressor.Compress(table, tableComp); bw.Write(comp); header.tableSize1 = comp.Length; header.tableSize2 = (header.tableSize1 + 3) & ~3; //Image bw.BaseStream.Position = 0x48 + header.tableSize2; header.imageFormat = (editMode) ? (byte)28 : header.imageFormat; comp = Compressor.Compress(new MemoryStream(importPic), picComp); bw.Write(comp); bw.WriteAlignment(4); header.imgDataSize = comp.Length; //Header bw.BaseStream.Position = 0; bw.WriteType(header); } }
/// <summary> /// /// </summary> /// <param name="input"></param> public BTX(Stream input) { using (var br = new BinaryReaderX(input, true)) { // Header Header = br.ReadType <FileHeader>(); br.SeekAlignment(); FileName = br.ReadCStringASCII(); // Setup var dataLength = Header.Width * Header.Height; var paletteDataLength = Header.ColorCount * 4; // Image br.BaseStream.Position = Header.ImageOffset; var texture = br.ReadBytes(dataLength); // Palette if (Header.Format == ImageFormat.Palette_8) { br.BaseStream.Position = Header.PaletteOffset; var palette = br.ReadBytes(paletteDataLength); var settings = new IndexedImageSettings(IndexEncodings[(int)Header.Format], PaletteEncodings[(int)Header.Format], Header.Width, Header.Height); var data = Kolors.Load(texture, palette, settings); Texture = data.image; Palette = data.palette; FormatName = IndexEncodings[(int)Header.Format].FormatName; PaletteFormatName = PaletteEncodings[(int)Header.Format].FormatName; HasPalette = true; } else { var settings = new ImageSettings(Encodings[(int)Header.Format], Header.Width, Header.Height); Texture = Kolors.Load(texture, settings); FormatName = Encodings[(int)Header.Format].FormatName; } } }
/// <summary> /// /// </summary> /// <param name="output"></param> public void Save(Stream output) { using (var bw = new BinaryWriterX(output, true)) { // Updates Header.Width = (short)Texture.Width; Header.Height = (short)Texture.Height; // Header bw.WriteType(Header); bw.WriteAlignment(); bw.WriteString(FileName, Encoding.ASCII, false); bw.WriteAlignment(); // Setup if (Header.Format == ImageFormat.Palette_8) { var settings = new IndexedImageSettings(IndexEncodings[(int)Header.Format], PaletteEncodings[(int)Header.Format], Header.Width, Header.Height) { QuantizationSettings = new QuantizationSettings(new WuColorQuantizer(6, 3), Header.Width, Header.Height) { ColorCount = 256, ParallelCount = 8 } }; var data = Kolors.Save(Texture, settings); bw.Write(data.indexData); bw.Write(data.paletteData); } else { var settings = new ImageSettings(Encodings[(int)Header.Format], Header.Width, Header.Height); var data = Kolors.Save(Texture, settings); bw.Write(data); } } }
public TEX(Stream input) { using (var br = new BinaryReaderX(input)) { // Set endianess if (br.PeekString() == "\0XET") { br.ByteOrder = ByteOrder = ByteOrder.BigEndian; } // Header Header = br.ReadType <FileHeader>(); HeaderInfo = new FileHeaderInfo { // Block 1 Version = (Version)(Header.Block1 & 0xFFF), Unknown1 = (int)((Header.Block1 >> 12) & 0xFFF), Unused1 = (int)((Header.Block1 >> 24) & 0xF), AlphaChannelFlags = (AlphaChannelFlags)((Header.Block1 >> 28) & 0xF), // Block 2 MipMapCount = (int)(Header.Block2 & 0x3F), Width = (int)((Header.Block2 >> 6) & 0x1FFF), Height = Math.Max((int)((Header.Block2 >> 19) & 0x1FFF), MinHeight), // Block 3 Unknown2 = (int)(Header.Block3 & 0xFF), Format = (byte)((Header.Block3 >> 8) & 0xFF), Unknown3 = (int)((Header.Block3 >> 16) & 0xFFFF) }; if (HeaderInfo.Version == Version._Switchv1 && !SwitchFormats.ContainsKey(HeaderInfo.Format)) { throw new InvalidOperationException($"Switch texture format 0x{HeaderInfo.Format.ToString("X2")} is not implemented."); } else if (!Formats.ContainsKey(HeaderInfo.Format)) { throw new InvalidOperationException($"Texture format 0x{HeaderInfo.Format.ToString("X2")} is not implemented."); } // TODO: Consider whether the following settings make more sense if conditioned by the ByteOrder (or Platform) //var format = HeaderInfo.Format.ToString().StartsWith("DXT1") ? Format.DXT1 : HeaderInfo.Format.ToString().StartsWith("DXT5") ? Format.DXT5 : HeaderInfo.Format; var encoding = (HeaderInfo.Version == Version._Switchv1) ? SwitchFormats[HeaderInfo.Format] : Formats[HeaderInfo.Format]; List <int> mipMaps = null; if (HeaderInfo.Version == Version._Switchv1) { var texOverallSize = br.ReadInt32(); if (texOverallSize > br.BaseStream.Length) { br.BaseStream.Position -= 4; SwitchUnknownData = br.ReadBytes(0x6C); texOverallSize = br.ReadInt32(); HeaderInfo.MipMapCount++; } mipMaps = br.ReadMultiple <int>(HeaderInfo.MipMapCount); } else if (HeaderInfo.Version != Version._3DSv1) { mipMaps = br.ReadMultiple <int>(HeaderInfo.MipMapCount); } for (var i = 0; i < mipMaps.Count; i++) { var texDataSize = 0; if (SwitchUnknownData != null) { if (i + 1 == HeaderInfo.MipMapCount) { continue; } texDataSize = mipMaps[i + 1] - mipMaps[i]; } else if (HeaderInfo.Version != Version._3DSv1) { texDataSize = (i + 1 < HeaderInfo.MipMapCount ? mipMaps[i + 1] : (int)br.BaseStream.Length) - mipMaps[i]; } else { texDataSize = Formats[HeaderInfo.Format].BitDepth * (HeaderInfo.Width >> i) * (HeaderInfo.Height >> i) / 8; } Settings = new ImageSettings(encoding, Math.Max(HeaderInfo.Width >> i, 2), Math.Max(HeaderInfo.Height >> i, 2)); //Set possible Swizzles if (HeaderInfo.Version == Version._3DSv1 || HeaderInfo.Version == Version._3DSv2 || HeaderInfo.Version == Version._3DSv3) { Settings.Swizzle = new CTRSwizzle(Settings.Width, Settings.Height, CtrTransformation.None, true); } else if (HeaderInfo.Version == Version._Switchv1) { Settings.Swizzle = new SwitchSwizzle(Settings.Width, Settings.Height, Settings.Encoding.BitDepth, GetSwitchSwizzleFormat(Settings.Encoding.FormatName), true); } else if (Settings.Encoding.FormatName.Contains("DXT")) { Settings.Swizzle = new BCSwizzle(Settings.Width, Settings.Height); } //Set possible pixel shaders if ((Format)HeaderInfo.Format == Format.DXT5_B) { Settings.PixelShader = ToNoAlpha; } else if ((Format)HeaderInfo.Format == Format.DXT5_YCbCr) { Settings.PixelShader = ToProperColors; } EncodingInfo info = null; if (HeaderInfo.Version == Version._3DSv1 || HeaderInfo.Version == Version._3DSv2 || HeaderInfo.Version == Version._3DSv3) { info = EncodingInfos.First(x => x.EncodingIndex == HeaderInfo.Format); } else if (HeaderInfo.Version == Version._Switchv1) { info = SwitchEncodingInfos.First(x => x.EncodingIndex == HeaderInfo.Format); } if (i == 0) { Bitmaps.Add(new BitmapInfo(Kolors.Load(br.ReadBytes(texDataSize), Settings), info)); } else { Bitmaps[0].MipMaps.Add(Kolors.Load(br.ReadBytes(texDataSize), Settings)); } } if (SwitchUnknownData != null) { SwitchOverflowingData = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); } } }
public void Save(Stream output) { using (var bw = new BinaryWriterX(output, ByteOrder)) { if (SwitchUnknownData != null) { HeaderInfo.MipMapCount--; } Header.Block1 = (uint)((int)HeaderInfo.Version | (HeaderInfo.Unknown1 << 12) | (HeaderInfo.Unused1 << 24) | ((int)HeaderInfo.AlphaChannelFlags << 28)); Header.Block2 = (uint)(HeaderInfo.MipMapCount | (HeaderInfo.Width << 6) | (HeaderInfo.Height << 19)); Header.Block3 = (uint)(HeaderInfo.Unknown2 | ((int)HeaderInfo.Format << 8) | (HeaderInfo.Unknown3 << 16)); bw.WriteType(Header); if (HeaderInfo.Version == Version._Switchv1 && SwitchUnknownData != null) { bw.Write(SwitchUnknownData); } //var format = HeaderInfo.Format.ToString().StartsWith("DXT1") ? Format.DXT1 : HeaderInfo.Format.ToString().StartsWith("DXT5") ? Format.DXT5 : HeaderInfo.Format; var encoding = (HeaderInfo.Version == Version._Switchv1) ? SwitchFormats[HeaderInfo.Format] : Formats[HeaderInfo.Format]; // Mipmap Downsampling if (Bitmaps.Count > 1 && HeaderInfo.MipMapCount > 1) { var firstBitmap = Bitmaps[0].Image; var width = firstBitmap.Width; var height = firstBitmap.Height; for (var i = 0; i < HeaderInfo.MipMapCount - 1; i++) { var bmp = new Bitmap(width / 2, height / 2); var gfx = Graphics.FromImage(bmp); gfx.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; gfx.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; gfx.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; gfx.DrawImage(firstBitmap, new Rectangle(0, 0, bmp.Width, bmp.Height)); Bitmaps[0].MipMaps[i] = bmp; width = bmp.Width; height = bmp.Height; } } var bitmaps = new List <byte[]>(); Settings = new ImageSettings(encoding, Bitmaps[0].Image.Width, Bitmaps[0].Image.Height); //Set possible Swizzles if (HeaderInfo.Version == Version._3DSv1 || HeaderInfo.Version == Version._3DSv2 || HeaderInfo.Version == Version._3DSv3) { Settings.Swizzle = new CTRSwizzle(Bitmaps[0].Image.Width, Bitmaps[0].Image.Height, CtrTransformation.None, true); } else if (HeaderInfo.Version == Version._Switchv1) { Settings.Swizzle = new SwitchSwizzle(Bitmaps[0].Image.Width, Bitmaps[0].Image.Height, Settings.Encoding.BitDepth, GetSwitchSwizzleFormat(Settings.Encoding.FormatName), true); //Switch Swizzle } else if (Settings.Encoding.FormatName.Contains("DXT")) { Settings.Swizzle = new BCSwizzle(Bitmaps[0].Image.Width, Bitmaps[0].Image.Height); } if ((Format)HeaderInfo.Format == Format.DXT5_B) { Settings.PixelShader = ToNoAlpha; } else if ((Format)HeaderInfo.Format == Format.DXT5_YCbCr) { Settings.PixelShader = ToOptimisedColors; } bitmaps.Add(Kolors.Save(Bitmaps[0].Image, Settings)); foreach (var mipmap in Bitmaps[0].MipMaps) { Settings = new ImageSettings(encoding, Bitmaps[0].Image.Width, Bitmaps[0].Image.Height); //Set possible Swizzles if (HeaderInfo.Version == Version._3DSv1 || HeaderInfo.Version == Version._3DSv2 || HeaderInfo.Version == Version._3DSv3) { Settings.Swizzle = new CTRSwizzle(mipmap.Width, mipmap.Height, CtrTransformation.None, true); } else if (HeaderInfo.Version == Version._Switchv1) { Settings.Swizzle = new SwitchSwizzle(mipmap.Width, mipmap.Height, Settings.Encoding.BitDepth, GetSwitchSwizzleFormat(Settings.Encoding.FormatName), true); //Switch Swizzle } else if (Settings.Encoding.FormatName.Contains("DXT")) { Settings.Swizzle = new BCSwizzle(mipmap.Width, mipmap.Height); } if ((Format)HeaderInfo.Format == Format.DXT5_B) { Settings.PixelShader = ToNoAlpha; } else if ((Format)HeaderInfo.Format == Format.DXT5_YCbCr) { Settings.PixelShader = ToOptimisedColors; } bitmaps.Add(Kolors.Save(mipmap, Settings)); } if (HeaderInfo.Version == Version._Switchv1) { if (SwitchUnknownData != null) { var listOffset = bw.BaseStream.Position; bw.BaseStream.Position += 8 + HeaderInfo.MipMapCount * sizeof(int); var offsets = new List <int>(); var relOffset = bw.BaseStream.Position; foreach (var bitmap in bitmaps) { var data = new byte[(bitmap.Length + 0x1FF) & ~0x1FF]; Array.Copy(bitmap, data, bitmap.Length); bw.Write(data); offsets.Add((int)(bw.BaseStream.Position - relOffset)); } offsets[offsets.Count - 1] = (offsets.Last() + 0x7FF) & ~0x7FF; bw.BaseStream.Position = relOffset + offsets.Last(); bw.Write(SwitchOverflowingData); var totalSize = bw.BaseStream.Position - 0x84 - HeaderInfo.MipMapCount * sizeof(int); bw.BaseStream.Position = listOffset; bw.Write((int)totalSize); bw.BaseStream.Position += 4; foreach (var offset in offsets) { bw.Write(offset); } } else { bw.BaseStream.Position += 0x4 + HeaderInfo.MipMapCount * sizeof(int); var offsets = new List <int>(); var relOffset = bw.BaseStream.Position; foreach (var bitmap in bitmaps) { offsets.Add((int)(bw.BaseStream.Position - relOffset)); var data = new byte[(bitmap.Length + 0x1FF) & ~0x1FF]; Array.Copy(bitmap, data, bitmap.Length); bw.Write(data); } var totalSize = bw.BaseStream.Position - (0x10 + 0x4 + HeaderInfo.MipMapCount * sizeof(int)); bw.BaseStream.Position = 0x10; bw.Write((int)totalSize); foreach (var offset in offsets) { bw.Write(offset); } } } else // Mipmaps, but not for Version 3DS v1 if (HeaderInfo.Version != Version._3DSv1) { var offset = HeaderInfo.Version == Version._PS3v1 ? HeaderInfo.MipMapCount * sizeof(int) + HeaderLength : 0; foreach (var bitmap in bitmaps) { bw.Write(offset); offset += bitmap.Length; } } // Bitmaps if (SwitchUnknownData == null) { foreach (var bitmap in bitmaps) { bw.Write(bitmap); } } } }
/// <inheritdoc cref="IIndexEncoding.Quantize(IEnumerable{Color},QuantizationSettings)"/> public (IEnumerable <IndexData> indices, IList <Color> palette) Quantize(IEnumerable <Color> colors, QuantizationSettings settings) { var(indices, palette) = Kolors.Quantize(colors, settings); return(indices.Select(x => new IndexData(x)), palette); }