Einzelnen Beitrag anzeigen

HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
961 Beiträge
 
Delphi 6 Professional
 
#15

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 14. Feb 2020, 07:06
Hmm..

Hab mir die letzte Version angeschaut..

Wieso gehst Du erst den kompletten Header durch, um dann wieder von vorne anzufangen, um nur die Pakete mit $C0 bis $C2 zu suchen.

Das lesen der Größe gibt bei mir bei einigen Bildern auch eine falsche Größe wieder...

Deshalb habe ich das mal aufgeräumt und überarbeitet:

Delphi-Quellcode:
type
  TByteArr = array of Byte;

  TJFIFSegment = packed record
    Fix : Byte;
    Kind : Byte;
  end;

  TSOFData = packed record
    SamplePrecision : Byte;
    Height : WORD; // Number of lines
    Width : WORD; // Number of samples per line
    Comp : Byte; // Number of image components in frame
// Data : TByteArr;
  end;
  PSOFData = ^TSOFData;

// Irgendwo aus dem Netz kopiert..
function ReverseWord(w: word): word;
asm
   {$IFDEF CPUX64}
   mov rax, rcx
   {$ENDIF}
   xchg al, ah
end;

function ReadWORD(FS : TFileStream; out AWord : WORD):boolean;
begin
  Result := (FS.Read(AWord,SizeOf(AWord)) = SizeOf(AWord));
  AWord := ReverseWord(AWord);
end;

function ReadSegmentHeader(FS : TFileStream; out Seg : TJFIFSegment):boolean;
begin
  Result := (FS.Read(Seg,SizeOf(Seg)) = SizeOf(Seg));
end;

function ReadData(FS : TFileStream; const ALength:WORD; var Data : TByteArr):boolean;
begin
  SetLength(Data, ALength);
  Result := (FS.Read(Data[0],ALength) = ALength);
end;


function GetJPEGImageSize(const AFileName : string; out AHeight, AWidth : integer):boolean;
var
  FS : TFileStream;
  SOI : WORD;
  SEG : TJFIFSegment;
  SegSize : WORD;

  C0 : PSOFData;
  tmpData : TByteArr;
begin
  Result := False;
  FS := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
  try
    if ReadWORD(FS, SOI) and (SOI = $FFD8) then begin // Start Of Image = Magic Bytes zur Erkennung von JPG

      While ReadSegmentHeader(FS, SEG) and (SEG.Fix = $FF) do begin

        if SEG.Kind = $DA then break; // Start of Scan = End of Header, danach nur noch Imagedaten

        if ReadWORD(FS, SegSize) then begin
          SegSize := SegSize -2; // Längenangaben um die 2 Byte für die Längenangaben selber reduziert
          case SEG.Kind of
            $C0, // Baseline DCT
            $C1, // Extended sequential DCT, Huffman coding
            $C2, // Progressive DCT, Huffman coding
            $C3, // Lossless (sequential), Huffman coding
            $C9, // Extended sequential DCT, arithmetic coding
            $CA, // Progressive DCT, arithmetic coding
            $CB : // Lossless (sequential), arithmetic coding
                  begin
                    // SOFx, im SOF steht am Anfang die Größe des Bildes, anschließend Daten zur Dekodierung
                    if ReadData(FS, SegSize, tmpData) then begin
                      C0 := PSOFData(@tmpData[0]);
                      AHeight := ReverseWord(C0.Height);
                      AWidth := ReverseWord(C0.Width);
                      Result := True;
                      Break;
                    end;
                  end;
           else
             FS.Position := FS.Position + SegSize; // Zum nächsten Segment, die weiteren werden nicht gebraucht.
          end;
        end;
      end;
    end;
  finally
    FS.Free;
  end;
end;

Das funktioniert nun auch mit den anders kodierten JPGs ($C3,$C9..) und liefert schnell die richtige Größe zurück.

(Außerdem ist es meiner Meinung nach besser lesbar )
(Ja ich Verwende Delphi 6 Pro und will NICHT wechseln!)
  Mit Zitat antworten Zitat