[Lazarus] TImage shows loaded image rotated by 90?

LacaK lacak at zoznam.sk
Mon Sep 26 14:07:06 CEST 2016


I have created small Exif reader for my own needs.
I have looked also in FCL TFPReaderJpeg (which uses pasjpeg) if there is 
no support, but I do not see.
If there would be interest for extending functionality of this reader to 
support reading of Exif information I can prepare patch.

Here is my simple implementation if somebody is interested:

========================================================================
   { TJpegExifReader }

   TJpegExifReader = class
     private
       type
         TIFDEntry = record
           TagNo: Word;
           DataType: Word;
           Count: DWord;
           Value: DWord; // This  tag  records  the  offset  from the  
start  of  the  TIFF  header  to  the  position  where  the value  
itself  is  recorded.
                         // In cases where the value fits in 4 bytes, 
the value itself is recorded. If the value is smaller than 4 bytes, the 
value is stored in the 4-byte area starting from the left, i.e., from 
the lower end
         end;

         TExifData = record
           Make,
           Model,
           Software,
           DateTime: string;
           Orientation: smallint;
           XResolution,
           YResolution: double;
           ResolutionUnit: smallint;
         end;

       var
         FBA: Word;

       function Swap(w: Word): Word; overload;
     protected
       FIFD: array of TIFDEntry;
       FExif: TExifData;
     public
       constructor Create(const FileName: string); overload;
       constructor Create(Stream: TFileStream); overload;

       function Orientation: smallint;
       function XResolution: double;
       function YResolution: double;
       function ResolutionUnit: string;
       function DateTime: TDateTime;
   end;

{ TJpegExifReader }

function TJpegExifReader.Swap(w: Word): Word;
begin
   if FBA = $4D4D then
     // Motorola align: first byte in data is highest byte (big endian)
     Result := BEtoN(w)
   else
     // Intel align: first byte in data is lowest byte (little endian)
     Result := LEtoN(w);
end;

constructor TJpegExifReader.Create(const FileName: string);
var
   Stream: TFileStream;
begin
   Stream := TFileStream.Create(FileName, fmOpenRead+fmShareDenyWrite);
   try
     Create(Stream);
   finally
     Stream.Free;
   end;
end;

constructor TJpegExifReader.Create(Stream: TFileStream);
type
   TRATIONAL=record
     numerator: dword;
     denominator: dword;
   end;
var
   w, Size: word;
   dw: dword;
   n,i,c: integer;
   S: AnsiString;
   R: TRATIONAL;
   D: double;
begin
   inherited Create;

   Stream.Position := 0;
   Stream.Read(w,SizeOf(w));
   if w <> NtoLE($D8FF) then Exit;      // 0-1: Jpeg SOI (Start of 
image: FFD8)
   Stream.Read(w, SizeOf(w));
   if w <> NtoLE($E1FF) then Exit;      // 2-3: APP1 (Application 
marker: FFE1)
   Stream.Read(Size, SizeOf(Size));     // 4-5: Size of APP1 data area 
(high byte first)
   Size := BEtoN(Size);
   Dec(Size, 2);
   // Exif
   Stream.Read(dw, SizeOf(dw));
   if dw <> NtoLE($66697845) then Exit; // 6-9: 'Exif'
   Stream.Read(w, SizeOf(w));
   if w <> NtoLE($0000) then Exit;      // 10-11: 0000
   Dec(Size, 6);
   // TIFF header
   Stream.Read(FBA, SizeOf(FBA));       // 12-13: byte order
   if (FBA<>$4949) and (FBA<>$4D4D) then Exit; // 4949=Intel, 4D4D=Motorola
   Stream.Read(w, SizeOf(w));
   if Swap(w) <> $002A then Exit;       // Tag Mark
   Stream.Read(dw, SizeOf(dw));         // Offset to first IFD (usualy 8)
   Dec(Size, 8);

   // IFD: Image file directory
   SetLength(FIFD, 0);
     Stream.Read(w, SizeOf(w));         // No of IFD entries
     Dec(Size, 2);
     n := Swap(w);
     SetLength(FIFD, Length(FIFD)+n);
     // Read IFD entries
     for i:=0 to n-1 do begin
       Stream.Read(FIFD[i], SizeOf(TIFDEntry));
       Dec(Size, SizeOf(TIFDEntry));
     end;
     Stream.Read(dw, SizeOf(dw));       // Offset to next IFD (0=last)

   // Parse IFD entries
   for i:=0 to n-1 do begin
     case Swap(FIFD[i].DataType) of
       1: w:=1; // unsigned byte
       2: w:=1; // ascii string (terminated with 0)
       3: w:=2; // unsigned short (2 bytes)
       4: w:=4; // unsigned long (4 bytes)
       5: w:=8; // unsigned rational (4+4 long)
       else w:=1;
     end;
     c := w * Swap(FIFD[i].Count);

     if c > 4 then begin
       // Value contains offset from TIFF header to data
       Stream.Position := 12 + Swap(FIFD[i].Value);
       case Swap(FIFD[i].DataType) of
         2: begin
            Dec(c);
            SetLength(S, c);
            Stream.Read(S[1], c);
            end;
         5: begin
            Stream.Read(R, SizeOf(R));
            D := R.numerator / R.denominator;
            end;
       end;
     end
     else
       case Swap(FIFD[i].DataType) of
         3: w := Swap(FIFD[i].Value);
       end;

     case Swap(FIFD[i].TagNo) of
       $010F: FExif.Make := S;
       $0110: FExif.Model := S;
       $0112: FExif.Orientation := w;
       $011A: FExif.XResolution := D;
       $011B: FExif.YResolution := D;
       $0128: FExif.ResolutionUnit := w;
       $0131: FExif.Software := S;
       $0132: FExif.DateTime := S;
       $8769: // offset to Exif SubIFD
     end;
   end;

end;

function TJpegExifReader.Orientation: smallint;
begin
   case FExif.Orientation of
     3:   Result := 180;
     6:   Result := 270; // The 0th row is the visual right-hand side of 
the image, and the 0th column is the visual top.
     else Result := 0;
   end;
end;

function TJpegExifReader.XResolution: double;
begin
   Result := FExif.XResolution;
end;
function TJpegExifReader.YResolution: double;
begin
   Result := FExif.YResolution;
end;
function TJpegExifReader.ResolutionUnit: string;
begin
   case FExif.ResolutionUnit of
     2:   Result := 'in';
     3:   Result := 'cm';
     else Result := '';
   end;
end;

function TJpegExifReader.DateTime: TDateTime;
begin
   Result := ComposeDateTime(
             StrToDate(Copy(FExif.DateTime,1,10), 'yyyy:mm:dd', ':'),
             StrToTime(Copy(FExif.DateTime,12,8), ':')
             );
end;

========================================================================
>>>> I have JPEG image with width=2448 and height=3264.
>>>
>>> No
>>>
>>>> All programs in Windows show image with respect to his dimensions, so
>>>> shorter is X and higher is Y.
>>>>
>>>> But when I load this image into TImage control (using Picture 
>>>> property)
>>>> image is shown rotated by 90 degrees, with width=3264 and height=2448
>>>
>>> That is correct. If you look at the exif info, you read:
>>
>> How do you read this Exif info ?
>
> with a decent viewer ;) (in windows I use irfanview)
>
>> When I right click image and look at Details tab I see there only
>> width=2448 and height=3264
>
> I see too
>
>> But when I use on-line exif viewer (at http://regex.info/exif.cgi) there
>> are swaped results and:
>> OrientationRotate = 90 CW
>>
>> So it seems, that you are right ;-)
>>
>> Next question then is: how can I detect this in Lazarus and do same as
>> "smart viewers" do ?
>> (in other words: is there and component, which allows me read this EXIF
>> information from JPEG image ?)
>
> Google ?
>
> Exif + Delphi -> torry.net -> NativeJpg v.1.33 or TExif v.1.3 or 
> TGJPGCommentInfoP v.1.0
>
> (looking for sources supporting D7)
>
> you may try that.
>
> Marc
>
>


More information about the Lazarus mailing list