Compare commits

..

No commits in common. "ce7f30f7e39506f96a09184f102bd87d187a3156" and "0bd448a8a96286feabf8429402ffb940c6b44e32" have entirely different histories.

11 changed files with 514 additions and 614 deletions

View File

@ -3,19 +3,17 @@
CC=clang
CFLAGS="-g -Wall -Werror -pedantic -fsanitize=address -fsanitize=undefined"
INCLUDES="\
-I$(find ./src -type d | xargs -I{} echo -n "-I{} ") \
-Isrc \
-Iintern/wizapp/aliases \
-Iintern/wizapp/cpath/include \
-Iintern/wizapp/dstr/include \
$(find intern/wizapp/mem/include -type d | xargs -I{} echo -n "-I{} ") \
$(pkg-config --cflags sdl2) \
"
LIBS="\
-lm \
$(pkg-config --libs sdl2) \
"
SRC="\
$(find ./src -name *.c | xargs -I{} echo -n "{} ") \
./src/*.c \
intern/wizapp/cpath/src/*.c \
intern/wizapp/dstr/src/*.c \
intern/wizapp/mem/src/*/*.c \

@ -1 +1 @@
Subproject commit 6195b521f5db2382e1e03318e78708cc9d96eb4c
Subproject commit be64571b0ec136fa785f02acca555e2f03dbd02d

View File

@ -1,40 +1,12 @@
#include "aliases.h"
#include "image.h"
#include "mem_allocator.h"
#include "mem_ctx.h"
#include "tiffread.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_video.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define AMASK 0xff000000
#define BMASK 0x00ff0000
#define GMASK 0x0000ff00
#define RMASK 0x000000ff
#define WHITE 0xff
typedef struct point Point;
struct point {
u32 x;
u32 y;
};
Point point_from_index(u32 index, u32 w);
int main(int argc, char *argv[]) {
int exit_code = EXIT_SUCCESS;
wapp_mem_ctx_init(10 * 1024 * 1024, 2 * 1024 * 1024);
Allocator ctx_main_allocator = wapp_mem_ctx_allocator(CTX_DEST_BUFFER_MAIN);
Allocator ctx_temp_allocator = wapp_mem_ctx_allocator(CTX_DEST_BUFFER_TEMP);
@ -42,134 +14,41 @@ int main(int argc, char *argv[]) {
const char *file_to_open = argc > 1 ? argv[1] : "./resources/test.tif";
Image *img = read_baseline_tiff(file_to_open, &ctx_main_allocator);
if (!img) {
exit_code = EXIT_FAILURE;
goto MAIN_FREE_CONTEXT;
}
read_baseline_tiff(file_to_open, &ctx_main_allocator);
FILE *out = fopen("test.ppm", "wb");
// FILE *fp = fopen(file_to_open, "rb");
char magic[] = {'P', '6', '\n'};
fwrite(magic, sizeof(magic), 1, out);
// TiffHdr header;
// fread(&header, sizeof(header), 1, fp);
char size[128];
sprintf(size, "%lu %lu\n", img->width, img->height);
u64 size_length = strlen(size);
fwrite(size, size_length, 1, out);
// TiffIFD ifd = {0};
char max[] = {'2', '5', '5', '\n'};
fwrite(max, sizeof(max), 1, out);
// printf("ORDER: %04x\n", header.order);
// printf("MAGIC: %04x\n", header.magic);
for (u64 i = 0; i < img->buf_length; i += 4) {
fwrite(&(img->data[i]), sizeof(u8), 3, out);
}
// fseek(fp, header.first_ifd_offset, SEEK_SET);
fclose(out);
// fread(&(ifd.count), sizeof(ifd.count), 1, fp);
// if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
// exit_code = EXIT_FAILURE;
// goto MAIN_FREE_CONTEXT;
// };
// printf("COUNT: %u\n", ifd.count);
// SDL_Window *window =
// SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED,
// SDL_WINDOWPOS_CENTERED,
// WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
// if (!window) {
// exit_code = EXIT_FAILURE;
// goto MAIN_QUIT_SDL;
// TiffField fields[ifd.count];
// memset(fields, 0, sizeof(TiffField) * ifd.count);
// for (u32 i = 0; i < ifd.count; ++i) {
// fread(fields + i, sizeof(TiffField), 1, fp);
// printf("ENTRY %02u (%lu)\n", i, ftell(fp));
// printf("\t TAG: %04u (%s)\n", fields[i].tag,
// tag_names[fields[i].tag] ? tag_names[fields[i].tag] : "UNKNOWN");
// printf("\t TYPE: 0x%04x (%s)\n", fields[i].type,
// field_types[fields[i].type].name ?
// field_types[fields[i].type].name
// : "UNKNOWN");
// printf("\t COUNT: %04u\n", fields[i].count);
// printf("\tVAL/OFF: %04u\n", fields[i].value_offset);
// }
// SDL_Renderer *renderer = SDL_CreateRenderer(
// window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
// if (!renderer) {
// exit_code = EXIT_FAILURE;
// goto MAIN_DESTROY_WINDOW;
// }
// SDL_Surface *surface = SDL_CreateRGBSurface(0, img->width, img->height, 32,
// RMASK, GMASK, BMASK, AMASK);
// if (!surface) {
// exit_code = EXIT_FAILURE;
// goto MAIN_DESTROY_RENDERER;
// }
// uint64_t surface_length = surface->h * surface->pitch;
// printf("%lu\n", surface_length);
// printf("%u\n", surface->w * surface->h);
// SDL_LockSurface(surface);
// u64 position = 0;
// for (u64 i = 0; i < surface_length; i += surface->pitch) {
// memcpy(&(((u32 *)surface->pixels)[i]), &(img->data[position]),
// surface->pitch / sizeof(Pixel));
// position += surface->pitch;
// }
// SDL_UnlockSurface(surface);
// SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
// if (!texture) {
// exit_code = EXIT_FAILURE;
// goto MAIN_DESTROY_SURFACE;
// }
// SDL_Rect dest = {
// .w = surface->w,
// .h = surface->h,
// .x = (WINDOW_WIDTH - surface->w) / 2,
// .y = (WINDOW_HEIGHT - surface->h) / 2,
// };
// bool running = true;
// SDL_Event event = {0};
// while (running) {
// while (SDL_PollEvent(&event)) {
// switch (event.type) {
// case SDL_QUIT:
// running = false;
// break;
// }
// }
// SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// SDL_RenderClear(renderer);
// SDL_RenderCopy(renderer, texture, NULL, &dest);
// SDL_RenderPresent(renderer);
// }
// SDL_DestroyTexture(texture);
// MAIN_DESTROY_SURFACE:
// SDL_FreeSurface(surface);
// MAIN_DESTROY_RENDERER:
// SDL_DestroyRenderer(renderer);
// MAIN_DESTROY_WINDOW:
// SDL_DestroyWindow(window);
// MAIN_QUIT_SDL:
// SDL_Quit();
MAIN_FREE_CONTEXT:
wapp_mem_ctx_free();
return exit_code;
}
Point point_from_index(u32 index, u32 w) {
Point out = {0};
out.x = index % w;
out.y = (index - (index % w)) / w;
return out;
return 0;
}

View File

@ -1,408 +0,0 @@
#include "tiffread.h"
#include "aliases.h"
#include "endianness.h"
#include "image.h"
#include "mem_allocator.h"
#include "mem_arena.h"
#include "mem_ctx.h"
#include "mem_libc.h"
#include <math.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define TIFF_MAGIC 0x002a
#define TIFF_FILENAME_MIN_LENGTH 6
#define TIFF_EXT_MAX_LENGTH 5
#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 INVALID_SAMPLE_COUNT 0
#define INVALID_ROWS_PER_STRIP 0
#define INVALID_STRIP_COUNT 0
#define INVALID_ALPHA_OFFSET -1
// clang-format off
// clang-format on
TiffHdr read_tiff_header(FILE *fp);
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
const Allocator *allocator);
TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd);
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset);
Image *read_baseline_tiff(const char *file, const Allocator *allocator) {
Image *img_out = NULL;
if (!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) {
goto READ_BASELINE_RETURN_IMG;
}
TiffHdr header = read_tiff_header(fp);
if (IS_NULL_HEADER(header)) {
goto READ_BASELINE_FILE_CLEANUP;
}
Allocator alloc;
if (!allocator) {
alloc = wapp_mem_libc_allocator();
} else {
alloc = *allocator;
}
TiffIFD ifd = read_ifd(fp, &header, header.first_ifd_offset, &alloc);
read_fields(fp, &header, &ifd);
READ_BASELINE_FILE_CLEANUP:
fclose(fp);
READ_BASELINE_RETURN_IMG:
return img_out;
}
TiffHdr read_tiff_header(FILE *fp) {
if (!fp) {
return NULL_TIFF_HEADER;
}
fseek(fp, 0, SEEK_SET);
TiffHdr header = NULL_TIFF_HEADER;
fread((void *)&header, sizeof(TiffHdr), 1, fp);
switch (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);
}
break;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
header.magic = ntohs(header.magic);
header.first_ifd_offset = ntohl(header.first_ifd_offset);
}
break;
default:
return NULL_TIFF_HEADER;
}
if (header.magic != TIFF_MAGIC) {
return NULL_TIFF_HEADER;
}
return header;
}
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
const Allocator *allocator) {
if (!fp || !header) {
return NULL_TIFF_IFD;
}
fseek(fp, offset, SEEK_SET);
TiffIFD ifd = NULL_TIFF_IFD;
fread(&(ifd.count), sizeof(ifd.count), 1, fp);
switch (header->order) {
case TIFF_ORDER_LITTLE_ENDIAN:
if (IS_BIG_ENDIAN) {
ifd.count = htons(ifd.count);
}
break;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
ifd.count = ntohs(ifd.count);
}
break;
}
u64 field_byte_count = sizeof(TiffField) * ifd.count;
ifd.fields =
(TiffField *)wapp_mem_allocator_alloc(allocator, field_byte_count);
fread(ifd.fields, field_byte_count, 1, fp);
fread(&(ifd.next_ifd), sizeof(ifd.next_ifd), 1, fp);
return ifd;
}
TiffImage read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd) {
TiffImage img_out = NULL_TIFF_IMAGE;
u32 image_type_identifier = TIFF_IMAGE_TYPE_BILEVEL;
for (u64 i = 0; i < ifd->count; ++i) {
TiffField *field = &(ifd->fields[i]);
switch (header->order) {
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
field->tag = ntohs(field->tag);
field->type = ntohs(field->type);
field->count = ntohl(field->count);
if (field_types[field->type].byte_count > 2 ||
field_types[field->type].byte_count * field->count > 4) {
field->value_offset = ntohl(field->value_offset);
} else {
field->value_offset = ntohs(field->value_offset);
}
}
break;
case TIFF_ORDER_LITTLE_ENDIAN:
if (IS_BIG_ENDIAN) {
field->tag = htons(field->tag);
field->type = htons(field->type);
field->count = htonl(field->count);
if (field_types[field->type].byte_count > 2 ||
field_types[field->type].byte_count * field->count > 4) {
field->value_offset = htonl(field->value_offset);
} else {
field->value_offset = htons(field->value_offset);
}
}
break;
}
switch (field->tag) {
case TIFF_PUBLIC_TAG_IMAGE_WIDTH:
if (field->count > 1) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.image_width = field->value_offset;
break;
case TIFF_PUBLIC_TAG_IMAGE_LENGTH:
if (field->count > 1) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.image_length = field->value_offset;
break;
case TIFF_PUBLIC_TAG_BITS_PER_SAMPLE:
image_type_identifier |= field->tag;
if (img_out.sample_count == INVALID_SAMPLE_COUNT) {
img_out.sample_count = field->count;
} else if (img_out.sample_count != field->count) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.bits_per_sample = field->value_offset;
img_out.bits_per_sample_offset =
img_out.sample_count * field_types[field->type].byte_count > 4;
break;
case TIFF_PUBLIC_TAG_COMPRESSION:
if (field->count > 1 ||
field->value_offset != TIFF_COMPRESSION_UNCOMPRESSED) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.compression = field->value_offset;
break;
case TIFF_PUBLIC_TAG_PHOTOMETRIC_INTERPRETATION:
if (field->count > 1) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.photometric_interpretation = field->value_offset;
break;
case TIFF_PUBLIC_TAG_STRIP_OFFSETS:
img_out.strip_offsets = field->value_offset;
if (img_out.strip_count == INVALID_STRIP_COUNT) {
img_out.strip_count = field->count;
} else if (img_out.strip_count != field->count) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.strip_offsets_offset =
img_out.strip_count * field_types[field->type].byte_count > 4;
break;
case TIFF_PUBLIC_TAG_SAMPLES_PER_PIXEL:
image_type_identifier |= field->tag;
if (field->count > 1) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.sample_count = field->value_offset;
break;
case TIFF_PUBLIC_TAG_ROWS_PER_STRIP:
if (field->count > 1) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.rows_per_strip = field->value_offset;
if (img_out.image_length > 0) {
img_out.strip_count =
floor((f64)(img_out.image_length + img_out.rows_per_strip - 1) /
img_out.rows_per_strip);
} else {
img_out.strip_count = INVALID_STRIP_COUNT;
}
break;
case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS:
img_out.strip_byte_counts = field->value_offset;
if (img_out.strip_count == INVALID_STRIP_COUNT) {
img_out.strip_count = field->count;
} else if (img_out.strip_count != field->count) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.strip_byte_counts_offset =
img_out.strip_count * field_types[field->type].byte_count > 4;
break;
case TIFF_PUBLIC_TAG_COLOR_MAP:
image_type_identifier |= field->tag;
break;
case TIFF_PUBLIC_TAG_EXTRA_SAMPLES:
img_out.extra_samples_count = field->count;
img_out.extra_samples = field->value_offset;
img_out.extra_samples_offset =
img_out.extra_samples_count * field_types[field->type].byte_count > 4;
break;
case TIFF_PUBLIC_TAG_PLANAR_CONFIGURATION:
if (field->count > 1 ||
field->value_offset != TIFF_PLANAR_CONFIG_CHUNKY) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
break;
}
}
if (image_type_identifier < TIFF_IMAGE_TYPE_BILEVEL ||
image_type_identifier > TIFF_IMAGE_TYPE_RGB) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
switch (image_type_identifier) {
case TIFF_IMAGE_TYPE_BILEVEL:
case TIFF_IMAGE_TYPE_GRAYSCALE:
if (img_out.photometric_interpretation >
TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
break;
case TIFF_IMAGE_TYPE_PALETTE:
if (img_out.photometric_interpretation !=
TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
break;
case TIFF_IMAGE_TYPE_RGB:
if (img_out.photometric_interpretation !=
TIFF_PHOTOMETRIC_INTERPRETATION_RGB) {
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
break;
default:
img_out = NULL_TIFF_IMAGE;
goto READ_FIELDS_RETURN_IMAGE;
}
img_out.type = image_type_identifier;
#if 1
// clang-format off
printf("SIZE (width, height) : %u, %u\n", img_out.image_width, img_out.image_length);
printf("SAMPLES (bits per sample, count, is offset) : %u, %u, %u\n", img_out.bits_per_sample, img_out.sample_count, img_out.bits_per_sample_offset);
printf("EXTRA SAMPLES (samples, count, is offset) : %u, %u, %u\n", img_out.extra_samples, img_out.extra_samples_count, img_out.extra_samples_offset);
printf("SAMPLES PER PIXEL : %u\n", img_out.sample_count);
printf("PHOTOMETRIC INTERPRETATION : %u\n", img_out.photometric_interpretation);
printf("ROWS PER STRIP (rows, strip count) : %u, %u\n", img_out.rows_per_strip, img_out.strip_count);
printf("STRIP OFFSETS (offsets, is offset) : %u, %u\n", img_out.strip_offsets, img_out.strip_offsets_offset);
printf("STRIP BYTES (byte count, is offset) : %u, %u\n", img_out.strip_byte_counts, img_out.strip_byte_counts_offset);
// clang-format on
#endif
READ_FIELDS_RETURN_IMAGE:
return img_out;
}
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset) {
if (!fp || !dst) {
return;
}
fseek(fp, offset, SEEK_SET);
fread(dst, count, 1, fp);
}

455
src/tiffread.c Normal file
View File

@ -0,0 +1,455 @@
#include "tiffread.h"
#include "aliases.h"
#include "endianness.h"
#include "image.h"
#include "mem_allocator.h"
#include "mem_ctx.h"
#include "mem_libc.h"
#include <math.h>
#include <netinet/in.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define TIFF_MAGIC 0x002a
#define TIFF_FILENAME_MIN_LENGTH 6
#define TIFF_EXT_MAX_LENGTH 5
#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)
enum tiff_image_types {
TIFF_IMAGE_BILEVEL,
TIFF_IMAGE_GRAYSCALE,
TIFF_IMAGE_PALETTE,
TIFF_IMAGE_RGB,
COUNT_TIFF_IMAGE,
};
// clang-format off
internal u16 tiff_image_identifiers[COUNT_TIFF_IMAGE] = {
[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,
};
// clang-format on
TiffHdr read_tiff_header(FILE *fp);
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
const Allocator *allocator);
Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd,
const Allocator *allocator);
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 *img_out = NULL;
if (!file) {
goto RETURN_IMG;
}
u64 name_length = strlen(file);
if (name_length < TIFF_FILENAME_MIN_LENGTH) {
goto 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 RETURN_IMG;
}
FILE *fp = fopen(file, "rb");
if (!fp) {
goto RETURN_IMG;
}
TiffHdr header = read_tiff_header(fp);
if (IS_NULL_HEADER(header)) {
goto FILE_CLEANUP;
}
Allocator alloc;
if (!allocator) {
alloc = wapp_mem_libc_allocator();
} else {
alloc = *allocator;
}
TiffIFD ifd = read_ifd(fp, &header, header.first_ifd_offset, &alloc);
read_fields(fp, &header, &ifd, &alloc);
FILE_CLEANUP:
fclose(fp);
RETURN_IMG:
return img_out;
}
TiffHdr read_tiff_header(FILE *fp) {
if (!fp) {
return NULL_TIFF_HEADER;
}
fseek(fp, 0, SEEK_SET);
TiffHdr header = NULL_TIFF_HEADER;
fread((void *)&header, sizeof(TiffHdr), 1, fp);
switch (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);
}
break;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
header.magic = ntohs(header.magic);
header.first_ifd_offset = ntohl(header.first_ifd_offset);
}
break;
default:
return NULL_TIFF_HEADER;
}
if (header.magic != TIFF_MAGIC) {
return NULL_TIFF_HEADER;
}
return header;
}
TiffIFD read_ifd(FILE *fp, const TiffHdr *header, u32 offset,
const Allocator *allocator) {
if (!fp || !header) {
return NULL_TIFF_IFD;
}
fseek(fp, offset, SEEK_SET);
TiffIFD ifd = NULL_TIFF_IFD;
fread(&(ifd.count), sizeof(ifd.count), 1, fp);
switch (header->order) {
case TIFF_ORDER_LITTLE_ENDIAN:
if (IS_BIG_ENDIAN) {
ifd.count = htons(ifd.count);
}
break;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
ifd.count = ntohs(ifd.count);
}
break;
}
u64 field_byte_count = sizeof(TiffField) * ifd.count;
ifd.fields =
(TiffField *)wapp_mem_allocator_alloc(allocator, field_byte_count);
fread(ifd.fields, field_byte_count, 1, fp);
fread(&(ifd.next_ifd), sizeof(ifd.next_ifd), 1, fp);
return ifd;
}
Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd,
const Allocator *allocator) {
if (!fp || !header || !ifd) {
return NULL;
}
u16 identifier = 0;
u16 photometric_interpretation = TIFF_PHOTOMETRIC_INTERPRETATION_INVALID;
u16 samples_per_pixel = 1;
u16 *bits_per_sample = NULL;
u32 strip_count = 1;
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) {
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;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
field->tag = ntohs(field->tag);
field->type = ntohs(field->type);
field->count = ntohl(field->count);
field->value_offset = ntohl(field->value_offset);
}
break;
}
switch (field->tag) {
case TIFF_PUBLIC_TAG_IMAGE_WIDTH:
if (field->count != 1) {
return NULL;
}
width = field->value_offset;
break;
case TIFF_PUBLIC_TAG_IMAGE_LENGTH:
if (field->count != 1) {
return NULL;
}
height = field->value_offset;
break;
case TIFF_PUBLIC_TAG_BITS_PER_SAMPLE:
identifier |= field->tag;
u64 byte_count = field->count * sizeof(u16);
bits_per_sample = wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, byte_count);
if (!bits_per_sample) {
break;
}
if (byte_count <= 4) {
bits_per_sample = (u16 *)&(field->value_offset);
} else {
read_from_file_with_offset(fp, bits_per_sample, byte_count,
field->value_offset);
for (u64 i = 0; i < field->count; ++i) {
switch (header->order) {
case TIFF_ORDER_LITTLE_ENDIAN:
if (IS_BIG_ENDIAN) {
bits_per_sample[i] = htons(bits_per_sample[i]);
}
break;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
bits_per_sample[i] = ntohs(bits_per_sample[i]);
}
break;
}
}
}
break;
case TIFF_PUBLIC_TAG_COMPRESSION:
if (field->count != 1) {
return NULL;
}
switch (field->value_offset) {
case TIFF_COMPRESSION_UNCOMPRESSED:
break;
case TIFF_COMPRESSION_CCITT_1D:
case TIFF_COMPRESSION_GROUP_3_FAX:
case TIFF_COMPRESSION_GROUP_4_FAX:
case TIFF_COMPRESSION_LZW:
case TIFF_COMPRESSION_JPEG:
case TIFF_COMPRESSION_PACK_BITS:
return NULL;
}
break;
case TIFF_PUBLIC_TAG_PHOTOMETRIC_INTERPRETATION:
photometric_interpretation = field->value_offset;
break;
case TIFF_PUBLIC_TAG_STRIP_OFFSETS: {
strip_offsets_type = field->type;
u16 field_type_byte_count = field_types[strip_offsets_type].byte_count;
u32 total_byte_count = field->count * field_type_byte_count;
strip_count = field->count;
strip_offsets =
(u8 *)wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, total_byte_count);
if (!strip_offsets) {
return NULL;
}
if (total_byte_count <= 4) {
memcpy(strip_offsets, &(field->value_offset), 4);
switch (header->order) {
case TIFF_ORDER_LITTLE_ENDIAN:
if (IS_BIG_ENDIAN) {
u32 val = htonl(*(u32 *)strip_offsets);
strip_offsets = (u8 *)&val;
}
break;
case TIFF_ORDER_BIG_ENDIAN:
if (IS_LITTLE_ENDIAN) {
u32 val = ntohl(*(u32 *)strip_offsets);
strip_offsets = (u8 *)&val;
}
break;
}
} else {
read_from_file_with_offset(fp, strip_offsets, total_byte_count,
field->value_offset);
}
break;
}
case TIFF_PUBLIC_TAG_SAMPLES_PER_PIXEL:
if (field->count != 1) {
return NULL;
}
identifier |= field->tag;
samples_per_pixel = field->value_offset;
break;
case TIFF_PUBLIC_TAG_ROWS_PER_STRIP:
if (field->count != 1 || height == 0) {
return NULL;
}
strip_count =
floor((f64)(height + field->value_offset - 1) / field->value_offset);
break;
case TIFF_PUBLIC_TAG_STRIP_BYTE_COUNTS: {
strip_byte_counts_type = field->type;
u16 field_type_byte_count = field_types[strip_offsets_type].byte_count;
u32 total_byte_count = field->count * field_type_byte_count;
strip_count = field->count;
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;
}
case TIFF_PUBLIC_TAG_PLANAR_CONFIGURATION:
if (field->count != 1 ||
field->value_offset != TIFF_PLANAR_CONFIG_CHUNKY) {
return NULL;
}
break;
case TIFF_PUBLIC_TAG_COLOR_MAP:
identifier |= field->tag;
break;
default:
break;
}
}
if (identifier == tiff_image_identifiers[TIFF_IMAGE_BILEVEL]) {
printf("BILEVEL\n");
} else if (identifier == tiff_image_identifiers[TIFF_IMAGE_GRAYSCALE]) {
printf("GRAYSCALE\n");
} else if (identifier == tiff_image_identifiers[TIFF_IMAGE_PALETTE]) {
printf("PALETTE\n");
} else if (identifier == tiff_image_identifiers[TIFF_IMAGE_RGB]) {
printf("RGB\n");
} else {
printf("UNKNOWN\n");
}
printf("WIDTH: %lu, HEIGHT: %lu\n", width, height);
printf("SAMPLES: %u\n", samples_per_pixel);
printf("STRIPS: %u\n", strip_count);
printf("PHOTOMETRIC INTERPRETATION: %u\n", photometric_interpretation);
for (u64 i = 0; i < samples_per_pixel; ++i) {
printf("%u\n", bits_per_sample[i]);
}
printf("OFFSETS: ");
for (u64 i = 0; i < strip_count; ++i) {
if (strip_offsets_type == TIFF_FIELD_TYPE_LONG) {
printf("%u ", ((u32 *)strip_offsets)[i]);
} else {
printf("%u ", ((u16 *)strip_offsets)[i]);
}
}
printf("\n");
printf("COUNTS: ");
for (u64 i = 0; i < strip_count; ++i) {
if (strip_byte_counts_type == TIFF_FIELD_TYPE_LONG) {
printf("%u ", ((u32 *)strip_byte_counts)[i]);
} else {
printf("%u ", ((u16 *)strip_byte_counts)[i]);
}
}
printf("\n");
return NULL;
}
void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset) {
if (!fp || !dst) {
return;
}
fseek(fp, offset, SEEK_SET);
fread(dst, count, 1, fp);
}

View File

@ -7,10 +7,19 @@ extern "C" {
#include "aliases.h"
#include "image.h"
#include <stdbool.h>
#define TAG_MAX_COUNT (UINT16_MAX * sizeof(const char *))
// Technically, the type parameter is 2-bytes long, but there aren't so many
// types specified in the spec, so UINT8_MAX should be sufficient
#define TYPE_MAX_COUNT (UINT8_MAX * sizeof(const char *))
// clang-format off
enum tiff_byte_order {
TIFF_ORDER_LITTLE_ENDIAN = 0x4949,
TIFF_ORDER_BIG_ENDIAN = 0x4d4d,
};
// clang-format on
typedef struct hdr TiffHdr;
struct hdr {
u16 order;
@ -18,31 +27,17 @@ struct hdr {
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;
};
// 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"
};
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"
};
@ -58,21 +53,24 @@ internal TiffFieldType field_types[TYPE_MAX_COUNT] = {
#include "tiff_field_types.inc"
#undef TIFF_TYPE_LOOKUP
};
// clang-format on
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,
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;
};
// clang-format off
enum tiff_compression {
TIFF_COMPRESSION_UNCOMPRESSED = 0x0001,
TIFF_COMPRESSION_CCITT_1D = 0x0002,
@ -102,28 +100,6 @@ enum tiff_planar_configuration {
};
// clang-format on
typedef struct tiff_image TiffImage;
struct tiff_image {
TiffImageType type;
u32 image_width;
u32 image_length;
u32 bits_per_sample;
u32 sample_count;
bool bits_per_sample_offset;
u16 compression;
u16 photometric_interpretation;
u32 strip_offsets;
bool strip_offsets_offset;
u32 rows_per_strip;
u32 strip_byte_counts;
bool strip_byte_counts_offset;
u32 strip_count;
u32 color_map;
u32 extra_samples;
u32 extra_samples_count;
bool extra_samples_offset;
};
Image *read_baseline_tiff(const char *file, const Allocator *allocator);
#ifdef __cplusplus