| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- import { getError, gfx, SpriteFrame } from "cc";
- import { XIAOMI } from "cc/env";
- /**
- * @en The image source, can be HTML canvas, image type or image in memory data
- * @zh 图像资源的原始图像源。可以来源于 HTML 元素也可以来源于内存。
- */
- type ImageSource = HTMLCanvasElement | HTMLImageElement | ImageBitmap; // 引擎解析到内存中可用的图像资源类型
- /**
- * @en The task for decryption
- * @zh 解密任务
- */
- type DecryptionTask = {
- name: string;
- image: HTMLImageElement;
- imgdata: ImageData;
- complete_call: (source: ImageSource)=>void;
- };
- const ccwindow: typeof window = typeof globalThis.jsb !== 'undefined' ? (typeof jsb.window !== 'undefined' ? jsb.window : globalThis) : globalThis;
- /** 解密序列,要与加密脚本中的设置保持一致 */
- const decryption_order = '3580419267'.split('').map(Number);
- /**
- * 解析图片
- * @param file
- * @param options
- * @param onComplete
- */
- export function parseImage(file: string|HTMLImageElement, options: any, onComplete: (err: any, img: ImageSource)=>void){
- if(task_list.length<=0){
- task_start_time = Date.now();
- }
- if(file){
- let start_time = Date.now();
- if(typeof file === 'string'){ // 一般原生环境中,并不直接传入图片资源,而是传入图片路径
- loadImage(file, (err, img)=>{
- let file_name = file.split('/').pop();
- logInfo('LoadImage', task_list.length, file_name, Date.now()-start_time);
- if(err){
- onComplete(err, null);
- }else{
- decryptionImage(file_name, img, source=>{
- onComplete(null, source);
- });
- }
- });
- }else if(file instanceof HTMLImageElement){ // 在浏览器环境中,直接传入图片资源
- let file_name = file.src.split('/').pop();
- logInfo('LoadImage', task_list.length, file_name, Date.now()-start_time);
- decryptionImage(file_name, file, source=>{
- onComplete(null, source);
- });
- }else{
- onComplete(getError(4930, file), null);
- }
- }else{
- // @ts-ignore
- onComplete(null, file);
- }
- }
- /**
- * 加载图片
- * @param url HTMLImageElement.src
- */
- function loadImage(url: string, loaddone: (err: any, image: HTMLImageElement)=>void){
- let img = new ccwindow.Image();
- if (ccwindow.location.protocol !== 'file:' || XIAOMI) {
- img.crossOrigin = 'anonymous';
- }
- function loadCallback() {
- img.removeEventListener('load', loadCallback);
- img.removeEventListener('error', errorCallback);
- loaddone(null, img);
- }
- function errorCallback() {
- img.removeEventListener('load', loadCallback);
- img.removeEventListener('error', errorCallback);
- loaddone(new Error(getError(4930, url)), null);
- }
- img.addEventListener('load', loadCallback);
- img.addEventListener('error', errorCallback);
- img.src = url;
- }
- /** 获取图片数据的离屏画布上下文对象 */
- var context2d_for_image_data: CanvasRenderingContext2D;
- /**
- * 获取图片数据
- * @param image
- */
- function getImageData(image: HTMLImageElement){
- if(!context2d_for_image_data){
- context2d_for_image_data = document.createElement('canvas').getContext('2d');
- }
- // 创建一个精灵帧
- let spf = SpriteFrame.createWithImage(image);
- // 创建一个缓冲区
- let buffer = new Uint8Array(image.width * image.height * 4);
- // 创建一个区域描述对象
- let region = new gfx.BufferTextureCopy();
- region.texOffset.x = 0;
- region.texOffset.y = 0;
- region.texExtent.width = image.width;
- region.texExtent.height = image.height;
- // 将纹理拷贝到缓冲区
- gfx.deviceManager.gfxDevice.copyTextureToBuffers(spf.getGFXTexture(), [buffer], [region]);
- // 创建一个ImageData对象
- let imageData = context2d_for_image_data.createImageData(image.width, image.height);
- // 将缓冲区数据拷贝到ImageData对象
- imageData.data.set(buffer);
- return imageData;
- }
- /**
- * 通过一个ImageData对象创建一个canvas资源
- * @param data
- * @param width
- * @param height
- */
- function createCanvasSource(data: ImageData, width: number, height: number){
- let canvas = ccwindow.document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- let ctx = canvas.getContext('2d');
- ctx.putImageData(data, 0, 0);
- return canvas;
- }
- /**
- * 解密图片
- * @param name
- * @param image
- * @param complete_call
- */
- function decryptionImage(name: string, image: HTMLImageElement, complete_call: (source: ImageSource)=>void){
- runTask({ name, image, imgdata: null, complete_call });
- }
- /** 解密任务列表 */
- const task_list: DecryptionTask[] = [];
- /** 解析完成的任务数量 */
- var idx_decrypt = 0;
- /** 资源创建完成的任务数量 */
- var idx_create = 0;
- /** 解析任务执行状态 */
- var ing_decrypt = false;
- /** 创建资源任务状态 */
- var ing_create = false;
- var task_start_time = 0;
- /**
- * 添加任务,并执行
- * @param task
- */
- function runTask(task: DecryptionTask){
- task_list.push(task);
- let start_time = Date.now();
- task.imgdata = getImageData(task.image);
- logInfo('GetImageData', task_list.length-1, task.name, Date.now()-start_time);
- runDecryption();
- }
- /** 执行解密任务 */
- function runDecryption(){
- if(ing_decrypt || idx_decrypt>=task_list.length){
- return void 0;
- }
- ing_decrypt = true;
- while(idx_decrypt < task_list.length){
- let task = task_list[idx_decrypt];
- let start_time = Date.now();
- let cut_step = Math.max(1, getUnderMaxPrime(Math.min(task.image.width, task.image.height)));
- decryptionBuffer(task.imgdata.data, decryption_order, cut_step);
- logInfo('Decryption', idx_decrypt, task.name, Date.now()-start_time);
- idx_decrypt++;
- }
- ing_decrypt = false;
- runCreateSource();
- }
- /** 执行创建资源任务 */
- function runCreateSource(){
- if(ing_create || idx_create>=idx_decrypt){
- return void 0;
- }
- ing_create = true;
- while(idx_create < idx_decrypt){
- let task = task_list[idx_create];
- let start_time = Date.now();
- task.complete_call(createCanvasSource(task.imgdata, task.image.width, task.image.height));
- logInfo('CreateSource', idx_create, task.name, Date.now()-start_time);
- idx_create++;
- }
- ing_create = false;
- if(idx_create>=task_list.length){
- logInfo('CurrtPartTask', idx_create, 'Done', Date.now()-task_start_time);
- }
- }
- /** 解密缓冲区 */
- var cache_buffer: Uint8Array|Uint8ClampedArray;
- /**
- * 解密缓冲区
- * @param buffer
- * @param order
- * @param cut_length
- */
- function decryptionBuffer(buffer: Uint8Array|Uint8ClampedArray, order: number[], cut_length: number){
- if(cut_length>=buffer.length || !(cut_length>0)){
- return buffer;
- }
- if(!cache_buffer || !(cache_buffer.length>=buffer.length)){
- cache_buffer = new Uint8Array(buffer.length);
- }
- let cut_count = Math.floor(buffer.length / cut_length);
- let idx_map_range: number;
- let idx_map: number[][];
- let offset = 0;
- do{
- idx_map = createDecryptionIdxMap(order, cut_count-offset);
- idx_map_range = idx_map[0].length * Math.pow(10, idx_map.length-1);
- for(let i = 0; i < idx_map_range; i++){
- let idx = (offset + i) * cut_length;
- let trans_idx = (offset + transformIndex(i, idx_map)) * cut_length;
- cache_buffer.set(buffer.slice(idx, idx+cut_length), trans_idx);
- }
- offset += idx_map_range;
- }while(offset < cut_count);
- buffer.set(cache_buffer.slice(0, cut_count * cut_length));
- return buffer;
- }
- /**
- * 转换索引
- * @param index
- * @param trans_map
- */
- function transformIndex(index: number, trans_map: number[][]){
- let list = index.toString().padStart(trans_map.length, '0').split('').map(el=>parseInt(el));
- let str = list.map((el, i)=>trans_map[i][el]).join('');
- return parseInt(str);
- }
- /**
- * 创建解密索引映射
- * @param length
- * @param order
- */
- function createDecryptionIdxMap(order: number[], length: number){
- let index_map = length.toString().split('')
- .map((el, idx)=>{
- let result: number[] = [];
- (new Array(idx==0 ? parseInt(el) : order.length))
- .fill(0).map((_, i)=>i)
- .sort((a, b)=>order.indexOf(a) - order.indexOf(b))
- .forEach((e, i)=> result[e] = i);
- return result;
- });
- return index_map;
- }
- /**
- * 获取小于指定值的最大素数
- * @param num
- */
- function getUnderMaxPrime(num: number){
- let value = num;
- let prime = Math.min(value, findMaxPrime(value));
- while(prime >= num){
- value = value-1;
- prime = Math.min(value, findMaxPrime(value));
- }
- return prime;
- }
- /**
- * 查找最大素数
- * @param num
- */
- function findMaxPrime(num: number){
- let max_prime = num;
- if(max_prime > 3){
- for(let i=max_prime; i>2; i--){
- let is_prime = true;
- for(let j=2; j*j<=i; j++){
- if(i % j == 0){
- is_prime = false;
- break;
- }
- }
- if(is_prime){
- max_prime = i;
- break;
- }
- }
- }else{
- max_prime = Math.max(2, max_prime);
- }
- return max_prime;
- }
- function logInfo(tag: string, index: number, name: string, time?: number){
- tag = '#' + index.toString().padStart(3, '0') + '@' + tag.padEnd(13, ' ') + '>>';
- if(time){
- tag += '|' + formatTime(time) + '|';
- }
- console.log(tag, name);
- }
- function formatTime(time: number){
- let s = Math.floor(time / 1000);
- let m = Math.floor(s / 60);
- let h = Math.floor(m / 60);
- return [h, m, s].map(v=>v.toString().padStart(2, '0')).join(':') + ' ' + (time % 1000).toString().padStart(3, '0') + 'ms';
- }
|