fpimage TIFF reader broken for big-endian images
Summary
Checking the TIFF reader with some data files I found that the reader is no longer able to read images with big-endian byte order ('MM' as first bytes). With fpc 3.2.2, there was no such problem.
System Information
- Operating system: Windows 11
- Processor architecture: x86-64
- Compiler version: 3.3.1 (0e289063)
- Device: Desktop PC
Steps to reproduce
Run the following test project with the image "cramps.tif" having big-endian byte order (taken from https://gitlab.com/libtiff/libtiff-pics.git).
Example Project
program tiff_test;
uses
SysUtils, fpimage, fpreadtiff;
const
FILE_NAME = 'cramps.tif';
var
img: TFPMemoryImage;
begin
img := TFPMemoryImage.Create(0, 0);
try
img.LoadFromFile(FILE_NAME);
WriteLn('Image ' + FILE_NAME + ' successfully loaded.');
except
on E: Exception do WriteLn(E.Message);
end;
ReadLn;
end.
The attachment contains this project together with the test image.
What is the current bug behavior?
With FPC 3.2.2 the test image is loaded successfully. With FPC 3.3.1, the image format is not reckognized and an error "wrong image format" is reported.
What is the expected (correct) behavior?
FPC 3.3.1 should load the image successfully, just like v3.2.2
Possible fixes
A big change of the reader has been made with the introduction of BigTIFF (commit d24b89fb). A large part of the ReadTIFFHeader
method was rewritten, and this is where the bug crept in:
Format detection
In the old code the elements of the header were read separately by the ReadByte
, ReadWord
, ReadDWord
and ReadQWord
methods, which internally take care of endianness. Now the header is read en-block into a TTiffHeader
record variable, and the elements of this record must be corrected manually for endianness. But this does not happen:
The TiffHeader.Version
value must be either 42 or 43 - otherwise the file format is rejected by the tiff reader. This value is a 2-byte number and thus affected by endianness:
// old code:
case TIFHeader.Version of ...
// correct code:
case FixEndian(TIFHeader.Version) of ...
Offset to the first image file directory
In case of the "old" TIFF format (header version = 42) the offset to the image file directory, IFDStart
, has been read together with the TIFF header. But again, the endianness is not handled:
// old code:
IFDStart:=TIFHeader.IFDStart
// correct code:
IFDStart:=FixEndian(TIFHeader.IFDStart)
With these changes the format is detected correctly.
Running a modified version of the test project against all tiff images in https://gitlab.com/libtiff/libtiff-pics.git it is observed that a large number of test files is not read correctly, but this is expected because the fcl-image tiff reader does not implement the entire tiff specification. Sometimes even different error messages are displayed between the FPC3.2.2 and FPC/main cases - this indicates that some other incorrect endianness code may still be lurking around somewhere. But at least, the correctly read files are the same in both cases.