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
JavaScript
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