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;
try
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);
exit;
end;
ms := TMemoryStream.Create;
try
imgInfo.ExifData.SaveThumbnailToStream(ms);
imgInfo.SaveThumbnailToStream(ms);
if ms.Size > 0 then begin
ms.Position := 0;
ThumbImg.Picture.LoadfromStream(ms);
ThumbImg.Picture.LoadFromStream(ms);
end else
ThumbImg.Picture.Clear;
finally

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,6 +58,7 @@ type
FWarnings: TStrings;
FMetadataKinds: TMetadataKinds;
FHeaderSegment: TBytes;
FJFXXThumbnail: TBytes;
FComment: String;
private
FExifData: TExifData;
@ -80,6 +81,7 @@ type
procedure LoadFromStream(AStream: TStream);
procedure Save;
procedure SaveToFile(const AFileName: String; AImgFile: String = '');
procedure SaveThumbnailToStream(AStream: TStream);
function CreateExifData(ABigEndian: Boolean = false): TExifData;
function CreateIptcData: TIptcData;
@ -132,6 +134,13 @@ type
end;
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
DataPrecision: Byte;
ImageHeight: Word;
@ -331,7 +340,8 @@ end;
function TImgInfo.HasThumbnail: boolean;
begin
Result := (FExifData <> nil) and FExifData.HasThumbnail;
Result := ((FExifData <> nil) and FExifData.HasThumbnail)
or (Length(FJFXXThumbnail) > 0);
end;
function TImgInfo.HasWarnings: boolean;
@ -433,6 +443,9 @@ begin
end;
procedure TImgInfo.ReadJpeg(AStream: TStream);
const
sJFIF: String[5] = 'JFIF'#0;
sJFXX: String[5] = 'JFXX'#0;
var
marker: Byte;
size: Word;
@ -441,6 +454,7 @@ var
buf: TBytes;
reader: TBasicMetadataReader;
bigEndian: Boolean;
hdr: TBytes;
{$IFNDEF FPC}
sa: ansistring;
{$ENDIF}
@ -517,17 +531,32 @@ begin
end;
M_JFIF:
begin
SetLength(FHeaderSegment, size);
AStream.Read(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
exit;
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);
Move(hdr[0], FHeaderSegment[0], size);
if (JFIFVersion[0] <> 1) then
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;
M_SOF0:
@ -573,6 +602,15 @@ begin
SaveToFile(FFileName);
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 = '');
var
ms: TMemoryStream;