Read the image in a separate thread and ensure each strip is read in its own thread (#1)
Reviewed-on: #1 Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com> Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
This commit is contained in:
		
							
								
								
									
										1
									
								
								compile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								compile
									
									
									
									
									
								
							| @@ -9,6 +9,7 @@ INCLUDES="\ | |||||||
| " | " | ||||||
| LIBS="\ | LIBS="\ | ||||||
| 	-lm \ | 	-lm \ | ||||||
|  | 	-pthread \ | ||||||
| 	$(pkg-config --libs sdl2) \ | 	$(pkg-config --libs sdl2) \ | ||||||
| " | " | ||||||
| SRC="\ | SRC="\ | ||||||
|   | |||||||
							
								
								
									
										75
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								src/main.c
									
									
									
									
									
								
							| @@ -4,10 +4,12 @@ | |||||||
| #include "tiffread.h" | #include "tiffread.h" | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
| #include <SDL2/SDL_events.h> | #include <SDL2/SDL_events.h> | ||||||
|  | #include <SDL2/SDL_rect.h> | ||||||
| #include <SDL2/SDL_render.h> | #include <SDL2/SDL_render.h> | ||||||
| #include <SDL2/SDL_surface.h> | #include <SDL2/SDL_surface.h> | ||||||
| #include <SDL2/SDL_video.h> | #include <SDL2/SDL_video.h> | ||||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||||
|  | #include <pthread.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| @@ -17,6 +19,8 @@ | |||||||
|  |  | ||||||
| #define WINDOW_WIDTH 800 | #define WINDOW_WIDTH 800 | ||||||
| #define WINDOW_HEIGHT 600 | #define WINDOW_HEIGHT 600 | ||||||
|  | #define VIEW_AREA_WIDTH 700 | ||||||
|  | #define VIEW_AREA_HEIGHT 500 | ||||||
|  |  | ||||||
| #define AMASK 0xff000000 | #define AMASK 0xff000000 | ||||||
| #define BMASK 0x00ff0000 | #define BMASK 0x00ff0000 | ||||||
| @@ -32,8 +36,14 @@ struct point { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| internal void write_debug_ppm6(const Image *img); | internal void write_debug_ppm6(const Image *img); | ||||||
| internal SDL_Texture *image_to_texture(SDL_Renderer *renderer, | internal void *load_tiff_image(void *args); | ||||||
|                                        const Image *img); | 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 main(int argc, char *argv[]) { | ||||||
|   int exit_code = EXIT_SUCCESS; |   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"; |   const char *file_to_open = argc > 1 ? argv[1] : "./resources/test.tif"; | ||||||
|  |  | ||||||
|   Image *img = read_baseline_tiff(file_to_open, arena); |   pthread_t img_thread; | ||||||
|   if (!img) { |   ImgThreadArgs args = {.filename = file_to_open, .arena = arena}; | ||||||
|  |   if (pthread_create(&img_thread, NULL, load_tiff_image, &args) != 0) { | ||||||
|     exit_code = EXIT_FAILURE; |     exit_code = EXIT_FAILURE; | ||||||
|     goto MAIN_DESTROY_ARENA; |     goto MAIN_DESTROY_ARENA; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   write_debug_ppm6(img); |  | ||||||
|  |  | ||||||
|   if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { |   if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { | ||||||
|     exit_code = EXIT_FAILURE; |     exit_code = EXIT_FAILURE; | ||||||
|     goto MAIN_DESTROY_ARENA; |     goto MAIN_DESTROY_ARENA; | ||||||
| @@ -73,18 +82,19 @@ int main(int argc, char *argv[]) { | |||||||
|     goto MAIN_DESTROY_WINDOW; |     goto MAIN_DESTROY_WINDOW; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SDL_Texture *texture = image_to_texture(renderer, img); |   SDL_Surface *surface = NULL; | ||||||
|   if (!texture) { |   if (pthread_join(img_thread, (void **)&surface) != 0 || !surface) { | ||||||
|     exit_code = EXIT_FAILURE; |     exit_code = EXIT_FAILURE; | ||||||
|     goto MAIN_DESTROY_RENDERER; |     goto MAIN_DESTROY_RENDERER; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SDL_Rect dest = { |   SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); | ||||||
|       .h = img->height, |   if (!texture) { | ||||||
|       .w = img->width, |     exit_code = EXIT_FAILURE; | ||||||
|       .x = (WINDOW_WIDTH - img->width) / 2, |     goto MAIN_DESTROY_SURFACE; | ||||||
|       .y = (WINDOW_HEIGHT - img->height) / 2, |   } | ||||||
|   }; |  | ||||||
|  |   SDL_Rect dest = get_dest_rect(surface); | ||||||
|  |  | ||||||
|   bool running = true; |   bool running = true; | ||||||
|  |  | ||||||
| @@ -110,6 +120,9 @@ int main(int argc, char *argv[]) { | |||||||
|  |  | ||||||
|   SDL_DestroyTexture(texture); |   SDL_DestroyTexture(texture); | ||||||
|  |  | ||||||
|  | MAIN_DESTROY_SURFACE: | ||||||
|  |   SDL_FreeSurface(surface); | ||||||
|  |  | ||||||
| MAIN_DESTROY_RENDERER: | MAIN_DESTROY_RENDERER: | ||||||
|   SDL_DestroyRenderer(renderer); |   SDL_DestroyRenderer(renderer); | ||||||
|  |  | ||||||
| @@ -146,17 +159,19 @@ internal void write_debug_ppm6(const Image *img) { | |||||||
|   fclose(out); |   fclose(out); | ||||||
| } | } | ||||||
|  |  | ||||||
| internal SDL_Texture *image_to_texture(SDL_Renderer *renderer, | internal void *load_tiff_image(void *args) { | ||||||
|                                        const Image *img) { |   ImgThreadArgs *img_args = (ImgThreadArgs *)args; | ||||||
|   SDL_Texture *output = NULL; |   Image *img = read_baseline_tiff(img_args->filename, img_args->arena); | ||||||
|   if (!renderer || !img) { |   if (!img) { | ||||||
|     goto IMAGE_TO_TEXTURE_RETURN; |     return NULL; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   write_debug_ppm6(img); | ||||||
|  |  | ||||||
|   SDL_Surface *surface = SDL_CreateRGBSurface(0, img->width, img->height, 32, |   SDL_Surface *surface = SDL_CreateRGBSurface(0, img->width, img->height, 32, | ||||||
|                                               RMASK, GMASK, BMASK, AMASK); |                                               RMASK, GMASK, BMASK, AMASK); | ||||||
|   if (!surface) { |   if (!surface) { | ||||||
|     goto IMAGE_TO_TEXTURE_RETURN; |     return NULL; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SDL_LockSurface(surface); |   SDL_LockSurface(surface); | ||||||
| @@ -170,10 +185,18 @@ internal SDL_Texture *image_to_texture(SDL_Renderer *renderer, | |||||||
|   } |   } | ||||||
|   SDL_UnlockSurface(surface); |   SDL_UnlockSurface(surface); | ||||||
|  |  | ||||||
|   output = SDL_CreateTextureFromSurface(renderer, surface); |   return surface; | ||||||
|  | } | ||||||
|   SDL_FreeSurface(surface); |  | ||||||
|  | internal SDL_Rect get_dest_rect(SDL_Surface *surface) { | ||||||
| IMAGE_TO_TEXTURE_RETURN: |   f64 ratio = (f64)(surface->h) / (f64)(surface->w); | ||||||
|   return output; |   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, | ||||||
|  |   }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,8 +4,10 @@ | |||||||
| #include "image.h" | #include "image.h" | ||||||
| #include "mem_arena.h" | #include "mem_arena.h" | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  | #include <bits/pthreadtypes.h> | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||||
|  | #include <pthread.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| @@ -200,6 +202,7 @@ struct tiff_image { | |||||||
|  |  | ||||||
| typedef struct tiff_reader TiffReader; | typedef struct tiff_reader TiffReader; | ||||||
| struct tiff_reader { | struct tiff_reader { | ||||||
|  |   const char *filename; | ||||||
|   FILE *fp; |   FILE *fp; | ||||||
|   TiffHdr header; |   TiffHdr header; | ||||||
|   TiffIFD ifd; |   TiffIFD ifd; | ||||||
| @@ -207,6 +210,17 @@ struct tiff_reader { | |||||||
|   Pixel *pixels; |   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_tiff_header(TiffReader *reader); | ||||||
| internal bool read_ifd(TiffReader *reader, Arena *arena); | internal bool read_ifd(TiffReader *reader, Arena *arena); | ||||||
| internal bool read_ifd_fields(TiffReader *reader); | 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 void read_bits_per_sample(TiffReader *reader); | ||||||
| internal bool read_image_pixels(TiffReader *reader, Arena *arena); | internal bool read_image_pixels(TiffReader *reader, Arena *arena); | ||||||
| internal bool read_strip_data(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, | internal void read_strip_data_field(const TiffReader *reader, | ||||||
|                                     StripDataField *field); |                                     StripDataField *field); | ||||||
| internal bool read_field(const TiffField *field, TiffImage *img); | 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; |     goto READ_BASELINE_RETURN_IMG; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   TiffReader reader = {.fp = fopen(file, "rb")}; |   TiffReader reader = {.filename = file, .fp = fopen(file, "rb")}; | ||||||
|   if (!reader.fp) { |   if (!reader.fp) { | ||||||
|     goto READ_BASELINE_RETURN_IMG; |     goto READ_BASELINE_RETURN_IMG; | ||||||
|   } |   } | ||||||
| @@ -534,9 +549,7 @@ internal bool read_image_pixels(TiffReader *reader, Arena *arena) { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   read_strips(reader); |   return read_strips(reader); | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| internal bool read_strip_data(TiffReader *reader, Arena *arena) { | internal bool read_strip_data(TiffReader *reader, Arena *arena) { | ||||||
| @@ -585,42 +598,93 @@ internal bool read_strip_data(TiffReader *reader, Arena *arena) { | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| internal void read_strips(TiffReader *reader) { | internal bool read_strips(TiffReader *reader) { | ||||||
|   u64 position = 0; |   u64 position = 0; | ||||||
|   u64 main_samples = reader->img.sample_count - reader->img.extra_samples_count; |   StripThreadArgs args[reader->img.strip_count]; | ||||||
|   TiffAlpha alpha = reader->img.alpha; |   pthread_t threads[reader->img.strip_count]; | ||||||
|  |  | ||||||
|   Pixel *p; |  | ||||||
|   u64 start_offset; |  | ||||||
|   u64 alpha_offset; |  | ||||||
|  |  | ||||||
|   for (u64 i = 0; i < reader->img.strip_count; ++i) { |   for (u64 i = 0; i < reader->img.strip_count; ++i) { | ||||||
|     const TiffStrip *strip = &(reader->img.strips[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) { |     if (!args[i].fp) { | ||||||
|       p = &(reader->pixels[position]); |       for (u64 j = 0; j < i; ++j) { | ||||||
|       start_offset = strip->offset + j * reader->img.sample_count; |         fclose(args[j].fp); | ||||||
|       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; |       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, | internal void read_strip_data_field(const TiffReader *reader, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user