Registriert seit: 11. Apr 2009
560 Beiträge
Delphi 12 Athens
|
AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
31. Jul 2020, 18:36
Ich habe die Prozedur zum Drehen von JPG an zwei Stellen verbessert.
Zum einen ist es das Auslesen des EXIF-Datums, das jetzt ordentlich geschieht.
Zum anderen habe ich bemerkt, dass der Code bei 64-Bit-Apps nicht funktionierte. Das war mir zunächst ein Rätsel, da es keinerlei Fehlermeldung gab, die DLL ganz offensichtlich korrekt angesteuert und auch die Datei verlustfrei neu gespeichert wurde; nur gedreht wurde sie nicht! Erst als ich auf die Idee kam, bei EncoderTransformValue
den Typ von Integer
zu NativeInt
zu ändern, ging es plötzlich. (Das erinnert mich an Sir Rufo: Kaum macht man's richtig, schon funktioniert's!)
Delphi-Quellcode:
uses ... IGDIPlus;
procedure DreheMitIGDIPlus;
Var i:integer;
Orientation: UInt32;
EXIFDatum:TDateTime;
DatListe:TStringDynArray;
IGDPImage: IGPImage;
PPropOItem,PPropDItem: PGPPropertyItem;
PropBufferSize,DateBufferSize: Cardinal;
DreheXGrad: TIGPEncoderValue;
EncoderParams: TIGPEncoderParameters;
EncoderTransformValue:NativeInt; // NICHT integer, sonst keine Funktion bei 64-Bit-Apps!
TempDatname:string;
EncoderCLSID: TGUID;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
procedure Initialisiere;
begin
PropBufferSize := 0;
PPropOItem := nil;
EncoderParams.Count := 1;
EncoderParams.Parameter[0].Guid := EncoderTransformation;
EncoderParams.Parameter[0].DataType := EncoderParameterValueTypeLong;
EncoderParams.Parameter[0].NumberOfValues := 1;
GetEncoderClsid('image/jpeg', EncoderCLSID);
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
function BufferGesetzt:Boolean;
begin
If (PropBufferSize > 0) and (DateBufferSize > 0)
then exit(True);
PropBufferSize := IGDPImage.GetPropertyItemSize(GPPropertyTagOrientation);
If PropBufferSize > 0
then GetMem(PPropOItem, PropBufferSize);
DateBufferSize := IGDPImage.GetPropertyItemSize(GPPropertyTagDateTime);
If DateBufferSize > 0
then GetMem(PPropDItem, DateBufferSize);
Result := (PropBufferSize > 0) and (DateBufferSize > 0);
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
function BestimmeRotation(var DreheXGrad:TIGPEncoderValue):Boolean;
var Orientation: UInt32;
begin
IGDPImage.GetPropertyItem(GPPropertyTagOrientation, PropBufferSize, PPropOItem);
Orientation := PUInt32(PPropOItem.value)^;
Case Orientation of
3: DreheXGrad := EncoderValueTransformRotate180;
6: DreheXGrad := EncoderValueTransformRotate90;
8: DreheXGrad := EncoderValueTransformRotate270;
else DreheXGrad := TIGPEncoderValue(100); // zum Testen "else DreheXGrad := EncoderValueTransformRotate90;" , wenn man keine Hochkant-Bilder hat
end;
Result := (DreheXGrad in [EncoderValueTransformRotate90..EncoderValueTransformRotate270]);
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
function BestimmeEXIFDatum:TDateTime;
var i:Cardinal; DateOrig: array of Byte; TxtEXIFDatum:string;
begin
IGDPImage.GetPropertyItem(GPPropertyTagDateTime, DateBufferSize, PPropDItem);
SetLength(DateOrig,DateBufferSize);
Move(PPropDItem.Value,DateOrig[1],DateBufferSize);
// DateBufferSize ist immer deutlich größer als die 19 Bytes des Datumeintrags. Die Byteanzahl vor dem Eintrag ist nicht konstant.
i := 0;
While i < (DateBufferSize - PPropDItem.Length) do begin
// Format des Datumseintrags ist JJJJ:MM:TT HH:MM:SS - die 4 Doppelpunkte stehen in normiertem Abstand zueinander
If (DateOrig[i] = $3A) and (DateOrig[i + 3] = $3A) and (DateOrig[i + 9] = $3A) and (DateOrig[i + 12] = $3A)
then break;
Inc(i);
end;
TxtEXIFDatum := Copy(TEncoding.ASCII.GetString(DateOrig),i - 3,19);
Try
Result :=
EncodeDate(StrToInt(Copy(TxtEXIFDatum, 1, 4)),StrToInt(Copy(TxtEXIFDatum, 6, 2)),StrToInt(Copy(TxtEXIFDatum, 9, 2))) +
EncodeTime(StrToInt(Copy(TxtEXIFDatum, 12, 2)),StrToInt(Copy(TxtEXIFDatum, 15, 2)),StrToInt(Copy(TxtEXIFDatum, 18, 2)), 0);
Except
Result := 0;
End;
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
function SpeichereJPG:Boolean;
begin
Result := False;
TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
If TFile.Exists(TempDatname)
then exit;
IGDPImage.Save(WideString(TempDatname),EncoderCLSID,@EncoderParams);
IGDPImage := nil;
If TFile.Exists(TempDatname) then begin
Result := DeleteFile(DatListe[i]);
If Result
then Result := RenameFile(TempDatname,DatListe[i])
else DeleteFile(TempDatname);
end;
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
function SetzeDatumAufEXIF(Datname:string;EXIFDatum:TDateTime):Boolean;
begin
Result := True;
Try
TFile.SetCreationTime(Datname,EXIFDatum);
TFile.SetLastWriteTime(Datname,EXIFDatum);
Except
Result := False;
End;
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
procedure RäumeAuf;
begin
FreeMem(PPropOItem);
FreeMem(PPropDItem);
IGDPImage := nil;
end;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
begin
Initialisiere;
Try
DatListe := TDirectory.GetFiles(Verz,'*.jpg');
For i := 0 to High(Datliste) do begin // zum Testen auf "0 to 0" setzen
IGDPImage := TIGPImage.Create(DatListe[i]);
If not BufferGesetzt
then exit;
EXIFDatum := BestimmeEXIFDatum;
If BestimmeRotation(DreheXGrad) then begin
EncoderTransformValue := Ord(DreheXGrad);
EncoderParams.Parameter[0].Value := @EncoderTransformValue;
Orientation := 1; // zum Testen z.B. auf 3 setzen, wenn man keine Hochkant-Bilder hat
PPropOItem.Value := @Orientation;
IGDPImage.SetPropertyItem(PPropOItem^);
If not SpeichereJPG
then exit;
SetzeDatumAufEXIF(DatListe[i],EXIFDatum);
end;
IGDPImage := nil;
end;
Finally
RäumeAuf;
end;
end;
|