Start implementing reading fields from scratch
This commit is contained in:
parent
8040dd7381
commit
38988120a3
@ -26,30 +26,22 @@
|
|||||||
#define NULL_TIFF_IFD ((TiffIFD){0})
|
#define NULL_TIFF_IFD ((TiffIFD){0})
|
||||||
#define IS_NULL_IFD(IFD) (IFD.count == 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_STRIP_COUNT 0
|
||||||
|
|
||||||
#define INVALID_ALPHA_OFFSET -1
|
#define INVALID_ALPHA_OFFSET -1
|
||||||
|
|
||||||
// clang-format off
|
// 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
|
// clang-format on
|
||||||
|
|
||||||
TiffHdr read_tiff_header(FILE *fp);
|
TiffHdr read_tiff_header(FILE *fp);
|
||||||
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
|
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
|
||||||
const Allocator *allocator);
|
const Allocator *allocator);
|
||||||
Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd,
|
TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd);
|
||||||
const Allocator *allocator);
|
|
||||||
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset);
|
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset);
|
||||||
|
|
||||||
Image *read_baseline_tiff(const char *file, const Allocator *allocator) {
|
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);
|
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:
|
READ_BASELINE_FILE_CLEANUP:
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@ -177,442 +170,230 @@ TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
|
|||||||
return ifd;
|
return ifd;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd,
|
TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd) {
|
||||||
const Allocator *allocator) {
|
TiffImage img_out = NULL_TIFF_IMAGE;
|
||||||
if (!fp || !header || !ifd) {
|
u32 image_type_identifier = TIFF_IMAGE_TYPE_BILEVEL;
|
||||||
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;
|
|
||||||
|
|
||||||
for (u64 i = 0; i < ifd->count; ++i) {
|
for (u64 i = 0; i < ifd->count; ++i) {
|
||||||
TiffField *field = &(ifd->fields[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:
|
case TIFF_ORDER_BIG_ENDIAN:
|
||||||
if (IS_LITTLE_ENDIAN) {
|
if (IS_LITTLE_ENDIAN) {
|
||||||
field->tag = ntohs(field->tag);
|
field->tag = ntohs(field->tag);
|
||||||
field->type = ntohs(field->type);
|
field->type = ntohs(field->type);
|
||||||
field->count = ntohl(field->count);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (field->tag) {
|
switch (field->tag) {
|
||||||
case TIFF_PUBLIC_TAG_IMAGE_WIDTH:
|
case TIFF_PUBLIC_TAG_IMAGE_WIDTH:
|
||||||
if (field->count != 1) {
|
if (field->count > 1) {
|
||||||
return NULL;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
width = field->value_offset;
|
img_out.image_width = field->value_offset;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_IMAGE_LENGTH:
|
case TIFF_PUBLIC_TAG_IMAGE_LENGTH:
|
||||||
if (field->count != 1) {
|
if (field->count > 1) {
|
||||||
return NULL;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
height = field->value_offset;
|
img_out.image_length = field->value_offset;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_BITS_PER_SAMPLE:
|
case TIFF_PUBLIC_TAG_BITS_PER_SAMPLE:
|
||||||
identifier |= field->tag;
|
image_type_identifier |= field->tag;
|
||||||
|
|
||||||
u64 byte_count = field->count * sizeof(u16);
|
if (img_out.sample_count == INVALID_SAMPLE_COUNT) {
|
||||||
bits_per_sample = wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, byte_count);
|
img_out.sample_count = field->count;
|
||||||
if (!bits_per_sample) {
|
} else if (img_out.sample_count != field->count) {
|
||||||
break;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte_count <= 4) {
|
img_out.bits_per_sample = field->value_offset;
|
||||||
bits_per_sample = (u16 *)&(field->value_offset);
|
|
||||||
} else {
|
|
||||||
read_from_file_with_offset(fp, bits_per_sample, byte_count,
|
|
||||||
field->value_offset);
|
|
||||||
|
|
||||||
for (u64 i = 0; i < field->count; ++i) {
|
img_out.bits_per_sample_offset =
|
||||||
switch (header->order) {
|
img_out.sample_count * field_types[field->type].byte_count > 4;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_COMPRESSION:
|
case TIFF_PUBLIC_TAG_COMPRESSION:
|
||||||
if (field->count != 1) {
|
if (field->count > 1 ||
|
||||||
return NULL;
|
field->value_offset != TIFF_COMPRESSION_UNCOMPRESSED) {
|
||||||
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (field->value_offset) {
|
img_out.compression = 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_PHOTOMETRIC_INTERPRETATION:
|
case TIFF_PUBLIC_TAG_PHOTOMETRIC_INTERPRETATION:
|
||||||
photometric_interpretation = field->value_offset;
|
if (field->count > 1) {
|
||||||
break;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
case TIFF_PUBLIC_TAG_STRIP_OFFSETS: {
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strip_offsets =
|
img_out.photometric_interpretation = field->value_offset;
|
||||||
(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
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:
|
case TIFF_PUBLIC_TAG_SAMPLES_PER_PIXEL:
|
||||||
if (field->count != 1) {
|
image_type_identifier |= field->tag;
|
||||||
return NULL;
|
|
||||||
|
if (field->count > 1) {
|
||||||
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
identifier |= field->tag;
|
img_out.sample_count = field->value_offset;
|
||||||
samples_per_pixel = field->value_offset;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_ROWS_PER_STRIP:
|
case TIFF_PUBLIC_TAG_ROWS_PER_STRIP:
|
||||||
if (field->count != 1 || height == 0) {
|
if (field->count > 1) {
|
||||||
return NULL;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
strip_count =
|
img_out.rows_per_strip = field->value_offset;
|
||||||
floor((f64)(height + field->value_offset - 1) / field->value_offset);
|
|
||||||
|
|
||||||
break;
|
if (img_out.image_length > 0) {
|
||||||
case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS: {
|
img_out.strip_count =
|
||||||
strip_byte_counts_type = field->type;
|
floor((f64)(img_out.image_length + img_out.rows_per_strip - 1) /
|
||||||
u16 field_type_byte_count = field_types[field->type].byte_count;
|
img_out.rows_per_strip);
|
||||||
u32 total_byte_count = field->count * field_type_byte_count;
|
|
||||||
|
|
||||||
if (strip_count == INVALID_STRIP_COUNT) {
|
|
||||||
strip_count = field->count;
|
|
||||||
} else {
|
} else {
|
||||||
if (strip_count != field->count) {
|
img_out.strip_count = INVALID_STRIP_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS:
|
||||||
case TIFF_PUBLIC_TAG_PLANAR_CONFIGURATION:
|
img_out.strip_byte_counts = field->value_offset;
|
||||||
if (field->count != 1 ||
|
|
||||||
field->value_offset != TIFF_PLANAR_CONFIG_CHUNKY) {
|
if (img_out.strip_count == INVALID_STRIP_COUNT) {
|
||||||
return NULL;
|
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;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_COLOR_MAP:
|
case TIFF_PUBLIC_TAG_COLOR_MAP:
|
||||||
identifier |= field->tag;
|
image_type_identifier |= field->tag;
|
||||||
break;
|
break;
|
||||||
case TIFF_PUBLIC_TAG_EXTRA_SAMPLES: {
|
case TIFF_PUBLIC_TAG_EXTRA_SAMPLES:
|
||||||
extra_samples = field->count;
|
img_out.extra_samples_count = field->count;
|
||||||
u64 byte_count = field->count * sizeof(u16);
|
|
||||||
|
|
||||||
u8 data_buf[byte_count];
|
img_out.extra_samples = field->value_offset;
|
||||||
|
|
||||||
if (byte_count <= 4) {
|
img_out.extra_samples_offset =
|
||||||
memcpy(data_buf, (void *)&(field->value_offset), byte_count);
|
img_out.extra_samples_count * field_types[field->type].byte_count > 4;
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
case TIFF_PUBLIC_TAG_PLANAR_CONFIGURATION:
|
||||||
default:
|
if (field->count > 1 ||
|
||||||
|
field->value_offset != TIFF_PLANAR_CONFIG_CHUNKY) {
|
||||||
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (identifier) {
|
if (image_type_identifier < TIFF_IMAGE_TYPE_BILEVEL ||
|
||||||
case TIFF_IMAGE_BILEVEL:
|
image_type_identifier > TIFF_IMAGE_TYPE_RGB) {
|
||||||
case TIFF_IMAGE_GRAYSCALE:
|
img_out = NULL_TIFF_IMAGE;
|
||||||
if (photometric_interpretation >
|
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) {
|
TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO) {
|
||||||
return NULL;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_IMAGE_PALETTE:
|
case TIFF_IMAGE_TYPE_PALETTE:
|
||||||
if (photometric_interpretation !=
|
if (img_out.photometric_interpretation !=
|
||||||
TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE) {
|
TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE) {
|
||||||
return NULL;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TIFF_IMAGE_RGB:
|
case TIFF_IMAGE_TYPE_RGB:
|
||||||
if (photometric_interpretation != TIFF_PHOTOMETRIC_INTERPRETATION_RGB) {
|
if (img_out.photometric_interpretation !=
|
||||||
return NULL;
|
TIFF_PHOTOMETRIC_INTERPRETATION_RGB) {
|
||||||
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
img_out = NULL_TIFF_IMAGE;
|
||||||
|
goto READ_FIELDS_RETURN_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("WIDTH: %lu, HEIGHT: %lu\n", width, height);
|
img_out.type = image_type_identifier;
|
||||||
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");
|
|
||||||
|
|
||||||
if (has_alpha && alpha_offset == INVALID_ALPHA_OFFSET) {
|
#if 1
|
||||||
return NULL;
|
// 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);
|
||||||
Image *img_out = NULL;
|
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);
|
||||||
Arena *temp_arena;
|
printf("PHOTOMETRIC INTERPRETATION : %u\n", img_out.photometric_interpretation);
|
||||||
u64 img_buf_size = width * height * sizeof(Pixel);
|
printf("ROWS PER STRIP (rows, strip count) : %u, %u\n", img_out.rows_per_strip, img_out.strip_count);
|
||||||
wapp_mem_arena_init(&temp_arena, img_buf_size * 4);
|
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);
|
||||||
u8 *temp_img_buf = wapp_mem_arena_alloc(temp_arena, img_buf_size);
|
// clang-format on
|
||||||
if (!temp_img_buf) {
|
#endif
|
||||||
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);
|
|
||||||
|
|
||||||
|
READ_FIELDS_RETURN_IMAGE:
|
||||||
return img_out;
|
return img_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,19 +7,10 @@ extern "C" {
|
|||||||
|
|
||||||
#include "aliases.h"
|
#include "aliases.h"
|
||||||
#include "image.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 *))
|
#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;
|
typedef struct hdr TiffHdr;
|
||||||
struct hdr {
|
struct hdr {
|
||||||
u16 order;
|
u16 order;
|
||||||
@ -27,34 +18,6 @@ struct hdr {
|
|||||||
u32 first_ifd_offset;
|
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;
|
typedef struct field TiffField;
|
||||||
struct field {
|
struct field {
|
||||||
u16 tag;
|
u16 tag;
|
||||||
@ -71,6 +34,45 @@ struct IFD {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// 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 {
|
enum tiff_compression {
|
||||||
TIFF_COMPRESSION_UNCOMPRESSED = 0x0001,
|
TIFF_COMPRESSION_UNCOMPRESSED = 0x0001,
|
||||||
TIFF_COMPRESSION_CCITT_1D = 0x0002,
|
TIFF_COMPRESSION_CCITT_1D = 0x0002,
|
||||||
@ -100,6 +102,28 @@ enum tiff_planar_configuration {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// 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);
|
Image *read_baseline_tiff(const char *file, const Allocator *allocator);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
Loading…
Reference in New Issue
Block a user