[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