import 'dart:typed_data'; | |
import '../../image.dart'; | |
import '../internal/bit_operators.dart'; | |
enum ExifValueType { | |
None, | |
Byte, | |
Ascii, | |
Short, | |
Long, | |
Rational, | |
SByte, | |
Undefined, | |
SShort, | |
SLong, | |
SRational, | |
Single, | |
Double | |
} | |
const ExifValueTypeString = [ | |
'None', | |
'Byte', | |
'Ascii', | |
'Short', | |
'Long', | |
'Rational', | |
'SByte', | |
'Undefined', | |
'SShort', | |
'SLong', | |
'SRational', | |
'Single', | |
'Double' | |
]; | |
const ExifValueTypeSize = [ | |
0, | |
1, | |
1, | |
2, | |
4, | |
8, | |
1, | |
1, | |
2, | |
4, | |
8, | |
4, | |
8 | |
]; | |
class Rational { | |
int numerator; | |
int denominator; | |
Rational(this.numerator, this.denominator); | |
void simplify() { | |
final d = numerator.gcd(denominator); | |
if (d != 0) { | |
numerator ~/= d; | |
denominator ~/= d; | |
} | |
} | |
int toInt() => denominator == 0 ? 0 : numerator ~/ denominator; | |
double toDouble() => denominator == 0 ? 0.0 : numerator / denominator; | |
bool operator ==(Object other) => | |
other is Rational && | |
numerator == other.numerator && denominator == other.denominator; | |
int get hashCode => Object.hash(numerator, denominator); | |
String toString() => '$numerator/$denominator'; | |
} | |
abstract class ExifValue { | |
ExifValue clone(); | |
ExifValueType get type; | |
int get length; | |
int get dataSize => ExifValueTypeSize[type.index] * length; | |
String get typeString => ExifValueTypeString[type.index]; | |
bool toBool([int index = 0]) => false; | |
int toInt([int index = 0]) => 0; | |
double toDouble([int index = 0]) => 0.0; | |
Uint8List toData() => Uint8List(0); | |
String toString() => ""; | |
Rational toRational([int index = 0]) => Rational(0, 1); | |
bool operator ==(Object other) => | |
other is ExifValue && | |
type == other.type && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => 0; | |
void write(OutputBuffer out); | |
void setBool(bool v, [int index = 0]) {} | |
void setInt(int v, [int index = 0]) {} | |
void setDouble(double v, [int index = 0]) {} | |
void setRational(int numerator, int denomitator, [int index = 0]) {} | |
void setString(String v) {} | |
} | |
class ExifByteValue extends ExifValue { | |
Uint8List value; | |
ExifByteValue(int value) | |
: value = Uint8List(1) { | |
this.value[0] = value; | |
} | |
ExifByteValue.list(Uint8List value) | |
: value = Uint8List.fromList(value); | |
ExifByteValue.data(InputBuffer data, int count) | |
: value = Uint8List.fromList(data.toUint8List()); | |
ExifValue clone() => ExifByteValue.list(value); | |
ExifValueType get type => ExifValueType.Byte; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifByteValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index]; | |
void setInt(int v, [int index = 0]) { value[index] = v; } | |
Uint8List toData() => value; | |
void write(OutputBuffer out) { | |
out.writeBytes(value); | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifAsciiValue extends ExifValue { | |
String value; | |
ExifAsciiValue(this.value); | |
ExifAsciiValue.list(List<int> value) | |
: value = String.fromCharCodes(value); | |
ExifAsciiValue.data(InputBuffer data, int count) | |
: value = count == 0 ? data.readString() : data.readString(count - 1); | |
ExifAsciiValue.string(this.value); | |
ExifValue clone() => ExifAsciiValue.string(value); | |
ExifValueType get type => ExifValueType.Ascii; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifAsciiValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => value.hashCode; | |
Uint8List toData() => Uint8List.fromList(value.codeUnits); | |
void write(OutputBuffer out) { | |
out.writeBytes(value.codeUnits); | |
} | |
String toString() => value; | |
void setString(String v) { value = v; } | |
} | |
class ExifShortValue extends ExifValue { | |
Uint16List value; | |
ExifShortValue(int value) | |
: value = Uint16List(1) { | |
this.value[0] = value; | |
} | |
ExifShortValue.list(List<int> value) | |
: value = Uint16List.fromList(value); | |
ExifShortValue.data(InputBuffer data, int count) | |
: value = Uint16List(count) { | |
for (int i = 0; i < count; ++i) { | |
value[i] = data.readUint16(); | |
} | |
} | |
ExifValue clone() => ExifShortValue.list(value); | |
ExifValueType get type => ExifValueType.Short; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifShortValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index]; | |
void setInt(int v, [int index = 0]) { value[index] = v; } | |
void write(OutputBuffer out) { | |
for (int i = 0, l = value.length; i < l; ++i) { | |
out.writeUint16(value[i]); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifLongValue extends ExifValue { | |
Uint32List value; | |
ExifLongValue(int value) | |
: value = Uint32List(1) { | |
this.value[0] = value; | |
} | |
ExifLongValue.list(List<int> value) | |
: value = Uint32List.fromList(value); | |
ExifLongValue.data(InputBuffer data, int count) | |
: value = Uint32List(count) { | |
for (int i = 0; i < count; ++i) { | |
value[i] = data.readUint32(); | |
} | |
} | |
ExifValue clone() => ExifLongValue.list(value); | |
ExifValueType get type => ExifValueType.Long; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifLongValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index]; | |
void setInt(int v, [int index = 0]) { value[index] = v; } | |
Uint8List toData() => Uint8List.view(value.buffer); | |
void write(OutputBuffer out) { | |
for (int i = 0, l = value.length; i < l; ++i) { | |
out.writeUint32(value[i]); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifRationalValue extends ExifValue { | |
List<Rational> value; | |
ExifRationalValue(int numerator, int denominator) | |
: value = [Rational(numerator, denominator)]; | |
ExifRationalValue.from(Rational r) | |
: value = [Rational(r.numerator, r.denominator)]; | |
ExifRationalValue.list(List<Rational> value) | |
: value = List<Rational>.from(value); | |
ExifRationalValue.data(InputBuffer data, int count) | |
: value = List<Rational>.generate(count, (i) => | |
Rational(data.readUint32(), data.readUint32())); | |
ExifValue clone() => ExifRationalValue.list(value); | |
ExifValueType get type => ExifValueType.Rational; | |
int get length => value.length; | |
int toInt([int index = 0]) => value[index].toInt(); | |
double toDouble([int index = 0]) => value[index].toDouble(); | |
Rational toRational([int index = 0]) => value[index]; | |
bool operator ==(Object other) => | |
other is ExifRationalValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
void setRational(int numerator, int denomitator, [int index = 0]) { | |
value[index].numerator = numerator; | |
value[index].denominator = denomitator; | |
} | |
void write(OutputBuffer out) { | |
for (var v in value) { | |
out.writeUint32(v.numerator); | |
out.writeUint32(v.denominator); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifSByteValue extends ExifValue { | |
Int8List value; | |
ExifSByteValue(int value) | |
: value = Int8List(1) { | |
this.value[0] = value; | |
} | |
ExifSByteValue.list(List<int> value) | |
: value = Int8List.fromList(value); | |
ExifSByteValue.data(InputBuffer data, int count) | |
: value = Int8List.fromList(Int8List.view(data.toUint8List().buffer)); | |
ExifValue clone() => ExifSByteValue.list(value); | |
ExifValueType get type => ExifValueType.SByte; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifSByteValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index]; | |
void setInt(int v, [int index = 0]) { value[index] = v; } | |
Uint8List toData() => Uint8List.view(value.buffer); | |
void write(OutputBuffer out) { | |
out.writeBytes(Uint8List.view(value.buffer)); | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifSShortValue extends ExifValue { | |
Int16List value; | |
ExifSShortValue(int value) | |
: value = Int16List(1) { | |
this.value[0] = value; | |
} | |
ExifSShortValue.list(List<int> value) | |
: value = Int16List.fromList(value); | |
ExifSShortValue.data(InputBuffer data, int count) | |
: value = Int16List(count) { | |
for (int i = 0; i < count; ++i) { | |
value[i] = data.readInt16(); | |
} | |
} | |
ExifValue clone() => ExifSShortValue.list(value); | |
ExifValueType get type => ExifValueType.SShort; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifSShortValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index]; | |
void setInt(int v, [int index = 0]) { value[index] = v; } | |
Uint8List toData() => Uint8List.view(value.buffer); | |
void write(OutputBuffer out) { | |
final v = Int16List(1); | |
final vb = Uint16List.view(v.buffer); | |
for (int i = 0, l = value.length; i < l; ++i) { | |
v[0] = value[i]; | |
out.writeUint16(vb[0]); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifSLongValue extends ExifValue { | |
Int32List value; | |
ExifSLongValue(int value) | |
: value = Int32List(1) { | |
this.value[0] = value; | |
} | |
ExifSLongValue.list(List<int> value) | |
: value = Int32List.fromList(value); | |
ExifSLongValue.data(InputBuffer data, int count) | |
: value = Int32List(count) { | |
for (int i = 0; i < count; ++i) { | |
value[i] = data.readInt32(); | |
} | |
} | |
ExifValue clone() => ExifSLongValue.list(value); | |
ExifValueType get type => ExifValueType.SLong; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifSLongValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index]; | |
void setInt(int v, [int index = 0]) { value[index] = v; } | |
Uint8List toData() => Uint8List.view(value.buffer); | |
void write(OutputBuffer out) { | |
for (int i = 0, l = value.length; i < l; ++i) { | |
out.writeUint32(int32ToUint32(value[i])); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifSRationalValue extends ExifValue { | |
List<Rational> value; | |
ExifSRationalValue(int numerator, int denominator) | |
: value = [Rational(numerator, denominator)]; | |
ExifSRationalValue.from(Rational value) | |
: value = [value]; | |
ExifSRationalValue.data(InputBuffer data, int count) | |
: value = List<Rational>.generate(count, (i) => | |
Rational(data.readInt32(), data.readInt32())); | |
ExifSRationalValue.list(List<Rational> value) | |
: value = List<Rational>.from(value); | |
ExifValue clone() => ExifSRationalValue.list(value); | |
ExifValueType get type => ExifValueType.SRational; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifSRationalValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
int toInt([int index = 0]) => value[index].toInt(); | |
double toDouble([int index = 0]) => value[index].toDouble(); | |
void setRational(int numerator, int denomitator, [int index = 0]) { | |
value[index].numerator = numerator; | |
value[index].denominator = denomitator; | |
} | |
void write(OutputBuffer out) { | |
for (var v in value) { | |
out.writeUint32(int32ToUint32(v.numerator)); | |
out.writeUint32(int32ToUint32(v.denominator)); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifSingleValue extends ExifValue { | |
Float32List value; | |
ExifSingleValue(double value) | |
: value = Float32List(1) { | |
this.value[0] = value; | |
} | |
ExifSingleValue.list(List<double> value) | |
: value = Float32List.fromList(value); | |
ExifSingleValue.data(InputBuffer data, int count) | |
: value = Float32List(count) { | |
for (int i = 0; i < count; ++i) { | |
value[i] = data.readFloat32(); | |
} | |
} | |
ExifValue clone() => ExifSingleValue.list(value); | |
ExifValueType get type => ExifValueType.Single; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifSingleValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
Uint8List toData() => Uint8List.view(value.buffer); | |
double toDouble([int index = 0]) => value[index]; | |
void setDouble(double v, [int index = 0]) { value[index] = v; } | |
void write(OutputBuffer out) { | |
for (int i = 0, l = value.length; i < l; ++i) { | |
out.writeFloat32(value[i]); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifDoubleValue extends ExifValue { | |
Float64List value; | |
ExifDoubleValue(double value) | |
: value = Float64List(1) { | |
this.value[0] = value; | |
} | |
ExifDoubleValue.list(List<double> value) | |
: value = Float64List.fromList(value); | |
ExifDoubleValue.data(InputBuffer data, int count) | |
: value = Float64List(count) { | |
for (int i = 0; i < count; ++i) { | |
value[i] = data.readFloat32(); | |
} | |
} | |
ExifValue clone() => ExifDoubleValue.list(value); | |
ExifValueType get type => ExifValueType.Double; | |
int get length => value.length; | |
bool operator ==(Object other) => | |
other is ExifDoubleValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
double toDouble([int index = 0]) => value[index]; | |
void setDouble(double v, [int index = 0]) { value[index] = v; } | |
Uint8List toData() => Uint8List.view(value.buffer); | |
void write(OutputBuffer out) { | |
for (int i = 0, l = value.length; i < l; ++i) { | |
out.writeFloat64(value[i]); | |
} | |
} | |
String toString() => value.length == 1 ? '${value[0]}' : '$value'; | |
} | |
class ExifUndefinedValue extends ExifValue { | |
Uint8List value; | |
ExifUndefinedValue.list(List<int> value) | |
: value = Uint8List.fromList(value); | |
ExifUndefinedValue.data(InputBuffer data, int count) | |
: value = Uint8List.fromList(data.toUint8List()); | |
ExifValue clone() => ExifUndefinedValue.list(value); | |
ExifValueType get type => ExifValueType.Undefined; | |
int get length => value.length; | |
Uint8List toData() => value; | |
bool operator ==(Object other) => | |
other is ExifUndefinedValue && | |
length == other.length && | |
hashCode == other.hashCode; | |
int get hashCode => Object.hashAll(value); | |
void write(OutputBuffer out) { | |
out.writeBytes(value); | |
} | |
String toString() => '<data>'; | |
} |