All formats

libtiff

LanguageC (usable from C++)
Licenselibtiff license (permissive, BSD-like)
Repositorygitlab.com/libtiff/libtiff
Documentationlibtiff.gitlab.io/libtiff
Depends onoptional zlib, libjpeg, liblzma, libzstd, libwebp, libdeflate, LERC, JBIG-KIT
Read / writeyes / yes, all baseline and most extension features: multi-page, strips and tiles, BigTIFF, 1-64 bits per sample, floating point

libtiff is the reference TIFF library. ImageMagick, GIMP, GDAL, Pillow, libvips, Qt and most other programs use it. It works at the level of IFDs and tags: you open a file, move between directories (pages), get and set tag values with TIFFGetField / TIFFSetField, and read or write strips, tiles or scanlines. Compression codecs depend on the optional libraries it was built with.

For simple reading, TIFFReadRGBAImage decodes any common TIFF (palette, grayscale, CMYK, YCbCr, 16-bit) to 8-bit RGBA. For full control use TIFFReadScanline, TIFFReadEncodedStrip or TIFFReadTile and handle the pixel layout yourself. It also ships command-line tools: tiffinfo, tiffdump, tiffcp, tiffsplit, tiffset.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libtiff-dev
# Fedora
sudo dnf install libtiff-devel
# macOS
brew install libtiff
# Windows (vcpkg)
vcpkg install tiff

With CMake:

cmake
find_package(TIFF REQUIRED)
target_link_libraries(myapp PRIVATE TIFF::TIFF)

Basic usage: read a TIFF as RGBA#

C
#include <tiffio.h>
#include <stdint.h>
#include <stdio.h>

int main(void) {
    TIFF *tif = TIFFOpen("in.tif", "r");
    if (!tif) return 1;  // libtiff prints the error

    uint32_t width = 0, height = 0;
    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);

    uint32_t *raster = (uint32_t *)_TIFFmalloc((tmsize_t)width * height * sizeof(uint32_t));
    if (!raster) {
        TIFFClose(tif);
        return 1;
    }
    // decode to ABGR packed pixels, top row first
    if (TIFFReadRGBAImageOriented(tif, width, height, raster, ORIENTATION_TOPLEFT, 0)) {
        uint32_t p = raster[0];
        printf("%u x %u, first pixel R=%u G=%u B=%u A=%u\n", width, height,
               TIFFGetR(p), TIFFGetG(p), TIFFGetB(p), TIFFGetA(p));
    }
    _TIFFfree(raster);
    TIFFClose(tif);
    return 0;
}

TIFFReadRGBAImage without “Oriented” returns the raster bottom row first.

How to write an RGB TIFF with LZW compression#

C
#include <tiffio.h>
#include <stdint.h>

int write_rgb(const char *path, const uint8_t *pixels, uint32_t width, uint32_t height) {
    TIFF *tif = TIFFOpen(path, "w");
    if (!tif) return 1;
    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
    TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
    TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL);
    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));
    TIFFSetField(tif, TIFFTAG_SOFTWARE, "my app");

    for (uint32_t y = 0; y < height; y++) {
        if (TIFFWriteScanline(tif, (void *)(pixels + (size_t)y * width * 3), y, 0) < 0) {
            TIFFClose(tif);
            return 1;
        }
    }
    TIFFClose(tif);
    return 0;
}

Other compressions: COMPRESSION_NONE, COMPRESSION_ADOBE_DEFLATE, COMPRESSION_PACKBITS, COMPRESSION_JPEG, COMPRESSION_CCITTFAX4 (1-bit only), COMPRESSION_ZSTD, COMPRESSION_WEBP. Check TIFFIsCODECConfigured(scheme) to see if your build has a codec.

How to count and iterate pages in a multi-page TIFF#

C
TIFF *tif = TIFFOpen("scan.tif", "r");
if (tif) {
    printf("%u pages\n", (unsigned)TIFFNumberOfDirectories(tif));
    do {
        uint32_t w = 0, h = 0;
        TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
        TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
        printf("page %u: %u x %u\n", (unsigned)TIFFCurrentDirectory(tif), w, h);
    } while (TIFFReadDirectory(tif));
    TIFFClose(tif);
}

TIFFSetDirectory(tif, n) jumps to page n (0-based).

How to write a multi-page TIFF#

Set the tags and write the rows of a page, then call TIFFWriteDirectory to finish it and start the next one:

C
TIFF *tif = TIFFOpen("pages.tif", "w");
for (int page = 0; page < num_pages; page++) {
    TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
    TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, num_pages);
    // ... set IMAGEWIDTH, IMAGELENGTH, BITSPERSAMPLE, etc. and write scanlines ...
    TIFFWriteDirectory(tif);
}
TIFFClose(tif);

How to read tags and print all of them#

C
char *software = NULL;
if (TIFFGetField(tif, TIFFTAG_SOFTWARE, &software))
    printf("Software: %s\n", software);

uint16_t compression = 0, bps = 0, spp = 0;
TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression);
TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
printf("compression %u, %u bits x %u samples, tiled: %d\n", compression, bps, spp, TIFFIsTiled(tif));

TIFFPrintDirectory(tif, stdout, 0);  // what tiffinfo prints

TIFFGetFieldDefaulted returns the default from the specification when the tag is missing.

How to write a BigTIFF file#

Add 8 to the mode string. Use BigTIFF for files that can exceed 4 GB:

C
TIFF *tif = TIFFOpen("big.tif", "w8");

On reading, TIFFOpen detects BigTIFF automatically and TIFFIsBigTIFF(tif) tells you which kind it is.

Tips#