circuitpython/shared-bindings/jpegio/JpegDecoder.c
2025-02-05 11:09:15 -08:00

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