From dde1cce8e41d6410bb7ca15a2aa4e889f3dbedef Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 12 May 2024 20:00:09 +0100 Subject: [PATCH 1/4] Load the image in a separate thread --- compile | 1 + src/main.c | 59 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 24 deletions(-) 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..f625743 100644 --- a/src/main.c +++ b/src/main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -32,8 +33,13 @@ 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); + +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 +51,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,17 +78,23 @@ 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_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); + if (!texture) { + exit_code = EXIT_FAILURE; + goto MAIN_DESTROY_SURFACE; + } + SDL_Rect dest = { - .h = img->height, - .w = img->width, - .x = (WINDOW_WIDTH - img->width) / 2, - .y = (WINDOW_HEIGHT - img->height) / 2, + .h = surface->h, + .w = surface->w, + .x = (WINDOW_WIDTH - surface->w) / 2, + .y = (WINDOW_HEIGHT - surface->h) / 2, }; bool running = true; @@ -110,6 +121,9 @@ int main(int argc, char *argv[]) { SDL_DestroyTexture(texture); +MAIN_DESTROY_SURFACE: + SDL_FreeSurface(surface); + MAIN_DESTROY_RENDERER: SDL_DestroyRenderer(renderer); @@ -146,17 +160,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 +186,5 @@ 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; } -- 2.39.5 From 19a87c5c4f59bac98ed58133189a6a491e049084 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 26 May 2024 14:49:29 +0100 Subject: [PATCH 2/4] Read each strip in a separate thread --- src/tiff/tiffread.c | 177 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 143 insertions(+), 34 deletions(-) diff --git a/src/tiff/tiffread.c b/src/tiff/tiffread.c index 747d38d..1f9473a 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; @@ -214,7 +217,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 +235,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 +538,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 +587,149 @@ internal bool read_strip_data(TiffReader *reader, Arena *arena) { return true; } -internal void read_strips(TiffReader *reader) { - u64 position = 0; - u64 main_samples = reader->img.sample_count - reader->img.extra_samples_count; - TiffAlpha alpha = reader->img.alpha; +// internal void 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; +// Pixel *p; +// u64 start_offset; +// u64 alpha_offset; + +// for (u64 i = 0; i < reader->img.strip_count; ++i) { +// const TiffStrip *strip = &(reader->img.strips[i]); + +// 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); +// } +// } + +// ++position; +// } +// } +// } + +typedef struct strip_reader_args StripReaderArgs; +struct strip_reader_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_strips(TiffReader *reader) { + bool output = true; + + u64 position = 0; + u64 strips_read = 0; + u64 threads_created = 0; + StripReaderArgs 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] = (StripReaderArgs){ + .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); - } - } - - ++position; + if (!args[i].fp) { + output = false; + goto READ_STRIPS_CLOSE_FILES; } + + position += pixel_count; + ++strips_read; } + + for (u64 i = 0; i < reader->img.strip_count; ++i) { + if (pthread_create(&(threads[i]), NULL, read_strip, (void *)&(args[i])) != + 0) { + break; + } + + ++threads_created; + } + + if (threads_created < reader->img.strip_count) { + for (u64 i = 0; i < strips_read; ++i) { + pthread_cancel(threads[i]); + } + + return false; + } + + for (u64 i = 0; i < reader->img.strip_count; ++i) { + pthread_join(threads[i], NULL); + } + +READ_STRIPS_CLOSE_FILES: + for (u64 i = 0; i < strips_read; ++i) { + fclose(args[i].fp); + } + + return output; +} + +internal void *read_strip(void *arg) { + StripReaderArgs *args = (StripReaderArgs *)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, -- 2.39.5 From c564cabc6610d8358afe05ecf497d69fc561672b Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 26 May 2024 15:09:16 +0100 Subject: [PATCH 3/4] Reorganise tiffread.c and reduce number of loops in read_strips --- src/tiff/tiffread.c | 109 +++++++++++++------------------------------- 1 file changed, 32 insertions(+), 77 deletions(-) diff --git a/src/tiff/tiffread.c b/src/tiff/tiffread.c index 1f9473a..4529720 100644 --- a/src/tiff/tiffread.c +++ b/src/tiff/tiffread.c @@ -210,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); @@ -587,68 +598,15 @@ internal bool read_strip_data(TiffReader *reader, Arena *arena) { return true; } -// internal void 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; - -// for (u64 i = 0; i < reader->img.strip_count; ++i) { -// const TiffStrip *strip = &(reader->img.strips[i]); - -// 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); -// } -// } - -// ++position; -// } -// } -// } - -typedef struct strip_reader_args StripReaderArgs; -struct strip_reader_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_strips(TiffReader *reader) { - bool output = true; - u64 position = 0; - u64 strips_read = 0; - u64 threads_created = 0; - StripReaderArgs args[reader->img.strip_count]; + 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] = (StripReaderArgs){ + args[i] = (StripThreadArgs){ .fp = fopen(reader->filename, "rb"), .strip = strip, .pixel_count = pixel_count, @@ -660,45 +618,42 @@ internal bool read_strips(TiffReader *reader) { }; if (!args[i].fp) { - output = false; - goto READ_STRIPS_CLOSE_FILES; + for (u64 j = 0; j < i; ++j) { + fclose(args[j].fp); + } + + return false; } position += pixel_count; - ++strips_read; } for (u64 i = 0; i < reader->img.strip_count; ++i) { - if (pthread_create(&(threads[i]), NULL, read_strip, (void *)&(args[i])) != - 0) { - break; + 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; } - - ++threads_created; - } - - if (threads_created < reader->img.strip_count) { - for (u64 i = 0; i < strips_read; ++i) { - pthread_cancel(threads[i]); - } - - return false; } for (u64 i = 0; i < reader->img.strip_count; ++i) { pthread_join(threads[i], NULL); - } - -READ_STRIPS_CLOSE_FILES: - for (u64 i = 0; i < strips_read; ++i) { fclose(args[i].fp); } - return output; + return true; } internal void *read_strip(void *arg) { - StripReaderArgs *args = (StripReaderArgs *)arg; + StripThreadArgs *args = (StripThreadArgs *)arg; Pixel *p; u64 write_offset = 0; -- 2.39.5 From 373c27572ec6eba668630517db63703305c225e5 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 26 May 2024 15:40:06 +0100 Subject: [PATCH 4/4] Add viewing area and ensure all images are displayed within it --- src/main.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index f625743..5b2e9ff 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include "tiffread.h" #include #include +#include #include #include #include @@ -18,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 @@ -34,6 +37,7 @@ struct point { internal void write_debug_ppm6(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 { @@ -90,12 +94,7 @@ int main(int argc, char *argv[]) { goto MAIN_DESTROY_SURFACE; } - SDL_Rect dest = { - .h = surface->h, - .w = surface->w, - .x = (WINDOW_WIDTH - surface->w) / 2, - .y = (WINDOW_HEIGHT - surface->h) / 2, - }; + SDL_Rect dest = get_dest_rect(surface); bool running = true; @@ -188,3 +187,16 @@ internal void *load_tiff_image(void *args) { 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, + }; +} -- 2.39.5