Start implementing reading fields from scratch
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user