Skip to content

Commit 1e619b5

Browse files
committed
feat(tiff): implement TransferRange tag
1 parent 5ad3889 commit 1e619b5

File tree

1 file changed

+25
-8
lines changed

1 file changed

+25
-8
lines changed

src/imageio/TiffImageLoader.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,31 +1104,49 @@ Task<void> postprocessRgb(
11041104
// function is a linear interpolation between these values.
11051105
tlog::debug() << "Found custom transfer function; applying...";
11061106

1107+
if (numColorChannels > 3) {
1108+
throw ImageLoadError{"TIFF images with transfer functions and more than 3 color channels are not supported."};
1109+
}
1110+
11071111
for (int c = 0; c < numColorChannels; ++c) {
11081112
if (!transferFunction[c]) {
11091113
throw ImageLoadError{fmt::format("Missing transfer function for channel {}", c)};
11101114
}
11111115
}
11121116

1113-
const float scale = 1.0f / 65535.0f;
1114-
11151117
const size_t bps = photometric == PHOTOMETRIC_PALETTE ? 16 : dataBitsPerSample;
11161118
const size_t maxIdx = (1ull << bps) - 1;
11171119

1120+
Vector3i transferRangeBlack = {0};
1121+
Vector3i transferRangeWhite = {65535};
1122+
1123+
static constexpr uint16_t TIFFTAG_TRANSFERRANGE = 342;
1124+
1125+
uint32_t numRead = 0;
1126+
if (uint16_t* transferRange; TIFFGetField(tif, TIFFTAG_TRANSFERRANGE, &numRead, &transferRange) && transferRange && numRead >= 6) {
1127+
transferRangeBlack = {transferRange[0], transferRange[2], transferRange[4]};
1128+
transferRangeWhite = {transferRange[1], transferRange[3], transferRange[5]};
1129+
1130+
tlog::debug() << fmt::format("Found transfer range [{}, {}]", transferRangeBlack, transferRangeWhite);
1131+
}
1132+
1133+
const Vector3f scale = Vector3f(1.0f) / Vector3f(transferRangeWhite - transferRangeBlack);
1134+
11181135
const size_t numPixels = (size_t)size.x() * size.y();
11191136
co_await ThreadPool::global().parallelForAsync<size_t>(
11201137
0,
11211138
numPixels,
11221139
numPixels * numColorChannels,
11231140
[&](size_t i) {
11241141
for (int c = 0; c < numColorChannels; ++c) {
1125-
float val = floatRgbaData[i * numRgbaChannels + c];
1142+
const float val = floatRgbaData[i * numRgbaChannels + c];
11261143

11271144
// Lerp the transfer function
1128-
size_t idx = clamp((size_t)(val * maxIdx), (size_t)0, maxIdx - 1);
1129-
float w = val * maxIdx - idx;
1130-
floatRgbaData[i * numRgbaChannels + c] = (1.0f - w) * transferFunction[c][idx] * scale +
1131-
w * transferFunction[c][idx + 1] * scale;
1145+
const size_t idx = clamp((size_t)(val * maxIdx) + transferRangeBlack[c], (size_t)0, maxIdx - 1);
1146+
const float w = val * maxIdx - idx - transferRangeBlack[c];
1147+
floatRgbaData[i * numRgbaChannels + c] = ((1.0f - w) * (float)transferFunction[c][idx] +
1148+
w * (float)transferFunction[c][idx + 1] - transferRangeBlack[c]) *
1149+
scale[c];
11321150
}
11331151
},
11341152
priority
@@ -1413,7 +1431,6 @@ Task<ImageData> readTiffImage(
14131431
sampleFormat = SAMPLEFORMAT_IEEEFP;
14141432

14151433
if (photometric == PHOTOMETRIC_YCBCR) {
1416-
tlog::debug() << "Converting JPEG YCbCr to RGB.";
14171434
photometric = PHOTOMETRIC_RGB;
14181435
}
14191436
}

0 commit comments

Comments
 (0)