Skip to content

Commit

Permalink
PNG decoder now loads image asynchronously
Browse files Browse the repository at this point in the history
  • Loading branch information
Polprzewodnikowy committed Jul 26, 2023
1 parent df6d926 commit 9142bf9
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 142 deletions.
5 changes: 4 additions & 1 deletion src/menu/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down
169 changes: 110 additions & 59 deletions src/menu/png_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
return PNG_ERR_OUT_OF_MEM;
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;
}
}

if (spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE) != SPNG_OK) {
spng_ctx_free(ctx);
return PNG_ERR_INT;

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;
}

if (spng_set_image_limits(ctx, max_width, max_height) != SPNG_OK) {
spng_ctx_free(ctx);
return PNG_ERR_INT;
decoder = calloc(1, sizeof(png_decoder_t));
if (decoder == NULL) {
return PNG_ERR_OUT_OF_MEM;
}

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_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(ctx, SPNG_FMT_RGB8, &image_size) != SPNG_OK) {
spng_ctx_free(ctx);
fclose(file);
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;
}

*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;
}

if ((row_buffer = malloc(ihdr.width * 3)) == NULL) {
spng_ctx_free(ctx);
fclose(file);
surface_free(image);
if ((decoder->row_buffer = malloc(decoder->ihdr.width * 3)) == NULL) {
png_decoder_deinit(true);
return PNG_ERR_OUT_OF_MEM;
}

do {
if ((err = spng_get_row_info(ctx, &row_info)) != SPNG_OK) {
break;
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(ctx, row_buffer, ihdr.width * 3);
err = spng_decode_row(decoder->ctx, decoder->row_buffer, decoder->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;
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;
}
}
} 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 (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);
}
}

return PNG_OK;
}
7 changes: 6 additions & 1 deletion src/menu/png_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 9142bf9

Please sign in to comment.