1/* 2 * Copyright (c) 2023 Huawei Device 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', { willReadFrequently: true }); 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 let rid = this.ximages.length; 56 57 let 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 let rid = this.ximages.length; 82 this.ximages[rid] = { stat: 0, path: path, tex: null }; 83 let image = new Image(); 84 image.src = path; //"http://localhost:8910/"+ 85 image.onload = function () { 86 let 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.allCuts[this.tmpCutid] = { 119 rid: rid, 120 x: x, 121 y: y, 122 w: w, 123 h: h, 124 u0: x / ww, 125 v0: y / hh, 126 u1: (x + w) / ww, 127 v1: y / hh, 128 u2: (x + w) / ww, 129 v2: (y + h) / hh, 130 u3: x / ww, 131 v3: (y + h) / hh, 132 }; 133 this.tmpCutid += 1; 134 return this.tmpCutid - 1; 135 } 136 makeCut(rid, x = 0, y = 0, w = -1, h = -1, ww = -1, hh = -1) { 137 if (ww === -1) { 138 ww = this.ximages[rid].w; 139 } 140 if (hh === -1) { 141 hh = this.ximages[rid].h; 142 } 143 if (w ===-1) { 144 w = ww; 145 } 146 if (h === -1) { 147 h = hh; 148 } 149 this.allCuts[this.aiCutid] = { 150 rid: rid, 151 x: x, 152 y: y, 153 w: w, 154 h: h, 155 u0: x / ww, 156 v0: y / hh, 157 u1: (x + w) / ww, 158 v1: y / hh, 159 u2: (x + w) / ww, 160 v2: (y + h) / hh, 161 u3: x / ww, 162 v3: (y + h) / hh, 163 }; 164 this.aiCutid += 1; 165 return this.aiCutid - 1; 166 } 167 timenow() { 168 let myDate = new Date(); 169 return myDate.getTime() / 1000; 170 } 171 172 PutTexture(tex, w, h) { 173 let rid = this.ximages.length; 174 this.ximages[rid] = { stat: 1, path: 'put' + rid, tex: tex, w: w, h: h }; 175 return rid; 176 } 177 178 loadTexture(width, height) { 179 let rid = this.ximages.length; 180 181 let texture = gl.createTexture(); 182 XTexture.initTextureStatus(texture); 183 gl.texImage2D( 184 gl.TEXTURE_2D, 185 0, 186 gl.RGBA, 187 width, 188 height, 189 0, 190 gl.RGBA, 191 gl.UNSIGNED_BYTE, 192 null 193 ); 194 195 this.ximages[rid] = { 196 stat: 1, 197 path: 'default' + rid, 198 tex: texture, 199 w: width, 200 h: height, 201 }; 202 return rid; 203 } 204 initTextImageData(s, size) { 205 this.textCtx.clearRect(0, 0, 1024, 256); 206 this.textCtx.font = size + "px '宋体'"; 207 this.textCtx.fillStyle = 'rgba(255,255,255,1)'; 208 this.textCtx.fillText(s, 1, 1); 209 let imgd = this.textCtx.getImageData(0, 0, 1024, 256).data; 210 let w = 1024; 211 let h = size + 5; 212 let x = 256; 213 while (x === 256) { 214 h -= 1; 215 for (x = 0; x < 128; x++) { 216 let p = (h * 1024 + x) * 4; 217 if (imgd[p] !== 0) { 218 break; 219 } 220 } 221 } 222 let y = h; 223 while (y === h) { 224 w -= 1; 225 for (y = 0; y < h; y++) { 226 let p = (y * 1024 + w) * 4; 227 if (imgd[p] !== 0) { 228 break; 229 } 230 } 231 } 232 return this.textCtx.getImageData(0, 0, w + 1, h + 1); 233 } 234 getText(s, size) { 235 let textIdx = s + size; 236 237 if (textIdx in this.textIdxs) { 238 this.textIdxs[textIdx].time = this.timenow(); 239 return this.textIdxs[textIdx].cid; 240 } 241 let imgd = this.initTextImageData(s, size); 242 let w = imgd.width; 243 let h = imgd.height; 244 let useHeight = Math.floor((h + 31) / 32); 245 let mask = 0; 246 for (let i = 0; i < useHeight; i++) mask |= 1 << i; 247 let rid = -1; 248 let off = -1; 249 for (let k in this.textImgs) { 250 for (let i = 0; i < 32 - useHeight + 1; i++) { 251 if ((this.textImgs[k].mask & (mask << i)) === 0) { 252 off = i; 253 break; 254 } 255 } 256 if (off !== -1) { 257 rid = k; 258 break; 259 } 260 } 261 if (rid === -1) { 262 rid = this.loadTexture(1024, 1024); 263 this.textImgs[rid] = { mask: 0 }; 264 off = 0; 265 } 266 let cid = this.makeCut(rid, 0, off * 32, w, h); 267 this.textImgs[rid]['mask'] |= mask << off; 268 this.textIdxs[textIdx] = { cid: cid, rid: rid, mask: mask << off, time: this.timenow(), }; 269 gl.activeTexture(gl.TEXTURE0); 270 gl.bindTexture(gl.TEXTURE_2D, this.ximages[rid].tex); 271 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 272 gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, off * 32, gl.RGBA, gl.UNSIGNED_BYTE, imgd); 273 return cid; 274 } 275 _FreshText() { 276 this.tmpCutid = 0; 277 let nt = this.timenow(); 278 let rm = []; 279 for (let idx in this.textIdxs) { 280 if (nt - this.textIdxs[idx].time > 3) { 281 this.textImgs[this.textIdxs[idx].rid].mask &= ~this.textIdxs[idx].mask; 282 delete this.allCuts[this.textIdxs[idx].cid]; 283 rm.push(idx); 284 } 285 } 286 for (let idx in rm) { 287 delete this.textIdxs[rm[idx]]; 288 } 289 } 290 static ExpandColor(c) { 291 return [ 292 ((c >> 16) & 0xff) / 255, 293 ((c >> 8) & 0xff) / 255, 294 (c & 0xff) / 255, 295 ((c >> 24) & 0xff) / 255]; //r,g,b,a 296 } 297} 298XTexture.pinstance_ = null; 299