type
TExifOrientation = (
eoError, eoNormal, eoMirrorHor, eoRotate180, eoMirrorVert,
eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270
); // all angle are clockwise
function DetectImageRotation(AStream: TStream): TExifOrientation;
type
TIFDRecord = packed record
TagID: Word;
DataType: Word;
DataCount: DWord;
DataValue: DWord;
end;
TTiffHeader = packed record
BOM: Array[0..1] of AnsiChar; // 'II' for little endian, 'MM' for big endian
Signature: Word; // Signature (42)
IFDOffset: DWord; // Offset where image data begin, from start of TIFF header
end;
const
EXIF_SIGNATURE: array[0..5] of AnsiChar = ('E', 'x', 'i', 'f', #0, #0);
LITTLE_ENDIAN_BOM: array[0..1] of AnsiChar = ('I', 'I');
BIG_ENDIAN_BOM: array[0..1] of AnsiChar = ('M', 'M');
var
bigEndian: Boolean;
function FixEndian16(AValue: Word): Word;
begin
if bigEndian then
Result := BEtoN(AValue)
else
Result := LEtoN(AValue);
end;
function FixEndian32(AValue: DWord): DWord;
begin
if bigEndian then
Result := BEtoN(AValue)
else
Result := LEtoN(AValue);
end;
var
p0, p: Int64;
streamSize: Int64;
marker: byte;
size: Word;
exif_hdr: array [0..5] of ansichar;
tiffhdr_start: Int64;
tiff_hdr: TTiffHeader;
numRecords: Word;
i: Integer;
ifdRec: TIFDRecord;
begin
Result := eoError;
if (LEToN(AStream.ReadWord) <> $D8FF) then
exit;
streamsize := AStream.Size;
p0 := AStream.Position;
p := p0;
while p < streamSize do begin
repeat
marker := AStream.ReadByte;
until marker <> $FF;
size := BEtoN(AStream.ReadWord) - 2;
p := AStream.Position;
if marker = $E1 then begin // EXIF marker
// Read EXIF header
AStream.Read(exif_hdr[0], SizeOf(exif_hdr));
if not CompareMem(@exif_hdr[0], @EXIF_SIGNATURE[0], SizeOf(exif_hdr)) then
begin
// No EXIF structure found
AStream.Position := p0;
exit;
end;
// Read TIFF header
tiffhdr_start := AStream.Position;
AStream.Read(tiff_hdr, SizeOf(tiff_hdr));
if CompareMem(@tiff_hdr.BOM[0], @BIG_ENDIAN_BOM[0], SizeOf(BIG_ENDIAN_BOM)) then
bigEndian := true
else
if CompareMem(@tiff_hdr.BOM[0], @LITTLE_ENDIAN_BOM[0], SizeOf(LITTLE_ENDIAN_BOM)) then
bigEndian := false
else begin
// no valid TIFF header
AStream.Position := p0;
exit;
end;
tiff_hdr.Signature := FixEndian16(tiff_hdr.Signature);
if tiff_hdr.Signature <> 42 then begin
// no valid TIFF header
AStream.Position := p0;
exit;
end;
// Determine where the first directory (IFD0) begins...
tiff_hdr.IFDOffset := FixEndian32(tiff_hdr.IFDOffset);
// ... and move stream to there.
AStream.Position := tiffhdr_start + tiff_hdr.IFDOffset;
// Read count of IFD entries
numRecords := FixEndian16(AStream.ReadWord);
// Read EXIF records from IFD0
for i := 0 to numRecords-1 do begin
AStream.Read(ifdRec, SizeOf(ifdRec));
ifdRec.TagID := FixEndian16(ifdRec.TagID);
// Found Orientation tag
if ifdRec.TagID = $0112 then begin
ifdRec.DataValue := FixEndian32(ifdRec.DataValue);
Result := TExifOrientation(ifdRec.DataValue);
AStream.Position := p0;
exit;
end;
end;
end;
AStream.Position := p + size;
end;
end;