From 9142bf983285f32610e8fad18329e353cd669b5a Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Wed, 26 Jul 2023 23:01:39 +0200 Subject: [PATCH] PNG decoder now loads image asynchronously --- src/menu/menu.c | 5 +- src/menu/png_decoder.c | 185 ++++++++++++++++++++++------------ src/menu/png_decoder.h | 7 +- src/menu/views/file_info.c | 82 +++++++-------- src/menu/views/image_viewer.c | 66 ++++++------ 5 files changed, 194 insertions(+), 151 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index c69ead36..ed89194d 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -2,13 +2,14 @@ #include -#include "boot/boot.h" #include "actions.h" #include "assets.h" +#include "boot/boot.h" #include "flashcart/flashcart.h" #include "menu_state.h" #include "menu.h" #include "mp3_player.h" +#include "png_decoder.h" #include "settings.h" #include "utils/fs.h" #include "views/views.h" @@ -201,6 +202,8 @@ void menu_run (boot_params_t *boot_params) { mixer_poll(audio_buffer, audio_buffer_length); audio_write_end(); } + + png_poll(); } menu_deinit(menu); diff --git a/src/menu/png_decoder.c b/src/menu/png_decoder.c index efb0d528..27b3be64 100644 --- a/src/menu/png_decoder.c +++ b/src/menu/png_decoder.c @@ -4,105 +4,156 @@ #include "png_decoder.h" -png_err_t png_decode (char *path, surface_t *image, int max_width, int max_height) { - spng_ctx *ctx; - enum spng_errno err = SPNG_OK; - path_t *file_path; +typedef struct { FILE *file; - size_t image_size; + + spng_ctx *ctx; struct spng_ihdr ihdr; - struct spng_row_info row_info; + + surface_t *image; uint8_t *row_buffer; - uint16_t *image_buffer; - image->buffer = NULL; + png_callback_t *callback; + void *callback_data; +} png_decoder_t; - if ((ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32)) == NULL) { +static png_decoder_t *decoder; + + +static void png_decoder_deinit (bool free_image) { + if (decoder != NULL) { + if (decoder->file != NULL) { + fclose(decoder->file); + } + if (decoder->ctx != NULL) { + spng_ctx_free(decoder->ctx); + } + if ((decoder->image != NULL) && free_image) { + surface_free(decoder->image); + free(decoder->image); + } + if (decoder->row_buffer != NULL) { + free(decoder->row_buffer); + } + free(decoder); + decoder = NULL; + } +} + + +png_err_t png_decode_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) { + path_t *file_path; + size_t image_size; + + if (decoder != NULL) { + return PNG_ERR_BUSY; + } + + decoder = calloc(1, sizeof(png_decoder_t)); + if (decoder == NULL) { return PNG_ERR_OUT_OF_MEM; } - if (spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE) != SPNG_OK) { - spng_ctx_free(ctx); - return PNG_ERR_INT; - } - - if (spng_set_image_limits(ctx, max_width, max_height) != SPNG_OK) { - spng_ctx_free(ctx); - return PNG_ERR_INT; - } - file_path = path_init("sd:/"); path_append(file_path, path); - if ((file = fopen(path_get(file_path), "r")) == NULL) { - spng_ctx_free(ctx); - path_free(file_path); + decoder->file = fopen(path_get(file_path), "r"); + path_free(file_path); + if (decoder->file == NULL) { + png_decoder_deinit(false); return PNG_ERR_NO_FILE; } - path_free(file_path); - if (spng_set_png_file(ctx, file) != SPNG_OK) { - spng_ctx_free(ctx); - fclose(file); + if ((decoder->ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32)) == NULL) { + png_decoder_deinit(false); + return PNG_ERR_OUT_OF_MEM; + } + + if (spng_set_crc_action(decoder->ctx, SPNG_CRC_USE, SPNG_CRC_USE) != SPNG_OK) { + png_decoder_deinit(false); return PNG_ERR_INT; } - if (spng_decoded_image_size(ctx, SPNG_FMT_RGB8, &image_size) != SPNG_OK) { - spng_ctx_free(ctx); - fclose(file); + if (spng_set_image_limits(decoder->ctx, max_width, max_height) != SPNG_OK) { + png_decoder_deinit(false); + return PNG_ERR_INT; + } + + if (spng_set_png_file(decoder->ctx, decoder->file) != SPNG_OK) { + png_decoder_deinit(false); + return PNG_ERR_INT; + } + + if (spng_decoded_image_size(decoder->ctx, SPNG_FMT_RGB8, &image_size) != SPNG_OK) { + png_decoder_deinit(false); return PNG_ERR_BAD_FILE; } - if (spng_decode_image(ctx, NULL, image_size, SPNG_FMT_RGB8, SPNG_DECODE_PROGRESSIVE) != SPNG_OK) { - spng_ctx_free(ctx); - fclose(file); + if (spng_decode_image(decoder->ctx, NULL, image_size, SPNG_FMT_RGB8, SPNG_DECODE_PROGRESSIVE) != SPNG_OK) { + png_decoder_deinit(false); return PNG_ERR_BAD_FILE; } - if (spng_get_ihdr(ctx, &ihdr) != SPNG_OK) { - spng_ctx_free(ctx); - fclose(file); + if (spng_get_ihdr(decoder->ctx, &decoder->ihdr) != SPNG_OK) { + png_decoder_deinit(false); return PNG_ERR_BAD_FILE; } - *image = surface_alloc(FMT_RGBA16, ihdr.width, ihdr.height); - if (image->buffer == NULL) { - spng_ctx_free(ctx); - fclose(file); + decoder->image = calloc(1, sizeof(surface_t)); + if (decoder->image == NULL) { + png_decoder_deinit(false); return PNG_ERR_OUT_OF_MEM; } - if ((row_buffer = malloc(ihdr.width * 3)) == NULL) { - spng_ctx_free(ctx); - fclose(file); - surface_free(image); + *decoder->image = surface_alloc(FMT_RGBA16, decoder->ihdr.width, decoder->ihdr.height); + if (decoder->image->buffer == NULL) { + png_decoder_deinit(true); return PNG_ERR_OUT_OF_MEM; } - do { - if ((err = spng_get_row_info(ctx, &row_info)) != SPNG_OK) { - break; - } - - err = spng_decode_row(ctx, row_buffer, ihdr.width * 3); - if (err == SPNG_OK || err == SPNG_EOI) { - image_buffer = image->buffer + (row_info.row_num * image->stride); - for (int i = 0; i < ihdr.width * 3; i += 3) { - uint8_t r = row_buffer[i + 0] >> 3; - uint8_t g = row_buffer[i + 1] >> 3; - uint8_t b = row_buffer[i + 2] >> 3; - *image_buffer++ = (r << 11) | (g << 6) | (b << 1) | 1; - } - } - } while (err == SPNG_OK); - - spng_ctx_free(ctx); - free(row_buffer); - fclose(file); - - if (err != SPNG_EOI) { - surface_free(image); - return PNG_ERR_BAD_FILE; + if ((decoder->row_buffer = malloc(decoder->ihdr.width * 3)) == NULL) { + png_decoder_deinit(true); + return PNG_ERR_OUT_OF_MEM; } + decoder->callback = callback; + decoder->callback_data = callback_data; + return PNG_OK; } + +void png_decode_abort (void) { + png_decoder_deinit(true); +} + +void png_poll (void) { + if (decoder) { + enum spng_errno err; + struct spng_row_info row_info; + + if ((err = spng_get_row_info(decoder->ctx, &row_info)) != SPNG_OK) { + decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data); + png_decoder_deinit(true); + return; + } + + err = spng_decode_row(decoder->ctx, decoder->row_buffer, decoder->ihdr.width * 3); + + if (err == SPNG_OK || err == SPNG_EOI) { + uint16_t *image_buffer = decoder->image->buffer + (row_info.row_num * decoder->image->stride); + for (int i = 0; i < decoder->ihdr.width * 3; i += 3) { + uint8_t r = decoder->row_buffer[i + 0] >> 3; + uint8_t g = decoder->row_buffer[i + 1] >> 3; + uint8_t b = decoder->row_buffer[i + 2] >> 3; + *image_buffer++ = (r << 11) | (g << 6) | (b << 1) | 1; + } + } + + if (err == SPNG_EOI) { + decoder->callback(PNG_OK, decoder->image, decoder->callback_data); + png_decoder_deinit(false); + } else if (err != SPNG_OK) { + decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data); + png_decoder_deinit(true); + } + } +} diff --git a/src/menu/png_decoder.h b/src/menu/png_decoder.h index 186a4d42..13d5ef9e 100644 --- a/src/menu/png_decoder.h +++ b/src/menu/png_decoder.h @@ -15,13 +15,18 @@ typedef enum { PNG_OK, PNG_ERR_INT, + PNG_ERR_BUSY, PNG_ERR_OUT_OF_MEM, PNG_ERR_NO_FILE, PNG_ERR_BAD_FILE, } png_err_t; +typedef void png_callback_t (png_err_t err, surface_t *decoded_image, void *callback_data); -png_err_t png_decode (char *path, surface_t *image, int max_width, int max_height); + +png_err_t png_decode_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data); +void png_decode_abort (void); +void png_poll (void); #endif diff --git a/src/menu/views/file_info.c b/src/menu/views/file_info.c index d4b36ba4..3f7e44d3 100644 --- a/src/menu/views/file_info.c +++ b/src/menu/views/file_info.c @@ -9,42 +9,19 @@ #include "utils/str_utils.h" #include "views.h" + #ifndef ROM_BOXART_PATH -#define ROM_BOXART_PATH "/menu/boxart/" +#define ROM_BOXART_PATH "/menu/boxart" #endif static FILINFO info; static const char *n64_rom_extensions[] = { "z64", "n64", "v64", NULL }; static rom_header_t rom_header; + +static bool boxart_image_loading; static surface_t *boxart_image; -static rspq_block_t *cached_boxart_image_dl; -/* loads a PNG image for a given ROM ID from ROM_BOXART_PATH. e.g. sd:/menu/boxart/.png */ -static void boxart_image_load (uint16_t id) { - char sd_boxart_path[26]; - sprintf(sd_boxart_path, ROM_BOXART_PATH"%.2s.png", (char*)&(id)); - - boxart_image = calloc(1, sizeof(surface_t)); - - if (png_decode(sd_boxart_path, boxart_image, 158, 112) == PNG_OK) { - debugf("Found and decoded boxart image: %s\n", sd_boxart_path); - - // FIXME: use relative layout. - - uint16_t x = (640 - 150) - (boxart_image->width / 2); - uint16_t y = (480 - 150) - (boxart_image->height / 2); - - rspq_block_begin(); - - rdpq_set_mode_copy(false); - rdpq_tex_blit(boxart_image, x, y, NULL); - - cached_boxart_image_dl = rspq_block_end(); - } else { - debugf("Error loading boxart image\n"); - } -} static char *format_rom_endian (uint32_t endian) { switch (endian) @@ -291,15 +268,15 @@ static void menu_fileinfo_draw_n64_rom_info(surface_t *d, layout_t *layout) { text_y += fragment_textf(text_x, text_y, " Save Type: %s\n", format_rom_save_type(rom_db_match_save_type(rom_header))); text_y += fragment_textf(text_x, text_y, " Expansion PAK: %s\n\n", format_rom_memory_type(rom_db_match_expansion_pak(rom_header))); - if (boxart_image != NULL && cached_boxart_image_dl != NULL) - { - rspq_block_run(cached_boxart_image_dl); + if (boxart_image) { + uint16_t x = (640 - 150) - (boxart_image->width / 2); + uint16_t y = (480 - 150) - (boxart_image->height / 2); + rdpq_set_mode_copy(false); + rdpq_tex_blit(boxart_image, x, y, NULL); } - } static void draw (menu_t *menu, surface_t *d) { - layout_t *layout = layout_get(); const int text_x = layout->offset_x + layout->offset_text_x; @@ -317,17 +294,15 @@ static void draw (menu_t *menu, surface_t *d) { // Text start fragment_text_start(text_color); - if (file_has_extensions(info.fname, n64_rom_extensions)) { menu_fileinfo_draw_n64_rom_info(d, layout); - } - else { + } else { menu_fileinfo_draw_unknown_info(d, layout); } - /* Ensure RDP mode and loaded texture dont mess up font drawing. */ + // Ensure RDP mode and loaded texture dont mess up font drawing. fragment_text_start(text_color); - + // Actions bar text_y = layout->actions_y + layout->offset_text_y; text_y += fragment_textf(text_x, text_y, "B: Exit"); @@ -335,16 +310,27 @@ static void draw (menu_t *menu, surface_t *d) { rdpq_detach_show(); } +static void boxart_image_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { + boxart_image_loading = false; + boxart_image = decoded_image; +} -static void dl_free (void *arg) { - if (cached_boxart_image_dl != NULL) { - rspq_block_free((rspq_block_t *) (arg)); +/* loads a PNG image for a given ROM ID from ROM_BOXART_PATH. e.g. sd:/menu/boxart/.png */ +static void boxart_image_load (uint16_t id) { + char sd_boxart_path[32]; + sprintf(sd_boxart_path, "%s/%.2s.png", ROM_BOXART_PATH, (char *) (&id)); + + if (png_decode_start(sd_boxart_path, 158, 112, boxart_image_callback, NULL) != PNG_OK) { + debugf("Error loading boxart image\n"); } } static void deinit (menu_t *menu) { - if (boxart_image != NULL) { - rdpq_call_deferred(dl_free, cached_boxart_image_dl); + if (boxart_image_loading) { + png_decode_abort(); + } + + if (boxart_image) { surface_free(boxart_image); free(boxart_image); } @@ -352,24 +338,30 @@ static void deinit (menu_t *menu) { void view_file_info_init (menu_t *menu) { + boxart_image_loading = true; boxart_image = NULL; - cached_boxart_image_dl = NULL; + + char *file_name = menu->browser.list[menu->browser.selected].name; path_t *file = path_clone(menu->browser.directory); - path_push(file, menu->browser.list[menu->browser.selected].name); + path_push(file, file_name); + if (f_stat(path_get(file), &info) != FR_OK) { menu->next_mode = MENU_MODE_ERROR; } - if (file_has_extensions(info.fname, n64_rom_extensions)) { + + if (file_has_extensions(file_name, n64_rom_extensions)) { rom_header = file_read_rom_header(path_get(file)); boxart_image_load(rom_header.metadata.unique_identifier); } + path_free(file); } void view_file_info_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); if (menu->next_mode != MENU_MODE_FILE_INFO) { diff --git a/src/menu/views/image_viewer.c b/src/menu/views/image_viewer.c index 77ec99e0..c5de324b 100644 --- a/src/menu/views/image_viewer.c +++ b/src/menu/views/image_viewer.c @@ -6,8 +6,8 @@ #include "views.h" +static bool image_loading; static surface_t *image; -static rspq_block_t *cached_image_dl; static void process (menu_t *menu) { @@ -19,50 +19,35 @@ static void process (menu_t *menu) { static void draw (menu_t *menu, surface_t *d) { rdpq_attach_clear(d, NULL); - if (image == NULL) { + if (!image) { fragment_loader(d); } else { - rspq_block_run(cached_image_dl); + uint16_t x = (d->width / 2) - (image->width / 2); + uint16_t y = (d->height / 2) - (image->height / 2); + + rdpq_set_mode_copy(false); + rdpq_tex_blit(image, x, y, NULL); } rdpq_detach_show(); } -static void deffered_image_load (menu_t *menu, surface_t *d) { - image = calloc(1, sizeof(surface_t)); +static void image_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { + image_loading = false; + image = decoded_image; - if (image == NULL) { - menu->next_mode = MENU_MODE_ERROR; - return; - } - - path_t *path = path_clone(menu->browser.directory); - path_push(path, menu->browser.list[menu->browser.selected].name); - - if (png_decode(path_get(path), image, 640, 480) == PNG_OK) { - uint16_t x = (d->width / 2) - (image->width / 2); - uint16_t y = (d->height / 2) - (image->height / 2); - - rspq_block_begin(); - - rdpq_set_mode_copy(false); - rdpq_tex_blit(image, x, y, NULL); - - cached_image_dl = rspq_block_end(); - } else { + if (err != PNG_OK) { + menu_t *menu = (menu_t *) (callback_data); menu->next_mode = MENU_MODE_ERROR; } - - path_free(path); -} - -static void dl_free (void *arg) { - rspq_block_free((rspq_block_t *) (arg)); } static void deinit (menu_t *menu) { - if (image != NULL) { - rdpq_call_deferred(dl_free, cached_image_dl); + if (image_loading) { + png_decode_abort(); + } + + if (image) { surface_free(image); free(image); } @@ -70,8 +55,19 @@ static void deinit (menu_t *menu) { void view_image_viewer_init (menu_t *menu) { + image_loading = false; image = NULL; - cached_image_dl = NULL; + + path_t *path = path_clone(menu->browser.directory); + path_push(path, menu->browser.list[menu->browser.selected].name); + + if (png_decode_start(path_get(path), 640, 480, image_callback, menu) == PNG_OK) { + image_loading = true; + } else { + menu->next_mode = MENU_MODE_ERROR; + } + + path_free(path); } void view_image_viewer_display (menu_t *menu, surface_t *display) { @@ -79,10 +75,6 @@ void view_image_viewer_display (menu_t *menu, surface_t *display) { draw(menu, display); - if (image == NULL) { - deffered_image_load(menu, display); - } - if (menu->next_mode != MENU_MODE_IMAGE_VIEWER) { deinit(menu); }