|
|
|
|
@@ -4,10 +4,13 @@
|
|
|
|
|
#include "image.h"
|
|
|
|
|
#include "mem_arena.h"
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <bits/pthreadtypes.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
@@ -18,14 +21,7 @@
|
|
|
|
|
#define IS_TIFF_EXTENSION(EXT) \
|
|
|
|
|
(strncmp(EXT, ".tif", 4) == 0 || strncmp(EXT, ".tiff", 5) == 0)
|
|
|
|
|
|
|
|
|
|
#define NULL_TIFF_HEADER ((TiffHdr){0})
|
|
|
|
|
#define IS_NULL_HEADER(HDR) (HDR.order == 0)
|
|
|
|
|
|
|
|
|
|
#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 NULL_TIFF_ALPHA ((TiffAlpha){0})
|
|
|
|
|
|
|
|
|
|
#define INVALID_SAMPLE_COUNT 0
|
|
|
|
|
#define INVALID_ROWS_PER_STRIP 0
|
|
|
|
|
@@ -33,47 +29,225 @@
|
|
|
|
|
|
|
|
|
|
#define INVALID_ALPHA_OFFSET -1
|
|
|
|
|
|
|
|
|
|
#define TIFF_SHORT_BYTE_COUNT field_types[TIFF_FIELD_TYPE_SHORT].byte_count
|
|
|
|
|
#define TIFF_LONG_BYTE_COUNT field_types[TIFF_FIELD_TYPE_LONG].byte_count
|
|
|
|
|
|
|
|
|
|
#define RGB_SAMPLE_COUNT 3
|
|
|
|
|
#define MIN_BITS_PER_SAMPLE 8
|
|
|
|
|
|
|
|
|
|
#define TEMP_ARENA_CAPACITY (20 * 1024 * 1024)
|
|
|
|
|
|
|
|
|
|
TiffHdr read_tiff_header(FILE *fp);
|
|
|
|
|
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset, Arena *arena);
|
|
|
|
|
TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd);
|
|
|
|
|
Pixel *load_image_pixels(FILE *fp, const TiffHdr *header, TiffImage *img,
|
|
|
|
|
Arena *arena);
|
|
|
|
|
bool read_strip_data(FILE *fp, const TiffHdr *header, TiffImage *img,
|
|
|
|
|
Arena *arena);
|
|
|
|
|
void read_strips(FILE *fp, const TiffImage *img, Pixel *buf);
|
|
|
|
|
bool read_field(const TiffField *field, TiffImage *img);
|
|
|
|
|
bool validate_image_type(const TiffImage *img);
|
|
|
|
|
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset);
|
|
|
|
|
#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,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
TIFF_COMPRESSION_GROUP_3_FAX = 0x0003,
|
|
|
|
|
TIFF_COMPRESSION_GROUP_4_FAX = 0x0004,
|
|
|
|
|
TIFF_COMPRESSION_LZW = 0x0005,
|
|
|
|
|
TIFF_COMPRESSION_JPEG = 0x0006,
|
|
|
|
|
TIFF_COMPRESSION_PACK_BITS = 0x8005,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum tiff_photometric_interpretation {
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0x0000,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 0x0001,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_RGB = 0x0002,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE = 0x0003,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK = 0x0004,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_CMYK = 0x0005,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_YCbCr = 0x0006,
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_CIELab = 0x0008,
|
|
|
|
|
|
|
|
|
|
TIFF_PHOTOMETRIC_INTERPRETATION_INVALID,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum tiff_planar_configuration {
|
|
|
|
|
TIFF_PLANAR_CONFIG_CHUNKY = 0x0001,
|
|
|
|
|
TIFF_PLANAR_CONFIG_PLANAR = 0x0002,
|
|
|
|
|
};
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
|
|
typedef struct short_long_value ShortLongValue;
|
|
|
|
|
struct short_long_value {
|
|
|
|
|
union {
|
|
|
|
|
u16 short_val;
|
|
|
|
|
u32 long_val;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct hdr TiffHdr;
|
|
|
|
|
struct hdr {
|
|
|
|
|
u16 order;
|
|
|
|
|
u16 magic;
|
|
|
|
|
u32 first_ifd_offset;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct field TiffField;
|
|
|
|
|
struct field {
|
|
|
|
|
u16 tag;
|
|
|
|
|
u16 type;
|
|
|
|
|
u32 count;
|
|
|
|
|
u32 value_offset;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct IFD TiffIFD;
|
|
|
|
|
struct IFD {
|
|
|
|
|
u16 count;
|
|
|
|
|
TiffField *fields;
|
|
|
|
|
u32 next_ifd;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
ALPHA_TYPE_UNDEFINED = 0,
|
|
|
|
|
ALPHA_TYPE_ASSOCIATED,
|
|
|
|
|
ALPHA_TYPE_UNASSOCIATED,
|
|
|
|
|
} AlphaType;
|
|
|
|
|
|
|
|
|
|
typedef struct tiff_alpha TiffAlpha;
|
|
|
|
|
struct tiff_alpha {
|
|
|
|
|
AlphaType type;
|
|
|
|
|
u32 sample_offset;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct tiff_sample_bits TiffSampleBits;
|
|
|
|
|
struct tiff_sample_bits {
|
|
|
|
|
u16 r;
|
|
|
|
|
u16 g;
|
|
|
|
|
u16 b;
|
|
|
|
|
u16 a;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct strip_data_field StripDataField;
|
|
|
|
|
struct strip_data_field {
|
|
|
|
|
u32 *strip_value;
|
|
|
|
|
const ShortLongValue *value_from_file;
|
|
|
|
|
u32 strip_index;
|
|
|
|
|
u32 length_in_bytes;
|
|
|
|
|
u16 type_byte_count;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct tiff_strip TiffStrip;
|
|
|
|
|
struct tiff_strip {
|
|
|
|
|
u32 offset;
|
|
|
|
|
u32 byte_count;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
u16 strip_offsets_type_byte_count;
|
|
|
|
|
ShortLongValue strip_offsets;
|
|
|
|
|
bool strip_offsets_offset;
|
|
|
|
|
u32 rows_per_strip;
|
|
|
|
|
u16 strip_byte_count_type_byte_count;
|
|
|
|
|
ShortLongValue 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;
|
|
|
|
|
TiffAlpha alpha;
|
|
|
|
|
TiffSampleBits rgba_bits_per_sample;
|
|
|
|
|
TiffStrip *strips;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct tiff_reader TiffReader;
|
|
|
|
|
struct tiff_reader {
|
|
|
|
|
const char *filename;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
TiffHdr header;
|
|
|
|
|
TiffIFD ifd;
|
|
|
|
|
TiffImage img;
|
|
|
|
|
Pixel *pixels;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct strip_thread_args StripThreadArgs;
|
|
|
|
|
struct strip_thread_args {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
const TiffStrip *strip;
|
|
|
|
|
u64 pixel_count;
|
|
|
|
|
u64 samples_per_pixel;
|
|
|
|
|
u64 main_samples;
|
|
|
|
|
TiffAlpha alpha;
|
|
|
|
|
Pixel *pixel_write_start;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
internal bool read_tiff_header(TiffReader *reader);
|
|
|
|
|
internal bool read_ifd(TiffReader *reader, Arena *arena);
|
|
|
|
|
internal bool read_ifd_fields(TiffReader *reader);
|
|
|
|
|
internal void read_alpha(TiffReader *reader);
|
|
|
|
|
internal void read_bits_per_sample(TiffReader *reader);
|
|
|
|
|
internal bool read_image_pixels(TiffReader *reader, Arena *arena);
|
|
|
|
|
internal bool read_strip_data(TiffReader *reader, Arena *arena);
|
|
|
|
|
internal bool read_strips(TiffReader *reader);
|
|
|
|
|
internal void *read_strip(void *arg);
|
|
|
|
|
internal void read_strip_data_field(const TiffReader *reader,
|
|
|
|
|
StripDataField *field);
|
|
|
|
|
internal bool read_field(const TiffField *field, TiffImage *img);
|
|
|
|
|
internal bool validate_image_type(const TiffImage *img);
|
|
|
|
|
internal void fread_with_offset(FILE *fp, void *dst, u64 count, u64 offset);
|
|
|
|
|
internal bool valid_tiff_file(const char *file);
|
|
|
|
|
internal f32 u8_normalise(u8 value);
|
|
|
|
|
internal u8 u8_denormalise(f32 value);
|
|
|
|
|
|
|
|
|
|
Image *read_baseline_tiff(const char *file, Arena *arena) {
|
|
|
|
|
Image *img_out = NULL;
|
|
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
|
if (!file || !arena || !valid_tiff_file(file)) {
|
|
|
|
|
goto READ_BASELINE_RETURN_IMG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 name_length = strlen(file);
|
|
|
|
|
if (name_length < TIFF_FILENAME_MIN_LENGTH) {
|
|
|
|
|
goto READ_BASELINE_RETURN_IMG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *ext = NULL;
|
|
|
|
|
for (u32 i = name_length - 1; i >= name_length - TIFF_FILENAME_MIN_LENGTH;
|
|
|
|
|
--i) {
|
|
|
|
|
if (file[i] == '.') {
|
|
|
|
|
ext = &(file[i]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ext || !IS_TIFF_EXTENSION(ext)) {
|
|
|
|
|
goto READ_BASELINE_RETURN_IMG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FILE *fp = fopen(file, "rb");
|
|
|
|
|
if (!fp) {
|
|
|
|
|
TiffReader reader = {.filename = file, .fp = fopen(file, "rb")};
|
|
|
|
|
if (!reader.fp) {
|
|
|
|
|
goto READ_BASELINE_RETURN_IMG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -82,118 +256,113 @@ Image *read_baseline_tiff(const char *file, Arena *arena) {
|
|
|
|
|
goto READ_BASELINE_FILE_CLEANUP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffHdr header = read_tiff_header(fp);
|
|
|
|
|
if (IS_NULL_HEADER(header)) {
|
|
|
|
|
if (!read_tiff_header(&reader)) {
|
|
|
|
|
goto READ_BASELINE_DESTROY_ARENA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffIFD ifd = read_ifd(fp, &header, header.first_ifd_offset, temp_arena);
|
|
|
|
|
if (IS_NULL_IFD(ifd)) {
|
|
|
|
|
if (!read_ifd(&reader, temp_arena)) {
|
|
|
|
|
goto READ_BASELINE_DESTROY_ARENA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffImage img = read_fields(fp, &header, &ifd);
|
|
|
|
|
if (IS_NULL_IMAGE(img)) {
|
|
|
|
|
if (!read_ifd_fields(&reader)) {
|
|
|
|
|
goto READ_BASELINE_DESTROY_ARENA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert((img.type == TIFF_IMAGE_TYPE_RGB) &&
|
|
|
|
|
assert((reader.img.type == TIFF_IMAGE_TYPE_RGB) &&
|
|
|
|
|
"Currently, only RGB images are supported");
|
|
|
|
|
|
|
|
|
|
Pixel *buf = load_image_pixels(fp, &header, &img, temp_arena);
|
|
|
|
|
if (!buf) {
|
|
|
|
|
bool image_is_8_bit =
|
|
|
|
|
(reader.img.rgba_bits_per_sample.r == MIN_BITS_PER_SAMPLE &&
|
|
|
|
|
reader.img.rgba_bits_per_sample.g == MIN_BITS_PER_SAMPLE &&
|
|
|
|
|
reader.img.rgba_bits_per_sample.b == MIN_BITS_PER_SAMPLE &&
|
|
|
|
|
reader.img.rgba_bits_per_sample.a == MIN_BITS_PER_SAMPLE);
|
|
|
|
|
assert(image_is_8_bit && "Currently, only 8-bit images are supported");
|
|
|
|
|
|
|
|
|
|
if (!read_image_pixels(&reader, temp_arena)) {
|
|
|
|
|
goto READ_BASELINE_DESTROY_ARENA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img_out = create_image(img.image_width, img.image_length, buf, arena);
|
|
|
|
|
img_out = create_image(reader.img.image_width, reader.img.image_length,
|
|
|
|
|
reader.pixels, arena);
|
|
|
|
|
|
|
|
|
|
READ_BASELINE_DESTROY_ARENA:
|
|
|
|
|
wapp_mem_arena_destroy(&temp_arena);
|
|
|
|
|
|
|
|
|
|
READ_BASELINE_FILE_CLEANUP:
|
|
|
|
|
fclose(fp);
|
|
|
|
|
fclose(reader.fp);
|
|
|
|
|
|
|
|
|
|
READ_BASELINE_RETURN_IMG:
|
|
|
|
|
return img_out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffHdr read_tiff_header(FILE *fp) {
|
|
|
|
|
if (!fp) {
|
|
|
|
|
return NULL_TIFF_HEADER;
|
|
|
|
|
}
|
|
|
|
|
internal bool read_tiff_header(TiffReader *reader) {
|
|
|
|
|
fread_with_offset(reader->fp, &(reader->header), sizeof(TiffHdr), 0);
|
|
|
|
|
|
|
|
|
|
TiffHdr header = NULL_TIFF_HEADER;
|
|
|
|
|
read_from_file_with_offset(fp, &header, sizeof(TiffHdr), 0);
|
|
|
|
|
|
|
|
|
|
switch (header.order) {
|
|
|
|
|
switch (reader->header.order) {
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
header.magic = htons(header.magic);
|
|
|
|
|
header.first_ifd_offset = htonl(header.first_ifd_offset);
|
|
|
|
|
reader->header.magic = htons(reader->header.magic);
|
|
|
|
|
reader->header.first_ifd_offset = htonl(reader->header.first_ifd_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
header.magic = ntohs(header.magic);
|
|
|
|
|
header.first_ifd_offset = ntohl(header.first_ifd_offset);
|
|
|
|
|
reader->header.magic = ntohs(reader->header.magic);
|
|
|
|
|
reader->header.first_ifd_offset = ntohl(reader->header.first_ifd_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return NULL_TIFF_HEADER;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (header.magic != TIFF_MAGIC) {
|
|
|
|
|
return NULL_TIFF_HEADER;
|
|
|
|
|
if (reader->header.magic != TIFF_MAGIC) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return header;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset, Arena *arena) {
|
|
|
|
|
if (!fp || !header) {
|
|
|
|
|
return NULL_TIFF_IFD;
|
|
|
|
|
}
|
|
|
|
|
internal bool read_ifd(TiffReader *reader, Arena *arena) {
|
|
|
|
|
fread_with_offset(reader->fp, &(reader->ifd.count), sizeof(reader->ifd.count),
|
|
|
|
|
reader->header.first_ifd_offset);
|
|
|
|
|
|
|
|
|
|
TiffIFD ifd = NULL_TIFF_IFD;
|
|
|
|
|
read_from_file_with_offset(fp, &(ifd.count), sizeof(ifd.count), offset);
|
|
|
|
|
|
|
|
|
|
switch (header->order) {
|
|
|
|
|
switch (reader->header.order) {
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
ifd.count = htons(ifd.count);
|
|
|
|
|
reader->ifd.count = htons(reader->ifd.count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
ifd.count = ntohs(ifd.count);
|
|
|
|
|
reader->ifd.count = ntohs(reader->ifd.count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 field_byte_count = sizeof(TiffField) * ifd.count;
|
|
|
|
|
ifd.fields = (TiffField *)wapp_mem_arena_alloc(arena, field_byte_count);
|
|
|
|
|
if (!(ifd.fields)) {
|
|
|
|
|
return NULL_TIFF_IFD;
|
|
|
|
|
u64 field_byte_count = sizeof(TiffField) * reader->ifd.count;
|
|
|
|
|
reader->ifd.fields =
|
|
|
|
|
(TiffField *)wapp_mem_arena_alloc(arena, field_byte_count);
|
|
|
|
|
if (!(reader->ifd.fields)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fread(ifd.fields, field_byte_count, 1, fp);
|
|
|
|
|
fread(&(ifd.next_ifd), sizeof(ifd.next_ifd), 1, fp);
|
|
|
|
|
fread(reader->ifd.fields, field_byte_count, 1, reader->fp);
|
|
|
|
|
fread(&(reader->ifd.next_ifd), sizeof(reader->ifd.next_ifd), 1, reader->fp);
|
|
|
|
|
|
|
|
|
|
return ifd;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd) {
|
|
|
|
|
TiffImage img_out = NULL_TIFF_IMAGE;
|
|
|
|
|
img_out.type = TIFF_IMAGE_TYPE_BILEVEL;
|
|
|
|
|
internal bool read_ifd_fields(TiffReader *reader) {
|
|
|
|
|
reader->img.type = TIFF_IMAGE_TYPE_BILEVEL;
|
|
|
|
|
|
|
|
|
|
for (u64 i = 0; i < ifd->count; ++i) {
|
|
|
|
|
TiffField *field = &(ifd->fields[i]);
|
|
|
|
|
for (u64 i = 0; i < reader->ifd.count; ++i) {
|
|
|
|
|
TiffField *field = &(reader->ifd.fields[i]);
|
|
|
|
|
|
|
|
|
|
switch (header->order) {
|
|
|
|
|
switch (reader->header.order) {
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
field->tag = ntohs(field->tag);
|
|
|
|
|
@@ -224,161 +393,341 @@ TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!read_field(field, &img_out)) {
|
|
|
|
|
img_out = NULL_TIFF_IMAGE;
|
|
|
|
|
goto READ_FIELDS_RETURN_IMAGE;
|
|
|
|
|
if (!read_field(field, &(reader->img))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!validate_image_type(&img_out)) {
|
|
|
|
|
img_out = NULL_TIFF_IMAGE;
|
|
|
|
|
goto READ_FIELDS_RETURN_IMAGE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
// 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.long_val, img_out.strip_offsets_offset);
|
|
|
|
|
printf("STRIP BYTES (byte count, is offset) : %u, %u\n", img_out.strip_byte_counts.long_val, img_out.strip_byte_counts_offset);
|
|
|
|
|
// clang-format on
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
READ_FIELDS_RETURN_IMAGE:
|
|
|
|
|
return img_out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pixel *load_image_pixels(FILE *fp, const TiffHdr *header, TiffImage *img,
|
|
|
|
|
Arena *arena) {
|
|
|
|
|
Pixel *buf = NULL;
|
|
|
|
|
u64 img_byte_count = sizeof(Pixel) * img->image_width * img->image_length;
|
|
|
|
|
|
|
|
|
|
if (!read_strip_data(fp, header, img, arena)) {
|
|
|
|
|
goto LOAD_IMAGE_PIXELS_RETURN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
for (u32 i = 0; i < img->strip_count; ++i) {
|
|
|
|
|
printf("%u, %u\n", img->strips[i].offset, img->strips[i].byte_count);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
buf = wapp_mem_arena_alloc(arena, img_byte_count);
|
|
|
|
|
if (!buf) {
|
|
|
|
|
goto LOAD_IMAGE_PIXELS_RETURN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
read_strips(fp, img, buf);
|
|
|
|
|
|
|
|
|
|
LOAD_IMAGE_PIXELS_RETURN:
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool read_strip_data(FILE *fp, const TiffHdr *header, TiffImage *img,
|
|
|
|
|
Arena *arena) {
|
|
|
|
|
if (!fp || !img || !arena) {
|
|
|
|
|
if (!validate_image_type(&(reader->img))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img->strips =
|
|
|
|
|
wapp_mem_arena_alloc(arena, sizeof(TiffStrip) * img->strip_count);
|
|
|
|
|
if (!img->strips) {
|
|
|
|
|
read_alpha(reader);
|
|
|
|
|
read_bits_per_sample(reader);
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
// clang-format off
|
|
|
|
|
printf("SIZE (width, height) : %u, %u\n", reader->img.image_width, reader->img.image_length);
|
|
|
|
|
printf("SAMPLES (bits per sample, count, is offset) : %u, %u, %u\n", reader->img.bits_per_sample, reader->img.sample_count, reader->img.bits_per_sample_offset);
|
|
|
|
|
printf("EXTRA SAMPLES (samples, count, is offset) : %u, %u, %u\n", reader->img.extra_samples, reader->img.extra_samples_count, reader->img.extra_samples_offset);
|
|
|
|
|
printf("ALPHA (type, offset) : %u, %u\n", reader->img.alpha.type, reader->img.alpha.sample_offset);
|
|
|
|
|
printf("SAMPLES PER PIXEL : %u\n", reader->img.sample_count);
|
|
|
|
|
printf("PHOTOMETRIC INTERPRETATION : %u\n", reader->img.photometric_interpretation);
|
|
|
|
|
printf("ROWS PER STRIP (rows, strip count) : %u, %u\n", reader->img.rows_per_strip, reader->img.strip_count);
|
|
|
|
|
printf("STRIP OFFSETS (offsets, is offset) : %u, %u\n", reader->img.strip_offsets.long_val, reader->img.strip_offsets_offset);
|
|
|
|
|
printf("STRIP BYTES (byte count, is offset) : %u, %u\n", reader->img.strip_byte_counts.long_val, reader->img.strip_byte_counts_offset);
|
|
|
|
|
// clang-format on
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void read_alpha(TiffReader *reader) {
|
|
|
|
|
reader->img.alpha = NULL_TIFF_ALPHA;
|
|
|
|
|
|
|
|
|
|
if (reader->img.extra_samples_count == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 byte_count = TIFF_SHORT_BYTE_COUNT * reader->img.extra_samples_count;
|
|
|
|
|
u16 samples[reader->img.extra_samples_count];
|
|
|
|
|
memset(samples, 0, byte_count);
|
|
|
|
|
|
|
|
|
|
if (reader->img.extra_samples_offset) {
|
|
|
|
|
fread_with_offset(reader->fp, samples, byte_count,
|
|
|
|
|
reader->img.extra_samples);
|
|
|
|
|
|
|
|
|
|
switch (reader->header.order) {
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
for (u64 i = 0; i < reader->img.extra_samples_count; ++i) {
|
|
|
|
|
samples[i] = ntohs(samples[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
for (u64 i = 0; i < reader->img.extra_samples_count; ++i) {
|
|
|
|
|
samples[i] = htons(samples[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
memcpy(samples, &(reader->img.extra_samples), byte_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (u32 i = 0; i < reader->img.extra_samples_count; ++i) {
|
|
|
|
|
u16 *sample = &(samples[i]);
|
|
|
|
|
if (*sample == TIFF_EXTRA_SAMPLE_ASSOCIATED_ALPHA ||
|
|
|
|
|
*sample == TIFF_EXTRA_SAMPLE_UNASSOCIATED_ALPHA) {
|
|
|
|
|
reader->img.alpha.type = *sample == TIFF_EXTRA_SAMPLE_ASSOCIATED_ALPHA
|
|
|
|
|
? ALPHA_TYPE_ASSOCIATED
|
|
|
|
|
: ALPHA_TYPE_UNASSOCIATED;
|
|
|
|
|
reader->img.alpha.sample_offset = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void read_bits_per_sample(TiffReader *reader) {
|
|
|
|
|
TiffAlpha alpha = reader->img.alpha;
|
|
|
|
|
u64 main_samples = reader->img.sample_count - reader->img.extra_samples_count;
|
|
|
|
|
|
|
|
|
|
u64 byte_count = TIFF_SHORT_BYTE_COUNT * reader->img.sample_count;
|
|
|
|
|
u16 bits_per_sample[reader->img.sample_count];
|
|
|
|
|
memset(bits_per_sample, 0, byte_count);
|
|
|
|
|
|
|
|
|
|
if (reader->img.bits_per_sample_offset) {
|
|
|
|
|
fread_with_offset(reader->fp, bits_per_sample, byte_count,
|
|
|
|
|
reader->img.bits_per_sample);
|
|
|
|
|
|
|
|
|
|
switch (reader->header.order) {
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
for (u64 i = 0; i < reader->img.sample_count; ++i) {
|
|
|
|
|
bits_per_sample[i] = ntohs(bits_per_sample[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
for (u64 i = 0; i < reader->img.sample_count; ++i) {
|
|
|
|
|
bits_per_sample[i] = htons(bits_per_sample[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
memcpy(bits_per_sample, &(reader->img.bits_per_sample), byte_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiffSampleBits *bits = &(reader->img.rgba_bits_per_sample);
|
|
|
|
|
*bits = (TiffSampleBits){0};
|
|
|
|
|
memcpy(bits, bits_per_sample, TIFF_SHORT_BYTE_COUNT * main_samples);
|
|
|
|
|
|
|
|
|
|
// Set missing samples if image doesn't have all RGB samples
|
|
|
|
|
if (main_samples < RGB_SAMPLE_COUNT) {
|
|
|
|
|
u64 count = RGB_SAMPLE_COUNT - main_samples;
|
|
|
|
|
for (u64 i = 0; i < count; ++i) {
|
|
|
|
|
u16 *value = &(((u16 *)(&bits))[main_samples + i]);
|
|
|
|
|
*value = MIN_BITS_PER_SAMPLE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (alpha.type == ALPHA_TYPE_UNDEFINED) {
|
|
|
|
|
bits->a = MIN_BITS_PER_SAMPLE;
|
|
|
|
|
} else {
|
|
|
|
|
void *alpha_sample = &(bits_per_sample[main_samples + alpha.sample_offset]);
|
|
|
|
|
memcpy(&(bits->a), alpha_sample, TIFF_SHORT_BYTE_COUNT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bits->r = bits->r < MIN_BITS_PER_SAMPLE ? MIN_BITS_PER_SAMPLE : bits->r;
|
|
|
|
|
bits->g = bits->g < MIN_BITS_PER_SAMPLE ? MIN_BITS_PER_SAMPLE : bits->g;
|
|
|
|
|
bits->b = bits->b < MIN_BITS_PER_SAMPLE ? MIN_BITS_PER_SAMPLE : bits->b;
|
|
|
|
|
bits->a = bits->a < MIN_BITS_PER_SAMPLE ? MIN_BITS_PER_SAMPLE : bits->a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal bool read_image_pixels(TiffReader *reader, Arena *arena) {
|
|
|
|
|
reader->pixels = NULL;
|
|
|
|
|
u64 img_byte_count =
|
|
|
|
|
sizeof(Pixel) * reader->img.image_width * reader->img.image_length;
|
|
|
|
|
|
|
|
|
|
if (!read_strip_data(reader, arena)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
for (u32 i = 0; i < reader->img.strip_count; ++i) {
|
|
|
|
|
printf("%u, %u\n", reader->img.strips[i].offset,
|
|
|
|
|
reader->img.strips[i].byte_count);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
reader->pixels = wapp_mem_arena_alloc(arena, img_byte_count);
|
|
|
|
|
if (!reader->pixels) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return read_strips(reader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal bool read_strip_data(TiffReader *reader, Arena *arena) {
|
|
|
|
|
reader->img.strips =
|
|
|
|
|
wapp_mem_arena_alloc(arena, sizeof(TiffStrip) * reader->img.strip_count);
|
|
|
|
|
if (!(reader->img.strips)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 offsets_total_bytes =
|
|
|
|
|
img->strip_count * img->strip_offsets_type_byte_count;
|
|
|
|
|
reader->img.strip_count * reader->img.strip_offsets_type_byte_count;
|
|
|
|
|
u32 byte_count_total_bytes =
|
|
|
|
|
img->strip_count * img->strip_byte_count_type_byte_count;
|
|
|
|
|
reader->img.strip_count * reader->img.strip_byte_count_type_byte_count;
|
|
|
|
|
|
|
|
|
|
if ((!(img->strip_offsets_offset) && offsets_total_bytes > sizeof(u32)) ||
|
|
|
|
|
(!(img->strip_byte_counts_offset) &&
|
|
|
|
|
byte_count_total_bytes > sizeof(u32))) {
|
|
|
|
|
if ((!(reader->img.strip_offsets_offset) &&
|
|
|
|
|
offsets_total_bytes > TIFF_LONG_BYTE_COUNT) ||
|
|
|
|
|
(!(reader->img.strip_byte_counts_offset) &&
|
|
|
|
|
byte_count_total_bytes > TIFF_LONG_BYTE_COUNT)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (u32 i = 0; i < img->strip_count; ++i) {
|
|
|
|
|
TiffStrip *strip = &(img->strips[i]);
|
|
|
|
|
u16 offset_size = reader->img.strip_offsets_type_byte_count;
|
|
|
|
|
u16 counts_size = reader->img.strip_byte_count_type_byte_count;
|
|
|
|
|
|
|
|
|
|
u16 offset_size = img->strip_offsets_type_byte_count;
|
|
|
|
|
u16 counts_size = img->strip_byte_count_type_byte_count;
|
|
|
|
|
for (u32 i = 0; i < reader->img.strip_count; ++i) {
|
|
|
|
|
TiffStrip *strip = &(reader->img.strips[i]);
|
|
|
|
|
StripDataField offsets = {
|
|
|
|
|
.strip_value = &(strip->offset),
|
|
|
|
|
.value_from_file = &(reader->img.strip_offsets),
|
|
|
|
|
.strip_index = i,
|
|
|
|
|
.length_in_bytes = offsets_total_bytes,
|
|
|
|
|
.type_byte_count = offset_size,
|
|
|
|
|
};
|
|
|
|
|
StripDataField counts = {
|
|
|
|
|
.strip_value = &(strip->byte_count),
|
|
|
|
|
.value_from_file = &(reader->img.strip_byte_counts),
|
|
|
|
|
.strip_index = i,
|
|
|
|
|
.length_in_bytes = byte_count_total_bytes,
|
|
|
|
|
.type_byte_count = counts_size,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (offsets_total_bytes > sizeof(u32)) {
|
|
|
|
|
u32 offset = img->strip_offsets.long_val + offset_size * i;
|
|
|
|
|
read_from_file_with_offset(fp, &(strip->offset), offset_size, offset);
|
|
|
|
|
|
|
|
|
|
switch (header->order) {
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
strip->offset = offset_size > sizeof(u16) ? ntohl(strip->offset)
|
|
|
|
|
: ntohs(strip->offset);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
strip->offset = offset_size > sizeof(u16) ? htonl(strip->offset)
|
|
|
|
|
: htons(strip->offset);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
memcpy(&(strip->offset), &(img->strip_offsets.long_val), offset_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (byte_count_total_bytes > sizeof(u32)) {
|
|
|
|
|
u32 offset = img->strip_byte_counts.long_val + counts_size * i;
|
|
|
|
|
read_from_file_with_offset(fp, &(strip->byte_count), counts_size, offset);
|
|
|
|
|
|
|
|
|
|
switch (header->order) {
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
strip->byte_count = counts_size > sizeof(u16)
|
|
|
|
|
? ntohl(strip->byte_count)
|
|
|
|
|
: ntohs(strip->byte_count);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
strip->byte_count = counts_size > sizeof(u16)
|
|
|
|
|
? htonl(strip->byte_count)
|
|
|
|
|
: htons(strip->byte_count);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
memcpy(&(strip->byte_count), &(img->strip_byte_counts.long_val),
|
|
|
|
|
counts_size);
|
|
|
|
|
}
|
|
|
|
|
read_strip_data_field(reader, &offsets);
|
|
|
|
|
read_strip_data_field(reader, &counts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void read_strips(FILE *fp, const TiffImage *img, Pixel *buf) {
|
|
|
|
|
internal bool read_strips(TiffReader *reader) {
|
|
|
|
|
u64 position = 0;
|
|
|
|
|
for (u64 i = 0; i < img->strip_count; ++i) {
|
|
|
|
|
const TiffStrip *strip = &(img->strips[i]);
|
|
|
|
|
StripThreadArgs args[reader->img.strip_count];
|
|
|
|
|
pthread_t threads[reader->img.strip_count];
|
|
|
|
|
|
|
|
|
|
for (u64 j = 0; j < strip->byte_count / img->sample_count; ++j) {
|
|
|
|
|
Pixel *p = &(buf[position]);
|
|
|
|
|
for (u64 i = 0; i < reader->img.strip_count; ++i) {
|
|
|
|
|
const TiffStrip *strip = &(reader->img.strips[i]);
|
|
|
|
|
u64 pixel_count = strip->byte_count / reader->img.sample_count;
|
|
|
|
|
args[i] = (StripThreadArgs){
|
|
|
|
|
.fp = fopen(reader->filename, "rb"),
|
|
|
|
|
.strip = strip,
|
|
|
|
|
.pixel_count = pixel_count,
|
|
|
|
|
.samples_per_pixel = reader->img.sample_count,
|
|
|
|
|
.main_samples =
|
|
|
|
|
reader->img.sample_count - reader->img.extra_samples_count,
|
|
|
|
|
.alpha = reader->img.alpha,
|
|
|
|
|
.pixel_write_start = &(reader->pixels[position]),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
read_from_file_with_offset(fp, p, img->sample_count,
|
|
|
|
|
strip->offset + j * img->sample_count);
|
|
|
|
|
if (!args[i].fp) {
|
|
|
|
|
for (u64 j = 0; j < i; ++j) {
|
|
|
|
|
fclose(args[j].fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (img->sample_count == 3) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
position += pixel_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (u64 i = 0; i < reader->img.strip_count; ++i) {
|
|
|
|
|
if (pthread_create(&(threads[i]), NULL, read_strip, &(args[i])) != 0) {
|
|
|
|
|
for (u64 j = 0; j < i; ++j) {
|
|
|
|
|
pthread_cancel(threads[i]);
|
|
|
|
|
fclose(args[j].fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close the remaining files
|
|
|
|
|
for (u64 j = i; j < reader->img.strip_count; ++j) {
|
|
|
|
|
fclose(args[j].fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool image_loaded = true;
|
|
|
|
|
|
|
|
|
|
for (u64 i = 0; i < reader->img.strip_count; ++i) {
|
|
|
|
|
if (pthread_join(threads[i], NULL) != 0) {
|
|
|
|
|
image_loaded = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(args[i].fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return image_loaded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void *read_strip(void *arg) {
|
|
|
|
|
StripThreadArgs *args = (StripThreadArgs *)arg;
|
|
|
|
|
|
|
|
|
|
Pixel *p;
|
|
|
|
|
u64 write_offset = 0;
|
|
|
|
|
u64 read_offset;
|
|
|
|
|
u64 alpha_offset;
|
|
|
|
|
|
|
|
|
|
for (u64 j = 0; j < args->pixel_count; ++j) {
|
|
|
|
|
p = args->pixel_write_start + write_offset;
|
|
|
|
|
read_offset = args->strip->offset + j * args->samples_per_pixel;
|
|
|
|
|
alpha_offset = read_offset + args->main_samples + args->alpha.sample_offset;
|
|
|
|
|
|
|
|
|
|
fread_with_offset(args->fp, p, args->main_samples, read_offset);
|
|
|
|
|
|
|
|
|
|
if (args->alpha.type == ALPHA_TYPE_UNDEFINED) {
|
|
|
|
|
p->a = 255;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fread_with_offset(args->fp, &(p->a), 1, alpha_offset);
|
|
|
|
|
|
|
|
|
|
++position;
|
|
|
|
|
}
|
|
|
|
|
if (args->alpha.type == ALPHA_TYPE_UNASSOCIATED) {
|
|
|
|
|
f32 a_norm = u8_normalise(p->a);
|
|
|
|
|
|
|
|
|
|
p->r = u8_denormalise(u8_normalise(p->r) * a_norm);
|
|
|
|
|
p->g = u8_denormalise(u8_normalise(p->g) * a_norm);
|
|
|
|
|
p->b = u8_denormalise(u8_normalise(p->b) * a_norm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool read_field(const TiffField *field, TiffImage *img) {
|
|
|
|
|
++write_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void read_strip_data_field(const TiffReader *reader,
|
|
|
|
|
StripDataField *field) {
|
|
|
|
|
u16 tiff_long_byte_count = field_types[TIFF_FIELD_TYPE_LONG].byte_count;
|
|
|
|
|
|
|
|
|
|
bool value_is_file_offset = field->length_in_bytes > tiff_long_byte_count;
|
|
|
|
|
u32 offset = field->type_byte_count * field->strip_index;
|
|
|
|
|
|
|
|
|
|
if (!value_is_file_offset) {
|
|
|
|
|
u8 *value = (u8 *)&(field->value_from_file->long_val);
|
|
|
|
|
memcpy(field->strip_value, value + offset, field->type_byte_count);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fread_with_offset(reader->fp, field->strip_value, field->type_byte_count,
|
|
|
|
|
field->value_from_file->long_val + offset);
|
|
|
|
|
|
|
|
|
|
switch (reader->header.order) {
|
|
|
|
|
case TIFF_ORDER_BIG_ENDIAN:
|
|
|
|
|
if (IS_LITTLE_ENDIAN) {
|
|
|
|
|
*(field->strip_value) = field->type_byte_count > TIFF_SHORT_BYTE_COUNT
|
|
|
|
|
? ntohl(*(field->strip_value))
|
|
|
|
|
: ntohs(*(field->strip_value));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TIFF_ORDER_LITTLE_ENDIAN:
|
|
|
|
|
if (IS_BIG_ENDIAN) {
|
|
|
|
|
*(field->strip_value) = field->type_byte_count > TIFF_SHORT_BYTE_COUNT
|
|
|
|
|
? htonl(*(field->strip_value))
|
|
|
|
|
: htons(*(field->strip_value));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal bool read_field(const TiffField *field, TiffImage *img) {
|
|
|
|
|
switch (field->tag) {
|
|
|
|
|
case TIFF_PUBLIC_TAG_IMAGE_WIDTH:
|
|
|
|
|
if (field->count > 1) {
|
|
|
|
|
@@ -431,9 +780,9 @@ bool read_field(const TiffField *field, TiffImage *img) {
|
|
|
|
|
case TIFF_PUBLIC_TAG_STRIP_OFFSETS:
|
|
|
|
|
img->strip_offsets_type_byte_count = field_types[field->type].byte_count;
|
|
|
|
|
|
|
|
|
|
if (img->strip_offsets_type_byte_count == sizeof(u16)) {
|
|
|
|
|
if (img->strip_offsets_type_byte_count == TIFF_SHORT_BYTE_COUNT) {
|
|
|
|
|
img->strip_offsets.short_val = field->value_offset;
|
|
|
|
|
} else if (img->strip_offsets_type_byte_count == sizeof(u32)) {
|
|
|
|
|
} else if (img->strip_offsets_type_byte_count == TIFF_LONG_BYTE_COUNT) {
|
|
|
|
|
img->strip_offsets.long_val = field->value_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -476,9 +825,9 @@ bool read_field(const TiffField *field, TiffImage *img) {
|
|
|
|
|
case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS:
|
|
|
|
|
img->strip_byte_count_type_byte_count = field_types[field->type].byte_count;
|
|
|
|
|
|
|
|
|
|
if (img->strip_byte_count_type_byte_count == sizeof(u16)) {
|
|
|
|
|
if (img->strip_byte_count_type_byte_count == TIFF_SHORT_BYTE_COUNT) {
|
|
|
|
|
img->strip_byte_counts.short_val = field->value_offset;
|
|
|
|
|
} else if (img->strip_byte_count_type_byte_count == sizeof(u32)) {
|
|
|
|
|
} else if (img->strip_byte_count_type_byte_count == TIFF_LONG_BYTE_COUNT) {
|
|
|
|
|
img->strip_byte_counts.long_val = field->value_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -515,7 +864,7 @@ bool read_field(const TiffField *field, TiffImage *img) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool validate_image_type(const TiffImage *img) {
|
|
|
|
|
internal bool validate_image_type(const TiffImage *img) {
|
|
|
|
|
if (img->type < TIFF_IMAGE_TYPE_BILEVEL || img->type > TIFF_IMAGE_TYPE_RGB) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -550,7 +899,7 @@ bool validate_image_type(const TiffImage *img) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset) {
|
|
|
|
|
internal void fread_with_offset(FILE *fp, void *dst, u64 count, u64 offset) {
|
|
|
|
|
if (!fp || !dst) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@@ -559,3 +908,29 @@ void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset) {
|
|
|
|
|
|
|
|
|
|
fread(dst, count, 1, fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal bool valid_tiff_file(const char *file) {
|
|
|
|
|
u64 name_length = strlen(file);
|
|
|
|
|
if (name_length < TIFF_FILENAME_MIN_LENGTH) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *ext = NULL;
|
|
|
|
|
for (u32 i = name_length - 1; i >= name_length - TIFF_FILENAME_MIN_LENGTH;
|
|
|
|
|
--i) {
|
|
|
|
|
if (file[i] == '.') {
|
|
|
|
|
ext = &(file[i]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ext || !IS_TIFF_EXTENSION(ext)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal f32 u8_normalise(u8 value) { return (f32)value / (f32)UINT8_MAX; }
|
|
|
|
|
|
|
|
|
|
internal u8 u8_denormalise(f32 value) { return (u8)(value * UINT8_MAX); }
|
|
|
|
|
|