@raven-js/cortex
Version:
Zero-dependency machine learning, AI, and data processing library for modern JavaScript
189 lines (183 loc) • 6.85 kB
JavaScript
/**
* @author Anonyfox <max@anonyfox.com>
* @license MIT
* @see {@link https://github.com/Anonyfox/ravenjs}
* @see {@link https://ravenjs.dev}
* @see {@link https://anonyfox.com}
*/
/**
* @file Progressive JPEG scan refinement (placeholder).
*
* Documentation:
* This module will implement progressive JPEG scan handling per DECODE.md:
* DC first/refine, AC first/refine with EOBRUN, and RST resets. No exports yet.
*/
import { decodeHuffmanSymbol } from "./huffman.js";
import { zigZagToNatural } from "./zigzag.js";
/**
* Decode a progressive scan with restart handling.
* - Supports DC first/refine and AC first/refine with EOBRUN.
* - Predictors persist across scans; pass in and modified in place.
*
* @param {import('./huffman.js').createBitReader extends (...args:any)=>infer R ? R : any} br
* @param {import('./parse.js').Frame} frame
* @param {{ components: { idx:number, id:number, td:number, ta:number }[], Ss:number, Se:number, Ah:number, Al:number }} scan
* @param {{ dc: any[], ac: any[] }} store
* @param {number} Ri
* @param {Int32Array} predictors
*/
export function decodeProgressiveScanWithDRI(br, frame, scan, store, Ri, predictors) {
const isDC = scan.Ss === 0 && scan.Se === 0;
const isFirst = scan.Ah === 0;
const Al = scan.Al | 0;
let mcusSinceRST = 0;
let rstIndex = 0;
let eobrun = 0;
for (let my = 0; my < frame.mcusPerColumn; my++) {
for (let mx = 0; mx < frame.mcusPerLine; mx++) {
for (let s = 0; s < scan.components.length; s++) {
const sc = scan.components[s];
const comp = frame.components[sc.idx];
for (let v = 0; v < comp.v; v++) {
for (let h = 0; h < comp.h; h++) {
const blockRow = my * comp.v + v;
const blockCol = mx * comp.h + h;
const coeffs = comp.blocks[blockRow][blockCol];
if (isDC) {
if (isFirst) {
const sdc = decodeHuffmanSymbol(br, store.dc[sc.td]);
const diff = br.receiveSigned(sdc) << Al;
predictors[sc.idx] += diff;
coeffs[0] = predictors[sc.idx];
} else {
// DC refine: append next bit at Al
const bit = br.readBit();
coeffs[0] |= bit << Al;
}
} else {
// AC scan
if (isFirst) {
let k = scan.Ss;
while (k <= scan.Se) {
if (eobrun > 0) {
eobrun--;
break;
}
const RS = decodeHuffmanSymbol(br, store.ac[sc.ta]);
const r = RS >> 4;
const sBits = RS & 0x0f;
if (sBits === 0) {
if (r < 15) {
// EOBRUN
const rbits = r;
const val = rbits ? br.receive(rbits) : 0;
eobrun = val + ((1 << rbits) - 1);
break;
} else {
// ZRL
k += 16;
continue;
}
}
// Skip r zeros
k += r;
if (k > scan.Se) break;
const zz = zigZagToNatural[k];
const vnew = br.receiveSigned(sBits) << Al;
coeffs[zz] = vnew;
k++;
}
} else {
// AC refine
let k = scan.Ss;
while (k <= scan.Se) {
// refine existing nonzeros in band and process zeros/new insertion
if (eobrun > 0) {
for (let t = k; t <= scan.Se; t++) {
const zz = zigZagToNatural[t];
const cv = coeffs[zz];
if (cv !== 0) {
const bit = br.readBit();
if (bit) coeffs[zz] += (cv > 0 ? 1 : -1) * (1 << Al);
}
}
eobrun--;
break;
}
const RS = decodeHuffmanSymbol(br, store.ac[sc.ta]);
let r = RS >> 4;
const sBits = RS & 0x0f;
if (sBits === 0) {
if (r < 15) {
// EOBRUN start
const rbits = r;
const val = rbits ? br.receive(rbits) : 0;
eobrun = val + ((1 << rbits) - 1);
continue;
} else {
// ZRL in refine: skip 16 zeros with refining of intervening nonzeros
let zeros = 16;
while (k <= scan.Se && zeros > 0) {
const zz = zigZagToNatural[k];
const cv = coeffs[zz];
if (cv !== 0) {
const bit = br.readBit();
if (bit) coeffs[zz] += (cv > 0 ? 1 : -1) * (1 << Al);
} else {
zeros--;
}
k++;
}
continue;
}
}
// Insert with run r
while (k <= scan.Se) {
const zz = zigZagToNatural[k];
const cv = coeffs[zz];
if (cv !== 0) {
const bit = br.readBit();
if (bit) coeffs[zz] += (cv > 0 ? 1 : -1) * (1 << Al);
} else {
if (r === 0) {
// new coefficient
const sign = br.readBit() ? -1 : 1; // sign bit per spec: 1 means negative; using convention
coeffs[zz] = sign * (1 << Al);
k++;
break;
}
r = (r - 1) | 0;
}
k++;
}
}
}
}
}
}
}
// Restart handling
if (Ri > 0) {
mcusSinceRST++;
if (mcusSinceRST === Ri) {
try {
br.alignToByte();
if (!br.hasMarker()) br.readBit();
} catch (err) {
if (err && err.code === "ERR_MARKER") {
const m = br.getMarker();
const expected = 0xffd0 + rstIndex;
if (m !== expected) rstIndex = (m - 0xffd0) & 7;
predictors.fill(0);
eobrun = 0;
mcusSinceRST = 0;
rstIndex = (rstIndex + 1) & 7;
continue;
}
throw err;
}
}
}
}
}
}