fpexif: Fix detection of EXIF structure if a JFIF extension APP0 marker segment is in file (JFXX). Extract JFXX thumbnail.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7194 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2019-11-21 15:11:12 +00:00
parent 8c301b7ce0
commit cd0aa90768
7 changed files with 73 additions and 24 deletions

View File

@ -179,17 +179,17 @@ begin
imgInfo := TImgInfo.Create; imgInfo := TImgInfo.Create;
try try
imgInfo.LoadFromFile(ShellTreeView.Path + Item.Caption); imgInfo.LoadFromFile(ShellTreeView.Path + Item.Caption);
if not (imgInfo.HasExif and imgInfo.HasThumbnail) then begin if not (imgInfo.HasThumbnail) then begin
ThumbImg.Picture.LoadFromFile(ShellTreeView.Path + Item.Caption); ThumbImg.Picture.LoadFromFile(ShellTreeView.Path + Item.Caption);
exit; exit;
end; end;
ms := TMemoryStream.Create; ms := TMemoryStream.Create;
try try
imgInfo.ExifData.SaveThumbnailToStream(ms); imgInfo.SaveThumbnailToStream(ms);
if ms.Size > 0 then begin if ms.Size > 0 then begin
ms.Position := 0; ms.Position := 0;
ThumbImg.Picture.LoadfromStream(ms); ThumbImg.Picture.LoadFromStream(ms);
end else end else
ThumbImg.Picture.Clear; ThumbImg.Picture.Clear;
finally finally

View File

@ -66,6 +66,11 @@
<Debugging> <Debugging>
<UseExternalDbgSyms Value="True"/> <UseExternalDbgSyms Value="True"/>
</Debugging> </Debugging>
<Options>
<Win32>
<GraphicApplication Value="True"/>
</Win32>
</Options>
</Linking> </Linking>
</CompilerOptions> </CompilerOptions>
<Debugging> <Debugging>

View File

@ -320,10 +320,10 @@ begin
end; end;
end; end;
if FImgInfo.HasThumbnail and Assigned(FImgInfo.ExifData) then begin if FImgInfo.HasThumbnail then begin
ms := TMemoryStream.Create; ms := TMemoryStream.Create;
try try
FImgInfo.ExifData.SaveThumbnailToStream(ms); FImgInfo.SaveThumbnailToStream(ms);
ms.Position := 0; ms.Position := 0;
PreviewImage.Picture.LoadFromStream(ms); PreviewImage.Picture.LoadFromStream(ms);
RotateBitmap(PreviewImage.Picture.Bitmap, FImageOrientation); RotateBitmap(PreviewImage.Picture.Bitmap, FImageOrientation);

View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<CONFIG> <CONFIG>
<ProjectOptions> <ProjectOptions>
<Version Value="11"/> <Version Value="12"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<General> <General>
<Flags>
<CompatibilityMode Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/> <SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="ExifSimpleDemo"/> <Title Value="ExifSimpleDemo"/>
<ResourceType Value="res"/> <ResourceType Value="res"/>
<UseXPManifest Value="True"/> <UseXPManifest Value="True"/>

View File

@ -130,7 +130,7 @@ object MainForm: TMainForm
Left = 312 Left = 312
Height = 19 Height = 19
Top = 10 Top = 10
Width = 128 Width = 127
BorderSpacing.Left = 8 BorderSpacing.Left = 8
Caption = 'Truncate binary tags' Caption = 'Truncate binary tags'
Checked = True Checked = True
@ -143,7 +143,7 @@ object MainForm: TMainForm
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = CbVerbosity AnchorSideTop.Control = CbVerbosity
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 448 Left = 447
Height = 19 Height = 19
Top = 10 Top = 10
Width = 123 Width = 123
@ -233,7 +233,7 @@ object MainForm: TMainForm
Left = 620 Left = 620
Height = 15 Height = 15
Top = 347 Top = 347
Width = 19 Width = 18
Anchors = [akLeft, akBottom] Anchors = [akLeft, akBottom]
BorderSpacing.Bottom = 4 BorderSpacing.Bottom = 4
Caption = 'Tag' Caption = 'Tag'

View File

@ -308,12 +308,16 @@ procedure TMainForm.LoadThumbnail;
var var
ms: TMemoryStream; ms: TMemoryStream;
begin begin
if (FImgInfo.ExifData = nil) or (not FImgInfo.Exifdata.HasThumbnail) then if not FImgInfo.HasThumbnail then
exit; exit;
//if (FImgInfo.ExifData = nil) or (not FImgInfo.Exifdata.HasThumbnail) then
// exit;
ms := TMemoryStream.Create; ms := TMemoryStream.Create;
try try
FImgInfo.ExifData.SaveThumbnailToStream(ms); FImgInfo.SaveThumbnailToStream(ms);
// FImgInfo.ExifData.SaveThumbnailToStream(ms);
ms.Position := 0; ms.Position := 0;
Thumbnail.Picture.LoadfromStream(ms); Thumbnail.Picture.LoadfromStream(ms);
finally finally

View File

@ -58,6 +58,7 @@ type
FWarnings: TStrings; FWarnings: TStrings;
FMetadataKinds: TMetadataKinds; FMetadataKinds: TMetadataKinds;
FHeaderSegment: TBytes; FHeaderSegment: TBytes;
FJFXXThumbnail: TBytes;
FComment: String; FComment: String;
private private
FExifData: TExifData; FExifData: TExifData;
@ -80,6 +81,7 @@ type
procedure LoadFromStream(AStream: TStream); procedure LoadFromStream(AStream: TStream);
procedure Save; procedure Save;
procedure SaveToFile(const AFileName: String; AImgFile: String = ''); procedure SaveToFile(const AFileName: String; AImgFile: String = '');
procedure SaveThumbnailToStream(AStream: TStream);
function CreateExifData(ABigEndian: Boolean = false): TExifData; function CreateExifData(ABigEndian: Boolean = false): TExifData;
function CreateIptcData: TIptcData; function CreateIptcData: TIptcData;
@ -132,6 +134,13 @@ type
end; end;
PJpegJFIFSegment = ^TJpegJFIFSegment; PJpegJFIFSegment = ^TJpegJFIFSegment;
TJpegJFXXSegment = packed record
Identifier: packed array[0..4] of AnsiChar; // 'JFXX'#0
ThumbnailFormat: byte; // 10: JPEG, 11: 1 byte-per-pixel palettized, 12: 3 byte-per-pixel RGB
end;
// ThumbnailData are following
PJpegJFXXSegment = ^TJpegJFXXSegment;
TJpegSOF0Segment = packed record TJpegSOF0Segment = packed record
DataPrecision: Byte; DataPrecision: Byte;
ImageHeight: Word; ImageHeight: Word;
@ -331,7 +340,8 @@ end;
function TImgInfo.HasThumbnail: boolean; function TImgInfo.HasThumbnail: boolean;
begin begin
Result := (FExifData <> nil) and FExifData.HasThumbnail; Result := ((FExifData <> nil) and FExifData.HasThumbnail)
or (Length(FJFXXThumbnail) > 0);
end; end;
function TImgInfo.HasWarnings: boolean; function TImgInfo.HasWarnings: boolean;
@ -433,6 +443,9 @@ begin
end; end;
procedure TImgInfo.ReadJpeg(AStream: TStream); procedure TImgInfo.ReadJpeg(AStream: TStream);
const
sJFIF: String[5] = 'JFIF'#0;
sJFXX: String[5] = 'JFXX'#0;
var var
marker: Byte; marker: Byte;
size: Word; size: Word;
@ -441,6 +454,7 @@ var
buf: TBytes; buf: TBytes;
reader: TBasicMetadataReader; reader: TBasicMetadataReader;
bigEndian: Boolean; bigEndian: Boolean;
hdr: TBytes;
{$IFNDEF FPC} {$IFNDEF FPC}
sa: ansistring; sa: ansistring;
{$ENDIF} {$ENDIF}
@ -517,17 +531,32 @@ begin
end; end;
M_JFIF: M_JFIF:
begin begin
SetLength(hdr, size);
AStream.Read(hdr[0], size);
with PJpegJFIFSegment(@hdr[0])^ do begin
if CompareMem(@Identifier[0], @sJFIF[1], Length(sJFIF)) then
begin
// JFIF APP0 marker segment
SetLength(FHeaderSegment, size); SetLength(FHeaderSegment, size);
AStream.Read(FHeaderSegment[0], size); Move(hdr[0], FHeaderSegment[0], size);
with PJpegJFIFSegment(@FHeaderSegment[0])^ do begin
if not (
(Identifier[0]='J') and (Identifier[1]='F') and
(Identifier[2]='I') and (Identifier[3]='F') and
(Identifier[4]=#0) )
then
exit;
if (JFIFVersion[0] <> 1) then if (JFIFVersion[0] <> 1) then
exit; exit;
end else
if CompareMem(@Identifier[0], @sJFXX[1], Length(sJFXX)) then
begin
// JFXX extension APP0 marker segment (optional)
// alternative location of a thumbnail image:
// https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#JFIF_extension_APP0_marker_segment
{
// --- not supported at the moment.
SetLength(FJFXXHeaderSegment, size);
Move(hdr[0], FJFXXHeaderSegment[0], size);
}
// --- not working... not a valid jpeg structure.
SetLength(FJFXXThumbnail, size - SizeOf(TJpegJFXXSegment)); //(AStream.Position - p));
Move(hdr[SizeOf(TJpegJFXXSegment)], FJFXXThumbnail[0], Length(FJFXXThumbnail));
end;
end; end;
end; end;
M_SOF0: M_SOF0:
@ -573,6 +602,15 @@ begin
SaveToFile(FFileName); SaveToFile(FFileName);
end; end;
procedure TImgInfo.SaveThumbnailToStream(AStream: TStream);
begin
if (FExifData <> nil) and ExifData.HasThumbnail then
FExifData.SaveThumbnailToStream(AStream)
else
if Length(FJFXXThumbnail) > 0 then
AStream.Write(FJFXXThumbnail[0], Length(FJFXXThumbnail));
end;
procedure TImgInfo.SaveToFile(const AFileName: String; AImgFile: String = ''); procedure TImgInfo.SaveToFile(const AFileName: String; AImgFile: String = '');
var var
ms: TMemoryStream; ms: TMemoryStream;