Files
2025-05-15 16:01:31 +02:00

1250 lines
39 KiB
VB.net

''' <summary>
''' Utility class for working with EXIF data in images. Provides abstraction
''' for most common data and generic utilities for work with all other.
''' </summary>
''' <remarks>
''' Copyright (c) Michal A. Valášek - Altair Communications, 2003-2005
''' Copmany: http://software.altaircom.net, E-mail: support@altaircom.net
''' Private: http://www.rider.cz, E-mail: rider@rider.cz
''' This is free software licensed under GNU Lesser General Public License
''' </remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 12.06.2004 Added capability to write EXIF data
''' [altair] 11.07.2004 Added option to change encoding
''' [altair] 04.09.2005 Changed source of Width and Height properties from EXIF to image
''' [altair] 05.09.2005 Code clean-up and minor changes
''' </history>
Public Class ExifWorks
Implements IDisposable
Private _Image As System.Drawing.Bitmap
Private _Encoding As System.Text.Encoding = System.Text.Encoding.UTF8
#Region " Type declarations "
''' <summary>
''' Contains possible values of EXIF tag names (ID)
''' </summary>
''' <remarks>See GdiPlusImaging.h</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Enum TagNames As Integer
ExifIFD = &H8769
GpsIFD = &H8825
NewSubfileType = &HFE
SubfileType = &HFF
ImageWidth = &H100
ImageHeight = &H101
BitsPerSample = &H102
Compression = &H103
PhotometricInterp = &H106
ThreshHolding = &H107
CellWidth = &H108
CellHeight = &H109
FillOrder = &H10A
DocumentName = &H10D
ImageDescription = &H10E
EquipMake = &H10F
EquipModel = &H110
StripOffsets = &H111
Orientation = &H112
SamplesPerPixel = &H115
RowsPerStrip = &H116
StripBytesCount = &H117
MinSampleValue = &H118
MaxSampleValue = &H119
XResolution = &H11A
YResolution = &H11B
PlanarConfig = &H11C
PageName = &H11D
XPosition = &H11E
YPosition = &H11F
FreeOffset = &H120
FreeByteCounts = &H121
GrayResponseUnit = &H122
GrayResponseCurve = &H123
T4Option = &H124
T6Option = &H125
ResolutionUnit = &H128
PageNumber = &H129
TransferFuncition = &H12D
SoftwareUsed = &H131
DateTime = &H132
Artist = &H13B
HostComputer = &H13C
Predictor = &H13D
WhitePoint = &H13E
PrimaryChromaticities = &H13F
ColorMap = &H140
HalftoneHints = &H141
TileWidth = &H142
TileLength = &H143
TileOffset = &H144
TileByteCounts = &H145
InkSet = &H14C
InkNames = &H14D
NumberOfInks = &H14E
DotRange = &H150
TargetPrinter = &H151
ExtraSamples = &H152
SampleFormat = &H153
SMinSampleValue = &H154
SMaxSampleValue = &H155
TransferRange = &H156
JPEGProc = &H200
JPEGInterFormat = &H201
JPEGInterLength = &H202
JPEGRestartInterval = &H203
JPEGLosslessPredictors = &H205
JPEGPointTransforms = &H206
JPEGQTables = &H207
JPEGDCTables = &H208
JPEGACTables = &H209
YCbCrCoefficients = &H211
YCbCrSubsampling = &H212
YCbCrPositioning = &H213
REFBlackWhite = &H214
ICCProfile = &H8773
Gamma = &H301
ICCProfileDescriptor = &H302
SRGBRenderingIntent = &H303
ImageTitle = &H320
Copyright = &H8298
ResolutionXUnit = &H5001
ResolutionYUnit = &H5002
ResolutionXLengthUnit = &H5003
ResolutionYLengthUnit = &H5004
PrintFlags = &H5005
PrintFlagsVersion = &H5006
PrintFlagsCrop = &H5007
PrintFlagsBleedWidth = &H5008
PrintFlagsBleedWidthScale = &H5009
HalftoneLPI = &H500A
HalftoneLPIUnit = &H500B
HalftoneDegree = &H500C
HalftoneShape = &H500D
HalftoneMisc = &H500E
HalftoneScreen = &H500F
JPEGQuality = &H5010
GridSize = &H5011
ThumbnailFormat = &H5012
ThumbnailWidth = &H5013
ThumbnailHeight = &H5014
ThumbnailColorDepth = &H5015
ThumbnailPlanes = &H5016
ThumbnailRawBytes = &H5017
ThumbnailSize = &H5018
ThumbnailCompressedSize = &H5019
ColorTransferFunction = &H501A
ThumbnailData = &H501B
ThumbnailImageWidth = &H5020
ThumbnailImageHeight = &H502
ThumbnailBitsPerSample = &H5022
ThumbnailCompression = &H5023
ThumbnailPhotometricInterp = &H5024
ThumbnailImageDescription = &H5025
ThumbnailEquipMake = &H5026
ThumbnailEquipModel = &H5027
ThumbnailStripOffsets = &H5028
ThumbnailOrientation = &H5029
ThumbnailSamplesPerPixel = &H502A
ThumbnailRowsPerStrip = &H502B
ThumbnailStripBytesCount = &H502C
ThumbnailResolutionX = &H502D
ThumbnailResolutionY = &H502E
ThumbnailPlanarConfig = &H502F
ThumbnailResolutionUnit = &H5030
ThumbnailTransferFunction = &H5031
ThumbnailSoftwareUsed = &H5032
ThumbnailDateTime = &H5033
ThumbnailArtist = &H5034
ThumbnailWhitePoint = &H5035
ThumbnailPrimaryChromaticities = &H5036
ThumbnailYCbCrCoefficients = &H5037
ThumbnailYCbCrSubsampling = &H5038
ThumbnailYCbCrPositioning = &H5039
ThumbnailRefBlackWhite = &H503A
ThumbnailCopyRight = &H503B
LuminanceTable = &H5090
ChrominanceTable = &H5091
FrameDelay = &H5100
LoopCount = &H5101
PixelUnit = &H5110
PixelPerUnitX = &H5111
PixelPerUnitY = &H5112
PaletteHistogram = &H5113
ExifExposureTime = &H829A
ExifFNumber = &H829D
ExifExposureProg = &H8822
ExifSpectralSense = &H8824
ExifISOSpeed = &H8827
ExifOECF = &H8828
ExifVer = &H9000
ExifDTOrig = &H9003
ExifDTDigitized = &H9004
ExifCompConfig = &H9101
ExifCompBPP = &H9102
ExifShutterSpeed = &H9201
ExifAperture = &H9202
ExifBrightness = &H9203
ExifExposureBias = &H9204
ExifMaxAperture = &H9205
ExifSubjectDist = &H9206
ExifMeteringMode = &H9207
ExifLightSource = &H9208
ExifFlash = &H9209
ExifFocalLength = &H920A
ExifMakerNote = &H927C
ExifUserComment = &H9286
ExifDTSubsec = &H9290
ExifDTOrigSS = &H9291
ExifDTDigSS = &H9292
ExifFPXVer = &HA000
ExifColorSpace = &HA001
ExifPixXDim = &HA002
ExifPixYDim = &HA003
ExifRelatedWav = &HA004
ExifInterop = &HA005
ExifFlashEnergy = &HA20B
ExifSpatialFR = &HA20C
ExifFocalXRes = &HA20E
ExifFocalYRes = &HA20F
ExifFocalResUnit = &HA210
ExifSubjectLoc = &HA214
ExifExposureIndex = &HA215
ExifSensingMethod = &HA217
ExifFileSource = &HA300
ExifSceneType = &HA301
ExifCfaPattern = &HA302
GpsVer = &H0
GpsLatitudeRef = &H1
GpsLatitude = &H2
GpsLongitudeRef = &H3
GpsLongitude = &H4
GpsAltitudeRef = &H5
GpsAltitude = &H6
GpsGpsTime = &H7
GpsGpsSatellites = &H8
GpsGpsStatus = &H9
GpsGpsMeasureMode = &HA
GpsGpsDop = &HB
GpsSpeedRef = &HC
GpsSpeed = &HD
GpsTrackRef = &HE
GpsTrack = &HF
GpsImgDirRef = &H10
GpsImgDir = &H11
GpsMapDatum = &H12
GpsDestLatRef = &H13
GpsDestLat = &H14
GpsDestLongRef = &H15
GpsDestLong = &H16
GpsDestBearRef = &H17
GpsDestBear = &H18
GpsDestDistRef = &H19
GpsDestDist = &H1A
End Enum
''' <summary>
''' Real position of 0th row and column of picture
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Enum Orientations
TopLeft = 1
TopRight = 2
BottomRight = 3
BottomLeft = 4
LeftTop = 5
RightTop = 6
RightBottom = 7
LftBottom = 8
End Enum
''' <summary>
''' Exposure programs
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Enum ExposurePrograms
Manual = 1
Normal = 2
AperturePriority = 3
ShutterPriority = 4
Creative = 5
Action = 6
Portrait = 7
Landscape = 8
End Enum
''' <summary>
''' Exposure metering modes
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Enum ExposureMeteringModes
Unknown = 0
Average = 1
CenterWeightedAverage = 2
Spot = 3
MultiSpot = 4
MultiSegment = 5
[Partial] = 6
Other = 255
End Enum
''' <summary>
''' Flash activity modes
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Enum FlashModes
NotFired = 0
Fired = 1
FiredButNoStrobeReturned = 5
FiredAndStrobeReturned = 7
End Enum
''' <summary>
''' Possible light sources (white balance)
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Enum LightSources
Unknown = 0
Daylight = 1
Fluorescent = 2
Tungsten = 3
Flash = 10
StandardLightA = 17
StandardLightB = 18
StandardLightC = 19
D55 = 20
D65 = 21
D75 = 22
Other = 255
End Enum
''' <summary>
''' EXIF data types
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 12.6.2004 Created
''' </history>
Public Enum ExifDataTypes As Short
UnsignedByte = 1
AsciiString = 2
UnsignedShort = 3
UnsignedLong = 4
UnsignedRational = 5
SignedByte = 6
Undefined = 7
SignedShort = 8
SignedLong = 9
SignedRational = 10
SingleFloat = 11
DoubleFloat = 12
End Enum
''' <summary>
''' Represents rational which is type of some Exif properties
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Structure Rational
Dim Numerator As Int32
Dim Denominator As Int32
''' <summary>
''' Converts rational to string representation
''' </summary>
''' <param name="Delimiter">Optional, default "/". String to be used as delimiter of components.</param>
''' <returns>String representation of the rational.</returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Shadows Function ToString(Optional ByVal Delimiter As String = "/") As String
Return Numerator & Delimiter & Denominator
End Function
''' <summary>
''' Converts rational to double precision real number
''' </summary>
''' <returns>The rational as double precision real number.</returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Function ToDouble() As Double
Return Numerator / Denominator
End Function
End Structure
#End Region
''' <summary>
''' Initializes new instance of this class.
''' </summary>
''' <param name="Bitmap">Bitmap to read exif information from</param>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Sub New(ByRef Bitmap As System.Drawing.Bitmap)
If Bitmap Is Nothing Then Throw New ArgumentNullException("Bitmap")
Me._Image = Bitmap
End Sub
''' <summary>
''' Initializes new instance of this class.
''' </summary>
''' <param name="FileName">Name of file to be loaded</param>
''' <remarks></remarks>
''' <history>
''' [altair] 13.06.2004 Created
''' </history>
Public Sub New(ByVal FileName As String)
Me._Image = DirectCast(System.Drawing.Bitmap.FromFile(FileName), System.Drawing.Bitmap)
End Sub
''' <summary>
''' Get or set encoding used for string metadata
''' </summary>
''' <value>Encoding used for string metadata</value>
''' <remarks>Default encoding is UTF-8</remarks>
''' <history>
''' [altair] 11.07.2004 Created
''' [altair] 05.09.2005 Changed from shared to instance member
''' </history>
Public Property Encoding() As System.Text.Encoding
Get
Return Me._Encoding
End Get
Set(ByVal Value As System.Text.Encoding)
If Value Is Nothing Then Throw New ArgumentNullException
Me._Encoding = Encoding
End Set
End Property
''' <summary>
''' Returns copy of bitmap this instance is working on
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
''' <history>
''' [altair] 13.06.2004 Created
''' </history>
Public Function GetBitmap() As System.Drawing.Bitmap
Return DirectCast(Me._Image.Clone(), System.Drawing.Bitmap)
End Function
''' <summary>
''' Returns all available data in formatted string form
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Overrides Function ToString() As String
Dim SB As New System.Text.StringBuilder
SB.Append("Image:")
SB.Append("\n\tDimensions: " & Me.Width & " x " & Me.Height & " px")
SB.Append("\n\tResolution: " & Me.ResolutionX & " x " & Me.ResolutionY & " dpi")
SB.Append("\n\tOrientation: " & [Enum].GetName(GetType(Orientations), Me.Orientation))
SB.Append("\n\tTitle: " & Me.Title)
SB.Append("\n\tDescription: " & Me.Description)
SB.Append("\n\tCopyright: " & Me.Copyright)
SB.Append("\nEquipment:")
SB.Append("\n\tMaker: " & Me.EquipmentMaker)
SB.Append("\n\tModel: " & Me.EquipmentModel)
SB.Append("\n\tSoftware: " & Me.Software)
SB.Append("\nDate and time:")
SB.Append("\n\tGeneral: " & Me.DateTimeLastModified.ToString())
SB.Append("\n\tOriginal: " & Me.DateTimeOriginal.ToString())
SB.Append("\n\tDigitized: " & Me.DateTimeDigitized.ToString())
SB.Append("\nShooting conditions:")
SB.Append("\n\tExposure time: " & Me.ExposureTime.ToString("N4") & " s")
SB.Append("\n\tExposure program: " & [Enum].GetName(GetType(ExposurePrograms), Me.ExposureProgram))
SB.Append("\n\tExposure mode: " & [Enum].GetName(GetType(ExposureMeteringModes), Me.ExposureMeteringMode))
SB.Append("\n\tAperture: F" & Me.Aperture.ToString("N2"))
SB.Append("\n\tISO sensitivity: " & Me.ISO)
SB.Append("\n\tSubject distance: " & Me.SubjectDistance.ToString("N2") & " m")
SB.Append("\n\tFocal length: " & Me.FocalLength)
SB.Append("\n\tFlash: " & [Enum].GetName(GetType(FlashModes), Me.FlashMode))
SB.Append("\n\tLight source (WB): " & [Enum].GetName(GetType(LightSources), Me.LightSource))
SB.Replace("\n", vbCrLf)
SB.Replace("\t", vbTab)
Return SB.ToString()
End Function
#Region " Nicely formatted well-known properties "
''' <summary>
''' Brand of equipment (EXIF EquipMake)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property EquipmentMaker() As String
Get
Return Me.GetPropertyString(TagNames.EquipMake)
End Get
End Property
''' <summary>
''' Model of equipment (EXIF EquipModel)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property EquipmentModel() As String
Get
Return Me.GetPropertyString(TagNames.EquipModel)
End Get
End Property
''' <summary>
''' Software used for processing (EXIF Software)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property Software() As String
Get
Return Me.GetPropertyString(TagNames.SoftwareUsed)
End Get
End Property
''' <summary>
''' Orientation of image (position of row 0, column 0) (EXIF Orientation)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property Orientation() As Orientations
Get
Dim X As Int32 = Me.GetPropertyInt16(TagNames.Orientation)
If Not [Enum].IsDefined(GetType(Orientations), X) Then
Return Orientations.TopLeft
Else
Return CType([Enum].Parse(GetType(Orientations), [Enum].GetName(GetType(Orientations), X)), Orientations)
End If
End Get
End Property
''' <summary>
''' Time when image was last modified (EXIF DateTime).
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Property DateTimeLastModified() As DateTime
Get
Try
Return DateTime.ParseExact(Me.GetPropertyString(TagNames.DateTime), "yyyy\:MM\:dd HH\:mm\:ss", Nothing)
Catch ex As Exception
Return DateTime.MinValue
End Try
End Get
Set(ByVal Value As DateTime)
Try
Me.SetPropertyString(TagNames.DateTime, Value.ToString("yyyy\:MM\:dd HH\:mm\:ss"))
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Time when image was taken (EXIF DateTimeOriginal).
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Property DateTimeOriginal() As DateTime
Get
Try
Return DateTime.ParseExact(Me.GetPropertyString(TagNames.ExifDTOrig), "yyyy\:MM\:dd HH\:mm\:ss", Nothing)
Catch ex As Exception
Return DateTime.MinValue
End Try
End Get
Set(ByVal Value As DateTime)
Try
Me.SetPropertyString(TagNames.ExifDTOrig, Value.ToString("yyyy\:MM\:dd HH\:mm\:ss"))
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Time when image was digitized (EXIF DateTimeDigitized).
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Property DateTimeDigitized() As DateTime
Get
Try
Return DateTime.ParseExact(Me.GetPropertyString(TagNames.ExifDTDigitized), "yyyy\:MM\:dd HH\:mm\:ss", Nothing)
Catch ex As Exception
Return DateTime.MinValue
End Try
End Get
Set(ByVal Value As DateTime)
Try
Me.SetPropertyString(TagNames.ExifDTDigitized, Value.ToString("yyyy\:MM\:dd HH\:mm\:ss"))
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Image width
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 04.09.2005 Changed output to Int32, load from image instead of EXIF
''' </history>
Public ReadOnly Property Width() As Int32
Get
Return Me._Image.Width
End Get
End Property
''' <summary>
''' Image height
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 04.09.2005 Changed output to Int32, load from image instead of EXIF
''' </history>
Public ReadOnly Property Height() As Int32
Get
Return Me._Image.Height
End Get
End Property
''' <summary>
''' X resolution in dpi (EXIF XResolution/ResolutionUnit)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property ResolutionX() As Double
Get
Dim R As Double = Me.GetPropertyRational(TagNames.XResolution).ToDouble()
If Me.GetPropertyInt16(TagNames.ResolutionUnit) = 3 Then
'-- resolution is in points/cm
Return R * 2.54
Else
'-- resolution is in points/inch
Return R
End If
End Get
End Property
''' <summary>
''' Y resolution in dpi (EXIF YResolution/ResolutionUnit)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property ResolutionY() As Double
Get
Dim R As Double = Me.GetPropertyRational(TagNames.YResolution).ToDouble()
If Me.GetPropertyInt16(TagNames.ResolutionUnit) = 3 Then
'-- resolution is in points/cm
Return R * 2.54
Else
'-- resolution is in points/inch
Return R
End If
End Get
End Property
''' <summary>
''' Image title (EXIF ImageTitle)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Property Title() As String
Get
Return Me.GetPropertyString(TagNames.ImageTitle)
End Get
Set(ByVal Value As String)
Try
Me.SetPropertyString(TagNames.ImageTitle, Value)
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' User comment (EXIF UserComment)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 13.06.2004 Created
''' </history>
Public Property UserComment() As String
Get
Return Me.GetPropertyString(TagNames.ExifUserComment)
End Get
Set(ByVal Value As String)
Try
Me.SetPropertyString(TagNames.ExifUserComment, Value)
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Artist name (EXIF Artist)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 13.06.2004 Created
''' </history>
Public Property Artist() As String
Get
Return Me.GetPropertyString(TagNames.Artist)
End Get
Set(ByVal Value As String)
Try
Me.SetPropertyString(TagNames.Artist, Value)
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Image description (EXIF ImageDescription)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Property Description() As String
Get
Return Me.GetPropertyString(TagNames.ImageDescription)
End Get
Set(ByVal Value As String)
Try
Me.SetPropertyString(TagNames.ImageDescription, Value)
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Image copyright (EXIF Copyright)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Property Copyright() As String
Get
Return Me.GetPropertyString(TagNames.Copyright)
End Get
Set(ByVal Value As String)
Try
Me.SetPropertyString(TagNames.Copyright, Value.ToString)
Catch ex As Exception
End Try
End Set
End Property
''' <summary>
''' Exposure time in seconds (EXIF ExifExposureTime/ExifShutterSpeed)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property ExposureTime() As Double
Get
If Me.IsPropertyDefined(TagNames.ExifExposureTime) Then
'-- Exposure time is explicitly specified
Return Me.GetPropertyRational(TagNames.ExifExposureTime).ToDouble
ElseIf Me.IsPropertyDefined(TagNames.ExifShutterSpeed) Then
'-- Compute exposure time from shutter speed
Return 1 / (2 ^ Me.GetPropertyRational(TagNames.ExifShutterSpeed).ToDouble)
Else
'-- Can't figure out
Return 0
End If
End Get
End Property
''' <summary>
''' Aperture value as F number (EXIF ExifFNumber/ExifApertureValue)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property Aperture() As Double
Get
If Me.IsPropertyDefined(TagNames.ExifFNumber) Then
Return Me.GetPropertyRational(TagNames.ExifFNumber).ToDouble()
ElseIf Me.IsPropertyDefined(TagNames.ExifAperture) Then
Return System.Math.Sqrt(2) ^ Me.GetPropertyRational(TagNames.ExifAperture).ToDouble()
Else
Return 0
End If
End Get
End Property
''' <summary>
''' Exposure program used (EXIF ExifExposureProg)
''' </summary>
''' <value></value>
''' <remarks>If not specified, returns Normal (2)</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property ExposureProgram() As ExposurePrograms
Get
Dim X As Int32 = Me.GetPropertyInt16(TagNames.ExifExposureProg)
If [Enum].IsDefined(GetType(ExposurePrograms), X) Then
Return CType([Enum].Parse(GetType(ExposurePrograms), [Enum].GetName(GetType(ExposurePrograms), X)), ExposurePrograms)
Else
Return ExposurePrograms.Normal
End If
End Get
End Property
''' <summary>
''' ISO sensitivity
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property ISO() As Int16
Get
Return Me.GetPropertyInt16(TagNames.ExifISOSpeed)
End Get
End Property
''' <summary>
''' Subject distance in meters (EXIF SubjectDistance)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property SubjectDistance() As Double
Get
Return Me.GetPropertyRational(TagNames.ExifSubjectDist).ToDouble()
End Get
End Property
''' <summary>
''' Exposure method metering mode used (EXIF MeteringMode)
''' </summary>
''' <value></value>
''' <remarks>If not specified, returns Unknown (0)</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property ExposureMeteringMode() As ExposureMeteringModes
Get
Dim X As Int32 = Me.GetPropertyInt16(TagNames.ExifMeteringMode)
If [Enum].IsDefined(GetType(ExposureMeteringModes), X) Then
Return CType([Enum].Parse(GetType(ExposureMeteringModes), [Enum].GetName(GetType(ExposureMeteringModes), X)), ExposureMeteringModes)
Else
Return ExposureMeteringModes.Unknown
End If
End Get
End Property
''' <summary>
''' Focal length of lenses in mm (EXIF FocalLength)
''' </summary>
''' <value></value>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property FocalLength() As Double
Get
Return Me.GetPropertyRational(TagNames.ExifFocalLength).ToDouble
End Get
End Property
''' <summary>
''' Flash mode (EXIF Flash)
''' </summary>
''' <value></value>
''' <remarks>If not present, value NotFired (0) is returned</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property FlashMode() As FlashModes
Get
Dim X As Int32 = Me.GetPropertyInt16(TagNames.ExifFlash)
If [Enum].IsDefined(GetType(FlashModes), X) Then
Return CType([Enum].Parse(GetType(FlashModes), [Enum].GetName(GetType(FlashModes), X)), FlashModes)
Else
Return FlashModes.NotFired
End If
End Get
End Property
''' <summary>
''' Light source / white balance (EXIF LightSource)
''' </summary>
''' <value></value>
''' <remarks>If not specified, returns Unknown (0).</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public ReadOnly Property LightSource() As LightSources
Get
Dim X As Int32 = Me.GetPropertyInt16(TagNames.ExifLightSource)
If [Enum].IsDefined(GetType(LightSources), X) Then
Return CType([Enum].Parse(GetType(LightSources), [Enum].GetName(GetType(LightSources), X)), LightSources)
Else
Return LightSources.Unknown
End If
End Get
End Property
#End Region
#Region " Support methods for working with EXIF properties "
''' <summary>
''' Checks if current image has specified certain property
''' </summary>
''' <param name="PID"></param>
''' <returns>True if image has specified property, False otherwise.</returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Function IsPropertyDefined(ByVal PID As Int32) As Boolean
Return CBool([Array].IndexOf(Me._Image.PropertyIdList, PID) > -1)
End Function
''' <summary>
''' Gets specified Int32 property
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="DefaultValue">Optional, default 0. Default value returned if property is not present.</param>
''' <remarks>Value of property or DefaultValue if property is not present.</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Function GetPropertyInt32(ByVal PID As Int32, Optional ByVal DefaultValue As Int32 = 0) As Int32
If Me.IsPropertyDefined(PID) Then
Return GetInt32(Me._Image.GetPropertyItem(PID).Value)
Else
Return DefaultValue
End If
End Function
''' <summary>
''' Gets specified Int16 property
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="DefaultValue">Optional, default 0. Default value returned if property is not present.</param>
''' <remarks>Value of property or DefaultValue if property is not present.</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Function GetPropertyInt16(ByVal PID As Int32, Optional ByVal DefaultValue As Int16 = 0) As Int16
If Me.IsPropertyDefined(PID) Then
Return GetInt16(Me._Image.GetPropertyItem(PID).Value)
Else
Return DefaultValue
End If
End Function
''' <summary>
''' Gets specified string property
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="DefaultValue">Optional, default String.Empty. Default value returned if property is not present.</param>
''' <returns></returns>
''' <remarks>Value of property or DefaultValue if property is not present.</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Function GetPropertyString(ByVal PID As Int32, Optional ByVal DefaultValue As String = "") As String
If Me.IsPropertyDefined(PID) Then
Return GetString(Me._Image.GetPropertyItem(PID).Value)
Else
Return DefaultValue
End If
End Function
''' <summary>
''' Gets specified property in raw form
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="DefaultValue">Optional, default Nothing. Default value returned if property is not present.</param>
''' <returns></returns>
''' <remarks>Is recommended to use typed methods (like <see cref="GetPropertyString" /> etc.) instead, when possible.</remarks>
''' <history>
''' [altair] 05.09.2005 Created
''' </history>
Public Function GetProperty(ByVal PID As Int32, Optional ByVal DefaultValue As Byte() = Nothing) As Byte()
If Me.IsPropertyDefined(PID) Then
Return Me._Image.GetPropertyItem(PID).Value
Else
Return DefaultValue
End If
End Function
''' <summary>
''' Gets specified rational property
''' </summary>
''' <param name="PID">Property ID</param>
''' <returns></returns>
''' <remarks>Value of property or 0/1 if not present.</remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Function GetPropertyRational(ByVal PID As Int32) As Rational
If Me.IsPropertyDefined(PID) Then
Return GetRational(Me._Image.GetPropertyItem(PID).Value)
Else
Dim R As Rational
R.Numerator = 0
R.Denominator = 1
Return R
End If
End Function
''' <summary>
''' Sets specified string property
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="Value">Value to be set</param>
''' <remarks></remarks>
''' <history>
''' [altair] 12.6.2004 Created
''' </history>
Public Sub SetPropertyString(ByVal PID As Int32, ByVal Value As String)
Dim Data() As Byte = Me._Encoding.GetBytes(Value & vbNullChar)
SetProperty(PID, Data, ExifDataTypes.AsciiString)
End Sub
''' <summary>
''' Sets specified Int16 property
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="Value">Value to be set</param>
''' <remarks></remarks>
''' <history>
''' [altair] 12.6.2004 Created
''' </history>
Public Sub SetPropertyInt16(ByVal PID As Int32, ByVal Value As Int16)
Dim Data(1) As Byte
Data(0) = CType(Value And &HFF, Byte)
Data(1) = CType((Value And &HFF00) >> 8, Byte)
SetProperty(PID, Data, ExifDataTypes.SignedShort)
End Sub
''' <summary>
''' Sets specified Int32 property
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="Value">Value to be set</param>
''' <remarks></remarks>
''' <history>
''' [altair] 13.06.2004 Created
''' </history>
Public Sub SetPropertyInt32(ByVal PID As Int32, ByVal Value As Int32)
Dim Data(3) As Byte
For I As Int32 = 0 To 3
Data(I) = CType(Value And &HFF, Byte)
Value >>= 8
Next
SetProperty(PID, Data, ExifDataTypes.SignedLong)
End Sub
''' <summary>
''' Sets specified propery in raw form
''' </summary>
''' <param name="PID">Property ID</param>
''' <param name="Data">Raw data</param>
''' <param name="Type">EXIF data type</param>
''' <remarks>Is recommended to use typed methods (like <see cref="SetPropertyString" /> etc.) instead, when possible.</remarks>
''' <history>
''' [altair] 12.6.2004 Created
''' </history>
Public Sub SetProperty(ByVal PID As Int32, ByVal Data() As Byte, ByVal Type As ExifDataTypes)
Dim P As System.Drawing.Imaging.PropertyItem = Me._Image.PropertyItems(0)
P.Id = PID
P.Value = Data
P.Type = Type
P.Len = Data.Length
Me._Image.SetPropertyItem(P)
End Sub
''' <summary>
''' Reads Int32 from EXIF bytearray.
''' </summary>
''' <param name="B">EXIF bytearray to process</param>
''' <returns></returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 05.09.2005 Changed from public shared to private instance method
''' </history>
Private Function GetInt32(ByVal B As Byte()) As Int32
If B.Length < 4 Then Throw New ArgumentException("Data too short (4 bytes expected)", "B")
Return B(3) << 24 Or B(2) << 16 Or B(1) << 8 Or B(0)
End Function
''' <summary>
''' Reads Int16 from EXIF bytearray.
''' </summary>
''' <param name="B">EXIF bytearray to process</param>
''' <returns></returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 05.09.2005 Changed from public shared to private instance method
''' </history>
Private Function GetInt16(ByVal B As Byte()) As Int16
If B.Length < 2 Then Throw New ArgumentException("Data too short (2 bytes expected)", "B")
Return B(1) << 8 Or B(0)
End Function
''' <summary>
''' Reads string from EXIF bytearray.
''' </summary>
''' <param name="B">EXIF bytearray to process</param>
''' <returns></returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 05.09.2005 Changed from public shared to private instance method
''' </history>
Private Function GetString(ByVal B As Byte()) As String
Dim R As String = Me._Encoding.GetString(B)
If R.EndsWith(vbNullChar) Then R = R.Substring(0, R.Length - 1)
Return R
End Function
''' <summary>
''' Reads rational from EXIF bytearray.
''' </summary>
''' <param name="B">EXIF bytearray to process</param>
''' <returns></returns>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' [altair] 05.09.2005 Changed from public shared to private instance method
''' </history>
Private Function GetRational(ByVal B As Byte()) As Rational
Dim R As New Rational, N(3), D(3) As Byte
Array.Copy(B, 0, N, 0, 4)
Array.Copy(B, 4, D, 0, 4)
R.Denominator = Me.GetInt32(D)
R.Numerator = Me.GetInt32(N)
Return R
End Function
#End Region
#Region " IDisposable implementation "
''' <summary>
''' Disposes unmanaged resources of this class
''' </summary>
''' <remarks></remarks>
''' <history>
''' [altair] 10.09.2003 Created
''' </history>
Public Sub Dispose() Implements System.IDisposable.Dispose
Me._Image.Dispose()
End Sub
#End Region
End Class