blob: 89182d1867912061ed61d1d734a67c52953ecc5e [file] [log] [blame]
import 'dart:typed_data';
import '../../util/input_buffer.dart';
import 'vp8l.dart';
import 'vp8l_transform.dart';
import 'webp_filters.dart';
import 'webp_info.dart';
class WebPAlpha {
InputBuffer input;
int width = 0;
int height = 0;
int method = 0;
int filter = 0;
int preProcessing = 0;
int rsrv = 1;
bool isAlphaDecoded = false;
WebPAlpha(this.input, this.width, this.height) {
int b = input.readByte();
method = b & 0x03;
filter = (b >> 2) & 0x03;
preProcessing = (b >> 4) & 0x03;
rsrv = (b >> 6) & 0x03;
if (isValid) {
if (method == ALPHA_NO_COMPRESSION) {
final int alphaDecodedSize = width * height;
if (input.length < alphaDecodedSize) {
rsrv = 1;
}
} else if (method == ALPHA_LOSSLESS_COMPRESSION) {
if (!_decodeAlphaHeader()) {
rsrv = 1;
}
} else {
rsrv = 1;
}
}
}
bool get isValid {
if (method < ALPHA_NO_COMPRESSION ||
method > ALPHA_LOSSLESS_COMPRESSION ||
filter >= WebPFilters.FILTER_LAST ||
preProcessing > ALPHA_PREPROCESSED_LEVELS ||
rsrv != 0) {
return false;
}
return true;
}
bool decode(int row, int numRows, Uint8List output) {
if (!isValid) {
return false;
}
dynamic unfilterFunc = WebPFilters.UNFILTERS[filter];
if (method == ALPHA_NO_COMPRESSION) {
final int offset = row * width;
final int numPixels = numRows * width;
output.setRange(offset, numPixels, input.buffer, input.position + offset);
} else {
if (!_decodeAlphaImageStream(row + numRows, output)) {
return false;
}
}
if (unfilterFunc != null) {
unfilterFunc(width, height, width, row, numRows, output);
}
if (preProcessing == ALPHA_PREPROCESSED_LEVELS) {
if (!_dequantizeLevels(output, width, height, row, numRows)) {
return false;
}
}
if (row + numRows == height) {
isAlphaDecoded = true;
}
return true;
}
bool _dequantizeLevels(
Uint8List data, int width, int height, int row, int num_rows) {
if (data == null ||
width <= 0 ||
height <= 0 ||
row < 0 ||
num_rows < 0 ||
row + num_rows > height) {
return false;
}
return true;
}
bool _decodeAlphaImageStream(int lastRow, Uint8List output) {
_vp8l.opaque = output;
// Decode (with special row processing).
return _use8bDecode
? _vp8l.decodeAlphaData(_vp8l.webp.width, _vp8l.webp.height, lastRow)
: _vp8l.decodeImageData(_vp8l.pixels, _vp8l.webp.width,
_vp8l.webp.height, lastRow, _vp8l.extractAlphaRows);
}
bool _decodeAlphaHeader() {
WebPInfo webp = WebPInfo();
webp.width = width;
webp.height = height;
_vp8l = InternalVP8L(input, webp);
_vp8l.ioWidth = width;
_vp8l.ioHeight = height;
_vp8l.decodeImageStream(webp.width, webp.height, true);
// Special case: if alpha data uses only the color indexing transform and
// doesn't use color cache (a frequent case), we will use DecodeAlphaData()
// method that only needs allocation of 1 byte per pixel (alpha channel).
if (_vp8l.transforms.length == 1 &&
_vp8l.transforms[0].type == VP8LTransform.COLOR_INDEXING_TRANSFORM &&
_vp8l.is8bOptimizable()) {
_use8bDecode = true;
_vp8l.allocateInternalBuffers8b();
} else {
_use8bDecode = false;
_vp8l.allocateInternalBuffers32b();
}
return true;
}
InternalVP8L _vp8l;
/// Although alpha channel
/// requires only 1 byte per
/// pixel, sometimes VP8LDecoder may need to allocate
/// 4 bytes per pixel internally during decode.
bool _use8bDecode = false;
// Alpha related constants.
static const int ALPHA_NO_COMPRESSION = 0;
static const int ALPHA_LOSSLESS_COMPRESSION = 1;
static const int ALPHA_PREPROCESSED_LEVELS = 1;
}