import 'dart:typed_data'; | |
import '../../exif_data.dart'; | |
import '../../image_exception.dart'; | |
import '../../internal/bit_operators.dart'; | |
import '../../util/input_buffer.dart'; | |
import 'jpeg.dart'; | |
import 'jpeg_adobe.dart'; | |
import 'jpeg_component.dart'; | |
import 'jpeg_frame.dart'; | |
import 'jpeg_info.dart'; | |
import 'jpeg_jfif.dart'; | |
import 'jpeg_scan.dart'; | |
class JpegData { | |
InputBuffer input; | |
JpegJfif jfif; | |
JpegAdobe adobe; | |
JpegFrame frame; | |
int resetInterval; | |
final exif = ExifData(); | |
final quantizationTables = List<Int16List>(Jpeg.NUM_QUANT_TBLS); | |
final frames = List<JpegFrame>(); | |
final huffmanTablesAC = List<dynamic>(); | |
final huffmanTablesDC = List<dynamic>(); | |
final components = List<_ComponentData>(); | |
bool validate(List<int> bytes) { | |
input = InputBuffer(bytes, bigEndian: true); | |
int marker = _nextMarker(); | |
if (marker != Jpeg.M_SOI) { | |
return false; | |
} | |
bool hasSOF = false; | |
bool hasSOS = false; | |
marker = _nextMarker(); | |
while (marker != Jpeg.M_EOI && !input.isEOS) { | |
// EOI (End of image) | |
_skipBlock(); | |
switch (marker) { | |
case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT) | |
case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT) | |
case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT) | |
hasSOF = true; | |
break; | |
case Jpeg.M_SOS: // SOS (Start of Scan) | |
hasSOS = true; | |
break; | |
default: | |
} | |
marker = _nextMarker(); | |
} | |
return hasSOF && hasSOS; | |
} | |
JpegInfo readInfo(List<int> bytes) { | |
input = InputBuffer(bytes, bigEndian: true); | |
int marker = _nextMarker(); | |
if (marker != Jpeg.M_SOI) { | |
return null; | |
} | |
JpegInfo info = JpegInfo(); | |
bool hasSOF = false; | |
bool hasSOS = false; | |
marker = _nextMarker(); | |
while (marker != Jpeg.M_EOI && !input.isEOS) { | |
// EOI (End of image) | |
switch (marker) { | |
case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT) | |
case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT) | |
case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT) | |
hasSOF = true; | |
_readFrame(marker, _readBlock()); | |
break; | |
case Jpeg.M_SOS: // SOS (Start of Scan) | |
hasSOS = true; | |
_skipBlock(); | |
break; | |
default: | |
_skipBlock(); | |
break; | |
} | |
marker = _nextMarker(); | |
} | |
if (frame != null) { | |
info.width = frame.samplesPerLine; | |
info.height = frame.scanLines; | |
} | |
frame = null; | |
frames.clear(); | |
return (hasSOF && hasSOS) ? info : null; | |
} | |
void read(List<int> bytes) { | |
input = InputBuffer(bytes, bigEndian: true); | |
_read(); | |
if (frames.length != 1) { | |
throw new ImageException('Only single frame JPEGs supported'); | |
} | |
for (int i = 0; i < frame.componentsOrder.length; ++i) { | |
/*JpegComponent component =*/ frame.components[frame.componentsOrder[i]]; | |
} | |
for (int i = 0; i < frame.componentsOrder.length; ++i) { | |
JpegComponent component = frame.components[frame.componentsOrder[i]]; | |
components.add(_ComponentData( | |
component.hSamples, | |
frame.maxHSamples, | |
component.vSamples, | |
frame.maxVSamples, | |
_buildComponentData(frame, component))); | |
} | |
} | |
int get width => frame.samplesPerLine; | |
int get height => frame.scanLines; | |
Uint8List getData(int width, int height) { | |
_ComponentData component1; | |
_ComponentData component2; | |
_ComponentData component3; | |
_ComponentData component4; | |
Uint8List component1Line; | |
Uint8List component2Line; | |
Uint8List component3Line; | |
Uint8List component4Line; | |
int offset = 0; | |
int Y, Cb, Cr, K, C, M, Ye, R, G, B; | |
bool colorTransform = false; | |
int dataLength = width * height * components.length; | |
Uint8List data = Uint8List(dataLength); | |
switch (components.length) { | |
case 1: | |
component1 = components[0]; | |
var lines = component1.lines; | |
int hShift1 = component1.hScaleShift; | |
int vShift1 = component1.vScaleShift; | |
for (int y = 0; y < height; y++) { | |
int y1 = y >> vShift1; | |
component1Line = lines[y1]; | |
for (int x = 0; x < width; x++) { | |
int x1 = x >> hShift1; | |
Y = component1Line[x1]; | |
data[offset++] = Y; | |
} | |
} | |
break; | |
case 2: | |
// PDF might compress two component data in custom color-space | |
component1 = components[0]; | |
component2 = components[1]; | |
int hShift1 = component1.hScaleShift; | |
int vShift1 = component1.vScaleShift; | |
int hShift2 = component2.hScaleShift; | |
int vShift2 = component2.vScaleShift; | |
for (int y = 0; y < height; y++) { | |
int y1 = y >> vShift1; | |
int y2 = y >> vShift2; | |
component1Line = component1.lines[y1]; | |
component2Line = component2.lines[y2]; | |
for (int x = 0; x < width; x++) { | |
int x1 = x >> hShift1; | |
int x2 = x >> hShift2; | |
Y = component1Line[x1]; | |
data[offset++] = Y; | |
Y = component2Line[x2]; | |
data[offset++] = Y; | |
} | |
} | |
break; | |
case 3: | |
// The default transform for three components is true | |
colorTransform = true; | |
component1 = components[0]; | |
component2 = components[1]; | |
component3 = components[2]; | |
var lines1 = component1.lines; | |
var lines2 = component2.lines; | |
var lines3 = component3.lines; | |
int hShift1 = component1.hScaleShift; | |
int vShift1 = component1.vScaleShift; | |
int hShift2 = component2.hScaleShift; | |
int vShift2 = component2.vScaleShift; | |
int hShift3 = component3.hScaleShift; | |
int vShift3 = component3.vScaleShift; | |
for (int y = 0; y < height; y++) { | |
int y1 = y >> vShift1; | |
int y2 = y >> vShift2; | |
int y3 = y >> vShift3; | |
component1Line = lines1[y1]; | |
component2Line = lines2[y2]; | |
component3Line = lines3[y3]; | |
for (int x = 0; x < width; x++) { | |
int x1 = x >> hShift1; | |
int x2 = x >> hShift2; | |
int x3 = x >> hShift3; | |
if (!colorTransform) { | |
data[offset++] = component1Line[x1]; | |
data[offset++] = component2Line[x2]; | |
data[offset++] = component3Line[x3]; | |
} else { | |
Y = component1Line[x1] << 8; | |
Cb = component2Line[x2] - 128; | |
Cr = component3Line[x3] - 128; | |
R = shiftR((Y + 359 * Cr + 128), 8); | |
G = shiftR((Y - 88 * Cb - 183 * Cr + 128), 8); | |
B = shiftR((Y + 454 * Cb + 128), 8); | |
data[offset++] = _clamp8(R); | |
data[offset++] = _clamp8(G); | |
data[offset++] = _clamp8(B); | |
} | |
} | |
} | |
break; | |
case 4: | |
if (adobe == null) { | |
throw new ImageException('Unsupported color mode (4 components)'); | |
} | |
// The default transform for four components is false | |
colorTransform = false; | |
// The adobe transform marker overrides any previous setting | |
if (adobe.transformCode != 0) { | |
colorTransform = true; | |
} | |
component1 = components[0]; | |
component2 = components[1]; | |
component3 = components[2]; | |
component4 = components[3]; | |
var lines1 = component1.lines; | |
var lines2 = component2.lines; | |
var lines3 = component3.lines; | |
var lines4 = component4.lines; | |
int hShift1 = component1.hScaleShift; | |
int vShift1 = component1.vScaleShift; | |
int hShift2 = component2.hScaleShift; | |
int vShift2 = component2.vScaleShift; | |
int hShift3 = component3.hScaleShift; | |
int vShift3 = component3.vScaleShift; | |
int hShift4 = component4.hScaleShift; | |
int vShift4 = component4.vScaleShift; | |
for (int y = 0; y < height; y++) { | |
int y1 = y >> vShift1; | |
int y2 = y >> vShift2; | |
int y3 = y >> vShift3; | |
int y4 = y >> vShift4; | |
component1Line = lines1[y1]; | |
component2Line = lines2[y2]; | |
component3Line = lines3[y3]; | |
component4Line = lines4[y4]; | |
for (int x = 0; x < width; x++) { | |
int x1 = x >> hShift1; | |
int x2 = x >> hShift2; | |
int x3 = x >> hShift3; | |
int x4 = x >> hShift4; | |
if (!colorTransform) { | |
C = component1Line[x1]; | |
M = component2Line[x2]; | |
Ye = component3Line[x3]; | |
K = component4Line[x4]; | |
} else { | |
Y = component1Line[x1]; | |
Cb = component2Line[x2]; | |
Cr = component3Line[x3]; | |
K = component4Line[x4]; | |
C = 255 - _clamp8((Y + 1.402 * (Cr - 128)).toInt()); | |
M = 255 - | |
_clamp8((Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)) | |
.toInt()); | |
Ye = 255 - _clamp8((Y + 1.772 * (Cb - 128)).toInt()); | |
} | |
data[offset++] = C; | |
data[offset++] = M; | |
data[offset++] = Ye; | |
data[offset++] = K; | |
} | |
} | |
break; | |
default: | |
throw new ImageException('Unsupported color mode'); | |
} | |
return data; | |
} | |
void _read() { | |
int marker = _nextMarker(); | |
if (marker != Jpeg.M_SOI) { | |
// SOI (Start of Image) | |
throw new ImageException('Start Of Image marker not found.'); | |
} | |
marker = _nextMarker(); | |
while (marker != Jpeg.M_EOI && !input.isEOS) { | |
InputBuffer block = _readBlock(); | |
switch (marker) { | |
case Jpeg.M_APP0: | |
case Jpeg.M_APP1: | |
case Jpeg.M_APP2: | |
case Jpeg.M_APP3: | |
case Jpeg.M_APP4: | |
case Jpeg.M_APP5: | |
case Jpeg.M_APP6: | |
case Jpeg.M_APP7: | |
case Jpeg.M_APP8: | |
case Jpeg.M_APP9: | |
case Jpeg.M_APP10: | |
case Jpeg.M_APP11: | |
case Jpeg.M_APP12: | |
case Jpeg.M_APP13: | |
case Jpeg.M_APP14: | |
case Jpeg.M_APP15: | |
case Jpeg.M_COM: | |
_readAppData(marker, block); | |
break; | |
case Jpeg.M_DQT: // DQT (Define Quantization Tables) | |
_readDQT(block); | |
break; | |
case Jpeg.M_SOF0: // SOF0 (Start of Frame, Baseline DCT) | |
case Jpeg.M_SOF1: // SOF1 (Start of Frame, Extended DCT) | |
case Jpeg.M_SOF2: // SOF2 (Start of Frame, Progressive DCT) | |
_readFrame(marker, block); | |
break; | |
case Jpeg.M_SOF3: | |
case Jpeg.M_SOF5: | |
case Jpeg.M_SOF6: | |
case Jpeg.M_SOF7: | |
case Jpeg.M_JPG: | |
case Jpeg.M_SOF9: | |
case Jpeg.M_SOF10: | |
case Jpeg.M_SOF11: | |
case Jpeg.M_SOF13: | |
case Jpeg.M_SOF14: | |
case Jpeg.M_SOF15: | |
throw new ImageException( | |
'Unhandled frame type ${marker.toRadixString(16)}'); | |
case Jpeg.M_DHT: // DHT (Define Huffman Tables) | |
_readDHT(block); | |
break; | |
case Jpeg.M_DRI: // DRI (Define Restart Interval) | |
_readDRI(block); | |
break; | |
case Jpeg.M_SOS: // SOS (Start of Scan) | |
_readSOS(block); | |
break; | |
case 0xff: // Fill bytes | |
if (input[0] != 0xff) { | |
input.offset--; | |
} | |
break; | |
default: | |
if (input[-3] == 0xff && input[-2] >= 0xc0 && input[-2] <= 0xfe) { | |
// could be incorrect encoding -- last 0xFF byte of the previous | |
// block was eaten by the encoder | |
input.offset -= 3; | |
break; | |
} | |
if (marker != 0) { | |
throw new ImageException( | |
'Unknown JPEG marker ' + marker.toRadixString(16)); | |
} | |
break; | |
} | |
marker = _nextMarker(); | |
} | |
} | |
void _skipBlock() { | |
int length = input.readUint16(); | |
if (length < 2) { | |
throw new ImageException('Invalid Block'); | |
} | |
input.offset += length - 2; | |
} | |
InputBuffer _readBlock() { | |
int length = input.readUint16(); | |
if (length < 2) { | |
throw new ImageException('Invalid Block'); | |
} | |
return input.readBytes(length - 2); | |
} | |
int _nextMarker() { | |
int c = 0; | |
if (input.isEOS) { | |
return c; | |
} | |
do { | |
do { | |
c = input.readByte(); | |
} while (c != 0xff && !input.isEOS); | |
if (input.isEOS) { | |
return c; | |
} | |
do { | |
c = input.readByte(); | |
} while (c == 0xff && !input.isEOS); | |
} while (c == 0 && !input.isEOS); | |
return c; | |
} | |
num _readExifValue(InputBuffer block, int format) { | |
const FMT_BYTE = 1; | |
//const FMT_STRING = 2; | |
const FMT_USHORT = 3; | |
const FMT_ULONG = 4; | |
const FMT_URATIONAL = 5; | |
const FMT_SBYTE = 6; | |
//const FMT_UNDEFINED = 7; | |
const FMT_SSHORT = 8; | |
const FMT_SLONG = 9; | |
const FMT_SRATIONAL = 10; | |
const FMT_SINGLE = 11; | |
const FMT_DOUBLE = 12; | |
switch (format) { | |
case FMT_SBYTE: | |
return block.readInt8(); | |
case FMT_BYTE: | |
return block.readByte(); | |
case FMT_USHORT: | |
return block.readUint16(); | |
case FMT_ULONG: | |
return block.readUint32(); | |
case FMT_URATIONAL: | |
case FMT_SRATIONAL: | |
{ | |
int num = block.readInt32(); | |
int den = block.readInt32(); | |
if (den == 0) { | |
return 0.0; | |
} | |
return num / den; | |
} | |
case FMT_SSHORT: | |
return block.readInt16(); | |
case FMT_SLONG: | |
return block.readInt32(); | |
// Not sure if this is correct (never seen float used in Exif format) | |
case FMT_SINGLE: | |
return block.readFloat32(); | |
case FMT_DOUBLE: | |
return block.readFloat64(); | |
default: | |
return 0; | |
} | |
} | |
void _readExifDir(InputBuffer block, [int nesting = 0]) { | |
if (nesting > 4) { | |
return; // Maximum Exif directory nesting exceeded (corrupt Exif header) | |
} | |
int numDirEntries = block.readUint16(); | |
const TAG_ORIENTATION = 0x0112; | |
const TAG_INTEROP_OFFSET = 0xA005; | |
const TAG_EXIF_OFFSET = 0x8769; | |
const maxFormats = 12; | |
const bytesPerFormat = const [0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8]; | |
for (int di = 0; di < numDirEntries; ++di) { | |
int tag = block.readUint16(); | |
int format = block.readUint16(); | |
int components = block.readUint32(); | |
if (format - 1 >= maxFormats) { | |
continue; | |
} | |
// too many components | |
if (components > 0x10000) { | |
continue; | |
} | |
int byteCount = bytesPerFormat[format]; | |
// If its bigger than 4 bytes, the dir entry contains an offset. | |
if (byteCount > 4) { | |
int offset = block.readUint32(); | |
if (offset + byteCount > block.length) { | |
continue; // Bogus pointer offset and / or bytecount value | |
} | |
//ValuePtr = OffsetBase+OffsetVal; | |
} | |
switch (tag) { | |
case TAG_ORIENTATION: | |
{ | |
num orientation = _readExifValue(block, format); | |
exif.orientation = orientation.toInt(); | |
} | |
break; | |
case TAG_EXIF_OFFSET: | |
case TAG_INTEROP_OFFSET: | |
break; | |
default: | |
// skip unknown tags | |
break; | |
} | |
} | |
} | |
void _readExifData(InputBuffer block) { | |
if (exif.rawData == null) { | |
exif.rawData = List<Uint8List>(); | |
} | |
Uint8List rawData = Uint8List.fromList(block.toUint8List()); | |
exif.rawData.add(rawData); | |
const EXIF_TAG = 0x45786966; // Exif\0\0 | |
if (block.readUint32() != EXIF_TAG) { | |
return; | |
} | |
if (block.readUint16() != 0) { | |
return; | |
} | |
bool saveEndian = block.bigEndian; | |
// Exif Directory | |
String alignment = block.readString(2); | |
if (alignment == 'II') { | |
// Exif is in Intel order | |
block.bigEndian = false; | |
} else if (alignment == 'MM') { | |
// Exif section in Motorola order | |
block.bigEndian = true; | |
} else { | |
return; | |
} | |
block.skip(2); | |
int offset = block.readUint32(); | |
if (offset < 8 || offset > 16) { | |
if (offset > block.length - 16) { | |
// invalid offset for first Exif IFD value ; | |
block.bigEndian = saveEndian; | |
return; | |
} | |
} | |
if (offset > 8) { | |
block.skip(offset - 8); | |
} | |
_readExifDir(block); | |
block.bigEndian = saveEndian; | |
} | |
void _readAppData(int marker, InputBuffer block) { | |
InputBuffer appData = block; | |
if (marker == Jpeg.M_APP0) { | |
// 'JFIF\0' | |
if (appData[0] == 0x4A && | |
appData[1] == 0x46 && | |
appData[2] == 0x49 && | |
appData[3] == 0x46 && | |
appData[4] == 0) { | |
jfif = JpegJfif(); | |
jfif.majorVersion = appData[5]; | |
jfif.minorVersion = appData[6]; | |
jfif.densityUnits = appData[7]; | |
jfif.xDensity = shiftL(appData[8], 8) | appData[9]; | |
jfif.yDensity = shiftL(appData[10], 8) | appData[11]; | |
jfif.thumbWidth = appData[12]; | |
jfif.thumbHeight = appData[13]; | |
int thumbSize = 3 * jfif.thumbWidth * jfif.thumbHeight; | |
jfif.thumbData = appData.subset(14 + thumbSize, offset: 14); | |
} | |
} else if (marker == Jpeg.M_APP1) { | |
// 'EXIF\0' | |
_readExifData(appData); | |
} else if (marker == Jpeg.M_APP14) { | |
// 'Adobe\0' | |
if (appData[0] == 0x41 && | |
appData[1] == 0x64 && | |
appData[2] == 0x6F && | |
appData[3] == 0x62 && | |
appData[4] == 0x65 && | |
appData[5] == 0) { | |
adobe = JpegAdobe(); | |
adobe.version = appData[6]; | |
adobe.flags0 = shiftL(appData[7], 8) | appData[8]; | |
adobe.flags1 = shiftL(appData[9], 8) | appData[10]; | |
adobe.transformCode = appData[11]; | |
} | |
} else { | |
//print("!!!! UNHANDLED APP TAG 0x${marker.toRadixString(16)}"); | |
} | |
} | |
void _readDQT(InputBuffer block) { | |
while (!block.isEOS) { | |
int n = block.readByte(); | |
int prec = shiftR(n, 4); | |
n &= 0x0F; | |
if (n >= Jpeg.NUM_QUANT_TBLS) { | |
throw new ImageException('Invalid number of quantization tables'); | |
} | |
if (quantizationTables[n] == null) { | |
quantizationTables[n] = Int16List(64); | |
} | |
Int16List tableData = quantizationTables[n]; | |
for (int i = 0; i < Jpeg.DCTSIZE2; i++) { | |
int tmp; | |
if (prec != 0) { | |
tmp = block.readUint16(); | |
} else { | |
tmp = block.readByte(); | |
} | |
tableData[Jpeg.dctZigZag[i]] = tmp; | |
} | |
} | |
if (!block.isEOS) { | |
throw new ImageException('Bad length for DQT block'); | |
} | |
} | |
void _readFrame(int marker, InputBuffer block) { | |
if (frame != null) { | |
throw new ImageException('Duplicate JPG frame data found.'); | |
} | |
frame = JpegFrame(); | |
frame.extended = (marker == Jpeg.M_SOF1); | |
frame.progressive = (marker == Jpeg.M_SOF2); | |
frame.precision = block.readByte(); | |
frame.scanLines = block.readUint16(); | |
frame.samplesPerLine = block.readUint16(); | |
int numComponents = block.readByte(); | |
for (int i = 0; i < numComponents; i++) { | |
int componentId = block.readByte(); | |
int x = block.readByte(); | |
int h = shiftR(x, 4) & 15; | |
int v = x & 15; | |
int qId = block.readByte(); | |
frame.componentsOrder.add(componentId); | |
frame.components[componentId] = | |
new JpegComponent(h, v, quantizationTables, qId); | |
} | |
frame.prepare(); | |
frames.add(frame); | |
} | |
void _readDHT(InputBuffer block) { | |
while (!block.isEOS) { | |
int index = block.readByte(); | |
Uint8List bits = Uint8List(16); | |
int count = 0; | |
for (int j = 0; j < 16; j++) { | |
bits[j] = block.readByte(); | |
count += bits[j]; | |
} | |
Uint8List huffmanValues = Uint8List(count); | |
for (int j = 0; j < count; j++) { | |
huffmanValues[j] = block.readByte(); | |
} | |
List ht; | |
if (index & 0x10 != 0) { | |
// AC table definition | |
index -= 0x10; | |
ht = huffmanTablesAC; | |
} else { | |
// DC table definition | |
ht = huffmanTablesDC; | |
} | |
if (ht.length <= index) { | |
ht.length = index + 1; | |
} | |
ht[index] = _buildHuffmanTable(bits, huffmanValues); | |
} | |
} | |
void _readDRI(InputBuffer block) { | |
resetInterval = block.readUint16(); | |
} | |
void _readSOS(InputBuffer block) { | |
int n = block.readByte(); | |
if (n < 1 || n > Jpeg.MAX_COMPS_IN_SCAN) { | |
throw new ImageException('Invalid SOS block'); | |
} | |
final components = List<dynamic>(n); | |
for (int i = 0; i < n; i++) { | |
int id = block.readByte(); | |
int c = block.readByte(); | |
if (!frame.components.containsKey(id)) { | |
throw new ImageException('Invalid Component in SOS block'); | |
} | |
JpegComponent component = frame.components[id]; | |
components[i] = component; | |
int dc_tbl_no = shiftR(c, 4) & 15; | |
int ac_tbl_no = c & 15; | |
if (dc_tbl_no < huffmanTablesDC.length) { | |
component.huffmanTableDC = huffmanTablesDC[dc_tbl_no] as List; | |
} | |
if (ac_tbl_no < huffmanTablesAC.length) { | |
component.huffmanTableAC = huffmanTablesAC[ac_tbl_no] as List; | |
} | |
} | |
int spectralStart = block.readByte(); | |
int spectralEnd = block.readByte(); | |
int successiveApproximation = block.readByte(); | |
int Ah = shiftR(successiveApproximation, 4) & 15; | |
int Al = successiveApproximation & 15; | |
new JpegScan(input, frame, components, resetInterval, spectralStart, | |
spectralEnd, Ah, Al) | |
.decode(); | |
} | |
List _buildHuffmanTable(Uint8List codeLengths, Uint8List values) { | |
int k = 0; | |
final code = List<dynamic>(); | |
int length = 16; | |
while (length > 0 && (codeLengths[length - 1] == 0)) { | |
length--; | |
} | |
code.add(new _JpegHuffman()); | |
_JpegHuffman p = code[0] as _JpegHuffman; | |
_JpegHuffman q; | |
for (int i = 0; i < length; i++) { | |
for (int j = 0; j < codeLengths[i]; j++) { | |
p = code.removeLast() as _JpegHuffman; | |
if (p.children.length <= p.index) { | |
p.children.length = p.index + 1; | |
} | |
p.children[p.index] = values[k]; | |
while (p.index > 0) { | |
p = code.removeLast() as _JpegHuffman; | |
} | |
p.index++; | |
code.add(p); | |
while (code.length <= i) { | |
q = _JpegHuffman(); | |
code.add(q); | |
if (p.children.length <= p.index) { | |
p.children.length = p.index + 1; | |
} | |
p.children[p.index] = q.children; | |
p = q; | |
} | |
k++; | |
} | |
if ((i + 1) < length) { | |
// p here points to last code | |
q = _JpegHuffman(); | |
code.add(q); | |
if (p.children.length <= p.index) { | |
p.children.length = p.index + 1; | |
} | |
p.children[p.index] = q.children; | |
p = q; | |
} | |
} | |
return code[0].children as List; | |
} | |
List<Uint8List> _buildComponentData( | |
JpegFrame frame, JpegComponent component) { | |
final int blocksPerLine = component.blocksPerLine; | |
final int blocksPerColumn = component.blocksPerColumn; | |
int samplesPerLine = shiftL(blocksPerLine, 3); | |
Int32List R = Int32List(64); | |
Uint8List r = Uint8List(64); | |
List<Uint8List> lines = List(blocksPerColumn * 8); | |
int l = 0; | |
for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) { | |
int scanLine = shiftL(blockRow, 3); | |
for (int i = 0; i < 8; i++) { | |
lines[l++] = Uint8List(samplesPerLine); | |
} | |
for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) { | |
_quantizeAndInverse(component.quantizationTable, | |
component.blocks[blockRow][blockCol] as Int32List, r, R); | |
int offset = 0; | |
int sample = shiftL(blockCol, 3); | |
for (int j = 0; j < 8; j++) { | |
Uint8List line = lines[scanLine + j]; | |
for (int i = 0; i < 8; i++) { | |
line[sample + i] = r[offset++]; | |
} | |
} | |
} | |
} | |
return lines; | |
} | |
static int toFix(double val) { | |
const int FIXED_POINT = 20; | |
const int ONE = 1 << FIXED_POINT; | |
return (val * ONE).toInt() & 0xffffffff; | |
} | |
static int _clamp8(int i) => i < 0 ? 0 : i > 255 ? 255 : i; | |
static Uint8List dctClip; | |
/// Quantize the coefficients and apply IDCT. | |
/// | |
/// A port of poppler's IDCT method which in turn is taken from: | |
/// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, | |
/// "Practical Fast 1-D DCT Algorithms with 11 Multiplications", | |
/// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991. | |
void _quantizeAndInverse(Int16List quantizationTable, Int32List coefBlock, | |
Uint8List dataOut, Int32List dataIn) { | |
Int32List p = dataIn; | |
const int dctClipOffset = 256; | |
const int dctClipLength = 768; | |
if (dctClip == null) { | |
dctClip = Uint8List(dctClipLength); | |
int i; | |
for (i = -256; i < 0; ++i) { | |
dctClip[dctClipOffset + i] = 0; | |
} | |
for (i = 0; i < 256; ++i) { | |
dctClip[dctClipOffset + i] = i; | |
} | |
for (i = 256; i < 512; ++i) { | |
dctClip[dctClipOffset + i] = 255; | |
} | |
} | |
// IDCT constants (20.12 fixed point format) | |
const int COS_1 = 4017; // cos(pi/16)*4096 | |
const int SIN_1 = 799; // sin(pi/16)*4096 | |
const int COS_3 = 3406; // cos(3*pi/16)*4096 | |
const int SIN_3 = 2276; // sin(3*pi/16)*4096 | |
const int COS_6 = 1567; // cos(6*pi/16)*4096 | |
const int SIN_6 = 3784; // sin(6*pi/16)*4096 | |
const int SQRT_2 = 5793; // sqrt(2)*4096 | |
const int SQRT_1D2 = 2896; // sqrt(2) / 2 | |
// de-quantize | |
for (int i = 0; i < 64; i++) { | |
p[i] = (coefBlock[i] * quantizationTable[i]); | |
} | |
// inverse DCT on rows | |
int row = 0; | |
for (int i = 0; i < 8; ++i, row += 8) { | |
// check for all-zero AC coefficients | |
if (p[1 + row] == 0 && | |
p[2 + row] == 0 && | |
p[3 + row] == 0 && | |
p[4 + row] == 0 && | |
p[5 + row] == 0 && | |
p[6 + row] == 0 && | |
p[7 + row] == 0) { | |
int t = shiftR((SQRT_2 * p[0 + row] + 512), 10); | |
p[row + 0] = t; | |
p[row + 1] = t; | |
p[row + 2] = t; | |
p[row + 3] = t; | |
p[row + 4] = t; | |
p[row + 5] = t; | |
p[row + 6] = t; | |
p[row + 7] = t; | |
continue; | |
} | |
// stage 4 | |
int v0 = shiftR((SQRT_2 * p[0 + row] + 128), 8); | |
int v1 = shiftR((SQRT_2 * p[4 + row] + 128), 8); | |
int v2 = p[2 + row]; | |
int v3 = p[6 + row]; | |
int v4 = shiftR((SQRT_1D2 * (p[1 + row] - p[7 + row]) + 128), 8); | |
int v7 = shiftR((SQRT_1D2 * (p[1 + row] + p[7 + row]) + 128), 8); | |
int v5 = shiftL(p[3 + row], 4); | |
int v6 = shiftL(p[5 + row], 4); | |
// stage 3 | |
int t = shiftR((v0 - v1 + 1), 1); | |
v0 = shiftR((v0 + v1 + 1), 1); | |
v1 = t; | |
t = shiftR((v2 * SIN_6 + v3 * COS_6 + 128), 8); | |
v2 = shiftR((v2 * COS_6 - v3 * SIN_6 + 128), 8); | |
v3 = t; | |
t = shiftR((v4 - v6 + 1), 1); | |
v4 = shiftR((v4 + v6 + 1), 1); | |
v6 = t; | |
t = shiftR((v7 + v5 + 1), 1); | |
v5 = shiftR((v7 - v5 + 1), 1); | |
v7 = t; | |
// stage 2 | |
t = shiftR((v0 - v3 + 1), 1); | |
v0 = shiftR((v0 + v3 + 1), 1); | |
v3 = t; | |
t = shiftR((v1 - v2 + 1), 1); | |
v1 = shiftR((v1 + v2 + 1), 1); | |
v2 = t; | |
t = shiftR((v4 * SIN_3 + v7 * COS_3 + 2048), 12); | |
v4 = shiftR((v4 * COS_3 - v7 * SIN_3 + 2048), 12); | |
v7 = t; | |
t = shiftR((v5 * SIN_1 + v6 * COS_1 + 2048), 12); | |
v5 = shiftR((v5 * COS_1 - v6 * SIN_1 + 2048), 12); | |
v6 = t; | |
// stage 1 | |
p[0 + row] = (v0 + v7); | |
p[7 + row] = (v0 - v7); | |
p[1 + row] = (v1 + v6); | |
p[6 + row] = (v1 - v6); | |
p[2 + row] = (v2 + v5); | |
p[5 + row] = (v2 - v5); | |
p[3 + row] = (v3 + v4); | |
p[4 + row] = (v3 - v4); | |
} | |
// inverse DCT on columns | |
for (int i = 0; i < 8; ++i) { | |
int col = i; | |
// check for all-zero AC coefficients | |
if (p[1 * 8 + col] == 0 && | |
p[2 * 8 + col] == 0 && | |
p[3 * 8 + col] == 0 && | |
p[4 * 8 + col] == 0 && | |
p[5 * 8 + col] == 0 && | |
p[6 * 8 + col] == 0 && | |
p[7 * 8 + col] == 0) { | |
int t = shiftR((SQRT_2 * dataIn[i] + 8192), 14); | |
p[0 * 8 + col] = t; | |
p[1 * 8 + col] = t; | |
p[2 * 8 + col] = t; | |
p[3 * 8 + col] = t; | |
p[4 * 8 + col] = t; | |
p[5 * 8 + col] = t; | |
p[6 * 8 + col] = t; | |
p[7 * 8 + col] = t; | |
continue; | |
} | |
// stage 4 | |
int v0 = shiftR((SQRT_2 * p[0 * 8 + col] + 2048), 12); | |
int v1 = shiftR((SQRT_2 * p[4 * 8 + col] + 2048), 12); | |
int v2 = p[2 * 8 + col]; | |
int v3 = p[6 * 8 + col]; | |
int v4 = | |
shiftR((SQRT_1D2 * (p[1 * 8 + col] - p[7 * 8 + col]) + 2048), 12); | |
int v7 = | |
shiftR((SQRT_1D2 * (p[1 * 8 + col] + p[7 * 8 + col]) + 2048), 12); | |
int v5 = p[3 * 8 + col]; | |
int v6 = p[5 * 8 + col]; | |
// stage 3 | |
int t = shiftR((v0 - v1 + 1), 1); | |
v0 = shiftR((v0 + v1 + 1), 1); | |
v1 = t; | |
t = shiftR((v2 * SIN_6 + v3 * COS_6 + 2048), 12); | |
v2 = shiftR((v2 * COS_6 - v3 * SIN_6 + 2048), 12); | |
v3 = t; | |
t = shiftR((v4 - v6 + 1), 1); | |
v4 = shiftR((v4 + v6 + 1), 1); | |
v6 = t; | |
t = shiftR((v7 + v5 + 1), 1); | |
v5 = shiftR((v7 - v5 + 1), 1); | |
v7 = t; | |
// stage 2 | |
t = shiftR((v0 - v3 + 1), 1); | |
v0 = shiftR((v0 + v3 + 1), 1); | |
v3 = t; | |
t = shiftR((v1 - v2 + 1), 1); | |
v1 = shiftR((v1 + v2 + 1), 1); | |
v2 = t; | |
t = shiftR((v4 * SIN_3 + v7 * COS_3 + 2048), 12); | |
v4 = shiftR((v4 * COS_3 - v7 * SIN_3 + 2048), 12); | |
v7 = t; | |
t = shiftR((v5 * SIN_1 + v6 * COS_1 + 2048), 12); | |
v5 = shiftR((v5 * COS_1 - v6 * SIN_1 + 2048), 12); | |
v6 = t; | |
// stage 1 | |
p[0 * 8 + col] = (v0 + v7); | |
p[7 * 8 + col] = (v0 - v7); | |
p[1 * 8 + col] = (v1 + v6); | |
p[6 * 8 + col] = (v1 - v6); | |
p[2 * 8 + col] = (v2 + v5); | |
p[5 * 8 + col] = (v2 - v5); | |
p[3 * 8 + col] = (v3 + v4); | |
p[4 * 8 + col] = (v3 - v4); | |
} | |
// convert to 8-bit integers | |
for (int i = 0; i < 64; ++i) { | |
dataOut[i] = dctClip[(dctClipOffset + 128 + shiftR((p[i] + 8), 4))]; | |
} | |
} | |
static const CRR = const [ | |
-179, | |
-178, | |
-177, | |
-175, | |
-174, | |
-172, | |
-171, | |
-170, | |
-168, | |
-167, | |
-165, | |
-164, | |
-163, | |
-161, | |
-160, | |
-158, | |
-157, | |
-156, | |
-154, | |
-153, | |
-151, | |
-150, | |
-149, | |
-147, | |
-146, | |
-144, | |
-143, | |
-142, | |
-140, | |
-139, | |
-137, | |
-136, | |
-135, | |
-133, | |
-132, | |
-130, | |
-129, | |
-128, | |
-126, | |
-125, | |
-123, | |
-122, | |
-121, | |
-119, | |
-118, | |
-116, | |
-115, | |
-114, | |
-112, | |
-111, | |
-109, | |
-108, | |
-107, | |
-105, | |
-104, | |
-102, | |
-101, | |
-100, | |
-98, | |
-97, | |
-95, | |
-94, | |
-93, | |
-91, | |
-90, | |
-88, | |
-87, | |
-86, | |
-84, | |
-83, | |
-81, | |
-80, | |
-79, | |
-77, | |
-76, | |
-74, | |
-73, | |
-72, | |
-70, | |
-69, | |
-67, | |
-66, | |
-64, | |
-63, | |
-62, | |
-60, | |
-59, | |
-57, | |
-56, | |
-55, | |
-53, | |
-52, | |
-50, | |
-49, | |
-48, | |
-46, | |
-45, | |
-43, | |
-42, | |
-41, | |
-39, | |
-38, | |
-36, | |
-35, | |
-34, | |
-32, | |
-31, | |
-29, | |
-28, | |
-27, | |
-25, | |
-24, | |
-22, | |
-21, | |
-20, | |
-18, | |
-17, | |
-15, | |
-14, | |
-13, | |
-11, | |
-10, | |
-8, | |
-7, | |
-6, | |
-4, | |
-3, | |
-1, | |
0, | |
1, | |
3, | |
4, | |
6, | |
7, | |
8, | |
10, | |
11, | |
13, | |
14, | |
15, | |
17, | |
18, | |
20, | |
21, | |
22, | |
24, | |
25, | |
27, | |
28, | |
29, | |
31, | |
32, | |
34, | |
35, | |
36, | |
38, | |
39, | |
41, | |
42, | |
43, | |
45, | |
46, | |
48, | |
49, | |
50, | |
52, | |
53, | |
55, | |
56, | |
57, | |
59, | |
60, | |
62, | |
63, | |
64, | |
66, | |
67, | |
69, | |
70, | |
72, | |
73, | |
74, | |
76, | |
77, | |
79, | |
80, | |
81, | |
83, | |
84, | |
86, | |
87, | |
88, | |
90, | |
91, | |
93, | |
94, | |
95, | |
97, | |
98, | |
100, | |
101, | |
102, | |
104, | |
105, | |
107, | |
108, | |
109, | |
111, | |
112, | |
114, | |
115, | |
116, | |
118, | |
119, | |
121, | |
122, | |
123, | |
125, | |
126, | |
128, | |
129, | |
130, | |
132, | |
133, | |
135, | |
136, | |
137, | |
139, | |
140, | |
142, | |
143, | |
144, | |
146, | |
147, | |
149, | |
150, | |
151, | |
153, | |
154, | |
156, | |
157, | |
158, | |
160, | |
161, | |
163, | |
164, | |
165, | |
167, | |
168, | |
170, | |
171, | |
172, | |
174, | |
175, | |
177, | |
178 | |
]; | |
static const CRG = const [ | |
5990656, | |
5943854, | |
5897052, | |
5850250, | |
5803448, | |
5756646, | |
5709844, | |
5663042, | |
5616240, | |
5569438, | |
5522636, | |
5475834, | |
5429032, | |
5382230, | |
5335428, | |
5288626, | |
5241824, | |
5195022, | |
5148220, | |
5101418, | |
5054616, | |
5007814, | |
4961012, | |
4914210, | |
4867408, | |
4820606, | |
4773804, | |
4727002, | |
4680200, | |
4633398, | |
4586596, | |
4539794, | |
4492992, | |
4446190, | |
4399388, | |
4352586, | |
4305784, | |
4258982, | |
4212180, | |
4165378, | |
4118576, | |
4071774, | |
4024972, | |
3978170, | |
3931368, | |
3884566, | |
3837764, | |
3790962, | |
3744160, | |
3697358, | |
3650556, | |
3603754, | |
3556952, | |
3510150, | |
3463348, | |
3416546, | |
3369744, | |
3322942, | |
3276140, | |
3229338, | |
3182536, | |
3135734, | |
3088932, | |
3042130, | |
2995328, | |
2948526, | |
2901724, | |
2854922, | |
2808120, | |
2761318, | |
2714516, | |
2667714, | |
2620912, | |
2574110, | |
2527308, | |
2480506, | |
2433704, | |
2386902, | |
2340100, | |
2293298, | |
2246496, | |
2199694, | |
2152892, | |
2106090, | |
2059288, | |
2012486, | |
1965684, | |
1918882, | |
1872080, | |
1825278, | |
1778476, | |
1731674, | |
1684872, | |
1638070, | |
1591268, | |
1544466, | |
1497664, | |
1450862, | |
1404060, | |
1357258, | |
1310456, | |
1263654, | |
1216852, | |
1170050, | |
1123248, | |
1076446, | |
1029644, | |
982842, | |
936040, | |
889238, | |
842436, | |
795634, | |
748832, | |
702030, | |
655228, | |
608426, | |
561624, | |
514822, | |
468020, | |
421218, | |
374416, | |
327614, | |
280812, | |
234010, | |
187208, | |
140406, | |
93604, | |
46802, | |
0, | |
-46802, | |
-93604, | |
-140406, | |
-187208, | |
-234010, | |
-280812, | |
-327614, | |
-374416, | |
-421218, | |
-468020, | |
-514822, | |
-561624, | |
-608426, | |
-655228, | |
-702030, | |
-748832, | |
-795634, | |
-842436, | |
-889238, | |
-936040, | |
-982842, | |
-1029644, | |
-1076446, | |
-1123248, | |
-1170050, | |
-1216852, | |
-1263654, | |
-1310456, | |
-1357258, | |
-1404060, | |
-1450862, | |
-1497664, | |
-1544466, | |
-1591268, | |
-1638070, | |
-1684872, | |
-1731674, | |
-1778476, | |
-1825278, | |
-1872080, | |
-1918882, | |
-1965684, | |
-2012486, | |
-2059288, | |
-2106090, | |
-2152892, | |
-2199694, | |
-2246496, | |
-2293298, | |
-2340100, | |
-2386902, | |
-2433704, | |
-2480506, | |
-2527308, | |
-2574110, | |
-2620912, | |
-2667714, | |
-2714516, | |
-2761318, | |
-2808120, | |
-2854922, | |
-2901724, | |
-2948526, | |
-2995328, | |
-3042130, | |
-3088932, | |
-3135734, | |
-3182536, | |
-3229338, | |
-3276140, | |
-3322942, | |
-3369744, | |
-3416546, | |
-3463348, | |
-3510150, | |
-3556952, | |
-3603754, | |
-3650556, | |
-3697358, | |
-3744160, | |
-3790962, | |
-3837764, | |
-3884566, | |
-3931368, | |
-3978170, | |
-4024972, | |
-4071774, | |
-4118576, | |
-4165378, | |
-4212180, | |
-4258982, | |
-4305784, | |
-4352586, | |
-4399388, | |
-4446190, | |
-4492992, | |
-4539794, | |
-4586596, | |
-4633398, | |
-4680200, | |
-4727002, | |
-4773804, | |
-4820606, | |
-4867408, | |
-4914210, | |
-4961012, | |
-5007814, | |
-5054616, | |
-5101418, | |
-5148220, | |
-5195022, | |
-5241824, | |
-5288626, | |
-5335428, | |
-5382230, | |
-5429032, | |
-5475834, | |
-5522636, | |
-5569438, | |
-5616240, | |
-5663042, | |
-5709844, | |
-5756646, | |
-5803448, | |
-5850250, | |
-5897052, | |
-5943854 | |
]; | |
static const CBG = const [ | |
2919680, | |
2897126, | |
2874572, | |
2852018, | |
2829464, | |
2806910, | |
2784356, | |
2761802, | |
2739248, | |
2716694, | |
2694140, | |
2671586, | |
2649032, | |
2626478, | |
2603924, | |
2581370, | |
2558816, | |
2536262, | |
2513708, | |
2491154, | |
2468600, | |
2446046, | |
2423492, | |
2400938, | |
2378384, | |
2355830, | |
2333276, | |
2310722, | |
2288168, | |
2265614, | |
2243060, | |
2220506, | |
2197952, | |
2175398, | |
2152844, | |
2130290, | |
2107736, | |
2085182, | |
2062628, | |
2040074, | |
2017520, | |
1994966, | |
1972412, | |
1949858, | |
1927304, | |
1904750, | |
1882196, | |
1859642, | |
1837088, | |
1814534, | |
1791980, | |
1769426, | |
1746872, | |
1724318, | |
1701764, | |
1679210, | |
1656656, | |
1634102, | |
1611548, | |
1588994, | |
1566440, | |
1543886, | |
1521332, | |
1498778, | |
1476224, | |
1453670, | |
1431116, | |
1408562, | |
1386008, | |
1363454, | |
1340900, | |
1318346, | |
1295792, | |
1273238, | |
1250684, | |
1228130, | |
1205576, | |
1183022, | |
1160468, | |
1137914, | |
1115360, | |
1092806, | |
1070252, | |
1047698, | |
1025144, | |
1002590, | |
980036, | |
957482, | |
934928, | |
912374, | |
889820, | |
867266, | |
844712, | |
822158, | |
799604, | |
777050, | |
754496, | |
731942, | |
709388, | |
686834, | |
664280, | |
641726, | |
619172, | |
596618, | |
574064, | |
551510, | |
528956, | |
506402, | |
483848, | |
461294, | |
438740, | |
416186, | |
393632, | |
371078, | |
348524, | |
325970, | |
303416, | |
280862, | |
258308, | |
235754, | |
213200, | |
190646, | |
168092, | |
145538, | |
122984, | |
100430, | |
77876, | |
55322, | |
32768, | |
10214, | |
-12340, | |
-34894, | |
-57448, | |
-80002, | |
-102556, | |
-125110, | |
-147664, | |
-170218, | |
-192772, | |
-215326, | |
-237880, | |
-260434, | |
-282988, | |
-305542, | |
-328096, | |
-350650, | |
-373204, | |
-395758, | |
-418312, | |
-440866, | |
-463420, | |
-485974, | |
-508528, | |
-531082, | |
-553636, | |
-576190, | |
-598744, | |
-621298, | |
-643852, | |
-666406, | |
-688960, | |
-711514, | |
-734068, | |
-756622, | |
-779176, | |
-801730, | |
-824284, | |
-846838, | |
-869392, | |
-891946, | |
-914500, | |
-937054, | |
-959608, | |
-982162, | |
-1004716, | |
-1027270, | |
-1049824, | |
-1072378, | |
-1094932, | |
-1117486, | |
-1140040, | |
-1162594, | |
-1185148, | |
-1207702, | |
-1230256, | |
-1252810, | |
-1275364, | |
-1297918, | |
-1320472, | |
-1343026, | |
-1365580, | |
-1388134, | |
-1410688, | |
-1433242, | |
-1455796, | |
-1478350, | |
-1500904, | |
-1523458, | |
-1546012, | |
-1568566, | |
-1591120, | |
-1613674, | |
-1636228, | |
-1658782, | |
-1681336, | |
-1703890, | |
-1726444, | |
-1748998, | |
-1771552, | |
-1794106, | |
-1816660, | |
-1839214, | |
-1861768, | |
-1884322, | |
-1906876, | |
-1929430, | |
-1951984, | |
-1974538, | |
-1997092, | |
-2019646, | |
-2042200, | |
-2064754, | |
-2087308, | |
-2109862, | |
-2132416, | |
-2154970, | |
-2177524, | |
-2200078, | |
-2222632, | |
-2245186, | |
-2267740, | |
-2290294, | |
-2312848, | |
-2335402, | |
-2357956, | |
-2380510, | |
-2403064, | |
-2425618, | |
-2448172, | |
-2470726, | |
-2493280, | |
-2515834, | |
-2538388, | |
-2560942, | |
-2583496, | |
-2606050, | |
-2628604, | |
-2651158, | |
-2673712, | |
-2696266, | |
-2718820, | |
-2741374, | |
-2763928, | |
-2786482, | |
-2809036, | |
-2831590 | |
]; | |
static const CBB = const [ | |
-227, | |
-225, | |
-223, | |
-222, | |
-220, | |
-218, | |
-216, | |
-214, | |
-213, | |
-211, | |
-209, | |
-207, | |
-206, | |
-204, | |
-202, | |
-200, | |
-198, | |
-197, | |
-195, | |
-193, | |
-191, | |
-190, | |
-188, | |
-186, | |
-184, | |
-183, | |
-181, | |
-179, | |
-177, | |
-175, | |
-174, | |
-172, | |
-170, | |
-168, | |
-167, | |
-165, | |
-163, | |
-161, | |
-159, | |
-158, | |
-156, | |
-154, | |
-152, | |
-151, | |
-149, | |
-147, | |
-145, | |
-144, | |
-142, | |
-140, | |
-138, | |
-136, | |
-135, | |
-133, | |
-131, | |
-129, | |
-128, | |
-126, | |
-124, | |
-122, | |
-120, | |
-119, | |
-117, | |
-115, | |
-113, | |
-112, | |
-110, | |
-108, | |
-106, | |
-105, | |
-103, | |
-101, | |
-99, | |
-97, | |
-96, | |
-94, | |
-92, | |
-90, | |
-89, | |
-87, | |
-85, | |
-83, | |
-82, | |
-80, | |
-78, | |
-76, | |
-74, | |
-73, | |
-71, | |
-69, | |
-67, | |
-66, | |
-64, | |
-62, | |
-60, | |
-58, | |
-57, | |
-55, | |
-53, | |
-51, | |
-50, | |
-48, | |
-46, | |
-44, | |
-43, | |
-41, | |
-39, | |
-37, | |
-35, | |
-34, | |
-32, | |
-30, | |
-28, | |
-27, | |
-25, | |
-23, | |
-21, | |
-19, | |
-18, | |
-16, | |
-14, | |
-12, | |
-11, | |
-9, | |
-7, | |
-5, | |
-4, | |
-2, | |
0, | |
2, | |
4, | |
5, | |
7, | |
9, | |
11, | |
12, | |
14, | |
16, | |
18, | |
19, | |
21, | |
23, | |
25, | |
27, | |
28, | |
30, | |
32, | |
34, | |
35, | |
37, | |
39, | |
41, | |
43, | |
44, | |
46, | |
48, | |
50, | |
51, | |
53, | |
55, | |
57, | |
58, | |
60, | |
62, | |
64, | |
66, | |
67, | |
69, | |
71, | |
73, | |
74, | |
76, | |
78, | |
80, | |
82, | |
83, | |
85, | |
87, | |
89, | |
90, | |
92, | |
94, | |
96, | |
97, | |
99, | |
101, | |
103, | |
105, | |
106, | |
108, | |
110, | |
112, | |
113, | |
115, | |
117, | |
119, | |
120, | |
122, | |
124, | |
126, | |
128, | |
129, | |
131, | |
133, | |
135, | |
136, | |
138, | |
140, | |
142, | |
144, | |
145, | |
147, | |
149, | |
151, | |
152, | |
154, | |
156, | |
158, | |
159, | |
161, | |
163, | |
165, | |
167, | |
168, | |
170, | |
172, | |
174, | |
175, | |
177, | |
179, | |
181, | |
183, | |
184, | |
186, | |
188, | |
190, | |
191, | |
193, | |
195, | |
197, | |
198, | |
200, | |
202, | |
204, | |
206, | |
207, | |
209, | |
211, | |
213, | |
214, | |
216, | |
218, | |
220, | |
222, | |
223, | |
225 | |
]; | |
} | |
class _JpegHuffman { | |
final children = List<dynamic>(); | |
int index = 0; | |
} | |
class _ComponentData { | |
int hSamples; | |
int maxHSamples; | |
int vSamples; | |
int maxVSamples; | |
List<Uint8List> lines; | |
int hScaleShift; | |
int vScaleShift; | |
_ComponentData(this.hSamples, this.maxHSamples, this.vSamples, | |
this.maxVSamples, this.lines) | |
: hScaleShift = (hSamples == 1 && maxHSamples == 2) ? 1 : 0, | |
vScaleShift = (vSamples == 1 && maxVSamples == 2) ? 1 : 0; | |
} |