blob: 54d2d85023f6a30ef09bc5815e4bc13c0646f0ed [file] [log] [blame]
import 'dart:typed_data';
import '../../image_exception.dart';
import '../../internal/internal.dart';
import '../../util/input_buffer.dart';
import '../../util/output_buffer.dart';
import 'exr_channel.dart';
import 'exr_compressor.dart';
import 'exr_huffman.dart';
import 'exr_part.dart';
import 'exr_wavelet.dart';
/// Wavelet compression
abstract class ExrPizCompressor extends ExrCompressor {
factory ExrPizCompressor(
ExrPart header, int maxScanLineSize, int numScanLines) =
InternalExrPizCompressor;
}
@internal
class InternalExrPizCompressor extends InternalExrCompressor
implements ExrPizCompressor {
InternalExrPizCompressor(
ExrPart header, this._maxScanLineSize, this._numScanLines)
: super(header as InternalExrPart) {
_channelData = List<_PizChannelData>(header.channels.length);
for (int i = 0; i < _channelData.length; ++i) {
_channelData[i] = _PizChannelData();
}
int tmpBufferSize = (_maxScanLineSize * _numScanLines) ~/ 2;
_tmpBuffer = Uint16List(tmpBufferSize);
}
int numScanLines() => _numScanLines;
Uint8List compress(InputBuffer inPtr, int x, int y, [int width, int height]) {
throw new ImageException('Piz compression not yet supported.');
}
Uint8List uncompress(InputBuffer inPtr, int x, int y,
[int width, int height]) {
if (width == null) {
width = header.width;
}
if (height == null) {
height = header.linesInBuffer;
}
int minX = x;
int maxX = x + width - 1;
int minY = y;
int maxY = y + height - 1;
if (maxX > header.width) {
maxX = header.width - 1;
}
if (maxY > header.height) {
maxY = header.height - 1;
}
decodedWidth = (maxX - minX) + 1;
decodedHeight = (maxY - minY) + 1;
int tmpBufferEnd = 0;
List<ExrChannel> channels = header.channels;
final int numChannels = channels.length;
for (int i = 0; i < numChannels; ++i) {
ExrChannel ch = channels[i];
_PizChannelData cd = _channelData[i];
cd.start = tmpBufferEnd;
cd.end = cd.start;
cd.nx = numSamples(ch.xSampling, minX, maxX);
cd.ny = numSamples(ch.ySampling, minY, maxY);
cd.ys = ch.ySampling;
cd.size = ch.size ~/ 2; //2=size(HALF)
tmpBufferEnd += cd.nx * cd.ny * cd.size;
}
int minNonZero = inPtr.readUint16();
int maxNonZero = inPtr.readUint16();
if (maxNonZero >= BITMAP_SIZE) {
throw new ImageException("Error in header for PIZ-compressed data "
"(invalid bitmap size).");
}
Uint8List bitmap = Uint8List(BITMAP_SIZE);
if (minNonZero <= maxNonZero) {
InputBuffer b = inPtr.readBytes(maxNonZero - minNonZero + 1);
for (int i = 0, j = minNonZero, len = b.length; i < len; ++i) {
bitmap[j++] = b[i];
}
}
Uint16List lut = Uint16List(USHORT_RANGE);
int maxValue = _reverseLutFromBitmap(bitmap, lut);
// Huffman decoding
int length = inPtr.readUint32();
ExrHuffman.uncompress(inPtr, length, _tmpBuffer, tmpBufferEnd);
// Wavelet decoding
for (int i = 0; i < numChannels; ++i) {
_PizChannelData cd = _channelData[i];
for (int j = 0; j < cd.size; ++j) {
ExrWavelet.decode(_tmpBuffer, cd.start + j, cd.nx, cd.size, cd.ny,
cd.nx * cd.size, maxValue);
}
}
// Expand the pixel data to their original range
_applyLut(lut, _tmpBuffer, tmpBufferEnd);
if (_output == null) {
_output = OutputBuffer(
size: (_maxScanLineSize * _numScanLines) + (65536 + 8192));
}
_output.rewind();
//int count = 0;
// Rearrange the pixel data into the format expected by the caller.
for (int y = minY; y <= maxY; ++y) {
for (int i = 0; i < numChannels; ++i) {
_PizChannelData cd = _channelData[i];
if ((y % cd.ys) != 0) {
continue;
}
for (int x = cd.nx * cd.size; x > 0; --x) {
_output.writeUint16(_tmpBuffer[cd.end++]);
}
}
}
return _output.getBytes() as Uint8List;
}
void _applyLut(List<int> lut, List<int> data, int nData) {
for (int i = 0; i < nData; ++i) {
data[i] = lut[data[i]];
}
}
int _reverseLutFromBitmap(Uint8List bitmap, Uint16List lut) {
int k = 0;
for (int i = 0; i < USHORT_RANGE; ++i) {
if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))) != 0) {
lut[k++] = i;
}
}
int n = k - 1;
while (k < USHORT_RANGE) {
lut[k++] = 0;
}
return n; // maximum k where lut[k] is non-zero,
}
static const int USHORT_RANGE = 1 << 16;
static const int BITMAP_SIZE = 8192;
OutputBuffer _output;
int _maxScanLineSize;
int _numScanLines;
List<_PizChannelData> _channelData;
Uint16List _tmpBuffer;
}
class _PizChannelData {
int start;
int end;
int nx;
int ny;
int ys;
int size;
}