A few years ago I wrote some code to deal with EXIF information in images, but never finished it. I've just added some code to include the orientation data.
Below is an example of getting the orientation data of an image with EXIF information:
Code: Select all
; Exif library for PureBasic
; Programming by Francis G. Loch
;
; Based on information from http://www.media.mit.edu/pia/Research/deepview/exif.html
; and https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
#Exif_Header = $45786966
#Tiff_MagicWord = $002A
#Marker_StartOfImage = $FFD8
#Marker_EndOfImage = $FFD9
#Marker_StartOfStream = $FFDA
#Marker_Application_APP0 = $FFE0
#Marker_Application_APP1 = $FFE1
#ByteAlign_Intel = $4949
#ByteAlign_Motorola = $4D4D
#ExifIFD_BodySerialNumber = $A431
#IFD0_ImageDescription = $010E
#IFD0_Make = $010F
#IFD0_Model = $0110
#IFD0_Orientation = $0112
#IFD0_XResolution = $011A
#IFD0_YResolution = $011B
#IFD0_ResolutionUnit = $0128
#IFD0_Software = $0131
#IFD0_DateTime = $0132
#IFD0_WhitePoint = $013E
#IFD0_PrimaryChromaticities = $013F
#IFD0_YCbCrCoefficients = $0211
#IFD0_YCbCrPositioning = $0213
#IFD0_ReferenceBlackWhite = $0214
#IFD0_Copyright = $8298
#IFD0_ExifOffset = $8769
#IFD0_CameraSerialNumber = $C62F
#SubIFD_DateTimeOriginal = $9003
#SubIFD_SerialNumber = $FDE9
Enumeration 1
#Exif_DataFormat_UnsignedByte
#Exif_DataFormat_AsciiStrings
#Exif_DataFormat_UnsignedShort
#Exif_DataFormat_UnsignedLong
#Exif_DataFormat_UnsignedRational
#Exif_DataFormat_SignedByte
#Exif_DataFormat_Undefined
#Exif_DataFormat_SignedShort
#Exif_DataFormat_SignedLong
#Exif_DataFormat_SignedRational
#Exif_DataFormat_SingleFloat
#Exif_DataFormat_DoubleFloat
EndEnumeration
Structure IFD0
Make.s
Model.s
Orientation.l
ExifOffset.q
CameraSerialNumber.s
EndStructure
Structure SubIFD
DateTimeOriginal.s
SerialNumber.s
EndStructure
Structure ExifTags
Offset.l
BodySerialNumber.s
IFD0.IFD0
SubIFD.SubIFD
EndStructure
Macro ConvertEndianWord(n)
(((n & $FF) << 8) | ((n >> 8) & $FF))
EndMacro
Macro ConvertEndianLong(n)
(((n & $FF) << 24) | ((n & $FF00) << 8) | ((n >> 8) & $FF00) | ((n >> 24) & $FF))
EndMacro
Procedure.w FixByteOrderWord(Value.w, ByteOrder)
Select ByteOrder
Case #ByteAlign_Intel
ProcedureReturn Value
Case #ByteAlign_Motorola
ProcedureReturn ConvertEndianWord(Value)
EndSelect
EndProcedure
Procedure.l FixByteOrderLong(Value.l, ByteOrder)
Select ByteOrder
Case #ByteAlign_Intel
ProcedureReturn Value
Case #ByteAlign_Motorola
ProcedureReturn ConvertEndianLong(Value)
EndSelect
EndProcedure
Procedure GetExifData(hndFile, *ExifTags.ExifTags, Offset = 0)
Protected i, TagID, TagCount, DataFormat, DataLength, DataOffset, DataValue.q, DataString$, ByteOrder, MagicWord, CurrentOffset, FileOffset
If IsFile(hndFile) = 0
ProcedureReturn 0
EndIf
FileSeek(hndFile, *ExifTags\Offset)
ByteOrder = ReadWord(hndFile)
If ByteOrder <> #ByteAlign_Intel And ByteOrder <> #ByteAlign_Motorola
ProcedureReturn 0
EndIf
MagicWord = ReadWord(hndFile)
If MagicWord <> #Tiff_MagicWord And MagicWord <> ConvertEndianWord(#Tiff_MagicWord)
ProcedureReturn 0
EndIf
If Offset
FileSeek(hndFile, *ExifTags\Offset + Offset)
Else
FileSeek(hndFile, *ExifTags\Offset + FixByteOrderLong(ReadLong(hndFile), ByteOrder))
EndIf
TagCount = FixByteOrderWord(ReadWord(hndFile), ByteOrder)
FileOffset = Loc(hndFile)
For i = 1 To TagCount
FileSeek(hndFile, FileOffset)
TagID = FixByteOrderWord(ReadWord(hndFile), ByteOrder) & $FFFF
DataFormat = FixByteOrderWord(ReadWord(hndFile), ByteOrder) & $FFFF
DataLength = FixByteOrderLong(ReadLong(hndFile), ByteOrder) & $FFFFFFFF
Select DataFormat
Case #Exif_DataFormat_AsciiStrings
If DataLength > 4
DataOffset = FixByteOrderLong(ReadLong(hndFile), ByteOrder)
FileSeek(hndFile, *ExifTags\Offset + DataOffset)
DataString$ = ReadString(hndFile, #PB_Ascii, DataLength)
FileSeek(hndFile, CurrentOffset)
Else
DataString$ = ReadString(hndFile, #PB_Ascii, DataLength)
EndIf
Select TagID
Case #IFD0_Make
*ExifTags\IFD0\Make = DataString$
Case #IFD0_Model
*ExifTags\IFD0\Model = DataString$
Case #ExifIFD_BodySerialNumber
*ExifTags\BodySerialNumber = DataString$
; Debug "ExifIFD Body Serial Number = " + DataString$ + " (Make: " + *ExifTags\IFD0\Make + ", Model: " + *ExifTags\IFD0\Model + ")"
Case #IFD0_CameraSerialNumber
*ExifTags\IFD0\CameraSerialNumber = DataString$
; Debug "IFD0 Camera Serial Number = " + DataString$ + " (Make: " + *ExifTags\IFD0\Make + ", Model: " + *ExifTags\IFD0\Model + ")"
Case #SubIFD_SerialNumber
*ExifTags\SubIFD\SerialNumber = DataString$
; Debug "SubIFD Serial Number = " + DataString$ + " (Make: " + *ExifTags\IFD0\Make + ", Model: " + *ExifTags\IFD0\Model + ")"
Case #SubIFD_DateTimeOriginal
*ExifTags\SubIFD\DateTimeOriginal = DataString$
EndSelect
Case #Exif_DataFormat_UnsignedShort
DataValue = FixByteOrderWord(ReadLong(hndFile), ByteOrder) & $FFFFFFFF
Select TagID
Case #IFD0_Orientation
*ExifTags\IFD0\Orientation = DataValue
EndSelect
Case #Exif_DataFormat_UnsignedLong
DataValue = FixByteOrderLong(ReadLong(hndFile), ByteOrder) & $FFFFFFFF
Select TagID
Case #IFD0_ExifOffset
*ExifTags\IFD0\ExifOffset = DataValue
EndSelect
EndSelect
FileOffset + 12
Next
EndProcedure
Procedure ReadExifFile(*ExifTags.ExifTags, FileName$)
Protected hndFile, AppMarker, Value, Offset, *mem, i
hndFile = ReadFile(#PB_Any, FileName$, #PB_File_SharedRead)
If hndFile = 0
Debug "Unable to open file! (" + FileName$ + ")"
ProcedureReturn 0
EndIf
Value = ReadWord(hndFile)
Select Value
Case ConvertEndianWord(#Marker_StartOfImage)
AppMarker = ConvertEndianWord(ReadWord(hndFile)) & $FFFF
If AppMarker = #Marker_Application_APP0
FileSeek(hndFile, ConvertEndianWord(ReadWord(hndFile)) & $FFFF + 4) ; Jump to end of APP0 section
AppMarker = ConvertEndianWord(ReadWord(hndFile)) & $FFFF
EndIf
If AppMarker = #Marker_Application_APP1
FileSeek(hndFile, 2, #PB_Relative)
If ReadLong(hndFile) = ConvertEndianLong(#Exif_Header) And ReadWord(hndFile) = 0
*ExifTags\Offset = Loc(hndFile)
GetExifData(hndFile, *ExifTags)
If *ExifTags\IFD0\ExifOffset
GetExifData(hndFile, *ExifTags, *ExifTags\IFD0\ExifOffset)
EndIf
EndIf
EndIf
Case #ByteAlign_Intel, #ByteAlign_Motorola
If FixByteOrderWord(ReadWord(hndFile), Value) = #Tiff_MagicWord
*ExifTags\Offset = 0
GetExifData(hndFile, *ExifTags)
If *ExifTags\IFD0\ExifOffset
GetExifData(hndFile, *ExifTags, *ExifTags\IFD0\ExifOffset)
EndIf
EndIf
Default
; Brute force method of finding the data. Not ideal!
If FileSize(FileName$) > 4096
FileSeek(hndFile, 0)
*mem = AllocateMemory(4096)
ReadData(hndFile, *mem, MemorySize(*mem))
For i = 0 To MemorySize(*mem) - 1 - 128 ; Reduced by 128 so we don't go outside the allocated memory
Value = PeekW(*mem + i)
If Value = #ByteAlign_Intel Or Value = #ByteAlign_Motorola
If FixByteOrderWord(PeekW(*mem + i + 2), Value) = #Tiff_MagicWord
*ExifTags\Offset = i
GetExifData(hndFile, *ExifTags, 0)
If *ExifTags\IFD0\ExifOffset
GetExifData(hndFile, *ExifTags, *ExifTags\IFD0\ExifOffset)
EndIf
Break
EndIf
EndIf
Next
FreeMemory(*mem)
EndIf
EndSelect
CloseFile(hndFile)
EndProcedure
;- Sample code
Define ExifTags.ExifTags
FileName$ = OpenFileRequester("Open Image", "", "", 0)
ReadExifFile(@ExifTags, FileName$)
GetExifData(0, @ExifTags)
Debug "Camera: " + ExifTags\IFD0\Make + " " + ExifTags\IFD0\Model
Select ExifTags\IFD0\Orientation
Case 1
Debug "Orientation: Do nothing"
Case 2
Debug "Orientation: Flip horizontally"
Case 3
Debug "Orientation: Rotate 180 degrees"
Case 4
Debug "Orientation: Flip vertically"
Case 5
Debug "Orientation: Rotate 90 degrees clockwise and flip horizontally"
Case 6
Debug "Orientation: Rotate 90 degrees clockwise"
Case 7
Debug "Orientation: Rotate 90 degrees clockwise and flip vertically"
Case 8
Debug "Orientation: Rotate 270 degrees clockwise"
Default
Debug "No orientation data"
EndSelect
It's not pretty, but hopefully it will be of use.