| 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; |
| } |
| |
| var 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 = new WebPInfo(); |
| webp.width = width; |
| webp.height = height; |
| |
| _vp8l = new 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; |
| } |