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:
parent
8c301b7ce0
commit
cd0aa90768
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
@ -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"/>
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user