206 lines
8.9 KiB
C
206 lines
8.9 KiB
C
// This file is part of the CircuitPython project: https://circuitpython.org
|
|
//
|
|
// SPDX-FileCopyrightText: Copyright (c) 2023 Jeff Epler for Adafruit Industries
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "py/obj.h"
|
|
#include "py/builtin.h"
|
|
#include "py/runtime.h"
|
|
|
|
#include "shared-bindings/bitmaptools/__init__.h"
|
|
#include "shared-bindings/displayio/Bitmap.h"
|
|
#include "shared-bindings/jpegio/JpegDecoder.h"
|
|
#include "shared-module/jpegio/JpegDecoder.h"
|
|
#include "shared-module/displayio/Bitmap.h"
|
|
|
|
//| class JpegDecoder:
|
|
//| """A JPEG decoder
|
|
//|
|
|
//| A JpegDecoder allocates a few thousand bytes of memory. To reduce memory fragmentation,
|
|
//| create a single JpegDecoder object and use it anytime a JPEG image needs to be decoded.
|
|
//|
|
|
//| Example::
|
|
//|
|
|
//| from jpegio import JpegDecoder
|
|
//| from displayio import Bitmap
|
|
//|
|
|
//| decoder = JpegDecoder()
|
|
//| width, height = decoder.open("/sd/example.jpg")
|
|
//| bitmap = Bitmap(width, height, 65535)
|
|
//| decoder.decode(bitmap)
|
|
//| # .. do something with bitmap
|
|
//| """
|
|
//|
|
|
//| def __init__(self) -> None:
|
|
//| """Create a JpegDecoder"""
|
|
//| ...
|
|
//|
|
|
static mp_obj_t jpegio_jpegdecoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
|
static const mp_arg_t allowed_args[] = {
|
|
};
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
jpegio_jpegdecoder_obj_t *self = mp_obj_malloc(jpegio_jpegdecoder_obj_t, &jpegio_jpegdecoder_type);
|
|
self->base.type = &jpegio_jpegdecoder_type;
|
|
common_hal_jpegio_jpegdecoder_construct(self);
|
|
|
|
return MP_OBJ_FROM_PTR(self);
|
|
}
|
|
|
|
//|
|
|
//| @overload
|
|
//| def open(self, filename: str) -> Tuple[int, int]: ...
|
|
//| @overload
|
|
//| def open(self, buffer: ReadableBuffer) -> Tuple[int, int]: ...
|
|
//| @overload
|
|
//| def open(self, bytesio: io.BytesIO) -> Tuple[int, int]:
|
|
//| """Use the specified object as the JPEG data source.
|
|
//|
|
|
//| The source may be a filename, a binary buffer in memory, or an opened binary stream.
|
|
//|
|
|
//| The single parameter is positional-only (write ``open(f)``, not
|
|
//| ``open(filename=f)`` but due to technical limitations this is
|
|
//| not shown in the function signature in the documentation.
|
|
//|
|
|
//| Returns the image size as the tuple ``(width, height)``."""
|
|
//|
|
|
static mp_obj_t jpegio_jpegdecoder_open(mp_obj_t self_in, mp_obj_t arg) {
|
|
jpegio_jpegdecoder_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
if (mp_obj_is_str(arg)) {
|
|
arg = mp_call_function_2(
|
|
MP_OBJ_FROM_PTR(&mp_builtin_open_obj),
|
|
arg,
|
|
MP_OBJ_NEW_QSTR(MP_QSTR_rb));
|
|
}
|
|
|
|
mp_buffer_info_t bufinfo;
|
|
const mp_stream_p_t *proto = mp_get_stream(arg);
|
|
|
|
if (proto && proto->read && !proto->is_text) {
|
|
return common_hal_jpegio_jpegdecoder_set_source_file(self, arg);
|
|
} else if (mp_get_buffer(arg, &bufinfo, MP_BUFFER_READ)) {
|
|
return common_hal_jpegio_jpegdecoder_set_source_buffer(self, arg);
|
|
}
|
|
mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q, %q, or %q, not %q"), MP_QSTR_data_source, MP_QSTR_str, MP_QSTR_BytesIO, MP_QSTR_ReadableBuffer);
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_2(jpegio_jpegdecoder_open_obj, jpegio_jpegdecoder_open);
|
|
|
|
//| def decode(
|
|
//| self,
|
|
//| bitmap: displayio.Bitmap,
|
|
//| scale: int = 0,
|
|
//| x: int = 0,
|
|
//| y: int = 0,
|
|
//| *,
|
|
//| x1: int,
|
|
//| y1: int,
|
|
//| x2: int,
|
|
//| y2: int,
|
|
//| skip_source_index: int,
|
|
//| skip_dest_index: int,
|
|
//| ) -> None:
|
|
//| """Decode JPEG data
|
|
//|
|
|
//| The bitmap must be large enough to contain the decoded image.
|
|
//| The pixel data is stored in the `displayio.Colorspace.RGB565_SWAPPED` colorspace.
|
|
//|
|
|
//| The image is optionally downscaled by a factor of ``2**scale``.
|
|
//| Scaling by a factor of 8 (scale=3) is particularly efficient in terms of decoding time.
|
|
//|
|
|
//| The remaining parameters are as for `bitmaptools.blit`.
|
|
//| Because JPEG is a lossy data format, chroma keying based on the "source
|
|
//| index" is not reliable, because the same original RGB value might end
|
|
//| up being decompressed as a similar but not equal color value. Using a
|
|
//| higher JPEG encoding quality can help, but ultimately it will not be
|
|
//| perfect.
|
|
//|
|
|
//| After a call to ``decode``, you must ``open`` a new JPEG. It is not
|
|
//| possible to repeatedly ``decode`` the same jpeg data, even if it is to
|
|
//| select different scales or crop regions from it.
|
|
//|
|
|
//| :param Bitmap bitmap: Optional output buffer
|
|
//| :param int scale: Scale factor from 0 to 3, inclusive.
|
|
//| :param int x: Horizontal pixel location in bitmap where source_bitmap upper-left
|
|
//| corner will be placed
|
|
//| :param int y: Vertical pixel location in bitmap where source_bitmap upper-left
|
|
//| corner will be placed
|
|
//| :param int x1: Minimum x-value for rectangular bounding box to be copied from the source bitmap
|
|
//| :param int y1: Minimum y-value for rectangular bounding box to be copied from the source bitmap
|
|
//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be copied from the source bitmap
|
|
//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be copied from the source bitmap
|
|
//| :param int skip_source_index: bitmap palette index in the source that will not be copied,
|
|
//| set to None to copy all pixels
|
|
//| :param int skip_dest_index: bitmap palette index in the destination bitmap that will not get overwritten
|
|
//| by the pixels from the source
|
|
//| """
|
|
//|
|
|
//|
|
|
static mp_obj_t jpegio_jpegdecoder_decode(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
jpegio_jpegdecoder_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
|
|
enum { ARG_bitmap, ARG_scale, ARG_x, ARG_y, ARGS_X1_Y1_X2_Y2, ARG_skip_source_index, ARG_skip_dest_index };
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_bitmap, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = mp_const_none } },
|
|
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 0 } },
|
|
{ MP_QSTR_x, MP_ARG_INT, {.u_int = 0 } },
|
|
{ MP_QSTR_y, MP_ARG_INT, {.u_int = 0 } },
|
|
ALLOWED_ARGS_X1_Y1_X2_Y2(0, 0),
|
|
{MP_QSTR_skip_source_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
|
{MP_QSTR_skip_dest_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
|
};
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
mp_obj_t bitmap_in = args[ARG_bitmap].u_obj;
|
|
mp_arg_validate_type(bitmap_in, &displayio_bitmap_type, MP_QSTR_bitmap);
|
|
displayio_bitmap_t *bitmap = MP_OBJ_TO_PTR(args[ARG_bitmap].u_obj);
|
|
|
|
int scale = args[ARG_scale].u_int;
|
|
mp_arg_validate_int_range(scale, 0, 3, MP_QSTR_scale);
|
|
|
|
int x = mp_arg_validate_int_range(args[ARG_x].u_int, 0, bitmap->width, MP_QSTR_x);
|
|
int y = mp_arg_validate_int_range(args[ARG_y].u_int, 0, bitmap->height, MP_QSTR_y);
|
|
bitmaptools_rect_t lim = bitmaptools_validate_coord_range_pair(&args[ARG_x1], bitmap->width, bitmap->height);
|
|
|
|
uint32_t skip_source_index;
|
|
bool skip_source_index_none; // flag whether skip_value was None
|
|
|
|
if (args[ARG_skip_source_index].u_obj == mp_const_none) {
|
|
skip_source_index = 0;
|
|
skip_source_index_none = true;
|
|
} else {
|
|
skip_source_index = mp_obj_get_int(args[ARG_skip_source_index].u_obj);
|
|
skip_source_index_none = false;
|
|
}
|
|
|
|
uint32_t skip_dest_index;
|
|
bool skip_dest_index_none; // flag whether skip_self_value was None
|
|
|
|
if (args[ARG_skip_dest_index].u_obj == mp_const_none) {
|
|
skip_dest_index = 0;
|
|
skip_dest_index_none = true;
|
|
} else {
|
|
skip_dest_index = mp_obj_get_int(args[ARG_skip_dest_index].u_obj);
|
|
skip_dest_index_none = false;
|
|
}
|
|
common_hal_jpegio_jpegdecoder_decode_into(self, bitmap, scale, x, y, &lim, skip_source_index, skip_source_index_none, skip_dest_index, skip_dest_index_none);
|
|
|
|
return mp_const_none;
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_KW(jpegio_jpegdecoder_decode_obj, 1, jpegio_jpegdecoder_decode);
|
|
|
|
static const mp_rom_map_elem_t jpegio_jpegdecoder_locals_dict_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&jpegio_jpegdecoder_open_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&jpegio_jpegdecoder_decode_obj) },
|
|
};
|
|
static MP_DEFINE_CONST_DICT(jpegio_jpegdecoder_locals_dict, jpegio_jpegdecoder_locals_dict_table);
|
|
|
|
MP_DEFINE_CONST_OBJ_TYPE(
|
|
jpegio_jpegdecoder_type,
|
|
MP_QSTR_JpegDecoder,
|
|
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
|
|
make_new, jpegio_jpegdecoder_make_new,
|
|
locals_dict, &jpegio_jpegdecoder_locals_dict
|
|
);
|