gputex 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/testing.d.ts CHANGED
@@ -110,8 +110,10 @@ type ASTC4x4Block = Uint8Array;
110
110
  * grayscale + opaque → luminance, opaque → RGB, otherwise RGBA.
111
111
  *
112
112
  * Algorithm per class:
113
- * 1. Quantise input to 8-bit; classify.
114
- * 2. Farthest-pair over the class's channels → initial (e0, e1).
113
+ * 1. Quantise input to 8-bit; classify (opaque colour tries both CEM 8
114
+ * bit budgets and keeps the lower-error one).
115
+ * 2. Farthest-pair over the class's channels → initial (e0, e1),
116
+ * quantised to the class's endpoint range.
115
117
  * 3. Assign per-texel indices by nearest palette entry.
116
118
  * 4. One LSQ refit pass; accept only if total error strictly decreases.
117
119
  * 5. CEM 8/12: flip endpoints (and reflect weights) if
@@ -121,13 +123,14 @@ type ASTC4x4Block = Uint8Array;
121
123
  declare function encodeASTC4x4Block(pixels: ASTC4x4Pixels): ASTC4x4Block;
122
124
  /**
123
125
  * Decode an ASTC 4×4 block produced by this encoder (or by any other
124
- * encoder that respects our narrow subset: block modes 0x042/0x053/0x253,
125
- * single partition, CEM 0/8/12 with 8-bit endpoints). Handles the
126
+ * encoder that respects our narrow subset: block modes 0x042/0x053/0x242/
127
+ * 0x253, single partition, CEM 0/8/12 — QUANT_192 endpoints with 0x242,
128
+ * 8-bit endpoints otherwise). Handles the
126
129
  * blue-contraction branch even though our encoder doesn't produce it, so
127
130
  * externally-supplied blocks round-trip predictably.
128
131
  *
129
132
  * The CEM is validated against the block mode's expected pairing (we only
130
- * ever emit the three fixed combinations above).
133
+ * ever emit the four fixed combinations above).
131
134
  *
132
135
  * Output: 16 RGBA pixels as 64 floats in [0, 1].
133
136
  */
package/dist/testing.js CHANGED
@@ -922,33 +922,49 @@ function decodeBC7Block(block) {
922
922
  // src/astc4x4_ref.ts
923
923
  var BLOCK_MODE_4x4_2BIT = 66;
924
924
  var BLOCK_MODE_4x4_3BIT = 83;
925
+ var BLOCK_MODE_4x4_4BIT = 578;
925
926
  var BLOCK_MODE_4x4_5BIT = 595;
926
927
  var CEM_LUM_DIRECT = 0;
927
928
  var CEM_RGB_DIRECT = 8;
928
929
  var CEM_RGBA_DIRECT = 12;
929
930
  var WEIGHT_UNQ_4 = [0, 21, 43, 64];
930
931
  var WEIGHT_UNQ_8 = [0, 9, 18, 27, 37, 46, 55, 64];
932
+ var WEIGHT_UNQ_16 = Array.from({ length: 16 }, (_, w) => {
933
+ const u = w << 2 | w >> 2;
934
+ return u > 32 ? u + 1 : u;
935
+ });
931
936
  var WEIGHT_UNQ_32 = Array.from({ length: 32 }, (_, w) => w <= 15 ? 2 * w : 2 * w + 2);
932
937
  var CLASS_LUM = {
933
938
  cem: CEM_LUM_DIRECT,
934
939
  blockMode: BLOCK_MODE_4x4_5BIT,
935
940
  channels: [0],
936
941
  weightBits: 5,
937
- unq: WEIGHT_UNQ_32
942
+ unq: WEIGHT_UNQ_32,
943
+ endpointRange: 256
938
944
  };
939
945
  var CLASS_RGB = {
940
946
  cem: CEM_RGB_DIRECT,
941
947
  blockMode: BLOCK_MODE_4x4_3BIT,
942
948
  channels: [0, 1, 2],
943
949
  weightBits: 3,
944
- unq: WEIGHT_UNQ_8
950
+ unq: WEIGHT_UNQ_8,
951
+ endpointRange: 256
952
+ };
953
+ var CLASS_RGB_Q192 = {
954
+ cem: CEM_RGB_DIRECT,
955
+ blockMode: BLOCK_MODE_4x4_4BIT,
956
+ channels: [0, 1, 2],
957
+ weightBits: 4,
958
+ unq: WEIGHT_UNQ_16,
959
+ endpointRange: 192
945
960
  };
946
961
  var CLASS_RGBA = {
947
962
  cem: CEM_RGBA_DIRECT,
948
963
  blockMode: BLOCK_MODE_4x4_2BIT,
949
964
  channels: [0, 1, 2, 3],
950
965
  weightBits: 2,
951
- unq: WEIGHT_UNQ_4
966
+ unq: WEIGHT_UNQ_4,
967
+ endpointRange: 256
952
968
  };
953
969
  function clamp2(v, lo, hi) {
954
970
  return v < lo ? lo : v > hi ? hi : v;
@@ -959,6 +975,84 @@ function to82(v) {
959
975
  function interp16(e0, e1, w) {
960
976
  return (64 - w) * e0 * 257 + w * e1 * 257 + 32 >> 6;
961
977
  }
978
+ function unq192(value) {
979
+ const trit = value >> 6;
980
+ const bits = value & 63;
981
+ const A = bits & 1 ? 511 : 0;
982
+ const f = bits >> 5 & 1;
983
+ const B = (bits >> 1 & 31) << 4 | f;
984
+ const T = (trit * 5 + B ^ A) & 511;
985
+ return (A & 128 | T >> 2) & 255;
986
+ }
987
+ var Q192_VALUE_OF = (() => {
988
+ const t = new Int16Array(256).fill(-1);
989
+ for (let v = 0; v < 192; v++) t[unq192(v)] = v;
990
+ return t;
991
+ })();
992
+ function nearest192(x) {
993
+ const v = clamp2(Math.round(x), 0, 255);
994
+ if (Q192_VALUE_OF[v] >= 0) return v;
995
+ const lo = v - 1;
996
+ const hi = v + 1;
997
+ const loOk = lo >= 0 && Q192_VALUE_OF[lo] >= 0;
998
+ const hiOk = hi <= 255 && Q192_VALUE_OF[hi] >= 0;
999
+ if (loOk && hiOk) return x - lo <= hi - x ? lo : hi;
1000
+ if (loOk) return x - lo <= 2 ? lo : v + 2;
1001
+ return hiOk ? hi : v - 2;
1002
+ }
1003
+ function decodeTrits(T) {
1004
+ const bit = (x, i) => x >> i & 1;
1005
+ let C;
1006
+ let t3;
1007
+ let t4;
1008
+ if ((T >> 2 & 7) === 7) {
1009
+ C = (T >> 5 & 7) << 2 | T & 3;
1010
+ t4 = 2;
1011
+ t3 = 2;
1012
+ } else {
1013
+ C = T & 31;
1014
+ if ((T >> 5 & 3) === 3) {
1015
+ t4 = 2;
1016
+ t3 = bit(T, 7);
1017
+ } else {
1018
+ t4 = bit(T, 7);
1019
+ t3 = T >> 5 & 3;
1020
+ }
1021
+ }
1022
+ let t0;
1023
+ let t1;
1024
+ let t2;
1025
+ if ((C & 3) === 3) {
1026
+ t2 = 2;
1027
+ t1 = bit(C, 4);
1028
+ t0 = bit(C, 3) << 1 | bit(C, 2) & ~bit(C, 3) & 1;
1029
+ } else if ((C >> 2 & 3) === 3) {
1030
+ t2 = 2;
1031
+ t1 = 2;
1032
+ t0 = C & 3;
1033
+ } else {
1034
+ t2 = bit(C, 4);
1035
+ t1 = C >> 2 & 3;
1036
+ t0 = bit(C, 1) << 1 | bit(C, 0) & ~bit(C, 1) & 1;
1037
+ }
1038
+ return [t0, t1, t2, t3, t4];
1039
+ }
1040
+ function encodeTrits(t0, t1, t2, t3, t4) {
1041
+ let C;
1042
+ if (t2 === 2 && t1 === 2) C = 12 | t0;
1043
+ else if (t2 === 2) C = t1 << 4 | t0 << 2 | 3;
1044
+ else C = t2 << 4 | t1 << 2 | t0;
1045
+ if (t3 === 2 && t4 === 2) return C >> 2 << 5 | 7 << 2 | C & 3;
1046
+ if (t4 === 2) return t3 << 7 | 3 << 5 | C;
1047
+ return t4 << 7 | t3 << 5 | C;
1048
+ }
1049
+ var TRIT_SLICES = [
1050
+ [0, 2],
1051
+ [2, 2],
1052
+ [4, 1],
1053
+ [5, 2],
1054
+ [7, 1]
1055
+ ];
962
1056
  var BitWriter1282 = class {
963
1057
  bits = 0n;
964
1058
  write(pos, nBits, value) {
@@ -1070,39 +1164,61 @@ function refitEndpoints2(vals, C, indices, unq) {
1070
1164
  }
1071
1165
  return { e0, e1 };
1072
1166
  }
1073
- function encodeASTC4x4Block(pixels) {
1074
- if (pixels.length !== 64) {
1075
- throw new Error(`encodeASTC4x4Block: expected 64 values (16 RGBA), got ${pixels.length}`);
1076
- }
1077
- const pixels8 = new Uint8Array(64);
1078
- for (let k = 0; k < 64; k++) pixels8[k] = to82(pixels[k]);
1079
- let gray = true;
1080
- let opaque = true;
1081
- for (let k = 0; k < 16; k++) {
1082
- const r = pixels8[k * 4];
1083
- if (pixels8[k * 4 + 1] !== r || pixels8[k * 4 + 2] !== r) gray = false;
1084
- if (pixels8[k * 4 + 3] !== 255) opaque = false;
1085
- }
1086
- const cls = opaque ? gray ? CLASS_LUM : CLASS_RGB : CLASS_RGBA;
1167
+ function quantEndpoint(cls, e) {
1168
+ return cls.endpointRange === 192 ? e.map(nearest192) : e.map((v) => clamp2(Math.round(v), 0, 255));
1169
+ }
1170
+ function fitClass(cls, pixels8) {
1087
1171
  const C = cls.channels.length;
1088
1172
  const vals = new Float64Array(16 * C);
1089
1173
  for (let k = 0; k < 16; k++) {
1090
1174
  for (let c = 0; c < C; c++) vals[k * C + c] = pixels8[k * 4 + cls.channels[c]];
1091
1175
  }
1092
1176
  const fp = farthestPair2(vals, C);
1093
- let e0 = Array.from({ length: C }, (_, c) => vals[fp.i0 * C + c]);
1094
- let e1 = Array.from({ length: C }, (_, c) => vals[fp.i1 * C + c]);
1177
+ let e0 = quantEndpoint(
1178
+ cls,
1179
+ Array.from({ length: C }, (_, c) => vals[fp.i0 * C + c])
1180
+ );
1181
+ let e1 = quantEndpoint(
1182
+ cls,
1183
+ Array.from({ length: C }, (_, c) => vals[fp.i1 * C + c])
1184
+ );
1095
1185
  let { indices, err } = totalSqError(vals, C, e0, e1, cls.unq);
1096
1186
  const refit = refitEndpoints2(vals, C, indices, cls.unq);
1097
1187
  if (refit) {
1098
- const second = totalSqError(vals, C, refit.e0, refit.e1, cls.unq);
1188
+ const r0 = quantEndpoint(cls, refit.e0);
1189
+ const r1 = quantEndpoint(cls, refit.e1);
1190
+ const second = totalSqError(vals, C, r0, r1, cls.unq);
1099
1191
  if (second.err < err) {
1100
- e0 = refit.e0;
1101
- e1 = refit.e1;
1192
+ e0 = r0;
1193
+ e1 = r1;
1102
1194
  indices = second.indices;
1103
1195
  err = second.err;
1104
1196
  }
1105
1197
  }
1198
+ return { e0, e1, indices, err };
1199
+ }
1200
+ function encodeASTC4x4Block(pixels) {
1201
+ if (pixels.length !== 64) {
1202
+ throw new Error(`encodeASTC4x4Block: expected 64 values (16 RGBA), got ${pixels.length}`);
1203
+ }
1204
+ const pixels8 = new Uint8Array(64);
1205
+ for (let k = 0; k < 64; k++) pixels8[k] = to82(pixels[k]);
1206
+ let gray = true;
1207
+ let opaque = true;
1208
+ for (let k = 0; k < 16; k++) {
1209
+ const r = pixels8[k * 4];
1210
+ if (pixels8[k * 4 + 1] !== r || pixels8[k * 4 + 2] !== r) gray = false;
1211
+ if (pixels8[k * 4 + 3] !== 255) opaque = false;
1212
+ }
1213
+ const classes = opaque ? gray ? [CLASS_LUM] : [CLASS_RGB, CLASS_RGB_Q192] : [CLASS_RGBA];
1214
+ let best = null;
1215
+ for (const cls2 of classes) {
1216
+ const fit = fitClass(cls2, pixels8);
1217
+ if (!best || fit.err < best.err) best = { cls: cls2, ...fit };
1218
+ }
1219
+ const { cls } = best;
1220
+ let { e0, e1, indices } = best;
1221
+ const C = cls.channels.length;
1106
1222
  {
1107
1223
  const nSum = Math.min(C, 3);
1108
1224
  let s0 = 0;
@@ -1128,9 +1244,29 @@ function packBlock3(cls, e0, e1, indices) {
1128
1244
  bw.write(0, 11, cls.blockMode);
1129
1245
  bw.write(11, 2, 0);
1130
1246
  bw.write(13, 4, cls.cem);
1131
- for (let c = 0; c < e0.length; c++) {
1132
- bw.write(17 + c * 16, 8, e0[c]);
1133
- bw.write(25 + c * 16, 8, e1[c]);
1247
+ const values = [];
1248
+ for (let c = 0; c < e0.length; c++) values.push(e0[c], e1[c]);
1249
+ if (cls.endpointRange === 192) {
1250
+ let pos = 17;
1251
+ for (let g = 0; g < values.length; g += 5) {
1252
+ const grp = values.slice(g, g + 5).map((v) => {
1253
+ const q = Q192_VALUE_OF[v];
1254
+ if (q < 0) throw new Error(`packBlock: ${v} is not a QUANT_192 level`);
1255
+ return q;
1256
+ });
1257
+ const t = [0, 0, 0, 0, 0];
1258
+ grp.forEach((q, i) => t[i] = q >> 6);
1259
+ const T = encodeTrits(t[0], t[1], t[2], t[3], t[4]);
1260
+ grp.forEach((q, i) => {
1261
+ bw.write(pos, 6, q & 63);
1262
+ pos += 6;
1263
+ const [start, count] = TRIT_SLICES[i];
1264
+ bw.write(pos, count, T >> start & (1 << count) - 1);
1265
+ pos += count;
1266
+ });
1267
+ }
1268
+ } else {
1269
+ values.forEach((v, i) => bw.write(17 + i * 8, 8, v));
1134
1270
  }
1135
1271
  for (let k = 0; k < 16; k++) {
1136
1272
  const w = indices[k];
@@ -1140,19 +1276,20 @@ function packBlock3(cls, e0, e1, indices) {
1140
1276
  }
1141
1277
  return bw.toBytes();
1142
1278
  }
1143
- var CLASS_BY_MODE = {
1144
- [BLOCK_MODE_4x4_2BIT]: CLASS_RGBA,
1145
- [BLOCK_MODE_4x4_3BIT]: CLASS_RGB,
1146
- [BLOCK_MODE_4x4_5BIT]: CLASS_LUM
1279
+ var CLASS_BY_MODE_CEM = {
1280
+ [`${BLOCK_MODE_4x4_2BIT}:${CEM_RGBA_DIRECT}`]: CLASS_RGBA,
1281
+ [`${BLOCK_MODE_4x4_3BIT}:${CEM_RGB_DIRECT}`]: CLASS_RGB,
1282
+ [`${BLOCK_MODE_4x4_4BIT}:${CEM_RGB_DIRECT}`]: CLASS_RGB_Q192,
1283
+ [`${BLOCK_MODE_4x4_5BIT}:${CEM_LUM_DIRECT}`]: CLASS_LUM
1147
1284
  };
1285
+ var SUPPORTED_MODES = /* @__PURE__ */ new Set([BLOCK_MODE_4x4_2BIT, BLOCK_MODE_4x4_3BIT, BLOCK_MODE_4x4_4BIT, BLOCK_MODE_4x4_5BIT]);
1148
1286
  function decodeASTC4x4Block(block) {
1149
1287
  if (block.length !== 16) {
1150
1288
  throw new Error(`decodeASTC4x4Block: expected 16 bytes, got ${block.length}`);
1151
1289
  }
1152
1290
  const br = new BitReader1282(block);
1153
1291
  const mode = br.read(0, 11);
1154
- const cls = CLASS_BY_MODE[mode];
1155
- if (!cls) {
1292
+ if (!SUPPORTED_MODES.has(mode)) {
1156
1293
  throw new Error(`decodeASTC4x4Block: unsupported block mode 0x${mode.toString(16)}`);
1157
1294
  }
1158
1295
  const partCount = br.read(11, 2);
@@ -1160,12 +1297,31 @@ function decodeASTC4x4Block(block) {
1160
1297
  throw new Error(`decodeASTC4x4Block: multi-partition blocks not supported (count=${partCount + 1})`);
1161
1298
  }
1162
1299
  const cem = br.read(13, 4);
1163
- if (cem !== cls.cem) {
1164
- throw new Error(`decodeASTC4x4Block: expected CEM ${cls.cem} with block mode 0x${mode.toString(16)}, got ${cem}`);
1300
+ const cls = CLASS_BY_MODE_CEM[`${mode}:${cem}`];
1301
+ if (!cls) {
1302
+ throw new Error(`decodeASTC4x4Block: unsupported CEM ${cem} with block mode 0x${mode.toString(16)}`);
1165
1303
  }
1166
1304
  const nVals = cls.channels.length * 2;
1167
1305
  const v = [];
1168
- for (let i = 0; i < nVals; i++) v.push(br.read(17 + i * 8, 8));
1306
+ if (cls.endpointRange === 192) {
1307
+ let pos = 17;
1308
+ for (let g = 0; g < nVals; g += 5) {
1309
+ const n = Math.min(5, nVals - g);
1310
+ const bits = [];
1311
+ let T = 0;
1312
+ for (let i = 0; i < n; i++) {
1313
+ bits.push(br.read(pos, 6));
1314
+ pos += 6;
1315
+ const [start, count] = TRIT_SLICES[i];
1316
+ T |= br.read(pos, count) << start;
1317
+ pos += count;
1318
+ }
1319
+ const t = decodeTrits(T);
1320
+ for (let i = 0; i < n; i++) v.push(unq192(t[i] << 6 | bits[i]));
1321
+ }
1322
+ } else {
1323
+ for (let i = 0; i < nVals; i++) v.push(br.read(17 + i * 8, 8));
1324
+ }
1169
1325
  let e0;
1170
1326
  let e1;
1171
1327
  if (cls.cem === CEM_LUM_DIRECT) {