Einzelnen Beitrag anzeigen

Benutzerbild von DataCool
DataCool

Registriert seit: 10. Feb 2003
Ort: Lingen
909 Beiträge
 
Delphi 10.3 Rio
 
#60

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

  Alt 10. Jul 2020, 17:35
Hier noch die Verwendung von GDI+. Nur einfaches Beispiel, ohne Ressourcenschutz oder Ähnliches. Leider bietet die Save-Procedure nicht die Option des Überschreibens, daher ein bisschen Nachbau. GDI+ ist sehr schnell. Zu beachten ist, dass hier die Blockgröße 16 Pixel beträgt, Höhe/Breite also ein Vielfaches von 16 sein müssen.
Frustrierend ist, dass GDI+ als 32-Bit-DLL natürlich wieder mal nicht in 64-Bit-Anwendungen benutzt werden kann.
Delphi-Quellcode:
uses ... GDIPAPI, GDIPOBJ,GDIPUTIL;

procedure DreheMitGDIPlus1;
Var i:integer;
  DatListe:TStringDynArray;
  GPImage: TGPImage;
  EncoderCLSID: TGUID;
  Ergebnis : Status;
  TempDatname:string;
const
  Verz = 'C:\Test\';
begin
  DatListe := TDirectory.GetFiles(Verz,'*.jpg');
  GetEncoderClsid('image/jpeg', EncoderCLSID);
  For i := 0 to High(DatListe) do begin
    GPImage := TGPImage.Create(DatListe[i]);
    GPImage.RotateFlip(Rotate90FlipNone);
    TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
    Ergebnis := GPImage.Save(TempDatname,EncoderCLSID);
    GPImage.Free;
    If Ergebnis = Ok then begin
      If DeleteFile(DatListe[i])
        then RenameFile(TempDatname,DatListe[i])
        else DeleteFile(TempDatname);
    end;
  end;
end;
Die nachfolgende Version ist die "volle". An ihr ist besonders interessant, dass sie nicht nur selbständig ermittelt, ob das JPG überhaupt gedreht werden muss, sondern auch den Zugriff auf weitere GDI-Funktionen bietet. Auch kann außer dem PropertyTagOrientation noch eine Vielzahl von weiteren EXIF-Markern ausgelesen werden.
Delphi-Quellcode:
uses ... GDIPAPI, GDIPOBJ,GDIPUTIL;

procedure DreheMitGDIPlus2;
Var i:integer;
  DatListe:TStringDynArray;
  GPImage: TGPImage;
  PPropItem: PPropertyItem;
  BufferSize: Cardinal;
  Orientation: Byte;
  RotateBy: EncoderValue;
  EncoderCLSID: TGUID;
  EncoderParams: TEncoderParameters;
  EncoderTransformValue:integer;
  Ergebnis : Status;
  TempDatname:string;
const
  Verz = 'C:\Test\';
begin
  DatListe := TDirectory.GetFiles(Verz,'*.jpg');
  GetEncoderClsid('image/jpeg', EncoderCLSID);
  FillChar(EncoderParams, SizeOf(EncoderParams), 0);
  EncoderParams.Count := 1;
  EncoderParams.Parameter[0].Guid := EncoderTransformation;
  EncoderParams.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  EncoderParams.Parameter[0].NumberOfValues := 1;
  For i := 0 to High(DatListe) do begin
    GPImage := TGPImage.Create(DatListe[i]);
    BufferSize := GPImage.GetPropertyItemSize(PropertyTagOrientation);
    If BufferSize > 0 then begin
      GetMem(PPropItem, BufferSize);
      Try
        GPImage.GetPropertyItem(PropertyTagOrientation, BufferSize, PPropItem);
        Orientation := PByte(PPropItem.value)^;
        case Orientation of
          3: RotateBy := EncoderValueTransformRotate180;
          6: RotateBy := EncoderValueTransformRotate90;
          8: RotateBy := EncoderValueTransformRotate270;
          else Continue;
        end;
        If (Orientation in [3,6,9]) then begin
          EncoderTransformValue := Ord(RotateBy);
          EncoderParams.Parameter[0].Value := @EncoderTransformValue;
          TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
          Ergebnis := GPImage.Save(WideString(TempDatname),EncoderCLSID,@EncoderParams);
          GPImage.Free;
          If Ergebnis = Ok then begin
            If DeleteFile(DatListe[i])
              then RenameFile(TempDatname,DatListe[i])
              else DeleteFile(TempDatname);
          end;
        end;
      Finally
        FreeMem(PPropItem);
      end;
    end;
  end;
end;
Danke @Benmik

Das kommt dem sehr nahe was ich gesucht habe.

Jetzt werden die Bilder in Delphi "korrekt" angezeigt nur im Windows-Explorer Vorschau noch falsch ... Weil der Orientation Tag nach der Drehung noch auf dem "alten" Wert steht. Eigentlich müßte er auf "1" zurück gesetzt werden.
Was mich zu meiner nächsten Frage führt :

Wie genau funktioniert
 GPImage.SetPropertyItem(PPropItem^); Speichert die Aufruf schon die entsprechende Information in die Datei ? Oder muss man explizit .Save aufrufen ?
Sorry, für die blöden Fragen, finde leider keine gescheite Doku zu GDI+.

Gretes Data
Der Horizont vieler Menschen ist ein Kreis mit Radius Null, und das nennen sie ihren Standpunkt.
  Mit Zitat antworten Zitat