From 8040dd738132b23e99a7c502043c5df5c939f475 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 21 Apr 2024 18:55:02 +0100 Subject: [PATCH] First botched attempt at reading image data :D --- compile | 6 +- src/{ => image}/image.c | 0 src/{ => image}/image.h | 0 src/main.c | 171 +++++++++++++++--- src/{ => tiff}/tiff_field_types.inc | 0 src/{ => tiff}/tiff_public_tags.inc | 0 src/{ => tiff}/tiffread.c | 266 +++++++++++++++++++++++----- src/{ => tiff}/tiffread.h | 0 src/{ => utils}/endianness.h | 0 9 files changed, 369 insertions(+), 74 deletions(-) rename src/{ => image}/image.c (100%) rename src/{ => image}/image.h (100%) rename src/{ => tiff}/tiff_field_types.inc (100%) rename src/{ => tiff}/tiff_public_tags.inc (100%) rename src/{ => tiff}/tiffread.c (61%) rename src/{ => tiff}/tiffread.h (100%) rename src/{ => utils}/endianness.h (100%) diff --git a/compile b/compile index 31a4a64..8ec864e 100755 --- a/compile +++ b/compile @@ -3,17 +3,19 @@ CC=clang CFLAGS="-g -Wall -Werror -pedantic -fsanitize=address -fsanitize=undefined" INCLUDES="\ - -Isrc \ + -I$(find ./src -type d | xargs -I{} echo -n "-I{} ") \ -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="\ - ./src/*.c \ + $(find ./src -name *.c | xargs -I{} echo -n "{} ") \ intern/wizapp/cpath/src/*.c \ intern/wizapp/dstr/src/*.c \ intern/wizapp/mem/src/*/*.c \ diff --git a/src/image.c b/src/image/image.c similarity index 100% rename from src/image.c rename to src/image/image.c diff --git a/src/image.h b/src/image/image.h similarity index 100% rename from src/image.h rename to src/image/image.h diff --git a/src/main.c b/src/main.c index 5828036..df79ff8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,40 @@ #include "aliases.h" +#include "image.h" #include "mem_allocator.h" #include "mem_ctx.h" #include "tiffread.h" +#include +#include +#include +#include +#include #include +#include #include +#include #include +#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); @@ -14,41 +42,134 @@ int main(int argc, char *argv[]) { const char *file_to_open = argc > 1 ? argv[1] : "./resources/test.tif"; - read_baseline_tiff(file_to_open, &ctx_main_allocator); + Image *img = read_baseline_tiff(file_to_open, &ctx_main_allocator); + if (!img) { + exit_code = EXIT_FAILURE; + goto MAIN_FREE_CONTEXT; + } - // FILE *fp = fopen(file_to_open, "rb"); + FILE *out = fopen("test.ppm", "wb"); - // TiffHdr header; - // fread(&header, sizeof(header), 1, fp); + char magic[] = {'P', '6', '\n'}; + fwrite(magic, sizeof(magic), 1, out); - // TiffIFD ifd = {0}; + char size[128]; + sprintf(size, "%lu %lu\n", img->width, img->height); + u64 size_length = strlen(size); + fwrite(size, size_length, 1, out); - // printf("ORDER: %04x\n", header.order); - // printf("MAGIC: %04x\n", header.magic); + char max[] = {'2', '5', '5', '\n'}; + fwrite(max, sizeof(max), 1, out); - // fseek(fp, header.first_ifd_offset, SEEK_SET); + for (u64 i = 0; i < img->buf_length; i += 4) { + fwrite(&(img->data[i]), sizeof(u8), 3, out); + } - // fread(&(ifd.count), sizeof(ifd.count), 1, fp); + fclose(out); - // printf("COUNT: %u\n", ifd.count); + // if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { + // exit_code = EXIT_FAILURE; + // goto MAIN_FREE_CONTEXT; + // }; - // 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_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; // } + // 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 0; + 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; } diff --git a/src/tiff_field_types.inc b/src/tiff/tiff_field_types.inc similarity index 100% rename from src/tiff_field_types.inc rename to src/tiff/tiff_field_types.inc diff --git a/src/tiff_public_tags.inc b/src/tiff/tiff_public_tags.inc similarity index 100% rename from src/tiff_public_tags.inc rename to src/tiff/tiff_public_tags.inc diff --git a/src/tiffread.c b/src/tiff/tiffread.c similarity index 61% rename from src/tiffread.c rename to src/tiff/tiffread.c index af51b0b..5dd104c 100644 --- a/src/tiffread.c +++ b/src/tiff/tiffread.c @@ -3,10 +3,12 @@ #include "endianness.h" #include "image.h" #include "mem_allocator.h" +#include "mem_arena.h" #include "mem_ctx.h" #include "mem_libc.h" #include #include +#include #include #include #include @@ -24,21 +26,22 @@ #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, +#define INVALID_STRIP_COUNT 0 - COUNT_TIFF_IMAGE, -}; +#define INVALID_ALPHA_OFFSET -1 // 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, +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 @@ -53,12 +56,12 @@ Image *read_baseline_tiff(const char *file, const Allocator *allocator) { Image *img_out = NULL; if (!file) { - goto RETURN_IMG; + goto READ_BASELINE_RETURN_IMG; } u64 name_length = strlen(file); if (name_length < TIFF_FILENAME_MIN_LENGTH) { - goto RETURN_IMG; + goto READ_BASELINE_RETURN_IMG; } const char *ext = NULL; @@ -71,17 +74,17 @@ Image *read_baseline_tiff(const char *file, const Allocator *allocator) { } if (!ext || !IS_TIFF_EXTENSION(ext)) { - goto RETURN_IMG; + goto READ_BASELINE_RETURN_IMG; } FILE *fp = fopen(file, "rb"); if (!fp) { - goto RETURN_IMG; + goto READ_BASELINE_RETURN_IMG; } TiffHdr header = read_tiff_header(fp); if (IS_NULL_HEADER(header)) { - goto FILE_CLEANUP; + goto READ_BASELINE_FILE_CLEANUP; } Allocator alloc; @@ -92,12 +95,12 @@ Image *read_baseline_tiff(const char *file, const Allocator *allocator) { } TiffIFD ifd = read_ifd(fp, &header, header.first_ifd_offset, &alloc); - read_fields(fp, &header, &ifd, &alloc); + img_out = read_fields(fp, &header, &ifd, &alloc); -FILE_CLEANUP: +READ_BASELINE_FILE_CLEANUP: fclose(fp); -RETURN_IMG: +READ_BASELINE_RETURN_IMG: return img_out; } @@ -186,8 +189,13 @@ Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd, 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 = 1; + u32 strip_count = INVALID_STRIP_COUNT; u8 *strip_offsets = NULL; u16 strip_offsets_type = TIFF_FIELD_TYPE_LONG; u8 *strip_byte_counts = NULL; @@ -293,9 +301,16 @@ Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd, break; case TIFF_PUBLIC_TAG_STRIP_OFFSETS: { strip_offsets_type = field->type; - u16 field_type_byte_count = field_types[strip_offsets_type].byte_count; + u16 field_type_byte_count = field_types[field->type].byte_count; u32 total_byte_count = field->count * field_type_byte_count; - strip_count = field->count; + + if (strip_count == INVALID_STRIP_COUNT) { + strip_count = field->count; + } else { + if (strip_count != field->count) { + return NULL; + } + } strip_offsets = (u8 *)wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, total_byte_count); @@ -350,9 +365,16 @@ Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd, 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; + u16 field_type_byte_count = field_types[field->type].byte_count; u32 total_byte_count = field->count * field_type_byte_count; - strip_count = field->count; + + if (strip_count == INVALID_STRIP_COUNT) { + strip_count = field->count; + } else { + if (strip_count != field->count) { + return NULL; + } + } strip_byte_counts = (u8 *)wapp_mem_ctx_alloc(CTX_DEST_BUFFER_TEMP, total_byte_count); @@ -396,52 +418,202 @@ Image *read_fields(FILE *fp, const TiffHdr *header, const TiffIFD *ifd, case TIFF_PUBLIC_TAG_COLOR_MAP: identifier |= field->tag; break; + case TIFF_PUBLIC_TAG_EXTRA_SAMPLES: { + extra_samples = field->count; + u64 byte_count = field->count * sizeof(u16); + + u8 data_buf[byte_count]; + + if (byte_count <= 4) { + memcpy(data_buf, (void *)&(field->value_offset), byte_count); + } else { + read_from_file_with_offset(fp, data_buf, byte_count, + field->value_offset); + } + + for (u32 i = 0; i < field->count; ++i) { + u16 sample = ((u16 *)&(field->value_offset))[i]; + + if (byte_count > 4) { + switch (header->order) { + case TIFF_ORDER_LITTLE_ENDIAN: + if (IS_BIG_ENDIAN) { + sample = htons(sample); + } + + break; + case TIFF_ORDER_BIG_ENDIAN: + if (IS_LITTLE_ENDIAN) { + sample = ntohs(sample); + } + + break; + } + } + + if (sample > TIFF_EXTRA_SAMPLE_UNASSOCIATED_ALPHA) { + return NULL; + } + + if (sample > TIFF_EXTRA_SAMPLE_UNSPECIFIED) { + if (has_alpha) { + return NULL; + } + + has_alpha = true; + associated_alpha = + sample == TIFF_EXTRA_SAMPLE_ASSOCIATED_ALPHA ? true : false; + alpha_offset = i; + } + } + + 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"); + switch (identifier) { + case TIFF_IMAGE_BILEVEL: + case TIFF_IMAGE_GRAYSCALE: + if (photometric_interpretation > + TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO) { + return NULL; + } + + break; + case TIFF_IMAGE_PALETTE: + if (photometric_interpretation != + TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE) { + return NULL; + } + + break; + case TIFF_IMAGE_RGB: + if (photometric_interpretation != TIFF_PHOTOMETRIC_INTERPRETATION_RGB) { + return NULL; + } + + break; + default: + return NULL; } printf("WIDTH: %lu, HEIGHT: %lu\n", width, height); printf("SAMPLES: %u\n", samples_per_pixel); + printf("EXTRA SAMPLES: %u\n", extra_samples); + printf("ALPHA: %s, %s, %d\n", has_alpha ? "HAS ALPHA" : "NO ALPHA", + has_alpha && associated_alpha ? "ASSOCIATED" + : has_alpha ? "UNASSOCIATED" + : "N/A", + alpha_offset); printf("STRIPS: %u\n", strip_count); printf("PHOTOMETRIC INTERPRETATION: %u\n", photometric_interpretation); + printf("BITS PER SAMPLE: "); for (u64 i = 0; i < samples_per_pixel; ++i) { - printf("%u\n", bits_per_sample[i]); + printf("%u ", bits_per_sample[i]); + } + printf("\n"); + + if (has_alpha && alpha_offset == INVALID_ALPHA_OFFSET) { + return NULL; } - printf("OFFSETS: "); + Image *img_out = NULL; + + Arena *temp_arena; + u64 img_buf_size = width * height * sizeof(Pixel); + wapp_mem_arena_init(&temp_arena, img_buf_size * 4); + + u8 *temp_img_buf = wapp_mem_arena_alloc(temp_arena, img_buf_size); + if (!temp_img_buf) { + return NULL; + } + + u64 position = 0; for (u64 i = 0; i < strip_count; ++i) { + u64 offset; + u64 count; + if (strip_offsets_type == TIFF_FIELD_TYPE_LONG) { - printf("%u ", ((u32 *)strip_offsets)[i]); + offset = ((u32 *)strip_offsets)[i]; } else { - printf("%u ", ((u16 *)strip_offsets)[i]); + offset = ((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]); + count = ((u32 *)strip_byte_counts)[i]; } else { - printf("%u ", ((u16 *)strip_byte_counts)[i]); + 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; } } - printf("\n"); - return NULL; + img_out = create_image(width, height, (Pixel *)temp_img_buf, allocator); + +READ_FIELDS_FREE_ARENA: + wapp_mem_arena_free(&temp_arena); + + return img_out; } void read_from_file_with_offset(FILE *fp, void *dst, u64 count, u64 offset) { diff --git a/src/tiffread.h b/src/tiff/tiffread.h similarity index 100% rename from src/tiffread.h rename to src/tiff/tiffread.h diff --git a/src/endianness.h b/src/utils/endianness.h similarity index 100% rename from src/endianness.h rename to src/utils/endianness.h