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