diff --git a/compile b/compile index 36400d0..1f9f224 100755 --- a/compile +++ b/compile @@ -9,6 +9,7 @@ INCLUDES="\ " LIBS="\ -lm \ + -pthread \ $(pkg-config --libs sdl2) \ " SRC="\ diff --git a/src/main.c b/src/main.c index df9fe69..5b2e9ff 100644 --- a/src/main.c +++ b/src/main.c @@ -4,10 +4,12 @@ #include "tiffread.h" #include #include +#include #include #include #include #include +#include #include #include #include @@ -17,6 +19,8 @@ #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 +#define VIEW_AREA_WIDTH 700 +#define VIEW_AREA_HEIGHT 500 #define AMASK 0xff000000 #define BMASK 0x00ff0000 @@ -32,8 +36,14 @@ struct point { }; internal void write_debug_ppm6(const Image *img); -internal SDL_Texture *image_to_texture(SDL_Renderer *renderer, - const Image *img); +internal void *load_tiff_image(void *args); +internal SDL_Rect get_dest_rect(SDL_Surface *surface); + +typedef struct img_thread_args ImgThreadArgs; +struct img_thread_args { + const char *filename; + Arena *arena; +}; int main(int argc, char *argv[]) { int exit_code = EXIT_SUCCESS; @@ -45,14 +55,13 @@ 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, arena); - if (!img) { + pthread_t img_thread; + ImgThreadArgs args = {.filename = file_to_open, .arena = arena}; + if (pthread_create(&img_thread, NULL, load_tiff_image, &args) != 0) { exit_code = EXIT_FAILURE; goto MAIN_DESTROY_ARENA; } - write_debug_ppm6(img); - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { exit_code = EXIT_FAILURE; goto MAIN_DESTROY_ARENA; @@ -73,18 +82,19 @@ int main(int argc, char *argv[]) { goto MAIN_DESTROY_WINDOW; } - SDL_Texture *texture = image_to_texture(renderer, img); - if (!texture) { + SDL_Surface *surface = NULL; + if (pthread_join(img_thread, (void **)&surface) != 0 || !surface) { exit_code = EXIT_FAILURE; goto MAIN_DESTROY_RENDERER; } - SDL_Rect dest = { - .h = img->height, - .w = img->width, - .x = (WINDOW_WIDTH - img->width) / 2, - .y = (WINDOW_HEIGHT - img->height) / 2, - }; + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); + if (!texture) { + exit_code = EXIT_FAILURE; + goto MAIN_DESTROY_SURFACE; + } + + SDL_Rect dest = get_dest_rect(surface); bool running = true; @@ -110,6 +120,9 @@ int main(int argc, char *argv[]) { SDL_DestroyTexture(texture); +MAIN_DESTROY_SURFACE: + SDL_FreeSurface(surface); + MAIN_DESTROY_RENDERER: SDL_DestroyRenderer(renderer); @@ -146,17 +159,19 @@ internal void write_debug_ppm6(const Image *img) { fclose(out); } -internal SDL_Texture *image_to_texture(SDL_Renderer *renderer, - const Image *img) { - SDL_Texture *output = NULL; - if (!renderer || !img) { - goto IMAGE_TO_TEXTURE_RETURN; +internal void *load_tiff_image(void *args) { + ImgThreadArgs *img_args = (ImgThreadArgs *)args; + Image *img = read_baseline_tiff(img_args->filename, img_args->arena); + if (!img) { + return NULL; } + write_debug_ppm6(img); + SDL_Surface *surface = SDL_CreateRGBSurface(0, img->width, img->height, 32, RMASK, GMASK, BMASK, AMASK); if (!surface) { - goto IMAGE_TO_TEXTURE_RETURN; + return NULL; } SDL_LockSurface(surface); @@ -170,10 +185,18 @@ internal SDL_Texture *image_to_texture(SDL_Renderer *renderer, } SDL_UnlockSurface(surface); - output = SDL_CreateTextureFromSurface(renderer, surface); - - SDL_FreeSurface(surface); - -IMAGE_TO_TEXTURE_RETURN: - return output; + return surface; +} + +internal SDL_Rect get_dest_rect(SDL_Surface *surface) { + f64 ratio = (f64)(surface->h) / (f64)(surface->w); + u64 width = surface->w <= VIEW_AREA_WIDTH ? surface->w : VIEW_AREA_WIDTH; + u64 height = width * ratio; + + return (SDL_Rect){ + .w = width, + .h = height, + .x = (WINDOW_WIDTH - width) / 2, + .y = (WINDOW_HEIGHT - height) / 2, + }; } diff --git a/src/tiff/tiffread.c b/src/tiff/tiffread.c index 747d38d..4529720 100644 --- a/src/tiff/tiffread.c +++ b/src/tiff/tiffread.c @@ -4,8 +4,10 @@ #include "image.h" #include "mem_arena.h" #include +#include #include #include +#include #include #include #include @@ -200,6 +202,7 @@ struct tiff_image { typedef struct tiff_reader TiffReader; struct tiff_reader { + const char *filename; FILE *fp; TiffHdr header; TiffIFD ifd; @@ -207,6 +210,17 @@ struct tiff_reader { 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); @@ -214,7 +228,8 @@ 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 void read_strips(TiffReader *reader); +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); @@ -231,7 +246,7 @@ Image *read_baseline_tiff(const char *file, Arena *arena) { goto READ_BASELINE_RETURN_IMG; } - TiffReader reader = {.fp = fopen(file, "rb")}; + TiffReader reader = {.filename = file, .fp = fopen(file, "rb")}; if (!reader.fp) { goto READ_BASELINE_RETURN_IMG; } @@ -534,9 +549,7 @@ internal bool read_image_pixels(TiffReader *reader, Arena *arena) { return false; } - read_strips(reader); - - return true; + return read_strips(reader); } internal bool read_strip_data(TiffReader *reader, Arena *arena) { @@ -585,42 +598,93 @@ internal bool read_strip_data(TiffReader *reader, Arena *arena) { return true; } -internal void read_strips(TiffReader *reader) { +internal bool read_strips(TiffReader *reader) { u64 position = 0; - u64 main_samples = reader->img.sample_count - reader->img.extra_samples_count; - TiffAlpha alpha = reader->img.alpha; - - Pixel *p; - u64 start_offset; - u64 alpha_offset; + StripThreadArgs args[reader->img.strip_count]; + pthread_t threads[reader->img.strip_count]; 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]), + }; - for (u64 j = 0; j < strip->byte_count / reader->img.sample_count; ++j) { - p = &(reader->pixels[position]); - start_offset = strip->offset + j * reader->img.sample_count; - alpha_offset = start_offset + main_samples + alpha.sample_offset; - - fread_with_offset(reader->fp, p, main_samples, start_offset); - - if (alpha.type == ALPHA_TYPE_UNDEFINED) { - p->a = 255; - } else { - fread_with_offset(reader->fp, &(p->a), 1, alpha_offset); - - if (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); - } + if (!args[i].fp) { + for (u64 j = 0; j < i; ++j) { + fclose(args[j].fp); } - ++position; + 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; } } + + for (u64 i = 0; i < reader->img.strip_count; ++i) { + pthread_join(threads[i], NULL); + fclose(args[i].fp); + } + + return true; +} + +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); + + 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); + } + } + + ++write_offset; + } + + return NULL; } internal void read_strip_data_field(const TiffReader *reader,