From 38988120a35cd8b4fa9e451d92bbdd51c9cf2129 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 21 Apr 2024 23:54:42 +0100 Subject: [PATCH] Start implementing reading fields from scratch --- src/tiff/tiffread.c | 515 +++++++++++++------------------------------- src/tiff/tiffread.h | 100 +++++---- 2 files changed, 210 insertions(+), 405 deletions(-) diff --git a/src/tiff/tiffread.c b/src/tiff/tiffread.c index 5dd104c..760f702 100644 --- a/src/tiff/tiffread.c +++ b/src/tiff/tiffread.c @@ -26,30 +26,22 @@ #define NULL_TIFF_IFD ((TiffIFD){0}) #define IS_NULL_IFD(IFD) (IFD.count == 0) +#define NULL_TIFF_IMAGE ((TiffImage){0}) +#define IS_NULL_IMAGE(IMG) (IMG.type == TIFF_IMAGE_TYPE_INVALID) + +#define INVALID_SAMPLE_COUNT 0 +#define INVALID_ROWS_PER_STRIP 0 #define INVALID_STRIP_COUNT 0 #define INVALID_ALPHA_OFFSET -1 // clang-format off -enum tiff_image_identifiers { - TIFF_IMAGE_BILEVEL = 0, - TIFF_IMAGE_GRAYSCALE = TIFF_PUBLIC_TAG_BITS_PER_SAMPLE, - TIFF_IMAGE_PALETTE = TIFF_PUBLIC_TAG_BITS_PER_SAMPLE | TIFF_PUBLIC_TAG_COLOR_MAP, - TIFF_IMAGE_RGB = TIFF_PUBLIC_TAG_BITS_PER_SAMPLE | TIFF_PUBLIC_TAG_SAMPLES_PER_PIXEL, -}; - -enum tiff_extra_samples { - TIFF_EXTRA_SAMPLE_UNSPECIFIED = 0x0000, - TIFF_EXTRA_SAMPLE_ASSOCIATED_ALPHA = 0x0001, - TIFF_EXTRA_SAMPLE_UNASSOCIATED_ALPHA = 0x0002, -}; // clang-format on TiffHdr read_tiff_header(FILE *fp); TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset, const Allocator *allocator); -Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd, - const Allocator *allocator); +TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd); void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset); Image *read_baseline_tiff(const char *file, const Allocator *allocator) { @@ -95,7 +87,8 @@ Image *read_baseline_tiff(const char *file, const Allocator *allocator) { } TiffIFD ifd = read_ifd(fp, &header, header.first_ifd_offset, &alloc); - img_out = read_fields(fp, &header, &ifd, &alloc); + + read_fields(fp, &header, &ifd); READ_BASELINE_FILE_CLEANUP: fclose(fp); @@ -177,442 +170,230 @@ TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset, return ifd; } -Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd, - const Allocator *allocator) { - if (!fp || !header || !ifd) { - return NULL; - } - - u16 identifier = 0; - - u16 photometric_interpretation = TIFF_PHOTOMETRIC_INTERPRETATION_INVALID; - - u16 samples_per_pixel = 1; - u16 *bits_per_sample = NULL; - u16 extra_samples = 0; - bool has_alpha = false; - bool associated_alpha = false; - i32 alpha_offset = - INVALID_ALPHA_OFFSET; // Alpha offset following colour samples - - u32 strip_count = INVALID_STRIP_COUNT; - u8 *strip_offsets = NULL; - u16 strip_offsets_type = TIFF_FIELD_TYPE_LONG; - u8 *strip_byte_counts = NULL; - u16 strip_byte_counts_type = TIFF_FIELD_TYPE_LONG; - - u64 width = 0; - u64 height = 0; +TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd) { + TiffImage img_out = NULL_TIFF_IMAGE; + u32 image_type_identifier = TIFF_IMAGE_TYPE_BILEVEL; for (u64 i = 0; i < ifd->count; ++i) { TiffField *field = &(ifd->fields[i]); - switch (header->order) { - case TIFF_ORDER_LITTLE_ENDIAN: - if (IS_BIG_ENDIAN) { - field->tag = htons(field->tag); - field->type = htons(field->type); - field->count = htonl(field->count); - field->value_offset = htonl(field->value_offset); - } - break; + switch (header->order) { case TIFF_ORDER_BIG_ENDIAN: if (IS_LITTLE_ENDIAN) { field->tag = ntohs(field->tag); field->type = ntohs(field->type); field->count = ntohl(field->count); - field->value_offset = ntohl(field->value_offset); - } + if (field_types[field->type].byte_count > 2 || + field_types[field->type].byte_count * field->count > 4) { + field->value_offset = ntohl(field->value_offset); + } else { + field->value_offset = ntohs(field->value_offset); + } + } + break; + case TIFF_ORDER_LITTLE_ENDIAN: + if (IS_BIG_ENDIAN) { + field->tag = htons(field->tag); + field->type = htons(field->type); + field->count = htonl(field->count); + + if (field_types[field->type].byte_count > 2 || + field_types[field->type].byte_count * field->count > 4) { + field->value_offset = htonl(field->value_offset); + } else { + field->value_offset = htons(field->value_offset); + } + } break; } switch (field->tag) { case TIFF_PUBLIC_TAG_IMAGE_WIDTH: - if (field->count != 1) { - return NULL; + if (field->count > 1) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - width = field->value_offset; + img_out.image_width = field->value_offset; break; case TIFF_PUBLIC_TAG_IMAGE_LENGTH: - if (field->count != 1) { - return NULL; + if (field->count > 1) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - height = field->value_offset; + img_out.image_length = field->value_offset; break; case TIFF_PUBLIC_TAG_BITS_PER_SAMPLE: - identifier |= field->tag; + image_type_identifier |= field->tag; - u64 byte_count = field->count * sizeof(u16); - bits_per_sample = wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, byte_count); - if (!bits_per_sample) { - break; + if (img_out.sample_count == INVALID_SAMPLE_COUNT) { + img_out.sample_count = field->count; + } else if (img_out.sample_count != field->count) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - if (byte_count <= 4) { - bits_per_sample = (u16 *)&(field->value_offset); - } else { - read_from_file_with_offset(fp, bits_per_sample, byte_count, - field->value_offset); + img_out.bits_per_sample = field->value_offset; - for (u64 i = 0; i < field->count; ++i) { - switch (header->order) { - case TIFF_ORDER_LITTLE_ENDIAN: - if (IS_BIG_ENDIAN) { - bits_per_sample[i] = htons(bits_per_sample[i]); - } - - break; - case TIFF_ORDER_BIG_ENDIAN: - if (IS_LITTLE_ENDIAN) { - bits_per_sample[i] = ntohs(bits_per_sample[i]); - } - - break; - } - } - } + img_out.bits_per_sample_offset = + img_out.sample_count * field_types[field->type].byte_count > 4; break; case TIFF_PUBLIC_TAG_COMPRESSION: - if (field->count != 1) { - return NULL; + if (field->count > 1 || + field->value_offset != TIFF_COMPRESSION_UNCOMPRESSED) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - switch (field->value_offset) { - case TIFF_COMPRESSION_UNCOMPRESSED: - break; - case TIFF_COMPRESSION_CCITT_1D: - case TIFF_COMPRESSION_GROUP_3_FAX: - case TIFF_COMPRESSION_GROUP_4_FAX: - case TIFF_COMPRESSION_LZW: - case TIFF_COMPRESSION_JPEG: - case TIFF_COMPRESSION_PACK_BITS: - return NULL; - } + img_out.compression = field->value_offset; break; case TIFF_PUBLIC_TAG_PHOTOMETRIC_INTERPRETATION: - photometric_interpretation = field->value_offset; - break; - case TIFF_PUBLIC_TAG_STRIP_OFFSETS: { - strip_offsets_type = field->type; - u16 field_type_byte_count = field_types[field->type].byte_count; - u32 total_byte_count = field->count * field_type_byte_count; - - if (strip_count == INVALID_STRIP_COUNT) { - strip_count = field->count; - } else { - if (strip_count != field->count) { - return NULL; - } + if (field->count > 1) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - strip_offsets = - (u8 *)wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, total_byte_count); - if (!strip_offsets) { - return NULL; - } - - if (total_byte_count <= 4) { - memcpy(strip_offsets, &(field->value_offset), 4); - - switch (header->order) { - case TIFF_ORDER_LITTLE_ENDIAN: - if (IS_BIG_ENDIAN) { - u32 val = htonl(*(u32 *)strip_offsets); - strip_offsets = (u8 *)&val; - } - - break; - case TIFF_ORDER_BIG_ENDIAN: - if (IS_LITTLE_ENDIAN) { - u32 val = ntohl(*(u32 *)strip_offsets); - strip_offsets = (u8 *)&val; - } - - break; - } - } else { - read_from_file_with_offset(fp, strip_offsets, total_byte_count, - field->value_offset); - } + img_out.photometric_interpretation = field->value_offset; break; - } + case TIFF_PUBLIC_TAG_STRIP_OFFSETS: + img_out.strip_offsets = field->value_offset; + if (img_out.strip_count == INVALID_STRIP_COUNT) { + img_out.strip_count = field->count; + } else if (img_out.strip_count != field->count) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; + } + + img_out.strip_offsets_offset = + img_out.strip_count * field_types[field->type].byte_count > 4; + + break; case TIFF_PUBLIC_TAG_SAMPLES_PER_PIXEL: - if (field->count != 1) { - return NULL; + image_type_identifier |= field->tag; + + if (field->count > 1) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - identifier |= field->tag; - samples_per_pixel = field->value_offset; + img_out.sample_count = field->value_offset; break; case TIFF_PUBLIC_TAG_ROWS_PER_STRIP: - if (field->count != 1 || height == 0) { - return NULL; + if (field->count > 1) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - strip_count = - floor((f64)(height + field->value_offset - 1) / field->value_offset); + img_out.rows_per_strip = field->value_offset; - break; - case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS: { - strip_byte_counts_type = field->type; - u16 field_type_byte_count = field_types[field->type].byte_count; - u32 total_byte_count = field->count * field_type_byte_count; - - if (strip_count == INVALID_STRIP_COUNT) { - strip_count = field->count; + if (img_out.image_length > 0) { + img_out.strip_count = + floor((f64)(img_out.image_length + img_out.rows_per_strip - 1) / + img_out.rows_per_strip); } else { - if (strip_count != field->count) { - return NULL; - } - } - - strip_byte_counts = - (u8 *)wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, total_byte_count); - if (!strip_byte_counts) { - return NULL; - } - - if (total_byte_count <= 4) { - memcpy(strip_byte_counts, &(field->value_offset), 4); - - switch (header->order) { - case TIFF_ORDER_LITTLE_ENDIAN: - if (IS_BIG_ENDIAN) { - u32 val = htonl(*(u32 *)strip_byte_counts); - strip_byte_counts = (u8 *)&val; - } - - break; - case TIFF_ORDER_BIG_ENDIAN: - if (IS_LITTLE_ENDIAN) { - u32 val = ntohl(*(u32 *)strip_byte_counts); - strip_byte_counts = (u8 *)&val; - } - - break; - } - } else { - read_from_file_with_offset(fp, strip_byte_counts, total_byte_count, - field->value_offset); + img_out.strip_count = INVALID_STRIP_COUNT; } break; - } - case TIFF_PUBLIC_TAG_PLANAR_CONFIGURATION: - if (field->count != 1 || - field->value_offset != TIFF_PLANAR_CONFIG_CHUNKY) { - return NULL; + case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS: + img_out.strip_byte_counts = field->value_offset; + + if (img_out.strip_count == INVALID_STRIP_COUNT) { + img_out.strip_count = field->count; + } else if (img_out.strip_count != field->count) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } + img_out.strip_byte_counts_offset = + img_out.strip_count * field_types[field->type].byte_count > 4; + break; case TIFF_PUBLIC_TAG_COLOR_MAP: - identifier |= field->tag; + image_type_identifier |= field->tag; break; - case TIFF_PUBLIC_TAG_EXTRA_SAMPLES: { - extra_samples = field->count; - u64 byte_count = field->count * sizeof(u16); + case TIFF_PUBLIC_TAG_EXTRA_SAMPLES: + img_out.extra_samples_count = field->count; - u8 data_buf[byte_count]; + img_out.extra_samples = field->value_offset; - if (byte_count <= 4) { - memcpy(data_buf, (void *)&(field->value_offset), byte_count); - } else { - read_from_file_with_offset(fp, data_buf, byte_count, - field->value_offset); - } - - for (u32 i = 0; i < field->count; ++i) { - u16 sample = ((u16 *)&(field->value_offset))[i]; - - if (byte_count > 4) { - switch (header->order) { - case TIFF_ORDER_LITTLE_ENDIAN: - if (IS_BIG_ENDIAN) { - sample = htons(sample); - } - - break; - case TIFF_ORDER_BIG_ENDIAN: - if (IS_LITTLE_ENDIAN) { - sample = ntohs(sample); - } - - break; - } - } - - if (sample > TIFF_EXTRA_SAMPLE_UNASSOCIATED_ALPHA) { - return NULL; - } - - if (sample > TIFF_EXTRA_SAMPLE_UNSPECIFIED) { - if (has_alpha) { - return NULL; - } - - has_alpha = true; - associated_alpha = - sample == TIFF_EXTRA_SAMPLE_ASSOCIATED_ALPHA ? true : false; - alpha_offset = i; - } - } + img_out.extra_samples_offset = + img_out.extra_samples_count * field_types[field->type].byte_count > 4; break; - } - default: + case TIFF_PUBLIC_TAG_PLANAR_CONFIGURATION: + if (field->count > 1 || + field->value_offset != TIFF_PLANAR_CONFIG_CHUNKY) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; + } + break; } } - switch (identifier) { - case TIFF_IMAGE_BILEVEL: - case TIFF_IMAGE_GRAYSCALE: - if (photometric_interpretation > + if (image_type_identifier < TIFF_IMAGE_TYPE_BILEVEL || + image_type_identifier > TIFF_IMAGE_TYPE_RGB) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; + } + + switch (image_type_identifier) { + case TIFF_IMAGE_TYPE_BILEVEL: + case TIFF_IMAGE_TYPE_GRAYSCALE: + if (img_out.photometric_interpretation > TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO) { - return NULL; + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } break; - case TIFF_IMAGE_PALETTE: - if (photometric_interpretation != + case TIFF_IMAGE_TYPE_PALETTE: + if (img_out.photometric_interpretation != TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE) { - return NULL; + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } break; - case TIFF_IMAGE_RGB: - if (photometric_interpretation != TIFF_PHOTOMETRIC_INTERPRETATION_RGB) { - return NULL; + case TIFF_IMAGE_TYPE_RGB: + if (img_out.photometric_interpretation != + TIFF_PHOTOMETRIC_INTERPRETATION_RGB) { + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } break; default: - return NULL; + img_out = NULL_TIFF_IMAGE; + goto READ_FIELDS_RETURN_IMAGE; } - printf("WIDTH: %lu, HEIGHT: %lu\n", width, height); - printf("SAMPLES: %u\n", samples_per_pixel); - printf("EXTRA SAMPLES: %u\n", extra_samples); - printf("ALPHA: %s, %s, %d\n", has_alpha ? "HAS ALPHA" : "NO ALPHA", - has_alpha && associated_alpha ? "ASSOCIATED" - : has_alpha ? "UNASSOCIATED" - : "N/A", - alpha_offset); - printf("STRIPS: %u\n", strip_count); - printf("PHOTOMETRIC INTERPRETATION: %u\n", photometric_interpretation); - printf("BITS PER SAMPLE: "); - for (u64 i = 0; i < samples_per_pixel; ++i) { - printf("%u ", bits_per_sample[i]); - } - printf("\n"); + img_out.type = image_type_identifier; - if (has_alpha && alpha_offset == INVALID_ALPHA_OFFSET) { - return NULL; - } - - Image *img_out = NULL; - - Arena *temp_arena; - u64 img_buf_size = width * height * sizeof(Pixel); - wapp_mem_arena_init(&temp_arena, img_buf_size * 4); - - u8 *temp_img_buf = wapp_mem_arena_alloc(temp_arena, img_buf_size); - if (!temp_img_buf) { - return NULL; - } - - u64 position = 0; - for (u64 i = 0; i < strip_count; ++i) { - u64 offset; - u64 count; - - if (strip_offsets_type == TIFF_FIELD_TYPE_LONG) { - offset = ((u32 *)strip_offsets)[i]; - } else { - offset = ((u16 *)strip_offsets)[i]; - } - - if (strip_byte_counts_type == TIFF_FIELD_TYPE_LONG) { - count = ((u32 *)strip_byte_counts)[i]; - } else { - count = ((u16 *)strip_byte_counts)[i]; - } - - u8 *strip_data = wapp_mem_arena_alloc(temp_arena, count); - read_from_file_with_offset(fp, strip_data, count, offset); - - if (samples_per_pixel == 4 && has_alpha) { - memcpy(&(temp_img_buf[position]), strip_data, count); - position += count; - } else if (samples_per_pixel > 4) { - for (u64 j = 0; j < count; ++j) { - memcpy(&(temp_img_buf[position]), &(strip_data[j]), 3); - j += 3; - - if (has_alpha) { - memcpy(&(temp_img_buf[position]), &(strip_data[j + alpha_offset]), 1); - j += 1; - } else { - temp_img_buf[position] = 255; - } - - j += samples_per_pixel - 4; - position += 1; - } - } else if (samples_per_pixel == 3) { - if (identifier == TIFF_IMAGE_RGB) { - for (u64 j = 0; j < count; ++j) { - memcpy(&(temp_img_buf[position]), &(strip_data[j]), 3); - j += 3; - position += 3; - - temp_img_buf[position++] = 255; - } - } else { - goto READ_FIELDS_FREE_ARENA; - } - } else if (samples_per_pixel == 1) { - if (identifier == TIFF_IMAGE_RGB) { - goto READ_FIELDS_FREE_ARENA; - } - - for (u64 j = 0; j < count; ++j) { - for (u64 k = 0; k < 3; ++k) { - memcpy(&(temp_img_buf[position]), &(strip_data[j]), 1); - } - - j += 1; - position += 3; - - if (has_alpha) { - memcpy(&(temp_img_buf[position]), &(strip_data[j + alpha_offset]), 1); - j += 1; - } else { - temp_img_buf[position] = 255; - } - - position += 1; - } - } else { - goto READ_FIELDS_FREE_ARENA; - } - } - - img_out = create_image(width, height, (Pixel *)temp_img_buf, allocator); - -READ_FIELDS_FREE_ARENA: - wapp_mem_arena_free(&temp_arena); +#if 1 + // clang-format off + printf("SIZE (width, height) : %u, %u\n", img_out.image_width, img_out.image_length); + printf("SAMPLES (bits per sample, count, is offset) : %u, %u, %u\n", img_out.bits_per_sample, img_out.sample_count, img_out.bits_per_sample_offset); + printf("EXTRA SAMPLES (samples, count, is offset) : %u, %u, %u\n", img_out.extra_samples, img_out.extra_samples_count, img_out.extra_samples_offset); + printf("SAMPLES PER PIXEL : %u\n", img_out.sample_count); + printf("PHOTOMETRIC INTERPRETATION : %u\n", img_out.photometric_interpretation); + printf("ROWS PER STRIP (rows, strip count) : %u, %u\n", img_out.rows_per_strip, img_out.strip_count); + printf("STRIP OFFSETS (offsets, is offset) : %u, %u\n", img_out.strip_offsets, img_out.strip_offsets_offset); + printf("STRIP BYTES (byte count, is offset) : %u, %u\n", img_out.strip_byte_counts, img_out.strip_byte_counts_offset); + // clang-format on +#endif +READ_FIELDS_RETURN_IMAGE: return img_out; } diff --git a/src/tiff/tiffread.h b/src/tiff/tiffread.h index 832deb1..d86c831 100644 --- a/src/tiff/tiffread.h +++ b/src/tiff/tiffread.h @@ -7,19 +7,10 @@ extern "C" { #include "aliases.h" #include "image.h" +#include -#define TAG_MAX_COUNT (UINT16_MAX * sizeof(const char *)) -// Technically, the type parameter is 2-bytes long, but there aren't so many -// types specified in the spec, so UINT8_MAX should be sufficient #define TYPE_MAX_COUNT (UINT8_MAX * sizeof(const char *)) -// clang-format off -enum tiff_byte_order { - TIFF_ORDER_LITTLE_ENDIAN = 0x4949, - TIFF_ORDER_BIG_ENDIAN = 0x4d4d, -}; -// clang-format on - typedef struct hdr TiffHdr; struct hdr { u16 order; @@ -27,34 +18,6 @@ struct hdr { u32 first_ifd_offset; }; -// clang-format off -enum tiff_public_tags { - #include "tiff_public_tags.inc" -}; - -internal const char *tag_names[TAG_MAX_COUNT] = { - #define TIFF_TAG_LOOKUP - #include "tiff_public_tags.inc" - #undef TIFF_TAG_LOOKUP -}; - -enum tiff_field_types { - #include "tiff_field_types.inc" -}; - -typedef struct field_type TiffFieldType; -struct field_type { - const char *name; - u16 byte_count; -}; - -internal TiffFieldType field_types[TYPE_MAX_COUNT] = { - #define TIFF_TYPE_LOOKUP - #include "tiff_field_types.inc" - #undef TIFF_TYPE_LOOKUP -}; -// clang-format on - typedef struct field TiffField; struct field { u16 tag; @@ -71,6 +34,45 @@ struct IFD { }; // clang-format off +enum tiff_byte_order { + TIFF_ORDER_LITTLE_ENDIAN = 0x4949, + TIFF_ORDER_BIG_ENDIAN = 0x4d4d, +}; + +enum tiff_public_tags { + #include "tiff_public_tags.inc" +}; + +enum tiff_field_types { + #include "tiff_field_types.inc" +}; + +typedef struct field_type TiffFieldType; +struct field_type { + const char *name; + u16 byte_count; +}; + +internal TiffFieldType field_types[TYPE_MAX_COUNT] = { + #define TIFF_TYPE_LOOKUP + #include "tiff_field_types.inc" + #undef TIFF_TYPE_LOOKUP +}; + +typedef enum tiff_image_type_names { + TIFF_IMAGE_TYPE_INVALID = 0, + TIFF_IMAGE_TYPE_BILEVEL = 1, + TIFF_IMAGE_TYPE_GRAYSCALE = TIFF_PUBLIC_TAG_BITS_PER_SAMPLE, + TIFF_IMAGE_TYPE_PALETTE = TIFF_PUBLIC_TAG_BITS_PER_SAMPLE | TIFF_PUBLIC_TAG_COLOR_MAP, + TIFF_IMAGE_TYPE_RGB = TIFF_PUBLIC_TAG_BITS_PER_SAMPLE | TIFF_PUBLIC_TAG_SAMPLES_PER_PIXEL, +} TiffImageType; + +enum tiff_extra_samples { + TIFF_EXTRA_SAMPLE_UNSPECIFIED = 0x0000, + TIFF_EXTRA_SAMPLE_ASSOCIATED_ALPHA = 0x0001, + TIFF_EXTRA_SAMPLE_UNASSOCIATED_ALPHA = 0x0002, +}; + enum tiff_compression { TIFF_COMPRESSION_UNCOMPRESSED = 0x0001, TIFF_COMPRESSION_CCITT_1D = 0x0002, @@ -100,6 +102,28 @@ enum tiff_planar_configuration { }; // clang-format on +typedef struct tiff_image TiffImage; +struct tiff_image { + TiffImageType type; + u32 image_width; + u32 image_length; + u32 bits_per_sample; + u32 sample_count; + bool bits_per_sample_offset; + u16 compression; + u16 photometric_interpretation; + u32 strip_offsets; + bool strip_offsets_offset; + u32 rows_per_strip; + u32 strip_byte_counts; + bool strip_byte_counts_offset; + u32 strip_count; + u32 color_map; + u32 extra_samples; + u32 extra_samples_count; + bool extra_samples_offset; +}; + Image *read_baseline_tiff(const char *file, const Allocator *allocator); #ifdef __cplusplus