Author |
Topic  |
|
timfa

USA
16 Posts |
Posted - Jun 18 2025 : 18:49:43
|
Hello,
I'm having issues loading/displaying TIFF files that use tiles. I was using ImageEn v13.7.0, but updated to v14.0.1 to see if the issue has been resolved; it hasn’t. My problem can be seen using the demo TiffHandler application located at Demos\InputOutput\TiffHandler. Following is some relevant info from the tiffdump utility: .\LHAFTC03-1011_20240513.Tif: Magic: 0x4949 <little-endian> Version: 0x2a Directory 0: offset 8 (0x8) next 1311542 (0x140336) SubFileType (254) LONG (4) 1<0> ImageWidth (256) LONG (4) 1<1280> ImageLength (257) LONG (4) 1<1024> BitsPerSample (258) SHORT (3) 1<8> Compression (259) LONG (4) 1<1> Photometric (262) SHORT (3) 1<1> SamplesPerPixel (277) LONG (4) 1<1> XResolution (282) RATIONAL (5) 1<72> YResolution (283) RATIONAL (5) 1<72> PlanarConfig (284) SHORT (3) 1<1> ResolutionUnit (296) SHORT (3) 1<1> TileWidth (322) SHORT (3) 1<1280> TileLength (323) SHORT (3) 1<1024> TileOffsets (324) LONG (4) 1<698> TileByteCounts (325) LONG (4) 1<702> JPEGProcessingMode (512) SHORT (3) 1<14> JPEGLosslessPredictors (517) SHORT (3) 1<1> JPEGPointTransform (518) SHORT (3) 1<0> JPEGDCTables (520) LONG (4) 1<426> <snip of custom tags>
Directory 1: offset 1311542 (0x140336) next 0 (0) SubFileType (254) LONG (4) 1<0> ImageWidth (256) LONG (4) 1<1280> ImageLength (257) LONG (4) 1<1024> BitsPerSample (258) SHORT (3) 1<8> Compression (259) LONG (4) 1<1> Photometric (262) SHORT (3) 1<1> SamplesPerPixel (277) LONG (4) 1<1> XResolution (282) RATIONAL (5) 1<72> YResolution (283) RATIONAL (5) 1<72> PlanarConfig (284) SHORT (3) 1<1> ResolutionUnit (296) SHORT (3) 1<1> TileWidth (322) SHORT (3) 1<1280> TileLength (323) SHORT (3) 1<1024> TileOffsets (324) LONG (4) 1<1312232> TileByteCounts (325) LONG (4) 1<1312236> JPEGProcessingMode (512) SHORT (3) 1<14> JPEGLosslessPredictors (517) SHORT (3) 1<1> JPEGPointTransform (518) SHORT (3) 1<0> JPEGDCTables (520) LONG (4) 1<426> <snip of custom tags>
Note from the above that TileWidth is 1280 and TileLength is 1024 with Compression set to 1 (none). This being the case, the byte count for each tile should be 1310720 (1280 x 1024). You can see in the above data that TileByteCounts is not 1310720 which is fine because in both cases for the two IFDs the values are offsets from the beginning of the file.
I think a special case is being encounter because TileOffsets has a value/offset of 1 meaning there is only one tile per IFD. What I believe is happening is that because the data type for these tags is LONG (i.e., 4 bytes), and because there is only 1 tile per IFD, the ImageEn code is interpreting this to mean that the values are stored in the TIFF IFD Entry’s Value/Offset bytes (i.e., bytes 8 – 11) when in fact the file offset is stored. This is because TileOffsets and TileByteCounts are special and your code treats them that way in some places, but not all. If you look at your function TIETIFFIFDReader.ReadIFD, you have the following code… <snip>
// Convert TAGS to LittleEndian (if needed)
if not LittleEndian then
for i := 0 to NumTags - 1 do
begin
if assigned(IFD) then
begin
// classic TIFF
IFD[i].IdTag := IESwapWord(IFD[i].IdTag);
IFD[i].DataType := IESwapWord(IFD[i].DataType);
IFD[i].DataNum := IESwapDWordI( IFD[i].DataNum );
if ( IFD[i].DataNum < 0 ) or
( IFD[i].DataNum > 4 ) or // 11.4.0: Integer overflow catch
( IFD[i].DataNum * IETIFFCalcTagSize(IFD[i].DataType) > 4 ) then
IFD[i].DataPos := IESwapDWord(IFD[i].DataPos); // this is an offset
End <snip> Not that this matters since the TIFF data in question is already Little Endian, but this code illustrates that had there been more than 1 tile in the IFD you would have treated the DataPos (or what I referred to as the Value/Offset as a file offset, the comment even says that). And in the case of the data I included, the values in question are offsets, but they aren’t being treated that way. Perhaps your code needs to use the CheckPairTag procedure, or something like it, to identify special tags that are always file offsets and handle them accordingly. I believe this is needed in ReadIFD and likely elsewhere. I think this issue only showed up for me because I have 1 tile and as such the offsets weren't handled as offsets.
In the case of my data above, your TiffHandler application shows TileOffsets as 3 and TileByteCounts as 702 for the first IFD and 7 and 1312236, respectively, for the second IFD. When I click the Show Page for the first IFD it displays an all-black image. The second IFD (page) shows the image but it clearly has a shift in the pixel alignment.
At this time I don’t have an image that I am able to upload to illustrate this issue.
I appreciate any help you can provide to resolve this issue.
Tim F |
|
xequte
    
39076 Posts |
Posted - Jun 19 2025 : 21:33:24
|
Thanks for the detail, Tim.
Is there are TIFF file that you can email me privately, so we can test the issue?
Nigel Xequte Software www.imageen.com
|
 |
|
xequte
    
39076 Posts |
Posted - Jun 25 2025 : 17:43:13
|
Hi Tim
These files are not standard-compliant. They store multiple frames as tiles, whereas according to the TIFF standard, tiles should only be used to store a single large image divided into smaller sub-images (tiles).
The crash occurs because the image size stored in the TIFF matches the size of a single tile, whereas it should represent the combined size of all tiles. In the latest beta we have pushed a fix that overrides the column/row count so that only the last tile is loaded for these types of files.
From now on, applications can use TIFF_GetTile to select which tile to load, enabling full support for these unconventional TIFFs:
ImageEnView.IO.Params.TIFF_GetTile := 1; // get second tile (frame)
ImageEnView.IO.LoadFromFile('S1810_Frame6_BW.tif');
Nigel Xequte Software www.imageen.com
|
 |
|
timfa

USA
16 Posts |
Posted - Jul 10 2025 : 19:05:40
|
Hello Nigel, I'm still messing with handling tiles. The issue with tiles occur when there is only 1 tile. When that occurs, I think your code runs into issues. It first occurs in function TIETIFFHandler.ReadIFD in the following section...
// read sub tags (like StripOffsets and TileOffsets)
lp := Stream.Position;
for i := 0 to ifd.Count-1 do
begin
tag := ifd[i];
CheckPairTag(xword(tag^.IdTag), tgpos, tglen);
if tgpos > -1 then
// if (tgpos > -1) and (xdword(tag^.DataNum) > 1) then
begin
if xdword(tag^.DataNum) > 1 then
newlist := AllocMem(xdword(tag^.DataNum) * sizeof(integer))
else
newlist := @tag^.DataPos;
for j := 0 to xdword(tag^.DataNum)-1 do
begin
l := GetIntegerByCode(ifd, tglen, j); // length in bytes
buf := AllocMem(l);
Stream.Position := GetIntegerByCode(ifd, tgpos, j); // position
Stream.Read(pbyte(buf)^, l);
newlist[j] := xdword( fBuffers.Add(buf) );
end;
if IETIFFCalcTagSize(xword(tag^.DataType)) * xdword(tag^.DataNum) > 4 then
freemem(pointer( fBuffers[xdword(tag^.DataPos)] ));
tag^.DataType := xword(4); // long type
if xdword(tag^.DataNum) > 1 then
tag^.DataPos := xdword( fBuffers.Add(newlist) ); // replace with the new list
end;
end;
I think your line "if tgpos > -1 then" needs to be altered as I show in the commented out line that follows it / your line such that when the tag length (or DataNum as you named it) is 1, the code in the IF block is skipped. Otherwise, you are corrupting tag^.DataPos when you execute the statement "newlist[j] := xdword( fBuffers.Add(buf) );". When DataNum is 1, DataPos for TileByteCounts is the address where the single tile's image data resides and DataPos for TileByteCounts is already set to the number of bytes in the tile so neither need to be altered in this loop.
In my testing, I used my version of the IF statement and things seem to work okay in TiffHandler. However, in my real code when I try to load the tile, the code loads a garbage tile image which I suspect is due to some other location in your code not handling tag^.DataPos correctly for cases where the tag DataNum is 1 for the TileOffsets and/or TileByteCounts tags.
Maybe the issue is in "procedure ProcessOffsetsTag(ntag: integer; var ByteCountTag: pointer);" in the line... pd := pdwordarray(ByteCountTag); And the associated " procedure ReadByteCountTag(ntag: integer; var ptr: pointer);" where it does a "CopyMemory(ptr, @DataPos, sz);".
I don't know if the above makes sense, but tile handling when there is only 1 tile has an issue.
Tim F |
 |
|
xequte
    
39076 Posts |
Posted - Jul 10 2025 : 22:02:57
|
Hi Tim
Before we investigate this further, can you confirm the issue still exists in our latest beta (as we have made a number of TIFF changes).
I have just emailed it to you.
Nigel Xequte Software www.imageen.com
|
 |
|
timfa

USA
16 Posts |
Posted - Jul 14 2025 : 13:38:31
|
I didn't get the email. How can I get the Beta?
Tim F |
 |
|
xequte
    
39076 Posts |
Posted - Jul 14 2025 : 18:44:18
|
Hi Tim
It is included in v14.2.0, so you might just want to download that.
Nigel Xequte Software www.imageen.com
|
 |
|
|
Topic  |
|
|
|