PythonExtra/ports/sh/objgintimage.c

373 lines
13 KiB
C

//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "objgintimage.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include <string.h>
STATIC mp_obj_t ptr_to_memoryview(void *ptr, int size, int typecode, bool rw)
{
if(ptr == NULL)
return mp_const_none;
if(rw)
typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
return mp_obj_new_memoryview(typecode, size, ptr);
}
#ifdef FX9860G
/* Heuristic to check if the image is read-only or not */
STATIC bool pointer_is_ro(void *data)
{
uintptr_t addr = (uintptr_t)data;
return !addr || (addr >= 0x00300000 && addr <= 0x00500000);
}
STATIC int image_data_size(int profile, int width, int height)
{
int layers = image_layer_count(profile);
int longwords = (width + 31) >> 5;
return layers * longwords * height * 4;
}
/* gint.image(profile, width, height, data)
Keyword labels are allowed but the order must remain the same. */
STATIC mp_obj_t image_make_new(const mp_obj_type_t *type, size_t n_args,
size_t n_kw, const mp_obj_t *args)
{
enum { ARG_profile, ARG_width, ARG_height, ARG_data };
static mp_arg_t const allowed_args[] = {
{ MP_QSTR_profile, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args),
allowed_args, vals);
int profile = vals[ARG_profile].u_int;
int width = vals[ARG_width].u_int;
int height = vals[ARG_height].u_int;
mp_obj_t data = vals[ARG_data].u_obj;
return objgintimage_make(type, profile, width, height, data);
}
mp_obj_t objgintimage_make(const mp_obj_type_t *type, int profile, int width,
int height, mp_obj_t data)
{
/* The supplied object must implement the buffer protocol */
mp_buffer_info_t buf;
if(!mp_get_buffer(data, &buf, MP_BUFFER_READ))
mp_raise_TypeError("data must be a buffer object");
/* Size and bounds checks */
int data_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(data));
if(width <= 0 || height <= 0)
mp_raise_ValueError("image width/height must be >0");
if(profile < 0 || profile >= 8)
mp_raise_ValueError("invalid image profile");
if(data_len < image_data_size(profile, width, height))
mp_raise_ValueError("data len() should be >= 4*ceil(w/32)*h*layers");
/* Construct image! */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t, type);
self->img.profile = profile;
self->img.width = width;
self->img.height = height;
self->img.data = NULL;
self->data = data;
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t objgintimage_make_from_gint_image(bopti_image_t const *img)
{
/* The original image is assumed to be valid. */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t,
&mp_type_gintimage);
memcpy(&self->img, img, sizeof *img);
int data_size = image_data_size(img->profile, img->width, img->height);
bool rw = !pointer_is_ro(img->data);
self->data = ptr_to_memoryview(img->data, data_size, 'B', rw);
return MP_OBJ_FROM_PTR(self);
}
STATIC void image_print(mp_print_t const *print, mp_obj_t self_in,
mp_print_kind_t kind)
{
(void)kind;
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
char const *data_str =
self->data != mp_const_none ? "py" :
pointer_is_ro(self->img.data) ? "ro" : "rw";
static char const * const fmt_names[] = {
"mono", "mono_alpha", "gray", "gray_alpha"
};
char const *format_str =
(self->img.profile < 4) ? fmt_names[self->img.profile] : "?";
mp_printf(print, "<%s image (%d layers), %dx%d (%s, %d bytes)>",
format_str, image_layer_count(self->img.profile), self->img.width,
self->img.height, data_str,
image_data_size(self->img.profile, self->img.width, self->img.height));
}
STATIC void image_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest)
{
if(dest[0] == MP_OBJ_NULL) {
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
if(attr == MP_QSTR_format)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.profile);
else if(attr == MP_QSTR_width)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.width);
else if(attr == MP_QSTR_height)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.height);
else if(attr == MP_QSTR_data)
dest[0] = self->data;
}
else {
mp_raise_msg(&mp_type_AttributeError,
MP_ERROR_TEXT("gint.image doesn't support changing attributes"));
}
}
#endif /* FX9860G */
#ifdef FXCG50
/* gint.image(format, color_count, width, height, stride, data, palette)
Keyword labels are allowed but the order must remain the same. */
STATIC mp_obj_t image_make_new(const mp_obj_type_t *type, size_t n_args,
size_t n_kw, const mp_obj_t *args)
{
enum { ARG_format, ARG_color_count, ARG_width, ARG_height, ARG_stride,
ARG_data, ARG_palette };
static mp_arg_t const allowed_args[] = {
{ MP_QSTR_format, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_color_count, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_stride, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_palette, MP_ARG_OBJ,
{.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args),
allowed_args, vals);
int format = vals[ARG_format].u_int;
int color_count = vals[ARG_color_count].u_int;
int width = vals[ARG_width].u_int;
int height = vals[ARG_height].u_int;
int stride = vals[ARG_stride].u_int;
mp_obj_t data = vals[ARG_data].u_obj;
mp_obj_t palette = vals[ARG_palette].u_obj;
return objgintimage_make(type, format, color_count, width, height, stride,
data, palette);
}
mp_obj_t objgintimage_make(const mp_obj_type_t *type, int format,
int color_count, int width, int height, int stride, mp_obj_t data,
mp_obj_t palette)
{
bool has_palette = palette != mp_const_none;
/* Type checks */
mp_buffer_info_t buf;
if(!mp_get_buffer(data, &buf, MP_BUFFER_READ))
mp_raise_TypeError("data must be a buffer object");
if(palette != mp_const_none && !mp_get_buffer(palette,&buf,MP_BUFFER_READ))
mp_raise_TypeError("palette must be None or a buffer object");
/* Size and bounds checks */
int data_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(data));
int palette_len =
has_palette ? MP_OBJ_SMALL_INT_VALUE(mp_obj_len(palette)) : 0;
if(width <= 0 || height <= 0)
mp_raise_ValueError("image width/height must be >0");
if(format < 0 || format >= 7 || format == IMAGE_DEPRECATED_P8)
mp_raise_ValueError("invalid image format");
if(data_len < stride * height)
mp_raise_ValueError("data len() should be >= stride * height");
if(IMAGE_IS_RGB16(format) && (color_count > 0 || has_palette))
mp_raise_ValueError("RGB format should have 0 colors and no palette");
if(IMAGE_IS_P8(format) &&
(color_count < 1 || color_count > 256 || !has_palette))
mp_raise_ValueError("P8 format should have palette and 1..256 colors");
if(IMAGE_IS_P4(format) && (color_count != 16 || !has_palette))
mp_raise_ValueError("P4 format should have palette and 16 colors");
if(has_palette && palette_len < 2 * color_count)
mp_raise_ValueError("palette len() should be >= 2*color_count");
/* Construct image! */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t, type);
self->img.format = format;
self->img.color_count = color_count;
self->img.width = width;
self->img.height = height;
self->img.stride = stride;
self->img.data = NULL;
self->img.palette = NULL;
self->data = data;
self->palette = palette;
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t objgintimage_make_from_gint_image(bopti_image_t const *img)
{
/* The original image is assumed to be valid. */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t,
&mp_type_gintimage);
memcpy(&self->img, img, sizeof *img);
int data_size = img->stride * img->height;
int typecode = 'B';
if(IMAGE_IS_RGB16(img->format)) {
// TODO: assert stride even
data_size >>= 1;
typecode = 'H';
}
self->data = ptr_to_memoryview(img->data, data_size, typecode,
(img->flags & IMAGE_FLAGS_DATA_ALLOC) != 0);
self->palette = ptr_to_memoryview((void *)img->palette,
img->color_count, 'H',
(img->flags & IMAGE_FLAGS_PALETTE_ALLOC) != 0);
return MP_OBJ_FROM_PTR(self);
}
STATIC char const *flag_string(int ro, int alloc)
{
static char const *flag_names[] = {
"rw", "ro", "alloc-rw", "alloc-ro",
};
return flag_names[!!ro + 2 * !!alloc];
}
STATIC void image_print(mp_print_t const *print, mp_obj_t self_in,
mp_print_kind_t kind)
{
(void)kind;
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
int f = self->img.flags;
char const *data_str = self->data != mp_const_none ? "py" :
flag_string(f & IMAGE_FLAGS_DATA_RO, f & IMAGE_FLAGS_DATA_ALLOC);
char const *palette_str = self->palette != mp_const_none ? "py" :
flag_string(f & IMAGE_FLAGS_PALETTE_RO, f & IMAGE_FLAGS_PALETTE_ALLOC);
static char const * const fmt_names[] = {
"RGB565", "RGB565A", "LEGACY_P8",
"P4_RGB565A", "P8_RGB565", "P8_RGB565A", "P4_RGB565",
};
char const *format_str =
(self->img.format < 7) ? fmt_names[self->img.format] : "?";
mp_printf(print, "<%s image, %dx%d (%s)",
format_str, self->img.width, self->img.height, data_str);
if(IMAGE_IS_INDEXED(self->img.format))
mp_printf(print, ", %d colors (%s)",
self->img.color_count, palette_str);
mp_printf(print, ">");
}
STATIC void image_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest)
{
if(dest[0] == MP_OBJ_NULL) {
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
if(attr == MP_QSTR_format)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.format);
else if(attr == MP_QSTR_flags)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.flags);
else if(attr == MP_QSTR_color_count)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.color_count);
else if(attr == MP_QSTR_width)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.width);
else if(attr == MP_QSTR_height)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.height);
else if(attr == MP_QSTR_stride)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.stride);
else if(attr == MP_QSTR_data)
dest[0] = self->data;
else if(attr == MP_QSTR_palette)
dest[0] = self->palette;
}
else {
mp_raise_msg(&mp_type_AttributeError,
MP_ERROR_TEXT("gint.image doesn't support changing attributes"));
}
}
#endif /* FXCG50 */
void objgintimage_get(mp_obj_t self_in, bopti_image_t *img)
{
if(!mp_obj_is_type(self_in, &mp_type_gintimage))
mp_raise_TypeError(MP_ERROR_TEXT("image must be a gint.image"));
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
*img = self->img;
img->data = NULL;
if(self->data != mp_const_none) {
mp_buffer_info_t buf;
if(!mp_get_buffer(self->data, &buf, MP_BUFFER_READ))
mp_raise_TypeError("data not a buffer object?!");
img->data = buf.buf;
}
#ifdef FXCG50
img->palette = NULL;
if(self->palette != mp_const_none) {
mp_buffer_info_t buf;
if(!mp_get_buffer(self->palette, &buf, MP_BUFFER_READ))
mp_raise_TypeError("palette not a buffer object?!");
img->palette = buf.buf;
}
#endif
}
MP_DEFINE_CONST_OBJ_TYPE(
mp_type_gintimage,
MP_QSTR_image,
MP_TYPE_FLAG_NONE,
make_new, image_make_new,
print, image_print,
attr, image_attr
);