1/* 2 * Copyright (c) 2022-2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { gl } from '../GLFrame.js'; 17 18export class XTexture { 19 static gi() { 20 if (XTexture.pinstance_ === null) { 21 XTexture.pinstance_ = new XTexture(); 22 } 23 return XTexture.pinstance_; 24 } 25 constructor() { 26 this.ximages = []; 27 this.allCuts = {}; 28 this.tmpCutid = 0; 29 this.aiCutid = 100; 30 31 this.textImgs = {}; 32 this.textIdxs = {}; 33 34 this.textTmpRid = this.loadTexture(1024, 256); 35 this.bfirst = true; 36 37 this.textCvs = document.createElement('canvas'); 38 this.textCvs.width = 1024; 39 this.textCvs.height = 256; 40 this.textCtx = this.textCvs.getContext('2d'); 41 this.textCtx.textBaseline = 'top'; 42 this.textCtx.textAlign = 'left'; 43 } 44 static initTextureStatus(tex) { 45 gl.activeTexture(gl.TEXTURE0); 46 gl.bindTexture(gl.TEXTURE_2D, tex); 47 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 48 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 49 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 50 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 51 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 52 } 53 loadTextureFromImage(path, keepdata = false) { 54 if (path === 'CUSTOM_TEXTURE_1') { 55 var rid = this.ximages.length; 56 57 var texture = gl.createTexture(); 58 XTexture.initTextureStatus(texture); 59 60 let tmp = new Uint8Array([255, 255, 255, 255]); 61 gl.texImage2D( 62 gl.TEXTURE_2D, 63 0, 64 gl.RGBA, 65 1, 66 1, 67 0, 68 gl.RGBA, 69 gl.UNSIGNED_BYTE, 70 tmp 71 ); 72 73 this.ximages[rid] = { stat: 1, path: path, tex: texture, w: 1, h: 1 }; 74 return rid; 75 } else { 76 for (let i = 0; i < this.ximages.length; i++) { 77 if (this.ximages[i].path === path) { 78 return i; 79 } 80 } 81 var rid = this.ximages.length; 82 this.ximages[rid] = { stat: 0, path: path, tex: null }; 83 var image = new Image(); 84 image.src = path; //"http://localhost:8910/"+ 85 image.onload = function () { 86 var texture = gl.createTexture(); 87 XTexture.initTextureStatus(texture); 88 89 gl.texImage2D( 90 gl.TEXTURE_2D, 91 0, 92 gl.RGBA, 93 gl.RGBA, 94 gl.UNSIGNED_BYTE, 95 image 96 ); 97 98 XTexture.pinstance_.ximages[rid].tex = texture; 99 XTexture.pinstance_.ximages[rid].img = image; 100 XTexture.pinstance_.ximages[rid].stat = 1; 101 XTexture.pinstance_.ximages[rid].w = image.width; 102 XTexture.pinstance_.ximages[rid].h = image.height; 103 }; 104 return rid; 105 } 106 } 107 TmpCut(rid, x = 0, y = 0, w = -1, h = -1, ww = 1024, hh = 1024) { 108 if (this.ximages[rid].stat !== 1) { 109 return -1; 110 } 111 112 if (w === -1) { 113 w = ww; 114 } 115 if (h === -1) { 116 h = hh; 117 } 118 this.callAllCuts(this.tmpCutid, rid, x, y, w, h, ww, hh); 119 this.tmpCutid += 1; 120 return this.tmpCutid - 1; 121 } 122 callAllCuts(param, rid, x, y, w, h, ww, hh) { 123 this.allCuts[param] = { 124 rid: rid, x: x, y: y, w: w, h: h, u0: x / ww, v0: y / hh, u1: (x + w) / ww, v1: y / hh, 125 u2: (x + w) / ww, v2: (y + h) / hh, u3: x / ww, v3: (y + h) / hh, 126 }; 127 } 128 makeCut(rid, x = 0, y = 0, w = -1, h = -1, ww = -1, hh = -1) { 129 if (ww === -1) { 130 ww = this.ximages[rid].w; 131 } 132 if (hh === -1) { 133 hh = this.ximages[rid].h; 134 } 135 if (w === -1) { 136 w = ww; 137 } 138 if (h === -1) { 139 h = hh; 140 } 141 this.callAllCuts(this.aiCutid, rid, x, y, w, h, ww, hh); 142 this.aiCutid += 1; 143 return this.aiCutid - 1; 144 } 145 timenow() { 146 let myDate = new Date(); 147 return myDate.getTime() / 1000; 148 } 149 150 PutTexture(tex, w, h) { 151 var rid = this.ximages.length; 152 this.ximages[rid] = { stat: 1, path: 'put' + rid, tex: tex, w: w, h: h }; 153 return rid; 154 } 155 156 loadTexture(width, height) { 157 var rid = this.ximages.length; 158 159 var texture = gl.createTexture(); 160 XTexture.initTextureStatus(texture); 161 gl.texImage2D( 162 gl.TEXTURE_2D, 163 0, 164 gl.RGBA, 165 width, 166 height, 167 0, 168 gl.RGBA, 169 gl.UNSIGNED_BYTE, 170 null 171 ); 172 173 this.ximages[rid] = { 174 stat: 1, 175 path: 'default' + rid, 176 tex: texture, 177 w: width, 178 h: height, 179 }; 180 return rid; 181 } 182 initTextImageData(s, size) { 183 this.textCtx.clearRect(0, 0, 1024, 256); 184 this.textCtx.font = size + "px 'Microsoft YaHei'"; 185 this.textCtx.fillStyle = 'rgba(255,255,255,1)'; 186 this.textCtx.fillText(s, 1, 1); 187 let imgd = this.textCtx.getImageData(0, 0, 1024, 256).data; 188 let w = 1024; 189 let h = size + 5; 190 let x = 128; 191 let lenMax = 128; 192 while (x === lenMax) { 193 h -= 1; 194 for (x = 0; x < lenMax; x++) { 195 let p = (h * 1024 + x) * 4; 196 if (imgd[p] !== 0) { 197 break; 198 } 199 } 200 } 201 let y = h; 202 while (y === h) { 203 w -= 1; 204 for (y = 0; y < h; y++) { 205 let p = (y * 1024 + w) * 4; 206 if (imgd[p] !== 0) { 207 break; 208 } 209 } 210 } 211 return this.textCtx.getImageData(0, 0, w + 1, h + 1); 212 } 213 getText(s, size) { 214 let textIdx = s + size; 215 216 if (textIdx in this.textIdxs) { 217 this.textIdxs[textIdx].time = this.timenow(); 218 return this.textIdxs[textIdx].cid; 219 } 220 let imgd = this.initTextImageData(s, size); 221 let w = imgd.width; 222 let h = imgd.height; 223 let useHeight = Math.floor((h + 31) / 32); 224 let mask = 0; 225 for (let i = 0; i < useHeight; i++) mask |= 1 << i; 226 let rid = -1; 227 let off = -1; 228 for (let k in this.textImgs) { 229 for (let i = 0; i < 32 - useHeight + 1; i++) { 230 if ((this.textImgs[k].mask & (mask << i)) == 0) { 231 off = i; 232 break; 233 } 234 } 235 if (off !== -1) { 236 rid = k; 237 break; 238 } 239 } 240 if (rid === -1) { 241 rid = this.loadTexture(1024, 1024); 242 this.textImgs[rid] = { mask: 0 }; 243 off = 0; 244 } 245 let cid = this.makeCut(rid, 0, off * 32, w, h); 246 let smask = 'mask'; 247 this.textImgs[rid][smask] |= mask << off; 248 this.textIdxs[textIdx] = {cid: cid, rid: rid, mask: mask << off, time: this.timenow(),}; 249 gl.activeTexture(gl.TEXTURE0); 250 gl.bindTexture(gl.TEXTURE_2D, this.ximages[rid].tex); 251 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 252 gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, off * 32, gl.RGBA, gl.UNSIGNED_BYTE, imgd); 253 return cid; 254 } 255 _FreshText() { 256 this.tmpCutid = 0; 257 let nt = this.timenow(); 258 let rm = []; 259 for (let idx in this.textIdxs) { 260 if (nt - this.textIdxs[idx].time > 10) { 261 this.textImgs[this.textIdxs[idx].rid].mask &= ~this.textIdxs[idx].mask; 262 delete this.allCuts[this.textIdxs[idx].cid]; 263 rm.push(idx); 264 } 265 } 266 for (let idx in rm) { 267 delete this.textIdxs[rm[idx]]; 268 } 269 } 270} 271XTexture.pinstance_ = null; 272