blob: 97aeed40b7d92bd57d46d65a83b1dfb2cd65a144 [file] [log] [blame]
import 'dart:typed_data';
import '../../internal/bit_operators.dart';
import '../../util/input_buffer.dart';
import 'vp8l.dart';
class VP8LTransform {
// enum VP8LImageTransformType
static const int PREDICTOR_TRANSFORM = 0;
static const int CROSS_COLOR_TRANSFORM = 1;
static const int SUBTRACT_GREEN = 2;
static const int COLOR_INDEXING_TRANSFORM = 3;
int type = 0;
int xsize = 0;
int ysize = 0;
Uint32List data;
int bits = 0;
void inverseTransform(int rowStart, int rowEnd, Uint32List inData, int rowsIn,
Uint32List outData, int rowsOut) {
final int width = xsize;
switch (type) {
case SUBTRACT_GREEN:
addGreenToBlueAndRed(
outData, rowsOut, rowsOut + (rowEnd - rowStart) * width);
break;
case PREDICTOR_TRANSFORM:
predictorInverseTransform(rowStart, rowEnd, outData, rowsOut);
if (rowEnd != ysize) {
// The last predicted row in this iteration will be the top-pred row
// for the first row in next iteration.
int start = rowsOut - width;
int end = start + width;
int offset = rowsOut + (rowEnd - rowStart - 1) * width;
outData.setRange(start, end, inData, offset);
}
break;
case CROSS_COLOR_TRANSFORM:
colorSpaceInverseTransform(rowStart, rowEnd, outData, rowsOut);
break;
case COLOR_INDEXING_TRANSFORM:
if (rowsIn == rowsOut && bits > 0) {
// Move packed pixels to the end of unpacked region, so that unpacking
// can occur seamlessly.
// Also, note that this is the only transform that applies on
// the effective width of VP8LSubSampleSize(xsize_, bits_). All other
// transforms work on effective width of xsize_.
final int outStride = (rowEnd - rowStart) * width;
final int inStride =
(rowEnd - rowStart) * InternalVP8L.subSampleSize(xsize, bits);
int src = rowsOut + outStride - inStride;
outData.setRange(src, src + inStride, inData, rowsOut);
colorIndexInverseTransform(
rowStart, rowEnd, inData, src, outData, rowsOut);
} else {
colorIndexInverseTransform(
rowStart, rowEnd, inData, rowsIn, outData, rowsOut);
}
break;
}
}
void colorIndexInverseTransformAlpha(
int yStart, int yEnd, InputBuffer src, InputBuffer dst) {
final int bitsPerPixel = 8 >> bits;
final int width = xsize;
Uint32List colorMap = this.data;
if (bitsPerPixel < 8) {
final int pixelsPerByte = 1 << bits;
final int countMask = pixelsPerByte - 1;
final bit_mask = (1 << bitsPerPixel) - 1;
for (int y = yStart; y < yEnd; ++y) {
int packed_pixels = 0;
for (int x = 0; x < width; ++x) {
// We need to load fresh 'packed_pixels' once every
// 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte
// is a power of 2, so can just use a mask for that, instead of
// decrementing a counter.
if ((x & countMask) == 0) {
packed_pixels = _getAlphaIndex(src[0]);
src.offset++;
}
int p = _getAlphaValue(colorMap[packed_pixels & bit_mask]);
;
dst[0] = p;
dst.offset++;
packed_pixels >>= bitsPerPixel;
}
}
} else {
for (int y = yStart; y < yEnd; ++y) {
for (int x = 0; x < width; ++x) {
int index = _getAlphaIndex(src[0]);
src.offset++;
dst[0] = _getAlphaValue(colorMap[index]);
dst.offset++;
}
}
}
}
void colorIndexInverseTransform(int yStart, int yEnd, Uint32List inData,
int src, Uint32List outData, int dst) {
final int bitsPerPixel = 8 >> bits;
final int width = xsize;
Uint32List colorMap = this.data;
if (bitsPerPixel < 8) {
final int pixelsPerByte = 1 << bits;
final int countMask = pixelsPerByte - 1;
final bit_mask = (1 << bitsPerPixel) - 1;
for (int y = yStart; y < yEnd; ++y) {
int packed_pixels = 0;
for (int x = 0; x < width; ++x) {
// We need to load fresh 'packed_pixels' once every
// 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte
// is a power of 2, so can just use a mask for that, instead of
// decrementing a counter.
if ((x & countMask) == 0) {
packed_pixels = _getARGBIndex(inData[src++]);
}
outData[dst++] = _getARGBValue(colorMap[packed_pixels & bit_mask]);
packed_pixels >>= bitsPerPixel;
}
}
} else {
for (int y = yStart; y < yEnd; ++y) {
for (int x = 0; x < width; ++x) {
outData[dst++] =
_getARGBValue(colorMap[_getARGBIndex(inData[src++])]);
}
}
}
}
/// Color space inverse transform.
void colorSpaceInverseTransform(
int yStart, int yEnd, Uint32List outData, int data) {
final int width = xsize;
final int mask = (1 << bits) - 1;
final int tilesPerRow = InternalVP8L.subSampleSize(width, bits);
int y = yStart;
int predRow = (y >> bits) * tilesPerRow; //this.data +
while (y < yEnd) {
int pred = predRow; // this.data+
_VP8LMultipliers m = _VP8LMultipliers();
for (int 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;
;
}
}
}
/// Inverse prediction.
void predictorInverseTransform(
int yStart, int yEnd, Uint32List outData, int data) {
final int width = xsize;
if (yStart == 0) {
// First Row follows the L (mode=1) mode.
final int pred0 = _predictor0(outData, outData[data - 1], 0);
_addPixelsEq(outData, data, pred0);
for (int x = 1; x < width; ++x) {
final int pred1 = _predictor1(outData, outData[data + x - 1], 0);
_addPixelsEq(outData, data + x, pred1);
}
data += width;
++yStart;
}
int y = yStart;
final int mask = (1 << bits) - 1;
final int tilesPerRow = InternalVP8L.subSampleSize(width, bits);
int predModeBase = (y >> bits) * tilesPerRow; //this.data +
while (y < yEnd) {
final int pred2 = _predictor2(outData, outData[data - 1], data - width);
int predModeSrc = predModeBase; //this.data +
// First pixel follows the T (mode=2) mode.
_addPixelsEq(outData, data, pred2);
// .. the rest:
int k = (this.data[predModeSrc++] >> 8) & 0xf;
var predFunc = PREDICTORS[k];
for (int x = 1; x < width; ++x) {
if ((x & mask) == 0) {
// start of tile. Read predictor function.
int k = ((this.data[predModeSrc++]) >> 8) & 0xf;
predFunc = PREDICTORS[k];
}
int d = outData[data + x - 1];
int pred = predFunc(outData, d, data + x - width);
_addPixelsEq(outData, data + x, pred);
}
data += width;
++y;
if ((y & mask) == 0) {
// Use the same mask, since tiles are squares.
predModeBase += tilesPerRow;
}
}
}
/// Add green to blue and red channels (i.e. perform the inverse transform of
/// 'subtract green').
void addGreenToBlueAndRed(Uint32List pixels, int data, int dataEnd) {
while (data < dataEnd) {
final int argb = pixels[data];
final int green = ((argb >> 8) & 0xff);
int redBlue = (argb & 0x00ff00ff);
redBlue += (green << 16) | green;
redBlue &= 0x00ff00ff;
pixels[data++] = (argb & 0xff00ff00) | redBlue;
}
}
static int _getARGBIndex(int idx) {
return (idx >> 8) & 0xff;
}
static int _getAlphaIndex(int idx) {
return idx;
}
static int _getARGBValue(int val) {
return val;
}
static int _getAlphaValue(int val) {
return (val >> 8) & 0xff;
}
/// In-place sum of each component with mod 256.
static void _addPixelsEq(Uint32List pixels, int a, int b) {
int pa = pixels[a];
final int alphaAndGreen = (pa & 0xff00ff00) + (b & 0xff00ff00);
final int redAndBlue = (pa & 0x00ff00ff) + (b & 0x00ff00ff);
pixels[a] = (alphaAndGreen & 0xff00ff00) | (redAndBlue & 0x00ff00ff);
}
static int _average2(int a0, int a1) {
return (((a0 ^ a1) & 0xfefefefe) >> 1) + (a0 & a1);
}
static int _average3(int a0, int a1, int a2) {
return _average2(_average2(a0, a2), a1);
}
static int _average4(int a0, int a1, int a2, int a3) {
return _average2(_average2(a0, a1), _average2(a2, a3));
}
/// Return 0, when a is a negative integer.
/// Return 255, when a is positive.
static int _clip255(int a) {
if (a < 0) {
return 0;
}
if (a > 255) {
return 255;
}
return a;
}
static int _addSubtractComponentFull(int a, int b, int c) {
return _clip255(a + b - c);
}
static int _clampedAddSubtractFull(int c0, int c1, int c2) {
final int a = _addSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
final int r = _addSubtractComponentFull(
(c0 >> 16) & 0xff, (c1 >> 16) & 0xff, (c2 >> 16) & 0xff);
final int g = _addSubtractComponentFull(
(c0 >> 8) & 0xff, (c1 >> 8) & 0xff, (c2 >> 8) & 0xff);
final int b = _addSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
return (a << 24) | (r << 16) | (g << 8) | b;
}
static int _addSubtractComponentHalf(int a, int b) {
return _clip255(a + (a - b) ~/ 2);
}
static int _clampedAddSubtractHalf(int c0, int c1, int c2) {
final int avg = _average2(c0, c1);
final int a = _addSubtractComponentHalf(avg >> 24, c2 >> 24);
final int r =
_addSubtractComponentHalf((avg >> 16) & 0xff, (c2 >> 16) & 0xff);
final int g =
_addSubtractComponentHalf((avg >> 8) & 0xff, (c2 >> 8) & 0xff);
final int b =
_addSubtractComponentHalf((avg >> 0) & 0xff, (c2 >> 0) & 0xff);
return (a << 24) | (r << 16) | (g << 8) | b;
}
static int _sub3(int a, int b, int c) {
final int pb = b - c;
final int pa = a - c;
return pb.abs() - pa.abs();
}
static int _select(int a, int b, int c) {
final int pa_minus_pb = _sub3((a >> 24), (b >> 24), (c >> 24)) +
_sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
_sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
_sub3((a) & 0xff, (b) & 0xff, (c) & 0xff);
return (pa_minus_pb <= 0) ? a : b;
}
//--------------------------------------------------------------------------
// Predictors
static int _predictor0(Uint32List pixels, int left, int top) {
return VP8L.ARGB_BLACK;
}
static int _predictor1(Uint32List pixels, int left, int top) {
return left;
}
static int _predictor2(Uint32List pixels, int left, int top) {
return pixels[top];
}
static int _predictor3(Uint32List pixels, int left, int top) {
return pixels[top + 1];
}
static int _predictor4(Uint32List pixels, int left, int top) {
return pixels[top - 1];
}
static int _predictor5(Uint32List pixels, int left, int top) {
return _average3(left, pixels[top], pixels[top + 1]);
}
static int _predictor6(Uint32List pixels, int left, int top) {
return _average2(left, pixels[top - 1]);
}
static int _predictor7(Uint32List pixels, int left, int top) {
return _average2(left, pixels[top]);
}
static int _predictor8(Uint32List pixels, int left, int top) {
return _average2(pixels[top - 1], pixels[top]);
}
static int _predictor9(Uint32List pixels, int left, int top) {
return _average2(pixels[top], pixels[top + 1]);
}
static int _predictor10(Uint32List pixels, int left, int top) {
return _average4(left, pixels[top - 1], pixels[top], pixels[top + 1]);
}
static int _predictor11(Uint32List pixels, int left, int top) {
return _select(pixels[top], left, pixels[top - 1]);
}
static int _predictor12(Uint32List pixels, int left, int top) {
return _clampedAddSubtractFull(left, pixels[top], pixels[top - 1]);
}
static int _predictor13(Uint32List pixels, int left, int top) {
return _clampedAddSubtractHalf(left, pixels[top], pixels[top - 1]);
}
static final PREDICTORS = [
_predictor0,
_predictor1,
_predictor2,
_predictor3,
_predictor4,
_predictor5,
_predictor6,
_predictor7,
_predictor8,
_predictor9,
_predictor10,
_predictor11,
_predictor12,
_predictor13,
_predictor0,
_predictor0
];
}
class _VP8LMultipliers {
final data = Uint8List(3);
// Note: the members are uint8_t, so that any negative values are
// automatically converted to "mod 256" values.
int get greenToRed => data[0];
set greenToRed(int m) => data[0] = m;
int get greenToBlue => data[1];
set greenToBlue(int m) => data[1] = m;
int get redToBlue => data[2];
set redToBlue(int m) => data[2] = m;
void clear() {
data[0] = 0;
data[1] = 0;
data[2] = 0;
}
set colorCode(int colorCode) {
data[0] = (colorCode >> 0) & 0xff;
data[1] = (colorCode >> 8) & 0xff;
data[2] = (colorCode >> 16) & 0xff;
}
int get colorCode => 0xff000000 | (data[2] << 16) | (data[1] << 8) | data[0];
int transformColor(int argb, bool inverse) {
final int green = (argb >> 8) & 0xff;
final int red = (argb >> 16) & 0xff;
int newRed = red;
int newBlue = argb & 0xff;
if (inverse) {
int g = colorTransformDelta(greenToRed, green);
newRed = (newRed + g) & 0xffffffff;
newRed &= 0xff;
newBlue =
(newBlue + colorTransformDelta(greenToBlue, green)) & 0xffffffff;
newBlue = (newBlue + colorTransformDelta(redToBlue, newRed)) & 0xffffffff;
newBlue &= 0xff;
} else {
newRed -= colorTransformDelta(greenToRed, green);
newRed &= 0xff;
newBlue -= colorTransformDelta(greenToBlue, green);
newBlue -= colorTransformDelta(redToBlue, red);
newBlue &= 0xff;
}
int c = (argb & 0xff00ff00) | ((newRed << 16) & 0xffffffff) | (newBlue);
return c;
}
int colorTransformDelta(int colorPred, int color) {
// There's a bug in dart2js (issue 16497) that requires I do this a bit
// convoluted to avoid having the optimizer butcher the code.
int a = uint8ToInt8(colorPred);
int b = uint8ToInt8(color);
int d = int32ToUint32(a * b);
return d >> 5;
}
}