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)

344 lines 12.5 kB
var _a; import { ArrayUtils } from '../../common/array-utils.js'; import { VP8L } from './vp8l.js'; import { VP8LImageTransformType } from './vp8l-image-transform-type.js'; import { VP8LInternal } from './vp8l-internal.js'; import { VP8LMultipliers } from './vp8l-multipliers.js'; export class VP8LTransform { constructor() { this._type = VP8LImageTransformType.predictor; this._xsize = 0; this._ysize = 0; this._bits = 0; } get type() { return this._type; } set type(v) { this._type = v; } get xsize() { return this._xsize; } set xsize(v) { this._xsize = v; } get ysize() { return this._ysize; } set ysize(v) { this._ysize = v; } get data() { return this._data; } set data(v) { this._data = v; } get bits() { return this._bits; } set bits(v) { this._bits = v; } inverseTransform(rowStart, rowEnd, inData, rowsIn, outData, rowsOut) { const width = this._xsize; switch (this._type) { case VP8LImageTransformType.subtractGreen: this.addGreenToBlueAndRed(outData, rowsOut, rowsOut + (rowEnd - rowStart) * width); break; case VP8LImageTransformType.predictor: this.predictorInverseTransform(rowStart, rowEnd, outData, rowsOut); if (rowEnd !== this._ysize) { const start = rowsOut - width; const end = start + width; const offset = rowsOut + (rowEnd - rowStart - 1) * width; ArrayUtils.copyRange(inData, offset, outData, start, end - start); } break; case VP8LImageTransformType.crossColor: this.colorSpaceInverseTransform(rowStart, rowEnd, outData, rowsOut); break; case VP8LImageTransformType.colorIndexing: if (rowsIn === rowsOut && this._bits > 0) { const outStride = (rowEnd - rowStart) * width; const inStride = (rowEnd - rowStart) * VP8LInternal.subSampleSize(this._xsize, this._bits); const src = rowsOut + outStride - inStride; ArrayUtils.copyRange(inData, rowsOut, outData, src, inStride); this.colorIndexInverseTransform(rowStart, rowEnd, inData, src, outData, rowsOut); } else { this.colorIndexInverseTransform(rowStart, rowEnd, inData, rowsIn, outData, rowsOut); } break; } } colorIndexInverseTransformAlpha(yStart, yEnd, src, dst) { const bitsPerPixel = 8 >>> this._bits; const width = this._xsize; const colorMap = this._data; if (bitsPerPixel < 8) { const pixelsPerByte = 1 << this._bits; const countMask = pixelsPerByte - 1; const bitMask = (1 << bitsPerPixel) - 1; for (let y = yStart; y < yEnd; ++y) { let packedPixels = 0; for (let x = 0; x < width; ++x) { if ((x & countMask) === 0) { packedPixels = _a.getAlphaIndex(src.get(0)); src.offset++; } const p = _a.getAlphaValue(colorMap[packedPixels & bitMask]); dst.set(0, p); dst.offset++; packedPixels >>>= bitsPerPixel; } } } else { for (let y = yStart; y < yEnd; ++y) { for (let x = 0; x < width; ++x) { const index = _a.getAlphaIndex(src.get(0)); src.offset++; dst.set(0, _a.getAlphaValue(colorMap[index])); dst.offset++; } } } } colorIndexInverseTransform(yStart, yEnd, inData, src, outData, dst) { let _src = src; let _dst = dst; const bitsPerPixel = 8 >>> this._bits; const width = this._xsize; const colorMap = this._data; if (bitsPerPixel < 8) { const pixelsPerByte = 1 << this._bits; const countMask = pixelsPerByte - 1; const bitMask = (1 << bitsPerPixel) - 1; for (let y = yStart; y < yEnd; ++y) { let packedPixels = 0; for (let x = 0; x < width; ++x) { if ((x & countMask) === 0) { packedPixels = _a.getARGBIndex(inData[_src++]); } outData[_dst++] = _a.getARGBValue(colorMap[packedPixels & bitMask]); packedPixels >>>= bitsPerPixel; } } } else { for (let y = yStart; y < yEnd; ++y) { for (let x = 0; x < width; ++x) { outData[_dst++] = _a.getARGBValue(colorMap[_a.getARGBIndex(inData[_src++])]); } } } } colorSpaceInverseTransform(yStart, yEnd, outData, data) { let _data = data; const width = this._xsize; const mask = (1 << this._bits) - 1; const tilesPerRow = VP8LInternal.subSampleSize(width, this._bits); let y = yStart; let predRow = (y >>> this._bits) * tilesPerRow; while (y < yEnd) { let pred = predRow; const m = new VP8LMultipliers(); for (let x = 0; x < width; ++x) { if ((x & mask) === 0) { m.colorCode = this._data[pred++]; } outData[_data + x] = m.transformColor(outData[_data + x], true); } _data += width; ++y; if ((y & mask) === 0) { predRow += tilesPerRow; } } } predictorInverseTransform(yStart, yEnd, outData, data) { let _data = data; let _yStart = yStart; const width = this._xsize; if (_yStart === 0) { const pred0 = _a.predictor0(outData, outData[_data - 1], 0); _a.addPixelsEq(outData, _data, pred0); for (let x = 1; x < width; ++x) { const pred1 = _a.predictor1(outData, outData[_data + x - 1], 0); _a.addPixelsEq(outData, _data + x, pred1); } _data += width; ++_yStart; } let y = _yStart; const mask = (1 << this._bits) - 1; const tilesPerRow = VP8LInternal.subSampleSize(width, this._bits); let predModeBase = (y >>> this._bits) * tilesPerRow; while (y < yEnd) { const pred2 = _a.predictor2(outData, outData[_data - 1], _data - width); let predModeSrc = predModeBase; _a.addPixelsEq(outData, _data, pred2); const k = (this._data[predModeSrc++] >>> 8) & 0xf; let predFunc = _a.predictors[k]; for (let x = 1; x < width; ++x) { if ((x & mask) === 0) { const k = (this._data[predModeSrc++] >>> 8) & 0xf; predFunc = _a.predictors[k]; } const d = outData[_data + x - 1]; const pred = predFunc.call(this, outData, d, _data + x - width); _a.addPixelsEq(outData, _data + x, pred); } _data += width; ++y; if ((y & mask) === 0) { predModeBase += tilesPerRow; } } } addGreenToBlueAndRed(pixels, data, dataEnd) { let _data = data; while (_data < dataEnd) { const argb = pixels[_data]; const green = (argb >>> 8) & 0xff; let redBlue = argb & 0x00ff00ff; redBlue += (green << 16) | green; redBlue &= 0x00ff00ff; pixels[_data++] = (argb & 0xff00ff00) | redBlue; } } static getARGBIndex(idx) { return (idx >>> 8) & 0xff; } static getAlphaIndex(idx) { return idx; } static getARGBValue(val) { return val; } static getAlphaValue(val) { return (val >>> 8) & 0xff; } static addPixelsEq(pixels, a, b) { const pa = pixels[a]; const alphaAndGreen = (pa & 0xff00ff00) + (b & 0xff00ff00); const redAndBlue = (pa & 0x00ff00ff) + (b & 0x00ff00ff); pixels[a] = (alphaAndGreen & 0xff00ff00) | (redAndBlue & 0x00ff00ff); } static average2(a0, a1) { return (((a0 ^ a1) & 0xfefefefe) >>> 1) + (a0 & a1); } static average3(a0, a1, a2) { return this.average2(this.average2(a0, a2), a1); } static average4(a0, a1, a2, a3) { return this.average2(this.average2(a0, a1), this.average2(a2, a3)); } static clip255(a) { if (a < 0) { return 0; } if (a > 255) { return 255; } return a; } static addSubtractComponentFull(a, b, c) { return this.clip255(a + b - c); } static clampedAddSubtractFull(c0, c1, c2) { const a = this.addSubtractComponentFull(c0 >>> 24, c1 >>> 24, c2 >>> 24); const r = this.addSubtractComponentFull((c0 >>> 16) & 0xff, (c1 >>> 16) & 0xff, (c2 >>> 16) & 0xff); const g = this.addSubtractComponentFull((c0 >>> 8) & 0xff, (c1 >>> 8) & 0xff, (c2 >>> 8) & 0xff); const b = this.addSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff); return (a << 24) | (r << 16) | (g << 8) | b; } static addSubtractComponentHalf(a, b) { return this.clip255(a + Math.trunc((a - b) / 2)); } static clampedAddSubtractHalf(c0, c1, c2) { const avg = this.average2(c0, c1); const a = this.addSubtractComponentHalf(avg >>> 24, c2 >>> 24); const r = this.addSubtractComponentHalf((avg >>> 16) & 0xff, (c2 >>> 16) & 0xff); const g = this.addSubtractComponentHalf((avg >>> 8) & 0xff, (c2 >>> 8) & 0xff); const b = this.addSubtractComponentHalf((avg >>> 0) & 0xff, (c2 >>> 0) & 0xff); return (a << 24) | (r << 16) | (g << 8) | b; } static sub3(a, b, c) { const pb = b - c; const pa = a - c; return Math.abs(pb) - Math.abs(pa); } static select(a, b, c) { const paMinusPb = this.sub3(a >>> 24, b >>> 24, c >>> 24) + this.sub3((a >>> 16) & 0xff, (b >>> 16) & 0xff, (c >>> 16) & 0xff) + this.sub3((a >>> 8) & 0xff, (b >>> 8) & 0xff, (c >>> 8) & 0xff) + this.sub3(a & 0xff, b & 0xff, c & 0xff); return paMinusPb <= 0 ? a : b; } static predictor0(_pixels, _left, _top) { return VP8L.argbBlack; } static predictor1(_pixels, left, _top) { return left; } static predictor2(pixels, _left, top) { return pixels[top]; } static predictor3(pixels, _left, top) { return pixels[top + 1]; } static predictor4(pixels, _left, top) { return pixels[top - 1]; } static predictor5(pixels, left, top) { return _a.average3(left, pixels[top], pixels[top + 1]); } static predictor6(pixels, left, top) { return _a.average2(left, pixels[top - 1]); } static predictor7(pixels, left, top) { return _a.average2(left, pixels[top]); } static predictor8(pixels, _left, top) { return _a.average2(pixels[top - 1], pixels[top]); } static predictor9(pixels, _left, top) { return _a.average2(pixels[top], pixels[top + 1]); } static predictor10(pixels, left, top) { return _a.average4(left, pixels[top - 1], pixels[top], pixels[top + 1]); } static predictor11(pixels, left, top) { return _a.select(pixels[top], left, pixels[top - 1]); } static predictor12(pixels, left, top) { return _a.clampedAddSubtractFull(left, pixels[top], pixels[top - 1]); } static predictor13(pixels, left, top) { return _a.clampedAddSubtractHalf(left, pixels[top], pixels[top - 1]); } } _a = VP8LTransform; VP8LTransform.predictors = [ _a.predictor0, _a.predictor1, _a.predictor2, _a.predictor3, _a.predictor4, _a.predictor5, _a.predictor6, _a.predictor7, _a.predictor8, _a.predictor9, _a.predictor10, _a.predictor11, _a.predictor12, _a.predictor13, _a.predictor0, _a.predictor0, ]; //# sourceMappingURL=vp8l-transform.js.map