Einzelnen Beitrag anzeigen

Benmik

Registriert seit: 11. Apr 2009
361 Beiträge
 
Delphi 10.3 Rio
 
#74

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten

  Alt 31. Jul 2020, 17: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;
  Mit Zitat antworten Zitat