|||
本文编写主要参考博客:
http://blog.csdn.net/abc553226713/article/details/8668839
Bundle文件与Bundlx文件内容分析
Bundle文件:
4字节存储长度 + 图片Byte[]循环存储
最大存储16384(128*128)这样的循环
可以看出参考文章中Bundle文件只存图片数据
*更正*
参考文章没写完全,10.2的Bundle前面有60个字节是描述性文字
已尝试将其全部置0,ArcServer依然可以正确显示图片
正确格式是:
60字节描述头 + (4字节存储长度 + 图片Byte[] 循环存储)
Bundlx文件:
内容结构 16byte +81920byte + 16byte
其中2个16字节 文件头信息与尾信息 属于描述性注记 内容无用
经实践证明故意修改ArcServer自带创建的Bundlx文件将前后16位描述性注记写为空的时候ArcServer也不会去理会,依然可以正确读取数据
中间的81920字节文件主体信息 内容包含:
81920 = 5*128*128 5字节存储偏移量 有128*128个5字节
所以每个Bundle文件最多存储128*128 = 16384个子文件
另外要注意这个5字节对应的JPG文件是按列排序的不是按行
注意:由于Bundle文件在文件头有60字节的描述,所以Bundlx文件默认第一个的偏移量一定是60
Bundlx文件偏移量解释
偏移量是描述一个图片在Bundle文件中的起点,偏移量由5位Buffer转Int数值,读取偏移量参考代码:
long offset =(long)(buffer[0]& 0xff) + (long)(buffer[1] & 0xff)
*256 + (long)(buffer[2]& 0xff) * 65536
+ (long)(buffer[3] &0xff) * 16777216
+ (long)(buffer[4]& 0xff) * 4294967296L;
这个表示方法表示偏移量是由低位往高位进行表示的
比如byte[5]{0,1,0,0,0}= 偏移 1 * 256 length
又如byte[5]{0,1,5,0,0}= 偏移 1 * 256 + 5 * 256 * 256 length
每一位表示 位值 * 256的位数开方将所有位相加得到位值
同理Bundle文件的4字节存储长度也是这个表达方式
实质上就是一个256进制与10进制的转换过程(因为一个Byte只能存256个不同的值,最大化利用)
Bundle/Bundlx文件命名规则解释
R+4位16进制数字+C+4位16进制数字组成
比如R0080C0180表示这个紧凑格式表达了
第129-256行384-512列
行列顺序从指定的切片原点算(这个与普通切片相同)
现在对同一范围同比例尺进行ArcServer紧凑切片与切片工具切片
发现Jpg起点与Bundle起点相差了一些
R2380C1280 转十进制表示 切片范围左上角起点:
9088行 4736列
R23cdC12b6 转十进制表示 切片范围左上角起点:
9165行 4790 列
出现差异是因为Jpg是由原点以Step为1递增到实际位置,实际范围的起点位置更为精确
而Bundle是由原点以Step为128递增到实际位置(行列数总是128的倍数)
所以要解决这个问题,必须重算更新的Jpg文件在所归属的Bundle文件范围,并将这些文件注册到这些范围中,类似注册空间索引,然后根据这些注册信息,将数据写入Bundle
如何实现切片成果JPG转Bundle功能
第一步 获取更新图片JPG文件计算每个文件的十进制行列
第二步 获取切片原点 计算包含此更新区域的Bundle文件命名 以及十进制的行列范围
第三步 写Bundle文件
3.1匹配每个JPG文件到Bundle范围中
3.2写入Jpg的大小并转高低位保存在头4个Byte中(注意,如果图片内容为空,依然要填写4个0000补齐位数)
3.3写入Byte[] 内容到Bundle文件中
3.4记录每个文件的行列号和并记录每个文件的序号与长度
第四步 写Bundlx文件
根据写Bundle产生的信息,依次按列填写每个文件的偏移量
这里个功能不仅可以从JPG转换到Bundle 而且还可以对已有Bundle进行JPG更新
★转换全代码★
使用 只需要指定 JPG的Path 与目标Bundle文件的Path 即可 调用样例代码
static void Main(string[] args)
{
PackageBundle BundleMaker = new PackageBundle();
BundleMaker._strJPGPath = @"C:\JPGList";
BundleMaker._strBundlePath = @"C:\_alllayers0";
BundleMaker.StartPackage();
}
class PackageBundle
{
public string _strJPGPath;
public string _strBundlePath;
private CommonTools _tool = new CommonTools();
public void StartPackage()
{
try
{
if (!new DirectoryInfo(_strBundlePath).Exists)
new DirectoryInfo(_strBundlePath).Create();
//读取一个Jpg文件夹,并将其组合为Bundle/Bundlx文件
//第一步 依次遍历每个L00 L01 级别
DirectoryInfo RootDir = new DirectoryInfo(_strJPGPath);//C:\Users\chop\Desktop\_alllayers
DirectoryInfo[] LevelDirList = RootDir.GetDirectories();
foreach (DirectoryInfo LevelDir in LevelDirList)
{
int MinRow,MinColumn,MaxRow,MaxColumn;
//获取JPG内容的范围
GetJpgNamingEnv(LevelDir.FullName,out MinRow,out MinColumn,out MaxRow,out MaxColumn);//C:\Users\chop\Desktop\_alllayers\L00
//获取Bundle文件
List<Bundle> BundleList = CreateBundleList(_strBundlePath + "\\" + LevelDir.Name, MinRow, MinColumn, MaxRow, MaxColumn);
//匹配JPG文件到Bundle文件
AttachJPGToBundle(LevelDir.FullName,BundleList);
//开始构造Bundle Bundlx文件
CreateBundleFiles(BundleList);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("打包完毕");
Console.ReadKey();
}
}
private void GetJpgNamingEnv(string strJPGLevelPath,out int MinRow,out int MinColumn,out int MaxRow,out int MaxColumn)
{
MinRow = 0; MinColumn = 0; MaxRow = 0; MaxColumn = 0;
//获取所有文件夹 他们代表着行
DirectoryInfo[] RowDirList = _tool.GetAllDirectory(strJPGLevelPath);
//获取所有文件 他们代表着列
FileInfo[] ColumnFileList = _tool.GetAllFiles(strJPGLevelPath);
//翻译每一个文件夹的名称(行)到 十进制 并且排序
List<int> RowList = new List<int>();
foreach (DirectoryInfo RowDir in RowDirList)
{
string Num16 = RowDir.Name.TrimStart('R');
string Num10 = _tool.To10(Num16);
RowList.Add(int.Parse(Num10));
}
RowList.Sort();
MinRow = RowList[0];
MaxRow = RowList[RowList.Count - 1];
List<int> ColumnList = new List<int>();
foreach (FileInfo Fileinfo in ColumnFileList)
{
string Num16 = Fileinfo.Name.TrimStart('C').TrimEnd(Fileinfo.Extension.ToCharArray());
string Num10 = _tool.To10(Num16);
ColumnList.Add(int.Parse(Num10));
}
ColumnList.Sort();
MinColumn = ColumnList[0];
MaxColumn = ColumnList[ColumnList.Count - 1];
}
class Bundle
{
public string strFileName;
public int MinRow;
public int MinColumn;
public int MaxRow;
public int MaxColumn;
public Bundle()
{
JPGList = new List<FileInfo>();
JPGIDList = new List<string>();
}
public List<FileInfo> JPGList;
public List<string> JPGIDList;
}
//根据JPG文件最大最小的行列 创建Bundle集合
private List<Bundle> CreateBundleList(string strBundleLevelDir,int MinRow, int MinColumn, int MaxRow, int MaxColumn)
{
//Bundle文件是行列为128个小JPG组成的 每行每列以128为Step从原点递增
//这里获取一个范围的Bundle 需要从最小的行列 开始计算起始Bundle
//然后由起始Bundle递增128 来增加Bundle数量 只要递增的Bundle的起始行列 始终在MaxRow MaxColumn范围内 那么这个Bundle就是有效Bundle
int Size = 128;
int BundleRow = (MinRow / Size) * Size;
int BundleColumn = (MinColumn / Size) * Size;
//这里建立起来的Bundle有可能是无效Bundle(因为更新区域不可能是规则的矩形),即不包含任何Jpg的Bundle,先不管,到后面判定时再删除这些Bundle
List<Bundle> BundleList = new List<Bundle>();
int BundleColumnMin = BundleColumn;
if (!new DirectoryInfo(strBundleLevelDir).Exists)
new DirectoryInfo(strBundleLevelDir).Create();
while(BundleRow < MaxRow)
{
while(BundleColumn < MaxColumn)
{
Bundle bundle = new Bundle();
bundle.MinRow = BundleRow;
bundle.MaxRow = BundleRow + Size;
bundle.MinColumn = BundleColumn;
bundle.MaxColumn = BundleColumn + Size;
bundle.strFileName = strBundleLevelDir + "\\" + "R" + _tool.To16(bundle.MinRow.ToString()) + "C" + _tool.To16(bundle.MinColumn.ToString()) + ".bundle";
BundleList.Add(bundle);
BundleColumn = BundleColumn + Size;
}
BundleColumn = BundleColumnMin;
BundleRow = BundleRow + Size;
}
return BundleList;
}
//将每个JPG归属到Bundle中
private void AttachJPGToBundle(string strJPGLevelPath, List<Bundle> BundleList)
{
//获取所有文件
FileInfo[] ColumnFileList = _tool.GetAllFiles(strJPGLevelPath);
//获取每个文件所归属的行列 用十进制表示
foreach (FileInfo colFile in ColumnFileList)
{
int dColumnNum = int.Parse(_tool.To10(colFile.Name.Substring(5, 4)));
int dRowNum = int.Parse(_tool.To10(colFile.DirectoryName.Substring(colFile.DirectoryName.Length - 4, 4)));
foreach (Bundle bundleFile in BundleList)
{
if ((bundleFile.MinRow <= dRowNum && dRowNum < bundleFile.MaxRow) && (bundleFile.MinColumn <= dColumnNum && dColumnNum < bundleFile.MaxColumn))
{
bundleFile.JPGList.Add(colFile);
bundleFile.JPGIDList.Add(dRowNum.ToString() + "-" + dColumnNum.ToString());
}
}
}
//除去冗余Bundle
for (int i = 0; i < BundleList.Count; i++)
{
if (BundleList[i].JPGList.Count == 0)
{
BundleList.RemoveAt(i);
i--;
}
}
}
private void CreateBundleFiles(List<Bundle> BundleList)
{
//从第一列128个开始往下写,写到N列 每次写到算好对应的16进制文件名并组成路径与List已有对比
foreach (Bundle bundleFile in BundleList)
{
//第一种情况Bundle不存在,这时需要从Bundle文件直接写入
if (!new FileInfo(bundleFile.strFileName).Exists)
{
WriteBundleFile(bundleFile);
}
//第二种情况Bundle已经存在 这时需要从Bundlx文件开始分析写入
else
{
RewriteBundleFile(bundleFile);
}
}
}
private void WriteBundleFile(Bundle bundleFile)
{
FileStream FsBundle = new FileStream(bundleFile.strFileName, FileMode.CreateNew, FileAccess.Write);
FileStream FsBundlx = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x", FileMode.CreateNew, FileAccess.Write);
FsBundle.Seek(0, SeekOrigin.Begin);//前60个描述内容统一置0
FsBundlx.Seek(0, SeekOrigin.Begin);//前16个描述内容统一置0
byte[] emptyBytes = new byte[60];
for (int ie = 0; ie < 60; ie++)
emptyBytes[ie] = 88;
FsBundle.Write(emptyBytes, 0, 60);
FsBundlx.Write(emptyBytes, 0, 16);//Bundlx的起点是60 因为Bundle前60个是描述Byte
int dOffset = 60;
for (int i = 0; i < 128; i++)
{
for (int j = 0; j < 128; j++)
{
string strCurrentID = (bundleFile.MinRow + j).ToString() + "-" + (bundleFile.MinColumn + i).ToString();
int dJPGIndex = bundleFile.JPGIDList.IndexOf(strCurrentID);
//写空白图片
if (dJPGIndex == -1)
{
FsBundle.Write(OffsetToByte4(0), 0, 4);
FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);
dOffset = dOffset + 4;
}
//写实际图片
else
{
byte[] JPGBytes = ReadJPGByte(bundleFile.JPGList[dJPGIndex].FullName);
FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);
FsBundle.Write(JPGBytes, 0, JPGBytes.Length);
FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);
dOffset = dOffset + 4 + JPGBytes.Length;
}
}
}
FsBundlx.Write(emptyBytes, 0, 16);//别忘了添加后16个描述内容 后2个描述内容统一置0
FsBundle.Close();
FsBundlx.Close();
}
private void RewriteBundleFile(Bundle bundleFile)
{
FileStream FsBundle = new FileStream(bundleFile.strFileName + ".Buffer", FileMode.CreateNew, FileAccess.Write);
FileStream FsBundlx = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x" + ".Buffer", FileMode.CreateNew, FileAccess.Write);
FileStream FsBundleSource = new FileStream(bundleFile.strFileName, FileMode.Open, FileAccess.Read);
FileStream FsBundlxSource = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x", FileMode.Open, FileAccess.Read);
FsBundle.Seek(0, SeekOrigin.Begin);//前60个描述内容统一置0
FsBundlx.Seek(0, SeekOrigin.Begin);//前16个描述内容统一置0
FsBundleSource.Seek(0, SeekOrigin.Begin);
FsBundlxSource.Seek(0, SeekOrigin.Begin);
byte[] emptyBytes = new byte[60];
for (int ie = 0; ie < 60; ie++)
emptyBytes[ie] = 88;
FsBundle.Write(emptyBytes, 0, 60);
FsBundlx.Write(emptyBytes, 0, 16);//Bundlx的起点是60 因为Bundle前60个是描述Byte
int dOffset = 60;
for (int i = 0; i < 128; i++)//column
{
for (int j = 0; j < 128; j++)//row
{
string strCurrentID = (bundleFile.MinRow + j).ToString() + "-" + (bundleFile.MinColumn + i).ToString();
int dJPGIndex = bundleFile.JPGIDList.IndexOf(strCurrentID);
//写已有Bundle内容图片
if (dJPGIndex == -1)
{
byte[] JPGBytes = GetBundleData(FsBundleSource, FsBundlxSource, j, i);
if (JPGBytes.Length == 0)
{
FsBundle.Write(OffsetToByte4(0), 0, 4);
FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);
dOffset = dOffset + 4;
}
else
{
FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);
FsBundle.Write(JPGBytes, 0, JPGBytes.Length);
FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);
dOffset = dOffset + 4 + JPGBytes.Length;
}
}
//写实际图片
else
{
byte[] JPGBytes = ReadJPGByte(bundleFile.JPGList[dJPGIndex].FullName);
FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);
FsBundle.Write(JPGBytes, 0, JPGBytes.Length);
FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);
dOffset = dOffset + 4 + JPGBytes.Length;
}
}
}
FsBundlx.Write(emptyBytes, 0, 16);//别忘了添加后16个描述内容 后2个描述内容统一置0
FsBundle.Close();
FsBundlx.Close();
FsBundleSource.Close();
FsBundlxSource.Close();
//全部写入完毕后 还需要将Buffer文件替换到实际命名的文件;
new FileInfo(bundleFile.strFileName).Delete();
new FileInfo(bundleFile.strFileName.TrimEnd('e') + "x").Delete();
new FileInfo(bundleFile.strFileName + ".Buffer").MoveTo(bundleFile.strFileName);
new FileInfo(bundleFile.strFileName.TrimEnd('e') + "x" + ".Buffer").MoveTo(bundleFile.strFileName.TrimEnd('e') + "x"); ;
}
private byte[] GetBundleData(FileStream FsBundleSource, FileStream FsBundlxSource,int dRow,int dColomn)
{
int dIndex = dColomn * 128 + dRow;
FsBundlxSource.Seek(16 + 5 * dIndex, SeekOrigin.Begin);
byte[] buffer = new byte[5];
FsBundlxSource.Read(buffer, 0, 5);
long offset = (long)(buffer[0] & 0xff) + (long)(buffer[1] & 0xff)
* 256 + (long)(buffer[2] & 0xff) * 65536
+ (long)(buffer[3] & 0xff) * 16777216
+ (long)(buffer[4] & 0xff) * 4294967296L;
FsBundleSource.Seek(offset, SeekOrigin.Begin);
byte[] lengthBytes = new byte[4];
FsBundleSource.Read(lengthBytes, 0, 4);
int length = (int)(lengthBytes[0] & 0xff)
+ (int)(lengthBytes[1] & 0xff) * 256
+ (int)(lengthBytes[2] & 0xff) * 65536
+ (int)(lengthBytes[3] & 0xff) * 16777216;
byte[] result = new byte[length];
FsBundleSource.Read(result, 0, length);
return result;
}
//10进制转256进制 4位表示法
private byte[] OffsetToByte4(int dOffset)
{
return System.BitConverter.GetBytes(dOffset);
}
//10进制转256进制 5位表示法
private byte[] OffsetToByte5(long dOffset)
{
byte[] byte8 = System.BitConverter.GetBytes(dOffset);
byte[] byte5 = new byte[] { byte8[0], byte8[1], byte8[2], byte8[3], byte8[4] };
return byte5;
}
//读取文件字节流到Byte[]
private byte[] ReadJPGByte(string strJPGPath)
{
using (FileStream fsRead = new FileStream(strJPGPath, FileMode.Open, FileAccess.Read))
{
byte[] Buffer = new byte[fsRead.Length];
fsRead.Read(Buffer, 0, Buffer.Length);
return Buffer;
}
}
}
淡墨青山
关注
————————————————
版权声明:本文为CSDN博主「淡墨青山」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/x15926292599/article/details/78794662
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-11-24 11:13
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社