崩坏学园2及大部分采用ETC1压缩格式的Unity3D游戏的拆包图处理

0x0 背景

众所周知(?) , 大部分(?)以Unity3D为引擎的手游为了进一步压缩资源大小, 在Android平台经常将贴图资源以ETC1格式压缩以减少体积. 蛋疼的是ETC1不支持Alpha通道....

程序猿们选择将原图拆分, 用一张贴图来单独记录Alpha信息. 这就给后来的拆(偷)包(图)带来了不便(不要脸(*≧▽≦)), 怎么办呢? 合并回去就行了呀!(理直气壮)

0x1 实现

众...... 一张RGBA图片包含三个颜色通道以及一个Alpha通道, 经过拆分之后就变成了一张图片记录原图的RGB参数, 一张图片仅记录Alpha参数.

如崩崩的拆包图: 

那么要想合并回去, 只需要在一图中获取RGB对应的值, 在二图中获取Alpha的值, 然后合并在一起生成一一张RGBA信息的图保存下来.

本文所用语言为C#, 首先用易于理解的GetPixel()方法写一次~

0x2 核心代码 - 获取像素法

 1 private Bitmap mergeImageOld(Bitmap rgbTexture,Bitmap alphaTexture)
 2 {
 3     textureWithAlpha = new Bitmap(rgbTexture.Width, rgbTexture.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); //新建一个与RGB同分辨率的Bitmap
 4     try
 5     {
 6         for (int i = 0; i < rgbTexture.Width; i++)
 7         {
 8             for (int j = 0; j < rgbTexture.Height; j++)
 9             {
10                 Color withAlpha = Color.FromArgb(alphaTexture.GetPixel(i, j).R, rgbTexture.GetPixel(i, j));
11                 textureWithAlpha.SetPixel(i, j, withAlpha);
12             }   //internal for end
13         }   //for end
14
15         return textureWithAlpha;
16     }
17     catch(Exception ex)
18     {
19         Console.WriteLine(ex.Message);
20         return textureWithAlpha;
21     }
22 }   //mergeImageOld()

其中6-13行是关键代码 for循环中逐行逐像素处理

这里第十行用到的重载为:

Color Color.FromArgb(int alpha,Color baseColor);

第一个参数就是Alpha值, 第二个参数为颜色, alphaTexture.GetPixel(i, j).R 的意思的是在alphaTexture中第i行第j个像素获取红色分量, rgbTexture.GetPixel(i, j) 的意思是在rgbTexture中获取颜色

Alpha值拿到了, 颜色也拿到了, 直接SetPixel()就好咯~

0x3 进阶代码 - 指针法

上面的代码跑起来有个最大的问题就是..........太鸡儿慢了........本身GetPixel()就慢如蜗牛  我们还要在两张图中GetPixel().....

笔者的电脑上处理一张1.21M 1024*1024大小的图片耗时2.4秒左右 这怎么能受得了

翻阅前辈资料后决定使用指针法来代替获取像素

使用指针必须在项目设置中勾选允许不安全的代码  并且涉及到指针操作的代码必须放在  unsafe {.....} 区域中  不然不让编译~

代码如下:

 1 public unsafe Bitmap mergeImage(Bitmap rgbTexture,Bitmap alphaTexture)
 2 {
 3     int width = rgbTexture.Width;
 4     int height = rgbTexture.Height;
 5
 6     try
 7     {
 8         BitmapData textureWithAlphaData = rgbTexture.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);    //将图像锁定到内存中以便操作
 9         BitmapData alphaTextureData = alphaTexture.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
10
11         byte* resultP = (byte*)textureWithAlphaData.Scan0;  //获取在内存的中首地址
12         byte* alphaP = (byte*)alphaTextureData.Scan0;
13
14         for (int j = 0; j < height; j++)
15         {
16             for (int i = 0; i < width; i++)
17             {
18                 resultP[3] = alphaP[2]; //ARBG在内存中存储顺序为GBRA 所以resultP[3]即为Alpha分量  resultP[2]即为红色分量
19                 resultP += 4;   //下移4个位置 处理下一个像素的信息
20                 alphaP += 4;
21             }
22         }
23
24         rgbTexture.UnlockBits(textureWithAlphaData);    //解锁
25         alphaTexture.UnlockBits(alphaTextureData);
26
27         return rgbTexture;
28     }
29     catch
30     {
31         return rgbTexture;
32     }
33 }   //mergeImage()

其中第8行要注意第二个参数设置成读写或只写(ImageLockMode.WriteOnly), 第三个参数因为这里需要带透明通道的ARGB格式所以设置成32位AGRB

第9行就可以设置成只读了(ImageLockMode.ReadOnly) 而且因为本身读的图片就不带Alpha通道 也可以将格式设置成 PixelFormat.Format32bppRgb

第18行注意内存中32位ARGB格式Bitmap的次序为[G,B,R,A] 所以Alpha分量其实在第四个位置

另外一定要注意释放资源  笔者放在了这个方法外面 不然处理数量一多分分钟内存爆炸

1 rgbTexture.Dispose();
2 alphaTexture.Dispose();
3 textureWithAlpha.Dispose();

0x4 指针法补充

本文的例子由于需要Alpha通道, 所以直接采用了32位ARGB格式, 32位的Bitmap中有每像素占用四个字节, 每行数据的长度必定为4的倍数,所以不用考虑对齐

而另外也很常见的24位图每个像素占用的字节数就为24/8 = 3, 这时候每行的数据长度就不一定为4的倍数了

举个栗子: 一张 10 * 10 的24位图片  每一行的数据长度为 3 * 10 = 30 字节  这时就会自动用"0"补充到32字节, 一共补充了 32 - 30 = 2个字节

那么如果在这种情况下使用指针来读取数据, 就必须跳过这些补位的字节  每行的实际字节数的获取方法为 BitmapData类中的属性 BitmapData.Stride

所以要处理24位的图片时, 需要在第一层for循环结尾处(即每行结尾处跳过占位的字节  如上面那个栗子就要跳2个字节)

代码如下 可以跟0x3中的对比一下

 1 byte* resultP = (byte*)textureWithAlphaData.Scan0;  //获取在内存的中首地址
 2 byte* alphaP = (byte*)alphaTextureData.Scan0;
 3
 4 int resultOffset = textureWithAlphaData.Stride - width * 3; //用实际占位过的长度来减去图片每行有效像素占用的长度  此行代码仅对24位图有效
 5 int alphaOffset = textureWithAlphaData.Stride - width * 3;
 6
 7 for (int j = 0; j < height; j++)
 8 {
 9     for (int i = 0; i < width; i++)
10     {
11         resultP[2] = alphaP[2]; //RBG在内存中存储顺序为GBR  这里仅仅举栗子方便对比  这行代码跑过之后会将2图的红色分量设置到1图中去
12         resultP += 3;   //下移3个位置 处理下一个像素的信息
13         alphaP += 3;
14     }
15     resultP += resultOffset;    //由于上面用的是图片的宽度  所以现在指针停在了占位字节的前面  所以这里需要跳过多出的字节
16     alphaP += alphaOffset;
17 }

0x5 结尾

代码地址: https://github.com/yyuueexxiinngg/HSoD2TextureMerge

用到的提取工具:https://github.com/Perfare/UnityStudio

两种方式的耗时对比如图:   (右键新标签打开)

原文地址:https://www.cnblogs.com/yyuueexxiinngg/p/8483949.html

时间: 2024-10-16 21:01:34

崩坏学园2及大部分采用ETC1压缩格式的Unity3D游戏的拆包图处理的相关文章

[Unity3D]关于U3D贴图格式压缩

http://blog.sina.com.cn/s/blog_5b6cb9500102vi6i.html 因为有不少人都问过我压缩格式的问题,今天飞哥又重新提醒了一次.整理一下发个贴,以供大家查阅和讨论. 各种纹理格式,大家参照下U3D MANUAL里面的具体描述介绍,这是官方的东西.但我觉得有一部内容是错的,例如占用内存大小. http://docs.unity3d.com/Manual/class-TextureImporter.html 基本知识点: DXT格式是Nvidia Tegra提

崩坏学园2八重之樱怎么打 玩法攻略分享

崩坏学园21.6八重の樱攻略 我们首先要了解规则,规则才是最重要的.跟据测试服,八重之樱的开放时间在周5到周日的每晚9点到11点.而这两个小时当中,分三个阶段.因为每个阶段打 的关卡都不一样,第一阶段在9点开始,第二阶段在9点40开始,第三阶段在10点20开始,因此,要想打完该关卡的孩子要注意时间安排. 八重之樱的关卡规则还是比较简单的,只要在该关卡活过180秒就行了,也就是3分钟.当然,即使活不过那3分钟,也会得到相应的积分,所以有些朋友即使打不过八重之樱也是很正常的,这个关卡我可以说,如果第

常用纹理和纹理压缩格式

转载至: http://blog.csdn.net/ynnmnm/article/details/44983545 by 夜风 简单纹理格式 RGBA8888 每个像素4字节,RGBA通道各占用8位 RGBA4444 每个像素2字节,RGBA通道各占用4位 RGB888 每个像素3字节,RGB通道各占用8位,无透明通道 RGB565 每个像素2字节,RGB通道各占用5/6/5位,无透明通道 RGBA5551 每个像素2字节,RGB通道各占用5位,透明通道1位,所以要么完全透明要么不透明 DXT纹

移动平台纹理压缩格式选择

1)移动平台纹理压缩格式选择2)Unity 2018是否在Mali GPU上支持Alpha 8格式3)如何在Unity自带的Navmesh上获取地面高度4)ParticleSystem无法重新播放5)UI开发中按界面的打开顺序返回到上级面板的问题 Texture Q:在这之前了解过纹理压缩的相关知识和UWA的一些推荐方式.但还是有一点小的疑问,所以在这里再次提出来,希望得到解答. 在纹理压缩格式的选择上,如果Android选用ETC,iOS选用PVRTC,因为有2的次方(ETC1和PVRTC)长

为啥我们的项目中从来不使用ETC1纹理格式

我们通过不同格式的效果来比较下就明白了 无压缩RGBA32格式的效果(所有GPU支持) 请无视接缝问题,那个是美术的UV给展错了... ATC压缩格式(Adreno系列GPU支持的格式) 肉眼几乎干不出差别,饱和度有稍许下降 PS:DXT.PVR的效果也差不多是这样,这里就不贴了,有兴趣的人自己可以试验下 最后是万恶的ETC1(所有GPU支持) 注意下图的红色箭头所指的线,这个仅出现在ETC1格式上,怀疑是它的压缩算法有缺陷,但具体原因并未做过调查 结论 这东西根本不能用啊,特别是对比度比较大的

音视频开发的压缩格式分析

网络摄像机和音视频开发作为网络应用的新型产品,适应网络传输的要求也必然成为产品开发的重要因素,而这其中视频图像的技术又成为关键.在目前中国网络摄像机和音视频开发的产品市场上,各种压缩技术百花齐放,且各有优势,为用户提供了很大的选择空间.AnyChat音视频开发平台使用的就是h.264主流的音视频编解码.现在小编整理几种视频的压缩格式进行对比分析:         JPEG.M-JPEG 有相当一部分国内外网络摄像机和音视频开发都是采用JPEG,Motion-JPEG压缩技术,JPEG.M-JPE

hadoop中4种压缩格式的特征的比较

1 gzip压缩 优点:压缩率比较高,而且压缩/解压速度也比较快:hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样:有hadoop native库:大部分linux系统都自带gzip命令,使用方便. 缺点:不支持split. 应用场景:当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用gzip压缩格式.譬如说一天或者一个小时的日志压缩成一个gzip 文件,运行mapreduce程序的时候通过多个gzip文件达到并发.hive程序,streaming程序,和j

ORACLE自动备份-压缩格式,定期删除.bat

@echo off echo ================================================ echo Windows环境下Oracle数据库的自动备份脚本--YPWANG echo 1. 使用当前日期命名备份文件. echo 2. 采用WinRAR压缩DMP和LOG文件echo 3. 自动删除30天前的备份.(可修改)echo ================================================ ::以“YYYYMMDD”格式取出当

采用[ICONIX] 方法实践分析和设计之六 [时序图](转)

采用[ICONIX] 方法实践BLOG设计之六 [时序图] 在前几篇文章中,我们分别进行了域模型和用例建模,并使用 Robustness工具进一步分析验证了相应用例的处理流程,并在相应模型(域模型)的基础上,通过Robustness方法引入相关的边界对象,控制对象(控制器),并更新了相应域模型中类的属性(字段).下面就可以进入到交互建模阶段了.如下图:    作为交互建模本身,就是要通过寻找对象之间的交互关系,进而进行方法(操作或行为)分配.    正所谓"只有在所有的用例为所有事件进程建立了交