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:
parent
53c70156a6
commit
ec2835e900
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,31 +598,81 @@ 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);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (alpha.type == ALPHA_TYPE_UNDEFINED) {
|
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;
|
p->a = 255;
|
||||||
} else {
|
} else {
|
||||||
fread_with_offset(reader->fp, &(p->a), 1, alpha_offset);
|
fread_with_offset(args->fp, &(p->a), 1, alpha_offset);
|
||||||
|
|
||||||
if (alpha.type == ALPHA_TYPE_UNASSOCIATED) {
|
if (args->alpha.type == ALPHA_TYPE_UNASSOCIATED) {
|
||||||
f32 a_norm = u8_normalise(p->a);
|
f32 a_norm = u8_normalise(p->a);
|
||||||
|
|
||||||
p->r = u8_denormalise(u8_normalise(p->r) * a_norm);
|
p->r = u8_denormalise(u8_normalise(p->r) * a_norm);
|
||||||
@ -618,9 +681,10 @@ internal void read_strips(TiffReader *reader) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++position;
|
++write_offset;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void read_strip_data_field(const TiffReader *reader,
|
internal void read_strip_data_field(const TiffReader *reader,
|
||||||
|
Loading…
Reference in New Issue
Block a user