blob: fa6ac1c115124dc6ce2a3a4dc5e23f355a0ed91d [file] [log] [blame]
import '../../image_exception.dart';
import '../../util/input_buffer.dart';
import 'jpeg.dart';
import 'jpeg_component.dart';
import 'jpeg_frame.dart';
class JpegScan {
InputBuffer input;
JpegFrame frame;
int precision;
int samplesPerLine;
int scanLines;
int mcusPerLine;
bool progressive;
int maxH;
int maxV;
List components;
int resetInterval;
int spectralStart;
int spectralEnd;
int successivePrev;
int successive;
int bitsData = 0;
int bitsCount = 0;
int eobrun = 0;
int successiveACState = 0;
int successiveACNextValue;
JpegScan(
this.input,
this.frame,
this.components,
this.resetInterval,
this.spectralStart,
this.spectralEnd,
this.successivePrev,
this.successive) {
precision = frame.precision;
samplesPerLine = frame.samplesPerLine;
scanLines = frame.scanLines;
mcusPerLine = frame.mcusPerLine;
progressive = frame.progressive;
maxH = frame.maxHSamples;
maxV = frame.maxVSamples;
}
void decode() {
int componentsLength = components.length;
JpegComponent component;
dynamic decodeFn;
if (progressive) {
if (spectralStart == 0) {
decodeFn = successivePrev == 0 ? _decodeDCFirst : _decodeDCSuccessive;
} else {
decodeFn = successivePrev == 0 ? _decodeACFirst : _decodeACSuccessive;
}
} else {
decodeFn = _decodeBaseline;
}
int mcu = 0;
int mcuExpected;
if (componentsLength == 1) {
mcuExpected =
(components[0].blocksPerLine * components[0].blocksPerColumn) as int;
} else {
mcuExpected = (mcusPerLine * frame.mcusPerColumn);
}
if (resetInterval == null || resetInterval == 0) {
resetInterval = mcuExpected;
}
int h, v;
while (mcu < mcuExpected) {
// reset interval stuff
for (int i = 0; i < componentsLength; i++) {
components[i].pred = 0;
}
eobrun = 0;
if (componentsLength == 1) {
component = components[0] as JpegComponent;
for (int n = 0; n < resetInterval; n++) {
_decodeBlock(component, decodeFn, mcu);
mcu++;
}
} else {
for (int n = 0; n < resetInterval; n++) {
for (int i = 0; i < componentsLength; i++) {
component = components[i] as JpegComponent;
h = component.hSamples;
v = component.vSamples;
for (int j = 0; j < v; j++) {
for (int k = 0; k < h; k++) {
_decodeMcu(component, decodeFn, mcu, j, k);
}
}
}
mcu++;
}
}
// find marker
bitsCount = 0;
int m1 = input[0];
int m2 = input[1];
if (m1 == 0xff) {
if (m2 >= Jpeg.M_RST0 && m2 <= Jpeg.M_RST7) {
input.offset += 2;
} else {
break;
}
}
}
}
int _readBit() {
if (bitsCount > 0) {
bitsCount--;
return (bitsData >> bitsCount) & 1;
}
if (input.isEOS) {
return null;
}
bitsData = input.readByte();
if (bitsData == 0xff) {
int nextByte = input.readByte();
if (nextByte != 0) {
throw new ImageException('unexpected marker: ' +
((bitsData << 8) | nextByte).toRadixString(16));
}
}
bitsCount = 7;
return (bitsData >> 7) & 1;
}
int _decodeHuffman(dynamic tree) {
dynamic node = tree;
int bit;
while ((bit = _readBit()) != null) {
node = node[bit];
if (node is num) {
return node.toInt();
}
}
return null;
}
int _receive(int length) {
int n = 0;
while (length > 0) {
int bit = _readBit();
if (bit == null) {
return null;
}
n = ((n << 1) | bit);
length--;
}
return n;
}
int _receiveAndExtend(int length) {
if (length == 1) {
return _readBit() == 1 ? 1 : -1;
}
int n = _receive(length);
if (n >= (1 << (length - 1))) {
return n;
}
return n + (-1 << length) + 1;
}
void _decodeBaseline(JpegComponent component, List zz) {
int t = _decodeHuffman(component.huffmanTableDC);
int diff = t == 0 ? 0 : _receiveAndExtend(t);
component.pred += diff;
zz[0] = component.pred;
int k = 1;
while (k < 64) {
var rs = _decodeHuffman(component.huffmanTableAC);
int s = rs & 15;
int r = rs >> 4;
if (s == 0) {
if (r < 15) {
break;
}
k += 16;
continue;
}
k += r;
s = _receiveAndExtend(s);
int z = Jpeg.dctZigZag[k];
zz[z] = s;
k++;
}
}
void _decodeDCFirst(JpegComponent component, List zz) {
int t = _decodeHuffman(component.huffmanTableDC);
int diff = (t == 0) ? 0 : (_receiveAndExtend(t) << successive);
component.pred += diff;
zz[0] = component.pred;
}
void _decodeDCSuccessive(JpegComponent component, List zz) {
zz[0] = (zz[0] | (_readBit() << successive));
}
void _decodeACFirst(JpegComponent component, List zz) {
if (eobrun > 0) {
eobrun--;
return;
}
int k = spectralStart;
int e = spectralEnd;
while (k <= e) {
int rs = _decodeHuffman(component.huffmanTableAC);
int s = rs & 15;
int r = rs >> 4;
if (s == 0) {
if (r < 15) {
eobrun = (_receive(r) + (1 << r) - 1);
break;
}
k += 16;
continue;
}
k += r;
int z = Jpeg.dctZigZag[k];
zz[z] = (_receiveAndExtend(s) * (1 << successive));
k++;
}
}
void _decodeACSuccessive(JpegComponent component, dynamic zz) {
int k = spectralStart;
int e = spectralEnd;
int s = 0;
int r = 0;
while (k <= e) {
int z = Jpeg.dctZigZag[k];
switch (successiveACState) {
case 0: // initial state
int rs = _decodeHuffman(component.huffmanTableAC);
if (rs == null) continue;
s = rs & 15;
r = rs >> 4;
if (s == 0) {
if (r < 15) {
eobrun = (_receive(r) + (1 << r));
successiveACState = 4;
} else {
r = 16;
successiveACState = 1;
}
} else {
if (s != 1) {
throw new ImageException('invalid ACn encoding');
}
successiveACNextValue = _receiveAndExtend(s);
successiveACState = r != 0 ? 2 : 3;
}
continue;
case 1: // skipping r zero items
case 2:
if (zz[z] != 0) {
zz[z] += (_readBit() << successive);
} else {
r--;
if (r == 0) {
successiveACState = successiveACState == 2 ? 3 : 0;
}
}
break;
case 3: // set value for a zero item
if (zz[z] != 0) {
zz[z] += (_readBit() << successive);
} else {
zz[z] = (successiveACNextValue << successive);
successiveACState = 0;
}
break;
case 4: // eob
if (zz[z] != 0) {
zz[z] += (_readBit() << successive);
}
break;
}
k++;
}
if (successiveACState == 4) {
eobrun--;
if (eobrun == 0) {
successiveACState = 0;
}
}
}
void _decodeMcu(
JpegComponent component, dynamic decodeFn, int mcu, int row, int col) {
int mcuRow = (mcu ~/ mcusPerLine);
int mcuCol = mcu % mcusPerLine;
int blockRow = mcuRow * component.vSamples + row;
int blockCol = mcuCol * component.hSamples + col;
int numCols = component.blocks[blockRow].length as int;
if (blockRow >= component.blocks.length || blockCol >= numCols) {
return;
}
decodeFn(component, component.blocks[blockRow][blockCol]);
}
void _decodeBlock(JpegComponent component, dynamic decodeFn, int mcu) {
int blockRow = mcu ~/ component.blocksPerLine;
int blockCol = mcu % component.blocksPerLine;
decodeFn(component, component.blocks[blockRow][blockCol]);
}
}