From c34b6f757fc385d99086bdd755f74ceabf613454 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 21 Oct 2021 11:45:11 -0500 Subject: [PATCH] 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])) ``` --- locale/circuitpython.pot | 11 +- ports/unix/displayio_colorspace_only.c | 90 ++++++++ .../unix/variants/coverage/mpconfigvariant.mk | 5 + py/argcheck.c | 2 +- py/circuitpy_defns.mk | 6 + py/circuitpy_mpconfig.mk | 3 + shared-bindings/displayio/ColorConverter.c | 1 - shared-bindings/displayio/ColorConverter.h | 1 + shared-bindings/displayio/Colorspace.c | 77 +++++++ shared-bindings/displayio/__init__.c | 45 ---- shared-bindings/displayio/__init__.h | 3 +- shared-bindings/gifio/GifWriter.c | 157 ++++++++++++++ shared-bindings/gifio/GifWriter.h | 41 ++++ shared-bindings/gifio/__init__.c | 47 +++++ shared-bindings/gifio/__init__.h | 0 shared-module/displayio/ColorConverter.c | 32 ++- shared-module/gifio/GifWriter.c | 198 ++++++++++++++++++ shared-module/gifio/GifWriter.h | 40 ++++ shared-module/gifio/__init__.c | 0 shared-module/gifio/__init__.h | 0 20 files changed, 700 insertions(+), 59 deletions(-) create mode 100644 ports/unix/displayio_colorspace_only.c create mode 100644 shared-bindings/displayio/Colorspace.c create mode 100644 shared-bindings/gifio/GifWriter.c create mode 100644 shared-bindings/gifio/GifWriter.h create mode 100644 shared-bindings/gifio/__init__.c create mode 100644 shared-bindings/gifio/__init__.h create mode 100644 shared-module/gifio/GifWriter.c create mode 100644 shared-module/gifio/GifWriter.h create mode 100644 shared-module/gifio/__init__.c create mode 100644 shared-module/gifio/__init__.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index f1f747e7e6..bb5c2ee6b2 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -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" diff --git a/ports/unix/displayio_colorspace_only.c b/ports/unix/displayio_colorspace_only.c new file mode 100644 index 0000000000..40325ec76a --- /dev/null +++ b/ports/unix/displayio_colorspace_only.c @@ -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); diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index e5d0f7e2d3..e614cd411e 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -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 diff --git a/py/argcheck.c b/py/argcheck.c index aa88eeed6c..c2066a7c39 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -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; } diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 7d2d128a73..ff2933caa4 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -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 \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index be373b78a2..443bd41d8b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -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) diff --git a/shared-bindings/displayio/ColorConverter.c b/shared-bindings/displayio/ColorConverter.c index c093c70128..2f40d8bd36 100644 --- a/shared-bindings/displayio/ColorConverter.c +++ b/shared-bindings/displayio/ColorConverter.c @@ -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" diff --git a/shared-bindings/displayio/ColorConverter.h b/shared-bindings/displayio/ColorConverter.h index 018db92c96..12fa8da20f 100644 --- a/shared-bindings/displayio/ColorConverter.h +++ b/shared-bindings/displayio/ColorConverter.h @@ -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); diff --git a/shared-bindings/displayio/Colorspace.c b/shared-bindings/displayio/Colorspace.c new file mode 100644 index 0000000000..3692dc29bc --- /dev/null +++ b/shared-bindings/displayio/Colorspace.c @@ -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); diff --git a/shared-bindings/displayio/__init__.c b/shared-bindings/displayio/__init__.c index 7ac2a84903..5588dc83e9 100644 --- a/shared-bindings/displayio/__init__.c +++ b/shared-bindings/displayio/__init__.c @@ -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 = { diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h index 938fcf3d98..b297e4d755 100644 --- a/shared-bindings/displayio/__init__.h +++ b/shared-bindings/displayio/__init__.h @@ -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); diff --git a/shared-bindings/gifio/GifWriter.c b/shared-bindings/gifio/GifWriter.c new file mode 100644 index 0000000000..8c9a9a3f3b --- /dev/null +++ b/shared-bindings/gifio/GifWriter.c @@ -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, +}; diff --git a/shared-bindings/gifio/GifWriter.h b/shared-bindings/gifio/GifWriter.h new file mode 100644 index 0000000000..203169cd75 --- /dev/null +++ b/shared-bindings/gifio/GifWriter.h @@ -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); diff --git a/shared-bindings/gifio/__init__.c b/shared-bindings/gifio/__init__.c new file mode 100644 index 0000000000..317eebce9b --- /dev/null +++ b/shared-bindings/gifio/__init__.c @@ -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); diff --git a/shared-bindings/gifio/__init__.h b/shared-bindings/gifio/__init__.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index be5b7d31a7..228d63d03b 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -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)); diff --git a/shared-module/gifio/GifWriter.c b/shared-module/gifio/GifWriter.c new file mode 100644 index 0000000000..a428b17a97 --- /dev/null +++ b/shared-module/gifio/GifWriter.c @@ -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 + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * 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); +} diff --git a/shared-module/gifio/GifWriter.h b/shared-module/gifio/GifWriter.h new file mode 100644 index 0000000000..e16de7c06b --- /dev/null +++ b/shared-module/gifio/GifWriter.h @@ -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; diff --git a/shared-module/gifio/__init__.c b/shared-module/gifio/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/shared-module/gifio/__init__.h b/shared-module/gifio/__init__.h new file mode 100644 index 0000000000..e69de29bb2