Skip to content

Commit 8e0f298

Browse files
Lior LahavTheNicker
authored andcommitted
Improve TIFF extra sample handling
Adds TIFF texel format detection for 16-bit RGB and RGBA data, 64-bit floating grayscale data, 4-bit grayscale data, and signed integer grayscale samples. Handles unsupported extra-sample layouts by extracting separate channel images or returning format-not-supported instead of continuing with unknown formats; fixes extra sample extraction to use the correct per-sample source offset and neutral photometric interpretation for extracted auxiliary channels.
1 parent 7c847b2 commit 8e0f298

1 file changed

Lines changed: 90 additions & 35 deletions

File tree

Codecs/CodecTiff/Source/CodecTiff.h

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <LLUtils/Exception.h>
1010
#include <IImagePlugin.h>
1111

12+
#include <utility>
13+
1214
namespace IMCodec
1315
{
1416
class CodecTiff : public IImagePlugin
@@ -66,20 +68,15 @@ namespace IMCodec
6668
case SAMPLEFORMAT_UINT:
6769
switch (samplesPerPixel)
6870
{
69-
case 1:
70-
switch (bitsPerSample)
71-
{
72-
case 8:
73-
texelFormat = TexelFormat::I_A8;
74-
break;
75-
}
76-
break;
7771
case 3:
7872
switch (bitsPerSample)
7973
{
8074
case 8:
8175
texelFormat = TexelFormat::I_R8_G8_B8;
8276
break;
77+
case 16:
78+
texelFormat = TexelFormat::I_R16_G16_B16;
79+
break;
8380
}
8481
break;
8582
case 4:
@@ -88,6 +85,9 @@ namespace IMCodec
8885
case 8:
8986
texelFormat = TexelFormat::I_R8_G8_B8_A8;
9087
break;
88+
case 16:
89+
texelFormat = TexelFormat::I_R16_G16_B16_A16;
90+
break;
9191
}
9292
break;
9393
}
@@ -112,6 +112,9 @@ namespace IMCodec
112112
case 32:
113113
texelFormat = TexelFormat::F_X32;
114114
break;
115+
case 64:
116+
texelFormat = TexelFormat::F_X64;
117+
break;
115118
default:
116119
break;
117120
}
@@ -127,37 +130,42 @@ namespace IMCodec
127130
case 1:
128131
texelFormat = TexelFormat::I_X1;
129132
break;
133+
case 4:
134+
texelFormat = TexelFormat::I_X4;
135+
break;
130136
case 8:
131137
texelFormat = TexelFormat::I_X8;
132138
break;
133139
case 16:
134140
texelFormat = TexelFormat::I_X16;
135141
break;
136-
case SAMPLEFORMAT_INT:
137-
switch (bitsPerSample)
138-
{
139-
case 8:
140-
texelFormat = TexelFormat::S_X8;
141-
break;
142-
case 16:
143-
texelFormat = TexelFormat::S_X16;
144-
break;
145-
default:
146-
break;
147-
}
142+
}
143+
break;
144+
}
145+
break;
146+
case SAMPLEFORMAT_INT:
147+
switch (samplesPerPixel)
148+
{
149+
case 1:
150+
switch (bitsPerSample)
151+
{
152+
case 8:
153+
texelFormat = TexelFormat::S_X8;
154+
break;
155+
case 16:
156+
texelFormat = TexelFormat::S_X16;
157+
break;
158+
default:
148159
break;
149160
}
150161
break;
151162
}
163+
break;
152164
}
153165
}
154166

155-
156-
if (texelFormat == TexelFormat::UNKNOWN)
157-
LL_ERROR(LLUtils::Exception::ErrorCode::NotImplemented, "CodecTiff: unsupported floating point format.");
158-
159-
return texelFormat;
160-
}
167+
return texelFormat;
168+
}
161169

162170
std::vector<ImageItemSharedPtr> GetImage(TIFF* tiff)
163171
{
@@ -201,6 +209,9 @@ namespace IMCodec
201209
TIFFGetField(tiff, TIFFTAG_MAXSAMPLEVALUE, &maxSampleValue);*/
202210
TIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &extraSamples, &sampleTypes);
203211

212+
if (samplesPerPixel <= extraSamples)
213+
return {};
214+
204215
const uint32_t bytesPerSample = bitsPerSample / CHAR_BIT;
205216

206217
uint32_t numberOfStripts = TIFFNumberOfStrips(tiff);
@@ -229,17 +240,38 @@ namespace IMCodec
229240
//If there's an alpha channel and it's interleaved in the image data, return as single image with alpha channel
230241
imageItem->descriptor.texelFormatDecompressed = imageItem->descriptor.texelFormatStorage = mainChannelTexelFormat;
231242

232-
if (planarConfig == PLANARCONFIG_CONTIG && extraSamples == 1 && sampleTypes[0] > 0)
243+
auto extractExtraSampleImages = [&]()
233244
{
234-
imageItem->descriptor.texelFormatStorage = imageItem->descriptor.texelFormatDecompressed = GetTexelFormat(sampleFormat, bitsPerSample, samplesPerPixel, photoMetric);
245+
return ExtactExtraSamples(width, height, rowPitch, samplesPerPixel, extraSamples, planarConfig
246+
, bytesPerSample, imageItem->data, photoMetric, bitsPerSample, sampleFormat);
247+
};
248+
249+
if (planarConfig == PLANARCONFIG_CONTIG && extraSamples > 0)
250+
{
251+
const auto interleavedTexelFormat = GetTexelFormat(sampleFormat, bitsPerSample, samplesPerPixel, photoMetric);
252+
253+
if (interleavedTexelFormat != TexelFormat::UNKNOWN)
254+
{
255+
imageItem->descriptor.texelFormatStorage = imageItem->descriptor.texelFormatDecompressed = interleavedTexelFormat;
256+
}
257+
else
258+
{
259+
auto extraChannelImages = extractExtraSampleImages();
260+
261+
if (extraChannelImages.empty() == false)
262+
imageItems = extraChannelImages;
263+
else
264+
return {};
265+
}
235266
}
236-
else
267+
else if (extraSamples > 0)
237268
{
238-
auto extraChannelImages = ExtactExtraSamples(width, height, rowPitch, samplesPerPixel, extraSamples, planarConfig
239-
, bytesPerSample, imageItem->data, photoMetric, bitsPerSample, sampleFormat);
269+
auto extraChannelImages = extractExtraSampleImages();
240270

241271
if (extraChannelImages.empty() == false)
242272
imageItems = extraChannelImages;
273+
else
274+
return {};
243275
}
244276
}
245277
else
@@ -274,9 +306,13 @@ namespace IMCodec
274306
{
275307

276308
const auto sampleSize = sampleCount * bytesPerSample;
309+
const auto samplePhotometric = sampleOffset == 0 ? photometric : PHOTOMETRIC_MINISBLACK;
277310

278311
auto imageItem = std::make_shared<ImageItem>();
279-
imageItem->descriptor.texelFormatStorage = GetTexelFormat(sampleFormat, bitsPerSample , sampleCount, photometric);
312+
imageItem->descriptor.texelFormatStorage = GetTexelFormat(sampleFormat, bitsPerSample , sampleCount, samplePhotometric);
313+
if (imageItem->descriptor.texelFormatStorage == TexelFormat::UNKNOWN)
314+
return {};
315+
280316
imageItem->descriptor.texelFormatDecompressed = imageItem->descriptor.texelFormatStorage;
281317
imageItem->descriptor.height = height;
282318
imageItem->descriptor.width = width;
@@ -293,7 +329,7 @@ namespace IMCodec
293329
{
294330
for (size_t x = 0; x < width; x++)
295331
{
296-
const size_t sourceRowIndex = sampleOffset * sampleSize + x * samplesPerPixel * bytesPerSample;
332+
const size_t sourceRowIndex = sampleOffset * bytesPerSample + x * samplesPerPixel * bytesPerSample;
297333
const size_t destRowIndex = x * sampleSize;
298334
memcpy(image.data() + destOffset + destRowIndex, sourceBuffer.data() + sourceOffset + sourceRowIndex, sampleSize);
299335
}
@@ -318,12 +354,25 @@ namespace IMCodec
318354

319355
if (extraSmaples > 0)
320356
{
357+
if (bytesPerSample == 0 || bitsPerSample % CHAR_BIT != 0 || samplesPerPixel <= extraSmaples)
358+
return {};
359+
321360
const auto mainImageSampleCount = samplesPerPixel - extraSmaples;
322361

323-
subChannels.push_back(extractSamples(0, mainImageSampleCount));
362+
auto mainImage = extractSamples(0, mainImageSampleCount);
363+
if (mainImage == nullptr)
364+
return {};
365+
366+
subChannels.push_back(std::move(mainImage));
324367

325368
for (int i = 0; i < extraSmaples; i++)
326-
subChannels.push_back(extractSamples(mainImageSampleCount + i, 1));
369+
{
370+
auto extraImage = extractSamples(mainImageSampleCount + i, 1);
371+
if (extraImage == nullptr)
372+
return {};
373+
374+
subChannels.push_back(std::move(extraImage));
375+
}
327376
}
328377

329378

@@ -344,6 +393,9 @@ namespace IMCodec
344393
int currentSubImage = 0;
345394

346395
auto firstImageItemChannles = GetImage(tiff);
396+
if (firstImageItemChannles.empty())
397+
return ImageResult::FormatNotSupported;
398+
347399
firstImageItem = firstImageItemChannles.at(0);
348400

349401
if (firstImageItemChannles.size() > 1)
@@ -365,6 +417,9 @@ namespace IMCodec
365417
}
366418

367419
auto nextImages = GetImage(tiff);
420+
if (nextImages.empty())
421+
return ImageResult::FormatNotSupported;
422+
368423
for (size_t i = 0; i < nextImages.size(); i++)
369424
{
370425
subImages.push_back(std::make_shared<Image>(nextImages.at(i), ImageItemType::Pages));

0 commit comments

Comments
 (0)