@@ -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