Implement gifio.GifWriter

This involves:
 * Adding a new "L8" colorspace for colorconverters
 * factoring out displayio_colorconverter_convert_pixel
 * Making a minimal "colorspace only" version of displayio for the
   unix port (testing purposes)
 * fixing an error message

I only tested writing B&W animated images, with the following script:
```python
import displayio
import gifio

with gifio.GifWriter("foo.gif", 64, 64, displayio.Colorspace.L8) as g:
    for i in range(0, 256, 14):
        data = bytes([i, 255-i] * 32 + [255-i, i] * 32) * 32
        print("add_frame")
        g.add_frame(data)

# expected to raise an error, buffer is not big enough
with gifio.GifWriter("/dev/null", 64, 64, displayio.Colorspace.L8) as g:
    g.add_frame(bytes([3,3,3]))
```
This commit is contained in:
Jeff Epler 2021-10-21 11:45:11 -05:00
parent 9da541ed2b
commit c34b6f757f
20 changed files with 700 additions and 59 deletions

View File

@ -104,11 +104,11 @@ msgid "%q length must be >= 1"
msgstr ""
#: py/argcheck.c
msgid "%q must <= %d"
msgid "%q must be %d-%d"
msgstr ""
#: py/argcheck.c
msgid "%q must be %d-%d"
#: py/argcheck.c shared-bindings/gifio/GifWriter.c
msgid "%q must be <= %d"
msgstr ""
#: py/argcheck.c
@ -3073,6 +3073,7 @@ msgstr ""
#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c
#: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/synthio/__init__.c
#: shared-module/gifio/GifWriter.c
msgid "file must be a file opened in byte mode"
msgstr ""
@ -4330,6 +4331,10 @@ msgstr ""
msgid "unsupported Xtensa instruction '%s' with %d arguments"
msgstr ""
#: shared-module/gifio/GifWriter.c
msgid "unsupported colorspace for GifWriter"
msgstr ""
#: py/objstr.c
#, c-format
msgid "unsupported format character '%c' (0x%x) at index %d"

View File

@ -0,0 +1,90 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/enum.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/displayio/__init__.h"
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB888, DISPLAYIO_COLORSPACE_RGB888);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAYIO_COLORSPACE_RGB565);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565_SWAPPED, DISPLAYIO_COLORSPACE_RGB565_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555, DISPLAYIO_COLORSPACE_RGB555);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555_SWAPPED, DISPLAYIO_COLORSPACE_RGB555_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565, DISPLAYIO_COLORSPACE_BGR565);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565_SWAPPED, DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555, DISPLAYIO_COLORSPACE_BGR555);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555_SWAPPED, DISPLAYIO_COLORSPACE_BGR555_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, L8, DISPLAYIO_COLORSPACE_L8);
//| class Colorspace:
//| """The colorspace for a `ColorConverter` to operate in"""
//|
//| RGB888: Colorspace
//| """The standard 24-bit colorspace. Bits 0-7 are blue, 8-15 are green, and 16-24 are red. (0xRRGGBB)"""
//|
//| RGB565: Colorspace
//| """The standard 16-bit colorspace. Bits 0-4 are blue, bits 5-10 are green, and 11-15 are red (0bRRRRRGGGGGGBBBBB)"""
//|
//| RGB565_SWAPPED: Colorspace
//| """The swapped 16-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB565"""
//|
//| RGB555: Colorspace
//| """The standard 15-bit colorspace. Bits 0-4 are blue, bits 5-9 are green, and 11-14 are red. The top bit is ignored. (0bxRRRRRGGGGGBBBBB)"""
//|
//| RGB555_SWAPPED: Colorspace
//| """The swapped 15-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB555"""
//|
MAKE_ENUM_MAP(displayio_colorspace) {
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB888),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, L8),
};
STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table);
MAKE_PRINTER(displayio, displayio_colorspace);
MAKE_ENUM_TYPE(displayio, ColorSpace, displayio_colorspace);
STATIC const mp_rom_map_elem_t displayio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_displayio) },
{ MP_ROM_QSTR(MP_QSTR_Colorspace), MP_ROM_PTR(&displayio_colorspace_type) },
};
STATIC MP_DEFINE_CONST_DICT(displayio_module_globals, displayio_module_globals_table);
const mp_obj_module_t displayio_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&displayio_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_displayio, displayio_module, CIRCUITPY_DISPLAYIO_COLORSPACE_ONLY);

View File

@ -27,5 +27,10 @@ SRC_C += $(SRC_QRIO)
CFLAGS += -DCIRCUITPY_QRIO=1
$(BUILD)/lib/quirc/lib/%.o: CFLAGS += -Wno-shadow -Wno-sign-compare -include shared-module/qrio/quirc_alloc.h
SRC_GIFIO := $(patsubst ../../%,%,$(wildcard ../../shared-bindings/gifio/*.c ../../shared-module/gifio/*.c)) shared/runtime/context_manager_helpers.c displayio_colorspace_only.c shared-module/displayio/ColorConverter.c shared-bindings/util.c
SRC_C += $(SRC_GIFIO)
CFLAGS += -DCIRCUITPY_GIFIO=1 -DCIRCUITPY_DISPLAYIO_COLORSPACE_ONLY=1
SRC_C += coverage.c
SRC_CXX += coveragecpp.cpp

View File

@ -165,7 +165,7 @@ mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name) {
mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t max, qstr arg_name) {
if (i > max) {
mp_raise_ValueError_varg(translate("%q must <= %d"), arg_name, max);
mp_raise_ValueError_varg(translate("%q must be <= %d"), arg_name, max);
}
return i;
}

View File

@ -197,6 +197,9 @@ endif
ifeq ($(CIRCUITPY_GETPASS),1)
SRC_PATTERNS += getpass/%
endif
ifeq ($(CIRCUITPY_GIFIO),1)
SRC_PATTERNS += gifio/%
endif
ifeq ($(CIRCUITPY_GNSS),1)
SRC_PATTERNS += gnss/%
endif
@ -465,6 +468,7 @@ $(filter $(SRC_PATTERNS), \
digitalio/Direction.c \
digitalio/DriveMode.c \
digitalio/Pull.c \
displayio/Colorspace.c \
fontio/Glyph.c \
math/__init__.c \
microcontroller/ResetReason.c \
@ -535,6 +539,8 @@ SRC_SHARED_MODULE_ALL = \
gamepadshift/GamePadShift.c \
gamepadshift/__init__.c \
getpass/__init__.c \
gifio/__init__.c \
gifio/GifWriter.c \
ipaddress/IPv4Address.c \
ipaddress/__init__.c \
keypad/__init__.c \

View File

@ -214,6 +214,9 @@ CFLAGS += -DCIRCUITPY_GAMEPADSHIFT=$(CIRCUITPY_GAMEPADSHIFT)
CIRCUITPY_GETPASS ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_GETPASS=$(CIRCUITPY_GETPASS)
CIRCUITPY_GIFIO ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_GIFIO=$(CIRCUITPY_GIFIO)
CIRCUITPY_GNSS ?= 0
CFLAGS += -DCIRCUITPY_GNSS=$(CIRCUITPY_GNSS)

View File

@ -33,7 +33,6 @@
#include "py/enum.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/util.h"
#include "supervisor/shared/translate.h"

View File

@ -36,6 +36,7 @@ extern const mp_obj_type_t displayio_colorconverter_type;
void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither, displayio_colorspace_t input_colorspace);
void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *colorconverter, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color);
uint32_t displayio_colorconverter_convert_pixel(displayio_colorspace_t colorspace, uint32_t pixel);
void common_hal_displayio_colorconverter_set_dither(displayio_colorconverter_t *self, bool dither);
bool common_hal_displayio_colorconverter_get_dither(displayio_colorconverter_t *self);

View File

@ -0,0 +1,77 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/enum.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/displayio/__init__.h"
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB888, DISPLAYIO_COLORSPACE_RGB888);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAYIO_COLORSPACE_RGB565);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565_SWAPPED, DISPLAYIO_COLORSPACE_RGB565_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555, DISPLAYIO_COLORSPACE_RGB555);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555_SWAPPED, DISPLAYIO_COLORSPACE_RGB555_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565, DISPLAYIO_COLORSPACE_BGR565);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565_SWAPPED, DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555, DISPLAYIO_COLORSPACE_BGR555);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555_SWAPPED, DISPLAYIO_COLORSPACE_BGR555_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, L8, DISPLAYIO_COLORSPACE_L8);
//| class Colorspace:
//| """The colorspace for a `ColorConverter` to operate in"""
//|
//| RGB888: Colorspace
//| """The standard 24-bit colorspace. Bits 0-7 are blue, 8-15 are green, and 16-24 are red. (0xRRGGBB)"""
//|
//| RGB565: Colorspace
//| """The standard 16-bit colorspace. Bits 0-4 are blue, bits 5-10 are green, and 11-15 are red (0bRRRRRGGGGGGBBBBB)"""
//|
//| RGB565_SWAPPED: Colorspace
//| """The swapped 16-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB565"""
//|
//| RGB555: Colorspace
//| """The standard 15-bit colorspace. Bits 0-4 are blue, bits 5-9 are green, and 11-14 are red. The top bit is ignored. (0bxRRRRRGGGGGBBBBB)"""
//|
//| RGB555_SWAPPED: Colorspace
//| """The swapped 15-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB555"""
//|
MAKE_ENUM_MAP(displayio_colorspace) {
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB888),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, L8),
};
STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table);
MAKE_PRINTER(displayio, displayio_colorspace);
MAKE_ENUM_TYPE(displayio, ColorSpace, displayio_colorspace);

View File

@ -69,50 +69,6 @@ STATIC mp_obj_t displayio_release_displays(void) {
}
MP_DEFINE_CONST_FUN_OBJ_0(displayio_release_displays_obj, displayio_release_displays);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB888, DISPLAYIO_COLORSPACE_RGB888);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAYIO_COLORSPACE_RGB565);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565_SWAPPED, DISPLAYIO_COLORSPACE_RGB565_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555, DISPLAYIO_COLORSPACE_RGB555);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555_SWAPPED, DISPLAYIO_COLORSPACE_RGB555_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565, DISPLAYIO_COLORSPACE_BGR565);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565_SWAPPED, DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555, DISPLAYIO_COLORSPACE_BGR555);
MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555_SWAPPED, DISPLAYIO_COLORSPACE_BGR555_SWAPPED);
//| class Colorspace:
//| """The colorspace for a `ColorConverter` to operate in"""
//|
//| RGB888: Colorspace
//| """The standard 24-bit colorspace. Bits 0-7 are blue, 8-15 are green, and 16-24 are red. (0xRRGGBB)"""
//|
//| RGB565: Colorspace
//| """The standard 16-bit colorspace. Bits 0-4 are blue, bits 5-10 are green, and 11-15 are red (0bRRRRRGGGGGGBBBBB)"""
//|
//| RGB565_SWAPPED: Colorspace
//| """The swapped 16-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB565"""
//|
//| RGB555: Colorspace
//| """The standard 15-bit colorspace. Bits 0-4 are blue, bits 5-9 are green, and 11-14 are red. The top bit is ignored. (0bxRRRRRGGGGGBBBBB)"""
//|
//| RGB555_SWAPPED: Colorspace
//| """The swapped 15-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB555"""
//|
MAKE_ENUM_MAP(displayio_colorspace) {
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB888),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565_SWAPPED),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555),
MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555_SWAPPED),
};
STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table);
MAKE_PRINTER(displayio, displayio_colorspace);
MAKE_ENUM_TYPE(displayio, ColorSpace, displayio_colorspace);
STATIC const mp_rom_map_elem_t displayio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_displayio) },
@ -135,7 +91,6 @@ STATIC const mp_rom_map_elem_t displayio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_release_displays), MP_ROM_PTR(&displayio_release_displays_obj) },
};
STATIC MP_DEFINE_CONST_DICT(displayio_module_globals, displayio_module_globals_table);
const mp_obj_module_t displayio_module = {

View File

@ -40,7 +40,7 @@ typedef enum {
CHIP_SELECT_TOGGLE_EVERY_BYTE
} display_chip_select_behavior_t;
typedef enum {
typedef enum displayio_colorspace {
DISPLAYIO_COLORSPACE_RGB888,
DISPLAYIO_COLORSPACE_RGB565,
DISPLAYIO_COLORSPACE_RGB555,
@ -50,6 +50,7 @@ typedef enum {
DISPLAYIO_COLORSPACE_BGR555,
DISPLAYIO_COLORSPACE_BGR565_SWAPPED,
DISPLAYIO_COLORSPACE_BGR555_SWAPPED,
DISPLAYIO_COLORSPACE_L8,
} displayio_colorspace_t;
typedef bool (*display_bus_bus_reset)(mp_obj_t bus);

View File

@ -0,0 +1,157 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/obj.h"
#if MICROPY_VFS
#include "extmod/vfs.h"
#endif
#include "py/runtime.h"
#include "shared-bindings/gifio/GifWriter.h"
#include "shared-module/gifio/GifWriter.h"
#include "shared/runtime/context_manager_helpers.h"
//| class GifWriter:
//| def __init__(self, file: Union[Typing.IO.BinaryIO, str], width:int, height:int, colorspace: displayio.Colorspace, loop:bool=True) -> None:
//| """Construct a GifWriter object
//|
//| :param file: Either a file open in bytes mode, or the name of a file to open in bytes mode.
//| :param width: The width of the image. All frames must have the same width.
//| :param height: The height of the image. All frames must have the same height.
//| :param colorspace: The colorspace of the image. All frames must have the same colorspace. Only 1- and 2-byte colorspace are supported, not ``RGB888``.
//| """
//| ...
//|
static mp_obj_t gifio_gifwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_file, ARG_width, ARG_height, ARG_colorspace, ARG_loop };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} },
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
{ MP_QSTR_colorspace, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} },
{ MP_QSTR_loop, MP_ARG_BOOL, { .u_bool = true } },
};
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);
mp_obj_t file = args[ARG_file].u_obj;
bool own_file = false;
if (mp_obj_is_str(file)) {
file = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), file, MP_OBJ_NEW_QSTR(MP_QSTR_wb));
own_file = true;
}
gifio_gifwriter_t *self = m_new_obj(gifio_gifwriter_t);
self->base.type = &gifio_gifwriter_type;
shared_module_gifio_gifwriter_construct(
self,
file,
args[ARG_width].u_int,
args[ARG_height].u_int,
(displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_colorspace].u_obj),
args[ARG_loop].u_bool,
own_file);
return self;
}
//| def __enter__(self) -> GifWriter:
//| """No-op used by Context Managers."""
//| ...
// Provided by context manager helper.
//| def __exit__(self) -> None:
//| """Automatically deinitializes the hardware when exiting a context. See
//| :ref:`lifetime-and-contextmanagers` for more info."""
//| ...
//|
static mp_obj_t gifio_gifwriter___exit__(size_t n_args, const mp_obj_t *args) {
gifio_gifwriter_t *self = MP_OBJ_TO_PTR(args[0]);
shared_module_gifio_gifwriter_deinit(self);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gifio_gifwriter___exit___obj, 4, 4, gifio_gifwriter___exit__);
//| def deinit(self) -> None:
//| """Close the underlying file."""
//| ...
//|
static mp_obj_t gifio_gifwriter_deinit(mp_obj_t self_in) {
gifio_gifwriter_t *self = MP_OBJ_TO_PTR(self_in);
shared_module_gifio_gifwriter_deinit(self);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_gifwriter_deinit_obj, gifio_gifwriter_deinit);
//| def add_frame(self, bitmap: ReadableBuffer, delay: float = 0.1) -> None:
//| """Add a frame to the GIF.
//|
//| :param bitmap: The frame data
//| :param delay: The frame delay in seconds. The GIF format rounds this to the nearest 1/100 second, and the largest permitted value is 655 seconds.
//| """
//| ...
static mp_obj_t gifio_gifwriter_add_frame(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_bitmap, ARG_delay };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_bitmap, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} },
{ MP_QSTR_delay, MP_ARG_OBJ, {.u_obj = NULL} },
};
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);
gifio_gifwriter_t *self = MP_OBJ_TO_PTR(pos_args[0]);
shared_module_gifio_gifwriter_check_for_deinit(self);
mp_float_t delay = mp_arg_validate_obj_float_non_negative(args[ARG_delay].u_obj, MICROPY_FLOAT_CONST(0.1), MP_QSTR_delay);
if (delay > MICROPY_FLOAT_CONST(655.)) {
mp_raise_ValueError_varg(translate("%q must be <= %d"), MP_QSTR_delay, 655);
}
int delay_centiseconds = (int)(delay * 100);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_bitmap].u_obj, &bufinfo, MP_BUFFER_READ);
shared_module_gifio_gifwriter_add_frame(self, &bufinfo, delay_centiseconds);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(gifio_gifwriter_add_frame_obj, 1, gifio_gifwriter_add_frame);
STATIC const mp_rom_map_elem_t gifio_gifwriter_locals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_GifWriter) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&gifio_gifwriter___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&gifio_gifwriter_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_add_frame), MP_ROM_PTR(&gifio_gifwriter_add_frame_obj) },
};
STATIC MP_DEFINE_CONST_DICT(gifio_gifwriter_locals, gifio_gifwriter_locals_table);
const mp_obj_type_t gifio_gifwriter_type = {
{ &mp_type_type },
.name = MP_QSTR_GifWriter,
.make_new = gifio_gifwriter_make_new,
.locals_dict = (mp_obj_dict_t *)&gifio_gifwriter_locals,
};

View File

@ -0,0 +1,41 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/obj.h"
#pragma once
typedef struct gifio_gifwriter gifio_gifwriter_t;
typedef enum displayio_colorspace displayio_colorspace_t;
extern const mp_obj_type_t gifio_gifwriter_type;
void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool own_file);
void shared_module_gifio_gifwriter_check_for_deinit(gifio_gifwriter_t *self);
bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self);
void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self);
void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_buffer_info_t *buf, int16_t delay);
void shared_module_gifio_gifwriter_close(gifio_gifwriter_t *self);

View File

@ -0,0 +1,47 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/obj.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "shared-bindings/gifio/GifWriter.h"
#include "shared-bindings/util.h"
//| """Access GIF-format images
//| """
//|
STATIC const mp_rom_map_elem_t gifio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gifio) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_GifWriter), MP_ROM_PTR(&gifio_gifwriter_type)},
};
STATIC MP_DEFINE_CONST_DICT(gifio_module_globals, gifio_module_globals_table);
const mp_obj_module_t gifio_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&gifio_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_gifio, gifio_module, CIRCUITPY_GIFIO);

View File

View File

@ -145,15 +145,10 @@ void common_hal_displayio_colorconverter_make_opaque(displayio_colorconverter_t
self->transparent_color = NO_TRANSPARENT_COLOR;
}
void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color) {
uint32_t pixel = input_pixel->pixel;
if (self->transparent_color == pixel) {
output_color->opaque = false;
return;
}
switch (self->input_colorspace) {
// Convert a single input pixel to RGB888
uint32_t displayio_colorconverter_convert_pixel(displayio_colorspace_t colorspace, uint32_t pixel) {
switch (colorspace) {
case DISPLAYIO_COLORSPACE_RGB565_SWAPPED:
pixel = __builtin_bswap16(pixel);
MP_FALLTHROUGH;
@ -198,10 +193,31 @@ void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d
}
break;
default:
case DISPLAYIO_COLORSPACE_RGB888:
break;
case DISPLAYIO_COLORSPACE_L8: {
uint32_t l8 = pixel & 0xff;
pixel = l8 * 0x010101;
}
break;
}
return pixel;
}
void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color) {
uint32_t pixel = input_pixel->pixel;
if (self->transparent_color == pixel) {
output_color->opaque = false;
return;
}
pixel = displayio_colorconverter_convert_pixel(self->input_colorspace, pixel);
if (self->dither) {
uint8_t randr = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x,input_pixel->tile_y));
uint8_t randg = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x + 33,input_pixel->tile_y));

View File

@ -0,0 +1,198 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "shared-module/gifio/GifWriter.h"
#include "shared-bindings/gifio/GifWriter.h"
#include "shared-bindings/displayio/ColorConverter.h"
#include "shared-bindings/util.h"
#define BLOCK_SIZE (126) // (2^7) - 2 // (DO NOT CHANGE!)
static void handle_error(const char *what, int error) {
if (error != 0) {
mp_raise_OSError(error);
}
}
static void write_data(gifio_gifwriter_t *self, const void *data, size_t size) {
int error = 0;
self->file_proto->write(self->file, data, size, &error);
handle_error("write_data", error);
}
static void write_byte(gifio_gifwriter_t *self, uint8_t value) {
write_data(self, &value, sizeof(value));
}
static void write_long(gifio_gifwriter_t *self, uint32_t value) {
write_data(self, &value, sizeof(value));
}
static void write_word(gifio_gifwriter_t *self, uint16_t value) {
write_data(self, &value, sizeof(value));
}
void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool own_file) {
self->file = file;
self->file_proto = mp_proto_get_or_throw(MP_QSTR_protocol_stream, file);
if (self->file_proto->is_text) {
mp_raise_TypeError(translate("file must be a file opened in byte mode"));
}
self->width = width;
self->height = height;
self->colorspace = colorspace;
self->own_file = own_file;
write_data(self, "GIF89a", 6);
write_word(self, width);
write_word(self, height);
write_data(self, (uint8_t []) {0xF6, 0x00, 0x00}, 3);
if (colorspace == DISPLAYIO_COLORSPACE_RGB888) {
mp_raise_TypeError(translate("unsupported colorspace for GifWriter"));
}
bool color = (colorspace != DISPLAYIO_COLORSPACE_L8);
if (color) {
for (int i = 0; i < 128; i++) {
int red = (int)(((((i & 0x60) >> 5) * 255) + 1.5) / 3);
int green = (int)(((((i & 0x1C) >> 2) * 255) + 3.5) / 7);
int blue = (int)((((i & 0x3) * 255) + 1.5) / 3);
write_data(self, (uint8_t []) {red, green, blue}, 3);
}
} else {
for (int i = 0; i < 128; i++) {
int gray = (int)(((i * 255) + 63.5) / 127);
write_data(self, (uint8_t []) {gray, gray, gray}, 3);
}
}
if (loop) {
write_data(self, (uint8_t []) {'!', 0xFF, 0x0B}, 3);
write_data(self, "NETSCAPE2.0", 11);
write_data(self, (uint8_t []) {0x03, 0x01, 0x00, 0x00, 0x00}, 5);
}
}
bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self) {
return !self->file;
}
void shared_module_gifio_gifwriter_check_for_deinit(gifio_gifwriter_t *self) {
if (shared_module_gifio_gifwriter_deinited(self)) {
raise_deinited_error();
}
}
void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self) {
if (!shared_module_gifio_gifwriter_deinited(self)) {
shared_module_gifio_gifwriter_close(self);
}
}
void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_buffer_info_t *bufinfo, int16_t delay) {
if (delay) {
write_data(self, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4);
write_word(self, delay);
write_word(self, 0); // end
}
write_byte(self, 0x2C);
write_long(self, 0);
write_word(self, self->width);
write_word(self, self->height);
write_data(self, (uint8_t []) {0x00, 0x07}, 2); // 7-bits
int pixel_count = self->width * self->height;
int blocks = (pixel_count + BLOCK_SIZE - 1) / BLOCK_SIZE;
uint8_t block_data[2 + BLOCK_SIZE];
block_data[1] = 0x80;
if (self->colorspace == DISPLAYIO_COLORSPACE_L8) {
mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(pixel_count - 1), false);
uint8_t *pixels = bufinfo->buf;
for (int i = 0; i < blocks; i++) {
assert(pixel_count >= 0);
int block_size = MIN(BLOCK_SIZE, pixel_count);
pixel_count -= block_size;
block_data[0] = 1 + block_size;
for (int j = 0; j < block_size; j++) {
block_data[j + 2] = (*pixels++) >> 1;
}
write_data(self, block_data, 2 + block_size);
}
} else {
mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false);
uint16_t *pixels = bufinfo->buf;
for (int i = 0; i < blocks; i++) {
int block_size = MIN(BLOCK_SIZE, pixel_count);
pixel_count -= block_size;
block_data[0] = 1 + block_size;
for (int j = 0; j < block_size; j++) {
int pixel = displayio_colorconverter_convert_pixel(self->colorspace, (*pixels++));
int red = (pixel >> (16 + 6)) & 0x3;
int green = (pixel >> (8 + 5)) & 0x7;
int blue = (pixel >> 6) & 0x3;
block_data[j + 2] = (red << 5) | (green << 2) | blue;
}
write_data(self, block_data, 2 + block_size);
}
}
write_data(self, (uint8_t []) {0x01, 0x81, 0x00}, 3); // end code
int error = 0;
self->file_proto->ioctl(self->file, MP_STREAM_FLUSH, 0, &error);
handle_error("flush", error);
}
void shared_module_gifio_gifwriter_close(gifio_gifwriter_t *self) {
// we want to ensure the stream is closed even if the first write failed, so we don't use write_data
int error1 = 0;
self->file_proto->write(self->file, ";", 1, &error1);
int error2 = 0;
if (self->own_file) {
self->file_proto->ioctl(self->file, MP_STREAM_CLOSE, 0, &error2);
} else {
self->file_proto->ioctl(self->file, MP_STREAM_FLUSH, 0, &error2);
}
self->file = NULL;
handle_error("write", error1);
handle_error("close", error2);
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include "py/obj.h"
#include "py/stream.h"
#include "shared-bindings/displayio/__init__.h"
typedef struct gifio_gifwriter {
mp_obj_base_t base;
mp_obj_t *file;
const mp_stream_p_t *file_proto;
displayio_colorspace_t colorspace;
int width, height;
bool own_file;
} gifio_gifwriter_t;

View File

View File