You've already forked lazarus-ccr
fpexif: Consider exif Orientation tag when loading image in MetadataViewer.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7096 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -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="MetadataViewer"/>
|
<Title Value="MetadataViewer"/>
|
||||||
<ResourceType Value="res"/>
|
<ResourceType Value="res"/>
|
||||||
<UseXPManifest Value="True"/>
|
<UseXPManifest Value="True"/>
|
||||||
|
@ -94,7 +94,7 @@ object MainForm: TMainForm
|
|||||||
Left = 8
|
Left = 8
|
||||||
Height = 15
|
Height = 15
|
||||||
Top = 4
|
Top = 4
|
||||||
Width = 94
|
Width = 93
|
||||||
BorderSpacing.Left = 8
|
BorderSpacing.Left = 8
|
||||||
BorderSpacing.Top = 4
|
BorderSpacing.Top = 4
|
||||||
BorderSpacing.Bottom = 4
|
BorderSpacing.Bottom = 4
|
||||||
@ -329,7 +329,7 @@ object MainForm: TMainForm
|
|||||||
Height = 25
|
Height = 25
|
||||||
Hint = 'Replaces the image date.'
|
Hint = 'Replaces the image date.'
|
||||||
Top = 5
|
Top = 5
|
||||||
Width = 66
|
Width = 67
|
||||||
AutoSize = True
|
AutoSize = True
|
||||||
BorderSpacing.Left = 8
|
BorderSpacing.Left = 8
|
||||||
BorderSpacing.Bottom = 4
|
BorderSpacing.Bottom = 4
|
||||||
|
@ -7,7 +7,7 @@ interface
|
|||||||
uses
|
uses
|
||||||
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ShellCtrls,
|
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ShellCtrls,
|
||||||
ExtCtrls, ComCtrls, StdCtrls,
|
ExtCtrls, ComCtrls, StdCtrls,
|
||||||
fpeMetadata, fpeMakerNote;
|
fpeGlobal, fpeMetadata, fpeMakerNote;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ type
|
|||||||
private
|
private
|
||||||
FImgInfo: TImgInfo;
|
FImgInfo: TImgInfo;
|
||||||
FImageLoaded: Boolean;
|
FImageLoaded: Boolean;
|
||||||
|
FImageOrientation: TExifOrientation;
|
||||||
procedure LoadFile(const AFileName: String);
|
procedure LoadFile(const AFileName: String);
|
||||||
procedure LoadFromIni;
|
procedure LoadFromIni;
|
||||||
procedure SaveToIni;
|
procedure SaveToIni;
|
||||||
@ -76,8 +77,8 @@ implementation
|
|||||||
{$R *.lfm}
|
{$R *.lfm}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
IniFiles, Math, StrUtils, DateUtils,
|
LCLType, IniFiles, Math, StrUtils, DateUtils, IntfGraphics,
|
||||||
fpeGlobal, fpeTags, fpeExifData, fpeIptcData;
|
fpeTags, fpeExifData, fpeIptcData;
|
||||||
|
|
||||||
const
|
const
|
||||||
TAG_ID_CAPTION = 'Tag ID';
|
TAG_ID_CAPTION = 'Tag ID';
|
||||||
@ -87,6 +88,85 @@ begin
|
|||||||
Result := ChangeFileExt(Application.ExeName, '.ini');
|
Result := ChangeFileExt(Application.ExeName, '.ini');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure RotateBitmap(const ABitmap: TBitmap; AOrientation: TExifOrientation);
|
||||||
|
Var
|
||||||
|
bmp: TBitmap;
|
||||||
|
srcImg, dstImg: TLazIntfImage;
|
||||||
|
imgHandle, imgMaskHandle: HBitmap;
|
||||||
|
i, j: integer;
|
||||||
|
w1, h1: Integer; // Input bitmap width and height diminished by 1
|
||||||
|
Begin
|
||||||
|
Assert(ABitmap <> nil, 'RotateBitmap: Input bitmap is expected not to be nil.');
|
||||||
|
|
||||||
|
if (AOrientation = eoUnknown) or (AOrientation = eoNormal) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
w1 := ABitmap.Width - 1;
|
||||||
|
h1 := ABitmap.Height - 1;
|
||||||
|
srcImg := TLazIntfImage.Create(0, 0);
|
||||||
|
try
|
||||||
|
srcImg.LoadFromBitmap(ABitmap.Handle, ABitmap.MaskHandle);
|
||||||
|
bmp := TBitmap.Create;
|
||||||
|
try
|
||||||
|
dstImg := TLazIntfImage.Create(0, 0);
|
||||||
|
try
|
||||||
|
if AOrientation in [eoRotate90, eoRotate270, eoMirrorHorRot90, eoMirrorHorRot270] then
|
||||||
|
begin
|
||||||
|
bmp.SetSize(ABitmap.Height, ABitmap.Width);
|
||||||
|
dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
|
||||||
|
case AOrientation of
|
||||||
|
eoRotate90:
|
||||||
|
for i:=0 to w1 do
|
||||||
|
for j:=0 to h1 do
|
||||||
|
dstImg.Colors[h1-j, i] := srcImg.Colors[i, j];
|
||||||
|
eoRotate270:
|
||||||
|
for i:=0 to w1 do
|
||||||
|
for j:=0 to h1 do
|
||||||
|
dstImg.Colors[j, w1-i] := srcImg.Colors[i, j];
|
||||||
|
eoMirrorHorRot90:
|
||||||
|
for i:=0 to w1 do
|
||||||
|
for j:=0 to h1 do
|
||||||
|
dstImg.Colors[h1-j, w1-i] := srcImg.Colors[i, j];
|
||||||
|
eoMirrorHorRot270:
|
||||||
|
for i:=0 to w1 do
|
||||||
|
for j:=0 to h1 do
|
||||||
|
dstImg.Colors[j, i] := srcImg.Colors[i, j];
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
if AOrientation in [eoRotate180, eoMirrorHor, eoMirrorVert] then
|
||||||
|
begin
|
||||||
|
bmp.SetSize(ABitmap.Width, ABitmap.Height);
|
||||||
|
dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
|
||||||
|
case AOrientation of
|
||||||
|
eoRotate180:
|
||||||
|
for i:=0 to w1 do
|
||||||
|
for j:=0 to h1 do
|
||||||
|
dstImg.Colors[w1-i, h1-j] := srcImg.Colors[i, j];
|
||||||
|
eoMirrorHor:
|
||||||
|
for j:=0 to h1 do
|
||||||
|
for i:=0 to w1 do
|
||||||
|
dstImg.Colors[w1-i, j] := srcImg.Colors[i, j];
|
||||||
|
eoMirrorVert:
|
||||||
|
for i:=0 to w1 do
|
||||||
|
for j:=0 to h1 do
|
||||||
|
dstImg.Colors[i, h1-j] := srcImg.Colors[i, j];
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
dstImg.CreateBitmaps(imgHandle, imgMaskHandle, false);
|
||||||
|
bmp.Handle := ImgHandle;
|
||||||
|
bmp.MaskHandle := ImgMaskHandle;
|
||||||
|
finally
|
||||||
|
dstImg.Free;
|
||||||
|
end;
|
||||||
|
ABitmap.Assign(bmp);
|
||||||
|
finally
|
||||||
|
bmp.Free;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
srcImg.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TMainForm }
|
{ TMainForm }
|
||||||
|
|
||||||
@ -167,6 +247,7 @@ var
|
|||||||
i: Integer;
|
i: Integer;
|
||||||
ms: TMemoryStream;
|
ms: TMemoryStream;
|
||||||
suffix: String;
|
suffix: String;
|
||||||
|
crs: TCursor;
|
||||||
begin
|
begin
|
||||||
FImageLoaded := false;
|
FImageLoaded := false;
|
||||||
Image.Picture.Clear;
|
Image.Picture.Clear;
|
||||||
@ -191,6 +272,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if FImgInfo.HasExif then begin
|
if FImgInfo.HasExif then begin
|
||||||
|
FImageOrientation := FImgInfo.ExifData.ImgOrientation;
|
||||||
FImgInfo.ExifData.ExportOptions := FImgInfo.ExifData.ExportOptions + [eoTruncateBinary];
|
FImgInfo.ExifData.ExportOptions := FImgInfo.ExifData.ExportOptions + [eoTruncateBinary];
|
||||||
for i := 0 to FImgInfo.ExifData.TagCount-1 do begin
|
for i := 0 to FImgInfo.ExifData.TagCount-1 do begin
|
||||||
lTag := FImgInfo.ExifData.TagByIndex[i];
|
lTag := FImgInfo.ExifData.TagByIndex[i];
|
||||||
@ -237,12 +319,14 @@ begin
|
|||||||
item.SubItems.Add(lTag.AsString);
|
item.SubItems.Add(lTag.AsString);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if FImgInfo.HasThumbnail then begin
|
|
||||||
|
if FImgInfo.HasThumbnail and Assigned(FImgInfo.ExifData) then begin
|
||||||
ms := TMemoryStream.Create;
|
ms := TMemoryStream.Create;
|
||||||
try
|
try
|
||||||
FImgInfo.ExifData.SaveThumbnailToStream(ms);
|
FImgInfo.ExifData.SaveThumbnailToStream(ms);
|
||||||
ms.Position := 0;
|
ms.Position := 0;
|
||||||
PreviewImage.Picture.LoadFromStream(ms);
|
PreviewImage.Picture.LoadFromStream(ms);
|
||||||
|
RotateBitmap(PreviewImage.Picture.Bitmap, FImageOrientation);
|
||||||
finally
|
finally
|
||||||
ms.Free;
|
ms.Free;
|
||||||
end;
|
end;
|
||||||
@ -255,8 +339,16 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
if PageControl1.ActivePage = PgImage then begin
|
if PageControl1.ActivePage = PgImage then begin
|
||||||
Image.Picture.LoadfromFile(AFileName);
|
crs := Screen.Cursor;
|
||||||
FImageLoaded := true;
|
try
|
||||||
|
Screen.Cursor := crHourglass;
|
||||||
|
Image.Picture.LoadFromFile(AFileName);
|
||||||
|
if Assigned(FImgInfo.ExifData) then
|
||||||
|
RotateBitmap(Image.Picture.Bitmap, FImageOrientation);
|
||||||
|
FImageLoaded := true;
|
||||||
|
finally
|
||||||
|
Screen.Cursor := crs;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
except
|
except
|
||||||
on E:Exception do begin
|
on E:Exception do begin
|
||||||
@ -325,13 +417,23 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.PageControl1Change(Sender: TObject);
|
procedure TMainForm.PageControl1Change(Sender: TObject);
|
||||||
|
var
|
||||||
|
crs: TCursor;
|
||||||
begin
|
begin
|
||||||
if FImgInfo = nil then
|
if FImgInfo = nil then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
if not FImageLoaded then begin
|
if not FImageLoaded then begin
|
||||||
Image.Picture.LoadfromFile(FImgInfo.FileName);
|
crs := Screen.Cursor;
|
||||||
FImageLoaded := true;
|
try
|
||||||
|
Screen.Cursor := crHourglass;
|
||||||
|
Image.Picture.LoadFromFile(FImgInfo.FileName);
|
||||||
|
if FImgInfo.ExifData <> nil then
|
||||||
|
RotateBitmap(Image.Picture.Bitmap, FImgInfo.ExifData.ImgOrientation);
|
||||||
|
FImageLoaded := true;
|
||||||
|
finally
|
||||||
|
Screen.Cursor := crs;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ type
|
|||||||
FOnEndReading: TExifEndReadingEvent;
|
FOnEndReading: TExifEndReadingEvent;
|
||||||
function GetImgHeight: Integer;
|
function GetImgHeight: Integer;
|
||||||
function GetImgWidth: Integer;
|
function GetImgWidth: Integer;
|
||||||
|
function GetOrientation: TExifOrientation;
|
||||||
function GetTagByID(ATagID: TTagID): TTag;
|
function GetTagByID(ATagID: TTagID): TTag;
|
||||||
function GetTagByIndex(AIndex: Integer): TTag;
|
function GetTagByIndex(AIndex: Integer): TTag;
|
||||||
function GetTagByName(AFullTagName: String): TTag;
|
function GetTagByName(AFullTagName: String): TTag;
|
||||||
@ -131,6 +132,8 @@ type
|
|||||||
read GetImgHeight;
|
read GetImgHeight;
|
||||||
property ImgWidth: Integer
|
property ImgWidth: Integer
|
||||||
read GetImgWidth;
|
read GetImgWidth;
|
||||||
|
property ImgOrientation: TExifOrientation
|
||||||
|
read GetOrientation;
|
||||||
|
|
||||||
property OnBeginReading: TExifBeginReadingEvent
|
property OnBeginReading: TExifBeginReadingEvent
|
||||||
read FOnBeginReading write FOnBeginReading;
|
read FOnBeginReading write FOnBeginReading;
|
||||||
@ -869,6 +872,18 @@ begin
|
|||||||
Result := tag.AsInteger;
|
Result := tag.AsInteger;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TExifData.GetOrientation: TExifOrientation;
|
||||||
|
var
|
||||||
|
tag: TTag;
|
||||||
|
begin
|
||||||
|
tag := TagByName['Orientation'];
|
||||||
|
if tag = nil then
|
||||||
|
Result := eoUnknown
|
||||||
|
else
|
||||||
|
Result := TExifOrientation(tag.AsInteger);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Finds the tag which defines the sub-IFD to which the specified tag belongs }
|
{ Finds the tag which defines the sub-IFD to which the specified tag belongs }
|
||||||
function TExifData.GetParentTag(ATag: TTag): TTag;
|
function TExifData.GetParentTag(ATag: TTag): TTag;
|
||||||
var
|
var
|
||||||
@ -1767,6 +1782,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
*)
|
*)
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
|
@ -1157,6 +1157,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
|
@ -84,11 +84,15 @@ type
|
|||||||
|
|
||||||
TLookupCompareFunc = function(AValue1, AValue2: String): Boolean;
|
TLookupCompareFunc = function(AValue1, AValue2: String): Boolean;
|
||||||
|
|
||||||
|
TExifOrientation = ( // all angles are clockwise
|
||||||
|
eoUnknown, eoNormal, eoMirrorHor, eoRotate180, eoMirrorVert,
|
||||||
|
eoMirrorHorRot270, eoRotate90, eoMirrorHorRot90, eoRotate270
|
||||||
|
);
|
||||||
|
|
||||||
EFpExif = class(Exception);
|
EFpExif = class(Exception);
|
||||||
EFpExifReader = class(EFpExif);
|
EFpExifReader = class(EFpExif);
|
||||||
EFpExifWriter = class(EFpExif);
|
EFpExifWriter = class(EFpExif);
|
||||||
|
|
||||||
// TTagAcceptProc = function(ATag: TTagEntry): Boolean;
|
|
||||||
|
|
||||||
const
|
const
|
||||||
TagElementSize: array[1..13] of Integer = (1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4);
|
TagElementSize: array[1..13] of Integer = (1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4);
|
||||||
|
@ -246,6 +246,7 @@ msgid "Extensible metadata platform"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fpestrconsts.rsfilenotfounderror
|
#: fpestrconsts.rsfilenotfounderror
|
||||||
|
#, object-pascal-format
|
||||||
msgid "File \"%s\" not found."
|
msgid "File \"%s\" not found."
|
||||||
msgstr "Datei \"%s\" nicht gefunden."
|
msgstr "Datei \"%s\" nicht gefunden."
|
||||||
|
|
||||||
@ -502,6 +503,7 @@ msgid "Humidity"
|
|||||||
msgstr "Feuchte"
|
msgstr "Feuchte"
|
||||||
|
|
||||||
#: fpestrconsts.rsimagedatafilenotexisting
|
#: fpestrconsts.rsimagedatafilenotexisting
|
||||||
|
#, object-pascal-format
|
||||||
msgid "File \"%s\" providing the image data does not exist."
|
msgid "File \"%s\" providing the image data does not exist."
|
||||||
msgstr "Datei \"%s\", die die Bilddaten zur Verfügung stellen sollte, existiert nicht."
|
msgstr "Datei \"%s\", die die Bilddaten zur Verfügung stellen sollte, existiert nicht."
|
||||||
|
|
||||||
@ -530,6 +532,7 @@ msgid "Image number"
|
|||||||
msgstr "Bildnummer"
|
msgstr "Bildnummer"
|
||||||
|
|
||||||
#: fpestrconsts.rsimageresourcenametoolong
|
#: fpestrconsts.rsimageresourcenametoolong
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Image resource name \"%s\" too long."
|
msgid "Image resource name \"%s\" too long."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -570,6 +573,7 @@ msgid "Incorrect file structure"
|
|||||||
msgstr "Falsche Dateistruktur"
|
msgstr "Falsche Dateistruktur"
|
||||||
|
|
||||||
#: fpestrconsts.rsincorrecttagtype
|
#: fpestrconsts.rsincorrecttagtype
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Incorrect tag type %d: Index=%d, TagID=$%.04x, File:\"%s\""
|
msgid "Incorrect tag type %d: Index=%d, TagID=$%.04x, File:\"%s\""
|
||||||
msgstr "Falscher Tag-Typ %d: Index=%d, TagID=$%.94x, Datei: \"%s\""
|
msgstr "Falscher Tag-Typ %d: Index=%d, TagID=$%.94x, Datei: \"%s\""
|
||||||
|
|
||||||
@ -598,6 +602,7 @@ msgid "IPTC data expected, but not found."
|
|||||||
msgstr "IPTC-Daten erwartet, aber nicht gefunden."
|
msgstr "IPTC-Daten erwartet, aber nicht gefunden."
|
||||||
|
|
||||||
#: fpestrconsts.rsiptcextendeddatasizenotsupported
|
#: fpestrconsts.rsiptcextendeddatasizenotsupported
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Data size %d not supported for an IPTC extended dataset."
|
msgid "Data size %d not supported for an IPTC extended dataset."
|
||||||
msgstr "Datengröße %d wird für einen erweiterten IPTC Datensatz nicht unterstützt."
|
msgstr "Datengröße %d wird für einen erweiterten IPTC Datensatz nicht unterstützt."
|
||||||
|
|
||||||
@ -630,6 +635,7 @@ msgid "Writing error of compressed data."
|
|||||||
msgstr "Fehler beim Schreiben der komprimierten Daten."
|
msgstr "Fehler beim Schreiben der komprimierten Daten."
|
||||||
|
|
||||||
#: fpestrconsts.rsjpegreadwriteerrorinsegment
|
#: fpestrconsts.rsjpegreadwriteerrorinsegment
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Read/write error in segment $FF%.2x"
|
msgid "Read/write error in segment $FF%.2x"
|
||||||
msgstr "Lese-/Schreibfehler in Segment $FF%.2x"
|
msgstr "Lese-/Schreibfehler in Segment $FF%.2x"
|
||||||
|
|
||||||
@ -852,6 +858,7 @@ msgid "Range check error."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fpestrconsts.rsreadincompleteifdrecord
|
#: fpestrconsts.rsreadincompleteifdrecord
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Read incomplete IFD record at stream position %d."
|
msgid "Read incomplete IFD record at stream position %d."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1073,6 +1080,7 @@ msgid "Supplemental category"
|
|||||||
msgstr "Zusatzkategorie"
|
msgstr "Zusatzkategorie"
|
||||||
|
|
||||||
#: fpestrconsts.rstagtypenotsupported
|
#: fpestrconsts.rstagtypenotsupported
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Tag \"%s\" has an unsupported type."
|
msgid "Tag \"%s\" has an unsupported type."
|
||||||
msgstr "Der Typ von Tag \"%s\" wird nicht unterstützt."
|
msgstr "Der Typ von Tag \"%s\" wird nicht unterstützt."
|
||||||
|
|
||||||
@ -1161,6 +1169,7 @@ msgid "White point"
|
|||||||
msgstr "Weißpunkt"
|
msgstr "Weißpunkt"
|
||||||
|
|
||||||
#: fpestrconsts.rswritingnotimplemented
|
#: fpestrconsts.rswritingnotimplemented
|
||||||
|
#, object-pascal-format
|
||||||
msgid "Writing of %s files not yet implemented."
|
msgid "Writing of %s files not yet implemented."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user