Start implementing reading fields from scratch

This commit is contained in:
Abdelrahman Said 2024-04-21 23:54:42 +01:00
parent 8040dd7381
commit 38988120a3
2 changed files with 210 additions and 405 deletions

View File

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

View File

@ -7,19 +7,10 @@ extern "C" {
#include "aliases.h"
#include "image.h"
#include <stdbool.h>
#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