UNPKG

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)

969 lines 40.7 kB
import { ColorUtils } from '../../color/color-utils.js'; import { ArrayUtils } from '../../common/array-utils.js'; import { InputBuffer } from '../../common/input-buffer.js'; import { LibError } from '../../error/lib-error.js'; import { MemoryImage } from '../../image/image.js'; import { VP8LBitReader } from './vp8l-bit-reader.js'; import { VP8LColorCache } from './vp8l-color-cache.js'; import { VP8LImageTransformType } from './vp8l-image-transform-type.js'; import { VP8LTransform } from './vp8l-transform.js'; import { WebPFormat } from './webp-format.js'; import { HuffmanTreeGroup } from './webp-huffman-tree-group.js'; import { StringUtils } from '../../common/string-utils.js'; import { ExifData } from '../../exif/exif-data.js'; import { HuffmanTables } from './webp-huffman-tables.js'; import { HuffmanCodeList } from './webp-huffman-code-list.js'; import { HuffmanTablesSegment } from './webp-huffman-table-segment.js'; export class VP8L { get webp() { return this._webp; } constructor(input, webp) { this._htreeGroups = []; this._transforms = []; this._ioWidth = 0; this._ioHeight = 0; this._colorCacheSize = 0; this._huffmanMask = 0; this._huffmanSubsampleBits = 0; this._huffmanXsize = 0; this._numHtreeGroups = 0; this._transformsSeen = 0; this._argbCache = 0; this._lastPixel = 0; this._lastRow = 0; this._input = input; this._webp = webp; this._br = new VP8LBitReader(input); } decodeHeader() { const signature = this._br.readBits(8); if (signature !== VP8L.vp8lMagicByte) return false; const width = this._br.readBits(14) + 1; const height = this._br.readBits(14) + 1; const hasAlpha = this._br.readBits(1) !== 0; this._ioWidth = width; this._ioHeight = height; this._webp.format = WebPFormat.lossless; this._webp.width = width; this._webp.height = height; this._webp.hasAlpha = hasAlpha; const version = this._br.readBits(3); if (version !== VP8L.vp8lVersion) return false; return true; } decode() { this._lastPixel = 0; if (!this.decodeHeader()) return undefined; this.decodeImageStream(this._ioWidth, this._ioHeight, true); this.allocateInternalBuffers32b(this._ioWidth); this._image = new MemoryImage({ width: this._ioWidth, height: this._ioHeight, numChannels: 4, }); if (!this.decodeImageData(this._pixels, this._webp.width, this._webp.height, this._webp.height, (_row, _waitForBiggestBatch) => { this.processRows(_row, _waitForBiggestBatch); })) { return undefined; } if (this._webp.exifData.length > 0) { const input = new InputBuffer({ buffer: StringUtils.getCodePoints(this._webp.exifData), }); this._image.exifData = ExifData.fromInputBuffer(input); } return this._image; } allocateInternalBuffers32b(finalWidth) { const numPixels = this._webp.width * this._webp.height; const cacheTopPixels = finalWidth; const cachePixels = finalWidth * VP8L.numArgbCacheRows; const totalNumPixels = numPixels + cacheTopPixels + cachePixels; const pixels32 = new Uint32Array(totalNumPixels); this._pixels = pixels32; this._pixels8 = new Uint8Array(pixels32.buffer); this._argbCache = numPixels + cacheTopPixels; return true; } allocateInternalBuffers8b() { const totalNumPixels = this._webp.width * this._webp.height; this._argbCache = 0; const n = totalNumPixels + (4 - (totalNumPixels % 4)); this._pixels8 = new Uint8Array(n); this._pixels = new Uint32Array(this._pixels8.buffer); return true; } decodeImageStream(xsize, ysize, isLevel0) { let transformXsize = xsize; let transformYsize = ysize; let colorCacheBits = 0; if (isLevel0) { while (this._br.readBits(1) !== 0) { const sizes = [transformXsize, transformYsize]; if (!this.readTransform(sizes)) throw new LibError('Invalid Transform'); transformXsize = sizes[0]; transformYsize = sizes[1]; } } if (this._br.readBits(1) !== 0) { colorCacheBits = this._br.readBits(4); if (!(colorCacheBits >= 1 && colorCacheBits <= VP8L.maxCacheBits)) throw new LibError('Invalid Color Cache'); } if (!this.readHuffmanCodes(transformXsize, transformYsize, colorCacheBits, isLevel0)) { throw new LibError('Invalid Huffman Codes'); } if (colorCacheBits > 0) { this._colorCacheSize = 1 << colorCacheBits; this._colorCache = new VP8LColorCache(colorCacheBits); } else { this._colorCacheSize = 0; } this._webp.width = transformXsize; this._webp.height = transformYsize; const numBits = this._huffmanSubsampleBits; this._huffmanXsize = VP8L.subSampleSize(transformXsize, numBits); this._huffmanMask = numBits === 0 ? ~0 : (1 << numBits) - 1; if (isLevel0) { this._lastPixel = 0; return undefined; } const totalSize = transformXsize * transformYsize; const data = new Uint32Array(totalSize); if (!this.decodeImageData(data, transformXsize, transformYsize, transformYsize, undefined)) { throw new LibError('Failed to decode image data.'); } this._lastPixel = 0; return data; } decodeImageData(data, width, height, lastRow, processFunc) { let row = Math.trunc(this._lastPixel / width); let col = this._lastPixel % width; let htreeGroup = this.getHtreeGroupForPos(col, row); let src = this._lastPixel; let lastCached = src; const srcEnd = width * height; const srcLast = width * lastRow; const lenCodeLimit = VP8L.numLiteralCodes + VP8L.numLengthCodes; const colorCacheLimit = lenCodeLimit + this._colorCacheSize; const colorCache = this._colorCacheSize > 0 ? this._colorCache : undefined; const mask = this._huffmanMask; while (src < srcLast) { if ((col & mask) === 0) htreeGroup = this.getHtreeGroupForPos(col, row); if (htreeGroup.isTrivialCode) { data[src] = htreeGroup.literalArb; ++src; ++col; if (col >= width) { col = 0; ++row; if (processFunc !== undefined && row <= lastRow) processFunc(row, true); if (colorCache !== undefined) { while (lastCached < src) { colorCache.insert(data[lastCached]); lastCached++; } } } continue; } this._br.fillBitWindow(); let code = 0; if (htreeGroup.usePackedTable) { const val = this._br.prefetchBits() & (VP8L.huffmanPackedTableSize - 1); const code32 = htreeGroup.packedTable[val]; if (code32.bits < VP8L.bitsSpecialMarker) { this._br.bitPos += code32.bits; data[src] = code32.value; code = VP8L.packedNonLiteralCode; } else { this._br.bitPos += code32.bits - VP8L.bitsSpecialMarker; code = code32.value; } if (this._br.isEOS) break; if (code === VP8L.packedNonLiteralCode) { ++src; ++col; if (col >= width) { col = 0; ++row; if (processFunc !== undefined && row <= lastRow) processFunc(row, true); if (colorCache !== undefined) { while (lastCached < src) { colorCache.insert(data[lastCached]); lastCached++; } } } continue; } } else { code = htreeGroup.readSymbol(VP8L.green, this._br); } if (code < VP8L.numLiteralCodes) { if (htreeGroup.isTrivialLiteral) { data[src] = htreeGroup.literalArb | (code << 8); } else { const red = htreeGroup.readSymbol(VP8L.red, this._br); const green = code; this._br.fillBitWindow(); const blue = htreeGroup.readSymbol(VP8L.blue, this._br); const alpha = htreeGroup.readSymbol(VP8L.alpha, this._br); const c = ColorUtils.rgbaToUint32(blue, green, red, alpha); data[src] = c; } ++src; ++col; if (col >= width) { col = 0; ++row; if (processFunc !== undefined && row <= lastRow) processFunc(row, true); if (colorCache !== undefined) { while (lastCached < src) { colorCache.insert(data[lastCached]); lastCached++; } } } } else if (code < lenCodeLimit) { const lengthSym = code - VP8L.numLiteralCodes; const length = this.getCopyLength(lengthSym); const distSymbol = htreeGroup.readSymbol(VP8L.dist, this._br); this._br.fillBitWindow(); const distCode = this.getCopyDistance(distSymbol); const dist = this.planeCodeToDistance(width, distCode); if (src < dist || srcEnd - src < length) { return false; } else { const dst = src - dist; for (let i = 0; i < length; ++i) { data[src + i] = data[dst + i]; } } src += length; col += length; while (col >= width) { col -= width; ++row; if (processFunc !== undefined && row <= lastRow) processFunc(row, true); } if ((col & mask) !== 0) htreeGroup = this.getHtreeGroupForPos(col, row); if (colorCache !== undefined) { while (lastCached < src) { colorCache.insert(data[lastCached]); lastCached++; } } } else if (code < colorCacheLimit) { const key = code - lenCodeLimit; while (lastCached < src) { colorCache.insert(data[lastCached]); lastCached++; } data[src] = colorCache.lookup(key); ++src; ++col; if (col >= width) { col = 0; ++row; if (processFunc !== undefined && row <= lastRow) processFunc(row, true); while (lastCached < src) { colorCache.insert(data[lastCached]); lastCached++; } } } else { return false; } } if (processFunc !== undefined) processFunc(row > lastRow ? lastRow : row, false); this._lastPixel = src; return true; } readTransform(transformSize) { let ok = true; const type = this._br.readBits(2); if ((this._transformsSeen & (1 << type)) !== 0) return false; this._transformsSeen |= 1 << type; const transform = new VP8LTransform(); this._transforms.push(transform); transform.type = type; transform.xsize = transformSize[0]; transform.ysize = transformSize[1]; switch (transform.type) { case VP8LImageTransformType.predictor: case VP8LImageTransformType.crossColor: transform.bits = this._br.readBits(3) + 2; transform.data = this.decodeImageStream(VP8L.subSampleSize(transform.xsize, transform.bits), VP8L.subSampleSize(transform.ysize, transform.bits), false); break; case VP8LImageTransformType.colorIndexing: { const numColors = this._br.readBits(8) + 1; const bits = numColors > 16 ? 0 : numColors > 4 ? 1 : numColors > 2 ? 2 : 3; transformSize[0] = VP8L.subSampleSize(transform.xsize, bits); transform.bits = bits; transform.data = this.decodeImageStream(numColors, 1, false); ok = this.expandColorMap(numColors, transform); break; } case VP8LImageTransformType.subtractGreen: break; default: throw new LibError('Invalid WebP transform type: $type'); } return ok; } expandColorMap(numColors, transform) { const finalNumColors = 1 << (8 >>> transform.bits); const newColorMap = new Uint32Array(finalNumColors); const data = new Uint8Array(transform.data.buffer); const newData = new Uint8Array(newColorMap.buffer); newColorMap[0] = transform.data[0]; let len = 4 * numColors; let i = 0; for (i = 4; i < len; ++i) { newData[i] = (data[i] + newData[i - 4]) & 0xff; } for (len = 4 * finalNumColors; i < len; ++i) { newData[i] = 0; } transform.data = newColorMap; return true; } getMetaIndex(image, xsize, bits, x, y) { if (bits === 0 || image === undefined) return 0; return image[xsize * (y >>> bits) + (x >>> bits)]; } getHtreeGroupForPos(x, y) { const metaIndex = this.getMetaIndex(this._huffmanImage, this._huffmanXsize, this._huffmanSubsampleBits, x, y); return this._htreeGroups[metaIndex]; } applyInverseTransforms(startRow, numRows, rows) { let n = this._transforms.length; const cachePixels = this._webp.width * numRows; const endRow = startRow + numRows; const rowsOut = this._argbCache; ArrayUtils.copyRange(this._pixels, rows, this._pixels, rowsOut, cachePixels); while (n-- > 0) { this._transforms[n].inverseTransform(startRow, endRow, this._pixels, rowsOut, this._pixels, rowsOut); } } processRows(row, waitForBiggestBatch) { const rows = this._webp.width * this._lastRow; if (waitForBiggestBatch) { if (row % VP8L.numArgbCacheRows !== 0) return; } const numRows = row - this._lastRow; if (numRows <= 0) { this._lastRow = row; return; } this.applyInverseTransforms(this._lastRow, numRows, rows); for (let y = 0, pi = this._argbCache, dy = this._lastRow; y < numRows; ++y, ++dy) { for (let x = 0; x < this._ioWidth; ++x, ++pi) { const c = this._pixels[pi]; const r = ColorUtils.uint32ToRed(c); const g = ColorUtils.uint32ToGreen(c); const b = ColorUtils.uint32ToBlue(c); const a = ColorUtils.uint32ToAlpha(c); this._image.setPixelRgba(x, dy, b, g, r, a); } } this._lastRow = row; } readHuffmanCodes(xSize, ySize, colorCacheBits, allowRecursion) { let huffmanImage = undefined; let numHtreeGroups = 1; let numHtreeGroupsMax = 1; let mapping = undefined; if (allowRecursion && this._br.readBits(1) !== 0) { const huffmanPrecision = VP8L.minHuffmanBits + this._br.readBits(VP8L.numHuffmanBits); const huffmanXsize = VP8L.subSampleSize(xSize, huffmanPrecision); const huffmanYsize = VP8L.subSampleSize(ySize, huffmanPrecision); const huffmanPixs = huffmanXsize * huffmanYsize; huffmanImage = this.decodeImageStream(huffmanXsize, huffmanYsize, false); if (huffmanImage === undefined) return false; this._huffmanSubsampleBits = huffmanPrecision; for (let i = 0; i < huffmanPixs; ++i) { const group = (huffmanImage[i] >>> 8) & 0xffff; huffmanImage[i] = group; if (group >= numHtreeGroupsMax) numHtreeGroupsMax = group + 1; } if (numHtreeGroupsMax > 1000 || numHtreeGroupsMax > xSize * ySize) { mapping = new Int32Array(numHtreeGroups); mapping.fill(0xff, 0, numHtreeGroups); numHtreeGroups = 0; for (let i = 0; i < huffmanPixs; ++i) { const mappedGroup = huffmanImage[i]; if (mapping[mappedGroup] === -1) { mapping[mappedGroup] = numHtreeGroups++; } huffmanImage[i] = mapping[mappedGroup]; } } else { numHtreeGroups = numHtreeGroupsMax; } } if (this._br.isEOS) return false; const htreeGroups = this.readHuffmanCodesHelper(colorCacheBits, numHtreeGroups, numHtreeGroupsMax, mapping); if (htreeGroups === undefined) return false; this._huffmanImage = huffmanImage; this._numHtreeGroups = numHtreeGroups; this._htreeGroups = htreeGroups; return true; } readHuffmanCode(alphabetSize, codeLengths, table) { let size = 0; let ok = false; const simpleCode = this._br.readBits(1); codeLengths.fill(0, 0, alphabetSize); if (simpleCode !== 0) { const numSymbols = this._br.readBits(1) + 1; const firstSymbolLenCode = this._br.readBits(1); let symbol = this._br.readBits(firstSymbolLenCode === 0 ? 1 : 8); codeLengths[symbol] = 1; if (numSymbols === 2) { symbol = this._br.readBits(8); codeLengths[symbol] = 1; } ok = true; } else { const codeLengthCodeLengths = new Int32Array(VP8L.numCodeLengthCodes); const numCodes = this._br.readBits(4) + 4; for (let i = 0; i < numCodes; ++i) { codeLengthCodeLengths[VP8L.codeLengthCodeOrder[i]] = this._br.readBits(3); } ok = this.readHuffmanCodeLengths(codeLengthCodeLengths, alphabetSize, codeLengths); } ok = ok && !this._br.isEOS; if (ok) { size = this.vp8lBuildHuffmanTable(table, VP8L.huffmanTableBits, codeLengths, alphabetSize); } return size; } readHuffmanCodeLengths(codeLengthCodeLengths, numSymbols, codeLengths) { let prevCodeLen = VP8L.defaultCodeLength; const tables = new HuffmanTables(1 << VP8L.lengthsTableBits); if (this.vp8lBuildHuffmanTable(tables, VP8L.lengthsTableBits, codeLengthCodeLengths, VP8L.numCodeLengthCodes) === 0) { return false; } let maxSymbol = 0; if (this._br.readBits(1) !== 0) { const lengthNBits = 2 + 2 * this._br.readBits(3); maxSymbol = 2 + this._br.readBits(lengthNBits); if (maxSymbol > numSymbols) return false; } else { maxSymbol = numSymbols; } let symbol = 0; while (symbol < numSymbols) { if (maxSymbol-- === 0) break; this._br.fillBitWindow(); const p = tables.currentSegment.start.get(this._br.prefetchBits() & VP8L.lengthsTableMask); this._br.bitPos += p.bits; const codeLen = p.value; if (codeLen < VP8L.codeLengthLiterals) { codeLengths[symbol++] = codeLen; if (codeLen !== 0) prevCodeLen = codeLen; } else { const usePrev = codeLen === VP8L.codeLengthRepeatCode; const slot = codeLen - VP8L.codeLengthLiterals; const extraBits = VP8L.codeLengthExtraBits[slot]; const repeatOffset = VP8L.codeLengthRepeatOffsets[slot]; let repeat = this._br.readBits(extraBits) + repeatOffset; if (symbol + repeat > numSymbols) return false; const length = usePrev ? prevCodeLen : 0; while (repeat-- > 0) { codeLengths[symbol++] = length; } } } return true; } readHuffmanCodesHelper(colorCacheBits, numHtreeGroups, numHtreeGroupsMax, mapping) { const maxAlphabetSize = VP8L.alphabetSize[0] + (colorCacheBits > 0 ? 1 << colorCacheBits : 0); const tableSize = VP8L.tableSize[colorCacheBits]; if (mapping === undefined && numHtreeGroups !== numHtreeGroupsMax) return undefined; const codeLengths = new Int32Array(maxAlphabetSize); const htreeGroups = ArrayUtils.generate(numHtreeGroups, (_) => new HuffmanTreeGroup()); this._huffmanTables = new HuffmanTables(numHtreeGroups * tableSize); for (let i = 0; i < numHtreeGroupsMax; ++i) { if (mapping !== undefined && mapping[i] === -1) { for (let j = 0; j < VP8L.huffmanCodesPerMetaCode; ++j) { let alphabetSize = VP8L.alphabetSize[j]; if (j === 0 && colorCacheBits > 0) alphabetSize += 1 << colorCacheBits; if (this.readHuffmanCode(alphabetSize, codeLengths, undefined) === 0) { return undefined; } } } else { let maxBits = 0; let isTrivialLiteral = true; let totalSize = 0; const htreeGroup = htreeGroups[mapping === undefined ? i : mapping[i]]; const htrees = htreeGroup.htrees; for (let j = 0; j < VP8L.huffmanCodesPerMetaCode; ++j) { let alphabetSize = VP8L.alphabetSize[j]; if (j === 0 && colorCacheBits > 0) alphabetSize += 1 << colorCacheBits; const size = this.readHuffmanCode(alphabetSize, codeLengths, this._huffmanTables); htrees[j] = this._huffmanTables.currentSegment.currentTable; if (size === 0) return undefined; if (isTrivialLiteral && VP8L.literalMap[j] === 1) { isTrivialLiteral = htrees[j].get(0).bits === 0; } totalSize += htrees[j].get(0).bits; this._huffmanTables.currentSegment.currentOffset += size; this._huffmanTables.currentSegment.currentTable = HuffmanCodeList.from(this._huffmanTables.currentSegment.currentTable, size); if (j <= VP8L.alpha) { let localMaxBits = codeLengths[0]; for (let k = 1; k < alphabetSize; ++k) { if (codeLengths[k] > localMaxBits) localMaxBits = codeLengths[k]; } maxBits += localMaxBits; } } htreeGroup.isTrivialLiteral = isTrivialLiteral; htreeGroup.isTrivialCode = false; if (isTrivialLiteral) { const red = htrees[VP8L.red].get(0).value; const blue = htrees[VP8L.blue].get(0).value; const alpha = htrees[VP8L.alpha].get(0).value; htreeGroup.literalArb = (alpha << 24) | (red << 16) | blue; if (totalSize === 0 && htrees[VP8L.green].get(0).value < VP8L.numLengthCodes) { htreeGroup.isTrivialCode = true; htreeGroup.literalArb |= htrees[VP8L.green].get(0).value << 8; } } htreeGroup.usePackedTable = !htreeGroup.isTrivialCode && maxBits < VP8L.huffmanPackedBits; if (htreeGroup.usePackedTable) this.buildPackedTable(htreeGroup); } } return htreeGroups; } buildPackedTable(htreeGroup) { for (let code = 0; code < VP8L.huffmanPackedTableSize; ++code) { let bits = code; const huff = htreeGroup.packedTable[bits]; const hcode = htreeGroup.htrees[VP8L.green].get(bits); if (hcode.value >= VP8L.numLiteralCodes) { huff.bits = hcode.bits + VP8L.bitsSpecialMarker; huff.value = hcode.value; } else { huff.bits = 0; huff.value = 0; bits >>= this.accumulateHCode(hcode, 8, huff); bits >>= this.accumulateHCode(htreeGroup.htrees[VP8L.red].get(bits), 16, huff); bits >>= this.accumulateHCode(htreeGroup.htrees[VP8L.blue].get(bits), 0, huff); bits >>= this.accumulateHCode(htreeGroup.htrees[VP8L.alpha].get(bits), 24, huff); } } } accumulateHCode(hcode, shift, huff) { huff.bits += hcode.bits; huff.value |= hcode.value << shift; return hcode.bits; } getCopyDistance(distanceSymbol) { if (distanceSymbol < 4) return distanceSymbol + 1; const extraBits = (distanceSymbol - 2) >>> 1; const offset = (2 + (distanceSymbol & 1)) << extraBits; return offset + this._br.readBits(extraBits) + 1; } getCopyLength(lengthSymbol) { return this.getCopyDistance(lengthSymbol); } planeCodeToDistance(xsize, planeCode) { if (planeCode > VP8L.codeToPlaneCodes) { return planeCode - VP8L.codeToPlaneCodes; } else { const distCode = VP8L.codeToPlane[planeCode - 1]; const yoffset = distCode >>> 4; const xoffset = 8 - (distCode & 0xf); const dist = yoffset * xsize + xoffset; return dist >= 1 ? dist : 1; } } is8bOptimizable() { if (this._colorCacheSize > 0) return false; for (let i = 0; i < this._numHtreeGroups; ++i) { const htrees = this._htreeGroups[i].htrees; if (htrees[VP8L.red].get(0).bits > 0) return false; if (htrees[VP8L.blue].get(0).bits > 0) return false; if (htrees[VP8L.alpha].get(0).bits > 0) return false; } return true; } extractPalettedAlphaRows(row) { const numRows = row - this._lastRow; const pIn = new InputBuffer({ buffer: this._pixels8, offset: this._webp.width * this._lastRow, }); if (numRows > 0) this.applyInverseTransformsAlpha(numRows, pIn); this._lastRow = row; } applyInverseTransformsAlpha(numRows, rows) { const startRow = this._lastRow; const endRow = startRow + numRows; const rowsOut = new InputBuffer({ buffer: this._opaque, offset: this._ioWidth * startRow, }); this._transforms[0].colorIndexInverseTransformAlpha(startRow, endRow, rows, rowsOut); } extractAlphaRows(lastRow, waitForBiggestBatch) { if (waitForBiggestBatch && lastRow % VP8L.numArgbCacheRows !== 0) return; let currentRow = this._lastRow; let numRows = lastRow - currentRow; let inPtr = this._ioWidth * currentRow; while (numRows > 0) { const numRowsToProcess = numRows > VP8L.numArgbCacheRows ? VP8L.numArgbCacheRows : numRows; const width = this._ioWidth; const cachePixels = width * numRowsToProcess; const dst = width * currentRow; const src = this._argbCache; this.applyInverseTransforms(currentRow, numRowsToProcess, inPtr); for (let i = 0; i < cachePixels; ++i) { this._opaque[dst + i] = (this._pixels[src + i] >> 8) & 0xff; } numRows -= numRowsToProcess; inPtr += numRowsToProcess * this._ioWidth; currentRow += numRowsToProcess; } this._lastRow = lastRow; } decodeAlphaData(width, height, lastRow) { let row = Math.trunc(this._lastPixel / width); let col = this._lastPixel % width; let htreeGroup = this.getHtreeGroupForPos(col, row); let pos = this._lastPixel; const end = width * height; const last = width * lastRow; const lenCodeLimit = VP8L.numLiteralCodes + VP8L.numLengthCodes; const mask = this._huffmanMask; while (!this._br.isEOS && pos < last) { if ((col & mask) === 0) htreeGroup = this.getHtreeGroupForPos(col, row); this._br.fillBitWindow(); const code = htreeGroup.readSymbol(VP8L.green, this._br); if (code < VP8L.numLiteralCodes) { this._pixels8[pos] = code; ++pos; ++col; if (col >= width) { col = 0; ++row; if (row % VP8L.numArgbCacheRows === 0) this.extractPalettedAlphaRows(row); } } else if (code < lenCodeLimit) { const lengthSym = code - VP8L.numLiteralCodes; const length = this.getCopyLength(lengthSym); const distSymbol = htreeGroup.readSymbol(VP8L.dist, this._br); this._br.fillBitWindow(); const distCode = this.getCopyDistance(distSymbol); const dist = this.planeCodeToDistance(width, distCode); if (pos >= dist && end - pos >= length) { for (let i = 0; i < length; ++i) { this._pixels8[pos + i] = this._pixels8[pos + i - dist]; } } else { this._lastPixel = pos; return true; } pos += length; col += length; while (col >= width) { col -= width; ++row; if (row % VP8L.numArgbCacheRows === 0) this.extractPalettedAlphaRows(row); } if (pos < last && (col & mask) !== 0) htreeGroup = this.getHtreeGroupForPos(col, row); } else { return false; } } this.extractPalettedAlphaRows(row); this._lastPixel = pos; return true; } replicateValue(table, key, step, end, bits, value) { let currentEnd = end; do { currentEnd -= step; table.get(key + currentEnd).bits = bits; table.get(key + currentEnd).value = value; } while (currentEnd > 0); } nextTableBitSize(count, len, rootBits) { let _len = len; let left = 1 << (_len - rootBits); while (_len < VP8L.maxAllowedCodeLength) { left -= count[_len]; if (left <= 0) break; ++_len; left <<= 1; } return _len - rootBits; } getNextKey(key, len) { let step = 1 << (len - 1); while ((key & step) !== 0) step >>= 1; return step !== 0 ? (key & (step - 1)) + step : key; } buildHuffmanTable(rootTable, rootBits, codeLengths, codeLengthsSize, sorted) { let totalSize = 1 << rootBits; const count = new Int32Array(VP8L.maxAllowedCodeLength + 1); const offset = new Int32Array(VP8L.maxAllowedCodeLength + 1); let tableOffset = 0; for (let symbol = 0; symbol < codeLengthsSize; ++symbol) { if (codeLengths[symbol] > VP8L.maxAllowedCodeLength) return 0; ++count[codeLengths[symbol]]; } if (count[0] === codeLengthsSize) return 0; offset[1] = 0; for (let len = 1; len < VP8L.maxAllowedCodeLength; ++len) { if (count[len] > 1 << len) return 0; offset[len + 1] = offset[len] + count[len]; } for (let symbol = 0; symbol < codeLengthsSize; ++symbol) { const symbolCodeLength = codeLengths[symbol]; if (codeLengths[symbol] > 0) { if (sorted !== undefined) { if (offset[symbolCodeLength] >= codeLengthsSize) return 0; sorted[offset[symbolCodeLength]++] = symbol; } else { offset[symbolCodeLength]++; } } } if (offset[VP8L.maxAllowedCodeLength] === 1) { if (sorted !== undefined) { this.replicateValue(rootTable, 0, 1, totalSize, 0, sorted[0]); } return totalSize; } { let low = 0xffffffff; const mask = totalSize - 1; let key = 0; let numNodes = 1; let numOpen = 1; let tableBits = rootBits; let tableSize = 1 << tableBits; let symbol = 0; for (let len = 1, step = 2; len <= rootBits; ++len, step <<= 1) { numOpen <<= 1; numNodes += numOpen; numOpen -= count[len]; if (numOpen < 0) return 0; if (rootTable === undefined) continue; for (; count[len] > 0; --count[len]) { const bits = len & 0xff; const value = sorted[symbol++]; this.replicateValue(rootTable, tableOffset + key, step, tableSize, bits, value); key = this.getNextKey(key, len); } } for (let len = rootBits + 1, step = 2; len <= VP8L.maxAllowedCodeLength; ++len, step <<= 1) { numOpen <<= 1; numNodes += numOpen; numOpen -= count[len]; if (numOpen < 0) return 0; for (; count[len] > 0; --count[len]) { if ((key & mask) !== low) { if (rootTable !== undefined) tableOffset += tableSize; tableBits = this.nextTableBitSize(count, len, rootBits); tableSize = 1 << tableBits; totalSize += tableSize; low = key & mask; if (rootTable !== undefined) { const bits = (tableBits + rootBits) & 0xff; const value = tableOffset - low; rootTable.get(low).bits = bits; rootTable.get(low).value = value; } } if (rootTable !== undefined) { const bits = (len - rootBits) & 0xff; const value = sorted[symbol++]; this.replicateValue(rootTable, tableOffset + (key >> rootBits), step, tableSize, bits, value); } key = this.getNextKey(key, len); } } if (numNodes !== 2 * offset[VP8L.maxAllowedCodeLength] - 1) return 0; } return totalSize; } vp8lBuildHuffmanTable(rootTable, rootBits, codeLengths, codeLengthsSize) { const totalSize = this.buildHuffmanTable(undefined, rootBits, codeLengths, codeLengthsSize, undefined); if (totalSize === 0 || rootTable === undefined) return totalSize; if (rootTable.currentSegment.currentOffset + totalSize >= rootTable.currentSegment.size) { const segmentSize = rootTable.currentSegment.size; const next = new HuffmanTablesSegment(); { const nextSize = totalSize > segmentSize ? totalSize : segmentSize; const nextStart = HuffmanCodeList.sized(nextSize); next.size = nextSize; next.start = nextStart; } next.currentTable = next.start; next.next = undefined; rootTable.currentSegment.next = next; rootTable.currentSegment = next; } const sorted = new Uint16Array(codeLengthsSize); this.buildHuffmanTable(rootTable.currentSegment.currentTable, rootBits, codeLengths, codeLengthsSize, sorted); return totalSize; } static subSampleSize(size, samplingBits) { return (size + (1 << samplingBits) - 1) >>> samplingBits; } } VP8L.green = 0; VP8L.red = 1; VP8L.blue = 2; VP8L.alpha = 3; VP8L.dist = 4; VP8L.numArgbCacheRows = 16; VP8L.numCodeLengthCodes = 19; VP8L.codeLengthCodeOrder = [ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; VP8L.huffmanTableBits = 8; VP8L.codeToPlaneCodes = 120; VP8L.codeToPlane = [ 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a, 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a, 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b, 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03, 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c, 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e, 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b, 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f, 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b, 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41, 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f, 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70, ]; VP8L.lengthsTableBits = 7; VP8L.lengthsTableMask = (1 << VP8L.lengthsTableBits) - 1; VP8L.codeLengthLiterals = 16; VP8L.codeLengthRepeatCode = 16; VP8L.codeLengthExtraBits = [2, 3, 7]; VP8L.codeLengthRepeatOffsets = [3, 3, 11]; VP8L.argbBlack = 0xff000000; VP8L.maxCacheBits = 11; VP8L.huffmanCodesPerMetaCode = 5; VP8L.huffmanPackedBits = 6; VP8L.huffmanPackedTableSize = 1 << VP8L.huffmanPackedBits; VP8L.minHuffmanBits = 2; VP8L.numHuffmanBits = 3; VP8L.defaultCodeLength = 8; VP8L.maxAllowedCodeLength = 15; VP8L.numLiteralCodes = 256; VP8L.numLengthCodes = 24; VP8L.numDistanceCodes = 40; VP8L.codeLengthCodes = 19; VP8L.packedNonLiteralCode = 0; VP8L.bitsSpecialMarker = 0x100; VP8L.alphabetSize = [ VP8L.numLiteralCodes + VP8L.numLengthCodes, VP8L.numLiteralCodes, VP8L.numLiteralCodes, VP8L.numLiteralCodes, VP8L.numDistanceCodes, ]; VP8L.fixedTableSize = 630 * 3 + 410; VP8L.tableSize = [ VP8L.fixedTableSize + 654, VP8L.fixedTableSize + 656, VP8L.fixedTableSize + 658, VP8L.fixedTableSize + 662, VP8L.fixedTableSize + 670, VP8L.fixedTableSize + 686, VP8L.fixedTableSize + 718, VP8L.fixedTableSize + 782, VP8L.fixedTableSize + 912, VP8L.fixedTableSize + 1168, VP8L.fixedTableSize + 1680, VP8L.fixedTableSize + 2704, ]; VP8L.literalMap = [0, 1, 1, 1, 0]; VP8L.vp8lMagicByte = 0x2f; VP8L.vp8lVersion = 0; //# sourceMappingURL=vp8l.js.map