mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-21 18:19:19 +01:00
PNG decoder now loads image asynchronously
This commit is contained in:
parent
df6d9265a0
commit
9142bf9832
@ -2,13 +2,14 @@
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
#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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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/<id>.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/<id>.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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user