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) XTexture.pinstance_ = new XTexture(); 21 return XTexture.pinstance_; 22 } 23 constructor() { 24 this.ximages = []; 25 this.allCuts = {}; 26 this.tmpCutid = 0; 27 this.aiCutid = 100; 28 29 this.textImgs = {}; 30 this.textIdxs = {}; 31 32 this.textTmpRid = this.loadTexture(1024, 256); 33 this.bfirst = true; 34 35 this.textCvs = document.createElement('canvas'); 36 this.textCvs.width = 1024; 37 this.textCvs.height = 256; 38 this.textCtx = this.textCvs.getContext('2d', { willReadFrequently: true }); 39 this.textCtx.textBaseline = 'top'; 40 this.textCtx.textAlign = 'left'; 41 } 42 static initTextureStatus(tex) { 43 gl.activeTexture(gl.TEXTURE0); 44 gl.bindTexture(gl.TEXTURE_2D, tex); 45 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 46 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 47 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 48 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 49 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 50 } 51 loadTextureFromImage(path, keepdata = false) { 52 if (path === 'CUSTOM_TEXTURE_1') { 53 var rid = this.ximages.length; 54 55 var texture = gl.createTexture(); 56 XTexture.initTextureStatus(texture); 57 58 let tmp = new Uint8Array([255, 255, 255, 255]); 59 gl.texImage2D( 60 gl.TEXTURE_2D, 61 0, 62 gl.RGBA, 63 1, 64 1, 65 0, 66 gl.RGBA, 67 gl.UNSIGNED_BYTE, 68 tmp 69 ); 70 71 this.ximages[rid] = { stat: 1, path: path, tex: texture, w: 1, h: 1 }; 72 return rid; 73 } else { 74 for (let i = 0; i < this.ximages.length; i++) { 75 if (this.ximages[i]['path'] === path) { 76 return i; 77 } 78 } 79 var rid = this.ximages.length; 80 this.ximages[rid] = { stat: 0, path: path, tex: null }; 81 var image = new Image(); 82 image.src = path; //"http://localhost:8910/"+ 83 image.onload = function () { 84 var texture = gl.createTexture(); 85 XTexture.initTextureStatus(texture); 86 87 gl.texImage2D( 88 gl.TEXTURE_2D, 89 0, 90 gl.RGBA, 91 gl.RGBA, 92 gl.UNSIGNED_BYTE, 93 image 94 ); 95 96 XTexture.pinstance_.ximages[rid].tex = texture; 97 XTexture.pinstance_.ximages[rid].img = image; 98 XTexture.pinstance_.ximages[rid].stat = 1; 99 XTexture.pinstance_.ximages[rid].w = image.width; 100 XTexture.pinstance_.ximages[rid].h = image.height; 101 }; 102 return rid; 103 } 104 } 105 TmpCut(rid, x = 0, y = 0, w = -1, h = -1, ww = 1024, hh = 1024) { 106 if (this.ximages[rid].stat !== 1) return -1; 107 108 if (w === -1) w = ww; 109 if (h === -1) h = hh; 110 this.allCuts[this.tmpCutid] = { 111 rid: rid, 112 x: x, 113 y: y, 114 w: w, 115 h: h, 116 u0: x / ww, 117 v0: y / hh, 118 u1: (x + w) / ww, 119 v1: y / hh, 120 u2: (x + w) / ww, 121 v2: (y + h) / hh, 122 u3: x / ww, 123 v3: (y + h) / hh, 124 }; 125 this.tmpCutid += 1; 126 return this.tmpCutid - 1; 127 } 128 makeCut(rid, x = 0, y = 0, w = -1, h = -1, ww = -1, hh = -1) { 129 if (ww === -1) ww = this.ximages[rid].w; 130 if (hh === -1) hh = this.ximages[rid].h; 131 if (w === -1) w = ww; 132 if (h === -1) h = hh; 133 this.allCuts[this.aiCutid] = { 134 rid: rid, 135 x: x, 136 y: y, 137 w: w, 138 h: h, 139 u0: x / ww, 140 v0: y / hh, 141 u1: (x + w) / ww, 142 v1: y / hh, 143 u2: (x + w) / ww, 144 v2: (y + h) / hh, 145 u3: x / ww, 146 v3: (y + h) / hh, 147 }; 148 this.aiCutid += 1; 149 return this.aiCutid - 1; 150 } 151 timenow() { 152 let myDate = new Date(); 153 return myDate.getTime() / 1000; 154 } 155 156 PutTexture(tex, w, h) { 157 var rid = this.ximages.length; 158 this.ximages[rid] = { stat: 1, path: 'put' + rid, tex: tex, w: w, h: h }; 159 return rid; 160 } 161 162 loadTexture(width, height) { 163 var rid = this.ximages.length; 164 165 var texture = gl.createTexture(); 166 XTexture.initTextureStatus(texture); 167 gl.texImage2D( 168 gl.TEXTURE_2D, 169 0, 170 gl.RGBA, 171 width, 172 height, 173 0, 174 gl.RGBA, 175 gl.UNSIGNED_BYTE, 176 null 177 ); 178 179 this.ximages[rid] = { 180 stat: 1, 181 path: 'default' + rid, 182 tex: texture, 183 w: width, 184 h: height, 185 }; 186 return rid; 187 } 188 initTextImageData(s, size) { 189 this.textCtx.clearRect(0, 0, 1024, 256); 190 this.textCtx.font = size + "px '宋体'"; 191 this.textCtx.fillStyle = 'rgba(255,255,255,1)'; 192 this.textCtx.fillText(s, 1, 1); 193 let imgd = this.textCtx.getImageData(0, 0, 1024, 256).data; 194 let w = 1024; 195 let h = size + 5; 196 let x = 256; 197 while (x === 256) { 198 h -= 1; 199 for (x = 0; x < 128; x++) { 200 let p = (h * 1024 + x) * 4; 201 if (imgd[p] !== 0) break; 202 } 203 } 204 let y = h; 205 while (y === h) { 206 w -= 1; 207 for (y = 0; y < h; y++) { 208 let p = (y * 1024 + w) * 4; 209 if (imgd[p] !== 0) break; 210 } 211 } 212 return this.textCtx.getImageData(0, 0, w + 1, h + 1); 213 } 214 getText(s, size) { 215 let textIdx = s + size; 216 217 if (textIdx in this.textIdxs) { 218 this.textIdxs[textIdx].time = this.timenow(); 219 return this.textIdxs[textIdx].cid; 220 } 221 let imgd = this.initTextImageData(s, size); 222 let w = imgd.width; 223 let h = imgd.height; 224 let useHeight = Math.floor((h + 31) / 32); 225 let mask = 0; 226 for (let i = 0; i < useHeight; i++) mask |= 1 << i; 227 let rid = -1; 228 let off = -1; 229 for (let k in this.textImgs) { 230 for (let i = 0; i < 32 - useHeight + 1; i++) { 231 if ((this.textImgs[k].mask & (mask << i)) === 0) { 232 off = i; 233 break; 234 } 235 } 236 if (off !== -1) { 237 rid = k; 238 break; 239 } 240 } 241 if (rid === -1) { 242 rid = this.loadTexture(1024, 1024); 243 this.textImgs[rid] = { mask: 0 }; 244 off = 0; 245 } 246 let cid = this.makeCut(rid, 0, off * 32, w, h); 247 this.textImgs[rid]['mask'] |= 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 > 3) { 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 static ExpandColor(c) { 271 return [ 272 ((c >> 16) & 0xff) / 255, 273 ((c >> 8) & 0xff) / 255, 274 (c & 0xff) / 255, 275 ((c >> 24) & 0xff) / 255];//r,g,b,a 276 } 277} 278XTexture.pinstance_ = null; 279