image-in-browser
Version:
Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)
1,339 lines • 65.9 kB
JavaScript
import { ArrayUtils } from '../../common/array-utils.js';
import { InputBuffer } from '../../common/input-buffer.js';
import { MemoryImage } from '../../image/image.js';
import { VP8BitReader } from './vp8-bit-reader.js';
import { VP8FInfo } from './vp8-f-info.js';
import { VP8Filter } from './vp8-filter.js';
import { VP8FilterHeader } from './vp8-filter-header.js';
import { VP8FrameHeader } from './vp8-frame-header.js';
import { VP8MB } from './vp8-mb.js';
import { VP8MBData } from './vp8-mb-data.js';
import { VP8PictureHeader } from './vp8-picture-header.js';
import { VP8Proba } from './vp8-proba.js';
import { VP8QuantMatrix } from './vp8-quant-matrix.js';
import { VP8SegmentHeader } from './vp8-segment-header.js';
import { VP8TopSamples } from './vp8-top-samples.js';
import { WebPAlpha } from './webp-alpha.js';
import { StringUtils } from '../../common/string-utils.js';
import { ExifData } from '../../exif/exif-data.js';
export class VP8 {
get webp() {
return this._webp;
}
constructor(input, webp) {
this._frameHeader = new VP8FrameHeader();
this._picHeader = new VP8PictureHeader();
this._filterHeader = new VP8FilterHeader();
this._segmentHeader = new VP8SegmentHeader();
this._cropLeft = 0;
this._cropRight = 0;
this._cropTop = 0;
this._cropBottom = 0;
this._mbWidth = 0;
this._mbHeight = 0;
this._tlMbX = 0;
this._tlMbY = 0;
this._brMbX = 0;
this._brMbY = 0;
this._numPartitions = 0;
this._partitions = ArrayUtils.fill(VP8.maxNumPartitions, undefined);
this._dither = false;
this._dqm = ArrayUtils.fill(VP8.numMbSegments, undefined);
this._useSkipProba = false;
this._skipP = 0;
this._intraL = new Uint8Array(4);
this._segment = 0;
this._mbX = 0;
this._mbY = 0;
this._input = input;
this._webp = webp;
}
static clip(v, M) {
return v < 0 ? 0 : v > M ? M : v;
}
static checkMode(mbX, mbY, mode) {
if (mode === VP8.bDcPred) {
if (mbX === 0) {
return mbY === 0 ? VP8.bDcPredNoTopLeft : VP8.bDcPredNoLeft;
}
else {
return mbY === 0 ? VP8.bDcPredNoTop : VP8.bDcPred;
}
}
return mode;
}
getHeaders() {
if (!this.decodeHeader()) {
return false;
}
this._proba = new VP8Proba();
for (let i = 0; i < VP8.numMbSegments; ++i) {
this._dqm[i] = new VP8QuantMatrix();
}
this._picHeader.width = this._webp.width;
this._picHeader.height = this._webp.height;
this._picHeader.xscale = (this._webp.width >>> 8) >>> 6;
this._picHeader.yscale = (this._webp.height >>> 8) >>> 6;
this._cropTop = 0;
this._cropLeft = 0;
this._cropRight = this._webp.width;
this._cropBottom = this._webp.height;
this._mbWidth = (this._webp.width + 15) >>> 4;
this._mbHeight = (this._webp.height + 15) >>> 4;
this._segment = 0;
this._br = new VP8BitReader(this._input.subarray(this._frameHeader.partitionLength));
this._input.skip(this._frameHeader.partitionLength);
this._picHeader.colorspace = this._br.get();
this._picHeader.clampType = this._br.get();
if (!this.parseSegmentHeader(this._segmentHeader, this._proba)) {
return false;
}
if (!this.parseFilterHeader()) {
return false;
}
if (!this.parsePartitions(this._input)) {
return false;
}
this.parseQuant();
this._br.get();
this.parseProba();
return true;
}
parseSegmentHeader(hdr, proba) {
hdr.useSegment = this._br.get() !== 0;
if (hdr.useSegment) {
hdr.updateMap = this._br.get() !== 0;
if (this._br.get() !== 0) {
hdr.absoluteDelta = this._br.get() !== 0;
for (let s = 0; s < VP8.numMbSegments; ++s) {
hdr.quantizer[s] =
this._br.get() !== 0 ? this._br.getSignedValue(7) : 0;
}
for (let s = 0; s < VP8.numMbSegments; ++s) {
hdr.filterStrength[s] =
this._br.get() !== 0 ? this._br.getSignedValue(6) : 0;
}
}
if (hdr.updateMap) {
for (let s = 0; s < VP8.mbFeatureTreeProbs; ++s) {
proba.segments[s] =
this._br.get() !== 0 ? this._br.getValue(8) : 255;
}
}
}
else {
hdr.updateMap = false;
}
return true;
}
parseFilterHeader() {
const hdr = this._filterHeader;
this._filterHeader.simple = this._br.get() !== 0;
this._filterHeader.level = this._br.getValue(6);
this._filterHeader.sharpness = this._br.getValue(3);
this._filterHeader.useLfDelta = this._br.get() !== 0;
if (hdr.useLfDelta) {
if (this._br.get() !== 0) {
for (let i = 0; i < VP8.numRefLfDeltas; ++i) {
if (this._br.get() !== 0) {
hdr.refLfDelta[i] = this._br.getSignedValue(6);
}
}
for (let i = 0; i < VP8.numModeLfDeltas; ++i) {
if (this._br.get() !== 0) {
hdr.modeLfDelta[i] = this._br.getSignedValue(6);
}
}
}
}
this._filterType = hdr.level === 0 ? 0 : hdr.simple ? 1 : 2;
return true;
}
parsePartitions(input) {
let sz = 0;
const bufEnd = input.length;
this._numPartitions = 1 << this._br.getValue(2);
const lastPart = this._numPartitions - 1;
let partStart = lastPart * 3;
if (bufEnd < partStart) {
return false;
}
for (let p = 0; p < lastPart; ++p) {
const szb = input.peek(3, sz);
const psize = szb.get(0) | (szb.get(1) << 8) | (szb.get(2) << 16);
let partEnd = partStart + psize;
if (partEnd > bufEnd) {
partEnd = bufEnd;
}
const pin = input.subarray(partEnd - partStart, partStart);
this._partitions[p] = new VP8BitReader(pin);
partStart = partEnd;
sz += 3;
}
const pin = input.subarray(bufEnd - partStart, input.position + partStart);
this._partitions[lastPart] = new VP8BitReader(pin);
return partStart < bufEnd;
}
parseQuant() {
const baseQ0 = this._br.getValue(7);
const dqy1Dc = this._br.get() !== 0 ? this._br.getSignedValue(4) : 0;
const dqy2Dc = this._br.get() !== 0 ? this._br.getSignedValue(4) : 0;
const dqy2Ac = this._br.get() !== 0 ? this._br.getSignedValue(4) : 0;
const dquvDc = this._br.get() !== 0 ? this._br.getSignedValue(4) : 0;
const dquvAc = this._br.get() !== 0 ? this._br.getSignedValue(4) : 0;
const hdr = this._segmentHeader;
for (let i = 0; i < VP8.numMbSegments; ++i) {
let q = 0;
if (hdr.useSegment) {
q = hdr.quantizer[i];
if (!hdr.absoluteDelta) {
q += baseQ0;
}
}
else {
if (i > 0) {
this._dqm[i] = this._dqm[0];
continue;
}
else {
q = baseQ0;
}
}
const m = this._dqm[i];
m.y1Mat[0] = VP8.dcTable[VP8.clip(q + dqy1Dc, 127)];
m.y1Mat[1] = VP8.acTable[VP8.clip(q + 0, 127)];
m.y2Mat[0] = VP8.dcTable[VP8.clip(q + dqy2Dc, 127)] * 2;
m.y2Mat[1] = (VP8.acTable[VP8.clip(q + dqy2Ac, 127)] * 101581) >>> 16;
if (m.y2Mat[1] < 8) {
m.y2Mat[1] = 8;
}
m.uvMat[0] = VP8.dcTable[VP8.clip(q + dquvDc, 117)];
m.uvMat[1] = VP8.acTable[VP8.clip(q + dquvAc, 127)];
m.uvQuant = q + dquvAc;
}
}
parseProba() {
const proba = this._proba;
for (let t = 0; t < VP8.numTypes; ++t) {
for (let b = 0; b < VP8.numBands; ++b) {
for (let c = 0; c < VP8.numCtx; ++c) {
for (let p = 0; p < VP8.numProbas; ++p) {
const v = this._br.getBit(VP8.coeffsUpdateProba[t][b][c][p]) !== 0
? this._br.getValue(8)
: VP8.coeffsProba0[t][b][c][p];
proba.bands[t][b].probas[c][p] = v;
}
}
}
}
this._useSkipProba = this._br.get() !== 0;
if (this._useSkipProba) {
this._skipP = this._br.getValue(8);
}
}
precomputeFilterStrengths() {
if (this._filterType > 0) {
const hdr = this._filterHeader;
for (let s = 0; s < VP8.numMbSegments; ++s) {
let baseLevel = 0;
if (this._segmentHeader.useSegment) {
baseLevel = this._segmentHeader.filterStrength[s];
if (!this._segmentHeader.absoluteDelta) {
baseLevel += hdr.level;
}
}
else {
baseLevel = hdr.level;
}
for (let i4x4 = 0; i4x4 <= 1; ++i4x4) {
const info = this._fStrengths[s][i4x4];
let level = baseLevel;
if (hdr.useLfDelta) {
level += hdr.refLfDelta[0];
if (i4x4 !== 0) {
level += hdr.modeLfDelta[0];
}
}
level = level < 0 ? 0 : level > 63 ? 63 : level;
if (level > 0) {
let iLevel = level;
if (hdr.sharpness > 0) {
if (hdr.sharpness > 4) {
iLevel >>>= 2;
}
else {
iLevel >>>= 1;
}
if (iLevel > 9 - hdr.sharpness) {
iLevel = 9 - hdr.sharpness;
}
}
if (iLevel < 1) {
iLevel = 1;
}
info.fInnerLevel = iLevel;
info.fLimit = 2 * level + iLevel;
info.hevThresh = level >= 40 ? 2 : level >= 15 ? 1 : 0;
}
else {
info.fLimit = 0;
}
info.fInner = i4x4 !== 0;
}
}
}
}
initFrame() {
if (this._webp.alphaData !== undefined) {
this._alphaData = this._webp.alphaData;
}
this._fStrengths = ArrayUtils.generate(VP8.numMbSegments, () => [new VP8FInfo(), new VP8FInfo()]);
this._yuvT = ArrayUtils.generate(this._mbWidth, () => new VP8TopSamples());
this._yuvBlock = new Uint8Array(VP8.yuvSize);
this._intraT = new Uint8Array(4 * this._mbWidth);
this._cacheYStride = 16 * this._mbWidth;
this._cacheUVStride = 8 * this._mbWidth;
const extraRows = VP8.filterExtraRows[this._filterType];
const extraY = extraRows * this._cacheYStride;
const extraUv = Math.trunc(extraRows / 2) * this._cacheUVStride;
this._cacheY = new InputBuffer({
buffer: new Uint8Array(16 * this._cacheYStride + extraY),
offset: extraY,
});
this._cacheU = new InputBuffer({
buffer: new Uint8Array(8 * this._cacheUVStride + extraUv),
offset: extraUv,
});
this._cacheV = new InputBuffer({
buffer: new Uint8Array(8 * this._cacheUVStride + extraUv),
offset: extraUv,
});
this._tmpY = new InputBuffer({
buffer: new Uint8Array(this._webp.width),
});
const uvWidth = (this._webp.width + 1) >>> 1;
this._tmpU = new InputBuffer({
buffer: new Uint8Array(uvWidth),
});
this._tmpV = new InputBuffer({
buffer: new Uint8Array(uvWidth),
});
{
const extraPixels = VP8.filterExtraRows[this._filterType];
if (this._filterType === 2) {
this._tlMbX = 0;
this._tlMbY = 0;
}
else {
this._tlMbX = Math.trunc((this._cropLeft - extraPixels) / 16);
this._tlMbY = Math.trunc((this._cropTop - extraPixels) / 16);
if (this._tlMbX < 0) {
this._tlMbX = 0;
}
if (this._tlMbY < 0) {
this._tlMbY = 0;
}
}
this._brMbY = Math.trunc((this._cropBottom + 15 + extraPixels) / 16);
this._brMbX = Math.trunc((this._cropRight + 15 + extraPixels) / 16);
if (this._brMbX > this._mbWidth) {
this._brMbX = this._mbWidth;
}
if (this._brMbY > this._mbHeight) {
this._brMbY = this._mbHeight;
}
}
this._mbInfo = ArrayUtils.generate(this._mbWidth + 1, () => new VP8MB());
this._mbData = ArrayUtils.generate(this._mbWidth, () => new VP8MBData());
this._fInfo = ArrayUtils.fill(this._mbWidth, undefined);
this.precomputeFilterStrengths();
this._dsp = new VP8Filter();
return true;
}
parseFrame() {
for (this._mbY = 0; this._mbY < this._brMbY; ++this._mbY) {
const tokenBr = this._partitions[this._mbY & (this._numPartitions - 1)];
for (; this._mbX < this._mbWidth; ++this._mbX) {
if (!this.decodeMB(tokenBr)) {
return false;
}
}
this._mbInfo[0].nz = 0;
this._mbInfo[0].nzDc = 0;
this._intraL.fill(VP8.bDcPred);
this._mbX = 0;
if (!this.processRow()) {
return false;
}
}
return true;
}
processRow() {
this.reconstructRow();
const useFilter = this._filterType > 0 &&
this._mbY >= this._tlMbY &&
this._mbY <= this._brMbY;
return this.finishRow(useFilter);
}
reconstructRow() {
const mbY = this._mbY;
const yDst = new InputBuffer({
buffer: this._yuvBlock,
offset: VP8.yOffset,
});
const uDst = new InputBuffer({
buffer: this._yuvBlock,
offset: VP8.uOffset,
});
const vDst = new InputBuffer({
buffer: this._yuvBlock,
offset: VP8.vOffset,
});
for (let mbX = 0; mbX < this._mbWidth; ++mbX) {
const block = this._mbData[mbX];
if (mbX > 0) {
for (let j = -1; j < 16; ++j) {
yDst.memcpy(j * VP8.bps - 4, 4, yDst, j * VP8.bps + 12);
}
for (let j = -1; j < 8; ++j) {
uDst.memcpy(j * VP8.bps - 4, 4, uDst, j * VP8.bps + 4);
vDst.memcpy(j * VP8.bps - 4, 4, vDst, j * VP8.bps + 4);
}
}
else {
for (let j = 0; j < 16; ++j) {
yDst.set(j * VP8.bps - 1, 129);
}
for (let j = 0; j < 8; ++j) {
uDst.set(j * VP8.bps - 1, 129);
vDst.set(j * VP8.bps - 1, 129);
}
if (mbY > 0) {
yDst.set(-1 - VP8.bps, uDst.set(-1 - VP8.bps, vDst.set(-1 - VP8.bps, 129)));
}
}
const topYuv = this._yuvT[mbX];
const coeffs = block.coeffs;
let bits = block.nonZeroY;
if (mbY > 0) {
yDst.memcpy(-VP8.bps, 16, topYuv.y);
uDst.memcpy(-VP8.bps, 8, topYuv.u);
vDst.memcpy(-VP8.bps, 8, topYuv.v);
}
else if (mbX === 0) {
yDst.memset(-VP8.bps - 1, 16 + 4 + 1, 127);
uDst.memset(-VP8.bps - 1, 8 + 1, 127);
vDst.memset(-VP8.bps - 1, 8 + 1, 127);
}
if (block.isIntra4x4) {
const topRight = InputBuffer.from(yDst, -VP8.bps + 16);
const topRight32 = topRight.toUint32Array();
if (mbY > 0) {
if (mbX >= this._mbWidth - 1) {
topRight.memset(0, 4, topYuv.y[15]);
}
else {
topRight.memcpy(0, 4, this._yuvT[mbX + 1].y);
}
}
const p = topRight32[0];
topRight32[3 * VP8.bps] = p;
topRight32[2 * VP8.bps] = p;
topRight32[VP8.bps] = p;
for (let n = 0; n < 16; ++n, bits = (bits << 2) & 0xffffffff) {
const dst = InputBuffer.from(yDst, VP8.kScan[n]);
VP8Filter.predLuma4[block.imodes[n]](dst);
this.doTransform(bits, new InputBuffer({
buffer: coeffs,
offset: n * 16,
}), dst);
}
}
else {
const predFunc = VP8.checkMode(mbX, mbY, block.imodes[0]);
VP8Filter.predLuma16[predFunc](yDst);
if (bits !== 0) {
for (let n = 0; n < 16; ++n, bits = (bits << 2) & 0xffffffff) {
const dst = InputBuffer.from(yDst, VP8.kScan[n]);
this.doTransform(bits, new InputBuffer({
buffer: coeffs,
offset: n * 16,
}), dst);
}
}
}
const bitsUv = block.nonZeroUV;
const predFunc = VP8.checkMode(mbX, mbY, block.uvmode);
VP8Filter.predChroma8[predFunc](uDst);
VP8Filter.predChroma8[predFunc](vDst);
const c1 = new InputBuffer({
buffer: coeffs,
offset: 16 * 16,
});
this.doUVTransform(bitsUv, c1, uDst);
const c2 = new InputBuffer({
buffer: coeffs,
offset: 20 * 16,
});
this.doUVTransform(bitsUv >>> 8, c2, vDst);
if (mbY < this._mbHeight - 1) {
ArrayUtils.copyRange(yDst.toUint8Array(), 15 * VP8.bps, topYuv.y, 0, 16);
ArrayUtils.copyRange(uDst.toUint8Array(), 7 * VP8.bps, topYuv.u, 0, 8);
ArrayUtils.copyRange(vDst.toUint8Array(), 7 * VP8.bps, topYuv.v, 0, 8);
}
const yOut = mbX * 16;
const uOut = mbX * 8;
const vOut = mbX * 8;
for (let j = 0; j < 16; ++j) {
const start = yOut + j * this._cacheYStride;
this._cacheY.memcpy(start, 16, yDst, j * VP8.bps);
}
for (let j = 0; j < 8; ++j) {
let start = uOut + j * this._cacheUVStride;
this._cacheU.memcpy(start, 8, uDst, j * VP8.bps);
start = vOut + j * this._cacheUVStride;
this._cacheV.memcpy(start, 8, vDst, j * VP8.bps);
}
}
}
doTransform(bits, src, dst) {
switch (bits >>> 30) {
case 3:
this._dsp.transform(src, dst, false);
break;
case 2:
this._dsp.transformAC3(src, dst);
break;
case 1:
this._dsp.transformDC(src, dst);
break;
default:
break;
}
}
doUVTransform(bits, src, dst) {
if ((bits & 0xff) !== 0) {
if ((bits & 0xaa) !== 0) {
this._dsp.transformUV(src, dst);
}
else {
this._dsp.transformDCUV(src, dst);
}
}
}
doFilter(mbX, mbY) {
const yBps = this._cacheYStride;
const fInfo = this._fInfo[mbX];
const yDst = InputBuffer.from(this._cacheY, mbX * 16);
const iLevel = fInfo.fInnerLevel;
const limit = fInfo.fLimit;
if (limit === 0) {
return;
}
if (this._filterType === 1) {
if (mbX > 0) {
this._dsp.simpleHFilter16(yDst, yBps, limit + 4);
}
if (fInfo.fInner) {
this._dsp.simpleHFilter16i(yDst, yBps, limit);
}
if (mbY > 0) {
this._dsp.simpleVFilter16(yDst, yBps, limit + 4);
}
if (fInfo.fInner) {
this._dsp.simpleVFilter16i(yDst, yBps, limit);
}
}
else {
const uvBps = this._cacheUVStride;
const uDst = InputBuffer.from(this._cacheU, mbX * 8);
const vDst = InputBuffer.from(this._cacheV, mbX * 8);
const hevThresh = fInfo.hevThresh;
if (mbX > 0) {
this._dsp.hFilter16(yDst, yBps, limit + 4, iLevel, hevThresh);
this._dsp.hFilter8(uDst, vDst, uvBps, limit + 4, iLevel, hevThresh);
}
if (fInfo.fInner) {
this._dsp.hFilter16i(yDst, yBps, limit, iLevel, hevThresh);
this._dsp.hFilter8i(uDst, vDst, uvBps, limit, iLevel, hevThresh);
}
if (mbY > 0) {
this._dsp.vFilter16(yDst, yBps, limit + 4, iLevel, hevThresh);
this._dsp.vFilter8(uDst, vDst, uvBps, limit + 4, iLevel, hevThresh);
}
if (fInfo.fInner) {
this._dsp.vFilter16i(yDst, yBps, limit, iLevel, hevThresh);
this._dsp.vFilter8i(uDst, vDst, uvBps, limit, iLevel, hevThresh);
}
}
}
filterRow() {
for (let mbX = this._tlMbX; mbX < this._brMbX; ++mbX) {
this.doFilter(mbX, this._mbY);
}
}
ditherRow() { }
finishRow(useFilter) {
const extraYRows = VP8.kFilterExtraRows[this._filterType];
const ySize = extraYRows * this._cacheYStride;
const uvSize = Math.trunc(extraYRows / 2) * this._cacheUVStride;
const yDst = InputBuffer.from(this._cacheY, -ySize);
const uDst = InputBuffer.from(this._cacheU, -uvSize);
const vDst = InputBuffer.from(this._cacheV, -uvSize);
const mbY = this._mbY;
const isFirstRow = mbY === 0;
const isLastRow = mbY >= this._brMbY - 1;
let yStart = this.macroBlockVPos(mbY);
let yEnd = this.macroBlockVPos(mbY + 1);
if (useFilter) {
this.filterRow();
}
if (this._dither) {
this.ditherRow();
}
if (!isFirstRow) {
yStart -= extraYRows;
this._y = InputBuffer.from(yDst);
this._u = InputBuffer.from(uDst);
this._v = InputBuffer.from(vDst);
}
else {
this._y = InputBuffer.from(this._cacheY);
this._u = InputBuffer.from(this._cacheU);
this._v = InputBuffer.from(this._cacheV);
}
if (!isLastRow) {
yEnd -= extraYRows;
}
if (yEnd > this._cropBottom) {
yEnd = this._cropBottom;
}
this._a = undefined;
if (this._alphaData !== undefined && yStart < yEnd) {
this._a = this.decompressAlphaRows(yStart, yEnd - yStart);
if (this._a === undefined) {
return false;
}
}
if (yStart < this._cropTop) {
const deltaY = this._cropTop - yStart;
yStart = this._cropTop;
this._y.offset += this._cacheYStride * deltaY;
this._u.offset += this._cacheUVStride * (deltaY >>> 1);
this._v.offset += this._cacheUVStride * (deltaY >>> 1);
if (this._a !== undefined) {
this._a.offset += this._webp.width * deltaY;
}
}
if (yStart < yEnd) {
this._y.offset += this._cropLeft;
this._u.offset += this._cropLeft >>> 1;
this._v.offset += this._cropLeft >>> 1;
if (this._a !== undefined) {
this._a.offset += this._cropLeft;
}
this.put(yStart - this._cropTop, this._cropRight - this._cropLeft, yEnd - yStart);
}
if (!isLastRow) {
this._cacheY.memcpy(-ySize, ySize, yDst, 16 * this._cacheYStride);
this._cacheU.memcpy(-uvSize, uvSize, uDst, 8 * this._cacheUVStride);
this._cacheV.memcpy(-uvSize, uvSize, vDst, 8 * this._cacheUVStride);
}
return true;
}
put(mbY, mbW, mbH) {
if (mbW <= 0 || mbH <= 0) {
return false;
}
this.emitFancyRGB(mbY, mbW, mbH);
this.emitAlphaRGB(mbY, mbW, mbH);
return true;
}
clip8(v) {
const d = (v & VP8.xorYuvMask2) === 0 ? v >>> VP8.yuvFix2 : v < 0 ? 0 : 255;
return d;
}
yuvToR(y, v) {
return this.clip8(VP8.kYScale * y + VP8.kVToR * v + VP8.kRCst);
}
yuvToG(y, u, v) {
return this.clip8(VP8.kYScale * y - VP8.kUToG * u - VP8.kVToG * v + VP8.kGCst);
}
yuvToB(y, u) {
return this.clip8(VP8.kYScale * y + VP8.kUToB * u + VP8.kBCst);
}
yuvToRgb(y, u, v, rgb) {
rgb.set(0, this.yuvToR(y, v));
rgb.set(1, this.yuvToG(y, u, v));
rgb.set(2, this.yuvToB(y, u));
}
yuvToRgba(y, u, v, rgba) {
this.yuvToRgb(y, u, v, rgba);
rgba.set(3, 0xff);
}
upSample(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len) {
const loadUv = (u, v) => {
return u | (v << 16);
};
const lastPixelPair = (len - 1) >>> 1;
let tlUv = loadUv(topU.get(0), topV.get(0));
let lUv = loadUv(curU.get(0), curV.get(0));
const uv0 = (3 * tlUv + lUv + 0x00020002) >>> 2;
this.yuvToRgba(topY.get(0), uv0 & 0xff, uv0 >>> 16, topDst);
if (bottomY !== undefined) {
const uv0 = (3 * lUv + tlUv + 0x00020002) >>> 2;
this.yuvToRgba(bottomY.get(0), uv0 & 0xff, uv0 >>> 16, bottomDst);
}
for (let x = 1; x <= lastPixelPair; ++x) {
const tUv = loadUv(topU.get(x), topV.get(x));
const uv = loadUv(curU.get(x), curV.get(x));
const avg = tlUv + tUv + lUv + uv + 0x00080008;
const diag12 = (avg + 2 * (tUv + lUv)) >>> 3;
const diag03 = (avg + 2 * (tlUv + uv)) >>> 3;
let uv0 = (diag12 + tlUv) >>> 1;
let uv1 = (diag03 + tUv) >>> 1;
this.yuvToRgba(topY.get(2 * x - 1), uv0 & 0xff, uv0 >>> 16, InputBuffer.from(topDst, (2 * x - 1) * 4));
this.yuvToRgba(topY.get(2 * x), uv1 & 0xff, uv1 >>> 16, InputBuffer.from(topDst, 2 * x * 4));
if (bottomY !== undefined) {
uv0 = (diag03 + lUv) >>> 1;
uv1 = (diag12 + uv) >>> 1;
this.yuvToRgba(bottomY.get(2 * x - 1), uv0 & 0xff, uv0 >>> 16, InputBuffer.from(bottomDst, (2 * x - 1) * 4));
this.yuvToRgba(bottomY.get(2 * x), uv1 & 0xff, uv1 >>> 16, InputBuffer.from(bottomDst, (2 * x + 0) * 4));
}
tlUv = tUv;
lUv = uv;
}
if ((len & 1) === 0) {
const uv0 = (3 * tlUv + lUv + 0x00020002) >>> 2;
this.yuvToRgba(topY.get(len - 1), uv0 & 0xff, uv0 >>> 16, InputBuffer.from(topDst, (len - 1) * 4));
if (bottomY !== undefined) {
const uv0 = (3 * lUv + tlUv + 0x00020002) >>> 2;
this.yuvToRgba(bottomY.get(len - 1), uv0 & 0xff, uv0 >>> 16, InputBuffer.from(bottomDst, (len - 1) * 4));
}
}
}
emitAlphaRGB(mbY, mbW, mbH) {
if (this._a === undefined) {
return;
}
const alpha = InputBuffer.from(this._a);
let startY = mbY;
let numRows = mbH;
if (startY === 0) {
--numRows;
}
else {
--startY;
alpha.offset -= this.webp.width;
}
if (this._cropTop + mbY + mbH === this._cropBottom) {
numRows = this._cropBottom - this._cropTop - startY;
}
for (let y = 0; y < numRows; ++y) {
for (let x = 0; x < mbW; ++x) {
const alphaValue = alpha.get(x);
this._output.getPixel(x, y + startY).a = alphaValue;
}
alpha.offset += this.webp.width;
}
}
emitFancyRGB(mbY, mbW, mbH) {
let numLinesOut = mbH;
const outputBytes = new Uint8Array(this._output.buffer);
const dst = new InputBuffer({
buffer: outputBytes,
offset: mbY * this.webp.width * 4,
});
const curY = InputBuffer.from(this._y);
const curU = InputBuffer.from(this._u);
const curV = InputBuffer.from(this._v);
let y = mbY;
const yEnd = mbY + mbH;
const uvW = (mbW + 1) >>> 1;
const stride = this.webp.width * 4;
const topU = InputBuffer.from(this._tmpU);
const topV = InputBuffer.from(this._tmpV);
if (y === 0) {
this.upSample(curY, undefined, curU, curV, curU, curV, dst, undefined, mbW);
}
else {
this.upSample(this._tmpY, curY, topU, topV, curU, curV, InputBuffer.from(dst, -stride), dst, mbW);
++numLinesOut;
}
topU.buffer = curU.buffer;
topV.buffer = curV.buffer;
for (; y + 2 < yEnd; y += 2) {
topU.offset = curU.offset;
topV.offset = curV.offset;
curU.offset += this._cacheUVStride;
curV.offset += this._cacheUVStride;
dst.offset += 2 * stride;
curY.offset += 2 * this._cacheYStride;
this.upSample(InputBuffer.from(curY, -this._cacheYStride), curY, topU, topV, curU, curV, InputBuffer.from(dst, -stride), dst, mbW);
}
curY.offset += this._cacheYStride;
if (this._cropTop + yEnd < this._cropBottom) {
this._tmpY.memcpy(0, mbW, curY);
this._tmpU.memcpy(0, uvW, curU);
this._tmpV.memcpy(0, uvW, curV);
numLinesOut--;
}
else {
if ((yEnd & 1) === 0) {
this.upSample(curY, undefined, curU, curV, curU, curV, InputBuffer.from(dst, stride), undefined, mbW);
}
}
return numLinesOut;
}
decompressAlphaRows(row, numRows) {
const width = this.webp.width;
const height = this.webp.height;
if (row < 0 || numRows <= 0 || row + numRows > height) {
return undefined;
}
if (row === 0) {
this._alphaPlane = new Uint8Array(width * height);
this._alpha = new WebPAlpha(this._alphaData, width, height);
}
if (!this._alpha.isAlphaDecoded) {
if (!this._alpha.decode(row, numRows, this._alphaPlane)) {
return undefined;
}
}
return new InputBuffer({
buffer: this._alphaPlane,
offset: row * width,
});
}
decodeMB(tokenBr) {
const left = this._mbInfo[0];
const mb = this._mbInfo[1 + this._mbX];
const block = this._mbData[this._mbX];
let skip = false;
if (this._segmentHeader.updateMap) {
this._segment =
this._br.getBit(this._proba.segments[0]) === 0
? this._br.getBit(this._proba.segments[1])
: 2 + this._br.getBit(this._proba.segments[2]);
}
skip = this._useSkipProba && this._br.getBit(this._skipP) !== 0;
this.parseIntraMode();
if (!skip) {
skip = this.parseResiduals(mb, tokenBr);
}
else {
mb.nz = 0;
left.nz = mb.nz;
if (!block.isIntra4x4) {
mb.nzDc = 0;
left.nzDc = mb.nzDc;
}
block.nonZeroY = 0;
block.nonZeroUV = 0;
}
if (this._filterType > 0) {
this._fInfo[this._mbX] =
this._fStrengths[this._segment][block.isIntra4x4 ? 1 : 0];
const finfo = this._fInfo[this._mbX];
finfo.fInner = finfo.fInner || !skip;
}
return true;
}
parseResiduals(mb, tokenBr) {
const bands = this._proba.bands;
const q = this._dqm[this._segment];
const block = this._mbData[this._mbX];
const dst = new InputBuffer({
buffer: block.coeffs,
});
const leftMb = this._mbInfo[0];
let tnz = 0;
let lnz = 0;
let nonZeroY = 0;
let nonZeroUV = 0;
let outTopNz = 0;
let outLeftNz = 0;
let first = 0;
dst.memset(0, dst.length, 0);
let acProba = [];
if (!block.isIntra4x4) {
const dc = new InputBuffer({
buffer: new Int16Array(16),
});
const ctx = mb.nzDc + leftMb.nzDc;
const nz = this.getCoeffs(tokenBr, bands[1], ctx, q.y2Mat, 0, dc);
leftMb.nzDc = nz > 0 ? 1 : 0;
mb.nzDc = leftMb.nzDc;
if (nz > 1) {
this.transformWHT(dc, dst);
}
else {
const dc0 = (dc.get(0) + 3) >>> 3;
for (let i = 0; i < 16 * 16; i += 16) {
dst.set(i, dc0);
}
}
first = 1;
acProba = bands[0];
}
else {
first = 0;
acProba = bands[3];
}
tnz = mb.nz & 0x0f;
lnz = leftMb.nz & 0x0f;
for (let y = 0; y < 4; ++y) {
let l = lnz & 1;
let nzCoeffs = 0;
for (let x = 0; x < 4; ++x) {
const ctx = l + (tnz & 1);
const nz = this.getCoeffs(tokenBr, acProba, ctx, q.y1Mat, first, dst);
l = nz > first ? 1 : 0;
tnz = (tnz >>> 1) | (l << 7);
nzCoeffs = this.nzCodeBits(nzCoeffs, nz, dst.get(0) !== 0 ? 1 : 0);
dst.offset += 16;
}
tnz >>>= 4;
lnz = (lnz >>> 1) | (l << 7);
nonZeroY = ((nonZeroY << 8) | nzCoeffs) >>> 0;
}
outTopNz = tnz;
outLeftNz = lnz >>> 4;
for (let ch = 0; ch < 4; ch += 2) {
let nzCoeffs = 0;
tnz = mb.nz >>> (4 + ch);
lnz = leftMb.nz >>> (4 + ch);
for (let y = 0; y < 2; ++y) {
let l = lnz & 1;
for (let x = 0; x < 2; ++x) {
const ctx = l + (tnz & 1);
const nz = this.getCoeffs(tokenBr, bands[2], ctx, q.uvMat, 0, dst);
l = nz > 0 ? 1 : 0;
tnz = (tnz >>> 1) | (l << 3);
nzCoeffs = this.nzCodeBits(nzCoeffs, nz, dst.get(0) !== 0 ? 1 : 0);
dst.offset += 16;
}
tnz >>>= 2;
lnz = (lnz >>> 1) | (l << 5);
}
nonZeroUV |= nzCoeffs << (4 * ch);
outTopNz |= (tnz << 4) << ch;
outLeftNz |= (lnz & 0xf0) << ch;
}
mb.nz = outTopNz;
leftMb.nz = outLeftNz;
block.nonZeroY = nonZeroY;
block.nonZeroUV = nonZeroUV;
block.dither = (nonZeroUV & 0xaaaa) !== 0 ? 0 : q.dither;
return (nonZeroY | nonZeroUV) === 0;
}
transformWHT(src, out) {
const tmp = new Int32Array(16);
let oi = 0;
for (let i = 0; i < 4; ++i) {
const a0 = src.get(0 + i) + src.get(12 + i);
const a1 = src.get(4 + i) + src.get(8 + i);
const a2 = src.get(4 + i) - src.get(8 + i);
const a3 = src.get(0 + i) - src.get(12 + i);
tmp[0 + i] = a0 + a1;
tmp[8 + i] = a0 - a1;
tmp[4 + i] = a3 + a2;
tmp[12 + i] = a3 - a2;
}
for (let i = 0; i < 4; ++i) {
const dc = tmp[0 + i * 4] + 3;
const a0 = dc + tmp[3 + i * 4];
const a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
const a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
const a3 = dc - tmp[3 + i * 4];
out.set(oi + 0, (a0 + a1) >>> 3);
out.set(oi + 16, (a3 + a2) >>> 3);
out.set(oi + 32, (a0 - a1) >>> 3);
out.set(oi + 48, (a3 - a2) >>> 3);
oi += 64;
}
}
nzCodeBits(nzCoeffs, nz, dcNz) {
let _nzCoeffs = nzCoeffs;
_nzCoeffs <<= 2;
_nzCoeffs |= nz > 3 ? 3 : nz > 1 ? 2 : dcNz;
return _nzCoeffs;
}
getLargeValue(br, p) {
let v = 0;
if (br.getBit(p[3]) === 0) {
if (br.getBit(p[4]) === 0) {
v = 2;
}
else {
v = 3 + br.getBit(p[5]);
}
}
else {
if (br.getBit(p[6]) === 0) {
if (br.getBit(p[7]) === 0) {
v = 5 + br.getBit(159);
}
else {
v = 7 + 2 * br.getBit(165);
v += br.getBit(145);
}
}
else {
const bit1 = br.getBit(p[8]);
const bit0 = br.getBit(p[9 + bit1]);
const cat = 2 * bit1 + bit0;
v = 0;
const tab = VP8.kCat3456[cat];
const len = tab.length;
for (let i = 0; i < len; ++i) {
v += v + br.getBit(tab[i]);
}
v += 3 + (8 << cat);
}
}
return v;
}
getCoeffs(br, prob, ctx, dq, n, out) {
let _n = n;
let p = prob[_n].probas[ctx];
for (; _n < 16; ++_n) {
if (br.getBit(p[0]) === 0) {
return _n;
}
while (br.getBit(p[1]) === 0) {
p = prob[VP8.kBands[++_n]].probas[0];
if (_n === 16) {
return 16;
}
}
{
const pCtx = prob[VP8.kBands[_n + 1]].probas;
let v = 0;
if (br.getBit(p[2]) === 0) {
v = 1;
p = pCtx[1];
}
else {
v = this.getLargeValue(br, p);
p = pCtx[2];
}
out.set(VP8.kZigzag[_n], br.getSigned(v) * dq[_n > 0 ? 1 : 0]);
}
}
return 16;
}
parseIntraMode() {
const ti = 4 * this._mbX;
const li = 0;
const top = this._intraT;
const left = this._intraL;
const block = this._mbData[this._mbX];
block.isIntra4x4 = this._br.getBit(145) === 0;
if (!block.isIntra4x4) {
const ymode = this._br.getBit(156) !== 0
? this._br.getBit(128) !== 0
? VP8.tmPred
: VP8.hPred
: this._br.getBit(163) !== 0
? VP8.vPred
: VP8.dcPred;
block.imodes[0] = ymode;
top.fill(ymode, ti, ti + 4);
left.fill(ymode, li, li + 4);
}
else {
const modes = block.imodes;
let mi = 0;
for (let y = 0; y < 4; ++y) {
let ymode = left[y];
for (let x = 0; x < 4; ++x) {
const prob = VP8.kBModesProba[top[ti + x]][ymode];
const b = this._br.getBit(prob[0]);
let i = VP8.kYModesIntra4[b];
while (i > 0) {
i = VP8.kYModesIntra4[2 * i + this._br.getBit(prob[i])];
}
ymode = -i;
top[ti + x] = ymode;
}
ArrayUtils.copyRange(top, ti, modes, mi, 4);
mi += 4;
left[y] = ymode;
}
}
block.uvmode =
this._br.getBit(142) === 0
? VP8.dcPred
: this._br.getBit(114) === 0
? VP8.vPred
: this._br.getBit(183) !== 0
? VP8.tmPred
: VP8.hPred;
}
decodeHeader() {
const bits = this._input.readUint24();
const keyFrame = (bits & 1) === 0;
if (!keyFrame) {
return false;
}
if (((bits >>> 1) & 7) > 3) {
return false;
}
if (((bits >>> 4) & 1) === 0) {
return false;
}
this._frameHeader.keyFrame = (bits & 1) === 0;
this._frameHeader.profile = (bits >>> 1) & 7;
this._frameHeader.show = (bits >>> 4) & 1;
this._frameHeader.partitionLength = bits >>> 5;
const signature = this._input.readUint24();
if (signature !== VP8.vp8Signature) {
return false;
}
this._webp.width = this._input.readUint16();
this._webp.height = this._input.readUint16();
return true;
}
decode() {
if (!this.getHeaders()) {
return undefined;
}
this._output = new MemoryImage({
width: this._webp.width,
height: this._webp.height,
numChannels: 4,
});
if (!this.initFrame()) {
return undefined;
}
if (!this.parseFrame()) {
return undefined;
}
if (this._webp.exifData.length > 0) {
const input = new InputBuffer({
buffer: StringUtils.getCodePoints(this._webp.exifData),
});
this._output.exifData = ExifData.fromInputBuffer(input);
}
return this._output;
}
macroBlockVPos(mbY) {
return mbY * 16;
}
}
VP8.filterExtraRows = [0, 2, 8];
VP8.vp8Signature = 0x2a019d;
VP8.mbFeatureTreeProbs = 3;
VP8.numMbSegments = 4;
VP8.numRefLfDeltas = 4;
VP8.numModeLfDeltas = 4;
VP8.maxNumPartitions = 8;
VP8.bDcPred = 0;
VP8.bTmPred = 1;
VP8.bVePred = 2;
VP8.bHePred = 3;
VP8.bRdPred = 4;
VP8.bVrPred = 5;
VP8.bLdPred = 6;
VP8.bVlPred = 7;
VP8.bHdPred = 8;
VP8.bHuPred = 9;
VP8.numBModes = VP8.bHuPred + 1 - VP8.bDcPred;
VP8.dcPred = VP8.bDcPred;
VP8.vPred = VP8.bVePred;
VP8.hPred = VP8.bHePred;
VP8.tmPred = VP8.bTmPred;
VP8.bPred = VP8.numBModes;
VP8.bDcPredNoTop = 4;
VP8.bDcPredNoLeft = 5;
VP8.bDcPredNoTopLeft = 6;
VP8.numBDcModes = 7;
VP8.numTypes = 4;
VP8.numBands = 8;
VP8.numCtx = 3;
VP8.numProbas = 11;
VP8.bps = 32;
VP8.yuvSize = VP8.bps * 17 + VP8.bps * 9;
VP8.ySize = VP8.bps * 17;
VP8.yOffset = Number(VP8.bps) + 8;
VP8.uOffset = VP8.yOffset + VP8.bps * 16 + VP8.bps;
VP8.vOffset = VP8.uOffset + 16;
VP8.yuvFix = 16;
VP8.yuvHalf = 1 << (VP8.yuvFix - 1);
VP8.yuvMask = (256 << VP8.yuvFix) - 1;
VP8.yuvRangeMin = -227;
VP8.yuvRangeMax = 256 + 226;
VP8.yuvFix2 = 14;
VP8.yuvHalf2 = 1 << (VP8.yuvFix2 - 1);
VP8.yuvMask2 = (256 << VP8.yuvFix2) - 1;
VP8.xorYuvMask2 = -VP8.yuvMask2 - 1;
VP8.kYScale = 19077;
VP8.kVToR = 26149;
VP8.kUToG = 6419;
VP8.kVToG = 13320;
VP8.kUToB = 33050;
VP8.kRCst = -VP8.kYScale * 16 - VP8.kVToR * 128 + VP8.yuvHalf2;
VP8.kGCst = -VP8.kYScale * 16 + VP8.kUToG * 128 + VP8.kVToG * 128 + VP8.yuvHalf2;
VP8.kBCst = -VP8.kYScale * 16 - VP8.kUToB * 128 + VP8.yuvHalf2;
VP8.kYModesIntra4 = [
-VP8.bDcPred,
1,
-VP8.bTmPred,
2,
-VP8.bVePred,
3,
4,
6,
-VP8.bHePred,
5,
-VP8.bRdPred,
-VP8.bVrPred,
-VP8.bLdPred,
7,
-VP8.bVlPred,
8,
-VP8.bHdPred,
-VP8.bHuPred,
];
VP8.kBModesProba = [
[
[231, 120, 48, 89, 115, 113, 120, 152, 112],
[152, 179, 64, 126, 170, 118, 46, 70, 95],
[175, 69, 143, 80, 85, 82, 72, 155, 103],
[56, 58, 10, 171, 218, 189, 17, 13, 152],
[114, 26, 17, 163, 44, 195, 21, 10, 173],
[121, 24, 80, 195, 26, 62, 44, 64, 85],
[144, 71, 10, 38, 171, 213, 144, 34, 26],
[170, 46, 55, 19, 136, 160, 33, 206, 71],
[63, 20, 8, 114, 114, 208, 12, 9, 226],
[81, 40, 11, 96, 182, 84, 29, 16, 36],
],
[
[134, 183, 89, 137, 98, 101, 106, 165, 148],
[72, 187, 100, 130, 157, 111, 32, 75, 80],
[66, 102, 167, 99, 74, 62, 40, 234, 128],
[41, 53, 9, 178, 241, 141, 26, 8, 107],
[74, 43, 26, 146, 73, 166, 49, 23, 157],
[65, 38, 105, 160, 51, 52, 31, 115, 128],
[104, 79, 12, 27, 217, 255, 87, 17, 7],
[87, 68, 71, 44, 114, 51, 15, 186, 23],
[47, 41, 14, 110, 182, 183, 21, 17, 194],
[66, 45, 25, 102, 197, 189, 23, 18, 22],
],
[
[88, 88, 147, 150, 42, 46, 45, 196, 205],
[43, 97, 183, 117, 85, 38, 35, 179, 61],
[39, 53, 200, 87, 26, 21, 43, 232, 171],
[56, 34, 51, 104, 114, 102, 29, 93, 77],
[39, 28, 85, 171, 58, 165, 90, 98, 64],
[34, 22, 116, 206, 23, 34, 43, 166, 73],
[107, 54, 32, 26, 51, 1, 81, 43, 31],
[68, 25, 106, 22, 64, 171, 36, 225, 114],
[34, 19, 21, 102, 132, 188, 16, 76, 124],
[62, 18, 78, 95, 85, 57, 50, 48, 51],
],
[
[193, 101, 35, 159, 215, 111, 89, 46, 111],
[60, 148, 31, 172, 219, 228, 21, 18, 111],
[112, 113, 77, 85, 179, 255, 38, 120, 114],
[40, 42, 1, 196, 245, 209, 10, 25, 109],
[88, 43, 29, 140, 166, 213, 37, 43, 154],
[61, 63, 30, 155, 67, 45, 68, 1, 209],
[100, 80, 8, 43, 154, 1, 51, 26, 71],
[142, 78, 78, 16, 255, 128, 34, 197, 171],
[41, 40, 5, 102, 211, 183, 4, 1, 221],
[51, 50, 17, 168, 209, 192, 23, 25, 82],
],
[
[138, 31, 36, 171, 27, 166, 38, 44, 229],
[67, 87, 58, 169, 82, 115, 26, 59, 179],
[63, 59, 90, 180, 59, 166, 93, 73, 154],
[40, 40, 21, 116, 143, 209, 34, 39, 175],
[47, 15, 16, 183, 34, 223, 49, 45, 183],
[46, 17, 33, 183, 6, 98, 15, 32, 183],
[57, 46, 22, 24, 128, 1, 54, 17, 37],
[65, 32, 73, 115, 28, 128, 23, 128, 205],
[40, 3, 9, 115, 51, 192, 18, 6, 223],
[87, 37, 9, 115, 59, 77, 64, 21, 47],
],
[
[104, 55, 44, 218, 9, 54, 53, 130, 226],
[64, 90, 70, 205, 40, 41, 23, 26, 57],
[54, 57, 112, 184, 5, 41, 38, 166, 213],
[30, 34, 26, 133, 152, 116, 10, 32, 134],
[39, 19, 53, 221, 26, 114, 32, 73, 255],
[31, 9, 65, 234, 2, 15, 1, 118, 73],
[75, 32, 12, 51, 192, 255, 160, 43, 51],
[88, 31, 35, 67, 102, 85, 55, 186, 85],
[56, 21, 23, 111, 59, 205, 45, 37, 192],
[55, 38, 70, 124, 73, 102, 1, 34, 98],
],
[
[125, 98, 42, 88, 104, 85, 117, 175, 82],
[95, 84, 53, 89, 128, 100, 113, 101, 45],
[75, 79, 123, 47, 51, 128, 81, 171, 1],
[57, 17, 5, 71, 102, 57, 53, 41, 49],
[38, 33, 13, 121, 57, 73, 26, 1, 85],
[41, 10, 67, 138, 77, 110, 90, 47, 114],
[115, 21, 2, 10, 102, 255, 166, 23, 6],
[101, 29, 16, 10, 85, 128, 101, 196, 26],
[57, 18, 10, 102, 102, 213, 34, 20, 43],
[117, 20, 15, 36, 163, 128, 68, 1, 26],
],
[
[102, 61, 71, 37, 34, 53, 31, 243, 192],
[69, 60, 71, 38, 73, 119, 28, 222, 37],
[68, 45, 128, 34, 1, 47, 11, 245, 171],
[62, 17, 19, 70, 146, 85, 55, 62, 70],
[37, 43, 37, 154, 100, 163, 85, 160, 1],
[63, 9, 92, 136, 28, 64, 32, 201, 85],
[75, 15, 9, 9, 64, 255, 184, 119, 16],
[86, 6, 28, 5, 64, 255, 25, 248, 1],
[56, 8, 17, 132, 137, 255, 55, 116, 128],
[58, 15, 20, 82, 135, 57, 26, 121, 40],
],
[
[164, 50, 31, 137, 154, 133, 25, 35, 218],
[51, 103, 44, 131, 131, 123, 31, 6, 158],
[86, 40, 64, 135, 148, 224, 45, 183, 128],
[22, 26, 17, 131, 240, 154, 14, 1, 209],
[45, 16, 21, 91, 64, 222, 7, 1, 197],
[56, 21, 39, 155, 60, 138, 23, 102, 213],
[83, 12, 13, 54, 192, 255, 68, 47, 28],
[85, 26, 85, 85, 128, 128, 32, 146, 171],
[18, 11, 7, 63, 144, 171, 4, 4, 246],
[35, 27, 10, 146, 174, 171, 12, 26, 128],
],
[
[190, 80, 35, 99, 180, 80, 126, 54, 45],
[85, 126, 47, 87, 176, 51, 41, 20, 32],
[101, 75, 128, 139, 118, 146, 116, 128, 85],
[56, 41, 15, 176, 236, 85, 37, 9, 62],
[71, 30, 17, 119, 118, 255, 17, 18, 138],
[101, 38, 60, 138, 55, 70, 43, 26, 142],
[146, 36, 19, 30, 171, 255, 97, 27, 20],
[138, 45, 61, 62, 219, 1, 81, 188, 64],
[32, 41, 20, 117, 151, 142, 20, 21, 163],
[112, 19, 12, 61, 195, 128, 48, 4, 24],
],
];
VP8.coeffsProba0 = [
[
[
[128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
[128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
[128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
],
[
[253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128],
[189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128],
[106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128],
],
[
[1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128],
[181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128],
[78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128],
],
[
[1, 185, 249, 255, 243, 255, 128, 128, 128, 128,