仿网站制作教学视频教程,专业企业网站建设价格,江西专业网站建设定制,做外贸网站卖什么好处图片
图片的格式
图片文件格式pngjpg
纹理压缩格式ETC1/2PVRTCASTC
图片的属性
图片属性解释分辨率宽高像素值#xff08;pt#xff09;#xff0c;如#xff1a;1024*1024位深度用来存储像素颜色的值#xff0c;如RGBA8888#xff0c;红黄蓝透明度4个维度每个8bitpt如1024*1024位深度用来存储像素颜色的值如RGBA8888红黄蓝透明度4个维度每个8bit一共就是32位一般使用的就是32位也被称为真彩色文件大小文件所占用的存储大小
图片的优化
图片的优化分为两种 文件大小优化这种优化会影响到包大小较小的图片大小对于手机存储容量和网络传输速度和时间会更友好。
一般的优化方式是使用压缩工具如pngquant、tinypng等直接压缩文件大小。 图片纹理优化
图片文件大小压缩不意味着读到内存中的大小会减少。
一般情况下以ARPG8888来说计算一个1024*1024分辨率的图片读到内存中的大小计算公式为
1024 * 1024 * 4 * 8 33,554,432 bit 4,194,304 byte ≈ 4 mb理解起来就是 1024 * 1024 个像素每个像素有 argb 4个通道每个通道含有 8 bit数据用来存储颜色值。
png格式不能直接被GPU识别需要在cpu把图片读进内存中解码后再传递给gpu使用这样做会造成一定的cpu消耗和很大的瞬时运行内存RAM占用。
因为大部分gpu对于压缩后的纹理有比较好的支持无需cpu解码占用内存小。于是我们要寻找一种合适的纹理压缩方式。
etc1不支持透明通道。etc2效果很差容易出现色块。PVRTC仅能在ios上使用.
这时候astc格式就展露在我们眼前IOS和安卓端都对astc有较好的支持率。
iPhone6及iPad mini 4以上iOS设备支持。大多数支持OpenGL ES 3.1或Vulkan的现代Android GPU也支持ASTC格式其中包括自Adreno 4xx / Snapdragon 4152015年起的高通GPU自Mali T6242012年起的ARM GPU自Tegra K12014年起的NVIDIA GPU以及自GX62502014年起的PowerVR GPU。
在运行内存中来说astc的内存使用能节省75%左右提升巨大。
cocoscreator中源码理解
creator引擎在编译时会把选中的纹理压缩类型记录到import文件夹下的json文件中多种图片格式以下划线‘_’区分开下图演示里选择了astc6x6格式和webp格式所以生成的类型就是
代码中在读取文件格式时会根据列举的文件类型判断当前机器是否支持。由下面代码图里能够看到734这里的7代表的就是第7个.astc4代表的就是第4个.webp。若当前设备不支持astc就会去寻找webp格式。 cocoscreator中源码整个下载流程的理解建议配合cocos2.4.11源码享用 一切的一切都要从bundle.js这个类说起类中的load函数会调用cc.assetManager.loadAny函数来下载相关资源并获取资源数据返回。 传递的参数是资源名paths类型type进度回调函数onProgress完成回调函数onComplete。 cc.assetManager是CCAssetManager类,这个类里有一个loadAny函数此函数简单封装了一个Task并把task传递到pipeline中。这里的pipeline是定义在shared.js中的new Pipeline(‘normal load’, [])一个名字叫做normal load的管线,异步执行任务管线填充在CCAssetManager类中pipeline.append(preprocess).append(load);包含一个preprocess和load方法。
task的状态为
{input: 资源名,onProgress: onProgress,onComplete: onComplete,options: {preset: default,__requestType__: path,type: type,bundle: bundle名,__outputAsArray__: false,}
}4. normal load–pipeline先调用preprocess这个函数里对Task的options属性做遍历这里也就是preset、requestType、type、bundle和__outputAsArray__五个key。 requestType、type、bundle和preset键值对保存到subOptions对象中__outputAsArray__和preset保存到leftOptions对象中并覆盖掉task的options属性。 新建一个subTaskinput为task.inputoptions为subOptions对象走transformPipeline管线流程同步执行任务获取的值设置为task.source和task.output。最后调用done()。
task的状态为
{input: 资源名,onProgress: onProgress,onComplete: onComplete,options: {preset: default,__outputAsArray__: false,},output: ,source: ,
}
subTask的状态为
{input: 资源名,options: {preset: default,__requestType__: path,type: type,bundle: bundle名,}
}5. transformPipeline里包含两个函数parse和combine。
parse管线任务先判断input若input为字符串新建一个无原型链对象item把item对象的【options.__requestType__值】也就是【RequestType.PATH】也就是【‘path’】设置为input值把options中的其他键值对都复制到item中这里包含四个键值对requestType、type、bundle和path。 再遍历item这里只有path这个key有处理其他key都过滤了通过bundle的_config找item.path值的info。这里的item.path也就是我们一开始传进来的资源名。info里包含了资源的uuid。 把config、info和uuid值填入到out对象中并把out对象push到task.output中。 管线会把task.output值赋值给task.input。
subTask的状态为
{options: {preset: default,__requestType__: path,type: type,bundle: bundle名,},input: [{config: bundle所有配置,uuid: 资源的uuid,info: {path: path, uuid: uuid}ext .json}]
}源码展示
function parse (task) {var input task.input, options task.options;input Array.isArray(input) ? input : [ input ];task.output [];for (var i 0; i input.length; i ) {var item input[i];var out RequestItem.create();if (typeof item string) {item Object.create(null);item[options.__requestType__ || RequestType.UUID] input[i];}if (typeof item object) {// local options will overlap glabal optionscc.js.addon(item, options);if (item.preset) {cc.js.addon(item, cc.assetManager.presets[item.preset]);}for (var key in item) {switch (key) {case RequestType.UUID: var uuid out.uuid decodeUuid(item.uuid);if (bundles.has(item.bundle)) {var config bundles.get(item.bundle)._config;var info config.getAssetInfo(uuid);if (info info.redirect) {if (!bundles.has(info.redirect)) throw new Error(Please load bundle ${info.redirect} first);config bundles.get(info.redirect)._config;info config.getAssetInfo(uuid);}out.config config;out.info info;}out.ext item.ext || .json;break;case __requestType__:case ext: case bundle:case preset:case type: break;case RequestType.DIR: if (bundles.has(item.bundle)) {var infos [];bundles.get(item.bundle)._config.getDirWithPath(item.dir, item.type, infos);for (let i 0, l infos.length; i l; i) {var info infos[i];input.push({uuid: info.uuid, __isNative__: false, ext: .json, bundle: item.bundle});}}out.recycle();out null;break;case RequestType.PATH: if (bundles.has(item.bundle)) {var config bundles.get(item.bundle)._config;var info config.getInfoWithPath(item.path, item.type);if (info info.redirect) {if (!bundles.has(info.redirect)) throw new Error(you need to load bundle ${info.redirect} first);config bundles.get(info.redirect)._config;info config.getAssetInfo(info.uuid);}if (!info) {out.recycle();throw new Error(Bundle ${item.bundle} doesnt contain ${item.path});}out.config config; out.uuid info.uuid;out.info info;}out.ext item.ext || .json;break;case RequestType.SCENE:if (bundles.has(item.bundle)) {var config bundles.get(item.bundle)._config;var info config.getSceneInfo(item.scene);if (info info.redirect) {if (!bundles.has(info.redirect)) throw new Error(you need to load bundle ${info.redirect} first);config bundles.get(info.redirect)._config;info config.getAssetInfo(info.uuid);}if (!info) {out.recycle();throw new Error(Bundle ${config.name} doesnt contain scene ${item.scene});}out.config config; out.uuid info.uuid;out.info info;}break;case __isNative__: out.isNative item.__isNative__;break;case RequestType.URL: out.url item.url;out.uuid item.uuid || item.url;out.ext item.ext || cc.path.extname(item.url);out.isNative item.__isNative__ ! undefined ? item.__isNative__ : true;break;default: out.options[key] item[key];}if (!out) break;}}if (!out) continue;task.output.push(out);if (!out.uuid !out.url) throw new Error(Can not parse this input: JSON.stringify(item));}return null;
}combine管线任务主要是拼出来完整的资源地址。
subTask的状态为
{options: {preset: default,__requestType__: path,type: type,bundle: bundle名,},output: [{config: bundle所有配置,uuid: 资源的uuid,info: {path: path, uuid: uuid}ext .jsonurl 具体地址}]
}
task的状态为
{input: 资源名,onProgress: onProgress,onComplete: onComplete,options: {preset: default,__outputAsArray__: false,},output: [{config: bundle所有配置,uuid: 资源的uuid,info: 具体信息ext .jsonurl 具体地址}],source: [{config: bundle所有配置,uuid: 资源的uuid,info: 具体信息ext .jsonurl 具体地址}],
}源码展示
function combine (task) {var input task.output task.input;for (var i 0; i input.length; i) {var item input[i];if (item.url) continue;var url , base ;var config item.config;if (item.isNative) {base (config config.nativeBase) ? (config.base config.nativeBase) : cc.assetManager.generalNativeBase;} else {base (config config.importBase) ? (config.base config.importBase) : cc.assetManager.generalImportBase;}let uuid item.uuid;var ver ;if (item.info) {if (item.isNative) {ver item.info.nativeVer ? (. item.info.nativeVer) : ;}else {ver item.info.ver ? (. item.info.ver) : ;}}// ugly hack, WeChat does not support loading font likes myfont.dw213.ttf. So append hash to directoryif (item.ext .ttf) {url ${base}/${uuid.slice(0, 2)}/${uuid}${ver}/${item.options.__nativeName__};}else {url ${base}/${uuid.slice(0, 2)}/${uuid}${ver}${item.ext};}item.url url;}return null;
}管线运行完毕返回subTask.output
回到normal load管线任务中的preprocess任务transformPipeline返回了对象设置成task的output和source值。把output的值赋值给input再把output设为null继续下一个normal load管线任务load。
task的状态为
{onProgress: onProgress,onComplete: onComplete,options: {preset: default,__outputAsArray__: false,},input: [{config: bundle所有配置,uuid: 资源的uuid,info: {path: path, uuid: uuid}ext .jsonurl 具体地址}],source: [{config: bundle所有配置,uuid: 资源的uuid,info: 具体信息ext .jsonurl 具体地址}],
}load函数里给task.options新增了progress和__exclude__字段progress用来记录资源下载进度。然后遍历每一个task.input对每一个需要load的资源新建了一个subTaskinput值为每一个task的input值onProgress为task.onProgressoptions为task.optionsprogress为task.progressonComplete为新的函数完成时调用更新progress中的值。再把这个subTask传递到loadOneAssetPipeline下载管道中。
subTask的状态为
{input: {config: bundle所有配置,uuid: 资源的uuid,info: {path: path, uuid: uuid}ext .jsonurl 具体地址},onProgress: onProgress,onComplete: newComplete,options: {preset: default,__outputAsArray__: false,__exclude__ {}},
}
task的状态为
{onProgress: onProgress,onComplete: onComplete,progress : { finish: 0, total: task.input.length, canInvoke: true },options: {preset: default,__outputAsArray__: false,__exclude__ {}},input: [{config: bundle所有配置,uuid: 资源的uuid,info: 具体信息ext .jsonurl 具体地址}],source: [{config: bundle所有配置,uuid: 资源的uuid,info: 具体信息ext .jsonurl 具体地址}],
}源码展示
function load (task, done) {let firstTask false;if (!task.progress) {task.progress { finish: 0, total: task.input.length, canInvoke: true };firstTask true;}var options task.options, progress task.progress;options.__exclude__ options.__exclude__ || Object.create(null);task.output [];forEach(task.input, function (item, cb) {let subTask Task.create({ input: item, onProgress: task.onProgress, options, progress, onComplete: function (err, item) {if (err !task.isFinish) {if (!cc.assetManager.force || firstTask) {if (!CC_EDITOR) {cc.error(err.message, err.stack);}progress.canInvoke false;done(err);}else {progress.canInvoke task.dispatch(progress, progress.finish, progress.total, item);}}task.output.push(item);subTask.recycle();cb();}});loadOneAssetPipeline.async(subTask);}, function () {options.__exclude__ null;if (task.isFinish) {clear(task, true);return task.dispatch(error);}gatherAsset(task);clear(task, true);done();});
}loadOneAssetPipeline下载单资源管线包含两个管线任务fetch和parse。fetch函数调用packManager.load获取资源数据。packManager.load先判断是否在files缓存中有无此id。由于我们在加载资源前必定已经加载了fgui的bin文件而我们都是选择合并json类型而bin文件下载时已经把依赖的json文件下载过了文件值都缓存到files中这里就直接拿到json信息了返回值赋值给task的file变量也就是包含资源类型的json串。进入下一个管线任务parse。parse函数中对未缓存的uuid资源调用parser.parse函数传递file值。其中又调用parser.parseImport函数传递file和options继续调用deserialize函数传递file和options继续调用cc.deserialize也就是deserialize-compiled文件中的deserialize函数传递file和options继续调用parseInstances继续调用deserializeCustomCCObject继续调用对象的_deserialize函数这里图片对象调用的就是CCTexture2D的_deserialize函数继续调用Texture2D._parseExt函数这个_parseExt函数里就做了判断对资源类型进行解析并判断设备是否支持纹理类型支持就返回该后缀。假设这里返回.png后缀设置Texture2D的_native属性为.png再设置其他属性如过滤方案并返回一个CCTexture2D对象。
这里会衍生到图片的其他属性值原串是这样的eg “0,9729,9729,33071,33071,0,0,1”第一个0表示图片的纹理类型0就是第0个.png。其他类型如下[‘.png’, ‘.jpg’, ‘.jpeg’, ‘.bmp’, ‘.webp’, ‘.pvr’, ‘.pkm’, ‘.astc’],第二个9729表示纹理缩小时设定的纹理过滤方案。const GL_NEAREST 9728;const GL_LINEAR 9729; 9728是最近点过滤采样时选择最近的点成本小但易产生锯齿。9729是线性过滤由4个颜色进行加权这种方法可以让纹理边缘看起来更平滑但需要进行更多的计算。第三个9729表示纹理放大时设定的纹理过滤方案。同上。第四个33071表示横轴纹理环绕方式。指定了当纹理坐标超出0到1的标准范围时该如何处理纹理的采样。10497是重复、33071是边缘拉伸、33648是镜像重复。第五个33071表示纵轴纹理环绕方式。同上。第六个0表示颜色是否预乘1表示预乘0表示非预乘。第七个0表示是否是否mipmaps。1表示使用0表示不使用。第八个1表示纹理是否参与合图。1表示参与0表示不参与。
parser.parse完成后调用loadDepends函数先为Texture2D添加addRef然后调用getDepends函数获取依赖再新建一个任务走下载管线完成实际图片加载。