123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- /**
- * 魔力宝贝图档解析脚本 - CGTool
- *
- * @Author HonorLee (dev@honorlee.me)
- * @Version 1.0 (2023-11-20)
- * @License GPL-3.0
- *
- * GraphicData.cs 图档解析类
- */
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Unity.Burst;
- using Unity.Collections;
- using Unity.Jobs;
- using UnityEngine;
- namespace CrossgateToolkit
- {
- //图档数据详情
- public class GraphicDetail
- {
- //索引
- public uint Index;
- //编号
- public uint Serial;
- //图档宽度
- public uint Width;
- //图档高度
- public uint Height;
- //图档偏移X
- public int OffsetX;
- //图档偏移Y
- public int OffsetY;
- //Palet调色板Index
- public int Palet;
- //图档Sprite
- public Sprite Sprite;
- //图档主色调,用于小地图绘制
- public Color32 PrimaryColor;
- }
-
-
-
- // 图档数据
- public static class GraphicData
- {
- // 常规图档缓存
- public static Dictionary<GraphicInfoData,Dictionary<int,GraphicDetail>> _cache = new Dictionary<GraphicInfoData, Dictionary<int, GraphicDetail>>();
-
- // 线性图档缓存
- public static Dictionary<GraphicInfoData,Dictionary<int,GraphicDetail>> _linearCache = new Dictionary<GraphicInfoData, Dictionary<int, GraphicDetail>>();
-
- // 获取图档
- public static GraphicDetail GetGraphicDetail(GraphicInfoData graphicInfoData, int palet = 0,int subPalet = 0,bool asLinear = false)
- {
- GraphicDetail graphicDetail = null;
- var checkCache = asLinear ? _linearCache : _cache;
-
- if (checkCache.ContainsKey(graphicInfoData))
- {
- if (checkCache[graphicInfoData].ContainsKey(palet))
- {
- graphicDetail = checkCache[graphicInfoData][palet];
- }
- else
- {
- graphicDetail = _loadGraphicDetail(graphicInfoData, palet, subPalet, asLinear);
- checkCache[graphicInfoData].Add(palet, graphicDetail);
- }
- }
- else
- {
- graphicDetail = _loadGraphicDetail(graphicInfoData, palet, subPalet, asLinear);
- checkCache.Add(graphicInfoData, new Dictionary<int, GraphicDetail>());
- checkCache[graphicInfoData].Add(palet, graphicDetail);
- }
-
- return graphicDetail;
- }
-
- // 解析图档
- private static GraphicDetail _loadGraphicDetail(GraphicInfoData graphicInfoData,int palet = 0,int subPalet = 0,bool asLinear = false)
- {
- GraphicDetail graphicDetail = new GraphicDetail();
-
- //获取图像数据
- List<Color32> pixels = UnpackGraphic(graphicInfoData, palet, subPalet);
- if(pixels==null) return null;
-
- graphicDetail.PrimaryColor = pixels.Last();
- pixels.RemoveAt(pixels.Count - 1);
- //直接通过Texture2D做偏移,并转为Sprite的偏移量
- Vector2 offset = new Vector2(0f, 1f);
- offset.x += -(graphicInfoData.OffsetX * 1f) / graphicInfoData.Width;
- offset.y -= (-graphicInfoData.OffsetY * 1f) / graphicInfoData.Height;
-
- //创建Texture2D对象
- Texture2D texture2D;
- Sprite sprite;
- // RGBA4444 减少内存占用
- texture2D = new Texture2D((int) graphicInfoData.Width, (int) graphicInfoData.Height,
- TextureFormat.RGBA4444, false, asLinear);
- // 固定点过滤
- if (asLinear) texture2D.filterMode = FilterMode.Bilinear;
- else texture2D.filterMode = FilterMode.Point;
- texture2D.SetPixels32(pixels.ToArray());
- // texture2D.LoadRawTextureData(rawTextureData);
- texture2D.Apply();
-
- sprite = Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), offset, 1,1,SpriteMeshType.FullRect);
- //写入数据
- graphicDetail.Index = graphicInfoData.Index;
- graphicDetail.Serial = graphicInfoData.Serial;
- graphicDetail.Width = graphicInfoData.Width;
- graphicDetail.Height = graphicInfoData.Height;
- graphicDetail.OffsetX = graphicInfoData.OffsetX;
- graphicDetail.OffsetY = graphicInfoData.OffsetY;
- graphicDetail.Palet = palet;
- graphicDetail.Sprite = sprite;
- return graphicDetail;
- }
- #region 地图合批
- // 合批数据
- private class BatchData
- {
- public int BatchOffsetX;
- public int BatchOffsetY;
- public GraphicDetail GraphicDetail;
- }
- // 图档合批
- private class TextureData
- {
- public int MaxHeight;
- public int MaxWidth;
- public List<BatchData> BatchDatas = new List<BatchData>();
- public List<GraphicInfoData> GraphicInfoDatas = new List<GraphicInfoData>();
- }
-
- /// <summary>
- /// 合批图档
- /// 通过指定图档序列,对图档进行合批处理,并返回合批后的图档数据
- /// </summary>
- /// <param name="graphicInfoDatas">图档索引数据序列</param>
- /// <param name="palet">调色板序号</param>
- /// <param name="maxTextureSize">单个Texture最大尺寸,地面数据建议2048,物件数据建议4096</param>
- /// <param name="padding">图档间隔,可以有效避免图档渲染时出现多余的黑边或像素黏连</param>
- /// <returns>合批后的图档数据,Key(unit)为图档数据编号,Value为图档数据</returns>
- public static Dictionary<uint, GraphicDetail> BakeGraphics(List<GraphicInfoData> graphicInfoDatas,int palet = 0, int maxTextureSize = 2048,int padding = 0)
- {
- // 单个Texture最大尺寸
- int maxWidth = maxTextureSize;
- int maxHeight = maxTextureSize;
-
- List<TextureData> textureDatas = new List<TextureData>();
- Dictionary<uint, GraphicDetail> graphicDataDic = new Dictionary<uint, GraphicDetail>();
- // 根据objectInfos的内,GraphicInfoData的Width,Height进行排序,优先排序Width,使图档从小到大排列
- graphicInfoDatas = graphicInfoDatas.OrderBy(obj => obj.Width).ThenBy(obj => obj.Height).ToList();
- int offsetX = 0; // X轴偏移量
- int offsetY = 0; // Y轴偏移量
- int maxRowHeight = 0; // 当前行最大高度
-
- TextureData textureData = new TextureData();
-
- for (var i = 0; i < graphicInfoDatas.Count; i++)
- {
- GraphicInfoData graphicInfoData = graphicInfoDatas[i];
- // 如果宽度超过4096,则换行
- if((graphicInfoData.Width + offsetX) > maxWidth)
- {
- offsetX = 0;
- offsetY = offsetY + maxRowHeight + padding;
- maxRowHeight = 0;
- }
- // 如果高度超过2048,则生成新的Texture2D
- if ((graphicInfoData.Height + offsetY) > maxHeight)
- {
- offsetX = 0;
- offsetY = 0;
- maxRowHeight = 0;
- textureDatas.Add(textureData);
- textureData = new TextureData();
- }
-
- BatchData batchData = new BatchData();
- batchData.BatchOffsetX = offsetX;
- batchData.BatchOffsetY = offsetY;
- batchData.GraphicDetail = GetGraphicDetail(graphicInfoData, palet);
- // graphicDatas.Add(graphicData);
-
- textureData.BatchDatas.Add(batchData);
- textureData.GraphicInfoDatas.Add(graphicInfoData);
-
-
- maxRowHeight = Mathf.Max(maxRowHeight, (int) graphicInfoData.Height);
- textureData.MaxHeight = Mathf.Max(textureData.MaxHeight, offsetY + maxRowHeight);
- textureData.MaxWidth = Mathf.Max(textureData.MaxWidth, offsetX + (int) graphicInfoData.Width);
- offsetX += (int) graphicInfoData.Width + padding;
- }
-
- //最后一次合并
- if (textureData.BatchDatas.Count > 0) textureDatas.Add(textureData);
-
- //合并Texture2D
- for (var i = 0; i < textureDatas.Count; i++)
- {
- TextureData textureDataPiece = textureDatas[i];
- // Debug.Log($"合并第{i}个Texture2D,最大高度:{textureDataPiece.MaxHeight},图像数量:{textureDataPiece.GraphicDatas.Count}");
- Color32[] colors = Enumerable.Repeat(new Color32(0,0,0,0), textureDataPiece.MaxWidth * textureDataPiece.MaxHeight).ToArray();
- Texture2D texture2DPiece = new Texture2D(textureDataPiece.MaxWidth, textureDataPiece.MaxHeight, TextureFormat.RGBA4444, false, false);
- texture2DPiece.filterMode = FilterMode.Point;
- texture2DPiece.SetPixels32(colors);
- for (var n = 0; n < textureDataPiece.BatchDatas.Count; n++)
- {
- BatchData batchData = textureDataPiece.BatchDatas[n];
- GraphicInfoData graphicInfoData = textureDataPiece.GraphicInfoDatas[n];
- if (batchData.GraphicDetail!=null)
- {
- Color32[] pixels = batchData.GraphicDetail.Sprite.texture.GetPixels32();
- texture2DPiece.SetPixels32(batchData.BatchOffsetX, batchData.BatchOffsetY, (int) graphicInfoData.Width, (int) graphicInfoData.Height,
- pixels.ToArray());
- }
- }
- texture2DPiece.Apply();
- Combine(texture2DPiece, textureDataPiece.BatchDatas);
- }
- void Combine(Texture2D texture2D,List<BatchData> batchDatas)
- {
- for (var i = 0; i < batchDatas.Count; i++)
- {
- BatchData batchData = batchDatas[i];
- //直接通过Texture2D做偏移,并转为Sprite的偏移量
- Vector2 offset = new Vector2(0f, 1f);
- offset.x += -(batchData.GraphicDetail.OffsetX * 1f) / batchData.GraphicDetail.Width;
- offset.y -= (-batchData.GraphicDetail.OffsetY * 1f) / batchData.GraphicDetail.Height;
- Sprite sprite = Sprite.Create(texture2D, new Rect(batchData.BatchOffsetX, batchData.BatchOffsetY, (int)batchData.GraphicDetail.Width, (int)batchData.GraphicDetail.Height),offset, 1, 1, SpriteMeshType.FullRect);
- GraphicDetail graphicDetail = new GraphicDetail()
- {
- Index = batchData.GraphicDetail.Index,
- Serial = batchData.GraphicDetail.Serial,
- Width = batchData.GraphicDetail.Width,
- Height = batchData.GraphicDetail.Height,
- OffsetX = batchData.GraphicDetail.OffsetX,
- OffsetY = batchData.GraphicDetail.OffsetY,
- Palet = batchData.GraphicDetail.Palet,
- Sprite = sprite,
- PrimaryColor = batchData.GraphicDetail.PrimaryColor
- };
-
- // graphicDataPiece.Sprite = sprite;
- graphicDataDic.Add(graphicDetail.Serial, graphicDetail);
- }
- }
- return graphicDataDic;
- }
- #endregion
-
- //解压图像数据
- private static List<Color32> UnpackGraphic(GraphicInfoData graphicInfoData,int PaletIndex=0,int SubPaletIndex=0){
- List<Color32> pixels = new List<Color32>();
- //获取调色板
- List<Color32> palet;
- //调整流指针
- BinaryReader fileReader = graphicInfoData.GraphicReader;
- fileReader.BaseStream.Position = graphicInfoData.Addr;
- //读入目标字节集
- byte[] Content = fileReader.ReadBytes((int) graphicInfoData.Length);
- //读取缓存字节集
- BinaryReader contentReader = new BinaryReader(new MemoryStream(Content));
- //16字节头信息
- byte[] RD = contentReader.ReadBytes(2);
- int Version = contentReader.ReadByte();
- int Unknow = contentReader.ReadByte();
- uint Width = contentReader.ReadUInt32();
- uint Height = contentReader.ReadUInt32();
- uint DataLen = contentReader.ReadUInt32();
- uint innerPaletLen = 0;
-
-
- // 低版本头部长度为16,高版本为20
- int headLen = 16;
- if (Version > 1)
- {
- headLen = 20;
- innerPaletLen = contentReader.ReadUInt32();
- }
-
- //数据长度
- int contentLen = (int)(DataLen - headLen);
- int pixelLen = (int) (graphicInfoData.Width * graphicInfoData.Height);
-
- int[] paletIndex;
- if (graphicInfoData.UnpackedPaletIndex == null)
- {
- //解压数据
- byte[] contentBytes = contentReader.ReadBytes((int) contentLen);
- NativeArray<byte> bytes = new NativeArray<byte>((int) contentBytes.Length, Allocator.TempJob);
- bytes.CopyFrom(contentBytes);
- long decompressLen = pixelLen + innerPaletLen;
-
- NativeArray<int> colorIndexs =
- new NativeArray<int>((int)decompressLen, Allocator.TempJob);
- DecompressJob decompressJob = new DecompressJob()
- {
- bytes = bytes,
- compressd = Version != 0,
- colorIndexs = colorIndexs
- };
- // decompressJob.Execute();
- decompressJob.Schedule().Complete();
- bytes.Dispose();
- paletIndex = colorIndexs.ToArray();
- graphicInfoData.UnpackedPaletIndex = paletIndex;
- colorIndexs.Dispose();
- }
- else
- {
- paletIndex = graphicInfoData.UnpackedPaletIndex;
- }
- if (SubPaletIndex > 0)
- {
- palet = Palet.GetPalet(SubPaletIndex);
- if (palet == null)
- {
- GraphicInfoData subPaletInfoData = GraphicInfo.GetGraphicInfoData((uint)SubPaletIndex);
- Graphic.GetGraphicDetail((uint)SubPaletIndex);
- palet = subPaletInfoData.InnerPalet;
- Palet.AddPalet(SubPaletIndex, palet);
- }
- }
- else
- {
- if (innerPaletLen > 0)
- {
- int[] innerPaletIndex = paletIndex.Skip(pixelLen).Take((int) innerPaletLen).ToArray();
- palet = AnalysisInnerPalet(innerPaletIndex).ToList();
- paletIndex = paletIndex.Take(pixelLen).ToArray();
- graphicInfoData.InnerPalet = palet;
- }
- else
- {
- palet = Palet.GetPalet(PaletIndex);
- }
- }
- //释放连接
- contentReader.Dispose();
- contentReader.Close();
-
- //主色调色值
- int r = 0;
- int g = 0;
- int b = 0;
- foreach (int index in paletIndex)
- {
- Color32 color32;
- if (index == 999 || (index > palet.Count - 1))
- {
- color32 = Color.clear;
- }
- else
- {
- color32 = palet[index];
- }
- pixels.Add(color32);
- r += color32.r;
- g += color32.g;
- b += color32.b;
- }
- //主色调计算及提亮
- r = r / pixels.Count * 3;
- g = g / pixels.Count * 3;
- b = b / pixels.Count * 3;
- if (r > 255) r = 255;
- if (g > 255) g = 255;
- if (b > 255) b = 255;
- int len = (int) (graphicInfoData.Width * graphicInfoData.Height);
- if (pixels.Count != len)
- {
- if (pixels.Count > len)
- {
- pixels = pixels.GetRange(0, len);
- }
- else
- {
- Color32[] temc = new Color32[len - pixels.Count];
- ArrayList.Repeat(Color.clear, len - pixels.Count).CopyTo(temc);
- pixels.AddRange(temc);
- }
- }
-
- //主色调加入最后
- pixels.Add(new Color32((byte) r, (byte) g, (byte) b, 255));
- return pixels;
- }
- //分析高版本内部调色板
- private static Color32[] AnalysisInnerPalet(int[] bytes)
- {
- int colorLen = bytes.Length / 3;
- Color32[] palet = new Color32[colorLen + 1];
- for (var i = 0; i < colorLen; i++)
- {
- int[] paletBytes = bytes.Skip(i * 3).Take(3).ToArray();
- Color32 color32 = new Color32();
- color32.r = (byte)paletBytes[2];
- color32.g = (byte)paletBytes[1];
- color32.b = (byte)paletBytes[0];
- color32.a = 0xFF;
- palet[i] = color32;
- }
- palet[colorLen] = Color.clear;
- return palet;
- }
- #region 测试解压
- private static int[] TestDecompress(byte[] bytes)
- {
- List<int> colorIndexs = new List<int>();
- int _index = -1;
- int next()
- {
- _index++;
- if (_index > bytes.Length - 1) return -1;
- return bytes[_index];
- }
- while (_index < (bytes.Length - 1))
- {
- int head = next();
- if (head == -1) break;
- int repeat = 0;
- if (head < 0x10)
- {
- repeat = head;
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(next());
- }
- }
- else if (head < 0x20)
- {
- repeat = head % 0x10 * 0x100 + next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(next());
- }
- }
- else if (head < 0x80)
- {
- repeat = head % 0x20 * 0x10000 + next() * 0x100 + next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(next());
- }
- }
- else if (head < 0x90)
- {
- repeat = head % 0x80;
- int index = next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(index);
- }
- }
- else if (head < 0xa0)
- {
- int index = next();
- repeat = head % 0x90 * 0x100 + next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(index);
- }
- }
- else if (head < 0xc0)
- {
- int index = next();
- repeat = head % 0xa0 * 0x10000 + next() * 0x100 + next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(index);
- }
- }
- else if (head < 0xd0)
- {
- repeat = head % 0xc0;
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(999);
- }
- }
- else if (head < 0xe0)
- {
- repeat = head % 0xd0 * 0x100 + next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(999);
- }
- }
- else if (head < 0xff)
- {
- repeat = head % 0xe0 * 0x10000 + next() * 0x100 + next();
- for (var i = 0; i < repeat; i++)
- {
- colorIndexs.Add(999);
- }
- }
- }
- return colorIndexs.ToArray();
- }
- #endregion 测试解压
-
- }
-
- //解压缩交给IJob处理
- [BurstCompile]
- public struct DecompressJob : IJob
- {
- [ReadOnly]
- public NativeArray<byte> bytes;
- public bool compressd;
- public NativeArray<int> colorIndexs;
-
- private int _maxIndex;
- private int _index;
- private int _colorIndex;
-
- private int NextByte()
- {
- _index++;
- if (_index > _maxIndex) return -1;
- return bytes[_index];
- }
- private void AddColorIndex(int index)
- {
- if (_colorIndex > colorIndexs.Length - 1) return;
- colorIndexs[_colorIndex] = index;
- _colorIndex++;
- }
- [BurstCompile]
- public void Execute()
- {
- _maxIndex = bytes.Length - 1;
- _index = -1;
- _colorIndex = 0;
-
- if (!compressd)
- {
- while (_index<=_maxIndex)
- {
- int pindex = NextByte();
- if(pindex==-1) break;
- AddColorIndex(pindex);
- }
- }
- else
- //压缩型数据解压
- {
- // int count = 0;
- while (_index<=_maxIndex)
- {
- // count++;
- int head = NextByte();
- if(head==-1) break;
- int repeat = 0;
- if (head < 0x10)
- {
- repeat = head;
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(NextByte());
- }
-
- }
- else if (head < 0x20)
- {
- repeat = head % 0x10 * 0x100 + NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(NextByte());
- }
-
- }
- else if (head < 0x80)
- {
- repeat = head % 0x20 * 0x10000 + NextByte() * 0x100 + NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(NextByte());
- }
-
- }
- else if (head < 0x90)
- {
- repeat = head % 0x80;
- int index = NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(index);
- }
-
- }
- else if (head < 0xa0)
- {
- int index = NextByte();
- repeat = head % 0x90 * 0x100 + NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(index);
- }
-
- }
- else if (head < 0xc0)
- {
- int index = NextByte();
- repeat = head % 0xa0 * 0x10000 + NextByte() * 0x100 + NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(index);
- }
-
- }
- else if (head < 0xd0)
- {
- repeat = head % 0xc0;
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(999);
- }
-
- }
- else if (head < 0xe0)
- {
- repeat = head % 0xd0 * 0x100 + NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(999);
- }
-
- }
- else
- {
- repeat = head % 0xe0 * 0x10000 + NextByte() * 0x100 + NextByte();
- for (var i = 0; i < repeat; i++)
- {
- AddColorIndex(999);
- }
- }
- }
- }
- }
- }
- }
|