Skip to content

Commit 8dfee77

Browse files
authored
tiffload: add 16-bit float support (libvips#3626)
1 parent e9c5a31 commit 8dfee77

File tree

2 files changed

+92
-7
lines changed

2 files changed

+92
-7
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- remove libgsf dependency in favor of libarchive [kleisauke]
2424
- better chunking for small shrinks [jcupitt]
2525
- use alpha range of 0.0 - 1.0 for scRGB images [DarthSim]
26+
- add support for 16-bit float TIFFs [DarthSim]
2627

2728
18/9/23 8.14.5
2829

libvips/foreign/tiff2vips.c

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,27 @@ typedef struct _Rtiff {
425425
int y_pos;
426426
} Rtiff;
427427

428+
/* Convert IEEE 754-2008 16-bit float to 32-bit float
429+
*/
430+
static inline float
431+
half_2_float(gushort h)
432+
{
433+
const float sign = (h >> 15) * -2 + 1;
434+
const int exp = ((h & 0x7C00) >> 10) - 15;
435+
const float prec = (h & 0x03FF);
436+
437+
switch (exp) {
438+
case 16:
439+
return INFINITY * sign;
440+
case -15:
441+
return sign / (float) (1 << 14) * (prec / 1024.0);
442+
default:
443+
return exp > 0
444+
? sign * (float) (1 << exp) * (1.0 + prec / 1024.0)
445+
: sign / (float) (1 << -exp) * (1.0 + prec / 1024.0);
446+
}
447+
}
448+
428449
/* Test for field exists.
429450
*/
430451
static int
@@ -869,6 +890,8 @@ rtiff_guess_format(Rtiff *rtiff)
869890
return VIPS_FORMAT_SHORT;
870891
if (sample_format == SAMPLEFORMAT_UINT)
871892
return VIPS_FORMAT_USHORT;
893+
if (sample_format == SAMPLEFORMAT_IEEEFP)
894+
return VIPS_FORMAT_FLOAT;
872895
break;
873896

874897
case 32:
@@ -1229,13 +1252,37 @@ rtiff_parse_fourbit(Rtiff *rtiff, VipsImage *out)
12291252
} \
12301253
}
12311254

1255+
/* GREY_LOOP implementation for 16-bit float
1256+
*/
1257+
#define GREY_LOOP_F16 \
1258+
{ \
1259+
gushort *p1; \
1260+
float *q1; \
1261+
\
1262+
p1 = (gushort *) p; \
1263+
q1 = (float *) q; \
1264+
for (x = 0; x < n; x++) { \
1265+
if (invert) \
1266+
q1[0] = 1.0 - half_2_float(p1[0]); \
1267+
else \
1268+
q1[0] = half_2_float(p1[0]); \
1269+
\
1270+
for (i = 1; i < samples_per_pixel; i++) \
1271+
q1[i] = half_2_float(p1[i]); \
1272+
\
1273+
q1 += samples_per_pixel; \
1274+
p1 += samples_per_pixel; \
1275+
} \
1276+
}
1277+
12321278
/* Per-scanline process function for greyscale images.
12331279
*/
12341280
static void
12351281
rtiff_greyscale_line(Rtiff *rtiff,
12361282
VipsPel *q, VipsPel *p, int n, void *client)
12371283
{
12381284
int samples_per_pixel = rtiff->header.samples_per_pixel;
1285+
int bits_per_sample = rtiff->header.bits_per_sample;
12391286
int photometric_interpretation =
12401287
rtiff->header.photometric_interpretation;
12411288
VipsBandFormat format = rtiff_guess_format(rtiff);
@@ -1274,7 +1321,12 @@ rtiff_greyscale_line(Rtiff *rtiff,
12741321
break;
12751322

12761323
case VIPS_FORMAT_FLOAT:
1277-
GREY_LOOP(float, 1.0);
1324+
if (bits_per_sample == 16) {
1325+
GREY_LOOP_F16;
1326+
}
1327+
else {
1328+
GREY_LOOP(float, 1.0);
1329+
}
12781330
break;
12791331

12801332
case VIPS_FORMAT_DOUBLE:
@@ -1565,6 +1617,27 @@ rtiff_memcpy_line(Rtiff *rtiff, VipsPel *q, VipsPel *p, int n, void *client)
15651617
memcpy(q, p, len);
15661618
}
15671619

1620+
/* Per-scanline process function when we just need to copy.
1621+
*/
1622+
static void
1623+
rtiff_memcpy_f16_line(Rtiff *rtiff, VipsPel *q, VipsPel *p, int n, void *client)
1624+
{
1625+
VipsImage *im = (VipsImage *) client;
1626+
size_t len = n * im->Bands;
1627+
1628+
if (im->BandFmt == VIPS_FORMAT_COMPLEX ||
1629+
im->BandFmt == VIPS_FORMAT_DPCOMPLEX)
1630+
len *= 2;
1631+
1632+
int i;
1633+
1634+
gushort *restrict hp = (gushort *) p;
1635+
float *restrict fq = (float *) q;
1636+
1637+
for (i = 0; i < len; i++)
1638+
fq[i] = half_2_float(hp[i]);
1639+
}
1640+
15681641
/* Read a regular multiband image where we can just copy pixels from the tiff
15691642
* buffer.
15701643
*/
@@ -1574,6 +1647,8 @@ rtiff_parse_copy(Rtiff *rtiff, VipsImage *out)
15741647
int samples_per_pixel = rtiff->header.samples_per_pixel;
15751648
int photometric_interpretation =
15761649
rtiff->header.photometric_interpretation;
1650+
int bits_per_sample = rtiff->header.bits_per_sample;
1651+
int sample_format = rtiff->header.sample_format;
15771652
int inkset = rtiff->header.inkset;
15781653

15791654
if (rtiff_non_fractional(rtiff))
@@ -1608,15 +1683,20 @@ rtiff_parse_copy(Rtiff *rtiff, VipsImage *out)
16081683
else
16091684
out->Type = VIPS_INTERPRETATION_MULTIBAND;
16101685

1611-
rtiff->sfn = rtiff_memcpy_line;
16121686
rtiff->client = out;
16131687

1614-
/* We expand YCBCR images to RGB using JPEGCOLORMODE_RGB, and this
1615-
* means we need a slightly larger read buffer for the edge pixels. In
1616-
* turn, this means we can't just memcpy to libvips regions.
1617-
*/
1618-
rtiff->memcpy = photometric_interpretation != PHOTOMETRIC_YCBCR;
1688+
if (bits_per_sample == 16 && sample_format == SAMPLEFORMAT_IEEEFP) {
1689+
rtiff->sfn = rtiff_memcpy_f16_line;
1690+
}
1691+
else {
1692+
rtiff->sfn = rtiff_memcpy_line;
16191693

1694+
/* We expand YCBCR images to RGB using JPEGCOLORMODE_RGB, and this
1695+
* means we need a slightly larger read buffer for the edge pixels. In
1696+
* turn, this means we can't just memcpy to libvips regions.
1697+
*/
1698+
rtiff->memcpy = photometric_interpretation != PHOTOMETRIC_YCBCR;
1699+
}
16201700
return 0;
16211701
}
16221702

@@ -2695,6 +2775,10 @@ rtiff_read_stripwise(Rtiff *rtiff, VipsImage *out)
26952775
else
26962776
vips_line_size = VIPS_IMAGE_SIZEOF_LINE(t[0]);
26972777

2778+
if (rtiff->header.bits_per_sample == 16 &&
2779+
rtiff->header.sample_format == SAMPLEFORMAT_IEEEFP)
2780+
vips_line_size /= 2;
2781+
26982782
if (vips_line_size != rtiff->header.scanline_size) {
26992783
vips_error("tiff2vips",
27002784
"%s", _("unsupported tiff image type"));

0 commit comments

Comments
 (0)