Skip to content

Commit

Permalink
PNGDEC: Clean up API, add get_palette and index copy.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gadgetoid committed Jul 21, 2023
1 parent 6db7a9a commit 5ec6903
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 34 deletions.
11 changes: 6 additions & 5 deletions micropython/modules/pngdec/pngdec.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(PNG_openFILE_obj, _PNG_openFILE);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(PNG_decode_obj, 1, _PNG_decode);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(PNG_getWidth_obj, _PNG_getWidth);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(PNG_getHeight_obj, _PNG_getHeight);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(PNG_getPalette_obj, _PNG_getPalette);

// class
STATIC const mp_rom_map_elem_t PNG_locals_dict_table[] = {
Expand All @@ -15,7 +16,7 @@ STATIC const mp_rom_map_elem_t PNG_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&PNG_decode_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&PNG_getWidth_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&PNG_getHeight_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&PNG_getHeight_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_palette), MP_ROM_PTR(&PNG_getPalette_obj) },
};

STATIC MP_DEFINE_CONST_DICT(PNG_locals_dict, PNG_locals_dict_table);
Expand Down Expand Up @@ -44,10 +45,10 @@ STATIC const mp_map_elem_t PNG_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_pngdec) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_PNG), (mp_obj_t)&PNG_type },

{ MP_ROM_QSTR(MP_QSTR_PNG_SCALE_FULL), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_PNG_SCALE_HALF), MP_ROM_INT(2) },
{ MP_ROM_QSTR(MP_QSTR_PNG_SCALE_QUARTER), MP_ROM_INT(4) },
{ MP_ROM_QSTR(MP_QSTR_PNG_SCALE_EIGHTH), MP_ROM_INT(8) },
{ MP_ROM_QSTR(MP_QSTR_PNG_NORMAL), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_PNG_POSTERISE), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_PNG_DITHER), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_PNG_COPY), MP_ROM_INT(2) },
};

STATIC MP_DEFINE_CONST_DICT(mp_module_PNG_globals, PNG_globals_table);
Expand Down
107 changes: 78 additions & 29 deletions micropython/modules/pngdec/pngdec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ typedef struct _ModPicoGraphics_obj_t {

typedef struct _PNG_decode_target {
void *target;
uint8_t flags = 0;
uint8_t mode = 0;
Point position = {0, 0};
Rect source = {0, 0, 0, 0};
Point scale = {1, 1};
Expand All @@ -39,8 +39,10 @@ typedef struct _PNG_obj_t {
int height;
} _PNG_obj_t;

enum FLAGS : uint8_t {
FLAG_NO_DITHER = 1u
enum DECODE_MODE : uint8_t {
MODE_POSTERIZE = 0u,
MODE_DITHER = 1u,
MODE_COPY = 2u,
};

void *pngdec_open_callback(const char *filename, int32_t *size) {
Expand Down Expand Up @@ -121,7 +123,7 @@ MICROPY_EVENT_POLL_HOOK
_PNG_decode_target *target = (_PNG_decode_target*)pDraw->pUser;
PicoGraphics *current_graphics = (PicoGraphics *)target->target;
Point current_position = target->position;
uint8_t current_flags = target->flags;
uint8_t current_mode = target->mode;
Point scale = target->scale;
// "pixel" is slow and clipped,
// guaranteeing we wont draw png data out of the framebuffer..
Expand Down Expand Up @@ -175,7 +177,7 @@ MICROPY_EVENT_POLL_HOOK
uint8_t a = pDraw->iHasAlpha ? pDraw->pPalette[768 + i] : 1;
if (a) {
if (current_graphics->pen_type == PicoGraphics::PEN_RGB332) {
if (current_flags & FLAG_NO_DITHER) {
if (current_mode == MODE_POSTERIZE || current_mode == MODE_COPY) {
// Posterized output to RGB332
current_graphics->set_pen(RGB(r, g, b).to_rgb332());
current_graphics->rectangle({current_position.x, current_position.y, scale.x, scale.y});
Expand All @@ -192,7 +194,12 @@ MICROPY_EVENT_POLL_HOOK
|| current_graphics->pen_type == PicoGraphics::PEN_3BIT
|| current_graphics->pen_type == PicoGraphics::PEN_INKY7) {

if(current_flags & FLAG_NO_DITHER) {
// Copy raw palette indexes over
if(current_mode == MODE_COPY) {
current_graphics->set_pen(i);
current_graphics->rectangle({current_position.x, current_position.y, scale.x, scale.y});
// Posterized output to the available palete
} else if(current_mode == MODE_POSTERIZE) {
int closest = RGB(r, g, b).closest(current_graphics->get_palette(), current_graphics->get_palette_size());
if (closest == -1) {
closest = 0;
Expand Down Expand Up @@ -285,32 +292,58 @@ mp_obj_t _PNG_openRAM(mp_obj_t self_in, mp_obj_t buffer) {

// decode
mp_obj_t _PNG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_scale, ARG_dither,
ARG_source_x, ARG_source_y, ARG_source_w, ARG_source_h,
ARG_scale_x, ARG_scale_y };
enum { ARG_self, ARG_x, ARG_y, ARG_scale, ARG_mode, ARG_source };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_y, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_dither, MP_ARG_OBJ, {.u_obj = mp_const_true} },
{ MP_QSTR_source_x, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_source_y, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_source_w, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_source_h, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_scale_x, MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_scale_y, MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_mode, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_source, MP_ARG_OBJ, {.u_obj = nullptr} },
};

mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

_PNG_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _PNG_obj_t);

// TODO: Implement integer 1x/2x/3x scaling by repeating pixels?
//int f = args[ARG_scale].u_int;
if(mp_obj_is_type(args[ARG_source].u_obj, &mp_type_tuple)){
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(args[ARG_source].u_obj, mp_obj_tuple_t);

if(tuple->len != 4) mp_raise_ValueError("decode(): source tuple must contain (x, y, w, h)");

self->decode_target->source = {
mp_obj_get_int(tuple->items[0]),
mp_obj_get_int(tuple->items[1]),
mp_obj_get_int(tuple->items[2]),
mp_obj_get_int(tuple->items[3])
};
} else {
self->decode_target->source = {0, 0, self->width, self->height};
}

// Scale is a single int, corresponds to both width/height
if (mp_obj_is_int(args[ARG_scale].u_obj)) {
self->decode_target->scale = {
mp_obj_get_int(args[ARG_scale].u_obj),
mp_obj_get_int(args[ARG_scale].u_obj)
};
// Scale is a tuple, separate scales for width/height
} else if(mp_obj_is_type(args[ARG_scale].u_obj, &mp_type_tuple)){
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(args[ARG_scale].u_obj, mp_obj_tuple_t);

if(tuple->len != 2) mp_raise_ValueError("decode(): scale tuple must contain (scale_x, scale_y)");

self->decode_target->scale = {
mp_obj_get_int(tuple->items[0]),
mp_obj_get_int(tuple->items[1])
};
// Something else, just roll with the default
} else {
self->decode_target->scale = {1, 1};
}

self->decode_target->flags = args[ARG_dither].u_obj == mp_const_false ? FLAG_NO_DITHER : 0;
self->decode_target->mode = args[ARG_mode].u_int;

self->decode_target->position = {args[ARG_x].u_int, args[ARG_y].u_int};

Expand All @@ -321,15 +354,6 @@ mp_obj_t _PNG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)

pngdec_open_helper(self);

int source_x = args[ARG_source_x].u_int;
int source_y = args[ARG_source_y].u_int;
int source_w = args[ARG_source_w].u_int == -1 ? self->width : args[ARG_source_w].u_int;
int source_h = args[ARG_source_h].u_int == -1 ? self->height : args[ARG_source_h].u_int;

self->decode_target->source = {source_x, source_y, source_w, source_h};

self->decode_target->scale = {args[ARG_scale_x].u_int, args[ARG_scale_y].u_int};

result = self->png->decode(self->decode_target, 0);

// Close the file since we've opened it on-demand
Expand All @@ -350,4 +374,29 @@ mp_obj_t _PNG_getHeight(mp_obj_t self_in) {
return mp_obj_new_int(self->height);
}

// get_height
mp_obj_t _PNG_getPalette(mp_obj_t self_in) {
_PNG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _PNG_obj_t);
pngdec_open_helper(self);

self->png->decode(nullptr, 0);

uint8_t *palette = self->png->getPalette();

mp_obj_t palette_out[256];

for(auto i = 0u; i < 256; i++) {
mp_obj_t entry[3] = {
mp_obj_new_int(*palette++),
mp_obj_new_int(*palette++),
mp_obj_new_int(*palette++)
};
palette_out[i] = mp_obj_new_tuple(3, entry);
}

self->png->close();

return mp_obj_new_list(256, palette_out);
}

}
1 change: 1 addition & 0 deletions micropython/modules/pngdec/pngdec.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ extern mp_obj_t _PNG_openFILE(mp_obj_t self_in, mp_obj_t filename);
extern mp_obj_t _PNG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t _PNG_getWidth(mp_obj_t self_in);
extern mp_obj_t _PNG_getHeight(mp_obj_t self_in);
extern mp_obj_t _PNG_getPalette(mp_obj_t self_in);

extern void *pngdec_open_callback(const char *filename, int32_t *size);

0 comments on commit 5ec6903

Please sign in to comment.