lazarus-ccr/components/fpexif/fpemakernotenikon.pas
2020-02-17 18:52:45 +00:00

517 lines
19 KiB
ObjectPascal

unit fpeMakerNoteNikon;
{$IFDEF FPC}
{$MODE DELPHI}
//{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
Classes, SysUtils,
fpeGlobal, fpeTags, fpeExifReadWrite;
type
TNikonMakerNoteReader = class(TMakerNoteReader)
protected
function AddTag(AStream: TStream; const AIFDRecord: TIFDRecord;
const AData: TBytes; AParent: TTagID): Integer; override;
procedure GetTagDefs(AStream: TStream); override;
function Prepare(AStream: TStream): Boolean; override;
end;
TNikonCropHiSpeedTag = class(TIntegerTag)
public
function GetAsString: String; override;
end;
TNikonLensTypeTag = class(TIntegerTag)
public
function GetAsString: string; override;
end;
TNikonLensTag = class(TFloatTag)
public
function GetAsString: string; override;
end;
TNikonShootingModeTag = class(TIntegerTag)
public
function GetAsString: String; override;
end;
TNikonNEFBitDepthTag = class(TIntegerTag)
public
function GetAsString: String; override;
end;
implementation
uses
Math,
fpeStrConsts, fpeUtils, fpeExifData;
resourcestring
rsNikonActiveDLightingLkUp = '0:Off,1:Low,3:Normal,5:High,7:Extra High,'+
'8:Extra High 1,9:Extra High 2,10:Extra High 3,11:Extra High 4,65535:Auto';
rsNikonAFAreaMode = '0:Single Area,1:Dynamic Area,2:Dynamic Area (closest subject),'+
'3:Group Dynamic,4:Single Area (wide),5:Dynamic Area (wide)';
rsNikonAFPoint = '0:Center,1:Top,2:Bottom,3:Mid-left,4:Mid-right,5:Upper-left,'+
'6:Upper-right,7:Lower-left,8:Lower-right,9:Far Left,10:Far Right';
rsNikonAFPointsInFocus = '0:(none),$7FF:All 11 points,1:Center,2:Top,4:Bottom,'+
'8:Mid-left,16:Mid-right,32:Upper-left,64:Upper-right,128:Lower-left,'+
'256:Lower-right,512:Far left,1024:Far right';
// To do: This is a bit-mask. Combinations should be calculated!
rsNikonColorModeLkup = '1:Color,2:Monochrome';
rsNikonColorSpaceLkup = '1:sRGB,2:Adobe RGB';
rsNikonConverterLkup = '0:Not used,1:Used';
rsNikonCropHiSpeedLkup = '0:Off,1:1.3x Crop,2:DX Crop,3:5/4 Crop,4:3/2 Crop,'+
'6:16/9 Crop,9:DX Movie Crop,11:FX Uncropped,12:DX Uncropped,17:1/1 Crop';
rsNikonCropHiSpeedMask = '%0:s (%1:dx%2:d cropped to %3:dx%4:d at pixel %5:d,%6:d)';
rsNikonDateDisplayFormat = '0:Y/M/D,1:M/D/Y,2:D/M/Y';
rsNikonFlashModeLkUp = '0:Did Not Fire,1:Fired, Manual,3:Not Ready,'+
'7:Fired External,8:Fired Commander Mode,9:Fired TTL Mode';
rsNikonHighISONoiseReductionLkUp = '0:Off,1:Minimal,2:Low,3:Medium Low,'+
'4:Normal,5:Medium High,6:High';
rsNikonImgAdjLkup = '0:Normal,1:Bright+,2:Bright-,3:Contrast+,4:Contrast-';
rsNikonISOLkup = '0:ISO80,2:ISO160,4:ISO320,5:ISO100';
rsNikonNEFCompressionLkUp = '1:Lossy (type 1),2: Uncompressed,3:Lossless,'+
'4:Lossy (type 2),5:Striped packed 12 bits,6:Uncompressed (reduced to 12 bit),'+
'7:Unpacked 12 bits,8:Small,9:Packed 12 bits';
rsNikonOffOn='-1:Off,1:On';
rsNikonQualityLkup = '1:Vga Basic,2:Vga Normal,3:Vga Fine,4:SXGA Basic,'+
'5:SXGA Normal,6:SXGA Fine,10:2 Mpixel Basic,11:2 Mpixel Normal,'+
'12:2 Mpixel Fine';
rsNikonRetoucheHistoryLkup = '0:None,3:B & W,4:Sepia,5:Trim,6:Small Picture,'+
'7:D-Lighting,8:Red Eye,9:Cyanotype,10:Sky Light,11:Warm Tone,'+
'12:Color Custom,13:Image Overlay,14:Red Intensifier,15:Green Intensifier,'+
'16:Blue Intensifier,17:Cross Screen,18:Quick Retouch,19:NEF Processing,'+
'23:Distortion Control,25:Fisheye,26:Straighten,29:Perspective Control,'+
'30:Color Outline,31:Soft Filter,32:Resize,33: Miniature Effect,'+
'34:Skin Softening,35:Selected Frame,37:Color Sketch,38:Selective Color,'+
'39:Glamour,40:Drawing,44:Pop,45:Toy Camera Effect 1,46:Toy Camera Effect 2,'+
'47:Cross Process (red),48:Cross Process (blue),49:Cross Process (green),'+
'50:Cross Process (yellow),51:Super Vivid,52:High-contrast Monochrome,'+
'53:High Key,54:Low Key';
rsNikonShutterModeLkUp = '0:Mechanical,16:Electronic,48:Electronic Front Curtain';
rsNikonVignetteControlLkUp = '0:Off,1:Low,3:Normal,5:High';
rsNikonVibrationReductionLkUp = '0:n/a,1:On,2:Off';
rsNikonVRModeLkUp = '0:Normal,1:On (1),2:Active,3:Sport';
rsNikonWhiteBalanceLkup = '0:Auto,1:Preset,2:Daylight,3:Incandescense,'+
'4:Fluorescence,5:Cloudy,6:SpeedLight';
const
M = DWord(TAGPARENT_MAKERNOTE);
// not tested
procedure BuildNikon1TagDefs(AList: TTagDefList);
begin
Assert(AList <> nil);
with AList do begin
AddUShortTag(M+$0002, 'FamilyID');
AddUShortTag(M+$0003, 'Quality', 1, '', rsNikonQualityLkup);
AddUShortTag(M+$0004, 'ColorMode', 1, '', rsNikonColorModeLkup);
AddUShortTag(M+$0005, 'ImageAdjustment', 1, '', rsNikonImgAdjLkup);
AddUShortTag(M+$0006, 'ISOSpeed', 1, '', rsNikonISOLkup);
AddUShortTag(M+$0007, 'WhiteBalance', 1, '', rsNikonWhiteBalanceLkup);
AddUShortTag(M+$0008, 'Focus');
AddUShortTag(M+$000A, 'DigitalZoom');
AddUShortTag(M+$000B, 'Converter', 1, '', rsNikonConverterLkup);
end;
end;
{ for Nikon D1, E880, E885, E990, E995, E2500, E5000
Ref http://www.tawbaware.com/990exif.htm
https://sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html }
procedure BuildNikon2TagDefs(AList: TTagDefList);
begin
Assert(AList <> nil);
with AList do begin
AddBinaryTag (M+$0001, 'Version', 4, '', '', '', TVersionTag);
AddUShortTag (M+$0002, 'ISO', 2);
AddStringTag (M+$0003, 'ColorMode');
AddStringTag (M+$0004, 'Quality');
AddStringTag (M+$0005, 'WhiteBalance');
AddStringtag (M+$0006, 'ImageSharpening');
AddStringTag (M+$0007, 'FocusMode');
AddStringTag (M+$0008, 'FlashSetting');
AddStringTag (M+$0009, 'FlashType');
AddURationalTag(M+$000A, 'UNKNOWN');
AddStringTag (M+$000F, 'ISOSelection');
AddStringTag (M+$0080, 'ImageAdjustment');
AddStringTag (M+$0081, 'ToneComp');
AddStringTag (M+$0082, 'AuxiliaryLens');
AddURationalTag(M+$0085, 'ManualFocusDistance');
AddURationalTag(M+$0086, 'DigitalZoom');
AddBinaryTag (M+$0088, 'AFInfo');
AddStringTag (M+$008D, 'ColorHue');
AddStringTag (M+$008F, 'SceneMode');
AddStringTag (M+$0090, 'LightSource');
AddBinaryTag (M+$0010, 'DataDump');
end;
end;
// Ref.: https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
procedure BuildNikon3TagDefs(AList: TTagDefList);
begin
Assert(AList <> nil);
with AList do begin
// Tags in main MakerNote IFD
AddBinaryTag (M+$0001, 'Version', 4, '', '', '', TVersionTag);
AddUShortTag (M+$0002, 'ISO', 2);
AddStringTag (M+$0003, 'ColorMode');
AddStringTag (M+$0004, 'Quality');
AddStringTag (M+$0005, 'WhiteBalance');
AddStringtag (M+$0006, 'Sharpness');
AddStringTag (M+$0007, 'FocusMode');
AddStringTag (M+$0008, 'FlashSetting');
AddStringTag (M+$0009, 'FlashType');
AddURationalTag(M+$000A, 'UNKNOWN');
AddSShortTag (M+$000B, 'WhiteBalanceFineTune');
AddURationalTag(M+$000C, 'WB_RBLevels', 4);
AddBinaryTag (M+$000D, 'ProgramShift', 1);
AddBinaryTag (M+$000E, 'ExposureDifference', 1);
AddBinaryTag (M+$000F, 'ISOSelection');
AddBinaryTag (M+$0010, 'DataDump');
// ...
AddBinaryTag (M+$0012, 'FlashExposureComp', 4);
AddUShortTag (M+$0013, 'ISO Setting', 2);
AddUShortTag (M+$0016, 'ImageBoundary', 4);
AddBinaryTag (M+$0017, 'ExternalFlashExposureComp', 4);
AddBinaryTag (M+$0018, 'FlashExposureBracketValue', 4);
AddSRationalTag(M+$0019, 'ExposureBracketValue');
AddStringTag (M+$001A, 'ImageProcessing');
AddUShortTag (M+$001B, 'CropHiSpeed', 7, '', rsNikonCropHiSpeedLkUp, '', TNikonCropHiSpeedTag);
AddBinaryTag (M+$001C, 'ExposureTuning', 3);
AddStringTag (M+$001D, 'SerialNumber', 1, '', '', nil, true);
AddUShortTag (M+$001E, 'ColorSpace', 1, '', rsNikonColorSpaceLkUp);
AddBinaryTag (M+$001F, 'VRInfo', 8);
AddByteTag (M+$0020, 'ImageAuthentication', 1, '', rsOffOn);
// 21
AddUShortTag (M+$0022, 'ActiveD-Lighting', 1, '', rsNikonActiveDLightingLkUp);
AddBinaryTag (M+$0024, 'WorldTime');
//...
AddUShortTag (M+$002A, 'VignetteControl', 1, '', rsNikonVignetteControlLkUp);
//...
AddUShortTag (M+$0034, 'ShutterMode', 1, '', '', rsNikonShutterModeLkUp);
//..
AddULongTag (M+$0037, 'MechanicalShutterCount');
AddBinaryTag (M+$0039, 'LocationInfo');
//..
AddUShortTag (M+$003D, 'BlackLevel', 4);
AddUShortTag (M+$004F, 'ColorTemperatureAuto');
AddStringTag (M+$0080, 'ImageAdjustment');
AddStringTag (M+$0081, 'ToneComp');
AddStringTag (M+$0082, 'AuxiliaryLens');
AddByteTag (M+$0083, 'LensType', 1, '', '', '', TNikonLensTypeTag);
AddURationalTag(M+$0084, 'Lens', 4, '', '', '', TNikonLensTag);
AddURationalTag(M+$0085, 'ManualFocusDistance');
AddURationalTag(M+$0086, 'DigitalZoom');
AddByteTag (M+$0087, 'FlashMode', 1, '', rsNikonFlashModeLkUp);
AddBinaryTag (M+$0088, 'AFInfo', 4);
// ..
AddUShortTag (M+$0089, 'ShootingMode', 1, '', '', '', TNikonShootingModeTag);
AddBinaryTag (M+$008B, 'LensFStops', 4);
AddBinaryTag (M+$008C, 'ContrastCurve');
AddStringTag (M+$008D, 'ColorHude');
AddStringTag (M+$008F, 'SceneMode');
AddStringTag (M+$0090, 'LightSource');
AddSShortTag (M+$0092, 'HueAdjustment');
AddUShortTag (M+$0093, 'NEFCompression', 1, '', rsNikonNEFCompressionLkUp);
AddSShortTag (M+$0094, 'Saturation');
AddStringTag (M+$0095, 'NoiseReduction');
AddBinaryTag (M+$0097, 'NEFLinearizationTable');
AddUShortTag (M+$0099, 'RawImageCenter', 2);
AddURationalTag(M+$009A, 'SensorPixelSize', 2);
AddStringTag (M+$009C, 'SceneAssist');
AddUShortTag (M+$009E, 'RetoucheHistory', 10, '', rsNikonRetoucheHistoryLkup);
AddStringTag (M+$00A0, 'SerialNumber');
AddULongTag (M+$00A2, 'ImageDataSize');
AddULongTag (M+$00A5, 'ImageCount');
AddULongTag (M+$00A6, 'DeletedImageCount');
AddULongTag (M+$00A7, 'ShutterCount', 1, '', '', '', nil, true);
AddStringTag (M+$00A9, 'ImageOptimization');
AddStringTag (M+$00AA, 'Saturation');
AddStringTag (M+$00AB, 'VariProgram');
AddStringTag (M+$00AC, 'ImageStabilization');
AddStringTag (M+$00AD, 'AFResponse');
AddUShortTag (M+$00B1, 'HighISONoiseReduction', 1, '', rsNikonHighISONoiseReductionLkup);
AddStringTag (M+$00B3, 'ToningEffect');
AddBinaryTag (M+$00B6, 'PowerUpTime');
// ...
AddBinaryTag (M+$00B8, 'FileInfo');
AddBinaryTag (M+$00BB, 'RetoucheInfo');
AddBinaryTag (M+$00C3, 'BarometerInfo');
AddStringTag (M+$0E09, 'NikonCaptureVersion');
// ...
AddUShortTag (M+$0E22, 'NEFBitDepth', 4, '', '', '', TNikonNEFBitDepthTag);
// AddBinaryTag (M+$0103, 'CompressionValue', 1, '', rsCompressionLkUp);
end;
end;
//==============================================================================
// MakerNote reader
//==============================================================================
function TNikonMakerNoteReader.AddTag(AStream: TStream;
const AIFDRecord: TIFDRecord; const AData: TBytes; AParent: TTagID): Integer;
var
tagDef: TTagDef;
t: TTagID;
idx: Integer;
b: Byte;
w: Word;
r: TExifRational;
begin
Result := -1;
tagDef := FindTagDef(AIFDRecord.TagID or AParent);
if (tagDef = nil) then
exit;
Result := inherited AddTag(AStream, AIFDRecord, AData, AParent);
t := tagDef.TagID;
case tagDef.TagID of
TAGPARENT_MAKERNOTE+$001F: // VR info
if Length(AData) >= 8 then
with FImgInfo.ExifData do begin
AddMakerNoteStringTag(0, t, 'VRInfoVersion', AData, 4, '');
AddMakerNoteTag(4, t, 'VibrationReduction', AData[4], rsNikonVibrationReductionLkUp, '', ttUInt8);
AddMakerNoteTag(6, t, 'VRMode', AData[6], rsNikonVRModeLkUp, '', ttUInt8);
end;
TAGPARENT_MAKERNOTE+$0088:
if Length(AData) >= 4 then // AF Info
with FImgInfo.ExifData do begin
b := AData[0];
AddMakerNoteTag(0, t, 'AFAreaMode', b, rsNikonAFAreaMode, '', ttUInt8);
b := AData[1];
AddMakerNoteTag(1, t, 'AFPoint', b, rsNikonAFPoint, '', ttUInt8);
w := FixEndian16(PWord(@AData[2])^);
AddmakerNoteTag(2, t, 'AFPointsInFocus', w, rsNikonAFPointsInFocus, '', ttUInt16);
end;
TAGPARENT_MAKERNOTE+$0024:
if Length(AData) >= 4 then // WorldTime
with FImgInfo.ExifData do begin
w := FixEndian16(PWord(@AData[0])^);
AddMakerNoteTag(0, t, 'TimeZone', Integer(w), '', '', ttSInt16);
AddMakerNoteTag(2, t, 'DaylightSavings', byte(AData[2]), rsNoYes, '', ttUInt8);
AddMakerNoteTag(3, t, 'DateDisplayFormat', byte(AData[3]), rsNikonDateDisplayformat, '', ttUInt8);
end;
TAGPARENT_MAKERNOTE+$00B8:
if Length(AData) >= 8 then
with FImgInfo.ExifData do begin
idx := 0;
AddMakerNoteStringTag(idx, t, 'FileInfoVersion', AData, 4);
inc(idx, 4);
w := FixEndian16(PWord(@AData[idx])^);
AddMakerNoteTag(idx, t, 'MemoryCardNumber', Integer(w), '', '', ttUInt16);
inc(idx, 2);
w := FixEndian16(PWord(@AData[idx])^);
AddMakerNoteTag(idx, t, 'DirectoryNumber', Integer(w), '', '', ttUInt16);
inc(idx, 2);
w := FixEndian16(PWord(@AData[idx])^);
AddMakerNoteTag(idx, t, 'FileNumber', Integer(w), '', '', ttUInt16);
end;
TAGPARENT_MAKERNOTE+$BB:
if Length(ADAta) >= 6 then
with FImgInfo.ExifData do begin
AddMakerNoteStringTag(0, t, 'RetouchInfoVersion', AData, 5);
AddMakerNoteTag(5, t, 'RetouchNEFProcessing', byte(AData[5]), '', rsNikonOffOn, ttSInt8);
end;
TAGPARENT_MAKERNOTE+$00C3:
if Length(AData) >= 10 then
with FImgInfo.ExifData do begin
idx := 0;
AddMakerNoteStringTag(idx, t, 'BarometerInfoVersion', AData, 6);
inc(idx, 6);
Move(AData[idx], r{%H-}, SizeOf(r));
r.Numerator := FixEndian32(r.Numerator);
r.Denominator := FixEndian32(r.Denominator);
AddMakerNoteTag(idx, t, 'Altitude', r.Numerator/r.Denominator, '', ttSRational);
end;
end;
end;
procedure TNikonMakerNoteReader.GetTagDefs(AStream: TStream);
var
b: TBytes; //array of byte;
tmp, tmp2: String;
p: Integer;
streamPos: Int64;
begin
if Uppercase(FMake) = 'NIKON CORPORATION' then begin
SetLength(b, 20);
streamPos := AStream.Position;
AStream.Read(b[0], 20);
AStream.Position := streamPos;
SetLength(tmp, 5);
Move(b[0], tmp[1], 5);
if (PosInBytes('Nikon'#00, b) = 0) and (
(PosInBytes('MM'#00#42#00#00#00#08, b) = 10) or
(PosInBytes('II'#42#00#08#00#00#00, b) = 10)
)
then
BuildNikon3TagDefs(FTagDefs)
else begin
p := Max(0, Pos(' ', FModel));
tmp2 := FModel[p+1];
if (FExifVersion > '0210') or
((FExifVersion = '') and (tmp2 = 'D') and (FImgFormat = ifTiff))
then
BuildNikon2TagDefs(FTagDefs)
else
if (tmp = 'Nikon') then
BuildNikon1TagDefs(FTagDefs)
else
BuildNikon2TagDefs(FTagDefs);
end;
end;
end;
function TNikonMakerNoteReader.Prepare(AStream: TStream): Boolean;
var
b: TBytes;
UCMake: String;
dw: DWord;
begin
Result := false;
UCMake := Uppercase(FMake);
if UCMake = 'NIKON CORPORATION' then begin
SetLength(b, 10);
AStream.Read(b[0], 10);
if PosInBytes('Nikon'#0{#$10#00#00}, b) = 0 then begin
// These MakerNotes are relative to the beginning of the MakerNote's
// TIFF header!
FStartPosition := AStream.Position;
AStream.Read(b[0], 4);
if PosInBytes('MM'#00#42, b) = 0 then
FBigEndian := true
else
if PosInBytes('II'#42#00, b) = 0 then
FBigEndian := false
else
exit;
dw := ReadDWord(AStream);
// dw := AStream.ReadDWord;
if FBigEndian then dw := BEToN(dw) else dw := LEToN(dw);
if dw = 8 then
Result := true;
// The stream is now at the beginning of the IFD structure used by
// the Nikon maker notes.
end;
end;
end;
//==============================================================================
// Special tags
//==============================================================================
function TNikonCropHiSpeedTag.GetAsString: String;
var
s: String;
intVal: TExifIntegerArray;
begin
if (FCount = 7) and (toDecodeValue in FOptions) then begin
intVal := AsIntegerArray;
s := Lookup(IntToStr(intVal[0]), FLkupTbl, @SameIntegerFunc);
Result := Format(rsNikonCropHiSpeedMask, [
s, intval[1], intVal[2], intVal[3], intVal[4], intVal[5], intVal[6]
]);
end else
Result := inherited;
end;
function TNikonLensTypeTag.GetAsString: String;
var
intVal: Integer;
begin
if (toDecodeValue in FOptions) then begin
Result := '';
intVal := AsInteger;
if intVal and 1 <> 0 then Result := Result + 'MF+';
if intVal and 2 <> 0 then Result := Result + 'D+';
if intVal and 4 <> 0 then Result := Result + 'G+';
if intVal and 8 <> 0 then Result := Result + 'VR+';
if intval and 16 <> 0 then Result := Result + '1+';
if intVal and 32 <> 0 then Result := Result + 'E+';
if Result <> '' then SetLength(Result, Length(Result)-1);
end else
Result := inherited;
end;
function TNikonLensTag.GetAsString: String;
var
values: TExifDoubleArray;
begin
values := AsFloatArray;
if (toDecodeValue in FOptions) and (Length(values) = 4) then
Result := Format('%g-%gmm f/%g-%g', [values[0], values[1], values[2], values[3]], fpExifFmtSettings)
else
Result := inherited;
end;
function TNikonShootingModetag.GetAsString: String;
var
intVal: Integer;
begin
if (toDecodeValue in FOptions) then begin
intVal := AsInteger;
if intVal = 0 then
Result := 'Single frame'
else begin
Result := '';
if intVal and 1 <> 0 then Result := Result + 'Continuous, ';
if intVal and 2 <> 0 then Result := Result + 'Delay, ';
if intVal and 4 <> 0 then Result := Result + 'PC Control, ';
if intVal and 8 <> 0 then Result := Result + 'Self-timer, ';
if intval and 16 <> 0 then Result := Result + 'Exposure bracketing, ';
if intVal and 32 <> 0 then Result := Result + 'Auto ISO, ';
if intVal and 64 <> 0 then Result := Result + 'White-balance bracketing, ';
if intVal and 128 <> 0 then Result := Result + 'IR control, ';
if intVal and 256 <> 0 then Result := Result + 'D-Lighting bracketing, ';
if Result <> '' then SetLength(Result, Length(Result)-2);
end;
end else
Result := inherited;
end;
function TNikonNEFBitDepthTag.GetAsString: String;
var
iVal: TExifIntegerArray;
i: Integer;
n: Integer;
begin
if (FCount = 4) and (toDecodeValue in FOptions) then begin
iVal := AsIntegerArray;
if iVal[0] = 0 then
Result := 'n/a (JPEG)'
else begin
Result := intToStr(iVal[0]);
n := 1;
for i:= Succ(Low(iVal)) to High(iVal) do
if iVal[i] = 0 then
break
else if iVal[i] = iVal[0] then
inc(n)
else begin
Result := inherited;
exit;
end;
Result := Result + ' x ' + IntToStr(n);
end;
end else
Result := inherited;
end;
initialization
RegisterMakerNoteReader(TNikonMakerNoteReader, 'Nikon Corporation;Nikon', '');
end.