Commit e5540c04 for libheif
commit e5540c042d9fec7e4d5863279182c83f24f364f9
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Mon Feb 16 14:27:54 2026 +0100
added a reader for tiled TIFFs, keeping the same tiling in the HEIF
diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc
index f13a377b..32a6c3d7 100644
--- a/examples/heif_enc.cc
+++ b/examples/heif_enc.cc
@@ -1037,6 +1037,46 @@ private:
};
+#if HAVE_LIBTIFF
+class input_tiles_generator_tiff : public input_tiles_generator
+{
+public:
+ input_tiles_generator_tiff(std::unique_ptr<TiledTiffReader> reader)
+ : m_reader(std::move(reader))
+ {
+ }
+
+ uint32_t nColumns() const override { return m_reader->nColumns(); }
+ uint32_t nRows() const override { return m_reader->nRows(); }
+
+ InputImage get_image(uint32_t tx, uint32_t ty, int /*output_bit_depth*/) override
+ {
+ heif_image* tile_image = nullptr;
+ heif_error err = m_reader->readTile(tx, ty, &tile_image);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Error reading TIFF tile " << tx << "," << ty << ": " << err.message << "\n";
+ exit(1);
+ }
+
+ InputImage input;
+ input.image = std::shared_ptr<heif_image>(tile_image,
+ [](heif_image* img) { heif_image_release(img); });
+ return input;
+ }
+
+ uint32_t imageWidth() const { return m_reader->imageWidth(); }
+ uint32_t imageHeight() const { return m_reader->imageHeight(); }
+ uint32_t tileWidth() const { return m_reader->tileWidth(); }
+ uint32_t tileHeight() const { return m_reader->tileHeight(); }
+
+ void readExif(InputImage* input_image) { m_reader->readExif(input_image); }
+
+private:
+ std::unique_ptr<TiledTiffReader> m_reader;
+};
+#endif
+
+
// TODO: we have to attach the input image Exif and XMP to the tiled image
heif_image_handle* encode_tiled(heif_context* ctx, heif_encoder* encoder, heif_encoding_options* options,
int output_bit_depth,
@@ -1830,12 +1870,57 @@ int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding
for (std::string input_filename : args) {
- InputImage input_image = load_image(input_filename, output_bit_depth);
+ InputImage input_image;
+ heif_image_tiling tiling{};
+ std::shared_ptr<input_tiles_generator> tile_generator;
+
+#if HAVE_LIBTIFF
+ // Auto-detect tiled TIFFs when not using explicit tiling options
+ if (!use_tiling && cut_tiles == 0) {
+ std::string suffix;
+ auto suffix_pos = input_filename.find_last_of('.');
+ if (suffix_pos != std::string::npos) {
+ suffix = input_filename.substr(suffix_pos + 1);
+ std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);
+ }
+
+ if (suffix == "tif" || suffix == "tiff") {
+ heif_error tiff_err;
+ auto tiff_reader = TiledTiffReader::open(input_filename.c_str(), &tiff_err);
+ if (tiff_err.code != heif_error_Ok) {
+ std::cerr << "Error opening TIFF: " << tiff_err.message << "\n";
+ return 1;
+ }
+
+ if (tiff_reader) {
+ auto tiff_gen = std::make_shared<input_tiles_generator_tiff>(std::move(tiff_reader));
+
+ // Read tile (0,0) as representative for nclx profile
+ input_image = tiff_gen->get_image(0, 0, output_bit_depth);
+ tiff_gen->readExif(&input_image);
+
+ tiling.version = 1;
+ tiling.num_columns = tiff_gen->nColumns();
+ tiling.num_rows = tiff_gen->nRows();
+ tiling.tile_width = tiff_gen->tileWidth();
+ tiling.tile_height = tiff_gen->tileHeight();
+ tiling.image_width = tiff_gen->imageWidth();
+ tiling.image_height = tiff_gen->imageHeight();
+ tiling.number_of_extra_dimensions = 0;
+
+ tile_generator = tiff_gen;
+ }
+ }
+ }
+#endif
+
+ // If no tiled TIFF was detected, load the image normally
+ if (!input_image.image) {
+ input_image = load_image(input_filename, output_bit_depth);
+ }
std::shared_ptr<heif_image> image = input_image.image;
- heif_image_tiling tiling{};
- std::shared_ptr<input_tiles_generator> tile_generator;
if (use_tiling) {
tile_generator = determine_input_images_tiling(input_filename, tiled_input_x_y);
if (tile_generator) {
@@ -1900,7 +1985,7 @@ int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding
heif_image_handle* handle;
- if (use_tiling || cut_tiles > 0) {
+ if (tile_generator) {
handle = encode_tiled(context, encoder, options, output_bit_depth, tile_generator, tiling);
}
else {
diff --git a/heifio/decoder_tiff.cc b/heifio/decoder_tiff.cc
index 5f9ccb56..246aa86d 100644
--- a/heifio/decoder_tiff.cc
+++ b/heifio/decoder_tiff.cc
@@ -399,80 +399,215 @@ static void suppress_warnings(const char* module, const char* fmt, va_list ap) {
}
-heif_error loadTIFF(const char* filename, InputImage *input_image) {
- TIFFSetWarningHandler(suppress_warnings);
-
- std::unique_ptr<TIFF, void(*)(TIFF*)> tifPtr(TIFFOpen(filename, "r"), [](TIFF* tif) { TIFFClose(tif); });
- if (!tifPtr) {
- struct heif_error err = {
- .code = heif_error_Invalid_input,
- .subcode = heif_suberror_Unspecified,
- .message = "Cannot open TIFF ile"};
- return err;
- }
-
- TIFF* tif = tifPtr.get();
- if (TIFFIsTiled(tif)) {
- struct heif_error err = {
- .code = heif_error_Unsupported_feature,
- .subcode = heif_suberror_Unspecified,
- .message = "Tiled TIFF images are not supported yet"};
- return err;
- }
-
- uint16_t shortv, samplesPerPixel, bps, config, format;
+static heif_error validateTiffFormat(TIFF* tif, uint16_t& samplesPerPixel, uint16_t& bps, uint16_t& config, bool& hasAlpha)
+{
+ uint16_t shortv;
if (TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &shortv) && shortv == PHOTOMETRIC_PALETTE) {
- struct heif_error err = {
- .code = heif_error_Unsupported_feature,
- .subcode = heif_suberror_Unspecified,
- .message = "Palette TIFF images are not supported yet"};
- return err;
+ return {heif_error_Unsupported_feature, heif_suberror_Unspecified,
+ "Palette TIFF images are not supported yet"};
}
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config);
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
if (samplesPerPixel != 1 && samplesPerPixel != 3 && samplesPerPixel != 4) {
- struct heif_error err = {
- .code = heif_error_Invalid_input,
- .subcode = heif_suberror_Unspecified,
- .message = "Only 1, 3 and 4 samples per pixel are supported."};
- return err;
+ return {heif_error_Invalid_input, heif_suberror_Unspecified,
+ "Only 1, 3 and 4 samples per pixel are supported."};
+ }
+
+ // Determine whether the 4th sample is true alpha or an unrelated extra sample
+ hasAlpha = false;
+ if (samplesPerPixel == 4) {
+ uint16_t extraCount = 0;
+ uint16_t* extraTypes = nullptr;
+ if (TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extraCount, &extraTypes) && extraCount > 0) {
+ hasAlpha = (extraTypes[0] == EXTRASAMPLE_ASSOCALPHA || extraTypes[0] == EXTRASAMPLE_UNASSALPHA);
+ }
+ else {
+ // No EXTRASAMPLES tag with 4 spp — assume RGBA for backward compatibility
+ hasAlpha = true;
+ }
}
TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps);
- if (bps != 8) {
- struct heif_error err = {
- .code = heif_error_Invalid_input,
- .subcode = heif_suberror_Unspecified,
- .message = "Only 8 bits per sample are supported."};
- return err;
+ if (bps != 8) {
+ return {heif_error_Invalid_input, heif_suberror_Unspecified,
+ "Only 8 bits per sample are supported."};
}
+ uint16_t format;
if (TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &format) && format != SAMPLEFORMAT_UINT) {
- struct heif_error err = {
- .code = heif_error_Invalid_input,
- .subcode = heif_suberror_Unspecified,
- .message = "Only UINT sample format is supported."};
- return err;
+ return {heif_error_Invalid_input, heif_suberror_Unspecified,
+ "Only UINT sample format is supported."};
+ }
+
+ return heif_error_ok;
+}
+
+
+static heif_error readTiledContiguous(TIFF* tif, uint32_t width, uint32_t height,
+ uint32_t tile_width, uint32_t tile_height,
+ uint16_t samplesPerPixel, bool hasAlpha, heif_image** out_image)
+{
+ uint16_t outSpp = (samplesPerPixel == 4 && !hasAlpha) ? 3 : samplesPerPixel;
+ heif_chroma chroma = (outSpp == 1) ? heif_chroma_monochrome
+ : (outSpp == 4) ? heif_chroma_interleaved_RGBA
+ : heif_chroma_interleaved_RGB;
+ heif_colorspace colorspace = (outSpp == 1) ? heif_colorspace_monochrome : heif_colorspace_RGB;
+ heif_channel channel = (outSpp == 1) ? heif_channel_Y : heif_channel_interleaved;
+
+ heif_error err = heif_image_create((int)width, (int)height, colorspace, chroma, out_image);
+ if (err.code != heif_error_Ok) return err;
+
+ heif_image_add_plane(*out_image, channel, (int)width, (int)height, outSpp * 8);
+
+ size_t out_stride;
+ uint8_t* out_plane = heif_image_get_plane2(*out_image, channel, &out_stride);
+
+ tmsize_t tile_buf_size = TIFFTileSize(tif);
+ std::vector<uint8_t> tile_buf(tile_buf_size);
+
+ uint32_t n_cols = (width + tile_width - 1) / tile_width;
+ uint32_t n_rows = (height + tile_height - 1) / tile_height;
+
+ for (uint32_t ty = 0; ty < n_rows; ty++) {
+ for (uint32_t tx = 0; tx < n_cols; tx++) {
+ tmsize_t read = TIFFReadEncodedTile(tif, TIFFComputeTile(tif, tx * tile_width, ty * tile_height, 0, 0),
+ tile_buf.data(), tile_buf_size);
+ if (read < 0) {
+ heif_image_release(*out_image);
+ *out_image = nullptr;
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Failed to read TIFF tile"};
+ }
+
+ uint32_t actual_w = std::min(tile_width, width - tx * tile_width);
+ uint32_t actual_h = std::min(tile_height, height - ty * tile_height);
+
+ for (uint32_t row = 0; row < actual_h; row++) {
+ uint8_t* dst = out_plane + (ty * tile_height + row) * out_stride + tx * tile_width * outSpp;
+ uint8_t* src = tile_buf.data() + row * tile_width * samplesPerPixel;
+ if (outSpp == samplesPerPixel) {
+ memcpy(dst, src, actual_w * outSpp);
+ }
+ else {
+ // Strip extra sample (RGBX -> RGB)
+ for (uint32_t x = 0; x < actual_w; x++) {
+ memcpy(dst + x * outSpp, src + x * samplesPerPixel, outSpp);
+ }
+ }
+ }
+ }
+ }
+
+ return heif_error_ok;
+}
+
+
+static heif_error readTiledSeparate(TIFF* tif, uint32_t width, uint32_t height,
+ uint32_t tile_width, uint32_t tile_height,
+ uint16_t samplesPerPixel, bool hasAlpha, heif_image** out_image)
+{
+ uint16_t outSpp = (samplesPerPixel == 4 && !hasAlpha) ? 3 : samplesPerPixel;
+ heif_chroma chroma = (outSpp == 1) ? heif_chroma_monochrome
+ : (outSpp == 4) ? heif_chroma_interleaved_RGBA
+ : heif_chroma_interleaved_RGB;
+ heif_colorspace colorspace = (outSpp == 1) ? heif_colorspace_monochrome : heif_colorspace_RGB;
+ heif_channel channel = (outSpp == 1) ? heif_channel_Y : heif_channel_interleaved;
+
+ heif_error err = heif_image_create((int)width, (int)height, colorspace, chroma, out_image);
+ if (err.code != heif_error_Ok) return err;
+
+ heif_image_add_plane(*out_image, channel, (int)width, (int)height, outSpp * 8);
+
+ size_t out_stride;
+ uint8_t* out_plane = heif_image_get_plane2(*out_image, channel, &out_stride);
+
+ tmsize_t tile_buf_size = TIFFTileSize(tif);
+ std::vector<uint8_t> tile_buf(tile_buf_size);
+
+ uint32_t n_cols = (width + tile_width - 1) / tile_width;
+ uint32_t n_rows = (height + tile_height - 1) / tile_height;
+
+ // Only interleave the first outSpp planes (skip the extra sample plane if !hasAlpha)
+ for (uint16_t s = 0; s < outSpp; s++) {
+ for (uint32_t ty = 0; ty < n_rows; ty++) {
+ for (uint32_t tx = 0; tx < n_cols; tx++) {
+ tmsize_t read = TIFFReadEncodedTile(tif, TIFFComputeTile(tif, tx * tile_width, ty * tile_height, 0, s),
+ tile_buf.data(), tile_buf_size);
+ if (read < 0) {
+ heif_image_release(*out_image);
+ *out_image = nullptr;
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Failed to read TIFF tile"};
+ }
+
+ uint32_t actual_w = std::min(tile_width, width - tx * tile_width);
+ uint32_t actual_h = std::min(tile_height, height - ty * tile_height);
+
+ for (uint32_t row = 0; row < actual_h; row++) {
+ uint8_t* dst = out_plane + (ty * tile_height + row) * out_stride + tx * tile_width * outSpp + s;
+ uint8_t* src = tile_buf.data() + row * tile_width;
+ for (uint32_t x = 0; x < actual_w; x++) {
+ dst[x * outSpp] = src[x];
+ }
+ }
+ }
+ }
+ }
+
+ return heif_error_ok;
+}
+
+
+heif_error loadTIFF(const char* filename, InputImage *input_image) {
+ TIFFSetWarningHandler(suppress_warnings);
+
+ std::unique_ptr<TIFF, void(*)(TIFF*)> tifPtr(TIFFOpen(filename, "r"), [](TIFF* tif) { TIFFClose(tif); });
+ if (!tifPtr) {
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Cannot open TIFF file"};
}
- struct heif_error err;
+ TIFF* tif = tifPtr.get();
+
+ uint16_t samplesPerPixel, bps, config;
+ bool hasAlpha;
+ heif_error err = validateTiffFormat(tif, samplesPerPixel, bps, config, hasAlpha);
+ if (err.code != heif_error_Ok) return err;
+
struct heif_image* image = nullptr;
- switch (config) {
- case PLANARCONFIG_CONTIG:
- err = readPixelInterleave(tif, samplesPerPixel, &image);
- break;
- case PLANARCONFIG_SEPARATE:
- err = readBandInterleave(tif, samplesPerPixel, &image);
- break;
- default:
- struct heif_error err = {
- .code = heif_error_Invalid_input,
- .subcode = heif_suberror_Unspecified,
- .message = "Unsupported planar configuration"};
- return err;
+ if (TIFFIsTiled(tif)) {
+ uint32_t width, height, tile_width, tile_height;
+ err = getImageWidthAndHeight(tif, width, height);
+ if (err.code != heif_error_Ok) return err;
+
+ if (!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) ||
+ !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height)) {
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Cannot read TIFF tile dimensions"};
+ }
+
+ switch (config) {
+ case PLANARCONFIG_CONTIG:
+ err = readTiledContiguous(tif, width, height, tile_width, tile_height, samplesPerPixel, hasAlpha, &image);
+ break;
+ case PLANARCONFIG_SEPARATE:
+ err = readTiledSeparate(tif, width, height, tile_width, tile_height, samplesPerPixel, hasAlpha, &image);
+ break;
+ default:
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Unsupported planar configuration"};
+ }
}
+ else {
+ switch (config) {
+ case PLANARCONFIG_CONTIG:
+ err = readPixelInterleave(tif, samplesPerPixel, &image);
+ break;
+ case PLANARCONFIG_SEPARATE:
+ err = readBandInterleave(tif, samplesPerPixel, &image);
+ break;
+ default:
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Unsupported planar configuration"};
+ }
+ }
+
if (err.code != heif_error_Ok) {
return err;
}
@@ -490,3 +625,144 @@ heif_error loadTIFF(const char* filename, InputImage *input_image) {
return heif_error_ok;
}
+
+// --- TiledTiffReader ---
+
+void TiledTiffReader::TiffCloser::operator()(void* tif) const {
+ if (tif) {
+ TIFFClose(static_cast<TIFF*>(tif));
+ }
+}
+
+
+std::unique_ptr<TiledTiffReader> TiledTiffReader::open(const char* filename, heif_error* out_err)
+{
+ TIFFSetWarningHandler(suppress_warnings);
+
+ TIFF* tif = TIFFOpen(filename, "r");
+ if (!tif) {
+ *out_err = {heif_error_Invalid_input, heif_suberror_Unspecified, "Cannot open TIFF file"};
+ return nullptr;
+ }
+
+ if (!TIFFIsTiled(tif)) {
+ TIFFClose(tif);
+ *out_err = heif_error_ok;
+ return nullptr;
+ }
+
+ auto reader = std::unique_ptr<TiledTiffReader>(new TiledTiffReader());
+ reader->m_tif.reset(tif);
+
+ uint16_t bps;
+ heif_error err = validateTiffFormat(tif, reader->m_samples_per_pixel, bps, reader->m_planar_config, reader->m_has_alpha);
+ if (err.code != heif_error_Ok) {
+ *out_err = err;
+ return nullptr;
+ }
+ reader->m_bits_per_sample = bps;
+
+ if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &reader->m_image_width) ||
+ !TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &reader->m_image_height)) {
+ *out_err = {heif_error_Invalid_input, heif_suberror_Unspecified, "Cannot read TIFF image dimensions"};
+ return nullptr;
+ }
+
+ if (!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &reader->m_tile_width) ||
+ !TIFFGetField(tif, TIFFTAG_TILELENGTH, &reader->m_tile_height)) {
+ *out_err = {heif_error_Invalid_input, heif_suberror_Unspecified, "Cannot read TIFF tile dimensions"};
+ return nullptr;
+ }
+
+ reader->m_n_columns = (reader->m_image_width + reader->m_tile_width - 1) / reader->m_tile_width;
+ reader->m_n_rows = (reader->m_image_height + reader->m_tile_height - 1) / reader->m_tile_height;
+
+ *out_err = heif_error_ok;
+ return reader;
+}
+
+
+TiledTiffReader::~TiledTiffReader() = default;
+
+
+heif_error TiledTiffReader::readTile(uint32_t tx, uint32_t ty, heif_image** out_image)
+{
+ TIFF* tif = static_cast<TIFF*>(m_tif.get());
+
+ uint32_t actual_w = std::min(m_tile_width, m_image_width - tx * m_tile_width);
+ uint32_t actual_h = std::min(m_tile_height, m_image_height - ty * m_tile_height);
+
+ uint16_t outSpp = (m_samples_per_pixel == 4 && !m_has_alpha) ? 3 : m_samples_per_pixel;
+ heif_chroma chroma = (outSpp == 1) ? heif_chroma_monochrome
+ : (outSpp == 4) ? heif_chroma_interleaved_RGBA
+ : heif_chroma_interleaved_RGB;
+ heif_colorspace colorspace = (outSpp == 1) ? heif_colorspace_monochrome : heif_colorspace_RGB;
+ heif_channel channel = (outSpp == 1) ? heif_channel_Y : heif_channel_interleaved;
+
+ heif_error err = heif_image_create((int)actual_w, (int)actual_h, colorspace, chroma, out_image);
+ if (err.code != heif_error_Ok) return err;
+
+ heif_image_add_plane(*out_image, channel, (int)actual_w, (int)actual_h, outSpp * 8);
+
+ size_t out_stride;
+ uint8_t* out_plane = heif_image_get_plane2(*out_image, channel, &out_stride);
+
+ tmsize_t tile_buf_size = TIFFTileSize(tif);
+ std::vector<uint8_t> tile_buf(tile_buf_size);
+
+ if (m_planar_config == PLANARCONFIG_CONTIG) {
+ tmsize_t read = TIFFReadEncodedTile(tif, TIFFComputeTile(tif, tx * m_tile_width, ty * m_tile_height, 0, 0),
+ tile_buf.data(), tile_buf_size);
+ if (read < 0) {
+ heif_image_release(*out_image);
+ *out_image = nullptr;
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Failed to read TIFF tile"};
+ }
+
+ for (uint32_t row = 0; row < actual_h; row++) {
+ uint8_t* dst = out_plane + row * out_stride;
+ uint8_t* src = tile_buf.data() + row * m_tile_width * m_samples_per_pixel;
+ if (outSpp == m_samples_per_pixel) {
+ memcpy(dst, src, actual_w * outSpp);
+ }
+ else {
+ for (uint32_t x = 0; x < actual_w; x++) {
+ memcpy(dst + x * outSpp, src + x * m_samples_per_pixel, outSpp);
+ }
+ }
+ }
+ }
+ else {
+ // PLANARCONFIG_SEPARATE: only read the first outSpp planes
+ for (uint16_t s = 0; s < outSpp; s++) {
+ tmsize_t read = TIFFReadEncodedTile(tif, TIFFComputeTile(tif, tx * m_tile_width, ty * m_tile_height, 0, s),
+ tile_buf.data(), tile_buf_size);
+ if (read < 0) {
+ heif_image_release(*out_image);
+ *out_image = nullptr;
+ return {heif_error_Invalid_input, heif_suberror_Unspecified, "Failed to read TIFF tile"};
+ }
+
+ for (uint32_t row = 0; row < actual_h; row++) {
+ uint8_t* dst = out_plane + row * out_stride + s;
+ uint8_t* src = tile_buf.data() + row * m_tile_width;
+ for (uint32_t x = 0; x < actual_w; x++) {
+ dst[x * outSpp] = src[x];
+ }
+ }
+ }
+ }
+
+ return heif_error_ok;
+}
+
+
+void TiledTiffReader::readExif(InputImage* input_image)
+{
+ TIFF* tif = static_cast<TIFF*>(m_tif.get());
+ std::unique_ptr<ExifTags> tags = ExifTags::Parse(tif);
+ if (tags) {
+ tags->Encode(&(input_image->exif));
+ }
+}
+
diff --git a/heifio/decoder_tiff.h b/heifio/decoder_tiff.h
index 81d04e2b..94cc2bc1 100644
--- a/heifio/decoder_tiff.h
+++ b/heifio/decoder_tiff.h
@@ -29,8 +29,43 @@
#include "decoder.h"
#include "libheif/heif.h"
+#include <memory>
+#include <cstdint>
LIBHEIF_API
heif_error loadTIFF(const char *filename, InputImage *input_image);
+class LIBHEIF_API TiledTiffReader {
+public:
+ ~TiledTiffReader();
+
+ // Returns a reader if the file is a tiled TIFF. If the TIFF is not tiled,
+ // returns nullptr with heif_error_Ok (caller should fall back to loadTIFF).
+ static std::unique_ptr<TiledTiffReader> open(const char* filename, heif_error* out_err);
+
+ uint32_t imageWidth() const { return m_image_width; }
+ uint32_t imageHeight() const { return m_image_height; }
+ uint32_t tileWidth() const { return m_tile_width; }
+ uint32_t tileHeight() const { return m_tile_height; }
+ uint32_t nColumns() const { return m_n_columns; }
+ uint32_t nRows() const { return m_n_rows; }
+
+ heif_error readTile(uint32_t tx, uint32_t ty, heif_image** out_image);
+ void readExif(InputImage* input_image);
+
+private:
+ TiledTiffReader() = default;
+
+ struct TiffCloser { void operator()(void* tif) const; };
+ std::unique_ptr<void, TiffCloser> m_tif;
+
+ uint32_t m_image_width = 0, m_image_height = 0;
+ uint32_t m_tile_width = 0, m_tile_height = 0;
+ uint32_t m_n_columns = 0, m_n_rows = 0;
+ uint16_t m_samples_per_pixel = 0;
+ uint16_t m_bits_per_sample = 0;
+ uint16_t m_planar_config = 0;
+ bool m_has_alpha = false;
+};
+
#endif // LIBHEIF_DECODER_TIFF_H